<commit_before>package scipipe

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// Task represents a single static shell command, or go function, to be
// executed, and are scheduled and managed by a corresponding Process
type Task struct {
	Name          string
	Command       string
	CustomExecute func(*Task)
	InIPs         map[string]*FileIP
	OutIPs        map[string]*FileIP
	Params        map[string]string
	Done          chan int
	cores         int
	workflow      *Workflow
	process       *Process
}

// ------------------------------------------------------------------------
// Factory method(s)
// ------------------------------------------------------------------------

// NewTask instantiates and initializes a new Task
func NewTask(workflow *Workflow, process *Process, name string, cmdPat string, inIPs map[string]*FileIP, outPathFuncs map[string]func(*Task) string, outPortsDoStream map[string]bool, params map[string]string, prepend string, customExecute func(*Task), cores int) *Task {
	t := &Task{
		Name:          name,
		InIPs:         inIPs,
		OutIPs:        make(map[string]*FileIP),
		Params:        params,
		Command:       "",
		CustomExecute: customExecute,
		Done:          make(chan int),
		cores:         cores,
		workflow:      workflow,
		process:       process,
	}

	// Create Out-IPs
	for oname, outPathFunc := range outPathFuncs {
		oip := NewFileIP(outPathFunc(t))
		if outPortsDoStream[oname] {
			oip.doStream = true
		}
		t.OutIPs[oname] = oip
	}
	t.Command = formatCommand(cmdPat, inIPs, t.OutIPs, params, prepend)
	return t
}

// formatCommand is a helper function for NewTask, that formats a shell command
// based on concrete file paths and parameter values
func formatCommand(cmd string, inIPs map[string]*FileIP, outIPs map[string]*FileIP, params map[string]string, prepend string) string {
	r := getShellCommandPlaceHolderRegex()
	placeHolderMatches := r.FindAllStringSubmatch(cmd, -1)
	for _, placeHolderMatch := range placeHolderMatches {
		var filePath string

		placeHolderStr := placeHolderMatch[0]
		portType := placeHolderMatch[1]
		portName := placeHolderMatch[2]
		sep := " " // Default

		switch portType {
		case "o":
			if outIPs[portName] == nil {
				Fail("Missing outpath for outport '", portName, "' for command '", cmd, "'")
			}
			filePath = outIPs[portName].TempPath()
		case "os":
			if outIPs[portName] == nil {
				Fail("Missing outpath for outport '", portName, "' for command '", cmd, "'")
			}
			filePath = outIPs[portName].FifoPath()
		case "i":
			if inIPs[portName] == nil {
				Fail("Missing in-IP for inport '", portName, "' for command '", cmd, "'")
			}
			// Identify if the place holder represents a reduce-type in-port
			reduceInputs := false
			if len(placeHolderMatch) > 3 {
				sep = placeHolderMatch[5]
				reduceInputs = true
			}
			if reduceInputs && inIPs[portName].Path() == "" {
				// Merge multiple input paths from a substream on the IP, into one string
				ips := []*FileIP{}
				for ip := range inIPs[portName].SubStream.Chan {
					ips = append(ips, ip)
				}
				paths := []string{}
				for _, ip := range ips {
					paths = append(paths, ip.Path())
				}
				filePath = strings.Join(paths, sep)
			} else {
				if inIPs[portName].Path() == "" {
					Fail("Missing inpath for inport '", portName, "', and no substream, for command '", cmd, "'")
				}
				if inIPs[portName].doStream {
					filePath = inIPs[portName].FifoPath()
				} else {
					filePath = inIPs[portName].Path()
				}
			}
		case "p":
			if params[portName] == "" {
				msg := fmt.Sprint("Missing param value param '", portName, "' for command '", cmd, "'")
				CheckWithMsg(errors.New(msg), msg)
			} else {
				filePath = params[portName]
			}
		default:
			Fail("Replace failed for port ", portName, " for command '", cmd, "'")
		}
		cmd = strings.Replace(cmd, placeHolderStr, filePath, -1)
	}

	// Add prepend string to the command
	if prepend != "" {
		cmd = fmt.Sprintf("%s %s", prepend, cmd)
	}

	return cmd
}

// ------------------------------------------------------------------------
// Main API methods: Accessor methods
// ------------------------------------------------------------------------

// InIP returns an IP for the in-port with name portName
func (t *Task) InIP(portName string) *FileIP {
	if t.InIPs[portName] == nil {
		Failf("No such in-portname (%s) in task (%s)\n", portName, t.Name)
	}
	return t.InIPs[portName]
}

// InPath returns the path name of an input file for the task
func (t *Task) InPath(portName string) string {
	return t.InIP(portName).Path()
}

// OutIP returns an IP for the in-port with name portName
func (t *Task) OutIP(portName string) *FileIP {
	if t.OutIPs[portName] == nil {
		Failf("No such out-portname (%s) in task (%s)\n", portName, t.Name)
	}
	return t.OutIPs[portName]
}

// OutPath returns the path name of an input file for the task
func (t *Task) OutPath(portName string) string {
	return t.OutIP(portName).Path()
}

// Param returns the value of a param, for the task
func (t *Task) Param(portName string) string {
	if param, ok := t.Params[portName]; ok {
		return param
	}
	Failf("No such param port '%s' for task '%s'\n", portName, t.Name)
	return "invalid"
}

// ------------------------------------------------------------------------
// Execute the task
// ------------------------------------------------------------------------

// Execute executes the task (the shell command or go function in CustomExecute)
func (t *Task) Execute() {
	defer close(t.Done)

	// Do some sanity checks
	if t.anyTempfileExists() {
		Failf("Task:%s-12s Existing temp files found so existing. Clean up .tmp files before restarting the workflow!", t.Name)
	}

	if t.anyOutputsExist() {
		Audit.Printf("Task:%s-12s Outputs exist, so not executing", t.Name)
		t.Done <- 1
		return
	}

	// Execute task
	t.workflow.IncConcurrentTasks(t.cores) // Will block if max concurrent tasks is reached
	t.createDirs()                         // Create output directories needed for any outputs
	startTime := time.Now()
	if t.CustomExecute != nil {
		outputsStr := ""
		for oipName, oip := range t.OutIPs {
			outputsStr += " " + oipName + ": " + oip.Path()
		}
		Audit.Printf("Task:%-12s Executing custom Go function, producing output(s):%s\n", t.Name, outputsStr)
		t.CustomExecute(t)
	} else {
		Audit.Printf("Task:%-12s Executing command: %s\n", t.Name, t.Command)
		t.executeCommand(t.Command)
	}
	finishTime := time.Now()
	t.writeAuditLogs(startTime, finishTime)
	t.atomizeIPs()
	t.workflow.DecConcurrentTasks(t.cores)

	t.Done <- 1
}

// ------------------------------------------------------------------------
// Helper methods for the Execute method
// ------------------------------------------------------------------------

// anyTempFileExists checks if any temporary workflow files exist and if so, returns true
func (t *Task) anyTempfileExists() (anyTempfileExists bool) {
	anyTempfileExists = false
	for _, oip := range t.OutIPs {
		if !oip.doStream {
			otmpPath := oip.TempPath()
			if _, err := os.Stat(otmpPath); err == nil {
				Warning.Printf("Task:%-12s Temp file already exists: %s (Note: If resuming from a failed run, clean up .tmp files first. Also, make sure that two processes don't produce the same output files!).\n", t.Name, otmpPath)
				anyTempfileExists = true
			}
		}
	}
	return
}

// anyOutputsExist if any output file IP, or temporary file IPs, exist
func (t *Task) anyOutputsExist() (anyFileExists bool) {
	anyFileExists = false
	for _, oip := range t.OutIPs {
		if !oip.doStream {
			opath := oip.Path()
			if _, err := os.Stat(opath); err == nil {
				Info.Printf("Task:%-12s Output file already exists, so skipping: %s\n", t.Name, opath)
				anyFileExists = true
			}
		}
	}
	return
}

// createDirs creates directories for out-IPs of the task
func (t *Task) createDirs() {
	for _, oip := range t.OutIPs {
		oipDir := filepath.Dir(oip.Path())
		err := os.MkdirAll(oipDir, 0777)
		CheckWithMsg(err, "Could not create directory: "+oipDir)
	}

}

// executeCommand executes the shell command cmd via bash
func (t *Task) executeCommand(cmd string) {
	out, err := exec.Command("bash", "-c", cmd).CombinedOutput()
	if err != nil {
		Failf("Command failed!\nCommand:\n%s\n\nOutput:\n%s\n\n", cmd, string(out))
	}
}

func (t *Task) writeAuditLogs(startTime time.Time, finishTime time.Time) {
	// Append audit info for the task to all its output IPs
	auditInfo := NewAuditInfo()
	auditInfo.Command = t.Command
	auditInfo.ProcessName = t.process.Name()
	auditInfo.Params = t.Params
	auditInfo.StartTime = startTime
	auditInfo.FinishTime = finishTime
	auditInfo.ExecTimeMS = finishTime.Sub(startTime) / time.Millisecond
	// Set the audit infos from incoming IPs into the "Upstream" map
	for _, iip := range t.InIPs {
		iipPath := iip.Path()
		iipAuditInfo := iip.AuditInfo()
		auditInfo.Upstream[iipPath] = iipAuditInfo
	}
	// Add the current audit info to output ips and write them to file
	for _, oip := range t.OutIPs {
		oip.SetAuditInfo(auditInfo)
		for _, iip := range t.InIPs {
			oip.AddKeys(iip.Keys())
		}
		oip.WriteAuditLogToFile()
	}
}

// Rename temporary output files to their proper file names
func (t *Task) atomizeIPs() {
	for _, oip := range t.OutIPs {
		if !oip.doStream {
			oip.Atomize()
		}
	}
}
<commit_msg>Include full error message when command fails<commit_after>package scipipe

import (
	"errors"
	"fmt"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"time"
)

// Task represents a single static shell command, or go function, to be
// executed, and are scheduled and managed by a corresponding Process
type Task struct {
	Name          string
	Command       string
	CustomExecute func(*Task)
	InIPs         map[string]*FileIP
	OutIPs        map[string]*FileIP
	Params        map[string]string
	Done          chan int
	cores         int
	workflow      *Workflow
	process       *Process
}

// ------------------------------------------------------------------------
// Factory method(s)
// ------------------------------------------------------------------------

// NewTask instantiates and initializes a new Task
func NewTask(workflow *Workflow, process *Process, name string, cmdPat string, inIPs map[string]*FileIP, outPathFuncs map[string]func(*Task) string, outPortsDoStream map[string]bool, params map[string]string, prepend string, customExecute func(*Task), cores int) *Task {
	t := &Task{
		Name:          name,
		InIPs:         inIPs,
		OutIPs:        make(map[string]*FileIP),
		Params:        params,
		Command:       "",
		CustomExecute: customExecute,
		Done:          make(chan int),
		cores:         cores,
		workflow:      workflow,
		process:       process,
	}

	// Create Out-IPs
	for oname, outPathFunc := range outPathFuncs {
		oip := NewFileIP(outPathFunc(t))
		if outPortsDoStream[oname] {
			oip.doStream = true
		}
		t.OutIPs[oname] = oip
	}
	t.Command = formatCommand(cmdPat, inIPs, t.OutIPs, params, prepend)
	return t
}

// formatCommand is a helper function for NewTask, that formats a shell command
// based on concrete file paths and parameter values
func formatCommand(cmd string, inIPs map[string]*FileIP, outIPs map[string]*FileIP, params map[string]string, prepend string) string {
	r := getShellCommandPlaceHolderRegex()
	placeHolderMatches := r.FindAllStringSubmatch(cmd, -1)
	for _, placeHolderMatch := range placeHolderMatches {
		var filePath string

		placeHolderStr := placeHolderMatch[0]
		portType := placeHolderMatch[1]
		portName := placeHolderMatch[2]
		sep := " " // Default

		switch portType {
		case "o":
			if outIPs[portName] == nil {
				Fail("Missing outpath for outport '", portName, "' for command '", cmd, "'")
			}
			filePath = outIPs[portName].TempPath()
		case "os":
			if outIPs[portName] == nil {
				Fail("Missing outpath for outport '", portName, "' for command '", cmd, "'")
			}
			filePath = outIPs[portName].FifoPath()
		case "i":
			if inIPs[portName] == nil {
				Fail("Missing in-IP for inport '", portName, "' for command '", cmd, "'")
			}
			// Identify if the place holder represents a reduce-type in-port
			reduceInputs := false
			if len(placeHolderMatch) > 3 {
				sep = placeHolderMatch[5]
				reduceInputs = true
			}
			if reduceInputs && inIPs[portName].Path() == "" {
				// Merge multiple input paths from a substream on the IP, into one string
				ips := []*FileIP{}
				for ip := range inIPs[portName].SubStream.Chan {
					ips = append(ips, ip)
				}
				paths := []string{}
				for _, ip := range ips {
					paths = append(paths, ip.Path())
				}
				filePath = strings.Join(paths, sep)
			} else {
				if inIPs[portName].Path() == "" {
					Fail("Missing inpath for inport '", portName, "', and no substream, for command '", cmd, "'")
				}
				if inIPs[portName].doStream {
					filePath = inIPs[portName].FifoPath()
				} else {
					filePath = inIPs[portName].Path()
				}
			}
		case "p":
			if params[portName] == "" {
				msg := fmt.Sprint("Missing param value param '", portName, "' for command '", cmd, "'")
				CheckWithMsg(errors.New(msg), msg)
			} else {
				filePath = params[portName]
			}
		default:
			Fail("Replace failed for port ", portName, " for command '", cmd, "'")
		}
		cmd = strings.Replace(cmd, placeHolderStr, filePath, -1)
	}

	// Add prepend string to the command
	if prepend != "" {
		cmd = fmt.Sprintf("%s %s", prepend, cmd)
	}

	return cmd
}

