<commit_before>package build

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"

	"github.com/cloud66/cxbuild/configuration"
	"github.com/cloud66/cxbuild/squash"
	"github.com/dchest/uniuri"
	"github.com/docker/docker/builder/parser"
	"github.com/fsouza/go-dockerclient"
)

// Builder is a simple Dockerfile builder
type Builder struct {
	Build    *Manifest
	UniqueID string // unique id for this build sequence. This is used for multi-tenanted environments
	Conf     *configuration.Config

	config *tls.Config
	docker docker.Client
	auth   *docker.AuthConfigurations
}

// NewBuilder creates a new builder in a new session
func NewBuilder(manifest *Manifest, conf *configuration.Config) *Builder {
	b := Builder{}
	b.Build = manifest
	b.UniqueID = conf.UniqueID
	b.Conf = conf

	certPath := b.Conf.DockerCert
	endpoint := b.Conf.DockerHost
	ca := path.Join(certPath, "ca.pem")
	cert := path.Join(certPath, "cert.pem")
	key := path.Join(certPath, "key.pem")
	client, err := docker.NewTLSClient(endpoint, cert, key, ca)
	if err != nil {
		b.Conf.Logger.Fatal(err.Error())
		return nil
	}

	b.docker = *client

	usr, err := user.Current()
	if err != nil {
		b.Conf.Logger.Fatalf("Failed to find the current user: %s", err.Error())
	}

	if _, err := os.Stat(filepath.Join(usr.HomeDir, ".dockercfg")); err == nil {
		authStream, err := os.Open(filepath.Join(usr.HomeDir, ".dockercfg"))
		if err != nil {
			b.Conf.Logger.Fatal("Unable to read .dockerconf file")
		}
		defer authStream.Close()

		auth, err := docker.NewAuthConfigurations(authStream)
		if err != nil {
			b.Conf.Logger.Fatalf("Invalid .dockerconf: %s", err.Error())
		}
		b.auth = auth
	}

	if err != nil {
		b.Conf.Logger.Fatalf("Failed to connect to Docker daemon %s", err.Error())
	}

	return &b
}

// StartBuild runs the build process end to end
func (b *Builder) StartBuild(startStep string) error {
	var steps []Step
	if startStep == "" {
		b.Conf.Logger.Notice("Starting the build chain")
		steps = b.Build.Steps
	} else {
		b.Conf.Logger.Notice("Starting the build chain from '%s'", startStep)
		for idx, s := range b.Build.Steps {
			if s.Name == startStep {
				steps = b.Build.Steps[idx:]
				break
			}
		}
	}

	for _, s := range steps {
		err := b.BuildStep(&s)
		if err != nil {
			return err
		}
	}

	// Clear after yourself: images, containers, etc (optional for premium users)
	for _, s := range b.Build.Steps {
		if s.Keep {
			continue
		}

		err := b.docker.RemoveImage(b.uniqueStepName(&s))
		if err != nil {
			return err
		}
	}
	return nil
}

func (b *Builder) uniqueStepName(step *Step) string {
	if b.UniqueID == "" {
		return step.Name
	}

	return strings.ToLower(fmt.Sprintf("%s.%s", b.UniqueID, step.Name))
}

// BuildStep builds a single step
func (b *Builder) BuildStep(step *Step) error {
	b.Conf.Logger.Notice("Building %s", step.Name)
	// fix the Dockerfile
	err := b.replaceFromField(step)
	if err != nil {
		return err
	}

	// call Docker to build the Dockerfile (from the parsed file)
	opts := docker.BuildImageOptions{
		Name:                b.uniqueStepName(step),
		Dockerfile:          filepath.Base(b.uniqueDockerfile(step)),
		NoCache:             b.Conf.NoCache,
		SuppressOutput:      b.Conf.SuppressOutput,
		RmTmpContainer:      b.Conf.RmTmpContainers,
		ForceRmTmpContainer: b.Conf.ForceRmTmpContainer,
		OutputStream:        os.Stdout, // TODO: use a multi writer to get a stream out for the API
		ContextDir:          b.Build.Workdir,
	}

	if b.auth != nil {
		opts.AuthConfigs = *b.auth
	}

	err = b.docker.BuildImage(opts)
	if err != nil {
		return err
	}

	// if there are any artefacts to be picked up, create a container and copy them over
	// we also need a container if there are cleanup commands
	if len(step.Artefacts) > 0 || len(step.Cleanup.Commands) > 0 {
		b.Conf.Logger.Notice("Building container based on the image")

		// create a container
		container, err := b.createContainer(step)
		if err != nil {
			return err
		}

		if len(step.Cleanup.Commands) > 0 {
			// start the container
			b.Conf.Logger.Notice("Starting container to run cleanup commands")
			startOpts := &docker.HostConfig{}
			err := b.docker.StartContainer(container.ID, startOpts)
			if err != nil {
				return err
			}

			for _, cmd := range step.Cleanup.Commands {
				b.Conf.Logger.Debug("Running cleanup command %s", cmd)
				// create an exec for the commands
				execOpts := docker.CreateExecOptions{
					Container:    container.ID,
					AttachStdin:  false,
					AttachStdout: true,
					AttachStderr: true,
					Tty:          false,
					Cmd:          strings.Split(cmd, " "),
				}
				execObj, err := b.docker.CreateExec(execOpts)
				if err != nil {
					return err
				}

				success := make(chan struct{})
				startExecOpts := docker.StartExecOptions{
					OutputStream: os.Stdout,
					ErrorStream:  os.Stderr,
					RawTerminal:  true,
				}

				go func() {
					if err := b.docker.StartExec(execObj.ID, startExecOpts); err != nil {
						b.Conf.Logger.Error("Failed to run cleanup commands %s", err.Error())
					}
					success <- struct{}{}
				}()
				<-success
			}

			// commit the container
			cmtOpts := docker.CommitContainerOptions{
				Container: container.ID,
			}

			b.Conf.Logger.Debug("Commiting the container into an image")
			img, err := b.docker.CommitContainer(cmtOpts)
			if err != nil {
				return err
			}

			b.Conf.Logger.Debug("Stopping the container")
			err = b.docker.StopContainer(container.ID, 0)
			if err != nil {
				return err
			}

			tmpFile, err := ioutil.TempFile("", "cxbuild-export-")
			if err != nil {
				return err
			}
			defer tmpFile.Close()
			tarWriter, err := os.Create(tmpFile.Name())
			if err != nil {
				return err
			}
			defer tarWriter.Close()
			// save the container
			expOpts := docker.ExportImageOptions{
				Name:         img.ID,
				OutputStream: tarWriter,
			}

			b.Conf.Logger.Notice("Exporting cleaned up container to %s", tmpFile.Name())
			err = b.docker.ExportImage(expOpts)
			if err != nil {
				return err
			}

			// Squash
			sqTmpFile, err := ioutil.TempFile("", "cxbuild-export-")
			if err != nil {
				return err
			}
			defer sqTmpFile.Close()
			b.Conf.Logger.Notice("Squashing the image into %s", sqTmpFile.Name())

			squasher := squash.Squasher{Conf: b.Conf}
			err = squasher.Squash(tmpFile.Name(), sqTmpFile.Name(), "squashed")
			if err != nil {
				return err
			}

			return nil

			b.Conf.Logger.Debug("Removing exported temp files")
			err = os.Remove(tmpFile.Name())
			if err != nil {
				return err
			}
			// TODO: Load
			err = os.Remove(sqTmpFile.Name())
			if err != nil {
				return err
			}
		}

		if len(step.Artefacts) > 0 {
			b.Conf.Logger.Notice("Copying artefacts")

			for _, art := range step.Artefacts {
				err = b.copyToHost(&art, container.ID)
				if err != nil {
					return err
				}
			}
		}

		// remove the created container
		removeOpts := docker.RemoveContainerOptions{
			ID:            container.ID,
			RemoveVolumes: true,
			Force:         true,
		}

		b.Conf.Logger.Debug("Removing built container '%s'", container.ID)
		err = b.docker.RemoveContainer(removeOpts)
		if err != nil {
			return err
		}
	}

	// clean up the parsed docker file. It will remain there if there was a problem
	err = os.Remove(b.uniqueDockerfile(step))
	if err != nil {
		return err
	}

	return nil
}

// this replaces the FROM field in the Dockerfile to one with the previous step's unique name
// it stores the parsed result Dockefile in uniqueSessionName file
func (b *Builder) replaceFromField(step *Step) error {
	b.Conf.Logger.Notice("Parsing and converting '%s'", step.Dockerfile)

	rwc, err := os.Open(path.Join(b.Build.Workdir, step.Dockerfile))
	if err != nil {
		return err
	}
	defer rwc.Close()

	node, err := parser.Parse(rwc)
	if err != nil {
		return err
	}

	for _, child := range node.Children {
		if child.Value == "from" {
			// found it. is it from anyone we know?
			if child.Next == nil {
				return errors.New("invalid Dockerfile. No valid FROM found")
			}

			imageName := child.Next.Value
			found, err := step.Manifest.FindStepByName(imageName)
			if err != nil {
				return err
			}

			if found != nil {
				child.Next.Value = b.uniqueStepName(found)
			}
		}
	}

	// did it have any effect?
	err = ioutil.WriteFile(b.uniqueDockerfile(step), []byte(dumpDockerfile(node)), 0644)
	if err != nil {
		return err
	}

	return nil
}

func (b *Builder) copyToHost(a *Artefact, container string) error {
	// create the dest folder if not there
	err := os.MkdirAll(a.Dest, 0777)
	if err != nil {
		return err
	}

	destFile := path.Join(b.Build.Workdir, a.Dest, filepath.Base(a.Source))
	dest, err := os.Create(destFile)
	if err != nil {
		return err
	}
	defer dest.Close()

	opt := docker.CopyFromContainerOptions{
		OutputStream: dest,
		Container:    container,
		Resource:     a.Source,
	}

	b.Conf.Logger.Info("Copying from %s to %s", a.Source, destFile)

	return b.docker.CopyFromContainer(opt)
}

func (b *Builder) createContainer(step *Step) (*docker.Container, error) {
	config := docker.Config{
		AttachStdout: true,
		AttachStdin:  false,
		AttachStderr: true,
		Image:        b.uniqueStepName(step),
		Cmd:          []string{"/bin/bash"},
		Tty:          true,
	}

	opts := docker.CreateContainerOptions{
		Name:   b.uniqueStepName(step) + "." + uniuri.New(),
		Config: &config,
	}
	container, err := b.docker.CreateContainer(opts)
	if err != nil {
		return nil, err
	}

	return container, nil
}

func dumpDockerfile(node *parser.Node) string {
	str := ""
	str += node.Value

	if len(node.Flags) > 0 {
		str += fmt.Sprintf(" %q", node.Flags)
	}

	for _, n := range node.Children {
		str += dumpDockerfile(n) + "\n"
	}

	if node.Next != nil {
		for n := node.Next; n != nil; n = n.Next {
			if len(n.Children) > 0 {
				str += " " + dumpDockerfile(n)
			} else {
				str += " " + n.Value
			}
		}
	}

	return strings.TrimSpace(str)
}

func (b *Builder) uniqueDockerfile(step *Step) string {
	return filepath.Join(b.Build.Workdir, b.uniqueStepName(step))
}
<commit_msg>full squash<commit_after>package build

import (
	"crypto/tls"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"

	"github.com/cloud66/cxbuild/configuration"
	"github.com/cloud66/cxbuild/squash"
	"github.com/dchest/uniuri"
	"github.com/docker/docker/builder/parser"
	"github.com/fsouza/go-dockerclient"
)

// Builder is a simple Dockerfile builder
type Builder struct {
	Build    *Manifest
	UniqueID string // unique id for this build sequence. This is used for multi-tenanted environments
	Conf     *configuration.Config

	config *tls.Config
	docker docker.Client
	auth   *docker.AuthConfigurations
}

// NewBuilder creates a new builder in a new session
func NewBuilder(manifest *Manifest, conf *configuration.Config) *Builder {
	b := Builder{}
	b.Build = manifest
	b.UniqueID = conf.UniqueID
	b.Conf = conf

	certPath := b.Conf.DockerCert
	endpoint := b.Conf.DockerHost
	ca := path.Join(certPath, "ca.pem")
	cert := path.Join(certPath, "cert.pem")
	key := path.Join(certPath, "key.pem")
	client, err := docker.NewTLSClient(endpoint, cert, key, ca)
	if err != nil {
		b.Conf.Logger.Fatal(err.Error())
		return nil
	}

	b.docker = *client

	usr, err := user.Current()
	if err != nil {
		b.Conf.Logger.Fatalf("Failed to find the current user: %s", err.Error())
	}

	if _, err := os.Stat(filepath.Join(usr.HomeDir, ".dockercfg")); err == nil {
		authStream, err := os.Open(filepath.Join(usr.HomeDir, ".dockercfg"))
		if err != nil {
			b.Conf.Logger.Fatal("Unable to read .dockerconf file")
		}
		defer authStream.Close()

		auth, err := docker.NewAuthConfigurations(authStream)
		if err != nil {
			b.Conf.Logger.Fatalf("Invalid .dockerconf: %s", err.Error())
		}
		b.auth = auth
	}

	if err != nil {
		b.Conf.Logger.Fatalf("Failed to connect to Docker daemon %s", err.Error())
	}

	return &b
}

