<commit_before>package consensus

import (
	"github.com/golang/glog"
	"github.com/heidi-ann/ios/msgs"
	"time"
)

var noop = msgs.ClientRequest{-1, -1, true, "noop"}

// RunMaster implements the Master mode
func RunMaster(view int, commit_index int, initial bool, io *msgs.Io, config Config) {
	// setup
	glog.Info("Starting up master in view ", view)
	glog.Info("Master is configured to delegate replication to ",config.DelegateReplication)
	majority := Majority(config.N)

	// determine next safe index
	index := -1
	if !initial {
		// dispatch new view requests
		req := msgs.NewViewRequest{config.ID, view}
		(*io).OutgoingBroadcast.Requests.NewView <- req

		// collect responses
		glog.Info("Waiting for ", majority, " new view responses")
		min_index := commit_index
		// TODO: FEATURE add option to wait longer

		for i := 0; i < majority; {
			msg := <-(*io).Incoming.Responses.NewView
			// check msg replies to the msg we just sent
			if msg.Request == req {
				res := msg.Response
				glog.Info("Received ", res)
				if res.Index > index {
					index = res.Index
				} else if res.Index < min_index {
					min_index = res.Index
				}
				i++
				// TODO: BUG need to check view
				glog.Info("Successful new view received, waiting for ", majority-i, " more")
			}

		}
		glog.Info("Index is ", index)

		// recover entries
		for curr_index := commit_index + 1; curr_index <= index; curr_index++ {
			RunRecoveryCoordinator(view, curr_index, io, config)
		}

	}
	// store the first coordinator to ask
	coordinator := config.ID
	if config.DelegateReplication > 0 {
		coordinator += 1
	}

	if config.BatchInterval == 0 {
		glog.Info("Ready to handle requests. No batching enabled")
		// handle client requests (1 at a time)
		for {

			// wait for request
			req := <-io.IncomingRequests
			glog.Info("Request received: ", req)

			// if possible, handle request without replication
			if !req.Replicate {
				io.OutgoingRequests <- req
				glog.Info("Read-only request was handled without replication: ", req)
			} else {
				index++
				glog.Info("Request assigned index: ", index)
				// ok := RunCoordinator(view, index, []msgs.ClientRequest{req}, io, config, true)
				entry := msgs.Entry{view, false, []msgs.ClientRequest{req}}
				coord := msgs.CoordinateRequest{config.ID, view, index, true, entry}
				io.OutgoingUnicast[coordinator].Requests.Coordinate <- coord
				// TODO: BUG: need to handle coordinator failure
				reply := <-(*io).Incoming.Responses.Coordinate
				// TODO: check msg replies to the msg we just sent
				if !reply.Response.Success {
					break
				}
				// glog.Info("Finished replicating request: ", req)

				// rotate coordinator is nessacary
				if config.DelegateReplication > 1 {
					coordinator += 1
					if coordinator>config.ID + config.DelegateReplication {
						coordinator = config.ID + 1
					}
				}
			}

		}
	} else {
		glog.Info("Ready to handle request. Batch every ", config.BatchInterval, " microseconds")
		for {
			// setup for holding requests
			reqs := make([]msgs.ClientRequest, config.MaxBatch)
			reqs_num := 0

			// start collecting requests
			timeout := make(chan bool, 1)
			go func() {
				<-time.After(time.Microsecond * time.Duration(config.BatchInterval))
				timeout <- true
			}()

			exit := false
			for exit == false {
				select {
				case req := <-io.IncomingRequests:
					if !req.Replicate {
						io.OutgoingRequests <- req
						glog.Info("Request handled without replication: ", req)
					} else {
						reqs[reqs_num] = req
						glog.Info("Request ", reqs_num, " is : ", req)
						reqs_num = reqs_num + 1
						if reqs_num == config.MaxBatch {
							exit = true
							break
						}
					}
				case <-timeout:
					exit = true
					break
				}
			}

			// assign requests to coordinators
			if reqs_num > 0 {
				glog.Info("Starting to replicate ", reqs_num, " requests")
				reqs_small := reqs[:reqs_num]
				index++
				// ok := RunCoordinator(view, index, reqs_small, io, config, true)
				entry := msgs.Entry{view, false, reqs_small}
				coord := msgs.CoordinateRequest{config.ID, view, index, true, entry}
				io.OutgoingUnicast[coordinator].Requests.Coordinate <- coord
				reply := <- io.Incoming.Responses.Coordinate
				// TODO: check msg replies to the msg we just sent
				if !reply.Response.Success {
					break
				}
				glog.Info("Finished replicating requests: ", reqs_small)

				// rotate coordinator is nessacary
				if config.DelegateReplication > 1 {
					coordinator += 1
					if coordinator>config.ID + config.DelegateReplication {
						coordinator = config.ID + 1
					}
				}
			}
		}

	}

	glog.Warning("Master stepping down")

}
<commit_msg>allow parrallel coordinators<commit_after>package consensus

import (
	"github.com/golang/glog"
	"github.com/heidi-ann/ios/msgs"
	"time"
)

var noop = msgs.ClientRequest{-1, -1, true, "noop"}

// RunMaster implements the Master mode
func RunMaster(view int, commit_index int, initial bool, io *msgs.Io, config Config) {
	// setup
	glog.Info("Starting up master in view ", view)
	glog.Info("Master is configured to delegate replication to ",config.DelegateReplication)
	majority := Majority(config.N)

	// determine next safe index
	index := -1
	if !initial {
		// dispatch new view requests
		req := msgs.NewViewRequest{config.ID, view}
		(*io).OutgoingBroadcast.Requests.NewView <- req

		// collect responses
		glog.Info("Waiting for ", majority, " new view responses")
		min_index := commit_index
		// TODO: FEATURE add option to wait longer

		for i := 0; i < majority; {
			msg := <-(*io).Incoming.Responses.NewView
			// check msg replies to the msg we just sent
			if msg.Request == req {
				res := msg.Response
				glog.Info("Received ", res)
				if res.Index > index {
					index = res.Index
				} else if res.Index < min_index {
					min_index = res.Index
				}
				i++
				// TODO: BUG need to check view
				glog.Info("Successful new view received, waiting for ", majority-i, " more")
			}

		}
		glog.Info("Index is ", index)

		// recover entries
		for curr_index := commit_index + 1; curr_index <= index; curr_index++ {
			RunRecoveryCoordinator(view, curr_index, io, config)
		}

	}
	// store the first coordinator to ask
	coordinator := config.ID
	if config.DelegateReplication > 0 {
		coordinator += 1
	}

	if config.BatchInterval == 0 {
		glog.Info("Ready to handle requests. No batching enabled")
		// handle client requests (1 at a time)
		for {

			// wait for request
			req := <-io.IncomingRequests
			glog.Info("Request received: ", req)

			// if possible, handle request without replication
			if !req.Replicate {
				io.OutgoingRequests <- req
				glog.Info("Read-only request was handled without replication: ", req)
			} else {
				index++
				glog.Info("Request assigned index: ", index)
				// ok := RunCoordinator(view, index, []msgs.ClientRequest{req}, io, config, true)
				entry := msgs.Entry{view, false, []msgs.ClientRequest{req}}
				coord := msgs.CoordinateRequest{config.ID, view, index, true, entry}
				io.OutgoingUnicast[coordinator].Requests.Coordinate <- coord
				// TODO: BUG: need to handle coordinator failure
				go func() {
					reply := <-(*io).Incoming.Responses.Coordinate
					// TODO: check msg replies to the msg we just sent
					if !reply.Response.Success {
						glog.Warning("Commit unsuccessful")
					}
					glog.Info("Finished replicating request: ", req)
				}()

				// rotate coordinator is nessacary
				if config.DelegateReplication > 1 {
					coordinator += 1
					if coordinator>config.ID + config.DelegateReplication {
						coordinator = config.ID + 1
					}
				}
			}

		}
	} else {
		glog.Info("Ready to handle request. Batch every ", config.BatchInterval, " microseconds")
		for {
			// setup for holding requests
			reqs := make([]msgs.ClientRequest, config.MaxBatch)
			reqs_num := 0

			// start collecting requests
			timeout := make(chan bool, 1)
			go func() {
				<-time.After(time.Microsecond * time.Duration(config.BatchInterval))
				timeout <- true
			}()

			exit := false
			for exit == false {
				select {
				case req := <-io.IncomingRequests:
					if !req.Replicate {
						io.OutgoingRequests <- req
						glog.Info("Request handled without replication: ", req)
					} else {
						reqs[reqs_num] = req
						glog.Info("Request ", reqs_num, " is : ", req)
						reqs_num = reqs_num + 1
						if reqs_num == config.MaxBatch {
							exit = true
							break
						}
					}
				case <-timeout:
					exit = true
					break
				}
			}

			// assign requests to coordinators
			if reqs_num > 0 {
				glog.Info("Starting to replicate ", reqs_num, " requests")
				reqs_small := reqs[:reqs_num]
				index++
				// ok := RunCoordinator(view, index, reqs_small, io, config, true)
				entry := msgs.Entry{view, false, reqs_small}
				coord := msgs.CoordinateRequest{config.ID, view, index, true, entry}
				io.OutgoingUnicast[coordinator].Requests.Coordinate <- coord
				go func() {
					reply := <- io.Incoming.Responses.Coordinate
					// TODO: check msg replies to the msg we just sent
					if !reply.Response.Success {
						glog.Warning("Commit unsuccessful")
					}
					glog.Info("Finished replicating requests: ", reqs_small)
				} ()
				// rotate coordinator is nessacary
				if config.DelegateReplication > 1 {
					coordinator += 1
					if coordinator>config.ID + config.DelegateReplication {
						coordinator = config.ID + 1
					}
				}
			}
		}

	}

	glog.Warning("Master stepping down")

}
<|endoftext|>


<commit_before>package service

import (
	"database/sql"
	_ "github.com/lib/pq"
	"github.com/nleof/goyesql"
	"log"
	"os"
)

// PsqlDao is an entity that implements per-user connection pool limits and
// facilitates access to SQL queries loaded from disk.
type PsqlDao struct {
	// dao is a collection of *sql.DB identified by strings (usernames), the point
	// being that each user gets their own connection pool
	dao  map[string]*sql.DB
	ddl  goyesql.Queries
	dml  goyesql.Queries
	conn string
}

// AppDB is an identifier for a specific *sql.DB in our dao map
const AppDB = "_app"