// ------------------------------------------------------------------------
// Main API methods: Accessor methods
// ------------------------------------------------------------------------

// InIP returns an IP for the in-port with name portName
func (t *Task) InIP(portName string) *FileIP {
	if t.InIPs[portName] == nil {
		Failf("No such in-portname (%s) in task (%s)\n", portName, t.Name)
	}
	return t.InIPs[portName]
}

// InPath returns the path name of an input file for the task
func (t *Task) InPath(portName string) string {
	return t.InIP(portName).Path()
}

// OutIP returns an IP for the in-port with name portName
func (t *Task) OutIP(portName string) *FileIP {
	if t.OutIPs[portName] == nil {
		Failf("No such out-portname (%s) in task (%s)\n", portName, t.Name)
	}
	return t.OutIPs[portName]
}

// OutPath returns the path name of an input file for the task
func (t *Task) OutPath(portName string) string {
	return t.OutIP(portName).Path()
}

// Param returns the value of a param, for the task
func (t *Task) Param(portName string) string {
	if param, ok := t.Params[portName]; ok {
		return param
	}
	Failf("No such param port '%s' for task '%s'\n", portName, t.Name)
	return "invalid"
}

// ------------------------------------------------------------------------
// Execute the task
// ------------------------------------------------------------------------

// Execute executes the task (the shell command or go function in CustomExecute)
func (t *Task) Execute() {
	defer close(t.Done)

	// Do some sanity checks
	if t.anyTempfileExists() {
		Failf("Task:%s-12s Existing temp files found so existing. Clean up .tmp files before restarting the workflow!", t.Name)
	}

	if t.anyOutputsExist() {
		Audit.Printf("Task:%s-12s Outputs exist, so not executing", t.Name)
		t.Done <- 1
		return
	}

	// Execute task
	t.workflow.IncConcurrentTasks(t.cores) // Will block if max concurrent tasks is reached
	t.createDirs()                         // Create output directories needed for any outputs
	startTime := time.Now()
	if t.CustomExecute != nil {
		outputsStr := ""
		for oipName, oip := range t.OutIPs {
			outputsStr += " " + oipName + ": " + oip.Path()
		}
		Audit.Printf("Task:%-12s Executing custom Go function, producing output(s):%s\n", t.Name, outputsStr)
		t.CustomExecute(t)
	} else {
		Audit.Printf("Task:%-12s Executing command: %s\n", t.Name, t.Command)
		t.executeCommand(t.Command)
	}
	finishTime := time.Now()
	t.writeAuditLogs(startTime, finishTime)
	t.atomizeIPs()
	t.workflow.DecConcurrentTasks(t.cores)

	t.Done <- 1
}

// ------------------------------------------------------------------------
// Helper methods for the Execute method
// ------------------------------------------------------------------------

// anyTempFileExists checks if any temporary workflow files exist and if so, returns true
func (t *Task) anyTempfileExists() (anyTempfileExists bool) {
	anyTempfileExists = false
	for _, oip := range t.OutIPs {
		if !oip.doStream {
			otmpPath := oip.TempPath()
			if _, err := os.Stat(otmpPath); err == nil {
				Warning.Printf("Task:%-12s Temp file already exists: %s (Note: If resuming from a failed run, clean up .tmp files first. Also, make sure that two processes don't produce the same output files!).\n", t.Name, otmpPath)
				anyTempfileExists = true
			}
		}
	}
	return
}

// anyOutputsExist if any output file IP, or temporary file IPs, exist
func (t *Task) anyOutputsExist() (anyFileExists bool) {
	anyFileExists = false
	for _, oip := range t.OutIPs {
		if !oip.doStream {
			opath := oip.Path()
			if _, err := os.Stat(opath); err == nil {
				Info.Printf("Task:%-12s Output file already exists, so skipping: %s\n", t.Name, opath)
				anyFileExists = true
			}
		}
	}
	return
}

// createDirs creates directories for out-IPs of the task
func (t *Task) createDirs() {
	for _, oip := range t.OutIPs {
		oipDir := filepath.Dir(oip.Path())
		err := os.MkdirAll(oipDir, 0777)
		CheckWithMsg(err, "Could not create directory: "+oipDir)
	}

}

// executeCommand executes the shell command cmd via bash
func (t *Task) executeCommand(cmd string) {
	out, err := exec.Command("bash", "-c", cmd).CombinedOutput()
	if err != nil {
		Failf("Command failed!\nCommand:\n%s\n\nOutput:\n%s\nOriginal error:%s\n", cmd, string(out), err.Error())
	}
}

func (t *Task) writeAuditLogs(startTime time.Time, finishTime time.Time) {
	// Append audit info for the task to all its output IPs
	auditInfo := NewAuditInfo()
	auditInfo.Command = t.Command
	auditInfo.ProcessName = t.process.Name()
	auditInfo.Params = t.Params
	auditInfo.StartTime = startTime
	auditInfo.FinishTime = finishTime
	auditInfo.ExecTimeMS = finishTime.Sub(startTime) / time.Millisecond
	// Set the audit infos from incoming IPs into the "Upstream" map
	for _, iip := range t.InIPs {
		iipPath := iip.Path()
		iipAuditInfo := iip.AuditInfo()
		auditInfo.Upstream[iipPath] = iipAuditInfo
	}
	// Add the current audit info to output ips and write them to file
	for _, oip := range t.OutIPs {
		oip.SetAuditInfo(auditInfo)
		for _, iip := range t.InIPs {
			oip.AddKeys(iip.Keys())
		}
		oip.WriteAuditLogToFile()
	}
}

// Rename temporary output files to their proper file names
func (t *Task) atomizeIPs() {
	for _, oip := range t.OutIPs {
		if !oip.doStream {
			oip.Atomize()
		}
	}
}
<|endoftext|>


<commit_before>package api

// package api
//
// import (
// 	"encoding/json"
// 	"github.com/flexiant/concerto/api/types"
// 	"github.com/flexiant/concerto/testdata"
// 	"github.com/flexiant/concerto/utils"
// 	"github.com/stretchr/testify/assert"
// 	"testing"
// )
//
// func TestGetDomainList(t *testing.T) {
//
// 	assert := assert.New(t)
//
// 	domainsTest, err := testdata.GetDomainData()
// 	assert.Nil(err, "Couldn't load domain test data")
//
// 	var domainsIn []types.Domain
// 	for _, domainTest := range domainsTest {
// 		if domainTest.FieldsOK {
// 			domainsIn = append(domainsIn, domainTest.Domain)
// 		}
// 	}
//
// 	getDomainList(t, &domainsIn)
//
// }
//
// func getDomainList(t *testing.T, domainsIn *[]types.Domain) {
// 	assert := assert.New(t)
//
// 	// wire up
// 	cs := &utils.MockConcertoService{}
// 	ds, err := NewDomainService(cs)
// 	assert.Nil(err, "Couldn't load domain service")
// 	assert.NotNil(ds, "Domain service not instanced")
//
// 	// to json
// 	dIn, err := json.Marshal(domainsIn)
// 	assert.Nil(err, "Domain test data corrupted")
//
// 	// call service
// 	cs.On("Get", "/v1/dns/domains").Return(dIn, 200, nil)
// 	domainsOut, err := ds.GetDomainList()
// 	assert.Nil(err, "Error getting domain list")
// 	assert.Equal(*domainsIn, domainsOut, "GetDomainList returned different domains")
// }
<commit_msg>api tests<commit_after>package api

import (
	"encoding/json"
	"github.com/flexiant/concerto/api/types"
	"github.com/flexiant/concerto/testdata"
	"github.com/flexiant/concerto/utils"
	"github.com/stretchr/testify/assert"
	"testing"
)

func TestGetDomainList(t *testing.T) {

	assert := assert.New(t)

	domainsTest, err := testdata.GetDomainData()
	assert.Nil(err, "Couldn't load domain test data")

	var domainsIn []types.Domain
	for _, domainTest := range domainsTest {
		if domainTest.FieldsOK {
			domainsIn = append(domainsIn, domainTest.Domain)
		}
	}

	getDomainList(t, &domainsIn)

}

func getDomainList(t *testing.T, domainsIn *[]types.Domain) {
	assert := assert.New(t)

	// wire up
	cs := &utils.MockConcertoService{}
	ds, err := NewDomainService(cs)
	assert.Nil(err, "Couldn't load domain service")
	assert.NotNil(ds, "Domain service not instanced")

	// to json
	dIn, err := json.Marshal(domainsIn)
	assert.Nil(err, "Domain test data corrupted")

	// call service
	cs.On("Get", "/v1/dns/domains").Return(dIn, 200, nil)
	domainsOut, err := ds.GetDomainList()
	assert.Nil(err, "Error getting domain list")
	assert.Equal(*domainsIn, domainsOut, "GetDomainList returned different domains")
}
<|endoftext|>


<commit_before>package routes

import (
	"encoding/json"
	"github.com/igm/pubsub"
	"gopkg.in/igm/sockjs-go.v2/sockjs"
	"log"
)

var pub pubsub.Publisher

type Message struct {
	UserName string `json:"username"`
	PhotoID  int64  `json:"photoID"`
	Type     string `json:"type"`
}

func sendMessage(msg *Message) {
	pub.Publish(msg)
}

func messageHandler(session sockjs.Session) {
	go func() {
		reader, _ := pub.SubChannel(nil)
		for {
			select {
			case msg, ok := <-reader:
				if !ok {
					log.Println("channel closed")
					return
				}
				msg = msg.(*Message)
				if body, err := json.Marshal(msg); err == nil {
					log.Println("message:", string(body))
					if err = session.Send(string(body)); err != nil {
						log.Println(err)
						return
					}
				}
			}
		}
	}()
	for {
		if msg, err := session.Recv(); err == nil {
			pub.Publish(msg)
			continue
		}
		break
	}
}
<commit_msg>Code tidyup<commit_after>package routes

import (
	"encoding/json"
	"github.com/igm/pubsub"
	"gopkg.in/igm/sockjs-go.v2/sockjs"
	"log"
)

var pub pubsub.Publisher

type Message struct {
	UserName string `json:"username"`
	PhotoID  int64  `json:"photoID"`
	Type     string `json:"type"`
}

func sendMessage(msg *Message) {
	pub.Publish(msg)
}

func messageHandler(session sockjs.Session) {
	go func() {
		reader, _ := pub.SubChannel(nil)
		for {
			select {
			case msg, ok := <-reader:
				if !ok {
					log.Println("channel closed")
					return
				}
				msg = msg.(*Message)
				if body, err := json.Marshal(msg); err == nil {
					log.Println("message:", string(body))
					if err = session.Send(string(body)); err != nil {
						log.Println(err)
						return
					}
				}
			}
		}
	}()
}
<|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.84"
<commit_msg>functions: 0.3.85 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.85"
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"bufio"
	"net"
	"os"
)

func main() {
	var sock *net.TCPConn
	if s, ret := net.Dial("tcp", "localhost:6667"); ret != nil {
		fmt.Println("Error: " + ret.String())
		return
	} else {
		sock, _ = s.(*net.TCPConn)
	}

	io := bufio.NewReadWriter(bufio.NewReader(sock),
		bufio.NewWriter(sock))
	for {
		var err os.Error
		var in string
		if in, err = io.ReadString('\n'); err != nil {
			fmt.Println("Error! " + err.String())
			return
		}
		if _, err = io.WriteString(in); err != nil {
			fmt.Println("Error! " + err.String())
			return
		}
		io.Flush()
		fmt.Print(in)
	}
}
<commit_msg>removed test.go<commit_after><|endoftext|>


<commit_before>package version

// Version of Functions
var Version = "0.3.273"
<commit_msg>fnserver: 0.3.274 release [skip ci]<commit_after>package version

// Version of Functions
var Version = "0.3.274"
<|endoftext|>


<commit_before>package v1

import (
	"database/sql"

	"git.zxq.co/ripple/rippleapi/common"
)

type friendData struct {
	userData
	IsMutual bool `json:"is_mutual"`
}