// StartBuild runs the build process end to end
func (b *Builder) StartBuild(startStep string) error {
	var steps []Step
	if startStep == "" {
		b.Conf.Logger.Notice("Starting the build chain")
		steps = b.Build.Steps
	} else {
		b.Conf.Logger.Notice("Starting the build chain from '%s'", startStep)
		for idx, s := range b.Build.Steps {
			if s.Name == startStep {
				steps = b.Build.Steps[idx:]
				break
			}
		}
	}

	for _, s := range steps {
		err := b.BuildStep(&s)
		if err != nil {
			return err
		}
	}

	// Clear after yourself: images, containers, etc (optional for premium users)
	for _, s := range b.Build.Steps {
		if s.Keep {
			continue
		}

		b.Conf.Logger.Debug("Removing unwanted image %s", b.uniqueStepName(&s))
		err := b.docker.RemoveImage(b.uniqueStepName(&s))
		if err != nil {
			return err
		}
	}
	return nil
}

func (b *Builder) uniqueStepName(step *Step) string {
	if b.UniqueID == "" {
		return step.Name
	}

	return strings.ToLower(fmt.Sprintf("%s.%s", b.UniqueID, step.Name))
}

// BuildStep builds a single step
func (b *Builder) BuildStep(step *Step) error {
	b.Conf.Logger.Notice("Building %s", step.Name)
	// fix the Dockerfile
	err := b.replaceFromField(step)
	if err != nil {
		return err
	}

	// call Docker to build the Dockerfile (from the parsed file)
	opts := docker.BuildImageOptions{
		Name:                b.uniqueStepName(step),
		Dockerfile:          filepath.Base(b.uniqueDockerfile(step)),
		NoCache:             b.Conf.NoCache,
		SuppressOutput:      b.Conf.SuppressOutput,
		RmTmpContainer:      b.Conf.RmTmpContainers,
		ForceRmTmpContainer: b.Conf.ForceRmTmpContainer,
		OutputStream:        os.Stdout, // TODO: use a multi writer to get a stream out for the API
		ContextDir:          b.Build.Workdir,
	}

	if b.auth != nil {
		opts.AuthConfigs = *b.auth
	}

	err = b.docker.BuildImage(opts)
	if err != nil {
		return err
	}

	// if there are any artefacts to be picked up, create a container and copy them over
	// we also need a container if there are cleanup commands
	if len(step.Artefacts) > 0 || len(step.Cleanup.Commands) > 0 {
		b.Conf.Logger.Notice("Building container based on the image")

		// create a container
		container, err := b.createContainer(step)
		if err != nil {
			return err
		}

		if len(step.Cleanup.Commands) > 0 {
			// start the container
			b.Conf.Logger.Notice("Starting container %s to run cleanup commands", container.ID)
			startOpts := &docker.HostConfig{}
			err := b.docker.StartContainer(container.ID, startOpts)
			if err != nil {
				return err
			}

			for _, cmd := range step.Cleanup.Commands {
				b.Conf.Logger.Debug("Running cleanup command %s on %s", cmd, container.ID)
				// create an exec for the commands
				execOpts := docker.CreateExecOptions{
					Container:    container.ID,
					AttachStdin:  false,
					AttachStdout: true,
					AttachStderr: true,
					Tty:          false,
					Cmd:          strings.Split(cmd, " "),
				}
				execObj, err := b.docker.CreateExec(execOpts)
				if err != nil {
					return err
				}

				success := make(chan struct{})
				startExecOpts := docker.StartExecOptions{
					OutputStream: os.Stdout,
					ErrorStream:  os.Stderr,
					RawTerminal:  true,
				}

				go func() {
					if err := b.docker.StartExec(execObj.ID, startExecOpts); err != nil {
						b.Conf.Logger.Error("Failed to run cleanup commands %s", err.Error())
					}
					success <- struct{}{}
				}()
				<-success
			}

			// commit the container
			cmtOpts := docker.CommitContainerOptions{
				Container: container.ID,
			}

			b.Conf.Logger.Debug("Commiting the container %s", container.ID)
			img, err := b.docker.CommitContainer(cmtOpts)
			if err != nil {
				return err
			}

			b.Conf.Logger.Debug("Stopping the container %s", container.ID)
			err = b.docker.StopContainer(container.ID, 0)
			if err != nil {
				return err
			}

			tmpFile, err := ioutil.TempFile("", "cxbuild-export-")
			if err != nil {
				return err
			}
			defer tmpFile.Close()
			tarWriter, err := os.Create(tmpFile.Name())
			if err != nil {
				return err
			}
			defer tarWriter.Close()
			// save the container
			expOpts := docker.ExportImageOptions{
				Name:         img.ID,
				OutputStream: tarWriter,
			}

			b.Conf.Logger.Notice("Exporting cleaned up container %s to %s", img.ID, tmpFile.Name())
			err = b.docker.ExportImage(expOpts)
			if err != nil {
				return err
			}

			// Squash
			sqTmpFile, err := ioutil.TempFile("", "cxbuild-export-")
			if err != nil {
				return err
			}
			defer sqTmpFile.Close()
			b.Conf.Logger.Notice("Squashing image %s into %s", sqTmpFile.Name(), img.ID)

			squasher := squash.Squasher{Conf: b.Conf}
			err = squasher.Squash(tmpFile.Name(), sqTmpFile.Name(), b.uniqueStepName(step))
			if err != nil {
				return err
			}

			b.Conf.Logger.Debug("Removing exported temp files")
			err = os.Remove(tmpFile.Name())
			if err != nil {
				return err
			}
			// Load
			sqashedFile, err := os.Open(sqTmpFile.Name())
			if err != nil {
				return err
			}
			defer sqashedFile.Close()

			loadOps := docker.LoadImageOptions{
				InputStream: sqashedFile,
			}
			b.Conf.Logger.Debug("Loading squashed image into docker")
			err = b.docker.LoadImage(loadOps)
			if err != nil {
				return err
			}

			err = os.Remove(sqTmpFile.Name())
			if err != nil {
				return err
			}
		}

		if len(step.Artefacts) > 0 {
			b.Conf.Logger.Notice("Copying artefacts from %s", container.ID)

			for _, art := range step.Artefacts {
				err = b.copyToHost(&art, container.ID)
				if err != nil {
					return err
				}
			}
		}

		// remove the created container
		removeOpts := docker.RemoveContainerOptions{
			ID:            container.ID,
			RemoveVolumes: true,
			Force:         true,
		}

		b.Conf.Logger.Debug("Removing built container %s", container.ID)
		err = b.docker.RemoveContainer(removeOpts)
		if err != nil {
			return err
		}
	}

	// clean up the parsed docker file. It will remain there if there was a problem
	err = os.Remove(b.uniqueDockerfile(step))
	if err != nil {
		return err
	}

	return nil
}

// this replaces the FROM field in the Dockerfile to one with the previous step's unique name
// it stores the parsed result Dockefile in uniqueSessionName file
func (b *Builder) replaceFromField(step *Step) error {
	b.Conf.Logger.Notice("Parsing and converting '%s'", step.Dockerfile)

	rwc, err := os.Open(path.Join(b.Build.Workdir, step.Dockerfile))
	if err != nil {
		return err
	}
	defer rwc.Close()

	node, err := parser.Parse(rwc)
	if err != nil {
		return err
	}

	for _, child := range node.Children {
		if child.Value == "from" {
			// found it. is it from anyone we know?
			if child.Next == nil {
				return errors.New("invalid Dockerfile. No valid FROM found")
			}

			imageName := child.Next.Value
			found, err := step.Manifest.FindStepByName(imageName)
			if err != nil {
				return err
			}

			if found != nil {
				child.Next.Value = b.uniqueStepName(found)
			}
		}
	}

	// did it have any effect?
	err = ioutil.WriteFile(b.uniqueDockerfile(step), []byte(dumpDockerfile(node)), 0644)
	if err != nil {
		return err
	}

	return nil
}

func (b *Builder) copyToHost(a *Artefact, container string) error {
	// create the dest folder if not there
	err := os.MkdirAll(a.Dest, 0777)
	if err != nil {
		return err
	}

	destFile := path.Join(b.Build.Workdir, a.Dest, filepath.Base(a.Source))
	dest, err := os.Create(destFile)
	if err != nil {
		return err
	}
	defer dest.Close()

	opt := docker.CopyFromContainerOptions{
		OutputStream: dest,
		Container:    container,
		Resource:     a.Source,
	}

	b.Conf.Logger.Info("Copying from %s to %s", a.Source, destFile)

	return b.docker.CopyFromContainer(opt)
}

func (b *Builder) createContainer(step *Step) (*docker.Container, error) {
	config := docker.Config{
		AttachStdout: true,
		AttachStdin:  false,
		AttachStderr: true,
		Image:        b.uniqueStepName(step),
		Cmd:          []string{"/bin/bash"},
		Tty:          true,
	}

	opts := docker.CreateContainerOptions{
		Name:   b.uniqueStepName(step) + "." + uniuri.New(),
		Config: &config,
	}
	container, err := b.docker.CreateContainer(opts)
	if err != nil {
		return nil, err
	}

	return container, nil
}

func dumpDockerfile(node *parser.Node) string {
	str := ""
	str += node.Value

	if len(node.Flags) > 0 {
		str += fmt.Sprintf(" %q", node.Flags)
	}

	for _, n := range node.Children {
		str += dumpDockerfile(n) + "\n"
	}

	if node.Next != nil {
		for n := node.Next; n != nil; n = n.Next {
			if len(n.Children) > 0 {
				str += " " + dumpDockerfile(n)
			} else {
				str += " " + n.Value
			}
		}
	}

	return strings.TrimSpace(str)
}

func (b *Builder) uniqueDockerfile(step *Step) string {
	return filepath.Join(b.Build.Workdir, b.uniqueStepName(step))
}
<|endoftext|>


<commit_before>// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package chihaya implements the ability to boot the Chihaya BitTorrent
// tracker with your own imports that can dynamically register additional
// functionality.
package chihaya

import (
	"flag"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"syscall"

	"github.com/golang/glog"

	"github.com/chihaya/chihaya/api"
	"github.com/chihaya/chihaya/config"
	"github.com/chihaya/chihaya/http"
	"github.com/chihaya/chihaya/stats"
	"github.com/chihaya/chihaya/tracker"
	"github.com/chihaya/chihaya/udp"

	// See the README for how to import custom drivers.
	_ "github.com/chihaya/chihaya/backend/noop"
)

var (
	maxProcs   int
	configPath string
)

func init() {
	flag.IntVar(&maxProcs, "maxprocs", runtime.NumCPU(), "maximum parallel threads")
	flag.StringVar(&configPath, "config", "", "path to the configuration file")
}

// Boot starts Chihaya. By exporting this function, anyone can import their own
// custom drivers into their own package main and then call chihaya.Boot.
func Boot() {
	defer glog.Flush()

	flag.Parse()

	runtime.GOMAXPROCS(maxProcs)
	glog.V(1).Info("Set max threads to ", maxProcs)

	debugBoot()
	defer debugShutdown()

	cfg, err := config.Open(configPath)
	if err != nil {
		glog.Fatalf("Failed to parse configuration file: %s\n", err)
	}

	if cfg == &config.DefaultConfig {
		glog.V(1).Info("Using default config")
	} else {
		glog.V(1).Infof("Loaded config file: %s", configPath)
	}

	stats.DefaultStats = stats.New(cfg.StatsConfig)

	tkr, err := tracker.New(cfg)
	if err != nil {
		glog.Fatal("New: ", err)
	}

	var servers []interface {
		Serve()
		Stop()
	}

	if cfg.APIConfig.ListenAddr != "" {
		srv := api.NewServer(cfg, tkr)
		servers = append(servers, srv)
	}

	if cfg.HTTPConfig.ListenAddr != "" {
		srv := http.NewServer(cfg, tkr)
		servers = append(servers, srv)
	}

	if cfg.UDPConfig.ListenAddr != "" {
		srv := udp.NewServer(cfg, tkr)
		servers = append(servers, srv)
	}

	var wg sync.WaitGroup
	for _, srv := range servers {
		wg.Add(1)
		go func() {
			defer wg.Done()
			srv.Serve()
		}()
	}

	shutdown := make(chan os.Signal)
	signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		wg.Wait()
		signal.Stop(shutdown)
		close(shutdown)
	}()

	<-shutdown
	glog.Info("Shutting down...")

	for _, srv := range servers {
		srv.Stop()
	}

	<-shutdown

	if err := tkr.Close(); err != nil {
		glog.Errorf("Failed to shut down tracker cleanly: %s", err.Error())
	}
}
<commit_msg>fix closure starting all servers<commit_after>// Copyright 2015 The Chihaya Authors. All rights reserved.
// Use of this source code is governed by the BSD 2-Clause license,
// which can be found in the LICENSE file.

