<commit_before>// level game server
// https://github.com/heynemann/level
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Bernardo Heynemann <heynemann@gmail.com>

package service

import (
	"fmt"
	"os"

	"github.com/heynemann/level/extensions/serviceRegistry"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/uber-go/zap"
)

var logLevels = map[string]int{
	"debug": int(zap.DebugLevel),
	"info":  int(zap.InfoLevel),
	"warn":  int(zap.WarnLevel),
	"error": int(zap.ErrorLevel),
	"panic": int(zap.PanicLevel),
	"fatal": int(zap.FatalLevel),
}

//Service describes a server interface
type Service interface {
	SetServerFlags(cmd *cobra.Command)
	SetDefaultConfigurations(*viper.Viper)
}

//Server identifies a service Server
type Server struct {
	Logger          zap.Logger
	ConfigPath      string
	Config          *viper.Viper
	ServiceRegistry *registry.ServiceRegistry
	Service         registry.Service
	ServerService   Service
	LogLevel        string
	Quit            chan bool
}

//NewServer creates an configures a new server instance
func NewServer(serv registry.Service, logger zap.Logger, configPath string) (*Server, error) {
	var service Service
	var ok bool
	if service, ok = serv.(Service); !ok {
		return nil, fmt.Errorf(
			"Service %s does not implement interface Service. Please refer to the docs.",
			serv.GetServiceDetails().Name,
		)
	}

	s := &Server{Service: serv, ServerService: service, Logger: logger, ConfigPath: configPath}
	err := s.Configure()
	if err != nil {
		return nil, err
	}

	return s, nil
}

func (s *Server) initializeServiceRegistry() error {
	natsURL := s.Config.GetString("services.nats.URL")
	l := s.Logger.With(
		zap.String("operation", "initializeServiceRegistry"),
		zap.String("natsURL", natsURL),
	)

	l.Debug("Initializing registry...")
	sr, err := registry.NewServiceRegistry(
		natsURL,
		s.Logger,
	)
	if err != nil {
		l.Error("Error initializing service registry.", zap.Error(err))
		return err
	}

	l.Info("Service registry initialized successfully.")
	s.ServiceRegistry = sr

	l.Debug("Registering service...")
	s.ServiceRegistry.Register(s.Service)
	l.Info("Service registered successfully.")

	return nil
}

//Configure the server
func (s *Server) Configure() error {
	s.Config = viper.New()
	s.SetDefaultConfiguration()
	s.ServerService.SetDefaultConfigurations(s.Config)

	s.LoadConfiguration(s.ConfigPath)
	err := s.initializeServiceRegistry()
	return err
}

//SetDefaultConfiguration options
func (s *Server) SetDefaultConfiguration() {
	s.Logger.Debug("Setting default configuration")
	s.Config.SetDefault("services.nats.url", "nats://localhost:4222")

	s.Config.SetDefault("services.redis.host", "localhost")
	s.Config.SetDefault("services.redis.port", 4444)
}

//LoadConfiguration from filesystem
func (s *Server) LoadConfiguration(configPath string) {
	if configPath == "" {
		s.Logger.Panic("Could not load configuration due to empty config path.")
		os.Exit(-1)
	}

	s.Config.SetConfigFile(configPath)
	s.Config.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := s.Config.ReadInConfig(); err == nil {
		s.Logger.Info("Loaded configuration file.", zap.String("configPath", s.Config.ConfigFileUsed()))
	}
}

//Listen to incoming messages
func (s *Server) Listen() {
	s.Logger.Info("Service listening for messages...")
	s.Quit = make(chan bool)

	for {
		select {
		case <-s.Quit:
			return
		}
	}
}

//Close the server running
func (s *Server) Close() {
	s.Quit <- true
}

func (s *Server) setServerFlags(cmd *cobra.Command) {
	cmd.PersistentFlags().StringVarP(&s.ConfigPath, "config", "c", "./config/local.yaml", "configuration file to initialize this server with")
	cmd.PersistentFlags().StringVarP(&s.LogLevel, "loglevel", "l", "warn", "default log level for this backend server")
}

func getCommandFor(s *Server) *cobra.Command {
	details := s.Service.GetServiceDetails()

	return &cobra.Command{
		Use:   details.Name,
		Short: details.Description,
		Long:  details.Description,

		Run: func(cmd *cobra.Command, args []string) {
			s.Logger = s.Logger.With(
				zap.String("serverName", details.Name),
				zap.String("serverDescription", details.Description),
				zap.String("serverVersion", details.Version),
				zap.String("serverID", details.ServiceID),
			)

			s.setServerFlags(cmd)
			s.ServerService.SetServerFlags(cmd)

			s.Logger.Debug("Running backend server")
			s.Listen()
		},
	}
}

//Run a new Service
func Run(serv registry.Service, logger zap.Logger, configPath string) error {
	s, err := NewServer(serv, logger, configPath)
	if err != nil {
		s.Logger.Error("Backend server finalized with error!", zap.Error(err))
		os.Exit(-1)
	}

	cmd := getCommandFor(s)
	if err = cmd.Execute(); err != nil {
		s.Logger.Error("Backend server finalized with error!", zap.Error(err))
		os.Exit(-1)
	}

	return nil
}
<commit_msg>Running multiple services in the same server<commit_after>// level game server
// https://github.com/heynemann/level
//
// Licensed under the MIT license:
// http://www.opensource.org/licenses/mit-license
// Copyright © 2016 Bernardo Heynemann <heynemann@gmail.com>

package service

import (
	"fmt"
	"os"

	"github.com/heynemann/level/extensions/serviceRegistry"
	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/uber-go/zap"
)

var logLevels = map[string]int{
	"debug": int(zap.DebugLevel),
	"info":  int(zap.InfoLevel),
	"warn":  int(zap.WarnLevel),
	"error": int(zap.ErrorLevel),
	"panic": int(zap.PanicLevel),
	"fatal": int(zap.FatalLevel),
}

//Service describes a server interface
type Service interface {
	SetServerFlags(cmd *cobra.Command)
	SetDefaultConfigurations(*viper.Viper)
}

//Server identifies a service Server
type Server struct {
	Logger          zap.Logger
	ConfigPath      string
	Config          *viper.Viper
	ServiceRegistry *registry.ServiceRegistry
	Service         registry.Service
	ServerService   Service
	LogLevel        string
	Quit            chan bool
}

//NewServer creates an configures a new server instance
func NewServer(serv registry.Service, logger zap.Logger, configPath string) (*Server, error) {
	var service Service
	var ok bool
	if service, ok = serv.(Service); !ok {
		return nil, fmt.Errorf(
			"Service %s does not implement interface Service. Please refer to the docs.",
			serv.GetServiceDetails().Name,
		)
	}

	s := &Server{Service: serv, ServerService: service, Logger: logger, ConfigPath: configPath}
	err := s.Configure()
	if err != nil {
		return nil, err
	}

	return s, nil
}

func (s *Server) initializeServiceRegistry() error {
	natsURL := s.Config.GetString("services.nats.URL")
	l := s.Logger.With(
		zap.String("operation", "initializeServiceRegistry"),
		zap.String("natsURL", natsURL),
	)

	l.Debug("Initializing registry...")
	sr, err := registry.NewServiceRegistry(
		natsURL,
		s.Logger,
	)
	if err != nil {
		l.Error("Error initializing service registry.", zap.Error(err))
		return err
	}

	l.Info("Service registry initialized successfully.")
	s.ServiceRegistry = sr

	l.Debug("Registering service...")
	s.ServiceRegistry.Register(s.Service)
	l.Info("Service registered successfully.")

	return nil
}

//Configure the server
func (s *Server) Configure() error {
	s.Config = viper.New()
	s.SetDefaultConfiguration()
	s.ServerService.SetDefaultConfigurations(s.Config)

	s.LoadConfiguration(s.ConfigPath)
	err := s.initializeServiceRegistry()
	return err
}

//SetDefaultConfiguration options
func (s *Server) SetDefaultConfiguration() {
	s.Logger.Debug("Setting default configuration")
	s.Config.SetDefault("services.nats.url", "nats://localhost:4222")

	s.Config.SetDefault("services.redis.host", "localhost")
	s.Config.SetDefault("services.redis.port", 4444)
}

//LoadConfiguration from filesystem
func (s *Server) LoadConfiguration(configPath string) {
	if configPath == "" {
		s.Logger.Panic("Could not load configuration due to empty config path.")
		os.Exit(-1)
	}

	s.Config.SetConfigFile(configPath)
	s.Config.AutomaticEnv() // read in environment variables that match

	// If a config file is found, read it in.
	if err := s.Config.ReadInConfig(); err == nil {
		s.Logger.Info("Loaded configuration file.", zap.String("configPath", s.Config.ConfigFileUsed()))
	}
}

//Listen to incoming messages
func (s *Server) Listen() {
	s.Logger.Info("Service listening for messages...")
	s.Quit = make(chan bool)

	for {
		select {
		case <-s.Quit:
			return
		}
	}
}

//Close the server running
func (s *Server) Close() {
	s.Quit <- true
}

func (s *Server) setServerFlags(cmd *cobra.Command) {
	cmd.PersistentFlags().StringVarP(&s.ConfigPath, "config", "c", "./config/local.yaml", "configuration file to initialize this server with")
	cmd.PersistentFlags().StringVarP(&s.LogLevel, "loglevel", "l", "warn", "default log level for this backend server")
}

func getCommandFor(s *Server) *cobra.Command {
	details := s.Service.GetServiceDetails()

	return &cobra.Command{
		Use:   details.Name,
		Short: details.Description,
		Long:  details.Description,

		Run: func(cmd *cobra.Command, args []string) {
			s.Logger = s.Logger.With(
				zap.String("serverName", details.Name),
				zap.String("serverDescription", details.Description),
				zap.String("serverVersion", details.Version),
				zap.String("serverID", details.ServiceID),
			)

			s.setServerFlags(cmd)
			s.ServerService.SetServerFlags(cmd)

			s.Logger.Debug("Running backend server")
			s.Listen()
		},
	}
}

//RunMultipleServices in a single server
func RunMultipleServices(logger zap.Logger, configPath string, services ...registry.Service) error {
	if len(services) == 0 {
		return fmt.Errorf("Can't configure server with no services.")
	}
	serv := services[0]

	s, err := NewServer(serv, logger, configPath)
	if err != nil {
		s.Logger.Error("Backend server finalized with error!", zap.Error(err))
		os.Exit(-1)
	}

	for i := 1; i < len(services); i++ {
		s.ServiceRegistry.Register(services[i])
	}

	cmd := getCommandFor(s)
	if err = cmd.Execute(); err != nil {
		s.Logger.Error("Backend server finalized with error!", zap.Error(err))
		os.Exit(-1)
	}

	return nil
}

//Run a new Service
func Run(serv registry.Service, logger zap.Logger, configPath string) error {
	return RunMultipleServices(logger, configPath, serv)
}
<|endoftext|>


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

// Tests for client.go

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"testing"
)

var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
	w.Header().Set("Last-Modified", "sometime")
	fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
})

func TestClient(t *testing.T) {
	ts := httptest.NewServer(robotsTxtHandler)
	defer ts.Close()

	r, err := Get(ts.URL)
	var b []byte
	if err == nil {
		b, err = ioutil.ReadAll(r.Body)
		r.Body.Close()
	}
	if err != nil {
		t.Error(err)
	} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
		t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
	}
}

func TestClientHead(t *testing.T) {
	ts := httptest.NewServer(robotsTxtHandler)
	defer ts.Close()

	r, err := Head(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := r.Header["Last-Modified"]; !ok {
		t.Error("Last-Modified header not found.")
	}
}

type recordingTransport struct {
	req *Request
}

func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err os.Error) {
	t.req = req
	return nil, os.NewError("dummy impl")
}

func TestGetRequestFormat(t *testing.T) {
	tr := &recordingTransport{}
	client := &Client{Transport: tr}
	url := "http://dummy.faketld/"
	client.Get(url) // Note: doesn't hit network
	if tr.req.Method != "GET" {
		t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
	}
	if tr.req.URL.String() != url {
		t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
	}
	if tr.req.Header == nil {
		t.Errorf("expected non-nil request Header")
	}
}