type friendsGETResponse struct {
	common.ResponseBase
	Friends []friendData `json:"friends"`
}

// FriendsGET is the API request handler for GET /friends.
// It retrieves an user's friends, and whether the friendship is mutual or not.
func FriendsGET(md common.MethodData) common.CodeMessager {
	var myFrienders []int
	myFriendersRaw, err := md.DB.Query("SELECT user1 FROM users_relationships WHERE user2 = ?", md.ID())
	if err != nil {
		md.Err(err)
		return Err500
	}
	defer myFriendersRaw.Close()
	for myFriendersRaw.Next() {
		var i int
		err := myFriendersRaw.Scan(&i)
		if err != nil {
			md.Err(err)
			continue
		}
		myFrienders = append(myFrienders, i)
	}
	if err := myFriendersRaw.Err(); err != nil {
		md.Err(err)
	}

	// Yes.
	const myFriendsQuery = `
SELECT
	users.id, users.username, users.register_datetime, users.privileges, users.latest_activity,

	users_stats.username_aka,
	users_stats.country
FROM users_relationships
LEFT JOIN users
ON users_relationships.user2 = users.id
LEFT JOIN users_stats
ON users_relationships.user2=users_stats.id
WHERE users_relationships.user1=?
ORDER BY users_relationships.id`

	results, err := md.DB.Query(myFriendsQuery+common.Paginate(md.Query("p"), md.Query("l"), 50), md.ID())
	if err != nil {
		md.Err(err)
		return Err500
	}

	var myFriends []friendData

	defer results.Close()
	for results.Next() {
		newFriend := friendPuts(md, results)
		for _, uid := range myFrienders {
			if uid == newFriend.ID {
				newFriend.IsMutual = true
				break
			}
		}
		myFriends = append(myFriends, newFriend)
	}
	if err := results.Err(); err != nil {
		md.Err(err)
	}

	r := friendsGETResponse{}
	r.Code = 200
	r.Friends = myFriends
	return r
}

func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) {
	var err error

	err = row.Scan(&user.ID, &user.Username, &user.RegisteredOn, &user.Privileges, &user.LatestActivity, &user.UsernameAKA, &user.Country)
	if err != nil {
		md.Err(err)
		return
	}

	return
}

type friendsWithResponse struct {
	common.ResponseBase
	Friends bool `json:"friend"`
	Mutual  bool `json:"mutual"`
}

// FriendsWithGET checks the current user is friends with the one passed in the request path.
func FriendsWithGET(md common.MethodData) common.CodeMessager {
	var r friendsWithResponse
	r.Code = 200
	uid := common.Int(md.Query("id"))
	if uid == 0 {
		return r
	}
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ? LIMIT 1), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ? LIMIT 1)", md.ID(), uid, md.ID(), uid).Scan(&r.Friends, &r.Mutual)
	if err != sql.ErrNoRows && err != nil {
		md.Err(err)
		return Err500
	}
	if !r.Friends {
		r.Mutual = false
	}
	return r
}

// FriendsAddGET is the GET version of FriendsAddPOST.
func FriendsAddGET(md common.MethodData) common.CodeMessager {
	return addFriend(md, common.Int(md.Query("id")))
}

func addFriend(md common.MethodData, u int) common.CodeMessager {
	if md.ID() == u {
		return common.SimpleResponse(406, "Just so you know: you can't add yourself to your friends.")
	}
	if !userExists(md, u) {
		return common.SimpleResponse(404, "I'd also like to be friends with someone who doesn't even exist (???), however that's NOT POSSIBLE.")
	}
	var (
		relExists bool
		isMutual  bool
	)
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ?), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ?)", md.ID(), u, md.ID(), u).Scan(&relExists, &isMutual)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
		return Err500
	}
	if !relExists {
		_, err := md.DB.Exec("INSERT INTO users_relationships(user1, user2) VALUES (?, ?)", md.User.UserID, u)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}
	var r friendsWithResponse
	r.Code = 200
	r.Friends = true
	r.Mutual = isMutual
	return r
}

// userExists makes sure an user exists.
func userExists(md common.MethodData, u int) (r bool) {
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ? AND "+
		md.User.OnlyUserPublic(true)+")", u).Scan(&r)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
	}
	return
}

// FriendsDelGET is the GET version of FriendDelPOST.
func FriendsDelGET(md common.MethodData) common.CodeMessager {
	return delFriend(md, common.Int(md.Query("id")))
}

func delFriend(md common.MethodData, u int) common.CodeMessager {
	_, err := md.DB.Exec("DELETE FROM users_relationships WHERE user1 = ? AND user2 = ?", md.ID(), u)
	if err != nil {
		md.Err(err)
		return Err500
	}
	r := friendsWithResponse{
		Friends: false,
		Mutual:  false,
	}
	r.Code = 200
	return r
}
<commit_msg>Add sorting stuff to GET v1/friends<commit_after>package v1

import (
	"database/sql"

	"git.zxq.co/ripple/rippleapi/common"
)

type friendData struct {
	userData
	IsMutual bool `json:"is_mutual"`
}

type friendsGETResponse struct {
	common.ResponseBase
	Friends []friendData `json:"friends"`
}

// FriendsGET is the API request handler for GET /friends.
// It retrieves an user's friends, and whether the friendship is mutual or not.
func FriendsGET(md common.MethodData) common.CodeMessager {
	var myFrienders []int
	myFriendersRaw, err := md.DB.Query("SELECT user1 FROM users_relationships WHERE user2 = ?", md.ID())
	if err != nil {
		md.Err(err)
		return Err500
	}
	defer myFriendersRaw.Close()
	for myFriendersRaw.Next() {
		var i int
		err := myFriendersRaw.Scan(&i)
		if err != nil {
			md.Err(err)
			continue
		}
		myFrienders = append(myFrienders, i)
	}
	if err := myFriendersRaw.Err(); err != nil {
		md.Err(err)
	}

	myFriendsQuery := `
SELECT
	users.id, users.username, users.register_datetime, users.privileges, users.latest_activity,

	users_stats.username_aka,
	users_stats.country
FROM users_relationships
LEFT JOIN users
ON users_relationships.user2 = users.id
LEFT JOIN users_stats
ON users_relationships.user2=users_stats.id
WHERE users_relationships.user1=?
`

	myFriendsQuery += common.Sort(md, common.SortConfiguration{
		Allowed: []string{
			"id",
			"username",
			"latest_activity",
		},
		Default: "users.id asc",
		Table:   "users",
	}) + "\n"

	results, err := md.DB.Query(myFriendsQuery+common.Paginate(md.Query("p"), md.Query("l"), 100), md.ID())
	if err != nil {
		md.Err(err)
		return Err500
	}

	var myFriends []friendData

	defer results.Close()
	for results.Next() {
		newFriend := friendPuts(md, results)
		for _, uid := range myFrienders {
			if uid == newFriend.ID {
				newFriend.IsMutual = true
				break
			}
		}
		myFriends = append(myFriends, newFriend)
	}
	if err := results.Err(); err != nil {
		md.Err(err)
	}

	r := friendsGETResponse{}
	r.Code = 200
	r.Friends = myFriends
	return r
}

func friendPuts(md common.MethodData, row *sql.Rows) (user friendData) {
	var err error

	err = row.Scan(&user.ID, &user.Username, &user.RegisteredOn, &user.Privileges, &user.LatestActivity, &user.UsernameAKA, &user.Country)
	if err != nil {
		md.Err(err)
		return
	}

	return
}

type friendsWithResponse struct {
	common.ResponseBase
	Friends bool `json:"friend"`
	Mutual  bool `json:"mutual"`
}

// FriendsWithGET checks the current user is friends with the one passed in the request path.
func FriendsWithGET(md common.MethodData) common.CodeMessager {
	var r friendsWithResponse
	r.Code = 200
	uid := common.Int(md.Query("id"))
	if uid == 0 {
		return r
	}
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ? LIMIT 1), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ? LIMIT 1)", md.ID(), uid, md.ID(), uid).Scan(&r.Friends, &r.Mutual)
	if err != sql.ErrNoRows && err != nil {
		md.Err(err)
		return Err500
	}
	if !r.Friends {
		r.Mutual = false
	}
	return r
}

// FriendsAddGET is the GET version of FriendsAddPOST.
func FriendsAddGET(md common.MethodData) common.CodeMessager {
	return addFriend(md, common.Int(md.Query("id")))
}

func addFriend(md common.MethodData, u int) common.CodeMessager {
	if md.ID() == u {
		return common.SimpleResponse(406, "Just so you know: you can't add yourself to your friends.")
	}
	if !userExists(md, u) {
		return common.SimpleResponse(404, "I'd also like to be friends with someone who doesn't even exist (???), however that's NOT POSSIBLE.")
	}
	var (
		relExists bool
		isMutual  bool
	)
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users_relationships WHERE user1 = ? AND user2 = ?), EXISTS(SELECT 1 FROM users_relationships WHERE user2 = ? AND user1 = ?)", md.ID(), u, md.ID(), u).Scan(&relExists, &isMutual)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
		return Err500
	}
	if !relExists {
		_, err := md.DB.Exec("INSERT INTO users_relationships(user1, user2) VALUES (?, ?)", md.User.UserID, u)
		if err != nil {
			md.Err(err)
			return Err500
		}
	}
	var r friendsWithResponse
	r.Code = 200
	r.Friends = true
	r.Mutual = isMutual
	return r
}

// userExists makes sure an user exists.
func userExists(md common.MethodData, u int) (r bool) {
	err := md.DB.QueryRow("SELECT EXISTS(SELECT 1 FROM users WHERE id = ? AND "+
		md.User.OnlyUserPublic(true)+")", u).Scan(&r)
	if err != nil && err != sql.ErrNoRows {
		md.Err(err)
	}
	return
}

// FriendsDelGET is the GET version of FriendDelPOST.
func FriendsDelGET(md common.MethodData) common.CodeMessager {
	return delFriend(md, common.Int(md.Query("id")))
}

func delFriend(md common.MethodData, u int) common.CodeMessager {
	_, err := md.DB.Exec("DELETE FROM users_relationships WHERE user1 = ? AND user2 = ?", md.ID(), u)
	if err != nil {
		md.Err(err)
		return Err500
	}
	r := friendsWithResponse{
		Friends: false,
		Mutual:  false,
	}
	r.Code = 200
	return r
}
<|endoftext|>


<commit_before>// +build OMIT

package main

import "fmt"

func f(left, right chan int) {
	left <- 1 + <-right
}

func main() {
	const n = 100000
	leftmost := make(chan int)
	right := leftmost
	left := leftmost
	for i := 0; i < n; i++ {
		right = make(chan int)
		go f(left, right)
		left = right
	}
	go func(c chan int) { c <- 1 }(right)
	fmt.Println(<-leftmost)
}
<commit_msg>go.talks/2013/concurrency: decrease length of the daisy-chain<commit_after>// +build OMIT

package main

import "fmt"

func f(left, right chan int) {
	left <- 1 + <-right
}

func main() {
	const n = 10000
	leftmost := make(chan int)
	right := leftmost
	left := leftmost
	for i := 0; i < n; i++ {
		right = make(chan int)
		go f(left, right)
		left = right
	}
	go func(c chan int) { c <- 1 }(right)
	fmt.Println(<-leftmost)
}
<|endoftext|>


<commit_before>package sdk