func NewPsqlDao(conn string) *PsqlDao {
	db := PsqlDao{}
	db.conn = os.Getenv("DATABASE_URL")

	if db.conn == "" {
		log.Fatal("DATABASE_URL environment variable not set!")
	}

	db.dao = make(map[string]*sql.DB)

	// Create all the Tables, Views if they do not exist
	db.ddl = goyesql.MustParseFile("data/sql/ddl.sql")

	db.EnsurePool(AppDB)

	logExec(db.dao[AppDB], (string)(db.ddl["create-user-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-command-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-tag-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-invocation-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-invocationtag-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-commandhistory-view"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-timestamp-index"]))

	// Load all data-manipulation queries
	db.dml = goyesql.MustParseFile("data/sql/queries.sql")

	log.Println("storage init completed")
	return &db
}

// Query fetches an SQL query that matches the given identifier
func (r *PsqlDao) Query(identifier string) string {
	return (string)(r.dml[(goyesql.Tag)(identifier)])
}

func logExec(conn *sql.DB, query string) {
	log.Printf("executing query: '%s'\n", query)
	_, err := conn.Exec(query)
	if err != nil {
		log.Fatal(err)
	} else {
		log.Printf("executed query: '%s'\n", query)
	}
}

// EnsurePool verifies that we have created a connection pool for the given user
func (r *PsqlDao) EnsurePool(user string) (db *sql.DB) {
	db, exists := r.dao[user]

	if !exists {
		var err error
		r.dao[user], err = sql.Open("postgres", r.conn)
		r.dao[user].SetMaxOpenConns(5)
		r.dao[user].SetMaxIdleConns(1)

		if err != nil {
			log.Fatal(err)
		}
	}
	return
}
<commit_msg>fix bad pointer on first EnsurePool<commit_after>package service

import (
	"database/sql"
	_ "github.com/lib/pq"
	"github.com/nleof/goyesql"
	"log"
	"os"
)

// PsqlDao is an entity that implements per-user connection pool limits and
// facilitates access to SQL queries loaded from disk.
type PsqlDao struct {
	// dao is a collection of *sql.DB identified by strings (usernames), the point
	// being that each user gets their own connection pool
	dao  map[string]*sql.DB
	ddl  goyesql.Queries
	dml  goyesql.Queries
	conn string
}

// AppDB is an identifier for a specific *sql.DB in our dao map
const AppDB = "_app"

func NewPsqlDao(conn string) *PsqlDao {
	db := PsqlDao{}
	db.conn = os.Getenv("DATABASE_URL")

	if db.conn == "" {
		log.Fatal("DATABASE_URL environment variable not set!")
	}

	db.dao = make(map[string]*sql.DB)

	// Create all the Tables, Views if they do not exist
	db.ddl = goyesql.MustParseFile("data/sql/ddl.sql")

	db.EnsurePool(AppDB)

	logExec(db.dao[AppDB], (string)(db.ddl["create-user-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-command-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-tag-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-invocation-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-invocationtag-table"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-commandhistory-view"]))
	logExec(db.dao[AppDB], (string)(db.ddl["create-timestamp-index"]))

	// Load all data-manipulation queries
	db.dml = goyesql.MustParseFile("data/sql/queries.sql")

	log.Println("storage init completed")
	return &db
}

// Query fetches an SQL query that matches the given identifier
func (r *PsqlDao) Query(identifier string) string {
	return (string)(r.dml[(goyesql.Tag)(identifier)])
}

func logExec(conn *sql.DB, query string) {
	log.Printf("executing query: '%s'\n", query)
	_, err := conn.Exec(query)
	if err != nil {
		log.Fatal(err)
	} else {
		log.Printf("executed query: '%s'\n", query)
	}
}

// EnsurePool verifies that we have created a connection pool for the given user
func (r *PsqlDao) EnsurePool(user string) *sql.DB {
	_, exists := r.dao[user]

	if !exists {
		var err error
		r.dao[user], err = sql.Open("postgres", r.conn)
		r.dao[user].SetMaxOpenConns(5)
		r.dao[user].SetMaxIdleConns(1)

		if err != nil {
			log.Fatal(err)
		}
	}
	return r.dao[user]
}
<|endoftext|>


<commit_before>// Copyright 2015 Openprovider Authors. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

package service

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/openprovider/whoisd/client"
	"github.com/openprovider/whoisd/config"
	"github.com/openprovider/whoisd/storage"
	"github.com/takama/daemon"
)

// Version of the Whois Daemon
// Date of current version release
const (
	Version = "0.3.4"
	Date    = "2015-11-05T16:07:17Z"
)

// simplest logger, which initialized during starts of the application
var (
	stdlog = log.New(os.Stdout, "[SERVICE]: ", log.Ldate|log.Ltime)
	errlog = log.New(os.Stderr, "[SERVICE:ERROR]: ", log.Ldate|log.Ltime|log.Lshortfile)
)

// Record - standard record (struct) for service package
type Record struct {
	Name   string
	Config *config.Record
	daemon.Daemon
}

// New - Create a new service record
func New(name, description string) (*Record, error) {
	daemonInstance, err := daemon.New(name, description)
	if err != nil {
		return nil, err
	}

	return &Record{name, config.New(), daemonInstance}, nil
}

// Run or manage the service
func (service *Record) Run() (string, error) {

	// if received any kind of command, do it
	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install":
			return service.Install()
		case "remove":
			return service.Remove()
		case "start":
			return service.Start()
		case "stop":
			return service.Stop()
		case "status":
			return service.Status()
		}
	}

	// Load configuration and get mapping
	bundle, err := service.Config.Load()
	if err != nil {
		return "Loading mapping file was unsuccessful", err
	}

	// Logs for what is host&port used
	serviceHostPort := fmt.Sprintf("%s:%d", service.Config.Host, service.Config.Port)
	stdlog.Printf("%s started on %s\n", service.Name, serviceHostPort)
	stdlog.Printf("Used storage %s on %s:%d\n",
		service.Config.Storage.StorageType,
		service.Config.Storage.Host,
		service.Config.Storage.Port,
	)

	// Set up listener for defined host and port
	listener, err := net.Listen("tcp", serviceHostPort)
	if err != nil {
		return "Possibly was a problem with the port binding", err
	}

	// set up channel to collect client queries
	channel := make(chan client.Record, service.Config.Connections)

	// set up current storage
	repository := storage.New(service.Config, bundle)

	// init workers
	for i := 0; i < service.Config.Workers; i++ {
		go client.ProcessClient(channel, repository)
	}

	// This block is for testing purpose only
	if service.Config.TestMode == true {
		// make pipe connections for testing
		// connIn will ready to write into by function ProcessClient
		connIn, connOut := net.Pipe()
		defer connIn.Close()
		defer connOut.Close()
		newClient := client.Record{Conn: connIn}

		// prepare query for ProcessClient
		newClient.Query = []byte(service.Config.TestQuery)

		// send it into channel
		channel <- newClient
		// just read answer from channel pipe
		buffer := make([]byte, 4096)
		numBytes, err := connOut.Read(buffer)
		stdlog.Println("Read bytes:", numBytes)
		return string(buffer), err
	}

	// Set up channel on which to send signal notifications.
	// We must use a buffered channel or risk missing the signal
	// if we're not ready to receive when the signal is sent.
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

	// set up channel on which to send accepted connections
	listen := make(chan net.Conn, service.Config.Connections)
	go acceptConnection(listener, listen)

	// loop work cycle with accept connections or interrupt
	// by system signal
	for {
		select {
		case conn := <-listen:
			newClient := client.Record{Conn: conn}
			go newClient.HandleClient(channel)
		case killSignal := <-interrupt:
			stdlog.Println("Got signal:", killSignal)
			stdlog.Println("Stoping listening on ", listener.Addr())
			listener.Close()
			if killSignal == os.Interrupt {
				return "Daemon was interruped by system signal", nil
			}
			return "Daemon was killed", nil
		}
	}

	// never happen, but need to complete code
	return "If you see that, you are lucky bastard", nil
}

// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
	defer func() {
		if recovery := recover(); recovery != nil {
			errlog.Println("Recovered in ListenConnection:", recovery)
		}
	}()
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		listen <- conn
	}
}
<commit_msg>Bumped version number to 0.4.0<commit_after>// Copyright 2015 Openprovider Authors. All rights reserved.
// Use of this source code is governed by a license
// that can be found in the LICENSE file.

package service

import (
	"fmt"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/openprovider/whoisd/client"
	"github.com/openprovider/whoisd/config"
	"github.com/openprovider/whoisd/storage"
	"github.com/takama/daemon"
)

// Version of the Whois Daemon
// Date of current version release
const (
	Version = "0.4.0"
	Date    = "2016-02-07T21:05:00Z"
)

// simplest logger, which initialized during starts of the application
var (
	stdlog = log.New(os.Stdout, "[SERVICE]: ", log.Ldate|log.Ltime)
	errlog = log.New(os.Stderr, "[SERVICE:ERROR]: ", log.Ldate|log.Ltime|log.Lshortfile)
)

// Record - standard record (struct) for service package
type Record struct {
	Name   string
	Config *config.Record
	daemon.Daemon
}

// New - Create a new service record
func New(name, description string) (*Record, error) {
	daemonInstance, err := daemon.New(name, description)
	if err != nil {
		return nil, err
	}

	return &Record{name, config.New(), daemonInstance}, nil
}

// Run or manage the service
func (service *Record) Run() (string, error) {

	// if received any kind of command, do it
	if len(os.Args) > 1 {
		command := os.Args[1]
		switch command {
		case "install":
			return service.Install()
		case "remove":
			return service.Remove()
		case "start":
			return service.Start()
		case "stop":
			return service.Stop()
		case "status":
			return service.Status()
		}
	}

	// Load configuration and get mapping
	bundle, err := service.Config.Load()
	if err != nil {
		return "Loading mapping file was unsuccessful", err
	}

	// Logs for what is host&port used
	serviceHostPort := fmt.Sprintf("%s:%d", service.Config.Host, service.Config.Port)
	stdlog.Printf("%s started on %s\n", service.Name, serviceHostPort)
	stdlog.Printf("Used storage %s on %s:%d\n",
		service.Config.Storage.StorageType,
		service.Config.Storage.Host,
		service.Config.Storage.Port,
	)

	// Set up listener for defined host and port
	listener, err := net.Listen("tcp", serviceHostPort)
	if err != nil {
		return "Possibly was a problem with the port binding", err
	}

	// set up channel to collect client queries
	channel := make(chan client.Record, service.Config.Connections)

	// set up current storage
	repository := storage.New(service.Config, bundle)

	// init workers
	for i := 0; i < service.Config.Workers; i++ {
		go client.ProcessClient(channel, repository)
	}

	// This block is for testing purpose only
	if service.Config.TestMode == true {
		// make pipe connections for testing
		// connIn will ready to write into by function ProcessClient
		connIn, connOut := net.Pipe()
		defer connIn.Close()
		defer connOut.Close()
		newClient := client.Record{Conn: connIn}

		// prepare query for ProcessClient
		newClient.Query = []byte(service.Config.TestQuery)

		// send it into channel
		channel <- newClient
		// just read answer from channel pipe
		buffer := make([]byte, 4096)
		numBytes, err := connOut.Read(buffer)
		stdlog.Println("Read bytes:", numBytes)
		return string(buffer), err
	}

	// Set up channel on which to send signal notifications.
	// We must use a buffered channel or risk missing the signal
	// if we're not ready to receive when the signal is sent.
	interrupt := make(chan os.Signal, 1)
	signal.Notify(interrupt, os.Interrupt, os.Kill, syscall.SIGTERM)

	// set up channel on which to send accepted connections
	listen := make(chan net.Conn, service.Config.Connections)
	go acceptConnection(listener, listen)

	// loop work cycle with accept connections or interrupt
	// by system signal
	for {
		select {
		case conn := <-listen:
			newClient := client.Record{Conn: conn}
			go newClient.HandleClient(channel)
		case killSignal := <-interrupt:
			stdlog.Println("Got signal:", killSignal)
			stdlog.Println("Stoping listening on ", listener.Addr())
			listener.Close()
			if killSignal == os.Interrupt {
				return "Daemon was interruped by system signal", nil
			}
			return "Daemon was killed", nil
		}
	}

	// never happen, but need to complete code
	return "If you see that, you are lucky bastard", nil
}

// Accept a client connection and collect it in a channel
func acceptConnection(listener net.Listener, listen chan<- net.Conn) {
	defer func() {
		if recovery := recover(); recovery != nil {
			errlog.Println("Recovered in ListenConnection:", recovery)
		}
	}()
	for {
		conn, err := listener.Accept()
		if err != nil {
			continue
		}
		listen <- conn
	}
}
<|endoftext|>


<commit_before>package main_test

import (
	"os"
	"path/filepath"
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/markelog/eclectica/io"
	"github.com/markelog/eclectica/plugins"
)

var _ = Describe("python", func() {
	var (
		pipBin = filepath.Join(bins, "pip")
		eIBin  = filepath.Join(bins, "easy_install")
	)

	Describe("2.x", func() {
		Describe("old", func() {
			if shouldRun("python2-old") == false {
				return
			}

			It(`should install "old" 2.6.9 version`, func() {
				Execute("go", "run", path, "python@2.6.9")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.6.9")).To(Equal(true))

				Command("go", "run", path, "rm", "python@2.6.9").Output()
			})

			It(`should install "old" 2.7.0 version`, func() {
				Execute("go", "run", path, "python@2.7.0")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.7.0")).To(Equal(true))

				Command("go", "run", path, "rm", "python@2.7.0").Output()
			})
		})

		Describe("2.7.x versions", func() {
			if shouldRun("python2.7") == false {
				return
			}

			BeforeEach(func() {
				Execute("go", "run", path, "python@2.7.10")
				Execute("go", "run", path, "python@2.7.12")
			})

			It(`should install 2.7.13 version`, func() {
				Execute("go", "run", path, "python@2.7.13")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.7.13")).To(Equal(true))

				Command("go", "run", path, "rm", "python@2.7.13").Output()
			})

			It(`should install latest 2.x.x version`, func() {
				Execute("go", "run", path, "python@2")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.")).To(Equal(true))
			})

			It("should list installed versions", func() {
				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.7.12")).To(Equal(true))
			})

			It("should use local version", func() {
				pwd, _ := os.Getwd()
				versionFile := filepath.Join(filepath.Dir(pwd), ".python-version")

				io.WriteFile(versionFile, "2.7.10")

				command, _ := Command("go", "run", path, "ls", "python").Output()
				Expect(strings.Contains(string(command), "♥ 2.7.10")).To(Equal(true))

				err := os.RemoveAll(versionFile)
				Expect(err).To(BeNil())
			})

			It("should list remote versions", func() {
				Expect(checkRemoteList("python", "2.x", 150)).To(Equal(true))
			})

			It("should remove version", func() {
				result := true

				Command("go", "run", path, "rm", "python@2.7.12").Output()

				plugin := plugins.New(&plugins.Args{
					Language: "python",
				})
				versions := plugin.List()

				for _, version := range versions {
					if version == "2.7.12" {
						result = false
					}
				}

				Expect(result).To(Equal(true))

				Execute("go", "run", path, "python@2.7.12")
			})

			It("should have pip installed when it delivered with binaries", func() {
				command, err := Command(pipBin).CombinedOutput()

				Expect(strings.Contains(string(command), "has not been established")).To(Equal(false))
				Expect(err).To(BeNil())
			})

			It("should have easy_install installed when it delivered with binaries", func() {
				command, _ := Command(eIBin).CombinedOutput()

				expected := "error: No urls, filenames, or requirements specified (see --help)"
				actual := string(command)

				Expect(actual).ToNot(ContainSubstring("has not been established"))
				Expect(actual).To(ContainSubstring(expected))
			})

			Describe("2.7.8 version", func() {
				BeforeEach(func() {
					Execute("go", "run", path, "python@2.7.8")
				})

				It("should have pip installed when downloaded", func() {
					command, err := Command(pipBin).CombinedOutput()

					Expect(strings.Contains(string(command), "has not been established")).To(Equal(false))
					Expect(err).To(BeNil())
				})

				It("should have easy_install installed when downloaded", func() {
					command, _ := Command(eIBin).CombinedOutput()

					expected := "error: No urls, filenames, or requirements specified (see --help)"
					actual := string(command)

					Expect(actual).ToNot(ContainSubstring("has not been established"))
					Expect(actual).To(ContainSubstring(expected))
				})
			})
		})
	})

	Describe("3.x", func() {
		if shouldRun("python3") == false {
			return
		}

		BeforeEach(func() {
			Execute("go", "run", path, "python@3.5.1")
			Execute("go", "run", path, "python@3.5.2")
		})

		It("should list installed versions", func() {
			command, _ := Command("go", "run", path, "ls", "python").Output()

			Expect(strings.Contains(string(command), "♥ 3.5.2")).To(Equal(true))
		})

		It("should use local version", func() {
			pwd, _ := os.Getwd()
			versionFile := filepath.Join(filepath.Dir(pwd), ".python-version")

			io.WriteFile(versionFile, "3.5.1")

			command, _ := Command("go", "run", path, "ls", "python").Output()

			Expect(strings.Contains(string(command), "♥ 3.5.1")).To(Equal(true))

			err := os.RemoveAll(versionFile)

			Expect(err).To(BeNil())
		})

		It("should list remote versions", func() {
			Expect(checkRemoteList("python", "3.x", 50)).To(Equal(true))
		})

		It("should remove version", func() {
			result := true

			Command("go", "run", path, "rm", "python@3.5.2").Output()

			plugin := plugins.New(&plugins.Args{
				Language: "python",
			})
			versions := plugin.List()

			for _, version := range versions {
				if version == "3.5.2" {
					result = false
				}
			}

			Expect(result).To(Equal(true))
			Execute("go", "run", path, "python@3.5.2")
		})

		It("should have pip installed when it delivered with binaries", func() {
			command, err := Command(pipBin).CombinedOutput()

			Expect(strings.Contains(string(command), "has not been established")).To(Equal(false))
			Expect(err).To(BeNil())
		})

		It("should have easy_install installed when it delivered with binaries", func() {
			command, _ := Command(eIBin).CombinedOutput()

			expected := "error: No urls, filenames, or requirements specified (see --help)"
			actual := string(command)

			Expect(actual).ToNot(ContainSubstring("has not been established"))
			Expect(actual).To(ContainSubstring(expected))
		})
	})
})
<commit_msg>Skip the test for python 2.7.0 check<commit_after>package main_test

import (
	"os"
	"path/filepath"
	"strings"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/markelog/eclectica/io"
	"github.com/markelog/eclectica/plugins"
)

var _ = Describe("python", func() {
	var (
		pipBin = filepath.Join(bins, "pip")
		eIBin  = filepath.Join(bins, "easy_install")
	)

	Describe("2.x", func() {
		Describe("old", func() {
			if shouldRun("python2-old") == false {
				return
			}

			It(`should install "old" 2.6.9 version`, func() {
				Execute("go", "run", path, "python@2.6.9")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.6.9")).To(Equal(true))

				Command("go", "run", path, "rm", "python@2.6.9").Output()
			})

			It(`should install "old" 2.7.0 version`, func() {
				Skip("zlib for some reason is not available on linux :/")

				Execute("go", "run", path, "python@2.7.0")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.7.0")).To(Equal(true))

				Command("go", "run", path, "rm", "python@2.7.0").Output()
			})
		})

		Describe("2.7.x versions", func() {
			if shouldRun("python2.7") == false {
				return
			}

			BeforeEach(func() {
				Execute("go", "run", path, "python@2.7.10")
				Execute("go", "run", path, "python@2.7.12")
			})

			It(`should install 2.7.13 version`, func() {
				Execute("go", "run", path, "python@2.7.13")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.7.13")).To(Equal(true))

				Command("go", "run", path, "rm", "python@2.7.13").Output()
			})

			It(`should install latest 2.x.x version`, func() {
				Execute("go", "run", path, "python@2")

				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.")).To(Equal(true))
			})

			It("should list installed versions", func() {
				command, _ := Command("go", "run", path, "ls", "python").Output()

				Expect(strings.Contains(string(command), "♥ 2.7.12")).To(Equal(true))
			})

			It("should use local version", func() {
				pwd, _ := os.Getwd()
				versionFile := filepath.Join(filepath.Dir(pwd), ".python-version")

				io.WriteFile(versionFile, "2.7.10")

				command, _ := Command("go", "run", path, "ls", "python").Output()
				Expect(strings.Contains(string(command), "♥ 2.7.10")).To(Equal(true))

				err := os.RemoveAll(versionFile)
				Expect(err).To(BeNil())
			})

			It("should list remote versions", func() {
				Expect(checkRemoteList("python", "2.x", 150)).To(Equal(true))
			})

			It("should remove version", func() {
				result := true

				Command("go", "run", path, "rm", "python@2.7.12").Output()

				plugin := plugins.New(&plugins.Args{
					Language: "python",
				})
				versions := plugin.List()

				for _, version := range versions {
					if version == "2.7.12" {
						result = false
					}
				}

				Expect(result).To(Equal(true))

				Execute("go", "run", path, "python@2.7.12")
			})

			It("should have pip installed when it delivered with binaries", func() {
				command, err := Command(pipBin).CombinedOutput()

				Expect(strings.Contains(string(command), "has not been established")).To(Equal(false))
				Expect(err).To(BeNil())
			})

			It("should have easy_install installed when it delivered with binaries", func() {
				command, _ := Command(eIBin).CombinedOutput()

				expected := "error: No urls, filenames, or requirements specified (see --help)"
				actual := string(command)

				Expect(actual).ToNot(ContainSubstring("has not been established"))
				Expect(actual).To(ContainSubstring(expected))
			})

			Describe("2.7.8 version", func() {
				BeforeEach(func() {
					Execute("go", "run", path, "python@2.7.8")
				})

				It("should have pip installed when downloaded", func() {
					command, err := Command(pipBin).CombinedOutput()

					Expect(strings.Contains(string(command), "has not been established")).To(Equal(false))
					Expect(err).To(BeNil())
				})

				It("should have easy_install installed when downloaded", func() {
					command, _ := Command(eIBin).CombinedOutput()

					expected := "error: No urls, filenames, or requirements specified (see --help)"
					actual := string(command)

					Expect(actual).ToNot(ContainSubstring("has not been established"))
					Expect(actual).To(ContainSubstring(expected))
				})
			})
		})
	})

	Describe("3.x", func() {
		if shouldRun("python3") == false {
			return
		}

		BeforeEach(func() {
			Execute("go", "run", path, "python@3.5.1")
			Execute("go", "run", path, "python@3.5.2")
		})

		It("should list installed versions", func() {
			command, _ := Command("go", "run", path, "ls", "python").Output()

			Expect(strings.Contains(string(command), "♥ 3.5.2")).To(Equal(true))
		})

		It("should use local version", func() {
			pwd, _ := os.Getwd()
			versionFile := filepath.Join(filepath.Dir(pwd), ".python-version")

			io.WriteFile(versionFile, "3.5.1")

			command, _ := Command("go", "run", path, "ls", "python").Output()

			Expect(strings.Contains(string(command), "♥ 3.5.1")).To(Equal(true))

			err := os.RemoveAll(versionFile)

			Expect(err).To(BeNil())
		})

		It("should list remote versions", func() {
			Expect(checkRemoteList("python", "3.x", 50)).To(Equal(true))
		})

		It("should remove version", func() {
			result := true

			Command("go", "run", path, "rm", "python@3.5.2").Output()

			plugin := plugins.New(&plugins.Args{
				Language: "python",
			})
			versions := plugin.List()

			for _, version := range versions {
				if version == "3.5.2" {
					result = false
				}
			}

			Expect(result).To(Equal(true))
			Execute("go", "run", path, "python@3.5.2")
		})

		It("should have pip installed when it delivered with binaries", func() {
			command, err := Command(pipBin).CombinedOutput()

			Expect(strings.Contains(string(command), "has not been established")).To(Equal(false))
			Expect(err).To(BeNil())
		})

		It("should have easy_install installed when it delivered with binaries", func() {
			command, _ := Command(eIBin).CombinedOutput()

			expected := "error: No urls, filenames, or requirements specified (see --help)"
			actual := string(command)

			Expect(actual).ToNot(ContainSubstring("has not been established"))
			Expect(actual).To(ContainSubstring(expected))
		})
	})
})
<|endoftext|>


<commit_before>package image

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

const (
	testAPIServerString = `
{
	"data": [
		{
			"id": 1,
			"infra": "test",
			"name": "travis-ci-awesome"
		}
	]
}
`
	testAPIServerEmptyResponseString = `{"data": []}`
)

var (
	testAPITagTestCases = []struct {
		P []*Params
		E [][]*tagSet
	}{
		{
			P: []*Params{
				{
					Infra:    "gce",
					Language: "ruby",
					OS:       "linux",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "gce",
					Language: "go",
					OS:       "linux",
					Group:    "dev",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "gce",
					Language: "ruby",
					Dist:     "precise",
					Group:    "edge",
					OS:       "linux",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "jupiterbrain",
					Language: "python",
					OS:       "osx",
					OsxImage: "xcode7",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "jupiterbrain",
					Language: "objective-c",
					OS:       "osx",
					OsxImage: "xcode6.4",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "jupiterbrain",
					Language: "node_js",
					OsxImage: "xcode6.1",
					Dist:     "yosammity",
					Group:    "fancy",
					OS:       "osx",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
			},
			E: [][]*tagSet{
				{
					&tagSet{[]string{"language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:linux"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"group_dev:true", "language_go:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_dev:true", "language_go:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_go:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_go:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_dev:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:linux"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"dist:precise", "group_edge:true", "language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise", "group_edge:true", "language_ruby:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise", "language_ruby:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_edge:true", "language_ruby:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_edge:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:linux"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"language_python:true", "os:osx", "osx_image:xcode7"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx", "osx_image:xcode7"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_python:true", "os:osx"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_python:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"osx_image:xcode7"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"language_objective-c:true", "os:osx", "osx_image:xcode6.4"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx", "osx_image:xcode6.4"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_objective-c:true", "os:osx"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_objective-c:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"osx_image:xcode6.4"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"dist:yosammity", "group_fancy:true", "language_node_js:true", "os:osx", "osx_image:xcode6.1"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx", "osx_image:xcode6.1"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:yosammity", "group_fancy:true", "language_node_js:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:yosammity", "language_node_js:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_fancy:true", "language_node_js:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_node_js:true", "os:osx"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_node_js:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"osx_image:xcode6.1"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:yosammity"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_fancy:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx"}, true, uint64(4), "corp/frob"},
				},
			},
		},
	}
)

func TestNewAPISelector(t *testing.T) {
	u, _ := url.Parse("https://foo:bar@whatever.example.com/images")
	assert.NotNil(t, NewAPISelector(u))
}

func TestAPISelector_Select(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, testAPIServerString)
	}))
	defer ts.Close()

	u, _ := url.Parse(ts.URL)

	as := NewAPISelector(u)

	actual, _ := as.Select(context.TODO(), &Params{
		Infra:    "test",
		Language: "ruby",
		OsxImage: "meow",
		Dist:     "yosamitty",
		Group:    "dev",
		OS:       "osx",
	})
	assert.Equal(t, actual, "travis-ci-awesome")
}