// Package chihaya implements the ability to boot the Chihaya BitTorrent
// tracker with your own imports that can dynamically register additional
// functionality.
package chihaya

import (
	"flag"
	"os"
	"os/signal"
	"runtime"
	"sync"
	"syscall"

	"github.com/golang/glog"

	"github.com/chihaya/chihaya/api"
	"github.com/chihaya/chihaya/config"
	"github.com/chihaya/chihaya/http"
	"github.com/chihaya/chihaya/stats"
	"github.com/chihaya/chihaya/tracker"
	"github.com/chihaya/chihaya/udp"

	// See the README for how to import custom drivers.
	_ "github.com/chihaya/chihaya/backend/noop"
)

var (
	maxProcs   int
	configPath string
)

func init() {
	flag.IntVar(&maxProcs, "maxprocs", runtime.NumCPU(), "maximum parallel threads")
	flag.StringVar(&configPath, "config", "", "path to the configuration file")
}

type server interface {
	Serve()
	Stop()
}

// Boot starts Chihaya. By exporting this function, anyone can import their own
// custom drivers into their own package main and then call chihaya.Boot.
func Boot() {
	defer glog.Flush()

	flag.Parse()

	runtime.GOMAXPROCS(maxProcs)
	glog.V(1).Info("Set max threads to ", maxProcs)

	debugBoot()
	defer debugShutdown()

	cfg, err := config.Open(configPath)
	if err != nil {
		glog.Fatalf("Failed to parse configuration file: %s\n", err)
	}

	if cfg == &config.DefaultConfig {
		glog.V(1).Info("Using default config")
	} else {
		glog.V(1).Infof("Loaded config file: %s", configPath)
	}

	stats.DefaultStats = stats.New(cfg.StatsConfig)

	tkr, err := tracker.New(cfg)
	if err != nil {
		glog.Fatal("New: ", err)
	}

	var servers []server

	if cfg.APIConfig.ListenAddr != "" {
		servers = append(servers, api.NewServer(cfg, tkr))
	}

	if cfg.HTTPConfig.ListenAddr != "" {
		servers = append(servers, http.NewServer(cfg, tkr))
	}

	if cfg.UDPConfig.ListenAddr != "" {
		servers = append(servers, udp.NewServer(cfg, tkr))
	}

	var wg sync.WaitGroup
	for _, srv := range servers {
		wg.Add(1)

		// If you don't explicitly pass the server, every goroutine captures the
		// last server in the list.
		go func(srv server) {
			defer wg.Done()
			srv.Serve()
		}(srv)
	}

	shutdown := make(chan os.Signal)
	signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)

	go func() {
		wg.Wait()
		signal.Stop(shutdown)
		close(shutdown)
	}()

	<-shutdown
	glog.Info("Shutting down...")

	for _, srv := range servers {
		srv.Stop()
	}

	<-shutdown

	if err := tkr.Close(); err != nil {
		glog.Errorf("Failed to shut down tracker cleanly: %s", err.Error())
	}
}
<|endoftext|>


<commit_before>package antibody

import (
	"os"
	"strings"

	"github.com/caarlos0/gohome"
	"github.com/getantibody/antibody/bundle"
	"github.com/getantibody/antibody/event"
)

// Antibody the main thing
type Antibody struct {
	Events chan event.Event
	Lines  []string
	Home   string
}

// New creates a new Antibody instance with the given parameters
func New(home string, lines []string) *Antibody {
	return &Antibody{
		Lines:  lines,
		Events: make(chan event.Event),
		Home:   home,
	}
}

// Bundle processes all given lines and returns the shell content to execute
func (a *Antibody) Bundle() (string, error) {
	var count int
	var total = len(a.Lines)
	var shs []string
	done := make(chan bool)

	for _, line := range a.Lines {
		go func(l string) {
			l = strings.TrimSpace(l)
			if l != "" && l[0] != '#' {
				bundle.New(a.Home, l).Get(a.Events)
			}
			done <- true
		}(line)
	}

	for {
		select {
		case event := <-a.Events:
			if event.Error != nil {
				return "", event.Error
			}
			shs = append(shs, event.Shell)
		case <-done:
			count++
			if count == total {
				return strings.Join(shs, "\n"), nil
			}
		}
	}
}

// Home finds the right home folder to use
func Home() string {
	home := os.Getenv("ANTIBODY_HOME")
	if home == "" {
		home = gohome.Cache("antibody")
	}
	return home
}
<commit_msg>wg wip<commit_after>package antibody

import (
	"os"
	"strings"
	"sync"

	"github.com/caarlos0/gohome"
	"github.com/getantibody/antibody/bundle"
	"github.com/getantibody/antibody/event"
)

// Antibody the main thing
type Antibody struct {
	Events chan event.Event
	Lines  []string
	Home   string
}

// New creates a new Antibody instance with the given parameters
func New(home string, lines []string) *Antibody {
	return &Antibody{
		Lines:  lines,
		Events: make(chan event.Event),
		Home:   home,
	}
}

// Bundle processes all given lines and returns the shell content to execute
func (a *Antibody) Bundle() (result string, err error) {
	var shs []string
	var wg sync.WaitGroup
	wg.Add(len(a.Lines))
	for _, line := range a.Lines {
		go func(l string) {
			l = strings.TrimSpace(l)
			if l != "" && l[0] != '#' {
				bundle.New(a.Home, l).Get(a.Events)
			}
		}(line)
	}

	go func() {
		for {
			event := <-a.Events
			if event.Error != nil {
				err = event.Error
			} else {
				shs = append(shs, event.Shell)
			}
			wg.Done()
		}
	}()
	wg.Wait()
	return strings.Join(shs, "\n"), err
}

// Home finds the right home folder to use
func Home() string {
	home := os.Getenv("ANTIBODY_HOME")
	if home == "" {
		home = gohome.Cache("antibody")
	}
	return home
}
<|endoftext|>


<commit_before>package circbuf

import (
	"fmt"
)

// Buffer implements a circular buffer. It is a fixed size,
// and new writes overwrite older data, such that for a buffer
// of size N, for any amount of writes, only the last N bytes
// are retained.
type Buffer struct {
	data        []byte
	size        int64
	writeCursor int64
	written     int64
}

// NewBuffer creates a new buffer of a given size. The size
// must be greater than 0.
func NewBuffer(size int64) (*Buffer, error) {
	if size <= 0 {
		return nil, fmt.Errorf("Size must be positive")
	}

	b := &Buffer{
		size: size,
		data: make([]byte, size),
	}
	return b, nil
}

// Write writes up to len(buf) bytes to the internal ring,
// overriding older data if necessary.
func (b *Buffer) Write(buf []byte) (int, error) {
	// Account for total bytes written
	n := len(buf)
	b.written += int64(n)

	// If the buffer is larger than ours, then we only care
	// about the last size bytes anyways
	if int64(n) > b.size {
		buf = buf[int64(n)-b.size:]
	}

	// Copy in place
	remain := b.size - b.writeCursor
	copy(b.data[b.writeCursor:], buf)
	if int64(len(buf)) > remain {
		copy(b.data, buf[remain:])
	}

	// Update location of the cursor
	b.writeCursor = ((b.writeCursor + int64(len(buf))) % b.size)
	return n, nil
}

// Size returns the size of the buffer
func (b *Buffer) Size() int64 {
	return b.size
}

// TotalWritten provides the total number of bytes written
func (b *Buffer) TotalWritten() int64 {
	return b.written
}

// Bytes provides a slice of the bytes written. This
// slice should not be written to.
func (b *Buffer) Bytes() []byte {
	switch {
	case b.written >= b.size && b.writeCursor == 0:
		return b.data
	case b.written > b.size:
		out := make([]byte, b.size)
		copy(out, b.data[b.writeCursor:])
		copy(out[b.size-b.writeCursor:], b.data[:b.writeCursor])
		return out
	default:
		return b.data[:b.writeCursor]
	}
}
<commit_msg>Adding String() method<commit_after>package circbuf

import (
	"fmt"
)

// Buffer implements a circular buffer. It is a fixed size,
// and new writes overwrite older data, such that for a buffer
// of size N, for any amount of writes, only the last N bytes
// are retained.
type Buffer struct {
	data        []byte
	size        int64
	writeCursor int64
	written     int64
}

// NewBuffer creates a new buffer of a given size. The size
// must be greater than 0.
func NewBuffer(size int64) (*Buffer, error) {
	if size <= 0 {
		return nil, fmt.Errorf("Size must be positive")
	}

	b := &Buffer{
		size: size,
		data: make([]byte, size),
	}
	return b, nil
}

// Write writes up to len(buf) bytes to the internal ring,
// overriding older data if necessary.
func (b *Buffer) Write(buf []byte) (int, error) {
	// Account for total bytes written
	n := len(buf)
	b.written += int64(n)

	// If the buffer is larger than ours, then we only care
	// about the last size bytes anyways
	if int64(n) > b.size {
		buf = buf[int64(n)-b.size:]
	}

	// Copy in place
	remain := b.size - b.writeCursor
	copy(b.data[b.writeCursor:], buf)
	if int64(len(buf)) > remain {
		copy(b.data, buf[remain:])
	}

	// Update location of the cursor
	b.writeCursor = ((b.writeCursor + int64(len(buf))) % b.size)
	return n, nil
}

// Size returns the size of the buffer
func (b *Buffer) Size() int64 {
	return b.size
}

// TotalWritten provides the total number of bytes written
func (b *Buffer) TotalWritten() int64 {
	return b.written
}

// Bytes provides a slice of the bytes written. This
// slice should not be written to.
func (b *Buffer) Bytes() []byte {
	switch {
	case b.written >= b.size && b.writeCursor == 0:
		return b.data
	case b.written > b.size:
		out := make([]byte, b.size)
		copy(out, b.data[b.writeCursor:])
		copy(out[b.size-b.writeCursor:], b.data[:b.writeCursor])
		return out
	default:
		return b.data[:b.writeCursor]
	}
}

// String returns the contents of the buffer as a string
func (b *Buffer) String() string {
	return string(b.Bytes())
}
<|endoftext|>


<commit_before>// Copyright 2012 Philip Puryear
//
// 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 (
	"flag"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
)

type ProgramArguments struct {
	KeepCase    bool
	KeepNonword bool
}

var gProgramArgs ProgramArguments

// Used to implement lexicographical string sorting.
type SortableRunes []rune

func (s SortableRunes) Len() int {
	return len(s)
}

func (s SortableRunes) Less(i, j int) bool {
	return s[i] < s[j]
}

func (s SortableRunes) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func CheckAnagrams(a, b string) bool {
	// Fast path: if a and b have different lengths, then they cannot be
	// anagrams.
	if len(a) != len(b) {
		return false
	}

	aRunes, bRunes := SortableRunes(a), SortableRunes(b)
	sort.Sort(aRunes)
	sort.Sort(bRunes)
	return string(aRunes) == string(bRunes)
}

func StripInput(input string) string {
	if !gProgramArgs.KeepCase {
		input = strings.ToLower(input)
	}
	if !gProgramArgs.KeepNonword {
		stripRegexp := regexp.MustCompile("\\W")
		return stripRegexp.ReplaceAllString(input, "")
	}
	return input
}

func Usage() {
	fmt.Fprint(os.Stderr, "usage: check_anagram [options] string1 string2\n\n"+
		"Prints \"Yes\" if string1 and string2 are anagrams, \"No\" otherwise.\n\n"+
		"Options:\n")
	flag.PrintDefaults()
}

func ParseCommandLine() {
	flag.Usage = Usage
	flag.BoolVar(&gProgramArgs.KeepCase, "keep-case", false,
		"do not ignore case")
	flag.BoolVar(&gProgramArgs.KeepNonword, "keep-nonword", false,
		"do not ignore non-word characters")
	flag.Parse()

	if flag.NArg() != 2 {
		fmt.Fprintln(os.Stderr, "incorrect number of inputs")
		Usage()
		os.Exit(1)
	}
}