func TestRedirects(t *testing.T) {
	var ts *httptest.Server
	ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		n, _ := strconv.Atoi(r.FormValue("n"))
		// Test Referer header. (7 is arbitrary position to test at)
		if n == 7 {
			if g, e := r.Referer, ts.URL+"/?n=6"; e != g {
				t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g)
			}
		}
		if n < 15 {
			Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
			return
		}
		fmt.Fprintf(w, "n=%d", n)
	}))
	defer ts.Close()

	c := &Client{}
	_, err := c.Get(ts.URL)
	if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with default client Get, expected error %q, got %q", e, g)
	}

	// HEAD request should also have the ability to follow redirects.
	_, err = c.Head(ts.URL)
	if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with default client Head, expected error %q, got %q", e, g)
	}

	// Do should also follow redirects.
	greq, _ := NewRequest("GET", ts.URL, nil)
	_, err = c.Do(greq)
	if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with default client Do, expected error %q, got %q", e, g)
	}

	var checkErr os.Error
	var lastVia []*Request
	c = &Client{CheckRedirect: func(_ *Request, via []*Request) os.Error {
		lastVia = via
		return checkErr
	}}
	res, err := c.Get(ts.URL)
	finalUrl := res.Request.URL.String()
	if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with custom client, expected error %q, got %q", e, g)
	}
	if !strings.HasSuffix(finalUrl, "/?n=15") {
		t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl)
	}
	if e, g := 15, len(lastVia); e != g {
		t.Errorf("expected lastVia to have contained %d elements; got %d", e, g)
	}

	checkErr = os.NewError("no redirects allowed")
	res, err = c.Get(ts.URL)
	finalUrl = res.Request.URL.String()
	if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)
	}
}
<commit_msg>http: Client test for streaming responses (no code changes)<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Tests for client.go

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
	"testing"
)

var robotsTxtHandler = HandlerFunc(func(w ResponseWriter, r *Request) {
	w.Header().Set("Last-Modified", "sometime")
	fmt.Fprintf(w, "User-agent: go\nDisallow: /something/")
})

func TestClient(t *testing.T) {
	ts := httptest.NewServer(robotsTxtHandler)
	defer ts.Close()

	r, err := Get(ts.URL)
	var b []byte
	if err == nil {
		b, err = ioutil.ReadAll(r.Body)
		r.Body.Close()
	}
	if err != nil {
		t.Error(err)
	} else if s := string(b); !strings.HasPrefix(s, "User-agent:") {
		t.Errorf("Incorrect page body (did not begin with User-agent): %q", s)
	}
}

func TestClientHead(t *testing.T) {
	ts := httptest.NewServer(robotsTxtHandler)
	defer ts.Close()

	r, err := Head(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if _, ok := r.Header["Last-Modified"]; !ok {
		t.Error("Last-Modified header not found.")
	}
}

type recordingTransport struct {
	req *Request
}

func (t *recordingTransport) RoundTrip(req *Request) (resp *Response, err os.Error) {
	t.req = req
	return nil, os.NewError("dummy impl")
}

func TestGetRequestFormat(t *testing.T) {
	tr := &recordingTransport{}
	client := &Client{Transport: tr}
	url := "http://dummy.faketld/"
	client.Get(url) // Note: doesn't hit network
	if tr.req.Method != "GET" {
		t.Errorf("expected method %q; got %q", "GET", tr.req.Method)
	}
	if tr.req.URL.String() != url {
		t.Errorf("expected URL %q; got %q", url, tr.req.URL.String())
	}
	if tr.req.Header == nil {
		t.Errorf("expected non-nil request Header")
	}
}

func TestRedirects(t *testing.T) {
	var ts *httptest.Server
	ts = httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		n, _ := strconv.Atoi(r.FormValue("n"))
		// Test Referer header. (7 is arbitrary position to test at)
		if n == 7 {
			if g, e := r.Referer, ts.URL+"/?n=6"; e != g {
				t.Errorf("on request ?n=7, expected referer of %q; got %q", e, g)
			}
		}
		if n < 15 {
			Redirect(w, r, fmt.Sprintf("/?n=%d", n+1), StatusFound)
			return
		}
		fmt.Fprintf(w, "n=%d", n)
	}))
	defer ts.Close()

	c := &Client{}
	_, err := c.Get(ts.URL)
	if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with default client Get, expected error %q, got %q", e, g)
	}

	// HEAD request should also have the ability to follow redirects.
	_, err = c.Head(ts.URL)
	if e, g := "Head /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with default client Head, expected error %q, got %q", e, g)
	}

	// Do should also follow redirects.
	greq, _ := NewRequest("GET", ts.URL, nil)
	_, err = c.Do(greq)
	if e, g := "Get /?n=10: stopped after 10 redirects", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with default client Do, expected error %q, got %q", e, g)
	}

	var checkErr os.Error
	var lastVia []*Request
	c = &Client{CheckRedirect: func(_ *Request, via []*Request) os.Error {
		lastVia = via
		return checkErr
	}}
	res, err := c.Get(ts.URL)
	finalUrl := res.Request.URL.String()
	if e, g := "<nil>", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with custom client, expected error %q, got %q", e, g)
	}
	if !strings.HasSuffix(finalUrl, "/?n=15") {
		t.Errorf("expected final url to end in /?n=15; got url %q", finalUrl)
	}
	if e, g := 15, len(lastVia); e != g {
		t.Errorf("expected lastVia to have contained %d elements; got %d", e, g)
	}

	checkErr = os.NewError("no redirects allowed")
	res, err = c.Get(ts.URL)
	finalUrl = res.Request.URL.String()
	if e, g := "Get /?n=1: no redirects allowed", fmt.Sprintf("%v", err); e != g {
		t.Errorf("with redirects forbidden, expected error %q, got %q", e, g)
	}
}

func TestStreamingGet(t *testing.T) {
	say := make(chan string)
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		w.(Flusher).Flush()
		for str := range say {
			w.Write([]byte(str))
			w.(Flusher).Flush()
		}
	}))
	defer ts.Close()

	c := &Client{}
	res, err := c.Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	var buf [10]byte
	for _, str := range []string{"i", "am", "also", "known", "as", "comet"} {
		say <- str
		n, err := io.ReadFull(res.Body, buf[0:len(str)])
		if err != nil {
			t.Fatalf("ReadFull on %q: %v", str, err)
		}
		if n != len(str) {
			t.Fatalf("Receiving %q, only read %d bytes", str, n)
		}
		got := string(buf[0:n])
		if got != str {
			t.Fatalf("Expected %q, got %q", str, got)
		}
	}
	close(say)
	_, err = io.ReadFull(res.Body, buf[0:1])
	if err != os.EOF {
		t.Fatalf("at end expected EOF, got %v", err)
	}
}
<|endoftext|>


<commit_before>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		shellPath, err := pathToSkiffCore()
		if err != nil {
			return err
		}
		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\n%s\n", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.Mkdir(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	authConf := cs.config.Auth
	if authConf != nil {
		if authConf.CopyRootKeys {
			rkf, err := os.Open("/root/.ssh/authorized_keys")
			if err != nil {
				return err
			}
			_, err = io.Copy(f, rkf)
			rkf.Close()
			if err != nil {
				return err
			}
			f.WriteString("\n")
		}
		for _, key := range authConf.SSHKeys {
			f.WriteString(key)
			f.WriteString("\n")
		}
	}

	f.Sync()
	if err := os.Chown(authorizedKeysPath, uid, gid); err != nil {
		return err
	}

	containerId, err := cs.waiter.WaitForContainer(cs.config.Container)
	if err != nil {
		return err
	}

	userConfPath := path.Join(euser.HomeDir, config.UserConfigFile)
	le.WithField("path", userConfPath).Debug("Writing user config...")
	userConf := cs.config.ToConfigUserShell(containerId)
	userConfData, err := userConf.Marshal()
	if err != nil {
		return err
	}

	userConfFile, err := os.OpenFile(userConfPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	if _, err := userConfFile.Write(userConfData); err != nil {
		return err
	}
	userConfFile.Close()

	return nil
}

// Wait waits for Execute() to finish.
func (i *UserSetup) Wait() error {
	i.wg.Wait()
	return i.err
}
<commit_msg>feat: set shell if not set already<commit_after>package setup

import (
	"fmt"
	"io"
	"os"
	"os/exec"
	"os/user"
	"path"
	"strconv"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/paralin/skiff-core/config"
)

// UserSetup sets up a container.
type UserSetup struct {
	config *config.ConfigUser
	waiter ContainerWaiter
	create bool

	wg  sync.WaitGroup
	err error
}

// NewUserSetup creates a new UserSetup.
func NewUserSetup(config *config.ConfigUser, waiter ContainerWaiter, createUsers bool) *UserSetup {
	return &UserSetup{config: config, waiter: waiter, create: createUsers}
}