func TestAPISelector_SelectDefault(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, testAPIServerEmptyResponseString)
	}))
	defer ts.Close()
	u, _ := url.Parse(ts.URL)
	actual, err := NewAPISelector(u).Select(context.TODO(), &Params{})
	assert.Equal(t, actual, "default")
	assert.NoError(t, err)
}

func TestAPISelector_SelectDefaultWhenBadResponse(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
	}))
	defer ts.Close()
	u, _ := url.Parse(ts.URL)
	as := NewAPISelector(u)
	as.SetMaxInterval(time.Millisecond)
	as.SetMaxElapsedTime(10 * time.Millisecond)
	actual, err := as.Select(context.TODO(), &Params{})
	assert.Equal(t, actual, "default")
	assert.EqualError(t, err, "expected 200 status code from job-board, received status=500 body=\"\"")
}

func TestAPISelector_SelectDefaultWhenBadJSON(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, `{"data`)
	}))
	defer ts.Close()
	u, _ := url.Parse(ts.URL)
	actual, err := NewAPISelector(u).Select(context.TODO(), &Params{})
	assert.Equal(t, actual, "default")
	assert.EqualError(t, err, "unexpected end of JSON input")
}

func TestAPISelector_SelectTrailingComma(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, testAPIServerString)
	}))
	defer ts.Close()

	u, _ := url.Parse(ts.URL)

	as := NewAPISelector(u)

	actual, err := as.Select(context.TODO(), &Params{
		Infra:    "test,",
		Language: "ruby,",
		OsxImage: "meow,",
		Dist:     "yosamitty,",
		Group:    "dev,",
		OS:       "osx,",
	})
	assert.Equal(t, actual, "default")
	assert.EqualError(t, err, "job was aborted because tag \"dist:yosamitty,\" contained \",\", this can happen when .travis.yml has a trailing comma")
}