/*
   Copyright 2016 Alexander I.Grafov <grafov@gmail.com>

   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.

   ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"encoding/json"
	"fmt"
)

// GetAllDatasources loads all datasources.
// It reflects GET /api/datasources API call.
func (r *Client) GetAllDatasources() ([]Datasource, error) {
	var (
		raw  []byte
		ds   []Datasource
		code int
		err  error
	)
	if raw, code, err = r.get("api/datasources", nil); err != nil {
		return nil, err
	}
	if code != 200 {
		return nil, fmt.Errorf("HTTP error %d: returns %s", code, raw)
	}
	err = json.Unmarshal(raw, &ds)
	return ds, err
}

// GetDatasource gets an datasource by ID.
// It reflects GET /api/datasources/:datasourceId API call.
func (r *Client) GetDatasource(id uint) (Datasource, error) {
	var (
		raw  []byte
		ds   Datasource
		code int
		err  error
	)
	if raw, code, err = r.get(fmt.Sprintf("api/datasources/%d", id), nil); err != nil {
		return ds, err
	}
	if code != 200 {
		return ds, fmt.Errorf("HTTP error %d: returns %s", code, raw)
	}
	err = json.Unmarshal(raw, &ds)
	return ds, err
}

// CreateDatasource creates a new datasource.
// It reflects POST /api/datasources API call.
func (r *Client) CreateDatasource(ds Datasource) (StatusMessage, error) {
	var (
		raw  []byte
		resp StatusMessage
		err  error
	)
	if raw, err = json.Marshal(ds); err != nil {
		return StatusMessage{}, err
	}
	if raw, _, err = r.post("api/datasources", nil, raw); err != nil {
		return StatusMessage{}, err
	}
	if err = json.Unmarshal(raw, &resp); err != nil {
		return StatusMessage{}, err
	}
	return resp, nil
}

// UpdateDatasource updates a datasource from data passed in argument.
// It reflects PUT /api/datasources/:datasourceId API call.
func (r *Client) UpdateDatasource(ds Datasource) (StatusMessage, error) {
	var (
		raw  []byte
		resp StatusMessage
		err  error
	)
	if raw, err = json.Marshal(ds); err != nil {
		return StatusMessage{}, err
	}
	if raw, _, err = r.post(fmt.Sprintf("api/datasources/%d", ds.ID), nil, raw); err != nil {
		return StatusMessage{}, err
	}
	if err = json.Unmarshal(raw, &resp); err != nil {
		return StatusMessage{}, err
	}
	return resp, nil
}

// DeleteDatasource deletes an existing datasource by ID.
// It reflects DELETE /api/datasources/:datasourceId API call.
func (r *Client) DeleteDatasource(id uint) (StatusMessage, error) {
	var (
		raw   []byte
		reply StatusMessage
		err   error
	)
	if raw, _, err = r.delete(fmt.Sprintf("api/datasources/%d", id)); err != nil {
		return StatusMessage{}, err
	}
	err = json.Unmarshal(raw, &reply)
	return reply, err
}

// GetDatasourceTypes gets all available plugins for the datasources.
// It reflects GET /api/datasources/plugins API call.
func (r *Client) GetDatasourceTypes() (map[string]DatasourceType, error) {
	var (
		raw     []byte
		dsTypes = make(map[string]DatasourceType)
		code    int
		err     error
	)
	if raw, code, err = r.get("api/datasources/plugins", nil); err != nil {
		return nil, err
	}
	if code != 200 {
		return nil, fmt.Errorf("HTTP error %d: returns %s", code, raw)
	}
	err = json.Unmarshal(raw, &dsTypes)
	return dsTypes, err
}
<commit_msg>The api/datasource/{id} endpoint requires a PUT not a POST (#7)<commit_after>package sdk

/*
   Copyright 2016 Alexander I.Grafov <grafov@gmail.com>

   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.

   ॐ तारे तुत्तारे तुरे स्व
*/

import (
	"encoding/json"
	"fmt"
)

// GetAllDatasources loads all datasources.
// It reflects GET /api/datasources API call.
func (r *Client) GetAllDatasources() ([]Datasource, error) {
	var (
		raw  []byte
		ds   []Datasource
		code int
		err  error
	)
	if raw, code, err = r.get("api/datasources", nil); err != nil {
		return nil, err
	}
	if code != 200 {
		return nil, fmt.Errorf("HTTP error %d: returns %s", code, raw)
	}
	err = json.Unmarshal(raw, &ds)
	return ds, err
}

// GetDatasource gets an datasource by ID.
// It reflects GET /api/datasources/:datasourceId API call.
func (r *Client) GetDatasource(id uint) (Datasource, error) {
	var (
		raw  []byte
		ds   Datasource
		code int
		err  error
	)
	if raw, code, err = r.get(fmt.Sprintf("api/datasources/%d", id), nil); err != nil {
		return ds, err
	}
	if code != 200 {
		return ds, fmt.Errorf("HTTP error %d: returns %s", code, raw)
	}
	err = json.Unmarshal(raw, &ds)
	return ds, err
}

// CreateDatasource creates a new datasource.
// It reflects POST /api/datasources API call.
func (r *Client) CreateDatasource(ds Datasource) (StatusMessage, error) {
	var (
		raw  []byte
		resp StatusMessage
		err  error
	)
	if raw, err = json.Marshal(ds); err != nil {
		return StatusMessage{}, err
	}
	if raw, _, err = r.post("api/datasources", nil, raw); err != nil {
		return StatusMessage{}, err
	}
	if err = json.Unmarshal(raw, &resp); err != nil {
		return StatusMessage{}, err
	}
	return resp, nil
}

// UpdateDatasource updates a datasource from data passed in argument.
// It reflects PUT /api/datasources/:datasourceId API call.
func (r *Client) UpdateDatasource(ds Datasource) (StatusMessage, error) {
	var (
		raw  []byte
		resp StatusMessage
		err  error
	)
	if raw, err = json.Marshal(ds); err != nil {
		return StatusMessage{}, err
	}
	if raw, _, err = r.put(fmt.Sprintf("api/datasources/%d", ds.ID), nil, raw); err != nil {
		return StatusMessage{}, err
	}
	if err = json.Unmarshal(raw, &resp); err != nil {
		return StatusMessage{}, err
	}
	return resp, nil
}

// DeleteDatasource deletes an existing datasource by ID.
// It reflects DELETE /api/datasources/:datasourceId API call.
func (r *Client) DeleteDatasource(id uint) (StatusMessage, error) {
	var (
		raw   []byte
		reply StatusMessage
		err   error
	)
	if raw, _, err = r.delete(fmt.Sprintf("api/datasources/%d", id)); err != nil {
		return StatusMessage{}, err
	}
	err = json.Unmarshal(raw, &reply)
	return reply, err
}

// GetDatasourceTypes gets all available plugins for the datasources.
// It reflects GET /api/datasources/plugins API call.
func (r *Client) GetDatasourceTypes() (map[string]DatasourceType, error) {
	var (
		raw     []byte
		dsTypes = make(map[string]DatasourceType)
		code    int
		err     error
	)
	if raw, code, err = r.get("api/datasources/plugins", nil); err != nil {
		return nil, err
	}
	if code != 200 {
		return nil, fmt.Errorf("HTTP error %d: returns %s", code, raw)
	}
	err = json.Unmarshal(raw, &dsTypes)
	return dsTypes, err
}
<|endoftext|>


<commit_before>//
// Written by Michael Mattioli
//

package main

import (
    "fmt"
)

// It takes one (1) minute to travel from one stop to another, there are eight (8) hours in a work
// day and sixty (60) minutes in an hour which totals four-hundred and eighty (480) minutes and,
// therefore, four-hundred and eighty (480) trips made in a work day.
const MaxTrips int = 480

// A BusDriver has a daily route composed of MaxTrips stops and a collection of gossip which can be
// shared with other BusDrivers.
type BusDriver struct {
    DailyRoute []int
    Gossips []*BusDriver
}

// NewBusDriver creates and initializes a new BusDriver. The BusDriver's route for the entire day is
// filled out and starts with one (1) gossip to share.
func NewBusDriver(r ...int) *BusDriver {
    var bd BusDriver
    for t := 0; t < MaxTrips; t++ {
        for s := range r {
            bd.DailyRoute = append(bd.DailyRoute, r[s])
        }
    }
    bd.Gossips = append(bd.Gossips, &bd)
    return &bd
}

// ExchangeGossip shares unknown gossip between two BusDrivers.
func ExchangeGossip(bd1, bd2 *BusDriver) {
    giveGossip := func(src, dst *BusDriver) {
        for sg := range src.Gossips {
            var known bool
            for dg := range dst.Gossips {
                if src.Gossips[sg] == dst.Gossips[dg] {
                    known = true
                }
            }
            if !known {
                dst.Gossips = append(dst.Gossips, src.Gossips[sg])
            }
        }
    }
    giveGossip(bd1, bd2)
    giveGossip(bd2, bd1)
}

// AllGossipExchanged determines if all of the BusDrivers have shared their gossip with each other.
func AllGossipExchanged(bds ...*BusDriver) bool {
    for i := 0; i < len(bds) - 1; i++ {
        // If a BusDriver only has 1 gossip then hasn't receeived anything.
        if len(bds[i].Gossips) == 1 {
            return false
        }
        // If any two BusDriver's gossips are not of equal length then everyone has not shared
        // their gossip with everyone.
        if len(bds[i].Gossips) != len(bds[i + 1].Gossips) {
            return false
        }
    }
    return true
}

// BusDriverGossipExchange calculates the number of stops each BusDriver must make before they all
// have shared each other's gossip.
func BusDriverGossipExchange(r ...[]int) int {

    var drvs []*BusDriver
    for br := range r {
        drvs = append(drvs, NewBusDriver(r[br]...))
    }

    for t := 0; t < MaxTrips; t++ {
        for src := range drvs {
            for dst := range drvs {
                switch {
                // They're the same BusDriver.
                case drvs[src] == drvs[dst]:
                    continue
                // They're both at the same bus stop.
                case drvs[src].DailyRoute[t] == drvs[dst].DailyRoute[t]:
                    ExchangeGossip(drvs[src], drvs[dst])
                }
            }
        }
        if AllGossipExchanged(drvs...) {
            return t
        }
    }

    return -1

}

func main() {

    tests := [][][]int{
        [][]int {
            []int{3, 1, 2, 3},
            []int{3, 2, 3, 1},
            []int{4, 2, 3, 4, 5},
        },
        [][]int{
            []int{2, 1, 2},
            []int{5, 2, 8},
        },
        [][]int{
            []int{7, 11, 2, 2, 4, 8, 2, 2},
            []int{3, 0, 11, 8},
            []int{5, 11, 8, 10, 3, 11},
            []int{5, 9, 2, 5, 0, 3},
            []int{7, 4, 8, 2, 8, 1, 0, 5},
            []int{3, 6, 8, 9},
            []int{4, 2, 11, 3, 3},
        },
        [][]int {
            []int{12, 23, 15, 2, 8, 20, 21, 3, 23, 3, 27, 20, 0},
            []int{21, 14, 8, 20, 10, 0, 23, 3, 24, 23, 0, 19, 14, 12, 10, 9, 12, 12, 11, 6, 27, 5},
            []int{8, 18, 27, 10, 11, 22, 29, 23, 14},
            []int{13, 7, 14, 1, 9, 14, 16, 12, 0, 10, 13, 19, 16, 17},
            []int{24, 25, 21, 4, 6, 19, 1, 3, 26, 11, 22, 28, 14, 14, 27, 7, 20, 8, 7, 4, 1, 8, 10, 18, 21},
            []int{13, 20, 26, 22, 6, 5, 6, 23, 26, 2, 21, 16, 26, 24},
            []int{6, 7, 17, 2, 22, 23, 21},
            []int{23, 14, 22, 28, 10, 23, 7, 21, 3, 20, 24, 23, 8, 8, 21, 13, 15, 6, 9, 17, 27, 17, 13, 14},
            []int{23, 13, 1, 15, 5, 16, 7, 26, 22, 29, 17, 3, 14, 16, 16, 18, 6, 10, 3, 14, 10, 17, 27, 25},
            []int{25, 28, 5, 21, 8, 10, 27, 21, 23, 28, 7, 20, 6, 6, 9, 29, 27, 26, 24, 3, 12, 10, 21, 10, 12, 17},
            []int{26, 22, 26, 13, 10, 19, 3, 15, 2, 3, 25, 29, 25, 19, 19, 24, 1, 26, 22, 10, 17, 19, 28, 11, 22, 2, 13},
            []int{8, 4, 25, 15, 20, 9, 11, 3, 19},
            []int{24, 29, 4, 17, 2, 0, 8, 19, 11, 28, 13, 4, 16, 5, 15, 25, 16, 5, 6, 1, 0, 19, 7, 4, 6},
            []int{16, 25, 15, 17, 20, 27, 1, 11, 1, 18, 14, 23, 27, 25, 26, 17, 1},
        },
    }

    for t := range tests {
        fmt.Println(BusDriverGossipExchange(tests[t]...))
    }

}
<commit_msg>Optimizations to challenge 264<commit_after>//
// Written by Michael Mattioli
//

package main

import (
    "fmt"
)

// It takes one (1) minute to travel from one stop to another, there are eight (8) hours in a work
// day and sixty (60) minutes in an hour which totals four-hundred and eighty (480) minutes and,
// therefore, four-hundred and eighty (480) trips made in a work day.
const MaxTrips int = 480

// A BusDriver has a daily route composed of MaxTrips stops and a collection of gossip which can be
// shared with other BusDrivers.
type BusDriver struct {
    DailyRoute []int
    Gossips []*BusDriver
}

// NewBusDriver creates and initializes a new BusDriver. The BusDriver's route for the entire day is
// filled out and starts with one (1) gossip to share.
func NewBusDriver(r ...int) *BusDriver {

    var bd BusDriver

    // Initial gossip.
    bd.Gossips = append(bd.Gossips, &bd)

    // Determine daily route.
    var t int
    for {
        for s := range r {
            if t == MaxTrips {
                return &bd
            }
            bd.DailyRoute = append(bd.DailyRoute, r[s])
            t++
        }
    }

}

// ExchangeGossip shares unknown gossip between two BusDrivers.
func ExchangeGossip(bd1, bd2 *BusDriver) {
    giveGossip := func(src, dst *BusDriver) {
        for sg := range src.Gossips {
            var known bool
            for dg := range dst.Gossips {
                if src.Gossips[sg] == dst.Gossips[dg] {
                    known = true
                }
            }
            if !known {
                dst.Gossips = append(dst.Gossips, src.Gossips[sg])
            }
        }
    }
    giveGossip(bd1, bd2)
    giveGossip(bd2, bd1)
}