func main() {
	ParseCommandLine()
	if CheckAnagrams(StripInput(flag.Arg(0)), StripInput(flag.Arg(1))) {
		fmt.Println("Yes")
	} else {
		fmt.Println("No")
	}
}
<commit_msg>[Go] Aesthetic control flow tweak.<commit_after>// Copyright 2012 Philip Puryear
//
// 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 (
	"flag"
	"fmt"
	"os"
	"regexp"
	"sort"
	"strings"
)

type ProgramArguments struct {
	KeepCase    bool
	KeepNonword bool
}

var gProgramArgs ProgramArguments

// Used to implement lexicographical string sorting.
type SortableRunes []rune

func (s SortableRunes) Len() int {
	return len(s)
}

func (s SortableRunes) Less(i, j int) bool {
	return s[i] < s[j]
}

func (s SortableRunes) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func CheckAnagrams(a, b string) bool {
	// Fast path: if a and b have different lengths, then they cannot be
	// anagrams.
	if len(a) != len(b) {
		return false
	}

	aRunes, bRunes := SortableRunes(a), SortableRunes(b)
	sort.Sort(aRunes)
	sort.Sort(bRunes)
	return string(aRunes) == string(bRunes)
}

func StripInput(input string) string {
	if !gProgramArgs.KeepCase {
		input = strings.ToLower(input)
	}
	if !gProgramArgs.KeepNonword {
		stripRegexp := regexp.MustCompile("\\W")
		input = stripRegexp.ReplaceAllString(input, "")
	}
	return input
}

func Usage() {
	fmt.Fprint(os.Stderr, "usage: check_anagram [options] string1 string2\n\n"+
		"Prints \"Yes\" if string1 and string2 are anagrams, \"No\" otherwise.\n\n"+
		"Options:\n")
	flag.PrintDefaults()
}

func ParseCommandLine() {
	flag.Usage = Usage
	flag.BoolVar(&gProgramArgs.KeepCase, "keep-case", false,
		"do not ignore case")
	flag.BoolVar(&gProgramArgs.KeepNonword, "keep-nonword", false,
		"do not ignore non-word characters")
	flag.Parse()

	if flag.NArg() != 2 {
		fmt.Fprintln(os.Stderr, "incorrect number of inputs")
		Usage()
		os.Exit(1)
	}
}

func main() {
	ParseCommandLine()
	if CheckAnagrams(StripInput(flag.Arg(0)), StripInput(flag.Arg(1))) {
		fmt.Println("Yes")
	} else {
		fmt.Println("No")
	}
}
<|endoftext|>


<commit_before>package checkerlution

import (
	"fmt"
	"github.com/couchbaselabs/logg"
	cbot "github.com/tleyden/checkers-bot"
	core "github.com/tleyden/checkers-core"
	ng "github.com/tleyden/neurgo"
)

type OperationMode int

const (
	RUNNING_MODE = iota
	TRAINING_MODE
)

type Checkerlution struct {
	ourTeamId            cbot.TeamType
	cortex               *ng.Cortex
	currentGameState     GameStateVector
	currentPossibleMove  ValidMoveCortexInput
	latestActuatorOutput []float64
	mode                 OperationMode
	latestFitnessScore   float64
}

func (c *Checkerlution) Start(ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.CreateNeurgoCortex()
	cortex := c.cortex
	cortex.Run()

}

func (c *Checkerlution) StartWithCortex(cortex *ng.Cortex, ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.setSensorActuatorFunctions(cortex)
	c.cortex = cortex
	cortex.Run()
}

func (c *Checkerlution) Think(gameState cbot.GameState) (bestMove cbot.ValidMove, ok bool) {

	ok = true
	ourTeam := gameState.Teams[c.ourTeamId]
	allValidMoves := ourTeam.AllValidMoves()
	if len(allValidMoves) > 0 {

		// convert into core.board representation
		board := gameState.Export()
		logg.LogTo("DEBUG", "Before move %v", board.CompactString(true))

		// generate best move (will be a core.move) -- initially, pick random
		move := c.generateBestMove(board)

		// search allValidMoves to find corresponding valid move
		found, bestValidMoveIndex := cbot.CorrespondingValidMoveIndex(move, allValidMoves)

		if !found {
			msg := "Could not find corresponding valid move: %v in %v"
			logg.LogPanic(msg, move, allValidMoves)
		} else {
			bestMove = allValidMoves[bestValidMoveIndex]
		}

		// this is just for debugging purposes
		player := cbot.GetCorePlayer(c.ourTeamId)
		boardPostMove := board.ApplyMove(player, move)
		logg.LogTo("DEBUG", "After move %v", boardPostMove.CompactString(true))

		return

	} else {
		ok = false
	}

	return

}

func (c *Checkerlution) GameFinished(gameState cbot.GameState) (shouldQuit bool) {
	switch c.mode {
	case TRAINING_MODE:
		shouldQuit = true
		c.latestFitnessScore = c.calculateFitness(gameState)
	case RUNNING_MODE:
		shouldQuit = false
	}
	return
}

func (c Checkerlution) Cortex() *ng.Cortex {
	return c.cortex
}

func (c *Checkerlution) SetMode(mode OperationMode) {
	c.mode = mode
}

func (c Checkerlution) calculateFitness(gameState cbot.GameState) (fitness float64) {
	weWon := (gameState.WinningTeam == c.ourTeamId)
	switch weWon {
	case true:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on winning.  Turn: %v", gameState.Turn)
		// fitness will be a positive number
		// the least amount of moves we made, the higher the fitness
		fitness = 200
		fitness -= float64(gameState.Turn)
		if fitness < 1 {
			fitness = 1 // lowest possible fitness when winning
		}
	case false:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on losing.  Turn: %v", gameState.Turn)
		// fitness will be a negative number
		// the least amount of moves we made, the lower (more negative)
		// the fitness, because we didn't put up much of a fight
		fitness = -200
		fitness += float64(gameState.Turn)
		if fitness > -1 {
			fitness = -1 // highest possible fitness when losing
		}
	}

	logg.LogTo("CHECKERLUTION", "calculateFitness returning: %v", fitness)
	return
}

func (c *Checkerlution) CreateNeurgoCortex() {

	uuid := ng.NewUuid()
	cortexUuid := fmt.Sprintf("cortex-%s", uuid)
	nodeId := ng.NewCortexId(cortexUuid)

	c.cortex = &ng.Cortex{
		NodeId: nodeId,
	}

	c.cortex.Init()

	c.CreateSensors()

	outputNeuron := c.CreateOutputNeuron()
	layer1Neurons := c.CreateHiddenLayer1Neurons(outputNeuron)
	layer2Neurons := c.CreateHiddenLayer2Neurons(layer1Neurons, outputNeuron)

	// combine all into single slice and add neurons to cortex
	neurons := []*ng.Neuron{}
	neurons = append(neurons, layer1Neurons...)
	neurons = append(neurons, layer2Neurons...)
	neurons = append(neurons, outputNeuron)
	c.cortex.SetNeurons(neurons)

	actuator := c.CreateActuator()

	// workaround for error
	// Cannot make outbound connection, dataChan == nil [recovered]
	c.cortex.Init()

	outputNeuron.ConnectOutbound(actuator)
	actuator.ConnectInbound(outputNeuron)

	c.cortex.MarshalJSONToFile("checkerlution_cortex.json")

}

func (c *Checkerlution) LoadNeurgoCortex(filename string) {

	cortex, err := ng.NewCortexFromJSONFile(filename)
	if err != nil {
		logg.LogPanic("Error reading cortex from: %v.  Err: %v", filename, err)
	}

	c.setSensorActuatorFunctions(cortex)

	c.cortex = cortex
}

func (c *Checkerlution) setSensorActuatorFunctions(cortex *ng.Cortex) {

	sensor := cortex.FindSensor(ng.NewSensorId("SensorGameState", 0))
	sensor.SensorFunction = c.sensorFuncGameState()
	sensor = cortex.FindSensor(ng.NewSensorId("SensorPossibleMove", 0))
	sensor.SensorFunction = c.sensorFuncPossibleMove()

	actuator := cortex.FindActuator(ng.NewActuatorId("Actuator", 0))
	actuator.ActuatorFunction = c.actuatorFunc()

}