func TestAPISelector_buildCandidateTags(t *testing.T) {
	as := NewAPISelector(nil)

	for _, tc := range testAPITagTestCases {
		for i, params := range tc.P {
			expectedJSON, _ := json.MarshalIndent(tc.E[i], "", "  ")
			tagSets, _ := as.buildCandidateTags(params)
			actualJSON, _ := json.MarshalIndent(tagSets, "", "  ")
			assert.JSONEq(t, string(expectedJSON), string(actualJSON))
		}
	}
}
<commit_msg>Update api_selector test<commit_after>package image

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"net/http/httptest"
	"net/url"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

const (
	testAPIServerString = `
{
	"data": [
		{
			"id": 1,
			"infra": "test",
			"name": "travis-ci-awesome"
		}
	]
}
`
	testAPIServerEmptyResponseString = `{"data": []}`
)

var (
	testAPITagTestCases = []struct {
		P []*Params
		E [][]*tagSet
	}{
		{
			P: []*Params{
				{
					Infra:    "gce",
					Language: "ruby",
					OS:       "linux",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "gce",
					Language: "go",
					OS:       "linux",
					Group:    "dev",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "gce",
					Language: "ruby",
					Dist:     "precise",
					Group:    "edge",
					OS:       "linux",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "jupiterbrain",
					Language: "python",
					OS:       "osx",
					OsxImage: "xcode7",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "jupiterbrain",
					Language: "objective-c",
					OS:       "osx",
					OsxImage: "xcode6.4",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
				{
					Infra:    "jupiterbrain",
					Language: "node_js",
					OsxImage: "xcode6.1",
					Dist:     "yosammity",
					Group:    "fancy",
					OS:       "osx",
					JobID:    uint64(4),
					Repo:     "corp/frob",
				},
			},
			E: [][]*tagSet{
				{
					&tagSet{[]string{"language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:linux"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"group_dev:true", "language_go:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_dev:true", "language_go:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_go:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_go:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_dev:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:linux"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"dist:precise", "group_edge:true", "language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise", "group_edge:true", "language_ruby:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise", "language_ruby:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_edge:true", "language_ruby:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true", "os:linux"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_ruby:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:precise"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_edge:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:linux"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"language_python:true", "os:osx", "osx_image:xcode7"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx", "osx_image:xcode7"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_python:true", "os:osx"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_python:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"osx_image:xcode7"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"language_objective-c:true", "os:osx", "osx_image:xcode6.4"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx", "osx_image:xcode6.4"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_objective-c:true", "os:osx"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_objective-c:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"osx_image:xcode6.4"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx"}, true, uint64(4), "corp/frob"},
				},
				{
					&tagSet{[]string{"dist:yosammity", "group_fancy:true", "language_node_js:true", "os:osx", "osx_image:xcode6.1"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx", "osx_image:xcode6.1"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:yosammity", "group_fancy:true", "language_node_js:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:yosammity", "language_node_js:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_fancy:true", "language_node_js:true"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_node_js:true", "os:osx"}, false, uint64(4), "corp/frob"},
					&tagSet{[]string{"language_node_js:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"osx_image:xcode6.1"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"dist:yosammity"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"group_fancy:true"}, true, uint64(4), "corp/frob"},
					&tagSet{[]string{"os:osx"}, true, uint64(4), "corp/frob"},
				},
			},
		},
	}
)

func TestNewAPISelector(t *testing.T) {
	u, _ := url.Parse("https://foo:bar@whatever.example.com/images")
	assert.NotNil(t, NewAPISelector(u))
}

func TestAPISelector_Select(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, testAPIServerString)
	}))
	defer ts.Close()

	u, _ := url.Parse(ts.URL)

	as := NewAPISelector(u)

	actual, _ := as.Select(context.TODO(), &Params{
		Infra:    "test",
		Language: "ruby",
		OsxImage: "meow",
		Dist:     "yosamitty",
		Group:    "dev",
		OS:       "osx",
	})
	assert.Equal(t, actual, "travis-ci-awesome")
}

func TestAPISelector_SelectDefault(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, testAPIServerEmptyResponseString)
	}))
	defer ts.Close()
	u, _ := url.Parse(ts.URL)
	actual, err := NewAPISelector(u).Select(context.TODO(), &Params{})
	assert.Equal(t, actual, "default")
	assert.NoError(t, err)
}

func TestAPISelector_SelectDefaultWhenBadResponse(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
	}))
	defer ts.Close()
	u, _ := url.Parse(ts.URL)
	as := NewAPISelector(u)
	as.SetMaxInterval(time.Millisecond)
	as.SetMaxElapsedTime(10 * time.Millisecond)
	actual, err := as.Select(context.TODO(), &Params{})
	assert.Equal(t, actual, "default")
	assert.EqualError(t, err, "expected 200 status code from job-board, received status=500 body=\"\"")
}

func TestAPISelector_SelectDefaultWhenBadJSON(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, `{"data`)
	}))
	defer ts.Close()
	u, _ := url.Parse(ts.URL)
	actual, err := NewAPISelector(u).Select(context.TODO(), &Params{})
	assert.Equal(t, actual, "default")
	assert.EqualError(t, err, "unexpected end of JSON input")
}

func TestAPISelector_SelectTrailingComma(t *testing.T) {
	ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
		fmt.Fprintf(w, testAPIServerString)
	}))
	defer ts.Close()

	u, _ := url.Parse(ts.URL)

	as := NewAPISelector(u)

	actual, err := as.Select(context.TODO(), &Params{
		Infra:    "test,",
		Language: "ruby,",
		OsxImage: "meow,",
		Dist:     "yosamitty,",
		Group:    "dev,",
		OS:       "osx,",
	})
	assert.Equal(t, actual, "default")
	assert.EqualError(t, err, "job was aborted because tag \"dist:yosamitty,\" contained \",\", this can happen when .travis.yml has a trailing comma")
}