// AllGossipExchanged determines if all of the BusDrivers have shared their gossip with each other.
func AllGossipExchanged(bds ...*BusDriver) bool {
    for i := 0; i < len(bds) - 1; i++ {
        switch {
        // If a BusDriver only has 1 gossip then hasn't receeived anything.
        case len(bds[i].Gossips) == 1:
            return false
        // If any two BusDriver's gossips are not of equal length then everyone has not shared their
        // gossip with everyone.
        case len(bds[i].Gossips) != len(bds[i + 1].Gossips):
            return false
        }
    }
    return true
}

// BusDriverGossipExchange calculates the number of stops each BusDriver must make before they all
// have shared each other's gossip. Returns -1 if all of the BusDrivers have not shared and heard
// all of the gossip there is to share and hear by the end of their routes.
func BusDriverGossipExchange(r ...[]int) int {

    var drvs []*BusDriver

    for br := range r {
        drvs = append(drvs, NewBusDriver(r[br]...))
    }

    for t := 0; t < MaxTrips; t++ {
        for src := range drvs {
            for dst := range drvs {
                switch {
                // Dont't exchange gossip with the same BusDriver.
                case drvs[src] == drvs[dst]:
                    continue
                // Two different BusDrivers at the same bus stop.
                case drvs[src].DailyRoute[t] == drvs[dst].DailyRoute[t]:
                    ExchangeGossip(drvs[src], drvs[dst])
                }
            }
        }
        if AllGossipExchanged(drvs...) {
            return t
        }
    }

    return -1

}

func main() {

    tests := [][][]int{
        [][]int {
            []int{3, 1, 2, 3},
            []int{3, 2, 3, 1},
            []int{4, 2, 3, 4, 5},
        },
        [][]int{
            []int{2, 1, 2},
            []int{5, 2, 8},
        },
        [][]int{
            []int{7, 11, 2, 2, 4, 8, 2, 2},
            []int{3, 0, 11, 8},
            []int{5, 11, 8, 10, 3, 11},
            []int{5, 9, 2, 5, 0, 3},
            []int{7, 4, 8, 2, 8, 1, 0, 5},
            []int{3, 6, 8, 9},
            []int{4, 2, 11, 3, 3},
        },
        [][]int {
            []int{12, 23, 15, 2, 8, 20, 21, 3, 23, 3, 27, 20, 0},
            []int{21, 14, 8, 20, 10, 0, 23, 3, 24, 23, 0, 19, 14, 12, 10, 9, 12, 12, 11, 6, 27, 5},
            []int{8, 18, 27, 10, 11, 22, 29, 23, 14},
            []int{13, 7, 14, 1, 9, 14, 16, 12, 0, 10, 13, 19, 16, 17},
            []int{24, 25, 21, 4, 6, 19, 1, 3, 26, 11, 22, 28, 14, 14, 27, 7, 20, 8, 7, 4, 1, 8, 10, 18, 21},
            []int{13, 20, 26, 22, 6, 5, 6, 23, 26, 2, 21, 16, 26, 24},
            []int{6, 7, 17, 2, 22, 23, 21},
            []int{23, 14, 22, 28, 10, 23, 7, 21, 3, 20, 24, 23, 8, 8, 21, 13, 15, 6, 9, 17, 27, 17, 13, 14},
            []int{23, 13, 1, 15, 5, 16, 7, 26, 22, 29, 17, 3, 14, 16, 16, 18, 6, 10, 3, 14, 10, 17, 27, 25},
            []int{25, 28, 5, 21, 8, 10, 27, 21, 23, 28, 7, 20, 6, 6, 9, 29, 27, 26, 24, 3, 12, 10, 21, 10, 12, 17},
            []int{26, 22, 26, 13, 10, 19, 3, 15, 2, 3, 25, 29, 25, 19, 19, 24, 1, 26, 22, 10, 17, 19, 28, 11, 22, 2, 13},
            []int{8, 4, 25, 15, 20, 9, 11, 3, 19},
            []int{24, 29, 4, 17, 2, 0, 8, 19, 11, 28, 13, 4, 16, 5, 15, 25, 16, 5, 6, 1, 0, 19, 7, 4, 6},
            []int{16, 25, 15, 17, 20, 27, 1, 11, 1, 18, 14, 23, 27, 25, 26, 17, 1},
        },
    }

    for t := range tests {
        fmt.Println(BusDriverGossipExchange(tests[t]...))
    }

}
<|endoftext|>


<commit_before>// Package pop3 implements the Post Office Protocol version 3.
package pop3

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
)

// MessageInfoはメッセージ番号、メッセージサイズ、およびユニークIDを持つ型です。
// ListAllまたはUidlAllメソッドの戻り値として使用されます。
// ListAllメソッドにて取得した場合は、メッセージ番号とメッセージサイズ、
// UidlAllメソッドにて取得した場合は、メッセージ番号とユニークIDのみに値が入っています。
type MessageInfo struct {
	Number int
	Size   uint64
	Uid    string
}

// ClientはPOPサーバーへの接続を表すクライアントです。
type Client struct {
	// TextはClientによって使用されるmail2.Connです。
	Text *Conn
	// keep a reference to the connection so it can be used to create a TLS
	// connection later
	conn net.Conn
}

// Dialは指定されたアドレスのPOPサーバーに接続された新規Clientを返します。
// アドレスはポート番号を含まなくてはなりません。
func Dial(addr string) (*Client, error) {
	conn, err := net.Dial("tcp", addr)

	if err != nil {
		return nil, err
	}

	return NewClient(conn)
}

// NewClient既存のコネクションを使用した新規Clientを返します。
func NewClient(conn net.Conn) (*Client, error) {
	text := NewConn(conn)

	_, err := text.ReadResponse()

	if err != nil {
		return nil, err
	}

	return &Client{Text: text, conn: conn}, nil
}

// Userは指定されたユーザを使用してサーバーに対してUSERコマンドを発行します。
func (c *Client) User(user string) error {
	return c.cmdSimple("USER %s", user)
}

// Passは指定されたパスワードを使用してサーバーに対してPASSコマンドを発行します。
func (c *Client) Pass(pass string) error {
	return c.cmdSimple("PASS %s", pass)
}

// Statはサーバーに対してSTARコマンドを発行します。
// サーバーに保存されているメール数、合計メールサイズおよびエラーが返ります。
func (c *Client) Stat() (int, uint64, error) {
	return c.cmdStatOrList("STAT", "STAT")
}

// Retrは指定されたメール番号を使用してサーバーに対してRetrコマンドを発行します。
// サーバーに保存されているメール数、合計メールサイズが返ります。
func (c *Client) Retr(number int) (string, error) {
	var err error

	err = c.Text.WriteLine("RETR %d", number)

	if err != nil {
		return "", err
	}

	_, err = c.Text.ReadResponse()

	if err != nil {
		return "", err
	}

	return c.Text.ReadToPeriod()
}

// Listは指定されたメール番号を使用してサーバーに対してLISTコマンドを発行します。
// 指定されたメール番号が存在する場合は、メール番号とサイズが返ります。
func (c *Client) List(number int) (int, uint64, error) {
	return c.cmdStatOrList("LIST", "LIST %d", number)
}

// ListAllはサーバーに対してLISTコマンドを発行します。
// 存在するメール件数分のMessageInfoが返ります。
func (c *Client) ListAll() ([]MessageInfo, error) {
	list := make([]MessageInfo, 0)

	err := c.cmdReadLines("LIST", func(line string) error {
		number, size, err := c.convertNumberAndSize(line)

		if err != nil {
			return err
		}

		list = append(list, MessageInfo{Number: number, Size: size})

		return nil
	})

	if err != nil {
		return nil, ResponseError(fmt.Sprintf("LISTコマンドのレスポンスが不正です。: %s", err.Error()))
	}

	return list, nil
}

// Uidlは指定されたメール番号を使用してサーバーに対してUIDLコマンドを発行します。
// 指定されたメール番号が存在する場合は、メール番号とユニークIDが返ります。
func (c *Client) Uidl(number int) (int, string, error) {
	var err error

	err = c.Text.WriteLine("UIDL %d", number)

	if err != nil {
		return 0, "", err
	}

	var msg string

	msg, err = c.Text.ReadResponse()

	if err != nil {
		return 0, "", err
	}

	var val int
	var uid string

	val, uid, err = c.convertNumberAndUid(msg)

	if err != nil {
		return 0, "", ResponseError(fmt.Sprintf("UIDLコマンドのレスポンスが不正です。: %s", err.Error()))
	}

	return val, uid, nil
}

// UidlAllはサーバーに対してUIDLコマンドを発行します。
// 存在するメール件数分のMessageInfoが返ります。
func (c *Client) UidlAll() ([]MessageInfo, error) {
	list := make([]MessageInfo, 0)

	err := c.cmdReadLines("UIDL", func(line string) error {
		number, uid, err := c.convertNumberAndUid(line)

		if err != nil {
			return err
		}

		list = append(list, MessageInfo{Number: number, Uid: uid})

		return nil
	})

	if err != nil {
		return nil, ResponseError(fmt.Sprintf("UIDLコマンドのレスポンスが不正です。: %s", err.Error()))
	}

	return list, nil
}

// Deleは指定されたメール番号を使用してサーバーに対してDELEコマンドを発行します。
func (c *Client) Dele(number int) error {
	return c.cmdSimple("DELE %d", number)
}

// Noopはサーバーに対してNOOPコマンドを発行します。
func (c *Client) Noop() error {
	return c.cmdSimple("NOOP")
}

// Rsetはサーバーに対してRSETコマンドを発行します。
func (c *Client) Rset() error {
	return c.cmdSimple("RSET")
}

// Quitはサーバーに対してQUITコマンドを発行します。
func (c *Client) Quit() error {
	return c.cmdSimple("QUIT")
}

func (c *Client) cmdSimple(format string, args ...interface{}) error {
	var err error

	err = c.Text.WriteLine(format, args...)

	if err != nil {
		return err
	}

	_, err = c.Text.ReadResponse()

	if err != nil {
		return err
	}

	return nil
}

func (c *Client) cmdStatOrList(name, format string, args ...interface{}) (int, uint64, error) {
	var err error

	err = c.Text.WriteLine(format, args...)

	if err != nil {
		return 0, 0, err
	}

	var msg string

	msg, err = c.Text.ReadResponse()

	if err != nil {
		return 0, 0, err
	}

	s := strings.Split(msg, " ")

	if len(s) < 2 {
		return 0, 0, ResponseError(fmt.Sprintf("%sコマンドのレスポンスが不正です。: %s", name, msg))
	}

	var val int
	var size uint64

	val, size, err = c.convertNumberAndSize(msg)

	if err != nil {
		return 0, 0, ResponseError(fmt.Sprintf("%sコマンドのレスポンスが不正です。: %s", name, err.Error()))
	}

	return val, size, nil
}

func (c *Client) cmdReadLines(cmnd string, lineFn lineFunc) error {
	var err error

	err = c.Text.WriteLine(cmnd)

	if err != nil {
		return err
	}

	_, err = c.Text.ReadResponse()

	if err != nil {
		return err
	}

	var lines []string

	lines, err = c.Text.ReadLines()

	if err != nil {
		return err
	}

	for _, line := range lines {
		err = lineFn(line)

		if err != nil {
			return err
		}
	}

	return nil
}

type lineFunc func(line string) error

func (c *Client) Close() error {
	return c.Text.Close()
}

func (c *Client) convertNumberAndSize(line string) (int, uint64, error) {
	var err error

	s := strings.Split(line, " ")

	if len(s) < 2 {
		return 0, 0, errors.New(fmt.Sprintf("分割後の配列数が2未満です。: %s", line))
	}

	var val int
	var size uint64

	if val, err = strconv.Atoi(s[0]); err != nil {
		return 0, 0, errors.New(fmt.Sprintf("配列要素[0]をint型に変換できません。: %s", line))
	}

	if size, err = strconv.ParseUint(s[1], 10, 64); err != nil {
		return 0, 0, errors.New(fmt.Sprintf("配列要素[1]をuint64型に変換できません。: %s", line))
	}

	return val, size, nil
}

func (c *Client) convertNumberAndUid(line string) (int, string, error) {
	var err error

	s := strings.Split(line, " ")

	if len(s) < 2 {
		return 0, "", errors.New(fmt.Sprintf("分割後の配列数が2未満です。: %s", line))
	}

	var val int

	if val, err = strconv.Atoi(s[0]); err != nil {
		return 0, "", errors.New(fmt.Sprintf("配列要素[0]をint型に変換できません。: %s", line))
	}

	return val, s[1], nil
}
<commit_msg>エラーメッセージを英語に変更。<commit_after>// Package pop3 implements the Post Office Protocol version 3.
package pop3