func (c *Checkerlution) CreateHiddenLayer1Neurons(outputNeuron *ng.Neuron) []*ng.Neuron {

	cortex := c.cortex
	neurons := []*ng.Neuron{}
	layerIndex := 0.25

	for i := 0; i < 40; i++ {
		name := fmt.Sprintf("hidden-layer-1-n-%d", i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, sensor := range cortex.Sensors {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateHiddenLayer2Neurons(layer1Neurons []*ng.Neuron, outputNeuron *ng.Neuron) []*ng.Neuron {

	neurons := []*ng.Neuron{}
	layerIndex := 0.35

	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("hidden-layer-1-n-%d", i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, layer1Neuron := range layer1Neurons {
			layer1Neuron.ConnectOutbound(neuron)
			weights := ng.RandomWeights(1)
			neuron.ConnectInboundWeighted(layer1Neuron, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateOutputNeuron() *ng.Neuron {

	layerIndex := 0.45
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableTanh(),
		NodeId:             ng.NewNeuronId("OutputNeuron", layerIndex),
		Bias:               ng.RandomBias(),
	}

	// Workaround for error:
	// Cannot make outbound connection, dataChan == nil [recovered]
	// The best fix is to just load nn from json
	neuron.Init()

	return neuron

}

func (c *Checkerlution) CreateActuator() *ng.Actuator {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: c.actuatorFunc(),
	}
	c.cortex.SetActuators([]*ng.Actuator{actuator})
	return actuator

}

func (c *Checkerlution) actuatorFunc() ng.ActuatorFunction {
	return func(outputs []float64) {
		logg.LogTo("CHECKERLUTION", "actuator func called with: %v", outputs)
		c.latestActuatorOutput = outputs
	}
}

func (c *Checkerlution) CreateSensors() {

	sensorLayer := 0.0

	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: c.sensorFuncGameState(),
	}

	c.cortex.SetSensors([]*ng.Sensor{sensorGameState})

}

func (c *Checkerlution) sensorFuncGameState() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func game state called on thinker: %p, returning: %v", c, c.currentGameState)
		if len(c.currentGameState) == 0 {
			logg.LogPanic("sensor would return invalid gamestate")
		}
		return c.currentGameState
	}
}

func (c *Checkerlution) sensorFuncPossibleMove() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func possible move called")
		return c.currentPossibleMove.VectorRepresentation()
	}
}

func (c Checkerlution) Stop() {

}

func (c *Checkerlution) generateBestMove(board core.Board) core.Move {
	evalFunc := c.getEvaluationFunction()
	player := cbot.GetCorePlayer(c.ourTeamId)

	// with depth = 5, not working too well on first move.  when
	// there are only a few pieces on the board it seems to work,
	// but with full board .. taking a long time.

	depth := 4 // TODO: crank this up higher
	bestMove, scorePostMove := board.Minimax(player, depth, evalFunc)
	logg.LogTo("DEBUG", "scorePostMove: %v", scorePostMove)
	return bestMove
}

func (c *Checkerlution) getEvaluationFunction() core.EvaluationFunction {

	evalFunc := func(currentPlayer core.Player, board core.Board) float64 {

		// convert the board into inputs for the neural net (32 elt vector)
		// taking into account whether this player is "us" or not
		gameStateVector := NewGameStateVector()
		gameStateVector.loadFromBoard(board, currentPlayer)

		// send input to the neural net
		logg.LogTo("CHECKERLUTION", "set currentGameState %v", gameStateVector)
		c.currentGameState = gameStateVector
		c.cortex.SyncSensors()
		c.cortex.SyncActuators()

		// get output
		logg.LogTo("CHECKERLUTION", "actuator output %v", c.latestActuatorOutput[0])

		// return output
		return c.latestActuatorOutput[0]

	}
	return evalFunc

}
<commit_msg>node names had wrong layer<commit_after>package checkerlution

import (
	"fmt"
	"github.com/couchbaselabs/logg"
	cbot "github.com/tleyden/checkers-bot"
	core "github.com/tleyden/checkers-core"
	ng "github.com/tleyden/neurgo"
)

type OperationMode int

const (
	RUNNING_MODE = iota
	TRAINING_MODE
)

type Checkerlution struct {
	ourTeamId            cbot.TeamType
	cortex               *ng.Cortex
	currentGameState     GameStateVector
	currentPossibleMove  ValidMoveCortexInput
	latestActuatorOutput []float64
	mode                 OperationMode
	latestFitnessScore   float64
}

func (c *Checkerlution) Start(ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.CreateNeurgoCortex()
	cortex := c.cortex
	cortex.Run()

}

func (c *Checkerlution) StartWithCortex(cortex *ng.Cortex, ourTeamId cbot.TeamType) {
	c.ourTeamId = ourTeamId
	c.setSensorActuatorFunctions(cortex)
	c.cortex = cortex
	cortex.Run()
}

func (c *Checkerlution) Think(gameState cbot.GameState) (bestMove cbot.ValidMove, ok bool) {

	ok = true
	ourTeam := gameState.Teams[c.ourTeamId]
	allValidMoves := ourTeam.AllValidMoves()
	if len(allValidMoves) > 0 {

		// convert into core.board representation
		board := gameState.Export()
		logg.LogTo("DEBUG", "Before move %v", board.CompactString(true))

		// generate best move (will be a core.move) -- initially, pick random
		move := c.generateBestMove(board)

		// search allValidMoves to find corresponding valid move
		found, bestValidMoveIndex := cbot.CorrespondingValidMoveIndex(move, allValidMoves)

		if !found {
			msg := "Could not find corresponding valid move: %v in %v"
			logg.LogPanic(msg, move, allValidMoves)
		} else {
			bestMove = allValidMoves[bestValidMoveIndex]
		}

		// this is just for debugging purposes
		player := cbot.GetCorePlayer(c.ourTeamId)
		boardPostMove := board.ApplyMove(player, move)
		logg.LogTo("DEBUG", "After move %v", boardPostMove.CompactString(true))

		return

	} else {
		ok = false
	}

	return

}

func (c *Checkerlution) GameFinished(gameState cbot.GameState) (shouldQuit bool) {
	switch c.mode {
	case TRAINING_MODE:
		shouldQuit = true
		c.latestFitnessScore = c.calculateFitness(gameState)
	case RUNNING_MODE:
		shouldQuit = false
	}
	return
}

func (c Checkerlution) Cortex() *ng.Cortex {
	return c.cortex
}

func (c *Checkerlution) SetMode(mode OperationMode) {
	c.mode = mode
}

func (c Checkerlution) calculateFitness(gameState cbot.GameState) (fitness float64) {
	weWon := (gameState.WinningTeam == c.ourTeamId)
	switch weWon {
	case true:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on winning.  Turn: %v", gameState.Turn)
		// fitness will be a positive number
		// the least amount of moves we made, the higher the fitness
		fitness = 200
		fitness -= float64(gameState.Turn)
		if fitness < 1 {
			fitness = 1 // lowest possible fitness when winning
		}
	case false:
		logg.LogTo("CHECKERLUTION", "calculateFitness based on losing.  Turn: %v", gameState.Turn)
		// fitness will be a negative number
		// the least amount of moves we made, the lower (more negative)
		// the fitness, because we didn't put up much of a fight
		fitness = -200
		fitness += float64(gameState.Turn)
		if fitness > -1 {
			fitness = -1 // highest possible fitness when losing
		}
	}

	logg.LogTo("CHECKERLUTION", "calculateFitness returning: %v", fitness)
	return
}

func (c *Checkerlution) CreateNeurgoCortex() {

	uuid := ng.NewUuid()
	cortexUuid := fmt.Sprintf("cortex-%s", uuid)
	nodeId := ng.NewCortexId(cortexUuid)

	c.cortex = &ng.Cortex{
		NodeId: nodeId,
	}

	c.cortex.Init()

	c.CreateSensors()

	outputNeuron := c.CreateOutputNeuron()
	layer1Neurons := c.CreateHiddenLayer1Neurons(outputNeuron)
	layer2Neurons := c.CreateHiddenLayer2Neurons(layer1Neurons, outputNeuron)

	// combine all into single slice and add neurons to cortex
	neurons := []*ng.Neuron{}
	neurons = append(neurons, layer1Neurons...)
	neurons = append(neurons, layer2Neurons...)
	neurons = append(neurons, outputNeuron)
	c.cortex.SetNeurons(neurons)

	actuator := c.CreateActuator()

	// workaround for error
	// Cannot make outbound connection, dataChan == nil [recovered]
	c.cortex.Init()

	outputNeuron.ConnectOutbound(actuator)
	actuator.ConnectInbound(outputNeuron)

	c.cortex.MarshalJSONToFile("checkerlution_cortex.json")

}

func (c *Checkerlution) LoadNeurgoCortex(filename string) {

	cortex, err := ng.NewCortexFromJSONFile(filename)
	if err != nil {
		logg.LogPanic("Error reading cortex from: %v.  Err: %v", filename, err)
	}

	c.setSensorActuatorFunctions(cortex)

	c.cortex = cortex
}

func (c *Checkerlution) setSensorActuatorFunctions(cortex *ng.Cortex) {

	sensor := cortex.FindSensor(ng.NewSensorId("SensorGameState", 0))
	sensor.SensorFunction = c.sensorFuncGameState()
	sensor = cortex.FindSensor(ng.NewSensorId("SensorPossibleMove", 0))
	sensor.SensorFunction = c.sensorFuncPossibleMove()

	actuator := cortex.FindActuator(ng.NewActuatorId("Actuator", 0))
	actuator.ActuatorFunction = c.actuatorFunc()

}

func (c *Checkerlution) CreateHiddenLayer1Neurons(outputNeuron *ng.Neuron) []*ng.Neuron {

	cortex := c.cortex
	neurons := []*ng.Neuron{}
	layerIndex := 0.25

	for i := 0; i < 40; i++ {
		name := fmt.Sprintf("hidden-layer-%f-n-%d", layerIndex, i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, sensor := range cortex.Sensors {
			sensor.ConnectOutbound(neuron)
			weights := ng.RandomWeights(sensor.VectorLength)
			neuron.ConnectInboundWeighted(sensor, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateHiddenLayer2Neurons(layer1Neurons []*ng.Neuron, outputNeuron *ng.Neuron) []*ng.Neuron {

	neurons := []*ng.Neuron{}
	layerIndex := 0.35

	for i := 0; i < 10; i++ {
		name := fmt.Sprintf("hidden-layer-%f-n-%d", layerIndex, i)
		neuron := &ng.Neuron{
			ActivationFunction: ng.EncodableTanh(),
			NodeId:             ng.NewNeuronId(name, layerIndex),
			Bias:               ng.RandomBias(),
		}

		// Workaround for error:
		// Cannot make outbound connection, dataChan == nil [recovered]
		// The best fix is to just load nn from json
		neuron.Init()

		for _, layer1Neuron := range layer1Neurons {
			layer1Neuron.ConnectOutbound(neuron)
			weights := ng.RandomWeights(1)
			neuron.ConnectInboundWeighted(layer1Neuron, weights)
		}

		// connect directly to output neuron
		neuron.ConnectOutbound(outputNeuron)
		weights := ng.RandomWeights(1)
		outputNeuron.ConnectInboundWeighted(neuron, weights)

		neurons = append(neurons, neuron)

	}
	return neurons

}

func (c *Checkerlution) CreateOutputNeuron() *ng.Neuron {

	layerIndex := 0.45
	neuron := &ng.Neuron{
		ActivationFunction: ng.EncodableTanh(),
		NodeId:             ng.NewNeuronId("OutputNeuron", layerIndex),
		Bias:               ng.RandomBias(),
	}

	// Workaround for error:
	// Cannot make outbound connection, dataChan == nil [recovered]
	// The best fix is to just load nn from json
	neuron.Init()

	return neuron

}

func (c *Checkerlution) CreateActuator() *ng.Actuator {

	actuatorNodeId := ng.NewActuatorId("Actuator", 0.5)
	actuator := &ng.Actuator{
		NodeId:           actuatorNodeId,
		VectorLength:     1,
		ActuatorFunction: c.actuatorFunc(),
	}
	c.cortex.SetActuators([]*ng.Actuator{actuator})
	return actuator

}

func (c *Checkerlution) actuatorFunc() ng.ActuatorFunction {
	return func(outputs []float64) {
		logg.LogTo("CHECKERLUTION", "actuator func called with: %v", outputs)
		c.latestActuatorOutput = outputs
	}
}

func (c *Checkerlution) CreateSensors() {

	sensorLayer := 0.0

	sensorGameStateNodeId := ng.NewSensorId("SensorGameState", sensorLayer)
	sensorGameState := &ng.Sensor{
		NodeId:         sensorGameStateNodeId,
		VectorLength:   32,
		SensorFunction: c.sensorFuncGameState(),
	}

	c.cortex.SetSensors([]*ng.Sensor{sensorGameState})

}

func (c *Checkerlution) sensorFuncGameState() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func game state called on thinker: %p, returning: %v", c, c.currentGameState)
		if len(c.currentGameState) == 0 {
			logg.LogPanic("sensor would return invalid gamestate")
		}
		return c.currentGameState
	}
}

func (c *Checkerlution) sensorFuncPossibleMove() ng.SensorFunction {
	return func(syncCounter int) []float64 {
		logg.LogTo("CHECKERLUTION", "sensor func possible move called")
		return c.currentPossibleMove.VectorRepresentation()
	}
}

func (c Checkerlution) Stop() {

}

func (c *Checkerlution) generateBestMove(board core.Board) core.Move {
	evalFunc := c.getEvaluationFunction()
	player := cbot.GetCorePlayer(c.ourTeamId)

	// with depth = 5, not working too well on first move.  when
	// there are only a few pieces on the board it seems to work,
	// but with full board .. taking a long time.

	depth := 4 // TODO: crank this up higher
	bestMove, scorePostMove := board.Minimax(player, depth, evalFunc)
	logg.LogTo("DEBUG", "scorePostMove: %v", scorePostMove)
	return bestMove
}

func (c *Checkerlution) getEvaluationFunction() core.EvaluationFunction {

	evalFunc := func(currentPlayer core.Player, board core.Board) float64 {

		// convert the board into inputs for the neural net (32 elt vector)
		// taking into account whether this player is "us" or not
		gameStateVector := NewGameStateVector()
		gameStateVector.loadFromBoard(board, currentPlayer)

		// send input to the neural net
		logg.LogTo("CHECKERLUTION", "set currentGameState %v", gameStateVector)
		c.currentGameState = gameStateVector
		c.cortex.SyncSensors()
		c.cortex.SyncActuators()

		// get output
		logg.LogTo("CHECKERLUTION", "actuator output %v", c.latestActuatorOutput[0])

		// return output
		return c.latestActuatorOutput[0]

	}
	return evalFunc

}
<|endoftext|>


<commit_before>package chip8

// OneJumpTo is the 1NNN opcode which jump to the NNN address
func OneJumpTo(m *Memory, opcode uint16) {
	m.PC = opcode & 0xFFFF
}

// ThreeEqSkip is the 3XNN opcode
// which skip the next instruction if VX equals NN
func ThreeEqSkip(m *Memory, opcode uint16) {
	vx := m.V[(opcode&0x0F00)>>16]
	if vx == byte(opcode&0x00FF) {
		m.PC += 2
	}
}

// FourNeqSkip is the 4XNN opcode
// which skip the next instruction if VX not equals NN
func FourNeqSkip(m *Memory, opcode uint16) {
	vx := m.V[(opcode&0x0F00)>>16]
	if vx != byte(opcode&0x00FF) {
		m.PC += 2
	}
}

// FiveEqSkip is the 5XY0 opcode
// which skip the next instruction if VX not equals NN
func FiveEqSkip(m *Memory, opcode uint16) {
	vx := m.V[(opcode&0x0F00)>>16]
	vy := m.V[(opcode&0x00F0)>>8]
	if vx == vy {
		m.PC += 2
	}
}

// SixSetRegister is the 6XNN opcode
// which set VX to NN
func SixSetRegister(m *Memory, opcode uint16) {
	m.V[(opcode&0x0F00)>>16] = byte(opcode & 0x00FF)
}

// SevenAddToRegister is the 7XNN opcode
// which add NN to VX
func SevenAddToRegister(m *Memory, opcode uint16) {
	sum := uint16(m.V[(opcode&0x0F00)>>16]) + opcode&0x00FF
	if sum > 0x00FF {
		m.VF = true
		//TODO is this the standard behaviour ?
		m.V[(opcode & 0x0F00)] = 0
	} else {
		m.V[(opcode&0x0F00)>>16] = byte(sum)
	}
}
<commit_msg>More opcodes<commit_after>package chip8

// OneJumpTo is the 1NNN opcode which jump to the NNN address
func OneJumpTo(m *Memory, opcode uint16) {
	m.PC = opcode & 0xFFFF
}

// ThreeEqSkip is the 3XNN opcode
// which skip the next instruction if VX equals NN
func ThreeEqSkip(m *Memory, opcode uint16) {
	vx := m.V[(opcode&0x0F00)>>16]
	if vx == byte(opcode&0x00FF) {
		m.PC += 2
	}
}

// FourNeqSkip is the 4XNN opcode
// which skip the next instruction if VX not equals NN
func FourNeqSkip(m *Memory, opcode uint16) {
	vx := m.V[(opcode&0x0F00)>>16]
	if vx != byte(opcode&0x00FF) {
		m.PC += 2
	}
}

// FiveEqSkip is the 5XY0 opcode
// which skip the next instruction if VX not equals NN
func FiveEqSkip(m *Memory, opcode uint16) {
	vx := m.V[(opcode&0x0F00)>>16]
	vy := m.V[(opcode&0x00F0)>>8]
	if vx == vy {
		m.PC += 2
	}
}

// SixSetRegister is the 6XNN opcode
// which set VX to NN
func SixSetRegister(m *Memory, opcode uint16) {
	m.V[(opcode&0x0F00)>>16] = byte(opcode & 0x00FF)
}

// SevenAddToRegister is the 7XNN opcode
// which add NN to VX
func SevenAddToRegister(m *Memory, opcode uint16) {
	sum := uint16(m.V[(opcode&0x0F00)>>16]) + opcode&0x00FF
	if sum > 0x00FF {
		m.VF = true
		//TODO is this the standard behaviour ?
		m.V[(opcode & 0x0F00)] = 0
	} else {
		m.V[(opcode&0x0F00)>>16] = byte(sum)
	}
}

// ASetAddressRegister is the ANNN opcode
// which set the Address register I to NNN
func ASetAddressRegister(m *Memory, opcode uint16) {
	m.I = opcode & 0x0FFF
}

// BJumpToV0 is the BNNN
// which jump to the address V0 + NNN
func BJumpToV0(m *Memory, opcode uint16) {
	m.PC = uint16(m.V[0]) + (opcode & 0x0FFF)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local

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

	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/schema"
)

var checkIfRoot = func() bool {
	return os.Getuid() == 0
}

var (
	configFields = schema.Fields{
		"root-dir":       schema.String(),
		"bootstrap-ip":   schema.String(),
		"network-bridge": schema.String(),
		"container":      schema.String(),
		"storage-port":   schema.ForceInt(),
		"namespace":      schema.String(),
		"lxc-clone":      schema.Bool(),
		"lxc-clone-aufs": schema.Bool(),
	}
	// The port defaults below are not entirely arbitrary.  Local user web
	// frameworks often use 8000 or 8080, so I didn't want to use either of
	// these, but did want the familiarity of using something in the 8000
	// range.
	configDefaults = schema.Defaults{
		"root-dir":       "",
		"network-bridge": "lxcbr0",
		"container":      string(instance.LXC),
		"bootstrap-ip":   schema.Omit,
		"storage-port":   8040,
		"namespace":      "",
		"lxc-clone":      schema.Omit,
		"lxc-clone-aufs": schema.Omit,
	}
)

type environConfig struct {
	*config.Config
	attrs map[string]interface{}
}

func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig {
	return &environConfig{
		Config: config,
		attrs:  attrs,
	}
}

// Since it is technically possible for two different users on one machine to
// have the same local provider name, we need to have a simple way to
// namespace the file locations, but more importantly the containers.
func (c *environConfig) namespace() string {
	return c.attrs["namespace"].(string)
}

func (c *environConfig) rootDir() string {
	return c.attrs["root-dir"].(string)
}

func (c *environConfig) container() instance.ContainerType {
	return instance.ContainerType(c.attrs["container"].(string))
}

func (c *environConfig) networkBridge() string {
	return c.attrs["network-bridge"].(string)
}

func (c *environConfig) storageDir() string {
	return filepath.Join(c.rootDir(), "storage")
}

func (c *environConfig) mongoDir() string {
	return filepath.Join(c.rootDir(), "db")
}

func (c *environConfig) logDir() string {
	return fmt.Sprintf("/var/log/juju-%s", c.namespace())
}

// bootstrapIPAddress returns the IP address of the bootstrap machine.
// As of 1.18 this is only set inside the environment, and not in the
// .jenv file.
func (c *environConfig) bootstrapIPAddress() string {
	addr, _ := c.attrs["bootstrap-ip"].(string)
	return addr
}

func (c *environConfig) storagePort() int {
	return c.attrs["storage-port"].(int)
}

func (c *environConfig) storageAddr() string {
	return fmt.Sprintf("%s:%d", c.bootstrapIPAddress(), c.storagePort())
}

func (c *environConfig) configFile(filename string) string {
	return filepath.Join(c.rootDir(), filename)
}

func (c *environConfig) lxcClone() bool {
	value, _ := c.attrs["lxc-clone"].(bool)
	return value
}

func (c *environConfig) lxcCloneAUFS() bool {
	value, _ := c.attrs["lxc-clone-aufs"].(bool)
	return value
}

func (c *environConfig) createDirs() error {
	for _, dirname := range []string{
		c.storageDir(),
		c.mongoDir(),
	} {
		logger.Tracef("creating directory %s", dirname)
		if err := os.MkdirAll(dirname, 0755); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>provider/local: use DefaultLogDir<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package local

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

	"launchpad.net/juju-core/agent"
	"launchpad.net/juju-core/environs/config"
	"launchpad.net/juju-core/instance"
	"launchpad.net/juju-core/schema"
)

var checkIfRoot = func() bool {
	return os.Getuid() == 0
}

var (
	configFields = schema.Fields{
		"root-dir":       schema.String(),
		"bootstrap-ip":   schema.String(),
		"network-bridge": schema.String(),
		"container":      schema.String(),
		"storage-port":   schema.ForceInt(),
		"namespace":      schema.String(),
		"lxc-clone":      schema.Bool(),
		"lxc-clone-aufs": schema.Bool(),
	}
	// The port defaults below are not entirely arbitrary.  Local user web
	// frameworks often use 8000 or 8080, so I didn't want to use either of
	// these, but did want the familiarity of using something in the 8000
	// range.
	configDefaults = schema.Defaults{
		"root-dir":       "",
		"network-bridge": "lxcbr0",
		"container":      string(instance.LXC),
		"bootstrap-ip":   schema.Omit,
		"storage-port":   8040,
		"namespace":      "",
		"lxc-clone":      schema.Omit,
		"lxc-clone-aufs": schema.Omit,
	}
)

type environConfig struct {
	*config.Config
	attrs map[string]interface{}
}

func newEnvironConfig(config *config.Config, attrs map[string]interface{}) *environConfig {
	return &environConfig{
		Config: config,
		attrs:  attrs,
	}
}

// Since it is technically possible for two different users on one machine to
// have the same local provider name, we need to have a simple way to
// namespace the file locations, but more importantly the containers.
func (c *environConfig) namespace() string {
	return c.attrs["namespace"].(string)
}

func (c *environConfig) rootDir() string {
	return c.attrs["root-dir"].(string)
}

func (c *environConfig) container() instance.ContainerType {
	return instance.ContainerType(c.attrs["container"].(string))
}

func (c *environConfig) networkBridge() string {
	return c.attrs["network-bridge"].(string)
}

func (c *environConfig) storageDir() string {
	return filepath.Join(c.rootDir(), "storage")
}

func (c *environConfig) mongoDir() string {
	return filepath.Join(c.rootDir(), "db")
}

func (c *environConfig) logDir() string {
	return fmt.Sprintf("%s-%s", agent.DefaultLogDir, c.namespace())
}

// bootstrapIPAddress returns the IP address of the bootstrap machine.
// As of 1.18 this is only set inside the environment, and not in the
// .jenv file.
func (c *environConfig) bootstrapIPAddress() string {
	addr, _ := c.attrs["bootstrap-ip"].(string)
	return addr
}

func (c *environConfig) storagePort() int {
	return c.attrs["storage-port"].(int)
}

func (c *environConfig) storageAddr() string {
	return fmt.Sprintf("%s:%d", c.bootstrapIPAddress(), c.storagePort())
}

func (c *environConfig) configFile(filename string) string {
	return filepath.Join(c.rootDir(), filename)
}

func (c *environConfig) lxcClone() bool {
	value, _ := c.attrs["lxc-clone"].(bool)
	return value
}

func (c *environConfig) lxcCloneAUFS() bool {
	value, _ := c.attrs["lxc-clone-aufs"].(bool)
	return value
}

func (c *environConfig) createDirs() error {
	for _, dirname := range []string{
		c.storageDir(),
		c.mongoDir(),
	} {
		logger.Tracef("creating directory %s", dirname)
		if err := os.MkdirAll(dirname, 0755); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package godoauth

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/docker/libtrust"
	"gopkg.in/yaml.v2"
)

type Configuration struct {
	Version string     `yaml:"version,omitempty"`
	Log     Log        `yaml:"log,omitempty"`
	Storage Storage    `yaml:"storage,omitempty"`
	Http    ServerConf `yaml:"http"`
	Token   Token      `yaml:"token"`
}

type Log struct {
	Level string `yaml:"level,omitempty"`
	File  string `yaml:"file,omitempty"`
}

type Storage struct {
	Vault Vault `yaml:"vault"`
}

type Vault struct {
	Host      string `yaml:"host"`
	Port      int    `yaml:"port"`
	AuthToken string `yaml:"auth_token"`
	Proto     string `yaml:"proto"`
}

type ServerConf struct {
	Addr    string    `yaml:"addr"`
	Timeout string    `yaml:"timeout"`
	Tls     ServerTls `yaml:"tls"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

type ServerTls struct {
	Certificate string `yaml:"certificate,omitempty"`
	Key         string `yaml:"key,omitempty"`
}

type Token struct {
	Issuer      string `yaml:"issuer"`
	Expiration  int64  `yaml:"expiration"`
	Certificate string `yaml:"certificate,omitempty"`
	Key         string `yaml:"key,omitempty"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

func (c *Configuration) Parse(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	if err := yaml.Unmarshal(b, c); err != nil {
		return err
	}
	if c.Token.Certificate != "" && c.Token.Key != "" {
		c.Token.publicKey, c.Token.privateKey, err = c.loadCerts(c.Token.Certificate, c.Token.Key)
		if err != nil {
			return err
		}
	}
	// Sign something dummy to find out which algorithm is used.
	_, sigAlg, err := c.Token.privateKey.Sign(strings.NewReader("whoami"), 0)
	if err != nil {
		return fmt.Errorf("failed to sign: %s", err)
	}
	// check if the library supports this sign algorithm
	if alg := jwt.GetSigningMethod(sigAlg); alg == nil {
		return fmt.Errorf("signing algorithm not supported: %s", sigAlg)
	}
	return nil
}

func (c *Configuration) loadCerts(certFile, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, err error) {
	certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}
	x509Cert, err := x509.ParseCertificate(certificate.Certificate[0])
	if err != nil {
		return
	}
	pk, err = libtrust.FromCryptoPublicKey(x509Cert.PublicKey)
	if err != nil {
		return
	}
	prk, err = libtrust.FromCryptoPrivateKey(certificate.PrivateKey)
	return
}
<commit_msg>Refactor Tls -> TLS to match Go conventions.<commit_after>package godoauth

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"strings"

	"github.com/dgrijalva/jwt-go"
	"github.com/docker/libtrust"
	"gopkg.in/yaml.v2"
)

type Configuration struct {
	Version string     `yaml:"version,omitempty"`
	Log     Log        `yaml:"log,omitempty"`
	Storage Storage    `yaml:"storage,omitempty"`
	Http    ServerConf `yaml:"http"`
	Token   Token      `yaml:"token"`
}

type Log struct {
	Level string `yaml:"level,omitempty"`
	File  string `yaml:"file,omitempty"`
}

type Storage struct {
	Vault Vault `yaml:"vault"`
}

type Vault struct {
	Host      string `yaml:"host"`
	Port      int    `yaml:"port"`
	AuthToken string `yaml:"auth_token"`
	Proto     string `yaml:"proto"`
}

type ServerConf struct {
	Addr    string    `yaml:"addr"`
	Timeout string    `yaml:"timeout"`
	TLS     ServerTLS `yaml:"tls"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

type ServerTLS struct {
	Certificate string `yaml:"certificate,omitempty"`
	Key         string `yaml:"key,omitempty"`
}

type Token struct {
	Issuer      string `yaml:"issuer"`
	Expiration  int64  `yaml:"expiration"`
	Certificate string `yaml:"certificate,omitempty"`
	Key         string `yaml:"key,omitempty"`

	publicKey  libtrust.PublicKey
	privateKey libtrust.PrivateKey
}

func (c *Configuration) Parse(path string) error {
	b, err := ioutil.ReadFile(path)
	if err != nil {
		return err
	}

	if err := yaml.Unmarshal(b, c); err != nil {
		return err
	}
	if c.Token.Certificate != "" && c.Token.Key != "" {
		c.Token.publicKey, c.Token.privateKey, err = c.loadCerts(c.Token.Certificate, c.Token.Key)
		if err != nil {
			return err
		}
	}
	// Sign something dummy to find out which algorithm is used.
	_, sigAlg, err := c.Token.privateKey.Sign(strings.NewReader("whoami"), 0)
	if err != nil {
		return fmt.Errorf("failed to sign: %s", err)
	}
	// check if the library supports this sign algorithm
	if alg := jwt.GetSigningMethod(sigAlg); alg == nil {
		return fmt.Errorf("signing algorithm not supported: %s", sigAlg)
	}
	return nil
}

func (c *Configuration) loadCerts(certFile, keyFile string) (pk libtrust.PublicKey, prk libtrust.PrivateKey, err error) {
	certificate, err := tls.LoadX509KeyPair(certFile, keyFile)
	if err != nil {
		return
	}
	x509Cert, err := x509.ParseCertificate(certificate.Certificate[0])
	if err != nil {
		return
	}
	pk, err = libtrust.FromCryptoPublicKey(x509Cert.PublicKey)
	if err != nil {
		return
	}
	prk, err = libtrust.FromCryptoPrivateKey(certificate.PrivateKey)
	return
}
<|endoftext|>


<commit_before>package connmgr

import (
	"errors"
	"fmt"
	"net"
	"time"
)

// 连接配置信息
type ConnConfig struct {
	ReadTimeout  uint // seconds
	WriteTimeout uint // seconds

	WriteQueueSize uint // output queue size
}

// Conn工厂
type ConnFactory struct {
	Config    *ConnConfig // optional
	ReadQueue chan *Event // 读入队列，待处理，由外部设置
}

func (self *ConnFactory) NewConn(net_conn net.Conn) (ret *Conn, err error) {
	if self.ReadQueue == nil {
		return nil, errors.New("read queue must not be nil")
	}

	cfg := self.Config
	queue_size := uint(16)
	if cfg != nil {
		queue_size = cfg.WriteQueueSize
	}

	return &Conn{
		NetConn:      net_conn,
		Disconnected: false,
		ReadQueue:    self.ReadQueue,
		WriteQueue:   make(chan *Event, queue_size),
		Tag:          0,
	}, nil
}

// 读实现
type ConnReader struct {
	Config      *ConnConfig // optional
	FrameReader FrameReader // must be set
}

func (self *ConnReader) ReadLoop(conn *Conn, stop <-chan bool) (err error) {
	if conn == nil {
		return errors.New("conn must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if conn.ReadQueue == nil {
		return errors.New("conn read queue must not be nil")
	}
	if self.FrameReader == nil {
		return errors.New("frame reader must not be nil")
	}

	for {
		var evt = new(Event)

		cfg := self.Config
		if cfg != nil && cfg.ReadTimeout >= 1 {
			d := time.Second * time.Duration(cfg.ReadTimeout)
			conn.NetConn.SetReadDeadline(time.Now().Add(d))
		}

		if evt.Frame, err = self.FrameReader.ReadFrame(conn); err != nil {
			return
		}

		if cfg != nil {
			conn.NetConn.SetReadDeadline(time.Time{})
		}

		if evt.Frame == nil {
			return errors.New("ReadFrame returns nil Frame")
		}

		if data := evt.Frame.FrameData(); data != nil {
			data.Rewind()
		}

		conn.ReadQueue <- evt
	}
	return
}

// 写实现
type ConnWriter struct {
	Config      *ConnConfig // optional
	FrameWriter FrameWriter // must be set
}

func (self *ConnWriter) WriteLoop(conn *Conn, stop <-chan bool) (err error) {
	if conn == nil {
		return errors.New("conn must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if conn.WriteQueue == nil {
		return errors.New("conn write queue must not be nil")
	}
	if self.FrameWriter == nil {
		return errors.New("frame writer must not be nil")
	}

	for {
		select {
		case evt, ok := <-conn.WriteQueue:
			if !ok {
				return errors.New("queue closed")
			}

			cfg := self.Config
			if cfg != nil && cfg.WriteTimeout >= 1 {
				d := time.Second * time.Duration(cfg.WriteTimeout)
				conn.NetConn.SetWriteDeadline(time.Now().Add(d))
			}

			if evt.MsgType == MESSAGE {
				if err = self.FrameWriter.WriteFrame(conn, evt.Frame); err != nil {
					return
				}
			}

			if cfg != nil {
				conn.NetConn.SetWriteDeadline(time.Time{})
			}

		case _, _ = <-stop:
			return
		}
	}

	return
}

// 连接处理入口
type ConnHandler struct {
	Reader Reader
	Writer Writer
	Stop   chan bool
}

func (self *ConnHandler) HandleConn(conn *Conn, stop <-chan bool) (err error) {
	if conn == nil {
		return errors.New("conn must not be nil")
	}
	if conn.ReadQueue == nil {
		return errors.New("conn read queue must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if self.Reader == nil {
		return errors.New("Reader must not be nil")
	}
	if self.Writer == nil {
		return errors.New("Writer must not be nil")
	}

	var sigstop = self.Stop
	if sigstop == nil {
		sigstop = make(chan bool, 0)
	}

	// connected event
	conn.ReadQueue <- &Event{CONNECTED, conn, nil}
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprint(e))
		}
		if sigstop != nil {
			close(sigstop)
		}

		// disconnect event
		conn.ReadQueue <- &Event{DISCONNECTED, conn, nil}
		conn.Close()
	}()

	// writer loop
	go self.Writer.WriteLoop(conn, sigstop)

	// reader loop
	if err = self.Reader.ReadLoop(conn, sigstop); err != nil {
		return
	}

	return
}

// 写分发
type ConnWriteDispatcher struct {
}

func (self *ConnWriteDispatcher) DispatchLoop(queue <-chan *Event, stop <-chan bool) (err error) {
	if queue == nil {
		return errors.New("write dispatch queue must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}

	for {
		select {
		case evt, ok := <-queue:
			if !ok {
				return errors.New("queue closed")
			}

			if !evt.Conn.Disconnected {
				evt.Conn.WriteQueue <- evt
			} else {
				// drop it
			}
		}
	}
	return
}

// 接收循环
type ConnAcceptor struct {
	Handler Handler
	Factory Factory

	SyncHandler bool // false: accept & async process
}

func (self *ConnAcceptor) AcceptLoop(listener *net.TCPListener, stop <-chan bool) (err error) {
	if listener == nil {
		return errors.New("listener must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if self.Handler == nil {
		return errors.New("Handler must not be nil")
	}
	if self.Factory == nil {
		return errors.New("Factory must not be nil")
	}

	var net_conn net.Conn
	for {
		net_conn, err = listener.Accept()
		if err != nil {
			return
		}

		if conn, _ := self.Factory.NewConn(net_conn); conn != nil {
			if self.SyncHandler {
				self.Handler.HandleConn(conn, stop)
			} else {
				go self.Handler.HandleConn(conn, stop)
			}
		}
	}
}
<commit_msg>ConnHandler use stop param now.<commit_after>package connmgr

import (
	"errors"
	"fmt"
	"net"
	"time"
)

// 连接配置信息
type ConnConfig struct {
	ReadTimeout  uint // seconds
	WriteTimeout uint // seconds

	WriteQueueSize uint // output queue size
}

// Conn工厂
type ConnFactory struct {
	Config    *ConnConfig // optional
	ReadQueue chan *Event // 读入队列，待处理，由外部设置
}

func (self *ConnFactory) NewConn(net_conn net.Conn) (ret *Conn, err error) {
	if self.ReadQueue == nil {
		return nil, errors.New("read queue must not be nil")
	}

	cfg := self.Config
	queue_size := uint(16)
	if cfg != nil {
		queue_size = cfg.WriteQueueSize
	}

	return &Conn{
		NetConn:      net_conn,
		Disconnected: false,
		ReadQueue:    self.ReadQueue,
		WriteQueue:   make(chan *Event, queue_size),
		Tag:          0,
	}, nil
}

// 读实现
type ConnReader struct {
	Config      *ConnConfig // optional
	FrameReader FrameReader // must be set
}

func (self *ConnReader) ReadLoop(conn *Conn, stop <-chan bool) (err error) {
	if conn == nil {
		return errors.New("conn must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if conn.ReadQueue == nil {
		return errors.New("conn read queue must not be nil")
	}
	if self.FrameReader == nil {
		return errors.New("frame reader must not be nil")
	}

	for {
		var evt = new(Event)

		cfg := self.Config
		if cfg != nil && cfg.ReadTimeout >= 1 {
			d := time.Second * time.Duration(cfg.ReadTimeout)
			conn.NetConn.SetReadDeadline(time.Now().Add(d))
		}

		if evt.Frame, err = self.FrameReader.ReadFrame(conn); err != nil {
			return
		}

		if cfg != nil {
			conn.NetConn.SetReadDeadline(time.Time{})
		}

		if evt.Frame == nil {
			return errors.New("ReadFrame returns nil Frame")
		}

		if data := evt.Frame.FrameData(); data != nil {
			data.Rewind()
		}

		conn.ReadQueue <- evt
	}
	return
}

// 写实现
type ConnWriter struct {
	Config      *ConnConfig // optional
	FrameWriter FrameWriter // must be set
}

func (self *ConnWriter) WriteLoop(conn *Conn, stop <-chan bool) (err error) {
	if conn == nil {
		return errors.New("conn must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if conn.WriteQueue == nil {
		return errors.New("conn write queue must not be nil")
	}
	if self.FrameWriter == nil {
		return errors.New("frame writer must not be nil")
	}

	for {
		select {
		case evt, ok := <-conn.WriteQueue:
			if !ok {
				return errors.New("queue closed")
			}

			cfg := self.Config
			if cfg != nil && cfg.WriteTimeout >= 1 {
				d := time.Second * time.Duration(cfg.WriteTimeout)
				conn.NetConn.SetWriteDeadline(time.Now().Add(d))
			}

			if evt.MsgType == MESSAGE {
				if err = self.FrameWriter.WriteFrame(conn, evt.Frame); err != nil {
					return
				}
			}

			if cfg != nil {
				conn.NetConn.SetWriteDeadline(time.Time{})
			}

		case _, _ = <-stop:
			return
		}
	}

	return
}

// 连接处理入口
type ConnHandler struct {
	Reader Reader
	Writer Writer
}

func (self *ConnHandler) HandleConn(conn *Conn, stop <-chan bool) (err error) {
	if conn == nil {
		return errors.New("conn must not be nil")
	}
	if conn.ReadQueue == nil {
		return errors.New("conn read queue must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if self.Reader == nil {
		return errors.New("Reader must not be nil")
	}
	if self.Writer == nil {
		return errors.New("Writer must not be nil")
	}

	// connected event
	conn.ReadQueue <- &Event{CONNECTED, conn, nil}
	defer func() {
		if e := recover(); e != nil {
			err = errors.New(fmt.Sprint(e))
		}
		// disconnect event
		conn.ReadQueue <- &Event{DISCONNECTED, conn, nil}
		conn.Close()
	}()

	// writer loop
	go self.Writer.WriteLoop(conn, stop)

	// reader loop
	if err = self.Reader.ReadLoop(conn, stop); err != nil {
		return
	}

	return
}

// 写分发
type ConnWriteDispatcher struct {
}

func (self *ConnWriteDispatcher) DispatchLoop(queue <-chan *Event, stop <-chan bool) (err error) {
	if queue == nil {
		return errors.New("write dispatch queue must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}

	for {
		select {
		case evt, ok := <-queue:
			if !ok {
				return errors.New("queue closed")
			}

			if !evt.Conn.Disconnected {
				evt.Conn.WriteQueue <- evt
			} else {
				// drop it
			}
		}
	}
	return
}

// 接收循环
type ConnAcceptor struct {
	Handler Handler
	Factory Factory

	SyncHandler bool // false: accept & async process
}

func (self *ConnAcceptor) AcceptLoop(listener *net.TCPListener, stop <-chan bool) (err error) {
	if listener == nil {
		return errors.New("listener must not be nil")
	}
	if stop == nil {
		return errors.New("stop chan must not be nil")
	}
	if self.Handler == nil {
		return errors.New("Handler must not be nil")
	}
	if self.Factory == nil {
		return errors.New("Factory must not be nil")
	}

	var net_conn net.Conn
	for {
		net_conn, err = listener.Accept()
		if err != nil {
			return
		}

		if conn, _ := self.Factory.NewConn(net_conn); conn != nil {
			if self.SyncHandler {
				self.Handler.HandleConn(conn, stop)
			} else {
				go self.Handler.HandleConn(conn, stop)
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Package tchannel implements a YARPC transport based on the TChannel
// protocol. The TChannel transport provides support for Unary RPCs only.
//
// Usage
//
// A ChannelTransport must be constructed to use this transport. You can
// provide an existing TChannel Channel to construct the Channel transport.
//
// 	ch := getTChannelChannel()
// 	tchannelTransport, err := tchannel.NewChannelTransport(tchannel.WithChannel(ch))
//
// Alternatively, you can let YARPC own and manage the TChannel Channel for
// you by providing the service name. Note that this is the name of the local
// service, not the name of the service you will be sending requests to.
//
// 	tchannelTransport, err := tchannel.NewChannelTransport(tchannel.ServiceName("myservice"))
//
// To serve a YARPC application over TChannel, pass a TChannel inbound in your
// yarpc.Config.
//
// 	myInbound := tchannelTransport.NewInbound()
// 	dispatcher := yarpc.NewDispatcher(yarpc.Config{
// 		Name: "myservice",
// 		Inbounds: yarpc.Inbounds{myInbound},
// 	})
//
// To make requests to a YARPC application that supports TChannel, pass a
// TChannel outbound in your yarpc.Config.
//
// 	myserviceOutbound := tchannelTransport.NewOutbound()
// 	dispatcher := yarpc.NewDispatcher(yarpc.Config{
// 		Name: "myservice",
// 		Outbounds: yarpc.OUtbounds{
// 			"outboundservice": {Unary: myserviceOutbound},
// 		},
// 	})
//
// Configuration
//
// A TChannel transport may be configured using YARPC's configuration system.
// See TransportConfig, InboundConfig, and OutboundConfig for details on the
// different configuration parameters supported by this transport.
package tchannel
<commit_msg>Update doc.go (#1427)<commit_after>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

// Package tchannel implements a YARPC transport based on the TChannel
// protocol. The TChannel transport provides support for Unary RPCs only.
//
// Usage
//
// A ChannelTransport must be constructed to use this transport. You can
// provide an existing TChannel Channel to construct the Channel transport.
//
// 	ch := getTChannelChannel()
// 	tchannelTransport, err := tchannel.NewChannelTransport(tchannel.WithChannel(ch))
//
// Alternatively, you can let YARPC own and manage the TChannel Channel for
// you by providing the service name. Note that this is the name of the local
// service, not the name of the service you will be sending requests to.
//
// 	tchannelTransport, err := tchannel.NewChannelTransport(tchannel.ServiceName("myservice"))
//
// To serve a YARPC application over TChannel, pass a TChannel inbound in your
// yarpc.Config.
//
// 	myInbound := tchannelTransport.NewInbound()
// 	dispatcher := yarpc.NewDispatcher(yarpc.Config{
// 		Name: "myservice",
// 		Inbounds: yarpc.Inbounds{myInbound},
// 	})
//
// To make requests to a YARPC application that supports TChannel, pass a
// TChannel outbound in your yarpc.Config.
//
// 	myserviceOutbound := tchannelTransport.NewOutbound()
// 	dispatcher := yarpc.NewDispatcher(yarpc.Config{
// 		Name: "myservice",
// 		Outbounds: yarpc.Outbounds{
// 			"outboundservice": {Unary: myserviceOutbound},
// 		},
// 	})
//
// Configuration
//
// A TChannel transport may be configured using YARPC's configuration system.
// See TransportConfig, InboundConfig, and OutboundConfig for details on the
// different configuration parameters supported by this transport.
package tchannel
<|endoftext|>


<commit_before>package config

import (
	"encoding/json"
	"fmt"
	"github.com/op/go-logging"
	"github.com/sellweek/TOGY/util"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"time"
)

const timeFormat = "15:04"
const dateFormat = "2006-1-2"

//Cofiguration as unmarshaled from JSON.
//Times are specified with minute precision in a format like this: 15:04
//Dates should have this format: 2010-3-12
//These two intermediate structs could be replaced by a map[string]interface{},
//but I tried to do it and the simplification that it affords
//would be outweighed by the need to use type assertions everywhere
//and some strange type errors.
type localConfig struct {
	PowerPoint   string
	UpdateURL    string
	LogFile      string
	Name         string
	CentralPath  string
	BroadcastDir string
}

type centralConfig struct {
	StandardTimeSettings map[string]string
	OverrideDays         map[string]map[string]string
	OverrideOn           bool
	OverrideOff          bool
	Weekends             bool
	UpdateInterval       int
}

//The real configuration struct.
type Config struct {
	Presentation         string
	UpdatePath           string
	StandardTimeSettings TimeConfig
	OverrideDays         map[int64]TimeConfig
	OverrideOn           bool
	OverrideOff          bool
	PowerPoint           string
	UpdateURL            string
	UpdateInterval       time.Duration
	Name                 string
	CentralPath          string
	Weekends             bool
	BroadcastDir         string
	*logging.Logger
}

//Struct representing time when the TV should be running.
type TimeConfig struct {
	TurnOn  time.Time
	TurnOff time.Time
}

//Loads configuration file from the specified path.
func getLocal(path string) (l localConfig, err error) {
	err = getJSONFile(path, &l)
	return
}

func (l localConfig) GetCentral() (c centralConfig, err error) {
	err = getJSONFile(l.CentralPath, &c)
	return
}

func Get(path string) (conf *Config, err error) {
	l, err := getLocal(path)
	if err != nil {
		return
	}
	c, err := l.GetCentral()
	if err != nil {
		return
	}
	conf, err = joinConfigs(l, c)
	return
}

func ColdStart(path string) (conf *Config, err error) {
	l, err := getLocal(path)
	if err != nil {
		return
	}
	conf = new(Config)
	joinLocal(l, conf)
	return
}

func joinLocal(l localConfig, c *Config) {
	c.PowerPoint = l.PowerPoint
	c.UpdateURL = l.UpdateURL
	c.Name = l.Name
	c.BroadcastDir = l.BroadcastDir
	c.CentralPath = l.CentralPath
	logOut, err := os.OpenFile(l.LogFile, os.O_APPEND, os.ModePerm)
	if err != nil {
		logOut, err = os.Create(l.LogFile)
		if err != nil {
			fmt.Println(err)
			logOut = os.Stderr
		}
	}
	//GetLogger always returns nil error so we can safely ignore it.
	logr, _ := logging.GetLogger("TOGY")
	c.Logger = logr
	backend := logging.NewLogBackend(logOut, "", log.LstdFlags|log.Lshortfile)
	logging.SetBackend(backend)
	logging.SetLevel(logging.NOTICE, "TOGY")
}

func joinCentral(c centralConfig, conf *Config) (err error) {
	conf.StandardTimeSettings, err = makeTimeConfig(c.StandardTimeSettings)
	conf.OverrideDays = make(map[int64]TimeConfig)
	if err != nil {
		return
	}
	for k, v := range c.OverrideDays {
		var (
			key int64
			t   time.Time
		)
		t, err = time.Parse(dateFormat, k)
		if err != nil {
			return
		}
		key = util.NormalizeDate(t).Unix()

		conf.OverrideDays[key], err = makeTimeConfig(v)
		if err != nil {
			return
		}
	}

	conf.UpdateInterval, err = time.ParseDuration(strconv.Itoa(c.UpdateInterval) + "s")

	conf.Weekends = c.Weekends
	conf.OverrideOn = c.OverrideOn
	conf.OverrideOff = c.OverrideOff
	return
}

//Converts jsonConfig to Config.
func joinConfigs(l localConfig, c centralConfig) (conf *Config, err error) {
	conf = new(Config)
	joinLocal(l, conf)
	err = joinCentral(c, conf)
	return
}

//Converts map of strings to strings (formatted as time) to a TimeConfig struct.
func makeTimeConfig(times map[string]string) (tc TimeConfig, err error) {
	on, err := time.Parse(timeFormat, times["TurnOn"])
	if err != nil {
		return
	}

	off, err := time.Parse(timeFormat, times["TurnOff"])
	if err != nil {
		return
	}

	tc.TurnOn = util.NormalizeTime(on)
	tc.TurnOff = util.NormalizeTime(off)

	return
}

func getJSONFile(path string, d interface{}) (err error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	err = json.Unmarshal(data, d)
	if err != nil {
		return
	}
	return
}
<commit_msg>Added Timestamp field to config<commit_after>package config

import (
	"encoding/json"
	"fmt"
	"github.com/op/go-logging"
	"github.com/sellweek/TOGY/util"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"time"
)

const timeFormat = "15:04"
const dateFormat = "2006-1-2"

//Cofiguration as unmarshaled from JSON.
//Times are specified with minute precision in a format like this: 15:04
//Dates should have this format: 2010-3-12
//These two intermediate structs could be replaced by a map[string]interface{},
//but I tried to do it and the simplification that it affords
//would be outweighed by the need to use type assertions everywhere
//and some strange type errors.
type localConfig struct {
	PowerPoint   string
	UpdateURL    string
	LogFile      string
	Name         string
	CentralPath  string
	BroadcastDir string
}

type centralConfig struct {
	StandardTimeSettings map[string]string
	OverrideDays         map[string]map[string]string
	OverrideOn           bool
	OverrideOff          bool
	Weekends             bool
	UpdateInterval       int
	Timestamp            int64
}

//The real configuration struct.
type Config struct {
	Presentation         string
	UpdatePath           string
	StandardTimeSettings TimeConfig
	OverrideDays         map[int64]TimeConfig
	OverrideOn           bool
	OverrideOff          bool
	PowerPoint           string
	UpdateURL            string
	UpdateInterval       time.Duration
	Name                 string
	CentralPath          string
	Weekends             bool
	BroadcastDir         string
	Timestamp            int64
	*logging.Logger
}

//Struct representing time when the TV should be running.
type TimeConfig struct {
	TurnOn  time.Time
	TurnOff time.Time
}

//Loads configuration file from the specified path.
func getLocal(path string) (l localConfig, err error) {
	err = getJSONFile(path, &l)
	return
}

func (l localConfig) GetCentral() (c centralConfig, err error) {
	err = getJSONFile(l.CentralPath, &c)
	return
}

func Get(path string) (conf *Config, err error) {
	l, err := getLocal(path)
	if err != nil {
		return
	}
	c, err := l.GetCentral()
	if err != nil {
		return
	}
	conf, err = joinConfigs(l, c)
	return
}

func ColdStart(path string) (conf *Config, err error) {
	l, err := getLocal(path)
	if err != nil {
		return
	}
	conf = new(Config)
	joinLocal(l, conf)
	return
}

func joinLocal(l localConfig, c *Config) {
	c.PowerPoint = l.PowerPoint
	c.UpdateURL = l.UpdateURL
	c.Name = l.Name
	c.BroadcastDir = l.BroadcastDir
	c.CentralPath = l.CentralPath
	logOut, err := os.OpenFile(l.LogFile, os.O_APPEND, os.ModePerm)
	if err != nil {
		logOut, err = os.Create(l.LogFile)
		if err != nil {
			fmt.Println(err)
			logOut = os.Stderr
		}
	}
	//GetLogger always returns nil error so we can safely ignore it.
	logr, _ := logging.GetLogger("TOGY")
	c.Logger = logr
	backend := logging.NewLogBackend(logOut, "", log.LstdFlags|log.Lshortfile)
	logging.SetBackend(backend)
	logging.SetLevel(logging.NOTICE, "TOGY")
}

func joinCentral(c centralConfig, conf *Config) (err error) {
	conf.StandardTimeSettings, err = makeTimeConfig(c.StandardTimeSettings)
	conf.OverrideDays = make(map[int64]TimeConfig)
	if err != nil {
		return
	}
	for k, v := range c.OverrideDays {
		var (
			key int64
			t   time.Time
		)
		t, err = time.Parse(dateFormat, k)
		if err != nil {
			return
		}
		key = util.NormalizeDate(t).Unix()

		conf.OverrideDays[key], err = makeTimeConfig(v)
		if err != nil {
			return
		}
	}

	conf.UpdateInterval, err = time.ParseDuration(strconv.Itoa(c.UpdateInterval) + "s")

	conf.Weekends = c.Weekends
	conf.OverrideOn = c.OverrideOn
	conf.OverrideOff = c.OverrideOff
	conf.Timestamp = c.Timestamp
	return
}

//Converts jsonConfig to Config.
func joinConfigs(l localConfig, c centralConfig) (conf *Config, err error) {
	conf = new(Config)
	joinLocal(l, conf)
	err = joinCentral(c, conf)
	return
}

//Converts map of strings to strings (formatted as time) to a TimeConfig struct.
func makeTimeConfig(times map[string]string) (tc TimeConfig, err error) {
	on, err := time.Parse(timeFormat, times["TurnOn"])
	if err != nil {
		return
	}

	off, err := time.Parse(timeFormat, times["TurnOff"])
	if err != nil {
		return
	}

	tc.TurnOn = util.NormalizeTime(on)
	tc.TurnOff = util.NormalizeTime(off)

	return
}

func getJSONFile(path string, d interface{}) (err error) {
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return
	}
	err = json.Unmarshal(data, d)
	if err != nil {
		return
	}
	return
}
<|endoftext|>