func TestAPISelector_buildCandidateTags(t *testing.T) {
	as := NewAPISelector(nil)

	for _, tc := range testAPITagTestCases {
		for i, params := range tc.P {
			expectedJSON, _ := json.MarshalIndent(tc.E[i], "", "  ")
			tagSets, _ := as.buildCandidateTags(params)
			actualJSON, _ := json.MarshalIndent(tagSets, "", "  ")
			assert.JSONEq(t, string(expectedJSON), string(actualJSON))
		}
	}
}
<|endoftext|>


<commit_before>package chessboard

import (
	"errors"
	"fmt"
)

type Color int

const (
	Unknown Color = iota
	Empty
	White
	Black
)

func (color Color) String() string {
	switch color {
	case Empty:
		return "Empty"
	case White:
		return "White"
	case Black:
		return "Black"
	default:
		return "Unknown"
	}
}

var starting = [8][8]byte{
	{'r', 'n', 'b', 'k', 'q', 'b', 'n', 'r'},
	{'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'},
	{'R', 'N', 'B', 'K', 'Q', 'B', 'N', 'R'},
}

type Board struct {
	matrix [8][8]byte
}

func NewBoard() *Board {
	board := new(Board)
	board.matrix = starting
	return board
}

func (board *Board) Print() {
	fmt.Print(" ")
	for i := 0; i < 8; i++ {
		fmt.Printf(" %c", 'a'+i)
	}
	fmt.Println()

	for i := 0; i < 8; i++ {
		fmt.Print(" +")
		for j := 0; j < 8; j++ {
			fmt.Print("-+")
		}
		fmt.Println()
		fmt.Printf("%d|", 8-i)
		for j := 0; j < 8; j++ {
			fmt.Printf("%c|", board.matrix[i][j])
		}
		fmt.Println(8 - i)
	}
	fmt.Print(" +")
	for i := 0; i < 8; i++ {
		fmt.Print("-+")
	}
	fmt.Println()

	fmt.Print(" ")
	for i := 0; i < 8; i++ {
		fmt.Printf(" %c", 'a'+i)
	}
	fmt.Println()
}

func (board Board) in(point Point) bool {
	return 0 <= point.x && point.x < 8 && 0 <= point.y && point.y < 8
}

func (board Board) color(point Point) (Color, error) {
	if board.in(point) == false {
		return Unknown, errors.New("out of board")
	}

	piece := board.matrix[point.y][point.x]
	if piece == ' ' {
		return Empty, nil
	} else if 'A' <= piece && piece <= 'Z' {
		return White, nil
	} else if 'a' <= piece && piece <= 'z' {
		return Black, nil
	} else {
		return Unknown, errors.New("Unknown color")
	}
}

func (board *Board) Move(from, to Point, color Color) error {
	if board.in(from) == false || board.in(to) == false {
		return errors.New("out of board")
	}

	fcolor, err := board.color(from)
	if fcolor != color || err != nil {
		return errors.New("cannot move this piece")
	}

	board.matrix[to.y][to.x] = board.matrix[from.y][from.x]
	board.matrix[from.y][from.x] = ' '

	return nil
}
<commit_msg>Implement a function returns its enemy color<commit_after>package chessboard

import (
	"errors"
	"fmt"
)

type Color int

const (
	Unknown Color = iota
	Empty
	White
	Black
)

func (color Color) String() string {
	switch color {
	case Empty:
		return "Empty"
	case White:
		return "White"
	case Black:
		return "Black"
	default:
		return "Unknown"
	}
}

func (color Color) Enemy() Color {
	switch color {
	case White:
		return Black
	case Black:
		return White
	default:
		return color
	}
}

var starting = [8][8]byte{
	{'r', 'n', 'b', 'k', 'q', 'b', 'n', 'r'},
	{'p', 'p', 'p', 'p', 'p', 'p', 'p', 'p'},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' '},
	{'P', 'P', 'P', 'P', 'P', 'P', 'P', 'P'},
	{'R', 'N', 'B', 'K', 'Q', 'B', 'N', 'R'},
}

type Board struct {
	matrix [8][8]byte
}

func NewBoard() *Board {
	board := new(Board)
	board.matrix = starting
	return board
}

func (board *Board) Print() {
	fmt.Print(" ")
	for i := 0; i < 8; i++ {
		fmt.Printf(" %c", 'a'+i)
	}
	fmt.Println()

	for i := 0; i < 8; i++ {
		fmt.Print(" +")
		for j := 0; j < 8; j++ {
			fmt.Print("-+")
		}
		fmt.Println()
		fmt.Printf("%d|", 8-i)
		for j := 0; j < 8; j++ {
			fmt.Printf("%c|", board.matrix[i][j])
		}
		fmt.Println(8 - i)
	}
	fmt.Print(" +")
	for i := 0; i < 8; i++ {
		fmt.Print("-+")
	}
	fmt.Println()

	fmt.Print(" ")
	for i := 0; i < 8; i++ {
		fmt.Printf(" %c", 'a'+i)
	}
	fmt.Println()
}

func (board Board) in(point Point) bool {
	return 0 <= point.x && point.x < 8 && 0 <= point.y && point.y < 8
}

func (board Board) color(point Point) (Color, error) {
	if board.in(point) == false {
		return Unknown, errors.New("out of board")
	}

	piece := board.matrix[point.y][point.x]
	if piece == ' ' {
		return Empty, nil
	} else if 'A' <= piece && piece <= 'Z' {
		return White, nil
	} else if 'a' <= piece && piece <= 'z' {
		return Black, nil
	} else {
		return Unknown, errors.New("Unknown color")
	}
}

func (board *Board) Move(from, to Point, color Color) error {
	if board.in(from) == false || board.in(to) == false {
		return errors.New("out of board")
	}

	fcolor, err := board.color(from)
	if fcolor != color || err != nil {
		return errors.New("cannot move this piece")
	}

	board.matrix[to.y][to.x] = board.matrix[from.y][from.x]
	board.matrix[from.y][from.x] = ' '

	return nil
}
<|endoftext|>


<commit_before>package container

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/mesos/mesos-go/api/v1/lib"
)

// DockerContainerizer represents a docker containerizer
type DockerContainerizer struct {
	Client *docker.Client
}

// NewDockerContainerizer initializes a new docker containerizer
func NewDockerContainerizer(socket string) (*DockerContainerizer, error) {
	// If socket is given without an explicit protocol such as tpc:// or http://,
	// we use unix:// one
	if strings.HasPrefix(socket, "/") {
		socket = "unix://" + socket
	}

	client, err := docker.NewClient(socket)
	if err != nil {
		return nil, err
	}

	return &DockerContainerizer{
		Client: client,
	}, nil
}

// ContainerRun launches a new container with the given containerizer
func (c *DockerContainerizer) ContainerRun(info Info) (string, error) {
	// Define network mode
	var networkMode string
	switch info.TaskInfo.GetContainer().GetDocker().GetNetwork() {
	case mesos.ContainerInfo_DockerInfo_HOST:
		networkMode = "host"
		break
	case mesos.ContainerInfo_DockerInfo_BRIDGE:
		networkMode = "bridge"
		break
	case mesos.ContainerInfo_DockerInfo_NONE:
		networkMode = "none"
		break
	case mesos.ContainerInfo_DockerInfo_USER:
		networkMode = "user"
		break
	}

	// Define ports mappings
	portsMappings := make(map[docker.Port][]docker.PortBinding)
	for _, mapping := range info.TaskInfo.GetContainer().GetDocker().GetPortMappings() {
		containerPort := docker.Port(fmt.Sprintf("%d/%s", mapping.GetContainerPort(), mapping.GetProtocol())) // ContainerPort needs to have the form port/protocol (eg. 80/tcp)
		hostPort := strconv.Itoa(int(mapping.HostPort))
		portsMappings[containerPort] = []docker.PortBinding{
			docker.PortBinding{
				HostPort: hostPort,
			},
		}
	}

	// Prepare container
	container, err := c.Client.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			CPUShares: int64(info.CPUSharesLimit),
			Image:     info.TaskInfo.GetContainer().GetDocker().GetImage(),
			Memory:    int64(info.MemoryLimit),
		},
		HostConfig: &docker.HostConfig{
			NetworkMode:  networkMode,
			PortBindings: portsMappings,
			Privileged:   info.TaskInfo.GetContainer().GetDocker().GetPrivileged(),
		},
	})

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

	// Start the container
	logrus.WithFields(logrus.Fields{
		"NetworkMode":   networkMode,
		"PortsMappings": portsMappings,
	}).Debug("Starting docker container")
	err = c.Client.StartContainer(container.ID, nil)
	if err != nil {
		return "", err
	}

	return container.ID, nil
}

// ContainerStop stops the given container
func (c *DockerContainerizer) ContainerStop(id string) error {
	return c.Client.StopContainer(id, 0)
}
<commit_msg>Add environment variables injection<commit_after>package container

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/fsouza/go-dockerclient"
	"github.com/mesos/mesos-go/api/v1/lib"
)

// DockerContainerizer represents a docker containerizer
type DockerContainerizer struct {
	Client *docker.Client
}

// NewDockerContainerizer initializes a new docker containerizer
func NewDockerContainerizer(socket string) (*DockerContainerizer, error) {
	// If socket is given without an explicit protocol such as tpc:// or http://,
	// we use unix:// one
	if strings.HasPrefix(socket, "/") {
		socket = "unix://" + socket
	}

	client, err := docker.NewClient(socket)
	if err != nil {
		return nil, err
	}

	return &DockerContainerizer{
		Client: client,
	}, nil
}

// ContainerRun launches a new container with the given containerizer
func (c *DockerContainerizer) ContainerRun(info Info) (string, error) {
	// Define network mode
	var networkMode string
	switch info.TaskInfo.GetContainer().GetDocker().GetNetwork() {
	case mesos.ContainerInfo_DockerInfo_HOST:
		networkMode = "host"
		break
	case mesos.ContainerInfo_DockerInfo_BRIDGE:
		networkMode = "bridge"
		break
	case mesos.ContainerInfo_DockerInfo_NONE:
		networkMode = "none"
		break
	case mesos.ContainerInfo_DockerInfo_USER:
		networkMode = "user"
		break
	}

	// Define ports mappings
	portsMappings := make(map[docker.Port][]docker.PortBinding)
	for _, mapping := range info.TaskInfo.GetContainer().GetDocker().GetPortMappings() {
		containerPort := docker.Port(fmt.Sprintf("%d/%s", mapping.GetContainerPort(), mapping.GetProtocol())) // ContainerPort needs to have the form port/protocol (eg. 80/tcp)
		hostPort := strconv.Itoa(int(mapping.HostPort))
		portsMappings[containerPort] = []docker.PortBinding{
			docker.PortBinding{
				HostPort: hostPort,
			},
		}
	}

	// Define environment variables
	// Docker needs to have a string slice with strings of the form key=val
	var stringifiedEnv []string
	environment := info.TaskInfo.Command.GetEnvironment().GetVariables()
	for _, variable := range environment {
		stringifiedEnv = append(stringifiedEnv, fmt.Sprintf("%s=%s", variable.GetName(), variable.GetValue()))
	}

	// Prepare container
	container, err := c.Client.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			CPUShares: int64(info.CPUSharesLimit),
			Env:       stringifiedEnv,
			Image:     info.TaskInfo.GetContainer().GetDocker().GetImage(),
			Memory:    int64(info.MemoryLimit),
		},
		HostConfig: &docker.HostConfig{
			NetworkMode:  networkMode,
			PortBindings: portsMappings,
			Privileged:   info.TaskInfo.GetContainer().GetDocker().GetPrivileged(),
		},
	})

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

	// Start the container
	logrus.WithFields(logrus.Fields{
		"NetworkMode":   networkMode,
		"PortsMappings": portsMappings,
	}).Debug("Starting docker container")
	err = c.Client.StartContainer(container.ID, nil)
	if err != nil {
		return "", err
	}

	return container.ID, nil
}