// Execute starts the user setup.
func (cs *UserSetup) Execute() (execError error) {
	cs.wg.Add(1)
	defer func() {
		cs.err = execError
		cs.wg.Done()
	}()

	// check if we are root
	if os.Geteuid() != 0 {
		return fmt.Errorf("Not running as root, cannot setup user %s", cs.config.Name())
	}

	conf := cs.config
	if conf.Container == "" {
		return fmt.Errorf("User %s must have container specified.", conf.Name())
	}

	conf.Container = ensureSlashPrefix(conf.Container)
	if !cs.waiter.CheckHasContainer(conf.Container) {
		return fmt.Errorf("User %s: no such container: %s", conf.Name(), conf.Container)
	}

	euser, eusererr := user.Lookup(conf.Name())
	if eusererr != nil {
		if _, ok := eusererr.(user.UnknownUserError); !ok {
			return eusererr
		}
	}

	le := log.WithField("user", conf.Name())
	shellPath, err := pathToSkiffCore()
	if err != nil {
		return err
	}
	if euser == nil {
		if !cs.create {
			return fmt.Errorf("User %s: not found, and create-users is not enabled.", conf.Name())
		}

		// attempt to create the user
		le.Debug("Creating user")
		err = execCmd(
			"adduser",
			"-D",
			//"-c",
			// fmt.Sprintf("Skiff-Core user %s", cs.config.Name()),
			// "-m",
			"-s",
			shellPath,
			cs.config.Name(),
		)
		if err != nil {
			return err
		}
		euser, err = user.Lookup(cs.config.Name())
		if err != nil {
			return err
		}
	} else {
		// Set the shell for the user
		le.WithField("path", shellPath).Debug("Setting shell")
		if err := execCmd("chsh", "-s", shellPath, cs.config.Name()); err != nil {
			return err
		}
	}

	// Set password
	if cs.config.Auth == nil || cs.config.Auth.Password == "" {
		le.Debug("Disabling password login")
		if err := execCmd("passwd", "-d", cs.config.Name()); err != nil {
			return err
		}
	} else {
		le.Debug("Setting password")
		passwd := strings.Replace(cs.config.Auth.Password, "\n", "", -1)
		passwordSet := strings.NewReader(fmt.Sprintf("%s\n%s\n", passwd, passwd))
		cmd := exec.Command("passwd", cs.config.Name())
		cmd.Stdin = passwordSet
		if err := cmd.Run(); err != nil {
			return err
		}
	}

	le.Debug("Setting up SSH keys")
	sshDir := path.Join(euser.HomeDir, ".ssh")
	authorizedKeysPath := path.Join(sshDir, "authorized_keys")
	if _, err := os.Stat(sshDir); os.IsNotExist(err) {
		if err := os.Mkdir(sshDir, 0700); err != nil {
			return err
		}
	}
	if err := os.Chmod(sshDir, 0700); err != nil {
		return err
	}
	uid, err := strconv.Atoi(euser.Uid)
	if err != nil {
		return err
	}
	gid, err := strconv.Atoi(euser.Gid)
	if err != nil {
		return err
	}
	if err := os.Chown(sshDir, uid, gid); err != nil {
		return err
	}

	f, err := os.OpenFile(authorizedKeysPath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
	if err != nil {
		return err
	}
	defer f.Close()

	authConf := cs.config.Auth
	if authConf != nil {
		if authConf.CopyRootKeys {
			rkf, err := os.Open("/root/.ssh/authorized_keys")
			if err != nil {
				return err
			}
			_, err = io.Copy(f, rkf)
			rkf.Close()
			if err != nil {
				return err
			}
			f.WriteString("\n")
		}
		for _, key := range authConf.SSHKeys {
			f.WriteString(key)
			f.WriteString("\n")
		}
	}

	f.Sync()
	if err := os.Chown(authorizedKeysPath, uid, gid); err != nil {
		return err
	}

	containerId, err := cs.waiter.WaitForContainer(cs.config.Container)
	if err != nil {
		return err
	}

	userConfPath := path.Join(euser.HomeDir, config.UserConfigFile)
	le.WithField("path", userConfPath).Debug("Writing user config...")
	userConf := cs.config.ToConfigUserShell(containerId)
	userConfData, err := userConf.Marshal()
	if err != nil {
		return err
	}

	userConfFile, err := os.OpenFile(userConfPath, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0640)
	if err != nil {
		return err
	}
	if _, err := userConfFile.Write(userConfData); err != nil {
		return err
	}
	userConfFile.Close()

	return nil
}

// Wait waits for Execute() to finish.
func (i *UserSetup) Wait() error {
	i.wg.Wait()
	return i.err
}
<|endoftext|>


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

package sort

import "testing"

func f(a []int, x int) func(int) bool {
	return func(i int) bool {
		return a[i] <= x
	}
}

var data = []int{0: -10, 1: -5, 2: 0, 3: 1, 4: 2, 5: 3, 6: 5, 7: 7, 8: 11, 9: 100, 10: 100, 11: 100, 12: 1000, 13: 10000}

var tests = []struct {
	name string
	n    int
	f    func(int) bool
	i    int
}{
	{"empty", 0, nil, 0},
	{"1 1", 1, func(i int) bool { return i <= 1 }, 0},
	{"1 false", 1, func(i int) bool { return false }, 0},
	{"1 true", 1, func(i int) bool { return true }, 0},
	{"1e9 991", 1e9, func(i int) bool { return i <= 991 }, 991},
	{"1e9 false", 1e9, func(i int) bool { return false }, 0},
	{"1e9 true", 1e9, func(i int) bool { return true }, 1e9 - 1},
	{"data -20", len(data), f(data, -20), 0},
	{"data -10", len(data), f(data, -10), 0},
	{"data -9", len(data), f(data, -9), 0},
	{"data -6", len(data), f(data, -6), 0},
	{"data -5", len(data), f(data, -5), 1},
	{"data 3", len(data), f(data, 3), 5},
	{"data 99", len(data), f(data, 99), 8},
	{"data 100", len(data), f(data, 100), 11},
	{"data 101", len(data), f(data, 101), 11},
	{"data 10000", len(data), f(data, 10000), 13},
	{"data 10001", len(data), f(data, 10001), 13},
	{"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] >= 7 }, 4},
	{"descending 7", 1e9, func(i int) bool { return 1e9-i >= 7 }, 1e9 - 7},
}

func TestSearch(t *testing.T) {
	for _, e := range tests {
		i := Search(e.n, e.f)
		if i != e.i {
			t.Errorf("%s: expected index %d; got %d", e.name, e.i, i)
		}
	}
}

// Smoke tests for convenience wrappers - not comprehensive.

var fdata = []float{0: -3.14, 1: 0, 2: 1, 3: 2, 4: 1000.7}
var sdata = []string{0: "f", 1: "foo", 2: "foobar", 3: "x"}

var wrappertests = []struct {
	name   string
	result int
	i      int
}{
	{"SearchInts", SearchInts(data, 11), 8},
	{"SearchFloats", SearchFloats(fdata, 2.1), 3},
	{"SearchStrings", SearchStrings(sdata, ""), 0},
	{"IntArray.Search", IntArray(data).Search(0), 2},
	{"FloatArray.Search", FloatArray(fdata).Search(2.0), 3},
	{"StringArray.Search", StringArray(sdata).Search("x"), 3},
}

func TestSearchWrappers(t *testing.T) {
	for _, e := range wrappertests {
		if e.result != e.i {
			t.Errorf("%s: expected index %d; got %d", e.name, e.i, e.result)
		}
	}
}
<commit_msg>sort.Search: added extra test to verify efficiency<commit_after>// Copyright 2010 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package sort

import "testing"

func f(a []int, x int) func(int) bool {
	return func(i int) bool {
		return a[i] <= x
	}
}

var data = []int{0: -10, 1: -5, 2: 0, 3: 1, 4: 2, 5: 3, 6: 5, 7: 7, 8: 11, 9: 100, 10: 100, 11: 100, 12: 1000, 13: 10000}

var tests = []struct {
	name string
	n    int
	f    func(int) bool
	i    int
}{
	{"empty", 0, nil, 0},
	{"1 1", 1, func(i int) bool { return i <= 1 }, 0},
	{"1 false", 1, func(i int) bool { return false }, 0},
	{"1 true", 1, func(i int) bool { return true }, 0},
	{"1e9 991", 1e9, func(i int) bool { return i <= 991 }, 991},
	{"1e9 false", 1e9, func(i int) bool { return false }, 0},
	{"1e9 true", 1e9, func(i int) bool { return true }, 1e9 - 1},
	{"data -20", len(data), f(data, -20), 0},
	{"data -10", len(data), f(data, -10), 0},
	{"data -9", len(data), f(data, -9), 0},
	{"data -6", len(data), f(data, -6), 0},
	{"data -5", len(data), f(data, -5), 1},
	{"data 3", len(data), f(data, 3), 5},
	{"data 99", len(data), f(data, 99), 8},
	{"data 100", len(data), f(data, 100), 11},
	{"data 101", len(data), f(data, 101), 11},
	{"data 10000", len(data), f(data, 10000), 13},
	{"data 10001", len(data), f(data, 10001), 13},
	{"descending a", 7, func(i int) bool { return []int{99, 99, 59, 42, 7, 0, -1, -1}[i] >= 7 }, 4},
	{"descending 7", 1e9, func(i int) bool { return 1e9-i >= 7 }, 1e9 - 7},
}

func TestSearch(t *testing.T) {
	for _, e := range tests {
		i := Search(e.n, e.f)
		if i != e.i {
			t.Errorf("%s: expected index %d; got %d", e.name, e.i, i)
		}
	}
}

// log2 computes the binary logarithm of x, rounded up to the next integer.
// (log2(0) == 0, log2(1) == 0, log2(2) == 1, log2(3) == 2, etc.)
//
func log2(x int) int {
	n := 0
	for p := 1; p < x; p += p {
		// p == 2**n
		n++
	}
	// p/2 < x <= p == 2**n
	return n
}

func TestSearchEfficiency(t *testing.T) {
	n := 100
	step := 1
	for exp := 2; exp < 10; exp++ {
		// n == 10**exp
		// step == 10**(exp-2)
		max := log2(n)
		for x := 0; x < n; x += step {
			count := 0
			i := Search(n, func(i int) bool { count++; return i <= x })
			if i != x {
				t.Errorf("n = %d: expected index %d; got %d", n, x, i)
			}
			if count > max {
				t.Errorf("n = %d, x = %d: expected <= %d calls; got %d", n, x, max, count)
			}
		}
		n *= 10
		step *= 10
	}
}

// Smoke tests for convenience wrappers - not comprehensive.

var fdata = []float{0: -3.14, 1: 0, 2: 1, 3: 2, 4: 1000.7}
var sdata = []string{0: "f", 1: "foo", 2: "foobar", 3: "x"}

var wrappertests = []struct {
	name   string
	result int
	i      int
}{
	{"SearchInts", SearchInts(data, 11), 8},
	{"SearchFloats", SearchFloats(fdata, 2.1), 3},
	{"SearchStrings", SearchStrings(sdata, ""), 0},
	{"IntArray.Search", IntArray(data).Search(0), 2},
	{"FloatArray.Search", FloatArray(fdata).Search(2.0), 3},
	{"StringArray.Search", StringArray(sdata).Search("x"), 3},
}

func TestSearchWrappers(t *testing.T) {
	for _, e := range wrappertests {
		if e.result != e.i {
			t.Errorf("%s: expected index %d; got %d", e.name, e.i, e.result)
		}
	}
}
<|endoftext|>


<commit_before>package timer

import (
	"github.com/bmizerany/assert"
	"doozer/store"
	"testing"
	"time"
	"strconv"
)

const (
	testPattern = "/timer/**"
)

func encodeTimer(path string, offset int64) string {
	future := time.Nanoseconds() + offset
	muta := store.MustEncodeSet(
		path,
		strconv.Itoa64(future),
		store.Clobber,
	)
	return muta
}

func TestManyOneshotTimers(t *testing.T) {
	st := store.New()
	timer := New(testPattern, 10*OneMillisecond, st)
	defer timer.Close()

	st.Ops <- store.Op{1, encodeTimer("/timer/longest", 40*OneMillisecond)}
	st.Ops <- store.Op{2, encodeTimer("/timer/short", 10*OneMillisecond)}
	st.Ops <- store.Op{3, encodeTimer("/timer/long", 25*OneMillisecond)}

	got := <-timer.C
	assert.Equal(t, got.Path, "/timer/short")
	assert.T(t, got.At <= time.Nanoseconds())

	got = <-timer.C
	assert.Equal(t, got.Path, "/timer/long")
	assert.T(t, got.At <= time.Nanoseconds())

	got = <-timer.C
	assert.Equal(t, got.Path, "/timer/longest")
	assert.T(t, got.At <= time.Nanoseconds())

	assert.Equal(t, 0, timer.ticks.Len())
}

func TestDeleteTimer(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	defer timer.Close()

	never := "/timer/never/ticks"
	does := "/timer/does/tick"

	// Wait one minute to ensure it doesn't tick before
	// the following delete and assert.
	st.Ops <- store.Op{1, encodeTimer(never, 30*OneMillisecond)}

	st.Ops <- store.Op{2, encodeTimer(does, 60*OneMillisecond)}

	st.Ops <- store.Op{3, store.MustEncodeDel(never, store.Clobber)}

	// If the first timer failed to delete, it would come out first.
	assert.Equal(t, does, (<-timer.C).Path) // From seqn 2
}

func TestUpdate(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	defer timer.Close()

	st.Ops <- store.Op{1, encodeTimer("/timer/y", 90*OneMillisecond)}
	st.Ops <- store.Op{2, encodeTimer("/timer/x", 30*OneMillisecond)}
	st.Ops <- store.Op{3, encodeTimer("/timer/x", 60*OneMillisecond)}

	// The deadline scheduled from seqn 2 should never fire. It should be
	// replaced by seqn 3.

	assert.Equal(t, "/timer/x", (<-timer.C).Path) // From seqn 3
	assert.Equal(t, "/timer/y", (<-timer.C).Path) // From seqn 1
}

func TestTimerStop(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	timer.Close()

	st.Ops <- store.Op{1, encodeTimer("/timer/y", 90*OneMillisecond)}
	st.Ops <- store.Op{2, encodeTimer("/timer/x", 30*OneMillisecond)}
	st.Wait(1)

	assert.Equal(t, 0, <-st.Watches) // From seqn 1
}
<commit_msg>timer: attempt to fix race in test<commit_after>package timer

import (
	"github.com/bmizerany/assert"
	"doozer/store"
	"testing"
	"time"
	"strconv"
)

const (
	testPattern = "/timer/**"
)

func encodeTimer(path string, offset int64) string {
	future := time.Nanoseconds() + offset
	muta := store.MustEncodeSet(
		path,
		strconv.Itoa64(future),
		store.Clobber,
	)
	return muta
}

func TestManyOneshotTimers(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	defer timer.Close()

	st.Ops <- store.Op{1, encodeTimer("/timer/longest", 40*OneMillisecond)}
	st.Ops <- store.Op{2, encodeTimer("/timer/short", 10*OneMillisecond)}
	st.Ops <- store.Op{3, encodeTimer("/timer/long", 25*OneMillisecond)}

	got := <-timer.C
	assert.Equal(t, got.Path, "/timer/short")
	assert.T(t, got.At <= time.Nanoseconds())

	got = <-timer.C
	assert.Equal(t, got.Path, "/timer/long")
	assert.T(t, got.At <= time.Nanoseconds())

	got = <-timer.C
	assert.Equal(t, got.Path, "/timer/longest")
	assert.T(t, got.At <= time.Nanoseconds())

	assert.Equal(t, 0, timer.ticks.Len())
}

func TestDeleteTimer(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	defer timer.Close()

	never := "/timer/never/ticks"
	does := "/timer/does/tick"

	// Wait one minute to ensure it doesn't tick before
	// the following delete and assert.
	st.Ops <- store.Op{1, encodeTimer(never, 30*OneMillisecond)}

	st.Ops <- store.Op{2, encodeTimer(does, 60*OneMillisecond)}

	st.Ops <- store.Op{3, store.MustEncodeDel(never, store.Clobber)}

	// If the first timer failed to delete, it would come out first.
	assert.Equal(t, does, (<-timer.C).Path) // From seqn 2
}

func TestUpdate(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	defer timer.Close()

	st.Ops <- store.Op{1, encodeTimer("/timer/y", 90*OneMillisecond)}
	st.Ops <- store.Op{2, encodeTimer("/timer/x", 30*OneMillisecond)}
	st.Ops <- store.Op{3, encodeTimer("/timer/x", 60*OneMillisecond)}

	// The deadline scheduled from seqn 2 should never fire. It should be
	// replaced by seqn 3.

	assert.Equal(t, "/timer/x", (<-timer.C).Path) // From seqn 3
	assert.Equal(t, "/timer/y", (<-timer.C).Path) // From seqn 1
}

func TestTimerStop(t *testing.T) {
	st := store.New()
	timer := New(testPattern, OneMillisecond, st)
	timer.Close()

	st.Ops <- store.Op{1, encodeTimer("/timer/y", 90*OneMillisecond)}
	st.Ops <- store.Op{2, encodeTimer("/timer/x", 30*OneMillisecond)}
	st.Wait(1)

	assert.Equal(t, 0, <-st.Watches) // From seqn 1
}
<|endoftext|>


<commit_before>package chlib

import (
	"bufio"
	"os"
	"strconv"
	"strings"

	"fmt"

	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/pflag"
)

type validationFunc func(string) bool

var reader = bufio.NewReader(os.Stdin)

func Prompt(np *jww.Notepad, prompt string, validator validationFunc) string {
	np.FEEDBACK.Printf("%s: ", prompt)
	ret, _ := reader.ReadString('\n')
	ret = strings.TrimRight(ret, "\r\n")
	for !validator(ret) {
		np.FEEDBACK.Printf("Invalid input\n%s: ", prompt)
		ret, _ = reader.ReadString('\n')
		ret = strings.TrimRight(ret, "\r\n")
	}
	return ret
}

func portsValidateStr(portsStr string) bool {
	if portsStr == "" {
		return true
	}
	for _, portStr := range strings.Split(portsStr, " ") {
		port, err := strconv.Atoi(portStr)
		if err != nil || port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func portsValidateInt(ports []int) bool {
	for _, port := range ports {
		if port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func labelSliceValidate(labels []string) bool {
	for _, labelStr := range labels {
		label := strings.Split(labelStr, "=")
		if len(label) != 2 || !LabelRegex.MatchString(label[0]) || !LabelRegex.MatchString(label[1]) {
			return false
		}
	}
	return true
}

func labelsValidate(labelsStr string) bool {
	return labelsStr == "" || labelSliceValidate(strings.Split(labelsStr, " "))
}

func envSliceValidate(envVars []string) bool {
	for _, envVarStr := range envVars {
		envVar := strings.Split(envVarStr, "=")
		if len(envVar) != 2 {
			return false
		}
	}
	return true
}

func envVarsValidate(envVarsStr string) bool {
	return envVarsStr == "" || envSliceValidate(strings.Split(envVarsStr, " "))
}

func cpuValidate(cpuStr string) bool {
	if cpuStr == "" {
		return true
	}
	return CpuRegex.MatchString(cpuStr)
}

func memValidate(memStr string) bool {
	if memStr == "" {
		return true
	}
	return MemRegex.MatchString(memStr)
}

func replicasValidate(replicasStr string) bool {
	if replicasStr == "" {
		return true
	}
	ret, err := strconv.Atoi(replicasStr)
	return err == nil && ret > 0
}

func volumesValidate(volumesStr string) bool {
	if volumesStr == "" {
		return true
	}
	for _, v := range strings.Fields(volumesStr) {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"") % 2 != 0 {
			return false
		}
	}
	return true
}

func volumesSliceValidate(vols []string) bool {
	for _, v := range vols {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"") % 2 != 0 {
			return false
		}
	}
	return true
}

func PromptParams(np *jww.Notepad) (params ConfigureParams) {
	params.Image = Prompt(np, "Enter image", ImageRegex.MatchString)
	if portsStr := Prompt(np, "Enter ports (PORT1 PORT2 ... PORTN)", portsValidateStr); portsStr != "" {
		for _, p := range strings.Split(portsStr, " ") {
			port, _ := strconv.Atoi(p)
			params.Ports = append(params.Ports, port)
		}
	}
	params.Labels = make(map[string]string)
	if labelsStr := Prompt(np, "Enter labels (key1=value1 key2=value2 ... keyN=valueN)", labelsValidate); labelsStr != "" {
		for _, labelStr := range strings.Split(labelsStr, " ") {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	}
	if commands := Prompt(np, "Enter commands (command1 command2 ... commandN)", func(string) bool { return true }); commands != "" {
		params.Command = strings.Split(commands, " ")
	}
	if envVarsStr := Prompt(np, "Enter environment variables (key1=value1 ... keyN=valueN)", envVarsValidate); envVarsStr != "" {
		for _, envVarStr := range strings.Split(envVarsStr, " ") {
			envVar := strings.Split(envVarStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  envVar[0],
				Value: envVar[1],
			})
		}
	}
	if volumesStr := Prompt(np, "Enter volumes (volumeLabel1[/subPath1]=/mountPath1 ... volumeLabelN[/subPathN]=/mountPathN)", volumesValidate); volumesStr != "" {
		for _, volumeStr := range strings.Fields(volumesStr) {
			volume := VolumeRegex.FindStringSubmatch(volumeStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     volume[1],
				SubPath:   volume[4],
				MountPath: volume[5],
			})
		}
	}
	if cpu := Prompt(np, fmt.Sprintf("Enter CPU cores (*m) [%s]", DefaultCPURequest), cpuValidate); cpu != "" {
		params.CPU = cpu
	} else {
		params.CPU = DefaultCPURequest
	}
	if memory := Prompt(np, fmt.Sprintf("Enter memory size (*Mi | *Gi) [%s]", DefaultMemoryRequest), memValidate); memory != "" {
		params.Memory = memory
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if replicas := Prompt(np, fmt.Sprintf("Enter replicas count [%d]", DefaultReplicas), replicasValidate); replicas != "" {
		params.Replicas, _ = strconv.Atoi(replicas)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}

func exitIfValidationError(np *jww.Notepad, validationResult bool, message string) {
	if !validationResult {
		np.FEEDBACK.Println(message)
		os.Exit(1)
	}
}

func ParamsFromArgs(np *jww.Notepad, flags *pflag.FlagSet) (params ConfigureParams) {
	chkErr := func(err error) {
		if err != nil {
			np.FEEDBACK.Println("flag get error: %s\n", err)
		}
	}
	var err error
	if flags.Changed("image") {
		params.Image, err = flags.GetString("image")
		chkErr(err)
		exitIfValidationError(np, ImageRegex.MatchString(params.Image), "Invalid image name")
	}
	if flags.Changed("port") {
		params.Ports, err = flags.GetIntSlice("port")
		chkErr(err)
		exitIfValidationError(np, portsValidateInt(params.Ports), "Invalid port found")
	}
	if flags.Changed("labels") {
		labelsSlice, err := flags.GetStringSlice("labels")
		chkErr(err)
		exitIfValidationError(np, labelSliceValidate(labelsSlice), "Invalid label found")
		for _, labelStr := range labelsSlice {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	} else {
		params.Labels = make(map[string]string)
	}
	if flags.Changed("command") {
		params.Command, err = flags.GetStringSlice("command")
		chkErr(err)
	}
	if flags.Changed("env") {
		envSlice, err := flags.GetStringSlice("env")
		chkErr(err)
		exitIfValidationError(np, envSliceValidate(envSlice), "Invalid environment variable found")
		for _, envStr := range envSlice {
			env := strings.Split(envStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  env[0],
				Value: env[1],
			})
		}
	}
	if flags.Changed("volumes") {
		volsSlice, err := flags.GetStringSlice("volumes")
		chkErr(err)
		exitIfValidationError(np, volumesSliceValidate(volsSlice), "Invalid volume parameter found")
		for _, volStr := range volsSlice {
			vol := VolumeRegex.FindStringSubmatch(volStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     vol[1],
				SubPath:   vol[4],
				MountPath: vol[5],
			})
		}
	}
	if flags.Changed("cpu") {
		params.CPU, err = flags.GetString("cpu")
		chkErr(err)
		exitIfValidationError(np, cpuValidate(params.CPU), "Invalid CPU format")
	} else {
		params.CPU = DefaultCPURequest
	}
	if flags.Changed("memory") {
		params.Memory, err = flags.GetString("memory")
		chkErr(err)
		exitIfValidationError(np, cpuValidate(params.Memory), "Invalid memory format")
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if flags.Changed("replicas") {
		params.Replicas, err = flags.GetInt("replicas")
		chkErr(err)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}
<commit_msg>Fix memory flag validation<commit_after>package chlib

import (
	"bufio"
	"os"
	"strconv"
	"strings"

	"fmt"

	jww "github.com/spf13/jwalterweatherman"
	"github.com/spf13/pflag"
)

type validationFunc func(string) bool

var reader = bufio.NewReader(os.Stdin)

func Prompt(np *jww.Notepad, prompt string, validator validationFunc) string {
	np.FEEDBACK.Printf("%s: ", prompt)
	ret, _ := reader.ReadString('\n')
	ret = strings.TrimRight(ret, "\r\n")
	for !validator(ret) {
		np.FEEDBACK.Printf("Invalid input\n%s: ", prompt)
		ret, _ = reader.ReadString('\n')
		ret = strings.TrimRight(ret, "\r\n")
	}
	return ret
}

func portsValidateStr(portsStr string) bool {
	if portsStr == "" {
		return true
	}
	for _, portStr := range strings.Split(portsStr, " ") {
		port, err := strconv.Atoi(portStr)
		if err != nil || port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func portsValidateInt(ports []int) bool {
	for _, port := range ports {
		if port <= 0 || port > 65535 {
			return false
		}
	}
	return true
}

func labelSliceValidate(labels []string) bool {
	for _, labelStr := range labels {
		label := strings.Split(labelStr, "=")
		if len(label) != 2 || !LabelRegex.MatchString(label[0]) || !LabelRegex.MatchString(label[1]) {
			return false
		}
	}
	return true
}

func labelsValidate(labelsStr string) bool {
	return labelsStr == "" || labelSliceValidate(strings.Split(labelsStr, " "))
}

func envSliceValidate(envVars []string) bool {
	for _, envVarStr := range envVars {
		envVar := strings.Split(envVarStr, "=")
		if len(envVar) != 2 {
			return false
		}
	}
	return true
}

func envVarsValidate(envVarsStr string) bool {
	return envVarsStr == "" || envSliceValidate(strings.Split(envVarsStr, " "))
}

func cpuValidate(cpuStr string) bool {
	if cpuStr == "" {
		return true
	}
	return CpuRegex.MatchString(cpuStr)
}

func memValidate(memStr string) bool {
	if memStr == "" {
		return true
	}
	return MemRegex.MatchString(memStr)
}

func replicasValidate(replicasStr string) bool {
	if replicasStr == "" {
		return true
	}
	ret, err := strconv.Atoi(replicasStr)
	return err == nil && ret > 0
}

func volumesValidate(volumesStr string) bool {
	if volumesStr == "" {
		return true
	}
	for _, v := range strings.Fields(volumesStr) {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"") % 2 != 0 {
			return false
		}
	}
	return true
}

func volumesSliceValidate(vols []string) bool {
	for _, v := range vols {
		if !VolumeRegex.MatchString(v) {
			return false
		}
		if strings.Count(v, "\"") % 2 != 0 {
			return false
		}
	}
	return true
}

func PromptParams(np *jww.Notepad) (params ConfigureParams) {
	params.Image = Prompt(np, "Enter image", ImageRegex.MatchString)
	if portsStr := Prompt(np, "Enter ports (PORT1 PORT2 ... PORTN)", portsValidateStr); portsStr != "" {
		for _, p := range strings.Split(portsStr, " ") {
			port, _ := strconv.Atoi(p)
			params.Ports = append(params.Ports, port)
		}
	}
	params.Labels = make(map[string]string)
	if labelsStr := Prompt(np, "Enter labels (key1=value1 key2=value2 ... keyN=valueN)", labelsValidate); labelsStr != "" {
		for _, labelStr := range strings.Split(labelsStr, " ") {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	}
	if commands := Prompt(np, "Enter commands (command1 command2 ... commandN)", func(string) bool { return true }); commands != "" {
		params.Command = strings.Split(commands, " ")
	}
	if envVarsStr := Prompt(np, "Enter environment variables (key1=value1 ... keyN=valueN)", envVarsValidate); envVarsStr != "" {
		for _, envVarStr := range strings.Split(envVarsStr, " ") {
			envVar := strings.Split(envVarStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  envVar[0],
				Value: envVar[1],
			})
		}
	}
	if volumesStr := Prompt(np, "Enter volumes (volumeLabel1[/subPath1]=/mountPath1 ... volumeLabelN[/subPathN]=/mountPathN)", volumesValidate); volumesStr != "" {
		for _, volumeStr := range strings.Fields(volumesStr) {
			volume := VolumeRegex.FindStringSubmatch(volumeStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     volume[1],
				SubPath:   volume[4],
				MountPath: volume[5],
			})
		}
	}
	if cpu := Prompt(np, fmt.Sprintf("Enter CPU cores (*m) [%s]", DefaultCPURequest), cpuValidate); cpu != "" {
		params.CPU = cpu
	} else {
		params.CPU = DefaultCPURequest
	}
	if memory := Prompt(np, fmt.Sprintf("Enter memory size (*Mi | *Gi) [%s]", DefaultMemoryRequest), memValidate); memory != "" {
		params.Memory = memory
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if replicas := Prompt(np, fmt.Sprintf("Enter replicas count [%d]", DefaultReplicas), replicasValidate); replicas != "" {
		params.Replicas, _ = strconv.Atoi(replicas)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}

func exitIfValidationError(np *jww.Notepad, validationResult bool, message string) {
	if !validationResult {
		np.FEEDBACK.Println(message)
		os.Exit(1)
	}
}

func ParamsFromArgs(np *jww.Notepad, flags *pflag.FlagSet) (params ConfigureParams) {
	chkErr := func(err error) {
		if err != nil {
			np.FEEDBACK.Println("flag get error: %s\n", err)
		}
	}
	var err error
	if flags.Changed("image") {
		params.Image, err = flags.GetString("image")
		chkErr(err)
		exitIfValidationError(np, ImageRegex.MatchString(params.Image), "Invalid image name")
	}
	if flags.Changed("port") {
		params.Ports, err = flags.GetIntSlice("port")
		chkErr(err)
		exitIfValidationError(np, portsValidateInt(params.Ports), "Invalid port found")
	}
	if flags.Changed("labels") {
		labelsSlice, err := flags.GetStringSlice("labels")
		chkErr(err)
		exitIfValidationError(np, labelSliceValidate(labelsSlice), "Invalid label found")
		for _, labelStr := range labelsSlice {
			label := strings.Split(labelStr, "=")
			params.Labels[label[0]] = label[1]
		}
	} else {
		params.Labels = make(map[string]string)
	}
	if flags.Changed("command") {
		params.Command, err = flags.GetStringSlice("command")
		chkErr(err)
	}
	if flags.Changed("env") {
		envSlice, err := flags.GetStringSlice("env")
		chkErr(err)
		exitIfValidationError(np, envSliceValidate(envSlice), "Invalid environment variable found")
		for _, envStr := range envSlice {
			env := strings.Split(envStr, "=")
			params.Env = append(params.Env, EnvVar{
				Name:  env[0],
				Value: env[1],
			})
		}
	}
	if flags.Changed("volumes") {
		volsSlice, err := flags.GetStringSlice("volumes")
		chkErr(err)
		exitIfValidationError(np, volumesSliceValidate(volsSlice), "Invalid volume parameter found")
		for _, volStr := range volsSlice {
			vol := VolumeRegex.FindStringSubmatch(volStr)
			params.Volumes = append(params.Volumes, Volume{
				Label:     vol[1],
				SubPath:   vol[4],
				MountPath: vol[5],
			})
		}
	}
	if flags.Changed("cpu") {
		params.CPU, err = flags.GetString("cpu")
		chkErr(err)
		exitIfValidationError(np, cpuValidate(params.CPU), "Invalid CPU format")
	} else {
		params.CPU = DefaultCPURequest
	}
	if flags.Changed("memory") {
		params.Memory, err = flags.GetString("memory")
		chkErr(err)
		exitIfValidationError(np, memValidate(params.Memory), "Invalid memory format")
	} else {
		params.Memory = DefaultMemoryRequest
	}
	if flags.Changed("replicas") {
		params.Replicas, err = flags.GetInt("replicas")
		chkErr(err)
	} else {
		params.Replicas = DefaultReplicas
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"
	"sync"

	circle "github.com/Shyp/go-circle"
	"github.com/Shyp/go-circle/wait"
	git "github.com/Shyp/go-git"
	"github.com/skratchdot/open-golang/open"
)

const help = `The circle binary interacts with a server that runs your tests.

Usage:

	circle command [arguments]

The commands are:

	open                Open the latest branch build in a browser.
	update              Update to the latest version
	version             Print the current version
	wait                Wait for tests to finish on a branch.
	download-artifacts  Download all artifacts.

Use "circle help [command]" for more information about a command.
`

const downloadUsage = `usage: download-artifacts <build-num>`

func usage() {
	fmt.Fprintf(os.Stderr, help)
	flag.PrintDefaults()
	os.Exit(2)
}

func init() {
	flag.Usage = usage
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// Given a set of command line args, return the git branch or an error. Returns
// the current git branch if no argument is specified
func getBranchFromArgs(args []string) (string, error) {
	if len(args) == 0 {
		return git.CurrentBranch()
	} else {
		return args[0], nil
	}
}

func doOpen(flags *flag.FlagSet) {
	args := flags.Args()
	branch, err := getBranchFromArgs(args)
	checkError(err)
	remote, err := git.GetRemoteURL("origin")
	checkError(err)
	cr, err := circle.GetTree(remote.Path, remote.RepoName, branch)
	checkError(err)
	if len(*cr) == 0 {
		fmt.Printf("No results, are you sure there are tests for %s/%s?\n",
			remote.Path, remote.RepoName)
		return
	}
	latestBuild := (*cr)[0]
	open.Start(latestBuild.BuildURL)
}

func doDownload(flags *flag.FlagSet) error {
	buildStr := flags.Arg(0)
	val, err := strconv.Atoi(buildStr)
	if err != nil {
		return err
	}
	remote, err := git.GetRemoteURL("origin")
	if err != nil {
		return err
	}
	arts, err := circle.GetArtifactsForBuild(remote.Path, remote.RepoName, val)
	if err != nil {
		return err
	}
	var wg sync.WaitGroup
	tempDir, err := ioutil.TempDir("", "circle-artifacts")
	if err != nil {
		return err
	}
	for _, art := range arts {
		wg.Add(1)
		go func(a *circle.CircleArtifact) {
			if err := circle.DownloadArtifact(a, tempDir, remote.Path); err != nil {
				fmt.Fprintln(os.Stderr, err)
			}
			wg.Done()
		}(art)
	}
	wg.Wait()
	fmt.Fprintf(os.Stderr, "Wrote all artifacts for build %d to %s\n", val, tempDir)
	return nil
}

func main() {
	waitflags := flag.NewFlagSet("wait", flag.ExitOnError)
	openflags := flag.NewFlagSet("open", flag.ExitOnError)
	downloadflags := flag.NewFlagSet("download-artifacts", flag.ExitOnError)
	downloadflags.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s\n\n", downloadUsage)
		downloadflags.PrintDefaults()
		os.Exit(1)
	}

	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		usage()
		return
	}
	switch flag.Arg(0) {
	case "open":
		openflags.Parse(os.Args[2:])
		doOpen(openflags)
	case "update":
		err := equinoxUpdate()
		checkError(err)
	case "version":
		fmt.Fprintf(os.Stderr, "circle version %s\n", circle.VERSION)
		os.Exit(1)
	case "wait":
		waitflags.Parse(os.Args[2:])
		args := waitflags.Args()
		branch, err := getBranchFromArgs(args)
		checkError(err)
		err = wait.Wait(branch)
		checkError(err)
	case "download-artifacts":
		if len(args) == 1 {
			fmt.Fprintf(os.Stderr, "usage: download-artifacts <build-number>\n")
			os.Exit(1)
		}
		downloadflags.Parse(args[1:])
		if err := doDownload(downloadflags); err != nil {
			log.Fatal(err)
		}

	default:
		usage()
	}
}
<commit_msg>use an errgroup instead of a WaitGroup<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"strconv"

	circle "github.com/Shyp/go-circle"
	"github.com/Shyp/go-circle/wait"
	git "github.com/Shyp/go-git"
	"github.com/skratchdot/open-golang/open"
	"golang.org/x/sync/errgroup"
)

const help = `The circle binary interacts with a server that runs your tests.

Usage:

	circle command [arguments]

The commands are:

	open                Open the latest branch build in a browser.
	update              Update to the latest version
	version             Print the current version
	wait                Wait for tests to finish on a branch.
	download-artifacts  Download all artifacts.

Use "circle help [command]" for more information about a command.
`

const downloadUsage = `usage: download-artifacts <build-num>`

func usage() {
	fmt.Fprintf(os.Stderr, help)
	flag.PrintDefaults()
	os.Exit(2)
}

func init() {
	flag.Usage = usage
}

func checkError(err error) {
	if err != nil {
		log.Fatal(err)
	}
}

// Given a set of command line args, return the git branch or an error. Returns
// the current git branch if no argument is specified
func getBranchFromArgs(args []string) (string, error) {
	if len(args) == 0 {
		return git.CurrentBranch()
	} else {
		return args[0], nil
	}
}

func doOpen(flags *flag.FlagSet) {
	args := flags.Args()
	branch, err := getBranchFromArgs(args)
	checkError(err)
	remote, err := git.GetRemoteURL("origin")
	checkError(err)
	cr, err := circle.GetTree(remote.Path, remote.RepoName, branch)
	checkError(err)
	if len(*cr) == 0 {
		fmt.Printf("No results, are you sure there are tests for %s/%s?\n",
			remote.Path, remote.RepoName)
		return
	}
	latestBuild := (*cr)[0]
	open.Start(latestBuild.BuildURL)
}

func doDownload(flags *flag.FlagSet) error {
	buildStr := flags.Arg(0)
	val, err := strconv.Atoi(buildStr)
	if err != nil {
		return err
	}
	remote, err := git.GetRemoteURL("origin")
	if err != nil {
		return err
	}
	arts, err := circle.GetArtifactsForBuild(remote.Path, remote.RepoName, val)
	if err != nil {
		return err
	}
	var g errgroup.Group
	tempDir, err := ioutil.TempDir("", "circle-artifacts")
	if err != nil {
		return err
	}
	for _, art := range arts {
		art := art
		g.Go(func() error {
			return circle.DownloadArtifact(art, tempDir, remote.Path)
		})
	}

	if err := g.Wait(); err != nil {
		fmt.Fprintln(os.Stderr, err)
		return err
	}
	fmt.Fprintf(os.Stderr, "Wrote all artifacts for build %d to %s\n", val, tempDir)
	return nil
}

func main() {
	waitflags := flag.NewFlagSet("wait", flag.ExitOnError)
	openflags := flag.NewFlagSet("open", flag.ExitOnError)
	downloadflags := flag.NewFlagSet("download-artifacts", flag.ExitOnError)
	downloadflags.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s\n\n", downloadUsage)
		downloadflags.PrintDefaults()
		os.Exit(1)
	}

	flag.Parse()
	args := flag.Args()
	if len(args) < 1 {
		usage()
		return
	}
	switch flag.Arg(0) {
	case "open":
		openflags.Parse(os.Args[2:])
		doOpen(openflags)
	case "update":
		err := equinoxUpdate()
		checkError(err)
	case "version":
		fmt.Fprintf(os.Stderr, "circle version %s\n", circle.VERSION)
		os.Exit(1)
	case "wait":
		waitflags.Parse(os.Args[2:])
		args := waitflags.Args()
		branch, err := getBranchFromArgs(args)
		checkError(err)
		err = wait.Wait(branch)
		checkError(err)
	case "download-artifacts":
		if len(args) == 1 {
			fmt.Fprintf(os.Stderr, "usage: download-artifacts <build-number>\n")
			os.Exit(1)
		}
		downloadflags.Parse(args[1:])
		if err := doDownload(downloadflags); err != nil {
			log.Fatal(err)
		}

	default:
		usage()
	}
}
<|endoftext|>


<commit_before>/*
Command gojava is a tool for creating Java bindings to Go packages.

Usage

	gojava [-v] [-o <jar>] [-s <dir>] build [<pkg1>, [<pkg2>...]]

	This generates a jar containing Java bindings to the specified Go packages.

	-o string
	    Path to write the generated jar file. (default "libgojava.jar")
	-s string
	    Additional path to scan for Java source code. These files will be compiled and
	    included in the final jar.
	-v  Verbose output.
*/
package main

import (
	"go/build"
	"path/filepath"
	"reflect"

	"fmt"
	"go/importer"
	"go/token"
	"go/types"
	"os"
	"os/exec"
	"strings"

	"io/ioutil"

	"archive/zip"
	"runtime"

	"flag"

	"github.com/sridharv/gomobile-java/bind"
)

func runCommand(cmd string, args ...string) error {
	if out, err := exec.Command(cmd, args...).CombinedOutput(); err != nil {
		return fmt.Errorf("%s %s: %v: %s", cmd, strings.Join(args, " "), err, string(out))
	}
	return nil
}

var javaHome = os.Getenv("JAVA_HOME")
var cwd string
var verbose = false

func verbosef(format string, a ...interface{}) {
	if !verbose {
		return
	}
	fmt.Printf(format, a...)
}

func initBuild() (string, func(), error) {
	if javaHome == "" {
		return "", nil, fmt.Errorf("$JAVA_HOME not set")
	}
	var err error
	if cwd, err = os.Getwd(); err != nil {
		return "", nil, err
	}
	tmpDir, err := ioutil.TempDir("", "gojava")
	if err != nil {
		return "", nil, err
	}
	return tmpDir, func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			fmt.Fprintln(os.Stderr, "failed to remove temp dir:", tmpDir, err)
		}
		if err := os.Chdir(cwd); err != nil {
			fmt.Fprintln(os.Stderr, "failed to change to dir:", cwd, err)
		}
	}, nil
}

func loadExportData(pkgs []string) ([]*types.Package, error) {
	// Load export data for the packages
	if err := runCommand("go", append([]string{"install"}, pkgs...)...); err != nil {
		return nil, err
	}
	typePkgs := make([]*types.Package, len(pkgs))

	for i, p := range pkgs {
		buildPkg, err := build.Import(p, cwd, build.AllowBinary)
		if err != nil {
			return nil, err
		}
		if typePkgs[i], err = importer.Default().Import(buildPkg.ImportPath); err != nil {
			return nil, err
		}
	}
	return typePkgs, nil
}

func createDirs(dirs ...string) error {
	for _, d := range dirs {
		if err := os.MkdirAll(d, 0700); err != nil {
			return err
		}
	}
	return nil
}

func bindPackages(bindDir, javaDir string, pkgs []*types.Package) ([]string, error) {
	fs, javaFiles := token.NewFileSet(), make([]string, 0)
	for _, p := range pkgs {
		goFile := filepath.Join(bindDir, "go_"+p.Name()+"main.go")
		f, err := os.OpenFile(goFile, os.O_CREATE|os.O_RDWR, 0600)
		if err != nil {
			return nil, fmt.Errorf("failed to open: %s: %v", goFile, err)
		}
		conf := &bind.GeneratorConfig{Writer: f, Fset: fs, Pkg: p, AllPkg: pkgs}
		if err := bind.GenGo(conf); err != nil {
			return nil, fmt.Errorf("failed to bind %s:%v", p.Name(), err)
		}
		if err := f.Close(); err != nil {
			return nil, err
		}
		javaFile := strings.Title(p.Name()) + ".java"
		if err := bindJava(javaDir, javaFile, conf, int(bind.Java)); err != nil {
			return nil, err
		}
		if err := bindJava(bindDir, "java_"+p.Name()+".c", conf, int(bind.JavaC)); err != nil {
			return nil, err
		}
		if err := bindJava(bindDir, p.Name()+".h", conf, int(bind.JavaH)); err != nil {
			return nil, err
		}
		javaFiles = append(javaFiles, filepath.Join(javaDir, javaFile))
	}
	return javaFiles, nil
}

func addExtraFiles(javaDir, sourceDir string) ([]string, error) {
	if sourceDir == "" {
		return nil, nil
	}
	var extraFiles []string
	err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if info.IsDir() {
			return nil
		}
		fileName, err := filepath.Rel(sourceDir, path)
		if err != nil {
			return err
		}
		if strings.HasSuffix(fileName, ".java") {
			p := filepath.Join(javaDir, fileName)
			extraFiles = append(extraFiles, p)
			return copyFile(p, path)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	if len(extraFiles) == 0 {
		verbosef("warning: argument -s was passed on command line, but no .java files were found in '%s'\n", sourceDir)
	}
	return extraFiles, nil
}

func createSupportFiles(bindDir, javaDir, mainFile string) error {
	bindPkg, err := build.Import(reflect.TypeOf(bind.ErrorList{}).PkgPath(), "", build.FindOnly)
	if err != nil {
		return err
	}
	bindJavaPkgDir := filepath.Join(bindPkg.Dir, "java")
	toCopy := []filePair{
		{filepath.Join(bindDir, "seq.go"), filepath.Join(bindPkg.Dir, "seq.go.support")},
		{filepath.Join(bindDir, "seq_java.go"), filepath.Join(bindJavaPkgDir, "seq_android.go.support")},
		{filepath.Join(bindDir, "seq.c"), filepath.Join(bindJavaPkgDir, "seq_android.c.support")},
		{filepath.Join(bindDir, "seq.h"), filepath.Join(bindJavaPkgDir, "seq.h")},
		{filepath.Join(javaDir, "Seq.java"), filepath.Join(bindJavaPkgDir, "Seq.java")},
		{filepath.Join(javaDir, "LoadJNI.java"), filepath.Join(bindPkg.Dir, "..", "..", "gojava", "LoadJNI.java")},
	}
	if err := copyFiles(toCopy); err != nil {
		return err
	}
	if err := ioutil.WriteFile(mainFile, []byte(fmt.Sprintf(javaMain, bindPkg.ImportPath)), 0600); err != nil {
		return err
	}
	inc1, inc2 := filepath.Join(javaHome, "include"), filepath.Join(javaHome, "include", runtime.GOOS)
	flagFile := filepath.Join(bindDir, "gojavacimport.go")

	return ioutil.WriteFile(flagFile, []byte(fmt.Sprintf(javaInclude, inc1, inc2)), 0600)
}

func buildGo(classDir, mainDir string) error {
	dylib := filepath.Join(classDir, "libgojava")
	if err := os.Chdir(mainDir); err != nil {
		return err
	}
	return runCommand("go", "build", "-o", dylib, "-buildmode=c-shared", ".")
}

func buildJava(jarDir, javaDir string, javaFiles []string) error {
	if err := os.Chdir(javaDir); err != nil {
		return err
	}
	javaFiles = append(javaFiles, filepath.Join(javaDir, "Seq.java"), filepath.Join(javaDir, "LoadJNI.java"))
	return runCommand("javac", append([]string{
		"-d", jarDir,
		"-sourcepath", filepath.Join(javaDir, ".."),
	}, javaFiles...)...)
}

func createJar(target, jarDir string) error {
	if err := os.Chdir(cwd); err != nil {
		return err
	}
	t, err := os.OpenFile(target, os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		return err
	}
	w := zip.NewWriter(t)
	verbosef("Building %s\n", target)
	if err := filepath.Walk(jarDir, func(path string, info os.FileInfo, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if info.IsDir() {
			return nil
		}
		fileName, err := filepath.Rel(jarDir, path)
		verbosef("Adding %s\n", fileName)
		if err != nil {
			return err
		}
		f, err := w.Create(fileName)
		if err != nil {
			return err
		}
		d, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		if _, err := f.Write(d); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}
	if err := w.Close(); err != nil {
		return err
	}
	if err := t.Close(); err != nil {
		return err
	}
	fmt.Printf("Finished building %s\n", target)
	return nil
}

func bindToJar(target string, sourceDir string, pkgs ...string) error {
	tmpDir, cleanup, err := initBuild()
	if err != nil {
		return err
	}
	defer cleanup()

	typePkgs, err := loadExportData(pkgs)
	if err != nil {
		return err
	}

	bindDir := filepath.Join(tmpDir, "gojava_bind")
	mainDir := filepath.Join(bindDir, "main")
	mainFile := filepath.Join(mainDir, "main.go")
	javaDir := filepath.Join(tmpDir, "src/go")
	jarDir := filepath.Join(tmpDir, "classes")
	classDir := filepath.Join(tmpDir, "classes/go")

	if err = createDirs(classDir, javaDir, mainDir); err != nil {
		return err
	}

	javaFiles, err := bindPackages(bindDir, javaDir, typePkgs)
	if err != nil {
		return err
	}
	extraFiles, err := addExtraFiles(javaDir, sourceDir)
	if err != nil {
		return err
	}
	javaFiles = append(javaFiles, extraFiles...)
	if err := createSupportFiles(bindDir, javaDir, mainFile); err != nil {
		return err
	}

	if err := buildGo(classDir, mainDir); err != nil {
		return err
	}
	if err := buildJava(jarDir, javaDir, javaFiles); err != nil {
		return err
	}
	return createJar(target, jarDir)
}

func copyFile(dst, src string) error {
	d, err := ioutil.ReadFile(src)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(dst, d, 0600)
}

type filePair struct {
	dst string
	src string
}

func copyFiles(files []filePair) error {
	for _, p := range files {
		if err := copyFile(p.dst, p.src); err != nil {
			return err
		}
	}
	return nil
}

func bindJava(dir, file string, conf *bind.GeneratorConfig, ft int) error {
	path := filepath.Join(dir, file)
	w, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		return fmt.Errorf("failed to generate %s: %v", path, err)
	}
	conf.Writer = w
	defer func() { conf.Writer = nil }()

	switch ft {
	case int(bind.Java):
		err = bind.GenJava(conf, "", bind.Java)
	case int(bind.JavaH):
		err = bind.GenJava(conf, "", bind.JavaH)
	case int(bind.JavaC):
		err = bind.GenJava(conf, "", bind.JavaC)
	default:
		err = fmt.Errorf("unsupported bind type: %d", ft)
	}
	if err != nil {
		return err
	}
	return w.Close()
}

const javaInclude = `package gojava_bind

// #cgo CFLAGS: -Wall -I%s -I%s
import "C"

`
const javaMain = `package main

import (
	_ %q
	_ ".."
)

func main() {}
`

const usage = `gojava is a tool for creating Java bindings to Go

Usage:

	gojava [-v] [-o <jar>] [-s <dir>] build [<pkg1>, [<pkg2>...]]

This generates a jar containing Java bindings to the specified Go packages.
`

func main() {
	o := flag.String("o", "libgojava.jar", "Path to the generated jar file.")
	s := flag.String("s", "", "Additional path to scan for Java source code.")
	flag.BoolVar(&verbose, "v", false, "Verbose output.")
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, usage)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() < 2 || flag.Args()[0] != "build" {
		flag.Usage()
		os.Exit(1)
	}
	if err := bindToJar(*o, *s, flag.Args()[1:]...); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Replace the generated jar atomically<commit_after>/*
Command gojava is a tool for creating Java bindings to Go packages.

Usage

	gojava [-v] [-o <jar>] [-s <dir>] build [<pkg1>, [<pkg2>...]]

	This generates a jar containing Java bindings to the specified Go packages.

	-o string
	    Path to write the generated jar file. (default "libgojava.jar")
	-s string
	    Additional path to scan for Java source code. These files will be compiled and
	    included in the final jar.
	-v  Verbose output.
*/
package main

import (
	"go/build"
	"path/filepath"
	"reflect"

	"fmt"
	"go/importer"
	"go/token"
	"go/types"
	"os"
	"os/exec"
	"strings"

	"io/ioutil"

	"archive/zip"
	"runtime"

	"flag"

	"github.com/sridharv/gomobile-java/bind"
)

func runCommand(cmd string, args ...string) error {
	if out, err := exec.Command(cmd, args...).CombinedOutput(); err != nil {
		return fmt.Errorf("%s %s: %v: %s", cmd, strings.Join(args, " "), err, string(out))
	}
	return nil
}

var javaHome = os.Getenv("JAVA_HOME")
var cwd string
var verbose = false

func verbosef(format string, a ...interface{}) {
	if !verbose {
		return
	}
	fmt.Printf(format, a...)
}

func initBuild() (string, func(), error) {
	if javaHome == "" {
		return "", nil, fmt.Errorf("$JAVA_HOME not set")
	}
	var err error
	if cwd, err = os.Getwd(); err != nil {
		return "", nil, err
	}
	tmpDir, err := ioutil.TempDir("", "gojava")
	if err != nil {
		return "", nil, err
	}
	return tmpDir, func() {
		if err := os.RemoveAll(tmpDir); err != nil {
			fmt.Fprintln(os.Stderr, "failed to remove temp dir:", tmpDir, err)
		}
		if err := os.Chdir(cwd); err != nil {
			fmt.Fprintln(os.Stderr, "failed to change to dir:", cwd, err)
		}
	}, nil
}

func loadExportData(pkgs []string) ([]*types.Package, error) {
	// Load export data for the packages
	if err := runCommand("go", append([]string{"install"}, pkgs...)...); err != nil {
		return nil, err
	}
	typePkgs := make([]*types.Package, len(pkgs))

	for i, p := range pkgs {
		buildPkg, err := build.Import(p, cwd, build.AllowBinary)
		if err != nil {
			return nil, err
		}
		if typePkgs[i], err = importer.Default().Import(buildPkg.ImportPath); err != nil {
			return nil, err
		}
	}
	return typePkgs, nil
}

func createDirs(dirs ...string) error {
	for _, d := range dirs {
		if err := os.MkdirAll(d, 0700); err != nil {
			return err
		}
	}
	return nil
}

func bindPackages(bindDir, javaDir string, pkgs []*types.Package) ([]string, error) {
	fs, javaFiles := token.NewFileSet(), make([]string, 0)
	for _, p := range pkgs {
		goFile := filepath.Join(bindDir, "go_"+p.Name()+"main.go")
		f, err := os.OpenFile(goFile, os.O_CREATE|os.O_RDWR, 0600)
		if err != nil {
			return nil, fmt.Errorf("failed to open: %s: %v", goFile, err)
		}
		conf := &bind.GeneratorConfig{Writer: f, Fset: fs, Pkg: p, AllPkg: pkgs}
		if err := bind.GenGo(conf); err != nil {
			return nil, fmt.Errorf("failed to bind %s:%v", p.Name(), err)
		}
		if err := f.Close(); err != nil {
			return nil, err
		}
		javaFile := strings.Title(p.Name()) + ".java"
		if err := bindJava(javaDir, javaFile, conf, int(bind.Java)); err != nil {
			return nil, err
		}
		if err := bindJava(bindDir, "java_"+p.Name()+".c", conf, int(bind.JavaC)); err != nil {
			return nil, err
		}
		if err := bindJava(bindDir, p.Name()+".h", conf, int(bind.JavaH)); err != nil {
			return nil, err
		}
		javaFiles = append(javaFiles, filepath.Join(javaDir, javaFile))
	}
	return javaFiles, nil
}

func addExtraFiles(javaDir, sourceDir string) ([]string, error) {
	if sourceDir == "" {
		return nil, nil
	}
	var extraFiles []string
	err := filepath.Walk(sourceDir, func(path string, info os.FileInfo, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if info.IsDir() {
			return nil
		}
		fileName, err := filepath.Rel(sourceDir, path)
		if err != nil {
			return err
		}
		if strings.HasSuffix(fileName, ".java") {
			p := filepath.Join(javaDir, fileName)
			extraFiles = append(extraFiles, p)
			return copyFile(p, path)
		}
		return nil
	})
	if err != nil {
		return nil, err
	}
	if len(extraFiles) == 0 {
		verbosef("warning: argument -s was passed on command line, but no .java files were found in '%s'\n", sourceDir)
	}
	return extraFiles, nil
}

func createSupportFiles(bindDir, javaDir, mainFile string) error {
	bindPkg, err := build.Import(reflect.TypeOf(bind.ErrorList{}).PkgPath(), "", build.FindOnly)
	if err != nil {
		return err
	}
	bindJavaPkgDir := filepath.Join(bindPkg.Dir, "java")
	toCopy := []filePair{
		{filepath.Join(bindDir, "seq.go"), filepath.Join(bindPkg.Dir, "seq.go.support")},
		{filepath.Join(bindDir, "seq_java.go"), filepath.Join(bindJavaPkgDir, "seq_android.go.support")},
		{filepath.Join(bindDir, "seq.c"), filepath.Join(bindJavaPkgDir, "seq_android.c.support")},
		{filepath.Join(bindDir, "seq.h"), filepath.Join(bindJavaPkgDir, "seq.h")},
		{filepath.Join(javaDir, "Seq.java"), filepath.Join(bindJavaPkgDir, "Seq.java")},
		{filepath.Join(javaDir, "LoadJNI.java"), filepath.Join(bindPkg.Dir, "..", "..", "gojava", "LoadJNI.java")},
	}
	if err := copyFiles(toCopy); err != nil {
		return err
	}
	if err := ioutil.WriteFile(mainFile, []byte(fmt.Sprintf(javaMain, bindPkg.ImportPath)), 0600); err != nil {
		return err
	}
	inc1, inc2 := filepath.Join(javaHome, "include"), filepath.Join(javaHome, "include", runtime.GOOS)
	flagFile := filepath.Join(bindDir, "gojavacimport.go")

	return ioutil.WriteFile(flagFile, []byte(fmt.Sprintf(javaInclude, inc1, inc2)), 0600)
}

func buildGo(classDir, mainDir string) error {
	dylib := filepath.Join(classDir, "libgojava")
	if err := os.Chdir(mainDir); err != nil {
		return err
	}
	return runCommand("go", "build", "-o", dylib, "-buildmode=c-shared", ".")
}

func buildJava(jarDir, javaDir string, javaFiles []string) error {
	if err := os.Chdir(javaDir); err != nil {
		return err
	}
	javaFiles = append(javaFiles, filepath.Join(javaDir, "Seq.java"), filepath.Join(javaDir, "LoadJNI.java"))
	return runCommand("javac", append([]string{
		"-d", jarDir,
		"-sourcepath", filepath.Join(javaDir, ".."),
	}, javaFiles...)...)
}

func createJar(target, jarDir string) error {
	if err := os.Chdir(cwd); err != nil {
		return err
	}

	dirName := filepath.Dir(target)
	if _, err := os.Stat(dirName); err != nil && err == os.ErrNotExist {
		if err = os.MkdirAll(dirName, 0644); err != nil {
			return fmt.Errorf("failed to create: %v: %+v", dirName, err)
		}
	}

	t, err := ioutil.TempFile(dirName, "."+filepath.Base(target)+".tmp")
	if err != nil {
		return fmt.Errorf("failed to create temporary file: %+v", err)
	}
	tempFileName := t.Name()
	var successful bool
	defer func() {
		if successful {
			return
		}
		if err := os.Remove(tempFileName); err != nil {
			fmt.Fprintf(os.Stderr, "WARNING: failed to delete temporary file %s: %+v", tempFileName, err)
		}
	}()
	w := zip.NewWriter(t)
	verbosef("Building %s\n", tempFileName)
	if err := filepath.Walk(jarDir, func(path string, info os.FileInfo, walkErr error) error {
		if walkErr != nil {
			return walkErr
		}
		if info.IsDir() {
			return nil
		}
		fileName, err := filepath.Rel(jarDir, path)
		verbosef("Adding %s\n", fileName)
		if err != nil {
			return err
		}
		f, err := w.Create(fileName)
		if err != nil {
			return err
		}
		d, err := ioutil.ReadFile(path)
		if err != nil {
			return err
		}
		if _, err := f.Write(d); err != nil {
			return err
		}
		return nil
	}); err != nil {
		return err
	}
	if err := w.Close(); err != nil {
		return err
	}
	if err := t.Close(); err != nil {
		return err
	}
	if err := os.Rename(tempFileName, target); err != nil {
		return fmt.Errorf("failed to atomically rename: %v", err)
	}
	successful = true
	fmt.Printf("Finished building %s\n", target)
	return nil
}

func bindToJar(target string, sourceDir string, pkgs ...string) error {
	tmpDir, cleanup, err := initBuild()
	if err != nil {
		return err
	}
	defer cleanup()

	typePkgs, err := loadExportData(pkgs)
	if err != nil {
		return err
	}

	bindDir := filepath.Join(tmpDir, "gojava_bind")
	mainDir := filepath.Join(bindDir, "main")
	mainFile := filepath.Join(mainDir, "main.go")
	javaDir := filepath.Join(tmpDir, "src/go")
	jarDir := filepath.Join(tmpDir, "classes")
	classDir := filepath.Join(tmpDir, "classes/go")

	if err = createDirs(classDir, javaDir, mainDir); err != nil {
		return err
	}

	javaFiles, err := bindPackages(bindDir, javaDir, typePkgs)
	if err != nil {
		return err
	}
	extraFiles, err := addExtraFiles(javaDir, sourceDir)
	if err != nil {
		return err
	}
	javaFiles = append(javaFiles, extraFiles...)
	if err := createSupportFiles(bindDir, javaDir, mainFile); err != nil {
		return err
	}

	if err := buildGo(classDir, mainDir); err != nil {
		return err
	}
	if err := buildJava(jarDir, javaDir, javaFiles); err != nil {
		return err
	}
	return createJar(target, jarDir)
}

func copyFile(dst, src string) error {
	d, err := ioutil.ReadFile(src)
	if err != nil {
		return err
	}
	return ioutil.WriteFile(dst, d, 0600)
}

type filePair struct {
	dst string
	src string
}

func copyFiles(files []filePair) error {
	for _, p := range files {
		if err := copyFile(p.dst, p.src); err != nil {
			return err
		}
	}
	return nil
}

func bindJava(dir, file string, conf *bind.GeneratorConfig, ft int) error {
	path := filepath.Join(dir, file)
	w, err := os.OpenFile(path, os.O_CREATE|os.O_RDWR, 0600)
	if err != nil {
		return fmt.Errorf("failed to generate %s: %v", path, err)
	}
	conf.Writer = w
	defer func() { conf.Writer = nil }()

	switch ft {
	case int(bind.Java):
		err = bind.GenJava(conf, "", bind.Java)
	case int(bind.JavaH):
		err = bind.GenJava(conf, "", bind.JavaH)
	case int(bind.JavaC):
		err = bind.GenJava(conf, "", bind.JavaC)
	default:
		err = fmt.Errorf("unsupported bind type: %d", ft)
	}
	if err != nil {
		return err
	}
	return w.Close()
}

const javaInclude = `package gojava_bind

// #cgo CFLAGS: -Wall -I%s -I%s
import "C"

`
const javaMain = `package main

import (
	_ %q
	_ ".."
)

func main() {}
`

const usage = `gojava is a tool for creating Java bindings to Go

Usage:

	gojava [-v] [-o <jar>] [-s <dir>] build [<pkg1>, [<pkg2>...]]

This generates a jar containing Java bindings to the specified Go packages.
`

func main() {
	o := flag.String("o", "libgojava.jar", "Path to the generated jar file.")
	s := flag.String("s", "", "Additional path to scan for Java source code.")
	flag.BoolVar(&verbose, "v", false, "Verbose output.")
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, usage)
		flag.PrintDefaults()
	}
	flag.Parse()
	if flag.NArg() < 2 || flag.Args()[0] != "build" {
		flag.Usage()
		os.Exit(1)
	}
	if err := bindToJar(*o, *s, flag.Args()[1:]...); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Package goldie provides test assertions based on golden files. It's typically
// used for testing responses with larger data bodies.
//
// The concept is straight forward. Valid response data is stored in a "golden
// file". The actual response data will be byte compared with the golden file
// and the test will fail if there is a difference.
//
// Updating the golden file can be done by running `go test -update ./...`.
package goldie

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var (
	// FixtureDir is the folder name for where the fixtures are stored. It's
	// relative to the "go test" path.
	FixtureDir = "fixtures"

	// FileNameSuffix is the suffix appended to the fixtures. Set to empty
	// string to disable file name suffixes.
	FileNameSuffix = ".golden"

	// FlagName is the name of the command line flag for go test.
	FlagName = "update"

	// FilePerms is used to set the permissions on the golden fixture files.
	FilePerms os.FileMode = 0644

	// DirPerms is used to set the permissions on the golden fixture folder.
	DirPerms os.FileMode = 0755

	// update determines if the actual received data should be written to the
	// golden files or not. This should be true when you need to update the
	// data, but false when actually running the tests.
	update = flag.Bool(FlagName, false, "Update golden test file fixture")
)

// Assert compares the actual data received with the expected data in the
// golden files. If the update flag is set, it will also update the golden
// file.
//
// `name` refers to the name of the test and it should typically be unique
// withing the package. Also it should be a valid file name (so keeping to
// `a-z0-9\-\_` is a good idea).
func Assert(t *testing.T, name string, actualData []byte) {
	if *update {
		err := Update(name, actualData)
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	}

	err := compare(name, actualData)
	if err != nil {
		switch err.(type) {
		case errFixtureNotFound:
			t.Error(err)
			t.FailNow()
		case errFixtureMismatch:
			t.Error(err)
		default:
			t.Error(err)
		}
	}
}

// Update will update the golden fixtures with the received actual data.
//
// This method does not need to be called from code, but it's exposed so that it
// can be explicitly called if needed. The more common approach would be to
// update using `go test -update ./...`.
func Update(name string, actualData []byte) error {
	err := ensureFixtureDir()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(goldenFileName(name), actualData, FilePerms)
	if err != nil {
		return err
	}

	return nil
}

// compare is reading the golden fixture file and compate the stored data with
// the actual data.
func compare(name string, actualData []byte) error {
	expectedData, err := ioutil.ReadFile(goldenFileName(name))
	if err != nil {
		if os.IsNotExist(err) {
			return newErrFixtureNotFound()
		} else {
			return errors.New(fmt.Sprintf("Expected %s to be nil", err.Error()))
		}
	}

	if !bytes.Equal(actualData, expectedData) {
		return newErrFixtureMismatch("Result did not match the golden file")
	}

	return nil
}

// ensureFixtureDir will create the fixture folder if it does not already exist.
func ensureFixtureDir() error {
	_, err := os.Stat(FixtureDir)
	if err == nil {
		return nil
	}

	if os.IsNotExist(err) {
		err = os.Mkdir(FixtureDir, DirPerms)
		if err != nil {
			return err
		}
	}

	return err
}

// goldenFileName simply returns the file name of the golden file fixture.
func goldenFileName(name string) string {
	return filepath.Join(FixtureDir, fmt.Sprintf("%s%s", name, FileNameSuffix))
}
<commit_msg>Print expected and actual output in error message<commit_after>// Package goldie provides test assertions based on golden files. It's typically
// used for testing responses with larger data bodies.
//
// The concept is straight forward. Valid response data is stored in a "golden
// file". The actual response data will be byte compared with the golden file
// and the test will fail if there is a difference.
//
// Updating the golden file can be done by running `go test -update ./...`.
package goldie

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"
)

var (
	// FixtureDir is the folder name for where the fixtures are stored. It's
	// relative to the "go test" path.
	FixtureDir = "fixtures"

	// FileNameSuffix is the suffix appended to the fixtures. Set to empty
	// string to disable file name suffixes.
	FileNameSuffix = ".golden"

	// FlagName is the name of the command line flag for go test.
	FlagName = "update"

	// FilePerms is used to set the permissions on the golden fixture files.
	FilePerms os.FileMode = 0644

	// DirPerms is used to set the permissions on the golden fixture folder.
	DirPerms os.FileMode = 0755

	// update determines if the actual received data should be written to the
	// golden files or not. This should be true when you need to update the
	// data, but false when actually running the tests.
	update = flag.Bool(FlagName, false, "Update golden test file fixture")
)

// Assert compares the actual data received with the expected data in the
// golden files. If the update flag is set, it will also update the golden
// file.
//
// `name` refers to the name of the test and it should typically be unique
// withing the package. Also it should be a valid file name (so keeping to
// `a-z0-9\-\_` is a good idea).
func Assert(t *testing.T, name string, actualData []byte) {
	if *update {
		err := Update(name, actualData)
		if err != nil {
			t.Error(err)
			t.FailNow()
		}
	}

	err := compare(name, actualData)
	if err != nil {
		switch err.(type) {
		case errFixtureNotFound:
			t.Error(err)
			t.FailNow()
		case errFixtureMismatch:
			t.Error(err)
		default:
			t.Error(err)
		}
	}
}

// Update will update the golden fixtures with the received actual data.
//
// This method does not need to be called from code, but it's exposed so that it
// can be explicitly called if needed. The more common approach would be to
// update using `go test -update ./...`.
func Update(name string, actualData []byte) error {
	err := ensureFixtureDir()
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(goldenFileName(name), actualData, FilePerms)
	if err != nil {
		return err
	}

	return nil
}

// compare is reading the golden fixture file and compate the stored data with
// the actual data.
func compare(name string, actualData []byte) error {
	expectedData, err := ioutil.ReadFile(goldenFileName(name))
	if err != nil {
		if os.IsNotExist(err) {
			return newErrFixtureNotFound()
		} else {
			return errors.New(fmt.Sprintf("Expected %s to be nil", err.Error()))
		}
	}

	if !bytes.Equal(actualData, expectedData) {
		return newErrFixtureMismatch(
			fmt.Sprintf("Result did not match the golden fixture.\n"+
				"Expected: %s\n"+
				"Got: %s",
				string(expectedData),
				string(actualData)))
	}

	return nil
}

// ensureFixtureDir will create the fixture folder if it does not already exist.
func ensureFixtureDir() error {
	_, err := os.Stat(FixtureDir)
	if err == nil {
		return nil
	}

	if os.IsNotExist(err) {
		err = os.Mkdir(FixtureDir, DirPerms)
		if err != nil {
			return err
		}
	}

	return err
}

// goldenFileName simply returns the file name of the golden file fixture.
func goldenFileName(name string) string {
	return filepath.Join(FixtureDir, fmt.Sprintf("%s%s", name, FileNameSuffix))
}
<|endoftext|>


<commit_before>// Package gopush provides an implementation of Push 3.0, a stack-based
// programming language designed for genetic programming
package gopush

import (
	"errors"
	"fmt"
	"math/rand"
	"strconv"
	"strings"
)

// Interpreter is a Push interpreter.
type Interpreter struct {
	Stacks      map[string]*Stack
	Options     Options
	Rand        *rand.Rand
	Definitions map[string]Code

	numEvalPush   int
	quoteNextName bool
}

// NewInterpreter returns a new Push Interpreter, configured with the provided Options.
func NewInterpreter(options Options) *Interpreter {
	interpreter := &Interpreter{
		Stacks:        make(map[string]*Stack),
		Options:       options,
		Rand:          rand.New(rand.NewSource(options.RandomSeed)),
		Definitions:   make(map[string]Code),
		numEvalPush:   0,
		quoteNextName: false,
	}

	// Setup stacks
	interpreter.Stacks["exec"] = newExecStack(interpreter)
	interpreter.Stacks["name"] = newNameStack(interpreter)

	if _, ok := options.AllowedTypes["boolean"]; ok {
		interpreter.Stacks["boolean"] = newBooleanStack(interpreter)
	}

	if _, ok := options.AllowedTypes["code"]; ok {
		interpreter.Stacks["code"] = newCodeStack(interpreter)
	}

	if _, ok := options.AllowedTypes["float"]; ok {
		interpreter.Stacks["float"] = newFloatStack(interpreter)
	}

	if _, ok := options.AllowedTypes["integer"]; ok {
		interpreter.Stacks["integer"] = newIntStack(interpreter)
	}

	return interpreter
}

func (i *Interpreter) stackOK(name string, mindepth int64) bool {
	s, ok := i.Stacks[name]
	if !ok {
		return false
	}

	if s.Len() < mindepth {
		return false
	}

	return true
}

func (i *Interpreter) printInterpreterState() {
	fmt.Println("Step", i.numEvalPush)
	for k, v := range i.Stacks {
		fmt.Printf("%s:\n", k)
		for i := len(v.Stack) - 1; i >= 0; i-- {
			fmt.Printf("- %v\n", v.Stack[i])
		}
	}
	fmt.Println()
	fmt.Println()
}

func (i *Interpreter) runCode(program Code) (err error) {

	// Recover from a panic that could occur while executing an instruction.
	// Because it is more convenient for functions to not return an error,
	// the functions that want to return an error panic instead.
	defer func() {
		if perr := recover(); perr != nil {
			err = perr.(error)
		}
	}()

	i.Stacks["exec"].Push(program)

	for i.Stacks["exec"].Len() > 0 && i.numEvalPush < i.Options.EvalPushLimit {

		if i.Options.Tracing {
			i.printInterpreterState()
		}

		item := i.Stacks["exec"].Pop().(Code)
		i.numEvalPush++

		// If the item on top of the exec stack is a list, push it in
		// reverse order
		if item.Literal == "" {
			for j := len(item.List) - 1; j >= 0; j-- {
				i.Stacks["exec"].Push(item.List[j])
			}
			continue
		}

		// Try to parse the item on top of the exec stack as a literal
		if intlit, err := strconv.ParseInt(item.Literal, 10, 64); err == nil {
			i.Stacks["integer"].Push(intlit)
			continue
		}

		if floatlit, err := strconv.ParseFloat(item.Literal, 64); err == nil {
			i.Stacks["float"].Push(floatlit)
			continue
		}

		if boollit, err := strconv.ParseBool(item.Literal); err == nil {
			i.Stacks["boolean"].Push(boollit)
			continue
		}

		// Try to parse the item on top of the exec stack as instruction
		if strings.Contains(item.Literal, ".") {
			stack := strings.ToLower(item.Literal[:strings.Index(item.Literal, ".")])
			operation := strings.ToLower(item.Literal[strings.Index(item.Literal, ".")+1:])

			s, ok := i.Stacks[stack]
			if !ok {
				return fmt.Errorf("unknown or disabled stack: %v", stack)
			}

			f, ok := s.Functions[operation]
			if !ok {
				return fmt.Errorf("unknown or disabled instruction %v.%v", stack, operation)
			}

			f()
			continue
		}

		// If the item is not an instruction, it must be a name, either
		// bound or unbound. If the quoteNextName flag is false, we can
		// check if the name is already bound.
		if !i.quoteNextName {
			if d, ok := i.Definitions[strings.ToLower(item.Literal)]; ok {
				// Name is already bound, push its value onto the exec stack
				i.Stacks["exec"].Push(d)
				continue
			}
		}

		// The name is not bound yet, so push it onto the name stack
		i.Stacks["name"].Push(strings.ToLower(item.Literal))
		i.quoteNextName = false
	}

	if i.numEvalPush >= i.Options.EvalPushLimit {
		return errors.New("the EvalPushLimit was exceeded")
	}

	return nil
}

// Run runs the given program written in the Push programming language until the
// EvalPushLimit is reached
func (i *Interpreter) Run(program string) error {
	c, err := ParseCode(program)
	if err != nil {
		return err
	}

	if i.Options.TopLevelPushCode {
		i.Stacks["code"].Push(c)
	}

	err = i.runCode(c)

	if i.Options.TopLevelPopCode {
		i.Stacks["code"].Pop()
	}

	if i.Options.Tracing {
		i.printInterpreterState()
	}

	return err
}
<commit_msg>Make sure we have a CODE stack before attempting to push/pop it<commit_after>// Package gopush provides an implementation of Push 3.0, a stack-based
// programming language designed for genetic programming
package gopush

import (
	"errors"
	"fmt"
	"math/rand"
	"strconv"
	"strings"
)

// Interpreter is a Push interpreter.
type Interpreter struct {
	Stacks      map[string]*Stack
	Options     Options
	Rand        *rand.Rand
	Definitions map[string]Code

	numEvalPush   int
	quoteNextName bool
}

// NewInterpreter returns a new Push Interpreter, configured with the provided Options.
func NewInterpreter(options Options) *Interpreter {
	interpreter := &Interpreter{
		Stacks:        make(map[string]*Stack),
		Options:       options,
		Rand:          rand.New(rand.NewSource(options.RandomSeed)),
		Definitions:   make(map[string]Code),
		numEvalPush:   0,
		quoteNextName: false,
	}

	// Setup stacks
	interpreter.Stacks["exec"] = newExecStack(interpreter)
	interpreter.Stacks["name"] = newNameStack(interpreter)

	if _, ok := options.AllowedTypes["boolean"]; ok {
		interpreter.Stacks["boolean"] = newBooleanStack(interpreter)
	}

	if _, ok := options.AllowedTypes["code"]; ok {
		interpreter.Stacks["code"] = newCodeStack(interpreter)
	}

	if _, ok := options.AllowedTypes["float"]; ok {
		interpreter.Stacks["float"] = newFloatStack(interpreter)
	}

	if _, ok := options.AllowedTypes["integer"]; ok {
		interpreter.Stacks["integer"] = newIntStack(interpreter)
	}

	return interpreter
}

func (i *Interpreter) stackOK(name string, mindepth int64) bool {
	s, ok := i.Stacks[name]
	if !ok {
		return false
	}

	if s.Len() < mindepth {
		return false
	}

	return true
}

func (i *Interpreter) printInterpreterState() {
	fmt.Println("Step", i.numEvalPush)
	for k, v := range i.Stacks {
		fmt.Printf("%s:\n", k)
		for i := len(v.Stack) - 1; i >= 0; i-- {
			fmt.Printf("- %v\n", v.Stack[i])
		}
	}
	fmt.Println()
	fmt.Println()
}

func (i *Interpreter) runCode(program Code) (err error) {

	// Recover from a panic that could occur while executing an instruction.
	// Because it is more convenient for functions to not return an error,
	// the functions that want to return an error panic instead.
	defer func() {
		if perr := recover(); perr != nil {
			err = perr.(error)
		}
	}()

	i.Stacks["exec"].Push(program)

	for i.Stacks["exec"].Len() > 0 && i.numEvalPush < i.Options.EvalPushLimit {

		if i.Options.Tracing {
			i.printInterpreterState()
		}

		item := i.Stacks["exec"].Pop().(Code)
		i.numEvalPush++

		// If the item on top of the exec stack is a list, push it in
		// reverse order
		if item.Literal == "" {
			for j := len(item.List) - 1; j >= 0; j-- {
				i.Stacks["exec"].Push(item.List[j])
			}
			continue
		}

		// Try to parse the item on top of the exec stack as a literal
		if intlit, err := strconv.ParseInt(item.Literal, 10, 64); err == nil {
			i.Stacks["integer"].Push(intlit)
			continue
		}

		if floatlit, err := strconv.ParseFloat(item.Literal, 64); err == nil {
			i.Stacks["float"].Push(floatlit)
			continue
		}

		if boollit, err := strconv.ParseBool(item.Literal); err == nil {
			i.Stacks["boolean"].Push(boollit)
			continue
		}

		// Try to parse the item on top of the exec stack as instruction
		if strings.Contains(item.Literal, ".") {
			stack := strings.ToLower(item.Literal[:strings.Index(item.Literal, ".")])
			operation := strings.ToLower(item.Literal[strings.Index(item.Literal, ".")+1:])

			s, ok := i.Stacks[stack]
			if !ok {
				return fmt.Errorf("unknown or disabled stack: %v", stack)
			}

			f, ok := s.Functions[operation]
			if !ok {
				return fmt.Errorf("unknown or disabled instruction %v.%v", stack, operation)
			}

			f()
			continue
		}

		// If the item is not an instruction, it must be a name, either
		// bound or unbound. If the quoteNextName flag is false, we can
		// check if the name is already bound.
		if !i.quoteNextName {
			if d, ok := i.Definitions[strings.ToLower(item.Literal)]; ok {
				// Name is already bound, push its value onto the exec stack
				i.Stacks["exec"].Push(d)
				continue
			}
		}

		// The name is not bound yet, so push it onto the name stack
		i.Stacks["name"].Push(strings.ToLower(item.Literal))
		i.quoteNextName = false
	}

	if i.numEvalPush >= i.Options.EvalPushLimit {
		return errors.New("the EvalPushLimit was exceeded")
	}

	return nil
}

// Run runs the given program written in the Push programming language until the
// EvalPushLimit is reached
func (i *Interpreter) Run(program string) error {
	c, err := ParseCode(program)
	if err != nil {
		return err
	}

	if i.Options.TopLevelPushCode {
		if s, ok := i.Stacks["code"]; ok {
			s.Push(c)
		}
	}

	err = i.runCode(c)

	if i.Options.TopLevelPopCode {
		if s, ok := i.Stacks["code"]; ok {
			s.Pop()
		}
	}

	if i.Options.Tracing {
		i.printInterpreterState()
	}

	return err
}
<|endoftext|>