import (
	"errors"
	"fmt"
	"net"
	"strconv"
	"strings"
)

// MessageInfoはメッセージ番号、メッセージサイズ、およびユニークIDを持つ型です。
// ListAllまたはUidlAllメソッドの戻り値として使用されます。
// ListAllメソッドにて取得した場合は、メッセージ番号とメッセージサイズ、
// UidlAllメソッドにて取得した場合は、メッセージ番号とユニークIDのみに値が入っています。
type MessageInfo struct {
	Number int
	Size   uint64
	Uid    string
}

// ClientはPOPサーバーへの接続を表すクライアントです。
type Client struct {
	// TextはClientによって使用されるmail2.Connです。
	Text *Conn
	// keep a reference to the connection so it can be used to create a TLS
	// connection later
	conn net.Conn
}

// Dialは指定されたアドレスのPOPサーバーに接続された新規Clientを返します。
// アドレスはポート番号を含まなくてはなりません。
func Dial(addr string) (*Client, error) {
	conn, err := net.Dial("tcp", addr)

	if err != nil {
		return nil, err
	}

	return NewClient(conn)
}

// NewClient既存のコネクションを使用した新規Clientを返します。
func NewClient(conn net.Conn) (*Client, error) {
	text := NewConn(conn)

	_, err := text.ReadResponse()

	if err != nil {
		return nil, err
	}

	return &Client{Text: text, conn: conn}, nil
}

// Userは指定されたユーザを使用してサーバーに対してUSERコマンドを発行します。
func (c *Client) User(user string) error {
	return c.cmdSimple("USER %s", user)
}

// Passは指定されたパスワードを使用してサーバーに対してPASSコマンドを発行します。
func (c *Client) Pass(pass string) error {
	return c.cmdSimple("PASS %s", pass)
}

// Statはサーバーに対してSTARコマンドを発行します。
// サーバーに保存されているメール数、合計メールサイズおよびエラーが返ります。
func (c *Client) Stat() (int, uint64, error) {
	return c.cmdStatOrList("STAT", "STAT")
}

// Retrは指定されたメール番号を使用してサーバーに対してRetrコマンドを発行します。
// サーバーに保存されているメール数、合計メールサイズが返ります。
func (c *Client) Retr(number int) (string, error) {
	var err error

	err = c.Text.WriteLine("RETR %d", number)

	if err != nil {
		return "", err
	}

	_, err = c.Text.ReadResponse()

	if err != nil {
		return "", err
	}

	return c.Text.ReadToPeriod()
}

// Listは指定されたメール番号を使用してサーバーに対してLISTコマンドを発行します。
// 指定されたメール番号が存在する場合は、メール番号とサイズが返ります。
func (c *Client) List(number int) (int, uint64, error) {
	return c.cmdStatOrList("LIST", "LIST %d", number)
}

// ListAllはサーバーに対してLISTコマンドを発行します。
// 存在するメール件数分のMessageInfoが返ります。
func (c *Client) ListAll() ([]MessageInfo, error) {
	list := make([]MessageInfo, 0)

	err := c.cmdReadLines("LIST", func(line string) error {
		number, size, err := c.convertNumberAndSize(line)

		if err != nil {
			return err
		}

		list = append(list, MessageInfo{Number: number, Size: size})

		return nil
	})

	if err != nil {
		return nil, err
	}

	return list, nil
}

// Uidlは指定されたメール番号を使用してサーバーに対してUIDLコマンドを発行します。
// 指定されたメール番号が存在する場合は、メール番号とユニークIDが返ります。
func (c *Client) Uidl(number int) (int, string, error) {
	var err error

	err = c.Text.WriteLine("UIDL %d", number)

	if err != nil {
		return 0, "", err
	}

	var msg string

	msg, err = c.Text.ReadResponse()

	if err != nil {
		return 0, "", err
	}

	var val int
	var uid string

	val, uid, err = c.convertNumberAndUid(msg)

	if err != nil {
		return 0, "", err
	}

	return val, uid, nil
}

// UidlAllはサーバーに対してUIDLコマンドを発行します。
// 存在するメール件数分のMessageInfoが返ります。
func (c *Client) UidlAll() ([]MessageInfo, error) {
	list := make([]MessageInfo, 0)

	err := c.cmdReadLines("UIDL", func(line string) error {
		number, uid, err := c.convertNumberAndUid(line)

		if err != nil {
			return err
		}

		list = append(list, MessageInfo{Number: number, Uid: uid})

		return nil
	})

	if err != nil {
		return nil, err
	}

	return list, nil
}

// Deleは指定されたメール番号を使用してサーバーに対してDELEコマンドを発行します。
func (c *Client) Dele(number int) error {
	return c.cmdSimple("DELE %d", number)
}

// Noopはサーバーに対してNOOPコマンドを発行します。
func (c *Client) Noop() error {
	return c.cmdSimple("NOOP")
}

// Rsetはサーバーに対してRSETコマンドを発行します。
func (c *Client) Rset() error {
	return c.cmdSimple("RSET")
}

// Quitはサーバーに対してQUITコマンドを発行します。
func (c *Client) Quit() error {
	return c.cmdSimple("QUIT")
}

func (c *Client) cmdSimple(format string, args ...interface{}) error {
	var err error

	err = c.Text.WriteLine(format, args...)

	if err != nil {
		return err
	}

	_, err = c.Text.ReadResponse()

	if err != nil {
		return err
	}

	return nil
}

func (c *Client) cmdStatOrList(name, format string, args ...interface{}) (int, uint64, error) {
	var err error

	err = c.Text.WriteLine(format, args...)

	if err != nil {
		return 0, 0, err
	}

	var msg string

	msg, err = c.Text.ReadResponse()

	if err != nil {
		return 0, 0, err
	}

	s := strings.Split(msg, " ")

	if len(s) < 2 {
		return 0, 0, ResponseError(fmt.Sprintf("invalid response format: %s", msg))
	}

	var val int
	var size uint64

	val, size, err = c.convertNumberAndSize(msg)

	if err != nil {
		return 0, 0, err
	}

	return val, size, nil
}

func (c *Client) cmdReadLines(cmnd string, lineFn lineFunc) error {
	var err error

	err = c.Text.WriteLine(cmnd)

	if err != nil {
		return err
	}

	_, err = c.Text.ReadResponse()

	if err != nil {
		return err
	}

	var lines []string

	lines, err = c.Text.ReadLines()

	if err != nil {
		return err
	}

	for _, line := range lines {
		err = lineFn(line)

		if err != nil {
			return err
		}
	}

	return nil
}

type lineFunc func(line string) error

func (c *Client) Close() error {
	return c.Text.Close()
}

func (c *Client) convertNumberAndSize(line string) (int, uint64, error) {
	var err error

	s := strings.Split(line, " ")

	if len(s) < 2 {
		return 0, 0, errors.New(fmt.Sprintf("the length of the array is less than 2: %s", line))
	}

	var val int
	var size uint64

	if val, err = strconv.Atoi(s[0]); err != nil {
		return 0, 0, errors.New(fmt.Sprintf("can not convert element[0] to int type: %s", line))
	}

	if size, err = strconv.ParseUint(s[1], 10, 64); err != nil {
		return 0, 0, errors.New(fmt.Sprintf("can not convert element[1] to uint64 type: %s", line))
	}

	return val, size, nil
}

func (c *Client) convertNumberAndUid(line string) (int, string, error) {
	var err error

	s := strings.Split(line, " ")

	if len(s) < 2 {
		return 0, "", errors.New(fmt.Sprintf("the length of the array is less than 2: %s", line))
	}

	var val int

	if val, err = strconv.Atoi(s[0]); err != nil {
		return 0, "", errors.New(fmt.Sprintf("can not convert element[0] to int type: %s", line))
	}

	return val, s[1], nil
}
<|endoftext|>


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

package main

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/appc/spec/schema/types"
	"github.com/coreos/rkt/common"
	"github.com/coreos/rkt/pkg/label"
	"github.com/coreos/rkt/pkg/lock"
	"github.com/coreos/rkt/pkg/pod"
	"github.com/coreos/rkt/pkg/user"
	"github.com/coreos/rkt/stage0"
	"github.com/coreos/rkt/store/imagestore"
	"github.com/coreos/rkt/store/treestore"
	"github.com/spf13/cobra"
)

var (
	cmdAppSandbox = &cobra.Command{
		Use:   "sandbox",
		Short: "Create an empty pod application sandbox",
		Long:  "Initializes an empty pod having no applications.",
		Run:   runWrapper(runAppSandbox),
	}
	flagAppPorts    appPortList
	flagAnnotations kvMap
	flagLabels      kvMap
)

func init() {
	cmdApp.AddCommand(cmdAppSandbox)

	addStage1ImageFlags(cmdAppSandbox.Flags())
	cmdAppSandbox.Flags().StringVar(&flagUUIDFileSave, "uuid-file-save", "", "write out pod UUID to specified file")
	cmdAppSandbox.Flags().Var(&flagNet, "net", "configure the pod's networking. Optionally, pass a list of user-configured networks to load and set arguments to pass to each network, respectively. Syntax: --net[=n[:args], ...]")
	cmdAppSandbox.Flags().BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem")
	cmdAppSandbox.Flags().Var(&flagDNS, "dns", "name servers to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().Var(&flagDNSSearch, "dns-search", "DNS search domains to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().Var(&flagDNSOpt, "dns-opt", "DNS options to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().StringVar(&flagHostname, "hostname", "", `pod's hostname. If empty, it will be "rkt-$PODUUID"`)
	cmdAppSandbox.Flags().Var(&flagAppPorts, "port", "ports to forward. format: \"name:proto:podPort:hostIP:hostPort\"")

	flagAppPorts = appPortList{}
	cmdAppSandbox.Flags().Var(&flagAnnotations, "user-annotation", "optional, set the pod's annotations in the form of key=value")
	cmdAppSandbox.Flags().Var(&flagLabels, "user-label", "optional, set the pod's label in the form of key=value")
}

func runAppSandbox(cmd *cobra.Command, args []string) int {
	s, err := imagestore.NewStore(storeDir())
	if err != nil {
		stderr.PrintE("cannot open store", err)
		return 1
	}

	ts, err := treestore.NewStore(treeStoreDir(), s)
	if err != nil {
		stderr.PrintE("cannot open treestore", err)
		return 1
	}

	config, err := getConfig()
	if err != nil {
		stderr.PrintE("cannot get configuration", err)
		return 1
	}

	s1img, err := getStage1Hash(s, ts, config)
	if err != nil {
		stderr.Error(err)
		return 1
	}

	p, err := pod.NewPod(getDataDir())
	if err != nil {
		stderr.PrintE("error creating new pod", err)
		return 1
	}

	if flagUUIDFileSave != "" {
		if err := pod.WriteUUIDToFile(p.UUID, flagUUIDFileSave); err != nil {
			stderr.PrintE("error saving pod UUID to file", err)
			return 1
		}
	}

	processLabel, mountLabel, err := label.InitLabels("/var/run/rkt/mcs", []string{})
	if err != nil {
		stderr.PrintE("error initialising SELinux", err)
		return 1
	}

	p.MountLabel = mountLabel
	cfg := stage0.CommonConfig{
		MountLabel:   mountLabel,
		ProcessLabel: processLabel,
		Store:        s,
		TreeStore:    ts,
		Stage1Image:  *s1img,
		UUID:         p.UUID,
		Debug:        globalFlags.Debug,
		Mutable:      true,
	}

	ovlOk := true
	if err := common.PathSupportsOverlay(getDataDir()); err != nil {
		if oerr, ok := err.(common.ErrOverlayUnsupported); ok {
			stderr.Printf("disabling overlay support: %q", oerr.Error())
			ovlOk = false
		} else {
			stderr.PrintE("error determining overlay support", err)
			return 1
		}
	}

	useOverlay := !flagNoOverlay && ovlOk

	pcfg := stage0.PrepareConfig{
		CommonConfig:       &cfg,
		UseOverlay:         useOverlay,
		PrivateUsers:       user.NewBlankUidRange(),
		SkipTreeStoreCheck: globalFlags.InsecureFlags.SkipOnDiskCheck(),
		Apps:               &rktApps,
		Ports:              []types.ExposedPort(flagAppPorts),
		UserAnnotations:    parseAnnotations(&flagAnnotations),
		UserLabels:         parseLabels(&flagLabels),
	}

	if globalFlags.Debug {
		stage0.InitDebug()
	}

	keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock)
	if err != nil {
		stderr.PrintE("cannot get shared prepare lock", err)
		return 1
	}

	err = stage0.Prepare(pcfg, p.Path(), p.UUID)
	if err != nil {
		stderr.PrintE("error setting up stage0", err)
		keyLock.Close()
		return 1
	}
	keyLock.Close()

	// get the lock fd for run
	lfd, err := p.Fd()
	if err != nil {
		stderr.PrintE("error getting pod lock fd", err)
		return 1
	}

	// skip prepared by jumping directly to run, we own this pod
	if err := p.ToRun(); err != nil {
		stderr.PrintE("unable to transition to run", err)
		return 1
	}

	rktgid, err := common.LookupGid(common.RktGroup)
	if err != nil {
		stderr.Printf("group %q not found, will use default gid when rendering images", common.RktGroup)
		rktgid = -1
	}

	DNSConfMode, DNSConfig, HostsEntries, err := parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt, flagDNSDomain)
	if err != nil {
		stderr.PrintE("error with dns flags", err)
		return 1
	}

	rcfg := stage0.RunConfig{
		CommonConfig:         &cfg,
		Net:                  flagNet,
		LockFd:               lfd,
		Interactive:          true,
		DNSConfMode:          DNSConfMode,
		DNSConfig:            DNSConfig,
		MDSRegister:          false,
		LocalConfig:          globalFlags.LocalConfigDir,
		RktGid:               rktgid,
		Hostname:             flagHostname,
		InsecureCapabilities: globalFlags.InsecureFlags.SkipCapabilities(),
		InsecurePaths:        globalFlags.InsecureFlags.SkipPaths(),
		InsecureSeccomp:      globalFlags.InsecureFlags.SkipSeccomp(),
		UseOverlay:           useOverlay,
		HostsEntries:         *HostsEntries,
	}

	_, manifest, err := p.PodManifest()
	if err != nil {
		stderr.PrintE("cannot get the pod manifest", err)
		return 1
	}
	rcfg.Apps = manifest.Apps
	stage0.Run(rcfg, p.Path(), getDataDir()) // execs, never returns

	return 1
}