// ContainerStop stops the given container
func (c *DockerContainerizer) ContainerStop(id string) error {
	return c.Client.StopContainer(id, 0)
}
<|endoftext|>


<commit_before>// Copyright 2016 The nvim-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.

// nvim-go is a msgpack remote plugin for Neovim
package main

import (
	"context"
	"log"
	"net/http"
	_ "net/http/pprof" // For pprof debugging.
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"nvim-go/autocmd"
	"nvim-go/command"
	"nvim-go/command/delve"
	"nvim-go/buildctx"
	"nvim-go/logger"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"go.uber.org/zap"
)

const (
	EnvLogLevel = "NVIM_GO_LOG_LEVEL"
	EnvDebug    = "NVIM_GO_DEBUG"
	EnvPprof    = "NVIM_GO_PPROF"
)

var (
	debug = os.Getenv(EnvDebug) != ""
	pprof = os.Getenv(EnvPprof) != ""
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger := logger.NewZapLogger()
	undo := zap.RedirectStdLog(zapLogger)
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	registerFn := func(p *plugin.Plugin) error {
		buildctxt := buildctx.NewContext()
		delve.Register(p, buildctxt)
		c := command.Register(ctx, p, buildctxt)
		autocmd.Register(ctx, p, buildctxt, c)

		if debug {
			// starts the gops agent
			if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
				return err
			}

			if pprof {
				const addr = "localhost:14715" // (n: 14)vim-(g: 7)(o: 15)
				zapLogger.Debug("start the pprof debugging", zap.String("listen at", addr))

				// enable the report of goroutine blocking events
				runtime.SetBlockProfileRate(1)
				go log.Println(http.ListenAndServe(addr, nil))
			}
		}

		return nil
	}
	go plugin.Main(registerFn)

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

	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Debug("main", zap.String("interrupted %s signal", sig.String()))
			return
		}
	}
}
<commit_msg>cmd/nvim-go: remove unused constants<commit_after>// Copyright 2016 The nvim-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.

// nvim-go is a msgpack remote plugin for Neovim
package main

import (
	"context"
	"log"
	"net/http"
	_ "net/http/pprof" // For pprof debugging.
	"os"
	"os/signal"
	"runtime"
	"syscall"

	"nvim-go/autocmd"
	"nvim-go/buildctx"
	"nvim-go/command"
	"nvim-go/command/delve"
	"nvim-go/logger"

	"github.com/google/gops/agent"
	"github.com/neovim/go-client/nvim/plugin"
	"go.uber.org/zap"
)

const (
	EnvDebug = "NVIM_GO_DEBUG"
	EnvPprof = "NVIM_GO_PPROF"
)

var (
	debug = os.Getenv(EnvDebug) != ""
	pprof = os.Getenv(EnvPprof) != ""
)

func main() {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	zapLogger := logger.NewZapLogger()
	undo := zap.RedirectStdLog(zapLogger)
	defer undo()
	ctx = logger.NewContext(ctx, zapLogger)

	registerFn := func(p *plugin.Plugin) error {
		buildctxt := buildctx.NewContext()
		delve.Register(p, buildctxt)
		c := command.Register(ctx, p, buildctxt)
		autocmd.Register(ctx, p, buildctxt, c)

		if debug {
			// starts the gops agent
			if err := agent.Listen(&agent.Options{NoShutdownCleanup: true}); err != nil {
				return err
			}

			if pprof {
				const addr = "localhost:14715" // (n: 14)vim-(g: 7)(o: 15)
				zapLogger.Debug("start the pprof debugging", zap.String("listen at", addr))

				// enable the report of goroutine blocking events
				runtime.SetBlockProfileRate(1)
				go log.Println(http.ListenAndServe(addr, nil))
			}
		}

		return nil
	}
	go plugin.Main(registerFn)

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

	select {
	case sig := <-sigc:
		switch sig {
		case syscall.SIGINT, syscall.SIGTERM:
			zapLogger.Debug("main", zap.String("interrupted %s signal", sig.String()))
			return
		}
	}
}
<|endoftext|>


<commit_before>package trafcacc

import (
	"encoding/gob"
	"encoding/hex"
	"log"
	"net"
	"sync"
)

type packet struct {
	Connid uint32
	Seqid  uint32
	Buf    []byte
}