/*
 * The sandbox uses a different style of port forwarding - instead of mapping
 * from port to app (via name), we just map ports directly.
 *
 * The format is name:proto:podPort:hostIP:hostPort
 * e.g. http:tcp:8080:0.0.0.0:80
 */
type appPortList []types.ExposedPort

func (apl *appPortList) Set(s string) error {
	parts := strings.SplitN(s, ":", 5)
	if len(parts) != 5 {
		return fmt.Errorf("--port invalid format")
	}

	// parsey parsey
	name, err := types.NewACName(parts[0])
	if err != nil {
		return err
	}

	proto := parts[1]
	switch proto {
	case "tcp", "udp":
	default:
		return fmt.Errorf("invalid protocol %q", proto)
	}

	p, err := strconv.ParseUint(parts[2], 10, 16)
	if err != nil {
		return err
	}
	podPortNo := uint(p)

	ip := net.ParseIP(parts[3])
	if ip == nil {
		return fmt.Errorf("could not parse IP %q", ip)
	}

	p, err = strconv.ParseUint(parts[4], 10, 16)
	if err != nil {
		return err
	}
	hostPortNo := uint(p)

	podSide := types.Port{
		Name:            *name,
		Protocol:        proto,
		Port:            podPortNo,
		Count:           1,
		SocketActivated: false,
	}

	hostSide := types.ExposedPort{
		Name:     *name,
		HostPort: hostPortNo,
		HostIP:   ip,
		PodPort:  &podSide,
	}

	*apl = append(*apl, hostSide)
	return nil
}

func (apl *appPortList) String() string {
	ss := make([]string, 0, len(*apl))
	for _, p := range *apl {
		ss = append(ss, fmt.Sprintf("%s:%s:%d:%s:%d",
			p.Name, p.PodPort.Protocol, p.PodPort.Port,
			p.HostIP, p.HostPort))

	}
	return strings.Join(ss, ",")
}

func (apl *appPortList) Type() string {
	return "appPortList"
}

// parseAnnotations converts the annotations set by '--user-annotation' flag,
// and returns types.UserAnnotations.
func parseAnnotations(flagAnnotations *kvMap) types.UserAnnotations {
	if flagAnnotations.IsEmpty() {
		return nil
	}
	annotations := make(types.UserAnnotations)
	for k, v := range flagAnnotations.mapping {
		annotations[k] = v
	}
	return annotations
}

// parseLabels converts the labels set by '--user-label' flag,
// and returns types.UserLabels.
func parseLabels(flagLabels *kvMap) types.UserLabels {
	if flagLabels.IsEmpty() {
		return nil
	}
	labels := make(types.UserLabels)
	for k, v := range flagLabels.mapping {
		labels[k] = v
	}
	return labels
}
<commit_msg>sandbox add missing flagDNSDomain and flagHostsEntries parameters<commit_after>// Copyright 2016 The rkt Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"net"
	"strconv"
	"strings"

	"github.com/appc/spec/schema/types"
	"github.com/coreos/rkt/common"
	"github.com/coreos/rkt/pkg/label"
	"github.com/coreos/rkt/pkg/lock"
	"github.com/coreos/rkt/pkg/pod"
	"github.com/coreos/rkt/pkg/user"
	"github.com/coreos/rkt/stage0"
	"github.com/coreos/rkt/store/imagestore"
	"github.com/coreos/rkt/store/treestore"
	"github.com/spf13/cobra"
)

var (
	cmdAppSandbox = &cobra.Command{
		Use:   "sandbox",
		Short: "Create an empty pod application sandbox",
		Long:  "Initializes an empty pod having no applications.",
		Run:   runWrapper(runAppSandbox),
	}
	flagAppPorts    appPortList
	flagAnnotations kvMap
	flagLabels      kvMap
)