// sendRaw only happens in backend to remote upstream addr
func (t *trafcacc) sendRaw(p packet) {
	log.Println("sendRaw", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	// use cpool here , conn by connid
	conn := t.cpool.get(p.Connid)
	u := t.upool.next()

	if conn == nil {
		var err error
		// dial
		switch u.proto {
		case "tcp":
			conn, err = net.Dial("tcp", u.addr)
			if err != nil {
				// reply error
				t.replyPkt(packet{Connid: p.Connid})
				return
			}
			t.cpool.add(p.Connid, conn)
			go func() {
				defer conn.Close()
				seqid := uint32(1)
				b := make([]byte, buffersize)
				for {
					n, err := conn.Read(b)
					if err != nil {
						t.replyPkt(packet{p.Connid, seqid, nil})
						break
					}
					t.replyPkt(packet{p.Connid, seqid, b[0:n]})
					seqid++
				}
			}()
		}
	}

	t.ensure(p, conn)
}

// send packed data to backend, only used on front-end
func (t *trafcacc) sendpkt(p packet) {
	log.Println("sendpkt", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	u := t.upool.next()

	func() {
		u.mux.Lock()
		defer u.mux.Unlock()
		if u.conn == nil {
			// dial
			switch u.proto {
			case "tcp":
				conn, err := net.Dial("tcp", u.addr)
				if err != nil {
					// reply error
					t.replyRaw(packet{Connid: p.Connid})
					return
				}
				u.conn = conn
				u.encoder = gob.NewEncoder(conn)
				u.decoder = gob.NewDecoder(conn)
				// build reading slaves
				go func() {
					for {
						p := packet{}
						err := u.decoder.Decode(&p)
						if err != nil {
							break
						}
						t.replyRaw(p)
					}
					u.close()
				}()
			case "udp":
				// TODO: udp
			}
		}
	}()

	err := u.encoder.Encode(&p)
	if err != nil {
		u.close()
		log.Println("sendpkt err:", err)
		// reply error
		t.replyRaw(packet{Connid: p.Connid})
		return
	}
}

// reply Raw only happens in front-end to client
func (t *trafcacc) replyRaw(p packet) {
	log.Println("replyRaw", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	conn := t.cpool.get(p.Connid)

	if conn == nil {
		log.Println("reply to no-exist client conn")
		return
	}
	if p.Buf == nil {
		conn.Close()
		t.cpool.del(p.Connid)
		t.closeQueue(p.Connid)
	} else {
		t.ensure(p, conn)
	}
}

func (t *trafcacc) replyPkt(p packet) {
	log.Println("replyPkt", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	conn := t.epool.next()
	if conn != nil {
		conn.Encode(p)
	}
}

type pktQueue struct {
	lastseq uint32
	cond    *sync.Cond
	queue   map[uint32][]byte
	closed  bool
}

func exists(queue map[uint32][]byte, seqid uint32) bool {
	_, ok := queue[seqid]
	return ok
}

func (t *trafcacc) closeQueue(connid uint32) {
	t.mux.Lock()
	defer t.mux.Unlock()
	pq := t.pq[connid]
	if pq != nil {
		pq.closed = true
		delete(t.pq, connid)
	}
}

// ensure Write in sequence
func (t *trafcacc) ensure(p packet, conn net.Conn) {
	// TODO: just write if it's udp
	t.mux.RLock()
	pq := t.pq[p.Connid]
	t.mux.RUnlock()

	if pq == nil {
		pq = &pktQueue{cond: sync.NewCond(&sync.Mutex{}), queue: make(map[uint32][]byte)}

		t.mux.Lock()
		t.pq[p.Connid] = pq
		t.mux.Unlock()

		go func() {
			defer func() {
				log.Println("exit cond")
				conn.Close()
				t.closeQueue(p.Connid)
			}()
			cond := pq.cond
			for {
				if pq.closed {
					return
				}

				cond.L.Lock()
				for !exists(pq.queue, pq.lastseq+1) && !pq.closed {
					log.Println(t.isbackend, "not exist", pq.queue, pq.lastseq+1)
					cond.Wait()
				}
				log.Println(t.isbackend, "is exist", pq.queue, pq.lastseq+1)

				for i := pq.lastseq + 1; ; i++ {
					buf, ok := pq.queue[i]
					if ok {
						if buf != nil {
							_, err := conn.Write(buf)
							if err != nil {
								// remove when connection closed
								pq.closed = true
								break
							}
						} else if i > 1 {
							pq.closed = true
							break
						}
						pq.lastseq = i
						delete(pq.queue, i)

					} else {
						break
					}
				}
				cond.L.Unlock()
			}
		}()
	}

	cond := pq.cond

	cond.L.Lock()
	pq.queue[p.Seqid] = p.Buf
	cond.L.Unlock()
	cond.Signal()
}
<commit_msg>some changes<commit_after>package trafcacc

import (
	"encoding/gob"
	"encoding/hex"
	"log"
	"net"
	"sync"
)

type packet struct {
	Connid uint32
	Seqid  uint32
	Buf    []byte
}

// sendRaw only happens in backend to remote upstream addr
func (t *trafcacc) sendRaw(p packet) {
	log.Println("sendRaw", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	// use cpool here , conn by connid
	conn := t.cpool.get(p.Connid)
	u := t.upool.next()

	if conn == nil {
		var err error
		// dial
		switch u.proto {
		case "tcp":
			conn, err = net.Dial("tcp", u.addr)
			if err != nil {
				// reply error
				t.replyPkt(packet{Connid: p.Connid})
				return
			}
			t.cpool.add(p.Connid, conn)
			go func() {
				defer func() {
					conn.Close()
					t.replyPkt(packet{p.Connid, p.Seqid, nil})
				}()
				seqid := uint32(1)
				b := make([]byte, buffersize)
				for {
					n, err := conn.Read(b)
					if err != nil {
						break
					}
					t.replyPkt(packet{p.Connid, seqid, b[0:n]})
					seqid++
				}
			}()
		}
	}

	t.ensure(p, conn)
}

// send packed data to backend, only used on front-end
func (t *trafcacc) sendpkt(p packet) {
	log.Println("sendpkt", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	u := t.upool.next()

	func() {
		u.mux.Lock()
		defer u.mux.Unlock()
		if u.conn == nil {
			// dial
			switch u.proto {
			case "tcp":
				conn, err := net.Dial("tcp", u.addr)
				if err != nil {
					// reply error
					t.replyRaw(packet{Connid: p.Connid})
					return
				}
				u.conn = conn
				u.encoder = gob.NewEncoder(conn)
				u.decoder = gob.NewDecoder(conn)
				// build reading slaves
				go func() {
					defer func() {
						u.close()
					}()
					for {
						p := packet{}
						err := u.decoder.Decode(&p)
						if err != nil {
							return
						}
						t.replyRaw(p)
						if p.Seqid != 1 && p.Buf == nil {
							return
						}
					}
				}()
			case "udp":
				// TODO: udp
			}
		}
	}()

	err := u.encoder.Encode(&p)
	if err != nil {
		u.close()
		log.Println("sendpkt err:", err)
		// reply error
		t.replyRaw(packet{Connid: p.Connid})
		return
	}
}

// reply Raw only happens in front-end to client
func (t *trafcacc) replyRaw(p packet) {
	log.Println("replyRaw", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	conn := t.cpool.get(p.Connid)

	if conn == nil {
		log.Println("reply to no-exist client conn")
		return
	}
	if p.Seqid != 1 && p.Buf == nil {
		conn.Close()
		t.cpool.del(p.Connid)
		t.closeQueue(p.Connid)
	} else {
		t.ensure(p, conn)
	}
}

func (t *trafcacc) replyPkt(p packet) {
	log.Println("replyPkt", t.isbackend, p.Connid, p.Seqid, len(p.Buf), hex.EncodeToString(p.Buf))
	conn := t.epool.next()
	if conn != nil {
		conn.Encode(p)
	}
}

type pktQueue struct {
	lastseq uint32
	cond    *sync.Cond
	queue   map[uint32][]byte
	closed  bool
}

func exists(queue map[uint32][]byte, seqid uint32) bool {
	_, ok := queue[seqid]
	return ok
}

func (t *trafcacc) closeQueue(connid uint32) {
	t.mux.Lock()
	defer t.mux.Unlock()
	pq := t.pq[connid]
	if pq != nil {
		pq.closed = true
		delete(t.pq, connid)
	}
}

// ensure Write in sequence
func (t *trafcacc) ensure(p packet, conn net.Conn) {
	// TODO: just write if it's udp
	t.mux.RLock()
	pq := t.pq[p.Connid]
	t.mux.RUnlock()

	if pq == nil {
		pq = &pktQueue{cond: sync.NewCond(&sync.Mutex{}), queue: make(map[uint32][]byte)}

		t.mux.Lock()
		t.pq[p.Connid] = pq
		t.mux.Unlock()

		go func() {
			defer func() {
				log.Println("exit cond")
				conn.Close()
				t.closeQueue(p.Connid)
			}()
			cond := pq.cond
			for {
				if pq.closed {
					return
				}

				cond.L.Lock()
				for !exists(pq.queue, pq.lastseq+1) && !pq.closed {
					log.Println(t.isbackend, "not exist", pq.queue, pq.lastseq+1)
					cond.Wait()
				}
				log.Println(t.isbackend, "is exist", pq.queue, pq.lastseq+1)

				for i := pq.lastseq + 1; ; i++ {
					buf, ok := pq.queue[i]
					if ok {
						if buf != nil {
							_, err := conn.Write(buf)
							if err != nil {
								// remove when connection closed
								pq.closed = true
								break
							}
						} else if i > 1 {
							pq.closed = true
							break
						}
						pq.lastseq = i
						delete(pq.queue, i)

					} else {
						break
					}
				}
				cond.L.Unlock()
			}
		}()
	}

	cond := pq.cond

	cond.L.Lock()
	pq.queue[p.Seqid] = p.Buf
	cond.L.Unlock()
	cond.Signal()
}
<|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.

// Cgo; see gmp.go for an overview.

// TODO(rsc):
//	Emit correct line number annotations.
//	Make gc understand the annotations.

package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"sort"
	"strings"
)

// A Package collects information about the package we're going to write.
type Package struct {
	PackageName string // name of package
	PackagePath string
	PtrSize     int64
	IntSize     int64
	GccOptions  []string
	GccIsClang  bool
	CgoFlags    map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
	Written     map[string]bool
	Name        map[string]*Name // accumulated Name from Files
	ExpFunc     []*ExpFunc       // accumulated ExpFunc from Files
	Decl        []ast.Decl
	GoFiles     []string // list of Go files
	GccFiles    []string // list of gcc output files
	Preamble    string   // collected preamble for _cgo_export.h
	CgoChecks   []string // see unsafeCheckPointerName
}

// A File collects information about a single Go input file.
type File struct {
	AST      *ast.File           // parsed AST
	Comments []*ast.CommentGroup // comments from file
	Package  string              // Package name
	Preamble string              // C preamble (doc comment on import "C")
	Ref      []*Ref              // all references to C.xxx in AST
	Calls    []*ast.CallExpr     // all calls to C.xxx in AST
	ExpFunc  []*ExpFunc          // exported functions for this file
	Name     map[string]*Name    // map from Go name to Name
}

func nameKeys(m map[string]*Name) []string {
	var ks []string
	for k := range m {
		ks = append(ks, k)
	}
	sort.Strings(ks)
	return ks
}

// A Ref refers to an expression of the form C.xxx in the AST.
type Ref struct {
	Name    *Name
	Expr    *ast.Expr
	Context string // "type", "expr", "call", or "call2"
}

func (r *Ref) Pos() token.Pos {
	return (*r.Expr).Pos()
}

// A Name collects information about C.xxx.
type Name struct {
	Go       string // name used in Go referring to package C
	Mangle   string // name used in generated Go
	C        string // name used in C
	Define   string // #define expansion
	Kind     string // "const", "type", "var", "fpvar", "func", "not-type"
	Type     *Type  // the type of xxx
	FuncType *FuncType
	AddError bool
	Const    string // constant definition
}

// IsVar reports whether Kind is either "var" or "fpvar"
func (n *Name) IsVar() bool {
	return n.Kind == "var" || n.Kind == "fpvar"
}

// A ExpFunc is an exported function, callable from C.
// Such functions are identified in the Go input file
// by doc comments containing the line //export ExpName
type ExpFunc struct {
	Func    *ast.FuncDecl
	ExpName string // name to use from C
	Doc     string
}

// A TypeRepr contains the string representation of a type.
type TypeRepr struct {
	Repr       string
	FormatArgs []interface{}
}

// A Type collects information about a type in both the C and Go worlds.
type Type struct {
	Size       int64
	Align      int64
	C          *TypeRepr
	Go         ast.Expr
	EnumValues map[string]int64
	Typedef    string
}

// A FuncType collects information about a function type in both the C and Go worlds.
type FuncType struct {
	Params []*Type
	Result *Type
	Go     *ast.FuncType
}

func usage() {
	fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
	flag.PrintDefaults()
	os.Exit(2)
}

var ptrSizeMap = map[string]int64{
	"386":      4,
	"amd64":    8,
	"arm":      4,
	"arm64":    8,
	"mips64":   8,
	"mips64le": 8,
	"ppc64":    8,
	"ppc64le":  8,
	"s390":     4,
	"s390x":    8,
}

var intSizeMap = map[string]int64{
	"386":      4,
	"amd64":    8,
	"arm":      4,
	"arm64":    8,
	"mips64":   8,
	"mips64le": 8,
	"ppc64":    8,
	"ppc64le":  8,
	"s390":     4,
	"s390x":    4,
}

var cPrefix string

var fset = token.NewFileSet()

var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
var dynout = flag.String("dynout", "", "write -dynimport output to this file")
var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")

// This flag is for bootstrapping a new Go implementation,
// to generate Go types that match the data layout and
// constant values used in the host's C libraries and system calls.
var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")

var objDir = flag.String("objdir", "", "object directory")
var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")

var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
var goarch, goos string

func main() {
	flag.Usage = usage
	flag.Parse()

	if *dynobj != "" {
		// cgo -dynimport is essentially a separate helper command
		// built into the cgo binary. It scans a gcc-produced executable
		// and dumps information about the imported symbols and the
		// imported libraries. The 'go build' rules for cgo prepare an
		// appropriate executable and then use its import information
		// instead of needing to make the linkers duplicate all the
		// specialized knowledge gcc has about where to look for imported
		// symbols and which ones to use.
		dynimport(*dynobj)
		return
	}

	if *godefs {
		// Generating definitions pulled from header files,
		// to be checked into Go repositories.
		// Line numbers are just noise.
		conf.Mode &^= printer.SourcePos
	}

	args := flag.Args()
	if len(args) < 1 {
		usage()
	}

	// Find first arg that looks like a go file and assume everything before
	// that are options to pass to gcc.
	var i int
	for i = len(args); i > 0; i-- {
		if !strings.HasSuffix(args[i-1], ".go") {
			break
		}
	}
	if i == len(args) {
		usage()
	}

	goFiles := args[i:]

	p := newPackage(args[:i])

	// Record CGO_LDFLAGS from the environment for external linking.
	if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
		args, err := splitQuoted(ldflags)
		if err != nil {
			fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
		}
		p.addToFlag("LDFLAGS", args)
	}

	// Need a unique prefix for the global C symbols that
	// we use to coordinate between gcc and ourselves.
	// We already put _cgo_ at the beginning, so the main
	// concern is other cgo wrappers for the same functions.
	// Use the beginning of the md5 of the input to disambiguate.
	h := md5.New()
	for _, input := range goFiles {
		f, err := os.Open(input)
		if err != nil {
			fatalf("%s", err)
		}
		io.Copy(h, f)
		f.Close()
	}
	cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])

	fs := make([]*File, len(goFiles))
	for i, input := range goFiles {
		f := new(File)
		f.ReadGo(input)
		f.DiscardCgoDirectives()
		fs[i] = f
	}

	if *objDir == "" {
		// make sure that _obj directory exists, so that we can write
		// all the output files there.
		os.Mkdir("_obj", 0777)
		*objDir = "_obj"
	}
	*objDir += string(filepath.Separator)

	for i, input := range goFiles {
		f := fs[i]
		p.Translate(f)
		for _, cref := range f.Ref {
			switch cref.Context {
			case "call", "call2":
				if cref.Name.Kind != "type" {
					break
				}
				*cref.Expr = cref.Name.Type.Go
			}
		}
		if nerrors > 0 {
			os.Exit(2)
		}
		p.PackagePath = f.Package
		p.Record(f)
		if *godefs {
			os.Stdout.WriteString(p.godefs(f, input))
		} else {
			p.writeOutput(f, input)
		}
	}

	if !*godefs {
		p.writeDefs()
	}
	if nerrors > 0 {
		os.Exit(2)
	}
}

// newPackage returns a new Package that will invoke
// gcc with the additional arguments specified in args.
func newPackage(args []string) *Package {
	goarch = runtime.GOARCH
	if s := os.Getenv("GOARCH"); s != "" {
		goarch = s
	}
	goos = runtime.GOOS
	if s := os.Getenv("GOOS"); s != "" {
		goos = s
	}
	ptrSize := ptrSizeMap[goarch]
	if ptrSize == 0 {
		fatalf("unknown ptrSize for $GOARCH %q", goarch)
	}
	intSize := intSizeMap[goarch]
	if intSize == 0 {
		fatalf("unknown intSize for $GOARCH %q", goarch)
	}

	// Reset locale variables so gcc emits English errors [sic].
	os.Setenv("LANG", "en_US.UTF-8")
	os.Setenv("LC_ALL", "C")

	p := &Package{
		PtrSize:  ptrSize,
		IntSize:  intSize,
		CgoFlags: make(map[string][]string),
		Written:  make(map[string]bool),
	}
	p.addToFlag("CFLAGS", args)
	return p
}

// Record what needs to be recorded about f.
func (p *Package) Record(f *File) {
	if p.PackageName == "" {
		p.PackageName = f.Package
	} else if p.PackageName != f.Package {
		error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
	}

	if p.Name == nil {
		p.Name = f.Name
	} else {
		for k, v := range f.Name {
			if p.Name[k] == nil {
				p.Name[k] = v
			} else if !reflect.DeepEqual(p.Name[k], v) {
				error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
			}
		}
	}

	if f.ExpFunc != nil {
		p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
		p.Preamble += "\n" + f.Preamble
	}
	p.Decl = append(p.Decl, f.AST.Decls...)
}
<commit_msg>cmd/go: add sparc64 pointer and int sizes<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.

// Cgo; see gmp.go for an overview.

// TODO(rsc):
//	Emit correct line number annotations.
//	Make gc understand the annotations.

package main

import (
	"crypto/md5"
	"flag"
	"fmt"
	"go/ast"
	"go/printer"
	"go/token"
	"io"
	"os"
	"path/filepath"
	"reflect"
	"runtime"
	"sort"
	"strings"
)

// A Package collects information about the package we're going to write.
type Package struct {
	PackageName string // name of package
	PackagePath string
	PtrSize     int64
	IntSize     int64
	GccOptions  []string
	GccIsClang  bool
	CgoFlags    map[string][]string // #cgo flags (CFLAGS, LDFLAGS)
	Written     map[string]bool
	Name        map[string]*Name // accumulated Name from Files
	ExpFunc     []*ExpFunc       // accumulated ExpFunc from Files
	Decl        []ast.Decl
	GoFiles     []string // list of Go files
	GccFiles    []string // list of gcc output files
	Preamble    string   // collected preamble for _cgo_export.h
	CgoChecks   []string // see unsafeCheckPointerName
}

// A File collects information about a single Go input file.
type File struct {
	AST      *ast.File           // parsed AST
	Comments []*ast.CommentGroup // comments from file
	Package  string              // Package name
	Preamble string              // C preamble (doc comment on import "C")
	Ref      []*Ref              // all references to C.xxx in AST
	Calls    []*ast.CallExpr     // all calls to C.xxx in AST
	ExpFunc  []*ExpFunc          // exported functions for this file
	Name     map[string]*Name    // map from Go name to Name
}

func nameKeys(m map[string]*Name) []string {
	var ks []string
	for k := range m {
		ks = append(ks, k)
	}
	sort.Strings(ks)
	return ks
}

// A Ref refers to an expression of the form C.xxx in the AST.
type Ref struct {
	Name    *Name
	Expr    *ast.Expr
	Context string // "type", "expr", "call", or "call2"
}

func (r *Ref) Pos() token.Pos {
	return (*r.Expr).Pos()
}

// A Name collects information about C.xxx.
type Name struct {
	Go       string // name used in Go referring to package C
	Mangle   string // name used in generated Go
	C        string // name used in C
	Define   string // #define expansion
	Kind     string // "const", "type", "var", "fpvar", "func", "not-type"
	Type     *Type  // the type of xxx
	FuncType *FuncType
	AddError bool
	Const    string // constant definition
}

// IsVar reports whether Kind is either "var" or "fpvar"
func (n *Name) IsVar() bool {
	return n.Kind == "var" || n.Kind == "fpvar"
}

// A ExpFunc is an exported function, callable from C.
// Such functions are identified in the Go input file
// by doc comments containing the line //export ExpName
type ExpFunc struct {
	Func    *ast.FuncDecl
	ExpName string // name to use from C
	Doc     string
}

// A TypeRepr contains the string representation of a type.
type TypeRepr struct {
	Repr       string
	FormatArgs []interface{}
}

// A Type collects information about a type in both the C and Go worlds.
type Type struct {
	Size       int64
	Align      int64
	C          *TypeRepr
	Go         ast.Expr
	EnumValues map[string]int64
	Typedef    string
}

// A FuncType collects information about a function type in both the C and Go worlds.
type FuncType struct {
	Params []*Type
	Result *Type
	Go     *ast.FuncType
}

func usage() {
	fmt.Fprint(os.Stderr, "usage: cgo -- [compiler options] file.go ...\n")
	flag.PrintDefaults()
	os.Exit(2)
}

var ptrSizeMap = map[string]int64{
	"386":      4,
	"amd64":    8,
	"arm":      4,
	"arm64":    8,
	"mips64":   8,
	"mips64le": 8,
	"ppc64":    8,
	"ppc64le":  8,
	"s390":     4,
	"s390x":    8,
	"sparc64":  8,
}

var intSizeMap = map[string]int64{
	"386":      4,
	"amd64":    8,
	"arm":      4,
	"arm64":    8,
	"mips64":   8,
	"mips64le": 8,
	"ppc64":    8,
	"ppc64le":  8,
	"s390":     4,
	"s390x":    4,
	"sparc64":  8,
}

var cPrefix string

var fset = token.NewFileSet()

var dynobj = flag.String("dynimport", "", "if non-empty, print dynamic import data for that file")
var dynout = flag.String("dynout", "", "write -dynimport output to this file")
var dynpackage = flag.String("dynpackage", "main", "set Go package for -dynimport output")
var dynlinker = flag.Bool("dynlinker", false, "record dynamic linker information in -dynimport mode")

// This flag is for bootstrapping a new Go implementation,
// to generate Go types that match the data layout and
// constant values used in the host's C libraries and system calls.
var godefs = flag.Bool("godefs", false, "for bootstrap: write Go definitions for C file to standard output")

var objDir = flag.String("objdir", "", "object directory")
var importPath = flag.String("importpath", "", "import path of package being built (for comments in generated files)")
var exportHeader = flag.String("exportheader", "", "where to write export header if any exported functions")

var gccgo = flag.Bool("gccgo", false, "generate files for use with gccgo")
var gccgoprefix = flag.String("gccgoprefix", "", "-fgo-prefix option used with gccgo")
var gccgopkgpath = flag.String("gccgopkgpath", "", "-fgo-pkgpath option used with gccgo")
var importRuntimeCgo = flag.Bool("import_runtime_cgo", true, "import runtime/cgo in generated code")
var importSyscall = flag.Bool("import_syscall", true, "import syscall in generated code")
var goarch, goos string

func main() {
	flag.Usage = usage
	flag.Parse()

	if *dynobj != "" {
		// cgo -dynimport is essentially a separate helper command
		// built into the cgo binary. It scans a gcc-produced executable
		// and dumps information about the imported symbols and the
		// imported libraries. The 'go build' rules for cgo prepare an
		// appropriate executable and then use its import information
		// instead of needing to make the linkers duplicate all the
		// specialized knowledge gcc has about where to look for imported
		// symbols and which ones to use.
		dynimport(*dynobj)
		return
	}

	if *godefs {
		// Generating definitions pulled from header files,
		// to be checked into Go repositories.
		// Line numbers are just noise.
		conf.Mode &^= printer.SourcePos
	}

	args := flag.Args()
	if len(args) < 1 {
		usage()
	}

	// Find first arg that looks like a go file and assume everything before
	// that are options to pass to gcc.
	var i int
	for i = len(args); i > 0; i-- {
		if !strings.HasSuffix(args[i-1], ".go") {
			break
		}
	}
	if i == len(args) {
		usage()
	}

	goFiles := args[i:]

	p := newPackage(args[:i])

	// Record CGO_LDFLAGS from the environment for external linking.
	if ldflags := os.Getenv("CGO_LDFLAGS"); ldflags != "" {
		args, err := splitQuoted(ldflags)
		if err != nil {
			fatalf("bad CGO_LDFLAGS: %q (%s)", ldflags, err)
		}
		p.addToFlag("LDFLAGS", args)
	}

	// Need a unique prefix for the global C symbols that
	// we use to coordinate between gcc and ourselves.
	// We already put _cgo_ at the beginning, so the main
	// concern is other cgo wrappers for the same functions.
	// Use the beginning of the md5 of the input to disambiguate.
	h := md5.New()
	for _, input := range goFiles {
		f, err := os.Open(input)
		if err != nil {
			fatalf("%s", err)
		}
		io.Copy(h, f)
		f.Close()
	}
	cPrefix = fmt.Sprintf("_%x", h.Sum(nil)[0:6])

	fs := make([]*File, len(goFiles))
	for i, input := range goFiles {
		f := new(File)
		f.ReadGo(input)
		f.DiscardCgoDirectives()
		fs[i] = f
	}

	if *objDir == "" {
		// make sure that _obj directory exists, so that we can write
		// all the output files there.
		os.Mkdir("_obj", 0777)
		*objDir = "_obj"
	}
	*objDir += string(filepath.Separator)

	for i, input := range goFiles {
		f := fs[i]
		p.Translate(f)
		for _, cref := range f.Ref {
			switch cref.Context {
			case "call", "call2":
				if cref.Name.Kind != "type" {
					break
				}
				*cref.Expr = cref.Name.Type.Go
			}
		}
		if nerrors > 0 {
			os.Exit(2)
		}
		p.PackagePath = f.Package
		p.Record(f)
		if *godefs {
			os.Stdout.WriteString(p.godefs(f, input))
		} else {
			p.writeOutput(f, input)
		}
	}

	if !*godefs {
		p.writeDefs()
	}
	if nerrors > 0 {
		os.Exit(2)
	}
}

// newPackage returns a new Package that will invoke
// gcc with the additional arguments specified in args.
func newPackage(args []string) *Package {
	goarch = runtime.GOARCH
	if s := os.Getenv("GOARCH"); s != "" {
		goarch = s
	}
	goos = runtime.GOOS
	if s := os.Getenv("GOOS"); s != "" {
		goos = s
	}
	ptrSize := ptrSizeMap[goarch]
	if ptrSize == 0 {
		fatalf("unknown ptrSize for $GOARCH %q", goarch)
	}
	intSize := intSizeMap[goarch]
	if intSize == 0 {
		fatalf("unknown intSize for $GOARCH %q", goarch)
	}

	// Reset locale variables so gcc emits English errors [sic].
	os.Setenv("LANG", "en_US.UTF-8")
	os.Setenv("LC_ALL", "C")

	p := &Package{
		PtrSize:  ptrSize,
		IntSize:  intSize,
		CgoFlags: make(map[string][]string),
		Written:  make(map[string]bool),
	}
	p.addToFlag("CFLAGS", args)
	return p
}

// Record what needs to be recorded about f.
func (p *Package) Record(f *File) {
	if p.PackageName == "" {
		p.PackageName = f.Package
	} else if p.PackageName != f.Package {
		error_(token.NoPos, "inconsistent package names: %s, %s", p.PackageName, f.Package)
	}

	if p.Name == nil {
		p.Name = f.Name
	} else {
		for k, v := range f.Name {
			if p.Name[k] == nil {
				p.Name[k] = v
			} else if !reflect.DeepEqual(p.Name[k], v) {
				error_(token.NoPos, "inconsistent definitions for C.%s", fixGo(k))
			}
		}
	}

	if f.ExpFunc != nil {
		p.ExpFunc = append(p.ExpFunc, f.ExpFunc...)
		p.Preamble += "\n" + f.Preamble
	}
	p.Decl = append(p.Decl, f.AST.Decls...)
}
<|endoftext|>