func init() {
	cmdApp.AddCommand(cmdAppSandbox)

	addStage1ImageFlags(cmdAppSandbox.Flags())
	cmdAppSandbox.Flags().StringVar(&flagUUIDFileSave, "uuid-file-save", "", "write out pod UUID to specified file")
	cmdAppSandbox.Flags().Var(&flagNet, "net", "configure the pod's networking. Optionally, pass a list of user-configured networks to load and set arguments to pass to each network, respectively. Syntax: --net[=n[:args], ...]")
	cmdAppSandbox.Flags().BoolVar(&flagNoOverlay, "no-overlay", false, "disable overlay filesystem")
	cmdAppSandbox.Flags().Var(&flagDNS, "dns", "name servers to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().Var(&flagDNSSearch, "dns-search", "DNS search domains to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().Var(&flagDNSOpt, "dns-opt", "DNS options to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().StringVar(&flagDNSDomain, "dns-domain", "", "DNS domain to write in /etc/resolv.conf")
	cmdAppSandbox.Flags().Var(&flagHostsEntries, "hosts-entry", "Entries to add to the pod-wide /etc/hosts. Pass 'host' to use the host's /etc/hosts")
	cmdAppSandbox.Flags().StringVar(&flagHostname, "hostname", "", `pod's hostname. If empty, it will be "rkt-$PODUUID"`)
	cmdAppSandbox.Flags().Var(&flagAppPorts, "port", "ports to forward. format: \"name:proto:podPort:hostIP:hostPort\"")

	flagAppPorts = appPortList{}
	cmdAppSandbox.Flags().Var(&flagAnnotations, "user-annotation", "optional, set the pod's annotations in the form of key=value")
	cmdAppSandbox.Flags().Var(&flagLabels, "user-label", "optional, set the pod's label in the form of key=value")
}

func runAppSandbox(cmd *cobra.Command, args []string) int {
	s, err := imagestore.NewStore(storeDir())
	if err != nil {
		stderr.PrintE("cannot open store", err)
		return 1
	}

	ts, err := treestore.NewStore(treeStoreDir(), s)
	if err != nil {
		stderr.PrintE("cannot open treestore", err)
		return 1
	}

	config, err := getConfig()
	if err != nil {
		stderr.PrintE("cannot get configuration", err)
		return 1
	}

	s1img, err := getStage1Hash(s, ts, config)
	if err != nil {
		stderr.Error(err)
		return 1
	}

	p, err := pod.NewPod(getDataDir())
	if err != nil {
		stderr.PrintE("error creating new pod", err)
		return 1
	}

	if flagUUIDFileSave != "" {
		if err := pod.WriteUUIDToFile(p.UUID, flagUUIDFileSave); err != nil {
			stderr.PrintE("error saving pod UUID to file", err)
			return 1
		}
	}

	processLabel, mountLabel, err := label.InitLabels("/var/run/rkt/mcs", []string{})
	if err != nil {
		stderr.PrintE("error initialising SELinux", err)
		return 1
	}

	p.MountLabel = mountLabel
	cfg := stage0.CommonConfig{
		MountLabel:   mountLabel,
		ProcessLabel: processLabel,
		Store:        s,
		TreeStore:    ts,
		Stage1Image:  *s1img,
		UUID:         p.UUID,
		Debug:        globalFlags.Debug,
		Mutable:      true,
	}

	ovlOk := true
	if err := common.PathSupportsOverlay(getDataDir()); err != nil {
		if oerr, ok := err.(common.ErrOverlayUnsupported); ok {
			stderr.Printf("disabling overlay support: %q", oerr.Error())
			ovlOk = false
		} else {
			stderr.PrintE("error determining overlay support", err)
			return 1
		}
	}

	useOverlay := !flagNoOverlay && ovlOk

	pcfg := stage0.PrepareConfig{
		CommonConfig:       &cfg,
		UseOverlay:         useOverlay,
		PrivateUsers:       user.NewBlankUidRange(),
		SkipTreeStoreCheck: globalFlags.InsecureFlags.SkipOnDiskCheck(),
		Apps:               &rktApps,
		Ports:              []types.ExposedPort(flagAppPorts),
		UserAnnotations:    parseAnnotations(&flagAnnotations),
		UserLabels:         parseLabels(&flagLabels),
	}

	if globalFlags.Debug {
		stage0.InitDebug()
	}

	keyLock, err := lock.SharedKeyLock(lockDir(), common.PrepareLock)
	if err != nil {
		stderr.PrintE("cannot get shared prepare lock", err)
		return 1
	}

	err = stage0.Prepare(pcfg, p.Path(), p.UUID)
	if err != nil {
		stderr.PrintE("error setting up stage0", err)
		keyLock.Close()
		return 1
	}
	keyLock.Close()

	// get the lock fd for run
	lfd, err := p.Fd()
	if err != nil {
		stderr.PrintE("error getting pod lock fd", err)
		return 1
	}

	// skip prepared by jumping directly to run, we own this pod
	if err := p.ToRun(); err != nil {
		stderr.PrintE("unable to transition to run", err)
		return 1
	}

	rktgid, err := common.LookupGid(common.RktGroup)
	if err != nil {
		stderr.Printf("group %q not found, will use default gid when rendering images", common.RktGroup)
		rktgid = -1
	}

	DNSConfMode, DNSConfig, HostsEntries, err := parseDNSFlags(flagHostsEntries, flagDNS, flagDNSSearch, flagDNSOpt, flagDNSDomain)
	if err != nil {
		stderr.PrintE("error with dns flags", err)
		return 1
	}

	rcfg := stage0.RunConfig{
		CommonConfig:         &cfg,
		Net:                  flagNet,
		LockFd:               lfd,
		Interactive:          true,
		DNSConfMode:          DNSConfMode,
		DNSConfig:            DNSConfig,
		MDSRegister:          false,
		LocalConfig:          globalFlags.LocalConfigDir,
		RktGid:               rktgid,
		Hostname:             flagHostname,
		InsecureCapabilities: globalFlags.InsecureFlags.SkipCapabilities(),
		InsecurePaths:        globalFlags.InsecureFlags.SkipPaths(),
		InsecureSeccomp:      globalFlags.InsecureFlags.SkipSeccomp(),
		UseOverlay:           useOverlay,
		HostsEntries:         *HostsEntries,
	}

	_, manifest, err := p.PodManifest()
	if err != nil {
		stderr.PrintE("cannot get the pod manifest", err)
		return 1
	}
	rcfg.Apps = manifest.Apps
	stage0.Run(rcfg, p.Path(), getDataDir()) // execs, never returns

	return 1
}

/*
 * The sandbox uses a different style of port forwarding - instead of mapping
 * from port to app (via name), we just map ports directly.
 *
 * The format is name:proto:podPort:hostIP:hostPort
 * e.g. http:tcp:8080:0.0.0.0:80
 */
type appPortList []types.ExposedPort

func (apl *appPortList) Set(s string) error {
	parts := strings.SplitN(s, ":", 5)
	if len(parts) != 5 {
		return fmt.Errorf("--port invalid format")
	}

	// parsey parsey
	name, err := types.NewACName(parts[0])
	if err != nil {
		return err
	}

	proto := parts[1]
	switch proto {
	case "tcp", "udp":
	default:
		return fmt.Errorf("invalid protocol %q", proto)
	}

	p, err := strconv.ParseUint(parts[2], 10, 16)
	if err != nil {
		return err
	}
	podPortNo := uint(p)

	ip := net.ParseIP(parts[3])
	if ip == nil {
		return fmt.Errorf("could not parse IP %q", ip)
	}

	p, err = strconv.ParseUint(parts[4], 10, 16)
	if err != nil {
		return err
	}
	hostPortNo := uint(p)

	podSide := types.Port{
		Name:            *name,
		Protocol:        proto,
		Port:            podPortNo,
		Count:           1,
		SocketActivated: false,
	}

	hostSide := types.ExposedPort{
		Name:     *name,
		HostPort: hostPortNo,
		HostIP:   ip,
		PodPort:  &podSide,
	}

	*apl = append(*apl, hostSide)
	return nil
}

func (apl *appPortList) String() string {
	ss := make([]string, 0, len(*apl))
	for _, p := range *apl {
		ss = append(ss, fmt.Sprintf("%s:%s:%d:%s:%d",
			p.Name, p.PodPort.Protocol, p.PodPort.Port,
			p.HostIP, p.HostPort))

	}
	return strings.Join(ss, ",")
}

func (apl *appPortList) Type() string {
	return "appPortList"
}

// parseAnnotations converts the annotations set by '--user-annotation' flag,
// and returns types.UserAnnotations.
func parseAnnotations(flagAnnotations *kvMap) types.UserAnnotations {
	if flagAnnotations.IsEmpty() {
		return nil
	}
	annotations := make(types.UserAnnotations)
	for k, v := range flagAnnotations.mapping {
		annotations[k] = v
	}
	return annotations
}

// parseLabels converts the labels set by '--user-label' flag,
// and returns types.UserLabels.
func parseLabels(flagLabels *kvMap) types.UserLabels {
	if flagLabels.IsEmpty() {
		return nil
	}
	labels := make(types.UserLabels)
	for k, v := range flagLabels.mapping {
		labels[k] = v
	}
	return labels
}
<|endoftext|>


<commit_before>package charm

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"launchpad.net/juju-core/log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// InfoResponse is sent by the charm store in response to charm-info requests.
type InfoResponse struct {
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Sha256   string   `json:"sha256,omitempty"`
	Digest   string   `json:"digest,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

// Repository respresents a collection of charms.
type Repository interface {
	Get(curl *URL) (Charm, error)
	Latest(curl *URL) (int, error)
}

// store is a Repository that talks to the juju charm server (in ../store).
type store struct {
	baseURL   string
	cachePath string
}

const (
	storeURL  = "https://store.juju.ubuntu.com"
	cachePath = "$JUJU_HOME/cache"
	// cachePath = "$HOME/.juju/cache"
)

// Store returns a Repository that provides access to the juju charm store.
func Store() Repository {
	return &store{storeURL, os.ExpandEnv(cachePath)}
}

// info returns the revision and SHA256 digest of the charm referenced by curl.
func (s *store) info(curl *URL) (rev int, digest string, err error) {
	key := curl.String()
	resp, err := http.Get(s.baseURL + "/charm-info?charms=" + url.QueryEscape(key))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	infos := make(map[string]*InfoResponse)
	if err = json.Unmarshal(body, &infos); err != nil {
		return
	}
	info, found := infos[key]
	if !found {
		err = fmt.Errorf("charm: charm store returned response without charm %q", key)
		return
	}
	for _, w := range info.Warnings {
		log.Warningf("charm: charm store reports for %q: %s", key, w)
	}
	if info.Errors != nil {
		err = fmt.Errorf(
			"charm info errors for %q: %s", key, strings.Join(info.Errors, "; "),
		)
		return
	}
	return info.Revision, info.Sha256, nil
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (s *store) Latest(curl *URL) (int, error) {
	rev, _, err := s.info(curl.WithRevision(-1))
	return rev, err
}

// verify returns an error unless a file exists at path with a hex-encoded
// SHA256 matching digest.
func verify(path, digest string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		return err
	}
	if hex.EncodeToString(h.Sum(nil)) != digest {
		return fmt.Errorf("bad SHA256 of %q", path)
	}
	return nil
}

// Get returns the charm referenced by curl.
func (s *store) Get(curl *URL) (Charm, error) {
	if err := os.MkdirAll(s.cachePath, 0755); err != nil {
		return nil, err
	}
	rev, digest, err := s.info(curl)
	if err != nil {
		return nil, err
	}
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("charm: store returned charm with wrong revision for %q", curl.String())
	}
	path := filepath.Join(s.cachePath, Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		resp, err := http.Get(s.baseURL + "/charm/" + url.QueryEscape(curl.Path()))
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(s.cachePath, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := os.Rename(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return ReadBundle(path)
}

// LocalRepository represents a local directory containing subdirectories
// named after an Ubuntu series, each of which contains charms targeted for
// that series. For example:
//
//   /path/to/repository/oneiric/mongodb/
//   /path/to/repository/precise/mongodb.charm
//   /path/to/repository/precise/wordpress/
type LocalRepository struct {
	Path string
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (r *LocalRepository) Latest(curl *URL) (int, error) {
	ch, err := r.Get(curl.WithRevision(-1))
	if err != nil {
		return 0, err
	}
	return ch.Revision(), nil
}

func repoNotFound(path string) error {
	return fmt.Errorf("no repository found at %q", path)
}

func charmNotFound(curl *URL) error {
	return fmt.Errorf("no charms found matching %q", curl)
}

func mightBeCharm(info os.FileInfo) bool {
	if info.IsDir() {
		return !strings.HasPrefix(info.Name(), ".")
	}
	return strings.HasSuffix(info.Name(), ".charm")
}

// Get returns a charm matching curl, if one exists. If curl has a revision of
// -1, it returns the latest charm that matches curl. If multiple candidates
// satisfy the foregoing, the first one encountered will be returned.
func (r *LocalRepository) Get(curl *URL) (Charm, error) {
	if curl.Schema != "local" {
		return nil, fmt.Errorf("local repository got URL with non-local schema: %q", curl)
	}
	info, err := os.Stat(r.Path)
	if err != nil {
		if os.IsNotExist(err) {
			err = repoNotFound(r.Path)
		}
		return nil, err
	}
	if !info.IsDir() {
		return nil, repoNotFound(r.Path)
	}
	path := filepath.Join(r.Path, curl.Series)
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, charmNotFound(curl)
	}
	var latest Charm
	for _, info := range infos {
		if !mightBeCharm(info) {
			continue
		}
		chPath := filepath.Join(path, info.Name())
		if ch, err := Read(chPath); err != nil {
			log.Warningf("charm: failed to load charm at %q: %s", chPath, err)
		} else if ch.Meta().Name == curl.Name {
			if ch.Revision() == curl.Revision {
				return ch, nil
			}
			if latest == nil || ch.Revision() > latest.Revision() {
				latest = ch
			}
		}
	}
	if curl.Revision == -1 && latest != nil {
		return latest, nil
	}
	return nil, charmNotFound(curl)
}
<commit_msg>config: merged again after reverted revno 1031<commit_after>package charm

import (
	"crypto/sha256"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"launchpad.net/juju-core/log"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"
)

// InfoResponse is sent by the charm store in response to charm-info requests.
type InfoResponse struct {
	Revision int      `json:"revision"` // Zero is valid. Can't omitempty.
	Sha256   string   `json:"sha256,omitempty"`
	Digest   string   `json:"digest,omitempty"`
	Errors   []string `json:"errors,omitempty"`
	Warnings []string `json:"warnings,omitempty"`
}

// Repository respresents a collection of charms.
type Repository interface {
	Get(curl *URL) (Charm, error)
	Latest(curl *URL) (int, error)
}

// store is a Repository that talks to the juju charm server (in ../store).
type store struct {
	baseURL   string
	cachePath string
}

const (
	storeURL  = "https://store.juju.ubuntu.com"
	cachePath = "$JUJU_HOME/cache"
)

// Store returns a Repository that provides access to the juju charm store.
func Store() Repository {
	return &store{storeURL, os.ExpandEnv(cachePath)}
}

// info returns the revision and SHA256 digest of the charm referenced by curl.
func (s *store) info(curl *URL) (rev int, digest string, err error) {
	key := curl.String()
	resp, err := http.Get(s.baseURL + "/charm-info?charms=" + url.QueryEscape(key))
	if err != nil {
		return
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}
	infos := make(map[string]*InfoResponse)
	if err = json.Unmarshal(body, &infos); err != nil {
		return
	}
	info, found := infos[key]
	if !found {
		err = fmt.Errorf("charm: charm store returned response without charm %q", key)
		return
	}
	for _, w := range info.Warnings {
		log.Warningf("charm: charm store reports for %q: %s", key, w)
	}
	if info.Errors != nil {
		err = fmt.Errorf(
			"charm info errors for %q: %s", key, strings.Join(info.Errors, "; "),
		)
		return
	}
	return info.Revision, info.Sha256, nil
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (s *store) Latest(curl *URL) (int, error) {
	rev, _, err := s.info(curl.WithRevision(-1))
	return rev, err
}

// verify returns an error unless a file exists at path with a hex-encoded
// SHA256 matching digest.
func verify(path, digest string) error {
	f, err := os.Open(path)
	if err != nil {
		return err
	}
	defer f.Close()
	h := sha256.New()
	if _, err := io.Copy(h, f); err != nil {
		return err
	}
	if hex.EncodeToString(h.Sum(nil)) != digest {
		return fmt.Errorf("bad SHA256 of %q", path)
	}
	return nil
}

// Get returns the charm referenced by curl.
func (s *store) Get(curl *URL) (Charm, error) {
	if err := os.MkdirAll(s.cachePath, 0755); err != nil {
		return nil, err
	}
	rev, digest, err := s.info(curl)
	if err != nil {
		return nil, err
	}
	if curl.Revision == -1 {
		curl = curl.WithRevision(rev)
	} else if curl.Revision != rev {
		return nil, fmt.Errorf("charm: store returned charm with wrong revision for %q", curl.String())
	}
	path := filepath.Join(s.cachePath, Quote(curl.String())+".charm")
	if verify(path, digest) != nil {
		resp, err := http.Get(s.baseURL + "/charm/" + url.QueryEscape(curl.Path()))
		if err != nil {
			return nil, err
		}
		defer resp.Body.Close()
		f, err := ioutil.TempFile(s.cachePath, "charm-download")
		if err != nil {
			return nil, err
		}
		dlPath := f.Name()
		_, err = io.Copy(f, resp.Body)
		if cerr := f.Close(); err == nil {
			err = cerr
		}
		if err != nil {
			os.Remove(dlPath)
			return nil, err
		}
		if err := os.Rename(dlPath, path); err != nil {
			return nil, err
		}
	}
	if err := verify(path, digest); err != nil {
		return nil, err
	}
	return ReadBundle(path)
}

// LocalRepository represents a local directory containing subdirectories
// named after an Ubuntu series, each of which contains charms targeted for
// that series. For example:
//
//   /path/to/repository/oneiric/mongodb/
//   /path/to/repository/precise/mongodb.charm
//   /path/to/repository/precise/wordpress/
type LocalRepository struct {
	Path string
}

// Latest returns the latest revision of the charm referenced by curl, regardless
// of the revision set on curl itself.
func (r *LocalRepository) Latest(curl *URL) (int, error) {
	ch, err := r.Get(curl.WithRevision(-1))
	if err != nil {
		return 0, err
	}
	return ch.Revision(), nil
}

func repoNotFound(path string) error {
	return fmt.Errorf("no repository found at %q", path)
}

func charmNotFound(curl *URL) error {
	return fmt.Errorf("no charms found matching %q", curl)
}

func mightBeCharm(info os.FileInfo) bool {
	if info.IsDir() {
		return !strings.HasPrefix(info.Name(), ".")
	}
	return strings.HasSuffix(info.Name(), ".charm")
}

// Get returns a charm matching curl, if one exists. If curl has a revision of
// -1, it returns the latest charm that matches curl. If multiple candidates
// satisfy the foregoing, the first one encountered will be returned.
func (r *LocalRepository) Get(curl *URL) (Charm, error) {
	if curl.Schema != "local" {
		return nil, fmt.Errorf("local repository got URL with non-local schema: %q", curl)
	}
	info, err := os.Stat(r.Path)
	if err != nil {
		if os.IsNotExist(err) {
			err = repoNotFound(r.Path)
		}
		return nil, err
	}
	if !info.IsDir() {
		return nil, repoNotFound(r.Path)
	}
	path := filepath.Join(r.Path, curl.Series)
	infos, err := ioutil.ReadDir(path)
	if err != nil {
		return nil, charmNotFound(curl)
	}
	var latest Charm
	for _, info := range infos {
		if !mightBeCharm(info) {
			continue
		}
		chPath := filepath.Join(path, info.Name())
		if ch, err := Read(chPath); err != nil {
			log.Warningf("charm: failed to load charm at %q: %s", chPath, err)
		} else if ch.Meta().Name == curl.Name {
			if ch.Revision() == curl.Revision {
				return ch, nil
			}
			if latest == nil || ch.Revision() > latest.Revision() {
				latest = ch
			}
		}
	}
	if curl.Revision == -1 && latest != nil {
		return latest, nil
	}
	return nil, charmNotFound(curl)
}
<|endoftext|>