<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/benbjohnson/go-raft"
	"github.com/gorilla/mux"
	"log"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"os"
	"time"
)

//------------------------------------------------------------------------------
//
// Initialization
//
//------------------------------------------------------------------------------

var verbose bool

func init() {
	flag.BoolVar(&verbose, "v", false, "verbose logging")
	flag.BoolVar(&verbose, "verbose", false, "verbose logging")
}

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

type Info struct {
	Host string `json:"host"`
	Port int `json:"port"`
}

//------------------------------------------------------------------------------
//
// Variables
//
//------------------------------------------------------------------------------

var server *raft.Server
var logger *log.Logger

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	var err error
	logger = log.New(os.Stdout, "", log.LstdFlags)
	flag.Parse()
	if verbose {
		fmt.Println("Verbose logging enabled.\n")
	}

	// Setup commands.
	raft.RegisterCommand(&WriteFileCommand{})
	raft.RegisterCommand(&joinCommand{})

	// Use the present working directory if a directory was not passed in.
	var path string
	if flag.NArg() == 0 {
		path, _ = os.Getwd()
	} else {
		path = flag.Arg(0)
		if err := os.MkdirAll(path, 0744); err != nil {
			fatal("Unable to create path: %v", err)
		}
	}

	// Read server info from file or grab it from user.
	var info *Info = getInfo(path)
	name := fmt.Sprintf("%s:%d", info.Host, info.Port)
	fmt.Printf("Name: %s\n\n", name)

	t := transHandler{}

	// Setup new raft server.
	server, err = raft.NewServer(name, path, t)
	//server.DoHandler = DoHandler;
	server.SetElectionTimeout(2 * time.Second)
	server.SetHeartbeatTimeout(1 * time.Second)
	if err != nil {
		fatal("%v", err)
	}
	server.Start()

	// Join to another server if we don't have a log.
	if server.IsLogEmpty() {
		var leaderHost string
		fmt.Println("This server has no log. Please enter a server in the cluster to join\nto or hit enter to initialize a cluster.");
		fmt.Printf("Join to (host:port)> ");
		fmt.Scanf("%s", &leaderHost)
		if leaderHost == "" {
			server.Initialize()
		} else {
			join(server)
		}
	}

	// Create HTTP interface.
    r := mux.NewRouter()
    r.HandleFunc("/join", JoinHttpHandler).Methods("POST")
    r.HandleFunc("/vote", VoteHttpHandler).Methods("POST")
    r.HandleFunc("/log", GetLogHttpHandler).Methods("GET")
    r.HandleFunc("/log/append", AppendEntriesHttpHandler).Methods("POST")
    r.HandleFunc("/files/{filename}", ReadFileHttpHandler).Methods("GET")
    r.HandleFunc("/files/{filename}", WriteFileHttpHandler).Methods("POST")
    http.Handle("/", r)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", info.Port), nil))
}

func usage() {
	fatal("usage: raftd [PATH]")
}

//--------------------------------------
// Config
//--------------------------------------

func getInfo(path string) *Info {
	info := &Info{}

	// Read in the server info if available.
	infoPath := fmt.Sprintf("%s/info", path)
	if file, err := os.Open(infoPath); err == nil {
		if content, err := ioutil.ReadAll(file); err != nil {
			fatal("Unable to read info: %v", err)
		} else {
			if err = json.Unmarshal(content, &info); err != nil {
				fatal("Unable to parse info: %v", err)
			}
		}
		file.Close()

	// Otherwise ask user for info and write it to file.
	} else {
		fmt.Printf("Enter hostname: [localhost] ");
		fmt.Scanf("%s", &info.Host)
		info.Host = strings.TrimSpace(info.Host)
		if info.Host == "" {
			info.Host = "localhost"
		}

		fmt.Printf("Enter port: [4001] ");
		fmt.Scanf("%d", &info.Port)
		if info.Port == 0 {
			info.Port = 4001
		}

		// Write to file.
		content, _ := json.Marshal(info)
		content = []byte(string(content) + "\n")
		if err := ioutil.WriteFile(infoPath, content, 0644); err != nil {
			fatal("Unable to write info to file: %v", err)
		}
	}

	return info
}

//--------------------------------------
// Handlers
//--------------------------------------

// Send join requests to the leader.
func join(s *raft.Server) error {
	var b bytes.Buffer
	command := &joinCommand{}
	command.Name = s.Name()

	json.NewEncoder(&b).Encode(command)
	debug("[send] POST http://%v/join", "localhost:4001")
	resp, err := http.Post(fmt.Sprintf("http://%s/join", "localhost:4001"), "application/json", &b)
	if resp != nil {
		resp.Body.Close()
		if resp.StatusCode == http.StatusOK {
			return nil
		}
	}
	return fmt.Errorf("raftd: Unable to join: %v", err)
}

type transHandler struct {
	name string
}

// Sends AppendEntries RPCs to a peer when the server is the leader.
func (t transHandler) SendAppendEntriesRequest(server *raft.Server, peer *raft.Peer, req *raft.AppendEntriesRequest) (*raft.AppendEntriesResponse, error) {
	var aersp *raft.AppendEntriesResponse
	var b bytes.Buffer
	json.NewEncoder(&b).Encode(req)
	debug("[send] POST http://%s/log/append [%d]", peer.Name(), len(req.Entries))
	resp, err := http.Post(fmt.Sprintf("http://%s/log/append", peer.Name()), "application/json", &b)
	if resp != nil {
		aersp = &raft.AppendEntriesResponse{}
		if err = json.NewDecoder(resp.Body).Decode(&aersp); err == nil || err == io.EOF {
			return aersp, nil
		}
	}
	return aersp, fmt.Errorf("raftd: Unable to append entries: %v", err)
}

// Sends RequestVote RPCs to a peer when the server is the candidate.
func (t transHandler) SendVoteRequest(server *raft.Server, peer *raft.Peer, req *raft.RequestVoteRequest) (*raft.RequestVoteResponse, error) {
	var rvrsp *raft.RequestVoteResponse
	var b bytes.Buffer
	json.NewEncoder(&b).Encode(req)
	debug("[send] POST http://%s/vote", peer.Name())
	resp, err := http.Post(fmt.Sprintf("http://%s/vote", peer.Name()), "application/json", &b)
	if resp != nil {
		rvrsp := &raft.RequestVoteResponse{}
		if err = json.NewDecoder(resp.Body).Decode(&rvrsp); err == nil || err == io.EOF {
			return rvrsp, nil
		}
	}
	return rvrsp, fmt.Errorf("raftd: Unable to request vote: %v", err)
}

//--------------------------------------
// HTTP Handlers
//--------------------------------------

func GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
	debug("[recv] GET http://%v/log", server.Name())
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(server.LogEntries())
}

func JoinHttpHandler(w http.ResponseWriter, req *http.Request) {
	debug("[recv] POST http://%v/join", server.Name())
	command := &joinCommand{}
	if err := decodeJsonRequest(req, command); err == nil {
		if err = server.Do(command); err != nil {
			warn("raftd: Unable to join: %v", err)
			w.WriteHeader(http.StatusInternalServerError)
		} else {
			w.WriteHeader(http.StatusOK)
		}
	} else {
		warn("[join] ERROR: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func VoteHttpHandler(w http.ResponseWriter, req *http.Request) {
	rvreq := &raft.RequestVoteRequest{}
	err := decodeJsonRequest(req, rvreq)
	if err == nil {
		debug("[recv] POST http://%v/vote [%s]", server.Name(), rvreq.CandidateName)
		if resp, _ := server.RequestVote(rvreq); resp != nil {
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(resp)
			return
		}
	}
	w.WriteHeader(http.StatusInternalServerError)
}

func AppendEntriesHttpHandler(w http.ResponseWriter, req *http.Request) {
	aereq := &raft.AppendEntriesRequest{}
	err := decodeJsonRequest(req, aereq)
	if err == nil {
		debug("[recv] POST http://%s/log/append [%d]", server.Name(), len(aereq.Entries))
		if resp, _ := server.AppendEntries(aereq); resp != nil {
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(resp)
			return
		}
	}
	warn("[append] ERROR: %v", err)
	w.WriteHeader(http.StatusInternalServerError)
}

func WriteFileHttpHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	debug("[recv] POST http://%v/files/%s", server.Name(), vars["filename"])

	content, err := ioutil.ReadAll(req.Body)
	if err != nil {
		warn("raftd: Unable to read: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	command := &WriteFileCommand{}
	command.Filename = vars["filename"]
	command.Content = string(content)

	// unlikely to fail twice
	for {
		if server.State() == "leader" {
			if err = server.Do(command); err != nil {
				warn("raftd: Unable to write file: %v", err)
				w.WriteHeader(http.StatusInternalServerError)
			} else {
				// good to go
				w.WriteHeader(http.StatusOK)
				return
			}
		} else {
			// forward
			b := bytes.NewBuffer(content)
			leaderName := server.GetLeader()
			if leaderName =="" {
				// no luckey, during the voting process
				continue
			}
			debug("[send] POST http://%v/files/%s", leaderName, vars["filename"])
			_, err := http.Post(fmt.Sprintf("http://%v/files/%s", leaderName, vars["filename"]), "application/json", b)
			if err != nil {
				// should check other errors
				continue
			} else {
				//good to go
				return
			}
		}
	}
}

func ReadFileHttpHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	debug("[recv] GET http://%v/files/%s", server.Name(), vars["filename"])

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)
	path := fmt.Sprintf("%s/%s", server.Path(), vars["filename"])
	if content, err := ioutil.ReadFile(path); err == nil {
		w.Write(content)
	}
}

//--------------------------------------
// HTTP Utilities
//--------------------------------------

func decodeJsonRequest(req *http.Request, data interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(&data); err != nil && err != io.EOF {
		logger.Println("Malformed json request: %v", err)
		return fmt.Errorf("Malformed json request: %v", err)
	}
	return nil
}

func encodeJsonResponse(w http.ResponseWriter, status int, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)

	if data != nil {
		encoder := json.NewEncoder(w)
		encoder.Encode(data)
	}
}

//--------------------------------------
// Log
//--------------------------------------

func debug(msg string, v ...interface{}) {
	if verbose {
		logger.Printf("DEBUG " + msg + "\n", v...)
	}
}

func info(msg string, v ...interface{}) {
	logger.Printf("INFO  " + msg + "\n", v...)
}

func warn(msg string, v ...interface{}) {
	logger.Printf("WARN  " + msg + "\n", v...)
}

func fatal(msg string, v ...interface{}) {
	logger.Printf("FATAL " + msg + "\n", v...)
	os.Exit(1)
}

//------------------------------------------------------------------------------
//
// WriteFileCommand
//
//------------------------------------------------------------------------------

// This command allows a server write content to a file. Currently it only
// supports UTF-8 characters in the content.
type WriteFileCommand struct {
	Filename string `json:"filename"`
	Content string `json:"content"`
}

// The name of the command in the log.
func (c *WriteFileCommand) CommandName() string {
	return "file:write"
}

// Validates that the command can be executed on the current state machine.
func (c *WriteFileCommand) Validate(server *raft.Server) error {
	// TODO: Check that the file location is writeable.
	return nil
}

// Writes the contents to the file.
func (c *WriteFileCommand) Apply(server *raft.Server) error{
	path := fmt.Sprintf("%s/%s", server.Path(), c.Filename)
	return ioutil.WriteFile(path, []byte(c.Content), 0644)
}

//

// joinCommand
type joinCommand struct {
	Name string `json:"name"`
}

func (c *joinCommand) CommandName() string {
	return "join"
}

func (c *joinCommand) Apply(server *raft.Server) error {
	err := server.AddPeer(c.Name)
	return err
}
<commit_msg>Fix leader method call.<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/benbjohnson/go-raft"
	"github.com/gorilla/mux"
	"log"
	"io"
	"io/ioutil"
	"net/http"
	"strings"
	"os"
	"time"
)

//------------------------------------------------------------------------------
//
// Initialization
//
//------------------------------------------------------------------------------

var verbose bool

func init() {
	flag.BoolVar(&verbose, "v", false, "verbose logging")
	flag.BoolVar(&verbose, "verbose", false, "verbose logging")
}

//------------------------------------------------------------------------------
//
// Typedefs
//
//------------------------------------------------------------------------------

type Info struct {
	Host string `json:"host"`
	Port int `json:"port"`
}

//------------------------------------------------------------------------------
//
// Variables
//
//------------------------------------------------------------------------------

var server *raft.Server
var logger *log.Logger

//------------------------------------------------------------------------------
//
// Functions
//
//------------------------------------------------------------------------------

//--------------------------------------
// Main
//--------------------------------------

func main() {
	var err error
	logger = log.New(os.Stdout, "", log.LstdFlags)
	flag.Parse()
	if verbose {
		fmt.Println("Verbose logging enabled.\n")
	}

	// Setup commands.
	raft.RegisterCommand(&WriteFileCommand{})
	raft.RegisterCommand(&joinCommand{})

	// Use the present working directory if a directory was not passed in.
	var path string
	if flag.NArg() == 0 {
		path, _ = os.Getwd()
	} else {
		path = flag.Arg(0)
		if err := os.MkdirAll(path, 0744); err != nil {
			fatal("Unable to create path: %v", err)
		}
	}

	// Read server info from file or grab it from user.
	var info *Info = getInfo(path)
	name := fmt.Sprintf("%s:%d", info.Host, info.Port)
	fmt.Printf("Name: %s\n\n", name)

	t := transHandler{}

	// Setup new raft server.
	server, err = raft.NewServer(name, path, t, nil)
	//server.DoHandler = DoHandler;
	server.SetElectionTimeout(2 * time.Second)
	server.SetHeartbeatTimeout(1 * time.Second)
	if err != nil {
		fatal("%v", err)
	}
	server.Start()

	// Join to another server if we don't have a log.
	if server.IsLogEmpty() {
		var leaderHost string
		fmt.Println("This server has no log. Please enter a server in the cluster to join\nto or hit enter to initialize a cluster.");
		fmt.Printf("Join to (host:port)> ");
		fmt.Scanf("%s", &leaderHost)
		if leaderHost == "" {
			server.Initialize()
		} else {
			join(server)
		}
	}

	// Create HTTP interface.
    r := mux.NewRouter()
    r.HandleFunc("/join", JoinHttpHandler).Methods("POST")
    r.HandleFunc("/vote", VoteHttpHandler).Methods("POST")
    r.HandleFunc("/log", GetLogHttpHandler).Methods("GET")
    r.HandleFunc("/log/append", AppendEntriesHttpHandler).Methods("POST")
    r.HandleFunc("/files/{filename}", ReadFileHttpHandler).Methods("GET")
    r.HandleFunc("/files/{filename}", WriteFileHttpHandler).Methods("POST")
    http.Handle("/", r)
	log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", info.Port), nil))
}

func usage() {
	fatal("usage: raftd [PATH]")
}

//--------------------------------------
// Config
//--------------------------------------

func getInfo(path string) *Info {
	info := &Info{}

	// Read in the server info if available.
	infoPath := fmt.Sprintf("%s/info", path)
	if file, err := os.Open(infoPath); err == nil {
		if content, err := ioutil.ReadAll(file); err != nil {
			fatal("Unable to read info: %v", err)
		} else {
			if err = json.Unmarshal(content, &info); err != nil {
				fatal("Unable to parse info: %v", err)
			}
		}
		file.Close()

	// Otherwise ask user for info and write it to file.
	} else {
		fmt.Printf("Enter hostname: [localhost] ");
		fmt.Scanf("%s", &info.Host)
		info.Host = strings.TrimSpace(info.Host)
		if info.Host == "" {
			info.Host = "localhost"
		}

		fmt.Printf("Enter port: [4001] ");
		fmt.Scanf("%d", &info.Port)
		if info.Port == 0 {
			info.Port = 4001
		}

		// Write to file.
		content, _ := json.Marshal(info)
		content = []byte(string(content) + "\n")
		if err := ioutil.WriteFile(infoPath, content, 0644); err != nil {
			fatal("Unable to write info to file: %v", err)
		}
	}

	return info
}

//--------------------------------------
// Handlers
//--------------------------------------

// Send join requests to the leader.
func join(s *raft.Server) error {
	var b bytes.Buffer
	command := &joinCommand{}
	command.Name = s.Name()

	json.NewEncoder(&b).Encode(command)
	debug("[send] POST http://%v/join", "localhost:4001")
	resp, err := http.Post(fmt.Sprintf("http://%s/join", "localhost:4001"), "application/json", &b)
	if resp != nil {
		resp.Body.Close()
		if resp.StatusCode == http.StatusOK {
			return nil
		}
	}
	return fmt.Errorf("raftd: Unable to join: %v", err)
}

type transHandler struct {
	name string
}

// Sends AppendEntries RPCs to a peer when the server is the leader.
func (t transHandler) SendAppendEntriesRequest(server *raft.Server, peer *raft.Peer, req *raft.AppendEntriesRequest) (*raft.AppendEntriesResponse, error) {
	var aersp *raft.AppendEntriesResponse
	var b bytes.Buffer
	json.NewEncoder(&b).Encode(req)
	debug("[send] POST http://%s/log/append [%d]", peer.Name(), len(req.Entries))
	resp, err := http.Post(fmt.Sprintf("http://%s/log/append", peer.Name()), "application/json", &b)
	if resp != nil {
		aersp = &raft.AppendEntriesResponse{}
		if err = json.NewDecoder(resp.Body).Decode(&aersp); err == nil || err == io.EOF {
			return aersp, nil
		}
	}
	return aersp, fmt.Errorf("raftd: Unable to append entries: %v", err)
}

// Sends RequestVote RPCs to a peer when the server is the candidate.
func (t transHandler) SendVoteRequest(server *raft.Server, peer *raft.Peer, req *raft.RequestVoteRequest) (*raft.RequestVoteResponse, error) {
	var rvrsp *raft.RequestVoteResponse
	var b bytes.Buffer
	json.NewEncoder(&b).Encode(req)
	debug("[send] POST http://%s/vote", peer.Name())
	resp, err := http.Post(fmt.Sprintf("http://%s/vote", peer.Name()), "application/json", &b)
	if resp != nil {
		rvrsp := &raft.RequestVoteResponse{}
		if err = json.NewDecoder(resp.Body).Decode(&rvrsp); err == nil || err == io.EOF {
			return rvrsp, nil
		}
	}
	return rvrsp, fmt.Errorf("raftd: Unable to request vote: %v", err)
}

//--------------------------------------
// HTTP Handlers
//--------------------------------------

func GetLogHttpHandler(w http.ResponseWriter, req *http.Request) {
	debug("[recv] GET http://%v/log", server.Name())
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(http.StatusOK)
	json.NewEncoder(w).Encode(server.LogEntries())
}

func JoinHttpHandler(w http.ResponseWriter, req *http.Request) {
	debug("[recv] POST http://%v/join", server.Name())
	command := &joinCommand{}
	if err := decodeJsonRequest(req, command); err == nil {
		if err = server.Do(command); err != nil {
			warn("raftd: Unable to join: %v", err)
			w.WriteHeader(http.StatusInternalServerError)
		} else {
			w.WriteHeader(http.StatusOK)
		}
	} else {
		warn("[join] ERROR: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
	}
}

func VoteHttpHandler(w http.ResponseWriter, req *http.Request) {
	rvreq := &raft.RequestVoteRequest{}
	err := decodeJsonRequest(req, rvreq)
	if err == nil {
		debug("[recv] POST http://%v/vote [%s]", server.Name(), rvreq.CandidateName)
		if resp, _ := server.RequestVote(rvreq); resp != nil {
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(resp)
			return
		}
	}
	w.WriteHeader(http.StatusInternalServerError)
}

func AppendEntriesHttpHandler(w http.ResponseWriter, req *http.Request) {
	aereq := &raft.AppendEntriesRequest{}
	err := decodeJsonRequest(req, aereq)
	if err == nil {
		debug("[recv] POST http://%s/log/append [%d]", server.Name(), len(aereq.Entries))
		if resp, _ := server.AppendEntries(aereq); resp != nil {
			w.WriteHeader(http.StatusOK)
			json.NewEncoder(w).Encode(resp)
			return
		}
	}
	warn("[append] ERROR: %v", err)
	w.WriteHeader(http.StatusInternalServerError)
}

func WriteFileHttpHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	debug("[recv] POST http://%v/files/%s", server.Name(), vars["filename"])

	content, err := ioutil.ReadAll(req.Body)
	if err != nil {
		warn("raftd: Unable to read: %v", err)
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	command := &WriteFileCommand{}
	command.Filename = vars["filename"]
	command.Content = string(content)

	// unlikely to fail twice
	for {
		if server.State() == "leader" {
			if err = server.Do(command); err != nil {
				warn("raftd: Unable to write file: %v", err)
				w.WriteHeader(http.StatusInternalServerError)
			} else {
				// good to go
				w.WriteHeader(http.StatusOK)
				return
			}
		} else {
			// forward
			b := bytes.NewBuffer(content)
			leaderName := server.Leader()
			if leaderName =="" {
				// no luckey, during the voting process
				continue
			}
			debug("[send] POST http://%v/files/%s", leaderName, vars["filename"])
			_, err := http.Post(fmt.Sprintf("http://%v/files/%s", leaderName, vars["filename"]), "application/json", b)
			if err != nil {
				// should check other errors
				continue
			} else {
				//good to go
				return
			}
		}
	}
}

func ReadFileHttpHandler(w http.ResponseWriter, req *http.Request) {
	vars := mux.Vars(req)
	debug("[recv] GET http://%v/files/%s", server.Name(), vars["filename"])

	w.Header().Set("Content-Type", "text/plain")
	w.WriteHeader(http.StatusOK)
	path := fmt.Sprintf("%s/%s", server.Path(), vars["filename"])
	if content, err := ioutil.ReadFile(path); err == nil {
		w.Write(content)
	}
}

//--------------------------------------
// HTTP Utilities
//--------------------------------------

func decodeJsonRequest(req *http.Request, data interface{}) error {
	decoder := json.NewDecoder(req.Body)
	if err := decoder.Decode(&data); err != nil && err != io.EOF {
		logger.Println("Malformed json request: %v", err)
		return fmt.Errorf("Malformed json request: %v", err)
	}
	return nil
}

func encodeJsonResponse(w http.ResponseWriter, status int, data interface{}) {
	w.Header().Set("Content-Type", "application/json")
	w.WriteHeader(status)

	if data != nil {
		encoder := json.NewEncoder(w)
		encoder.Encode(data)
	}
}

//--------------------------------------
// Log
//--------------------------------------

func debug(msg string, v ...interface{}) {
	if verbose {
		logger.Printf("DEBUG " + msg + "\n", v...)
	}
}

func info(msg string, v ...interface{}) {
	logger.Printf("INFO  " + msg + "\n", v...)
}

func warn(msg string, v ...interface{}) {
	logger.Printf("WARN  " + msg + "\n", v...)
}

func fatal(msg string, v ...interface{}) {
	logger.Printf("FATAL " + msg + "\n", v...)
	os.Exit(1)
}

//------------------------------------------------------------------------------
//
// WriteFileCommand
//
//------------------------------------------------------------------------------

// This command allows a server write content to a file. Currently it only
// supports UTF-8 characters in the content.
type WriteFileCommand struct {
	Filename string `json:"filename"`
	Content string `json:"content"`
}

// The name of the command in the log.
func (c *WriteFileCommand) CommandName() string {
	return "file:write"
}

// Validates that the command can be executed on the current state machine.
func (c *WriteFileCommand) Validate(server *raft.Server) error {
	// TODO: Check that the file location is writeable.
	return nil
}

// Writes the contents to the file.
func (c *WriteFileCommand) Apply(server *raft.Server) error{
	path := fmt.Sprintf("%s/%s", server.Path(), c.Filename)
	return ioutil.WriteFile(path, []byte(c.Content), 0644)
}

//

// joinCommand
type joinCommand struct {
	Name string `json:"name"`
}

func (c *joinCommand) CommandName() string {
	return "join"
}

func (c *joinCommand) Apply(server *raft.Server) error {
	err := server.AddPeer(c.Name)
	return err
}
<|endoftext|>


<commit_before>package QesyGo

import (
	"github.com/garyburd/redigo/redis"
	"time"
)

type CacheRedis struct {
	Pool     *redis.Pool // redis connection pool
	Conninfo string
	Auth     string
}

func (cr *CacheRedis) newPool() {
	cr.Pool = &redis.Pool{
		MaxIdle:     30,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", cr.Conninfo)
			if err != nil {
				return nil, err
			}
			if cr.Auth != "" {
				if _, err := c.Do("AUTH", cr.Auth); err != nil {
					c.Close()
					return nil, err
				}
			}

			return c, err
		},
	}
}

//var pool = newPool()
func (cr *CacheRedis) Connect() error {
	cr.newPool()
	c := cr.Pool.Get()
	defer c.Close()
	return c.Err()
}

func (cr *CacheRedis) do(commandName string, args ...interface{}) (reply interface{}, err error) {
	c := cr.Pool.Get()
	defer c.Close()
	return c.Do(commandName, args...)
}

func (cr *CacheRedis) FlushAll() error {
	_, err := cr.do("FLUSHALL")
	return err
}

func (cr *CacheRedis) Get(key string) (string, error) {
	str, err := redis.String(cr.do("GET", key))
	return str, err
}

func (cr *CacheRedis) Set(key string, value string) error {
	_, err := cr.do("SET", key, value)
	return err
}

func (cr *CacheRedis) Del(key string) error {
	_, err := cr.do("DEL", key)
	return err
}

func (cr *CacheRedis) Exists(key string) (bool, error) {
	return redis.Bool(cr.do("EXISTS", key))
}

func (cr *CacheRedis) Expire(key string, second int) (bool, error) {
	return redis.Bool(cr.do("EXPIRE", key, second))
}

func (cr *CacheRedis) Keys(key string) ([]string, error) {
	return redis.Strings(cr.do("KEYS", key))
}

func (cr *CacheRedis) Ttl(key string) (interface{}, error) {
	return redis.Int(cr.do("TTL", key))
}

func (cr *CacheRedis) HMset(key string, arr interface{}) (interface{}, error) {
	return cr.do("HMSET", redis.Args{}.Add(key).AddFlat(arr)...)
}

func (cr *CacheRedis) HGet(key string, subKey string) (interface{}, error) {
	return cr.do("HGET", key, subKey)
}

func (cr *CacheRedis) HGetAll(key string) (map[string]string, error) {
	rsByte, err := cr.do("HGETALL", key)
	if err != nil {
		return nil, err
	}
	rs, err := redis.StringMap(rsByte, err)
	if err != nil {
		return nil, err
	}
	return rs, err
}

func (cr *CacheRedis) ZAdd(Key string, Score int64, Name string) (interface{}, error) {
	return cr.do("ZADD", Key, Score, Name)
}

func (cr *CacheRedis) ZCard(Key string) (int, error) {
	return redis.Int(cr.do("ZCARD", Key))
}

func (cr *CacheRedis) ZRange(Key string, Start int32, End int32) ([]map[string]string, error) {
	rank := []map[string]string{}
	if rsByte, err := cr.do("ZRANGE", Key, Start, End, "WITHSCORES"); err == nil {
		rsByteArr := rsByte.([]interface{})
		for i := 0; i < len(rsByteArr)/2; i++ {
			index := i * 2
			key := string(rsByteArr[index].([]byte))
			val := string(rsByteArr[index+1].([]byte))
			rank = append(rank, map[string]string{key: val})
		}
		return rank, err
	} else {
		return rank, err
	}
}

func (cr *CacheRedis) ZRevRange(Key string, Start int32, End int32) (interface{}, error) {
	rank := []map[string]string{}
	if rsByte, err := cr.do("ZREVRANGE", Key, Start, End, "WITHSCORES"); err == nil {
		rsByteArr := rsByte.([]interface{})
		for i := 0; i < len(rsByteArr)/2; i++ {
			index := i * 2
			key := string(rsByteArr[index].([]byte))
			val := string(rsByteArr[index+1].([]byte))
			rank = append(rank, map[string]string{key: val})
		}
		return rank, err
	} else {
		return rank, err
	}
}
<commit_msg>fix redis bugs<commit_after>package QesyGo

import (
	"github.com/garyburd/redigo/redis"
	"time"
)

type CacheRedis struct {
	Pool     *redis.Pool // redis connection pool
	Conninfo string
	Auth     string
}

func (cr *CacheRedis) newPool() {
	cr.Pool = &redis.Pool{
		MaxIdle:     30,
		IdleTimeout: 240 * time.Second,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", cr.Conninfo)
			if err != nil {
				return nil, err
			}
			if cr.Auth != "" {
				if _, err := c.Do("AUTH", cr.Auth); err != nil {
					c.Close()
					return nil, err
				}
			}

			return c, err
		},
	}
}

//var pool = newPool()
func (cr *CacheRedis) Connect() error {
	cr.newPool()
	c := cr.Pool.Get()
	defer c.Close()
	return c.Err()
}

func (cr *CacheRedis) do(commandName string, args ...interface{}) (reply interface{}, err error) {
	c := cr.Pool.Get()
	defer c.Close()
	return c.Do(commandName, args...)
}

func (cr *CacheRedis) FlushAll() error {
	_, err := cr.do("FLUSHALL")
	return err
}

func (cr *CacheRedis) Get(key string) (string, error) {
	str, err := redis.String(cr.do("GET", key))
	return str, err
}

func (cr *CacheRedis) Set(key string, value string) error {
	_, err := cr.do("SET", key, value)
	return err
}

func (cr *CacheRedis) Del(key string) error {
	_, err := cr.do("DEL", key)
	return err
}

func (cr *CacheRedis) Exists(key string) (bool, error) {
	return redis.Bool(cr.do("EXISTS", key))
}

func (cr *CacheRedis) Expire(key string, second int) (bool, error) {
	return redis.Bool(cr.do("EXPIRE", key, second))
}

func (cr *CacheRedis) Keys(key string) ([]string, error) {
	return redis.Strings(cr.do("KEYS", key))
}

func (cr *CacheRedis) Ttl(key string) (interface{}, error) {
	return redis.Int(cr.do("TTL", key))
}

func (cr *CacheRedis) HMset(key string, arr interface{}) (interface{}, error) {
	return cr.do("HMSET", redis.Args{}.Add(key).AddFlat(arr)...)
}

func (cr *CacheRedis) HGet(key string, subKey string) (interface{}, error) {
	return cr.do("HGET", key, subKey)
}

func (cr *CacheRedis) HGetAll(key string) (map[string]string, error) {
	rsByte, err := cr.do("HGETALL", key)
	if err != nil {
		return nil, err
	}
	rs, err := redis.StringMap(rsByte, err)
	if err != nil {
		return nil, err
	}
	return rs, err
}

func (cr *CacheRedis) ZAdd(Key string, Score int32, Name string) (interface{}, error) {
	return cr.do("ZADD", Key, Score, Name)
}

func (cr *CacheRedis) ZCard(Key string) (int, error) {
	return redis.Int(cr.do("ZCARD", Key))
}

func (cr *CacheRedis) ZRange(Key string, Start int32, End int32) ([]map[string]string, error) {
	rank := []map[string]string{}
	if rsByte, err := cr.do("ZRANGE", Key, Start, End, "WITHSCORES"); err == nil {
		rsByteArr := rsByte.([]interface{})
		for i := 0; i < len(rsByteArr)/2; i++ {
			index := i * 2
			key := string(rsByteArr[index].([]byte))
			val := string(rsByteArr[index+1].([]byte))
			rank = append(rank, map[string]string{key: val})
		}
		return rank, err
	} else {
		return rank, err
	}
}

func (cr *CacheRedis) ZRevRange(Key string, Start int32, End int32) (interface{}, error) {
	rank := []map[string]string{}
	if rsByte, err := cr.do("ZREVRANGE", Key, Start, End, "WITHSCORES"); err == nil {
		rsByteArr := rsByte.([]interface{})
		for i := 0; i < len(rsByteArr)/2; i++ {
			index := i * 2
			key := string(rsByteArr[index].([]byte))
			val := string(rsByteArr[index+1].([]byte))
			rank = append(rank, map[string]string{key: val})
		}
		return rank, err
	} else {
		return rank, err
	}
}
<|endoftext|>


<commit_before>package mmbot

import (
	"io/ioutil"
	"log"
	"mmbot/adapter"
	"mmbot/message"
	"mmbot/mmhook"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/robfig/cron"
)

type Robot struct {
	Config    *Config
	Client    adapter.Adapter
	Handlers  []Handler
	Routes    []Route
	Jobs      []Job
	scheduler *cron.Cron
	logger    *log.Logger
	quit      chan bool
}

func NewRobot(config *Config) *Robot {
	if config.Logger == nil {
		config.Logger = log.New(ioutil.Discard, "", 0)
	}
	bot := &Robot{
		Config: config,
		Client: mmhook.NewClient(config.AdapterConfig, config.Logger),
		logger: config.Logger,
		quit:   make(chan bool),
	}

	return bot
}

func (r *Robot) Run() {
	r.runLoop()

	r.Client.Stop()
	r.logger.Println("Stop adapter")

	if r.scheduler != nil {
		r.scheduler.Stop()
		r.logger.Println("Stop job scheduler")
	}
}

func (r *Robot) runLoop() {
	if !r.Config.DisableServer {
		go r.startServer()
	}
	go r.startClient()

	receiver := r.Client.Receiver()
	for {
		select {
		case <-r.quit:
			return
		case msg := <-receiver:
			r.handle(&msg)
		}
	}
}

func (r *Robot) Stop() {
	r.quit <- true
}

func (r *Robot) Send(msg *message.OutMessage) error {
	return r.Client.Send(msg)
}

func (r *Robot) SenderName() string {
	return r.Config.UserName
}

func (r *Robot) handle(msg *message.InMessage) {
	msg.Sender = r

	for _, handler := range r.Handlers {
		if handler.CanHandle(msg) {
			err := handler.Handle(msg)
			if err != nil {
				r.logger.Print(err)
			}
		}
	}
}

func (r *Robot) startClient() {
	err := r.Client.Run()
	if err != nil {
		r.logger.Print(err)
	}
	r.Stop()
}

func (r *Robot) startScheduler() {
	if r.Jobs == nil || len(r.Jobs) == 0 {
		return
	}

	r.scheduler = cron.New()
	for _, job := range r.Jobs {
		r.scheduler.AddFunc(job.Schedule, func() {
			job.Action(r)
		})
	}
	r.scheduler.Start()
	r.logger.Println("Start job scheduler")
}

func (r *Robot) startServer() {
	mux := mux.NewRouter()
	r.mountRoutes(mux)
	r.mountClient(mux)

	r.logger.Printf("Listening on %s\n", r.Config.Address())
	if err := http.ListenAndServe(r.Config.Address(), mux); err != nil {
		r.logger.Fatal(err)
	}
	r.Stop()
}

func (r *Robot) mountClient(mux *mux.Router) {
	hook := r.Client.IncomingWebHook()
	if hook == nil {
		return
	}

	path := hook.Path
	if path == "" {
		path = "/"
	}
	mux.Handle(path, hook.Handler)
}

func (r *Robot) mountRoutes(mux *mux.Router) {
	for _, route := range r.Routes {
		if route.Pattern == "" || route.Action == nil {
			log.Fatalf("Invalid route: %v", route)
		}
		mr := mux.HandleFunc(route.Pattern, r.wrapRouteAction(&route))
		if route.Methods != nil && len(route.Methods) > 0 {
			mr.Methods(route.Methods...)
		}
	}
}

func (r *Robot) wrapRouteAction(route *Route) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		route.Action(r, w, req)
	}
}

func (r *Robot) RouteVars(req *http.Request) map[string]string {
	return mux.Vars(req)
}
<commit_msg>Fix route handlers<commit_after>package mmbot

import (
	"io/ioutil"
	"log"
	"mmbot/adapter"
	"mmbot/message"
	"mmbot/mmhook"
	"net/http"

	"github.com/gorilla/mux"
	"github.com/robfig/cron"
)

type Robot struct {
	Config    *Config
	Client    adapter.Adapter
	Handlers  []Handler
	Routes    []Route
	Jobs      []Job
	scheduler *cron.Cron
	logger    *log.Logger
	quit      chan bool
}

func NewRobot(config *Config) *Robot {
	if config.Logger == nil {
		config.Logger = log.New(ioutil.Discard, "", 0)
	}
	bot := &Robot{
		Config: config,
		Client: mmhook.NewClient(config.AdapterConfig, config.Logger),
		logger: config.Logger,
		quit:   make(chan bool),
	}

	return bot
}

func (r *Robot) Run() {
	r.runLoop()

	r.Client.Stop()
	r.logger.Println("Stop adapter")

	if r.scheduler != nil {
		r.scheduler.Stop()
		r.logger.Println("Stop job scheduler")
	}
}

func (r *Robot) runLoop() {
	if !r.Config.DisableServer {
		go r.startServer()
	}
	go r.startClient()

	receiver := r.Client.Receiver()
	for {
		select {
		case <-r.quit:
			return
		case msg := <-receiver:
			r.handle(&msg)
		}
	}
}

func (r *Robot) Stop() {
	r.quit <- true
}

func (r *Robot) Send(msg *message.OutMessage) error {
	return r.Client.Send(msg)
}

func (r *Robot) SenderName() string {
	return r.Config.UserName
}

func (r *Robot) handle(msg *message.InMessage) {
	msg.Sender = r

	for _, handler := range r.Handlers {
		if handler.CanHandle(msg) {
			err := handler.Handle(msg)
			if err != nil {
				r.logger.Print(err)
			}
		}
	}
}

func (r *Robot) startClient() {
	err := r.Client.Run()
	if err != nil {
		r.logger.Print(err)
	}
	r.Stop()
}

func (r *Robot) startScheduler() {
	if r.Jobs == nil || len(r.Jobs) == 0 {
		return
	}

	r.scheduler = cron.New()
	for _, job := range r.Jobs {
		r.scheduler.AddFunc(job.Schedule, func() {
			job.Action(r)
		})
	}
	r.scheduler.Start()
	r.logger.Println("Start job scheduler")
}

func (r *Robot) startServer() {
	mux := mux.NewRouter()
	r.mountRoutes(mux)
	r.mountClient(mux)

	r.logger.Printf("Listening on %s\n", r.Config.Address())
	if err := http.ListenAndServe(r.Config.Address(), mux); err != nil {
		r.logger.Fatal(err)
	}
	r.Stop()
}

func (r *Robot) mountClient(mux *mux.Router) {
	hook := r.Client.IncomingWebHook()
	if hook == nil {
		return
	}

	path := hook.Path
	if path == "" {
		path = "/"
	}
	mux.Handle(path, hook.Handler)
}

func (r *Robot) mountRoutes(mux *mux.Router) {
	for _, route := range r.Routes {
		if route.Pattern == "" || route.Action == nil {
			log.Fatalf("Invalid route: %v", route)
		}

		wrapped := r.wrapRouteAction(route)
		mr := mux.HandleFunc(route.Pattern, wrapped)
		if route.Methods != nil && len(route.Methods) > 0 {
			mr.Methods(route.Methods...)
		}
	}
}

func (r *Robot) wrapRouteAction(route Route) func(http.ResponseWriter, *http.Request) {
	return func(w http.ResponseWriter, req *http.Request) {
		route.Action(r, w, req)
	}
}

func (r *Robot) RouteVars(req *http.Request) map[string]string {
	return mux.Vars(req)
}
<|endoftext|>


<commit_before>package hal

import (
	"fmt"
	"github.com/ccding/go-logging/logging"
	// "github.com/davecgh/go-spew/spew"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

// Robot receives messages from an adapter and sends them to listeners
type Robot struct {
	*Config
	Name    string
	Alias   string
	Adapter Adapter
	Logger  *logging.Logger
	Port    string
	Router  *http.ServeMux

	handlers   []Handler
	signalChan chan os.Signal
}

// Handlers returns the robot's handlers
func (robot *Robot) Handlers() []Handler {
	return robot.handlers
}

// NewRobot returns a new Robot instance
func NewRobot() (*Robot, error) {
	config := NewConfig()
	robot := &Robot{
		Name:       config.Name,
		Logger:     config.Logger,
		Port:       config.Port,
		Config:     config,
		Router:     newRouter(),
		signalChan: make(chan os.Signal, 1),
	}

	adapter, err := NewAdapter(robot)
	if err != nil {
		robot.Logger.Error(err)
		return nil, err
	}
	robot.SetAdapter(adapter)

	return robot, nil
}

// Handle registers a new handler with the robot
func (robot *Robot) Handle(handlers ...Handler) {
	robot.handlers = append(robot.handlers, handlers...)
}

// Receive dispatches messages to our handlers
func (robot *Robot) Receive(msg *Message) error {
	robot.Logger.Debug("slack - robot received message")
	for _, handler := range robot.handlers {
		response := NewResponse(robot, msg)
		err := handler.Handle(response)
		if err != nil {
			robot.Logger.Error(err)
			return err
		}
	}
	return nil
}

// Stop initiates the shutdown process
func (robot *Robot) Stop() error {
	robot.Adapter.Stop()
	robot.Logger.Info("Stopping robot.")

	return nil
}

// Run initiates the startup process
func (robot *Robot) Run() error {

	robot.Logger.Info("Starting robot.")
	go robot.Adapter.Run()
	// Start the HTTP server after the adapter, as adapter.Run() adds additional
	// handlers to the router.
	robot.Logger.Debug("Starting HTTP server. ")
	go http.ListenAndServe(`:`+robot.Port, robot.Router)

	signal.Notify(robot.signalChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	stop := false
	for !stop {
		select {
		case sig := <-robot.signalChan:
			switch sig {
			// case syscall.SIGHUP:
			// robot.Logger.Info("Reloading")
			case syscall.SIGINT, syscall.SIGTERM:
				stop = true
			}
		}
	}
	// Stop listening for new signals
	signal.Stop(robot.signalChan)

	// Initiate the shutdown process for our robot
	robot.Stop()

	return nil
}

func (robot *Robot) respondRegex(pattern string) string {
	str := `^(?:`
	if robot.Alias != "" {
		str += `(?:` + robot.Alias + `|` + robot.Name + `)`
	} else {
		str += robot.Name
	}
	str += `[:,]?)\s+(?:` + pattern + `)`
	return str
}

// newRouter initializes a new http.ServeMux and sets up several default routes
func newRouter() *http.ServeMux {
	router := http.NewServeMux()
	router.HandleFunc("/hal/ping", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "PONG")
	})

	router.HandleFunc("/hal/time", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Server time is: %s\n", time.Now())
	})

	return router
}

// func (robot *Robot) SetName(name string) {
// 	robot.Name = name
// }

// func (robot *Robot) SetLogger(logger *logging.Logger) {
// 	robot.Logger = logger
// }

// func (robot *Robot) SetAdapter(adapter Adapter) {
// 	robot.Adapter = adapter
// }

// func (robot *Robot) SetPort(port string) {
// 	robot.Port = port
// }

// func (robot *Robot) SetRouter(router *http.ServeMux) {
// 	robot.Router = router
// }
<commit_msg>whoops<commit_after>package hal

import (
	"fmt"
	"github.com/ccding/go-logging/logging"
	// "github.com/davecgh/go-spew/spew"
	"net/http"
	"os"
	"os/signal"
	"syscall"
	"time"
)

// Robot receives messages from an adapter and sends them to listeners
type Robot struct {
	*Config
	Name    string
	Alias   string
	Adapter Adapter
	Logger  *logging.Logger
	Port    string
	Router  *http.ServeMux

	handlers   []Handler
	signalChan chan os.Signal
}

// Handlers returns the robot's handlers
func (robot *Robot) Handlers() []Handler {
	return robot.handlers
}

// NewRobot returns a new Robot instance
func NewRobot() (*Robot, error) {
	config := NewConfig()
	robot := &Robot{
		Name:       config.Name,
		Logger:     config.Logger,
		Port:       config.Port,
		Config:     config,
		Router:     newRouter(),
		signalChan: make(chan os.Signal, 1),
	}

	adapter, err := NewAdapter(robot)
	if err != nil {
		robot.Logger.Error(err)
		return nil, err
	}
	robot.SetAdapter(adapter)

	return robot, nil
}

// Handle registers a new handler with the robot
func (robot *Robot) Handle(handlers ...Handler) {
	robot.handlers = append(robot.handlers, handlers...)
}

// Receive dispatches messages to our handlers
func (robot *Robot) Receive(msg *Message) error {
	robot.Logger.Debug("slack - robot received message")
	for _, handler := range robot.handlers {
		response := NewResponse(robot, msg)
		err := handler.Handle(response)
		if err != nil {
			robot.Logger.Error(err)
			return err
		}
	}
	return nil
}

// Stop initiates the shutdown process
func (robot *Robot) Stop() error {
	robot.Adapter.Stop()
	robot.Logger.Info("Stopping robot.")

	return nil
}

// Run initiates the startup process
func (robot *Robot) Run() error {

	robot.Logger.Info("Starting robot.")
	go robot.Adapter.Run()
	// Start the HTTP server after the adapter, as adapter.Run() adds additional
	// handlers to the router.
	robot.Logger.Debug("Starting HTTP server. ")
	go http.ListenAndServe(`:`+robot.Port, robot.Router)

	signal.Notify(robot.signalChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGHUP)

	stop := false
	for !stop {
		select {
		case sig := <-robot.signalChan:
			switch sig {
			// case syscall.SIGHUP:
			// robot.Logger.Info("Reloading")
			case syscall.SIGINT, syscall.SIGTERM:
				stop = true
			}
		}
	}
	// Stop listening for new signals
	signal.Stop(robot.signalChan)

	// Initiate the shutdown process for our robot
	robot.Stop()

	return nil
}

func (robot *Robot) respondRegex(pattern string) string {
	str := `^(?:`
	if robot.Alias != "" {
		str += `(?:` + robot.Alias + `|` + robot.Name + `)`
	} else {
		str += robot.Name
	}
	str += `[:,]?)\s+(?:` + pattern + `)`
	return str
}

// newRouter initializes a new http.ServeMux and sets up several default routes
func newRouter() *http.ServeMux {
	router := http.NewServeMux()
	router.HandleFunc("/hal/ping", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "PONG")
	})

	router.HandleFunc("/hal/time", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintf(w, "Server time is: %s\n", time.Now())
	})

	return router
}

// func (robot *Robot) SetName(name string) {
// 	robot.Name = name
// }

// func (robot *Robot) SetLogger(logger *logging.Logger) {
// 	robot.Logger = logger
// }

func (robot *Robot) SetAdapter(adapter Adapter) {
	robot.Adapter = adapter
}

// func (robot *Robot) SetPort(port string) {
// 	robot.Port = port
// }

// func (robot *Robot) SetRouter(router *http.ServeMux) {
// 	robot.Router = router
// }
<|endoftext|>


<commit_before>package scanfile

import (
	"fmt"
	"io"
	"os"
	"sync"
)

var LineFeed = byte('\n') //文本换行符标识
var BufSize = 1024 * 1024 // buf大小
var MaxResult = 10

func MemScan(mf *MemFiles, searchStr *string) string {
	var result ScanResult
	//计数器
	counter := InitCounter(MaxResult)

	//扫描结果输出通道
	out := make(chan *FileRes, 10)

	go func() {
		for _, memFc := range mf.MFile {
			go MemScanFile(memFc, searchStr, counter, out)
		}
	}()

	for i := 0; i < len(mf.MFile); i++ {
		result.AddFileRes(<-out)
	}

	result.AddCounter(counter)
	return result.ToJson()
}

/* 扫描文件 */
func MemScanFile(fc *fileContent, searchStr *string, counter *Counter, out chan *FileRes) {

	//文件 IO
	fileContentChan := make(chan *string, 10)

	go func() {
		for fsi := 0; fsi < fc.Size; fsi++ {
			if counter.IsMax() {
				close(fileContentChan)
				break

			}
			fileContentChan <- fc.getSegment(fsi).Content
		}
	}()

	go func() {
		fileRes := InitFileRes(fc.FileName)

		//使用多路复用 wg防止线程泄漏
		wg := sync.WaitGroup{}
		for i := 0; i < 3; i++ {
			wg.Add(1)
			go func() {
				for {

					if text, ok := <-fileContentChan; ok {

						if counter.IsMax() {
							//清空未读取channel
							clearFileContentChan(fileContentChan)

							break
						} else {
							if counter.IsMax() {
								break
							}

							rs := strScan(text, searchStr, counter)
							for i := 0; i < len(rs); i++ {
								fileRes.Add(rs[i])
							}
						}
					} else {
						break
					}
				}
				wg.Done()
			}()
		}
		wg.Wait()
		out <- fileRes
	}()
}

func Scan(files []string, searchStr *string) string {

	var result ScanResult
	//计数器
	counter := InitCounter(MaxResult)

	//扫描结果输出通道
	out := make(chan *FileRes, 10)

	fileCount := len(files)

	for i := 0; i < fileCount; i++ {
		go ScanFile(files[i], searchStr, counter, out)
	}

	for i := 0; i < fileCount; i++ {
		result.AddFileRes(<-out)
	}

	result.AddCounter(counter)
	return result.ToJson()
}

/* 扫描文件 */
func ScanFile(fileName string, searchStr *string, counter *Counter, out chan *FileRes) {
	//文件 IO
	fileContentChan := fileRead(fileName, counter)

	fileRes := InitFileRes(fileName)

	//使用多路复用 wg防止线程泄漏
	wg := sync.WaitGroup{}
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			for {
				if text, ok := <-fileContentChan; ok {
					if counter.IsMax() {
						//清空未读取channel
						clearFileContentChan(fileContentChan)
						break
					} else {
						if counter.IsMax() {
							break
						}
						rs := strScan(text, searchStr, counter)
						for i := 0; i < len(rs); i++ {
							fileRes.Add(rs[i])
						}
					}
				} else {
					break
				}
			}

			wg.Done()
		}()
	}
	wg.Wait()
	out <- fileRes
}

/* 消费闲置channel */
func clearFileContentChan(c chan *string) {
	for {

		if _, ok := <-c; ok == false {
			break
		}
	}
}

/* 文件IO操作，字符流放入channel */
func fileRead(fileName string, counter *Counter) chan *string {
	fileContentChan := make(chan *string, 5)
	go func() {
		fh, err := os.Open(fileName)
		if err != nil {
			panic(err)
		}

		//异常处理
		defer fh.Close()

		buf := make([]byte, BufSize)

		var start int64
		fh.Seek(start, 0)
		for {
			//超过计数器最大返回值 跳出程序
			if counter.IsMax() {
				break
			}
			n, err := fh.Read(buf)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n == 0 {
				break
			}

			l := lastByteIndex(buf, LineFeed)
			content := string(buf[0 : l+1])
			start += int64(l + 1)
			fh.Seek(start, 0)
			fileContentChan <- &content
		}
		close(fileContentChan)
	}()
	return fileContentChan
}

/* last byte in slice byte */
func lastByteIndex(s []byte, sep byte) int {
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == sep {
			return i
		}
	}
	return -1
}

type fileSegment struct {
	Index   int
	Content *string
}

func (self *fileSegment) toString() string {
	return *self.Content
}

type fileContent struct {
	FileName string
	Size     int
	Segment  map[int]*fileSegment
}

func newFileContent(fileName string) *fileContent {
	fc := new(fileContent)
	fc.FileName = fileName
	fc.Segment = make(map[int]*fileSegment)
	return fc
}

func (self *fileContent) addSegment(fs *fileSegment, index int) {
	self.Size++
	self.Segment[index] = fs
}

func (self *fileContent) getSegment(index int) *fileSegment {
	seg, has := self.Segment[index]
	if has {
		return seg
	} else {
		return nil
	}
}

func InitMemFileContent(fileName string) *fileContent {
	fileContentCh := IoFileRead(fileName)
	fc := newFileContent(fileName)

	index := 0
	for {
		if content, ok := <-fileContentCh; ok {
			s := new(fileSegment)
			s.Index++
			s.Content = content

			fc.addSegment(s, index)

			index++
		} else {
			break
		}
	}
	return fc
}

type MemFiles struct {
	MFile map[string]*fileContent
}

func (self *MemFiles) addFile(filePath string) {
	fc := InitMemFileContent(filePath)
	self.MFile[filePath] = fc
}

func InitMemFiles(files []string) *MemFiles {
	mf := new(MemFiles)
	mf.MFile = make(map[string]*fileContent)

	for _, v := range files {
		mf.addFile(v)
		fmt.Println("Load file ", v)
	}

	return mf
}

/* 文件IO操作，字符流放入channel */
func IoFileRead(fileName string) chan *string {
	fileContentChan := make(chan *string, 5)
	go func() {
		fh, err := os.Open(fileName)
		if err != nil {
			panic(err)
		}

		//异常处理
		defer fh.Close()

		buf := make([]byte, BufSize)

		var start int64
		fh.Seek(start, 0)
		for {
			n, err := fh.Read(buf)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n == 0 {
				break
			}

			l := lastByteIndex(buf, LineFeed)
			content := string(buf[0 : l+1])
			start += int64(l + 1)
			fh.Seek(start, 0)
			fileContentChan <- &content
		}
		close(fileContentChan)
	}()
	return fileContentChan
}
<commit_msg>修复关闭channel bug<commit_after>package scanfile

import (
	"fmt"
	"io"
	"os"
	"sync"
)

var LineFeed = byte('\n') //文本换行符标识
var BufSize = 1024 * 1024 // buf大小
var MaxResult = 10

func MemScan(mf *MemFiles, searchStr *string) string {
	var result ScanResult
	//计数器
	counter := InitCounter(MaxResult)

	//扫描结果输出通道
	out := make(chan *FileRes, 10)

	go func() {
		for _, memFc := range mf.MFile {
			go MemScanFile(memFc, searchStr, counter, out)
		}
	}()

	for i := 0; i < len(mf.MFile); i++ {
		result.AddFileRes(<-out)
	}

	result.AddCounter(counter)
	return result.ToJson()
}

/* 扫描文件 */
func MemScanFile(fc *fileContent, searchStr *string, counter *Counter, out chan *FileRes) {

	//文件 IO
	fileContentChan := make(chan *string, 10)

	go func() {
		for fsi := 0; fsi < fc.Size; fsi++ {
			if counter.IsMax() {
				close(fileContentChan)
				break

			}
			fileContentChan <- fc.getSegment(fsi).Content
		}
		close(fileContentChan)
	}()

	go func() {
		fileRes := InitFileRes(fc.FileName)

		//使用多路复用 wg防止线程泄漏
		wg := sync.WaitGroup{}
		for i := 0; i < 3; i++ {
			wg.Add(1)
			go func() {
				for {

					if text, ok := <-fileContentChan; ok {

						if counter.IsMax() {
							//清空未读取channel
							clearFileContentChan(fileContentChan)

							break
						} else {
							if counter.IsMax() {
								break
							}

							rs := strScan(text, searchStr, counter)
							for i := 0; i < len(rs); i++ {
								fileRes.Add(rs[i])
							}
						}
					} else {
						break
					}
				}
				wg.Done()
			}()
		}
		wg.Wait()
		out <- fileRes
	}()
}

func Scan(files []string, searchStr *string) string {

	var result ScanResult
	//计数器
	counter := InitCounter(MaxResult)

	//扫描结果输出通道
	out := make(chan *FileRes, 10)

	fileCount := len(files)

	for i := 0; i < fileCount; i++ {
		go ScanFile(files[i], searchStr, counter, out)
	}

	for i := 0; i < fileCount; i++ {
		result.AddFileRes(<-out)
	}

	result.AddCounter(counter)
	return result.ToJson()
}

/* 扫描文件 */
func ScanFile(fileName string, searchStr *string, counter *Counter, out chan *FileRes) {
	//文件 IO
	fileContentChan := fileRead(fileName, counter)

	fileRes := InitFileRes(fileName)

	//使用多路复用 wg防止线程泄漏
	wg := sync.WaitGroup{}
	for i := 0; i < 10; i++ {
		wg.Add(1)
		go func() {
			for {
				if text, ok := <-fileContentChan; ok {
					if counter.IsMax() {
						//清空未读取channel
						clearFileContentChan(fileContentChan)
						break
					} else {
						if counter.IsMax() {
							break
						}
						rs := strScan(text, searchStr, counter)
						for i := 0; i < len(rs); i++ {
							fileRes.Add(rs[i])
						}
					}
				} else {
					break
				}
			}

			wg.Done()
		}()
	}
	wg.Wait()
	out <- fileRes
}

/* 消费闲置channel */
func clearFileContentChan(c chan *string) {
	for {

		if _, ok := <-c; ok == false {
			break
		}
	}
}

/* 文件IO操作，字符流放入channel */
func fileRead(fileName string, counter *Counter) chan *string {
	fileContentChan := make(chan *string, 5)
	go func() {
		fh, err := os.Open(fileName)
		if err != nil {
			panic(err)
		}

		//异常处理
		defer fh.Close()

		buf := make([]byte, BufSize)

		var start int64
		fh.Seek(start, 0)
		for {
			//超过计数器最大返回值 跳出程序
			if counter.IsMax() {
				break
			}
			n, err := fh.Read(buf)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n == 0 {
				break
			}

			l := lastByteIndex(buf, LineFeed)
			content := string(buf[0 : l+1])
			start += int64(l + 1)
			fh.Seek(start, 0)
			fileContentChan <- &content
		}
		close(fileContentChan)
	}()
	return fileContentChan
}

/* last byte in slice byte */
func lastByteIndex(s []byte, sep byte) int {
	for i := len(s) - 1; i >= 0; i-- {
		if s[i] == sep {
			return i
		}
	}
	return -1
}

type fileSegment struct {
	Index   int
	Content *string
}

func (self *fileSegment) toString() string {
	return *self.Content
}

type fileContent struct {
	FileName string
	Size     int
	Segment  map[int]*fileSegment
}

func newFileContent(fileName string) *fileContent {
	fc := new(fileContent)
	fc.FileName = fileName
	fc.Segment = make(map[int]*fileSegment)
	return fc
}

func (self *fileContent) addSegment(fs *fileSegment, index int) {
	self.Size++
	self.Segment[index] = fs
}

func (self *fileContent) getSegment(index int) *fileSegment {
	seg, has := self.Segment[index]
	if has {
		return seg
	} else {
		return nil
	}
}

func InitMemFileContent(fileName string) *fileContent {
	fileContentCh := IoFileRead(fileName)
	fc := newFileContent(fileName)

	index := 0
	for {
		if content, ok := <-fileContentCh; ok {
			s := new(fileSegment)
			s.Index++
			s.Content = content

			fc.addSegment(s, index)

			index++
		} else {
			break
		}
	}
	return fc
}

type MemFiles struct {
	MFile map[string]*fileContent
}

func (self *MemFiles) addFile(filePath string) {
	fc := InitMemFileContent(filePath)
	self.MFile[filePath] = fc
}

func InitMemFiles(files []string) *MemFiles {
	mf := new(MemFiles)
	mf.MFile = make(map[string]*fileContent)

	for _, v := range files {
		mf.addFile(v)
		fmt.Println("Load file ", v)
	}

	return mf
}

/* 文件IO操作，字符流放入channel */
func IoFileRead(fileName string) chan *string {
	fileContentChan := make(chan *string, 5)
	go func() {
		fh, err := os.Open(fileName)
		if err != nil {
			panic(err)
		}

		//异常处理
		defer fh.Close()

		buf := make([]byte, BufSize)

		var start int64
		fh.Seek(start, 0)
		for {
			n, err := fh.Read(buf)
			if err != nil && err != io.EOF {
				panic(err)
			}
			if n == 0 {
				break
			}

			l := lastByteIndex(buf, LineFeed)
			content := string(buf[0 : l+1])
			start += int64(l + 1)
			fh.Seek(start, 0)
			fileContentChan <- &content
		}
		close(fileContentChan)
	}()
	return fileContentChan
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net/http"

	"github.com/sontags/env"
)

var port, logging string

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if logging != "" {
			log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
		}
		handler.ServeHTTP(w, r)
	})
}

func init() {
	env.Var(&port, "PORT", "8989", "Port that is binded")
	env.Var(&logging, "LOG", "", "If not empty, log output will be written to STDOUT")
}

func main() {
	env.Parse("S")
	log.Println("listening on port", port)
	log.Fatal(http.ListenAndServe(":"+port, Log(http.FileServer(http.Dir(".")))))
}
<commit_msg>made the path to serve configurable<commit_after>package main

import (
	"log"
	"net/http"

	"github.com/sontags/env"
)

var port, logging, dir string

func Log(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if logging != "" {
			log.Printf("%s %s %s\n", r.RemoteAddr, r.Method, r.URL)
		}
		handler.ServeHTTP(w, r)
	})
}

func init() {
	env.Var(&port, "PORT", "8989", "Port that should be binded")
	env.Var(&logging, "LOG", "", "If not empty, log output will be written to STDOUT")
	env.Var(&dir, "DIR", ".", "Directory that should be served")
}

func main() {
	env.Parse("S")
	log.Println("Listening on port", port)
	log.Fatal(http.ListenAndServe(":"+port, Log(http.FileServer(http.Dir(dir)))))
}
<|endoftext|>


<commit_before>// Package clac implements an RPN calculator.
package clac

import (
	"errors"

	"robpike.io/ivy/config"
	"robpike.io/ivy/value"
)

var (
	zero       value.Value = value.Int(0)
	E, Pi, Phi value.Value

	errTooFewArgs    = errors.New("too few arguments")
	errInvalidArg    = errors.New("invalid argument")
	errOutOfRange    = errors.New("argument out of range")
	errNoMoreChanges = errors.New("no more changes")
	errNoHistUpdate  = errors.New("") // for cmds that don't add to history

	ivyCfg = &config.Config{}
)

func init() {
	value.SetConfig(ivyCfg)
	E, Pi = value.Consts()
	e := &eval{}
	Phi = e.binary(e.binary(value.Int(1), "+", e.unary("sqrt", value.Int(5))), "/", value.Int(2))
}

// SetFormat sets the ivy output format
func SetFormat(format string) {
	ivyCfg.SetFormat(format)
}

// ParseNum wraps value.Parse() to handle panics on unexpected input
func ParseNum(tok string) (val value.Value, err error) {
	defer func() {
		if recover() != nil {
			err = errInvalidArg
		}
	}()
	return value.Parse(tok)
}

// Stack represents a stack of floating point numbers.
type Stack []value.Value

type stackHist struct {
	cur  int
	hist []Stack
}

func newStackHist() *stackHist {
	return &stackHist{hist: []Stack{Stack{}}}
}

func (s *stackHist) undo() bool {
	if s.cur <= 0 {
		return false
	}
	s.cur--
	return true
}

func (s *stackHist) redo() bool {
	if s.cur >= len(s.hist)-1 {
		return false
	}
	s.cur++
	return true
}

func (s *stackHist) push(stack Stack) {
	s.hist = append(s.hist[:s.cur+1], stack)
	s.cur++
}

func (s *stackHist) stack() Stack {
	return s.hist[s.cur]
}

// Clac represents an RPN calculator.
type Clac struct {
	working Stack
	hist    *stackHist
}

// New returns an initialized Clac instance.
func New() *Clac {
	c := &Clac{}
	c.Reset()
	return c
}

// Reset resets clac to its initial state
func (c *Clac) Reset() error {
	c.working = Stack{}
	c.hist = newStackHist()
	return errNoHistUpdate
}

// Stack returns the current stack.
func (c *Clac) Stack() Stack {
	return c.working
}

// Exec executes a clac command, along with necessary bookkeeping
func (c *Clac) Exec(f func() error) error {
	c.updateWorking()
	err := f()
	if err == nil {
		c.hist.push(c.working)
	}
	c.updateWorking()
	if err == errNoHistUpdate {
		return nil
	}
	return err
}

func (c *Clac) updateWorking() {
	c.working = append(Stack{}, c.hist.stack()...)
}

func (c *Clac) checkRange(pos, num int, isEndOK bool) (int, int, error) {
	max := len(c.working)
	if isEndOK {
		max++
	}
	start, end := pos, pos+num-1
	if start < 0 || start > end {
		return 0, 0, errOutOfRange
	}
	if start >= max || end >= max {
		return 0, 0, errOutOfRange
	}
	return start, end, nil
}

func (c *Clac) insert(vals []value.Value, pos int) error {
	idx, _, err := c.checkRange(pos, 1, true)
	if err != nil {
		return err
	}
	c.working = append(c.working[:idx], append(vals, c.working[idx:]...)...)
	return nil
}

func (c *Clac) push(x value.Value) error {
	return c.insert([]value.Value{x}, 0)
}

func (c *Clac) remove(pos, num int) ([]value.Value, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	vals := append([]value.Value{}, c.working[start:end+1]...)
	c.working = append(c.working[:start], c.working[end+1:]...)
	return vals, nil
}

func (c *Clac) pop() (value.Value, error) {
	x, err := c.remove(0, 1)
	if err != nil {
		return zero, errTooFewArgs
	}
	return x[0], err
}

// Trunc returns the given value rounded to the nearest integer toward 0
func Trunc(val value.Value) (value.Value, error) {
	e := &eval{}
	if isTrue(e.binary(val, ">=", zero)) {
		val = e.unary("floor", val)
	} else {
		val = e.unary("ceil", val)
	}
	return val, e.err
}

func (c *Clac) popIntMin(min int) (int, error) {
	val, err := c.pop()
	if err != nil {
		return 0, err
	}
	n, err := valToInt(val)
	if err != nil {
		return 0, err
	}
	if n < min {
		return 0, errOutOfRange
	}
	return n, nil
}

func valToInt(val value.Value) (int, error) {
	val, err := Trunc(val)
	if err != nil {
		return 0, err
	}
	ival, ok := val.(value.Int)
	if !ok {
		return 0, errInvalidArg
	}
	return int(ival), nil
}

func (c *Clac) popIndex() (int, error) {
	return c.popIntMin(0)
}

func (c *Clac) popCount() (int, error) {
	return c.popIntMin(1)
}

func (c *Clac) vals(pos, num int) (Stack, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	return c.working[start : end+1], nil
}

func (c *Clac) dup(pos, num int) error {
	vals, err := c.vals(pos, num)
	if err != nil {
		return err
	}
	return c.insert(vals, 0)
}

func (c *Clac) drop(pos, num int) error {
	_, err := c.remove(pos, num)
	return err
}

func (c *Clac) rotate(pos, num int, isDown bool) error {
	from, to := 0, pos
	if isDown {
		from, to = to, from
	}
	vals, err := c.remove(from, num)
	if err != nil {
		return err
	}
	return c.insert(vals, to)
}

func unary(op string, a value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = value.Unary(op, a)
	return val, err
}

func binary(a value.Value, op string, b value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = value.Binary(a, op, b)
	return val, err
}

func errVal(val interface{}) error {
	if val == nil {
		return nil
	}
	if err, ok := val.(error); ok {
		return err
	}
	return errors.New("unknown error")
}

type eval struct {
	err error
}

func (e *eval) e(f func() (value.Value, error)) value.Value {
	if e.err != nil {
		return zero
	}
	var val value.Value
	val, e.err = f()
	return val
}

func (e *eval) unary(op string, a value.Value) value.Value {
	return e.e(func() (value.Value, error) { return unary(op, a) })
}

func (e *eval) binary(a value.Value, op string, b value.Value) value.Value {
	return e.e(func() (value.Value, error) { return binary(a, op, b) })
}
<commit_msg>Add value.Context required by ivy change.<commit_after>// Package clac implements an RPN calculator.
package clac

import (
	"errors"

	"robpike.io/ivy/config"
	"robpike.io/ivy/value"
)

var (
	zero       value.Value = value.Int(0)
	E, Pi, Phi value.Value

	errTooFewArgs    = errors.New("too few arguments")
	errInvalidArg    = errors.New("invalid argument")
	errOutOfRange    = errors.New("argument out of range")
	errNoMoreChanges = errors.New("no more changes")
	errNoHistUpdate  = errors.New("") // for cmds that don't add to history

	ivyCfg = &config.Config{}
	ivyCtx value.Context
)

func init() {
	value.SetConfig(ivyCfg)
	E, Pi = value.Consts()
	e := &eval{}
	Phi = e.binary(e.binary(value.Int(1), "+", e.unary("sqrt", value.Int(5))), "/", value.Int(2))
}

// SetFormat sets the ivy output format
func SetFormat(format string) {
	ivyCfg.SetFormat(format)
}

// ParseNum wraps value.Parse() to handle panics on unexpected input
func ParseNum(tok string) (val value.Value, err error) {
	defer func() {
		if recover() != nil {
			err = errInvalidArg
		}
	}()
	return value.Parse(tok)
}

// Stack represents a stack of floating point numbers.
type Stack []value.Value

type stackHist struct {
	cur  int
	hist []Stack
}

func newStackHist() *stackHist {
	return &stackHist{hist: []Stack{Stack{}}}
}

func (s *stackHist) undo() bool {
	if s.cur <= 0 {
		return false
	}
	s.cur--
	return true
}

func (s *stackHist) redo() bool {
	if s.cur >= len(s.hist)-1 {
		return false
	}
	s.cur++
	return true
}

func (s *stackHist) push(stack Stack) {
	s.hist = append(s.hist[:s.cur+1], stack)
	s.cur++
}

func (s *stackHist) stack() Stack {
	return s.hist[s.cur]
}

// Clac represents an RPN calculator.
type Clac struct {
	working Stack
	hist    *stackHist
}

// New returns an initialized Clac instance.
func New() *Clac {
	c := &Clac{}
	c.Reset()
	return c
}

// Reset resets clac to its initial state
func (c *Clac) Reset() error {
	c.working = Stack{}
	c.hist = newStackHist()
	return errNoHistUpdate
}

// Stack returns the current stack.
func (c *Clac) Stack() Stack {
	return c.working
}

// Exec executes a clac command, along with necessary bookkeeping
func (c *Clac) Exec(f func() error) error {
	c.updateWorking()
	err := f()
	if err == nil {
		c.hist.push(c.working)
	}
	c.updateWorking()
	if err == errNoHistUpdate {
		return nil
	}
	return err
}

func (c *Clac) updateWorking() {
	c.working = append(Stack{}, c.hist.stack()...)
}

func (c *Clac) checkRange(pos, num int, isEndOK bool) (int, int, error) {
	max := len(c.working)
	if isEndOK {
		max++
	}
	start, end := pos, pos+num-1
	if start < 0 || start > end {
		return 0, 0, errOutOfRange
	}
	if start >= max || end >= max {
		return 0, 0, errOutOfRange
	}
	return start, end, nil
}

func (c *Clac) insert(vals []value.Value, pos int) error {
	idx, _, err := c.checkRange(pos, 1, true)
	if err != nil {
		return err
	}
	c.working = append(c.working[:idx], append(vals, c.working[idx:]...)...)
	return nil
}

func (c *Clac) push(x value.Value) error {
	return c.insert([]value.Value{x}, 0)
}

func (c *Clac) remove(pos, num int) ([]value.Value, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	vals := append([]value.Value{}, c.working[start:end+1]...)
	c.working = append(c.working[:start], c.working[end+1:]...)
	return vals, nil
}

func (c *Clac) pop() (value.Value, error) {
	x, err := c.remove(0, 1)
	if err != nil {
		return zero, errTooFewArgs
	}
	return x[0], err
}

// Trunc returns the given value rounded to the nearest integer toward 0
func Trunc(val value.Value) (value.Value, error) {
	e := &eval{}
	if isTrue(e.binary(val, ">=", zero)) {
		val = e.unary("floor", val)
	} else {
		val = e.unary("ceil", val)
	}
	return val, e.err
}

func (c *Clac) popIntMin(min int) (int, error) {
	val, err := c.pop()
	if err != nil {
		return 0, err
	}
	n, err := valToInt(val)
	if err != nil {
		return 0, err
	}
	if n < min {
		return 0, errOutOfRange
	}
	return n, nil
}

func valToInt(val value.Value) (int, error) {
	val, err := Trunc(val)
	if err != nil {
		return 0, err
	}
	ival, ok := val.(value.Int)
	if !ok {
		return 0, errInvalidArg
	}
	return int(ival), nil
}

func (c *Clac) popIndex() (int, error) {
	return c.popIntMin(0)
}

func (c *Clac) popCount() (int, error) {
	return c.popIntMin(1)
}

func (c *Clac) vals(pos, num int) (Stack, error) {
	start, end, err := c.checkRange(pos, num, false)
	if err != nil {
		return nil, err
	}
	return c.working[start : end+1], nil
}

func (c *Clac) dup(pos, num int) error {
	vals, err := c.vals(pos, num)
	if err != nil {
		return err
	}
	return c.insert(vals, 0)
}

func (c *Clac) drop(pos, num int) error {
	_, err := c.remove(pos, num)
	return err
}

func (c *Clac) rotate(pos, num int, isDown bool) error {
	from, to := 0, pos
	if isDown {
		from, to = to, from
	}
	vals, err := c.remove(from, num)
	if err != nil {
		return err
	}
	return c.insert(vals, to)
}

func unary(op string, a value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = value.Unary(ivyCtx, op, a)
	return val, err
}

func binary(a value.Value, op string, b value.Value) (val value.Value, err error) {
	defer func() { err = errVal(recover()) }()
	val = value.Binary(ivyCtx, a, op, b)
	return val, err
}

func errVal(val interface{}) error {
	if val == nil {
		return nil
	}
	if err, ok := val.(error); ok {
		return err
	}
	return errors.New("unknown error")
}

type eval struct {
	err error
}

func (e *eval) e(f func() (value.Value, error)) value.Value {
	if e.err != nil {
		return zero
	}
	var val value.Value
	val, e.err = f()
	return val
}

func (e *eval) unary(op string, a value.Value) value.Value {
	return e.e(func() (value.Value, error) { return unary(op, a) })
}

func (e *eval) binary(a value.Value, op string, b value.Value) value.Value {
	return e.e(func() (value.Value, error) { return binary(a, op, b) })
}
<|endoftext|>


<commit_before>// CLD2

package main

import (
	"flag"
	"fmt"
	// "net"
	"os"
	"strconv"
)

const TAG = "cld2"

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: cld2 [-h host] [-p port]\n")
}

func main() {
	// if len(os.Args) > 1 {
	// 	fmt.Fprintf(os.Stderr, "Usage: cld2\n");
	// 	os.Exit(1)
	// }

	var listenhost string
	var listenport int
	var _listenport string

	flag.StringVar(&listenhost, "h", "", "Hostname to bind for listening")
	flag.StringVar(&_listenport, "p", "8081", "Port to bind for listening")

	flag.Usage = usage
	flag.Parse()

	if _listenport == "auto" {
		listenport = 0
	} else {
		var err error
		listenport, err = strconv.Atoi(_listenport)
		if err != nil {
			fmt.Fprintf(os.Stderr,
			    "%s: Argument for -p is invalid\n", TAG)
			os.Exit(2)
		}
		if listenport < 1 || listenport > 65535 {
			fmt.Fprintf(os.Stderr,
			    "%s: Port number %d is out of range\n",
			    TAG, listenport)
			os.Exit(2)
		}
	}

	fmt.Printf("host %s port %d\n", listenhost, listenport) // P3
}

/*
 * This existential horror comes from practices like these:
 *
 * #define CLD_PKT_MAGIC "CLDc1pkt"
 * struct cld_pkt_hdr {
 *     quad_t magic;
 * } pkt;
 * memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
 *
 * In C, this produces different results on little endian and big endian hosts.
 * In other words, the author employed a platform-independent format, XDR,
 * then defeated it by coercing strings into ints.
 */
func strtole8(dst *int64, src string) {
	var a int64 = 0
	for i := 0; i < 8; i++ {
		a |= (int64(src[i]) << uint(i*8))
	}
	*dst = a
}

func le8tostr(v int64) string {
	var a8 [8]byte
	for i := 0; i < 8; i++ {
		a8[i] = byte(v >> uint(i*8))
	}
	return string(a8[0:8])
}
<commit_msg>Listen on a port<commit_after>// CLD2

package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"strconv"
)

const TAG = "cld2"

func usage() {
	fmt.Fprintf(os.Stderr, "Usage: cld2 [-h host] [-p port]\n")
}

func main() {
	// if len(os.Args) > 1 {
	// 	fmt.Fprintf(os.Stderr, "Usage: cld2\n");
	// 	os.Exit(1)
	// }

	var listenhost string
	var listenport int
	var _listenport string

	flag.StringVar(&listenhost, "h", "", "Hostname to bind for listening")
	flag.StringVar(&_listenport, "p", "8081", "Port to bind for listening")

	flag.Usage = usage
	flag.Parse()

	if _listenport == "auto" {
		listenport = 0
	} else {
		var err error
		listenport, err = strconv.Atoi(_listenport)
		if err != nil {
			fmt.Fprintf(os.Stderr,
			    "%s: Argument for -p is invalid\n", TAG)
			os.Exit(2)
		}
		if listenport < 1 || listenport > 65535 {
			fmt.Fprintf(os.Stderr,
			    "%s: Port number %d is out of range\n",
			    TAG, listenport)
			os.Exit(2)
		}
	}

	fmt.Printf("host %s port %d\n", listenhost, listenport) // P3

	listen_netloc := net.JoinHostPort(listenhost, strconv.Itoa(listenport))
	ln, err := net.Listen("tcp", listen_netloc)
	if err != nil {
		fmt.Fprintf(os.Stderr,
		    "%s: Listen(%s) error: %s\n",
		    TAG, listen_netloc, err.Error())
		os.Exit(1)
	}
	listen_addr := ln.Addr() // Addr XXX
	if listenport == 0 {
		// XXX write this to a file
		// XXX No way to get just port except parsing the string
		fmt.Printf("listening on addr %s\n", listen_addr.String())
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Fprintf(os.Stderr,
			    "%s: Accept error: %s\n", TAG, err.Error())
			os.Exit(1)
		}

		fmt.Printf("connection\n") // P3
		conn.Close()
	}
}

/*
 * This existential horror comes from practices like these:
 *
 * #define CLD_PKT_MAGIC "CLDc1pkt"
 * struct cld_pkt_hdr {
 *     quad_t magic;
 * } pkt;
 * memcpy(&pkt.magic, CLD_PKT_MAGIC, sizeof(pkt.magic));
 *
 * In C, this produces different results on little endian and big endian hosts.
 * In other words, the author employed a platform-independent format, XDR,
 * then defeated it by coercing strings into ints.
 */
func strtole8(dst *int64, src string) {
	var a int64 = 0
	for i := 0; i < 8; i++ {
		a |= (int64(src[i]) << uint(i*8))
	}
	*dst = a
}

func le8tostr(v int64) string {
	var a8 [8]byte
	for i := 0; i < 8; i++ {
		a8[i] = byte(v >> uint(i*8))
	}
	return string(a8[0:8])
}
<|endoftext|>


<commit_before>package espsdk

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"

	log "github.com/Sirupsen/logrus"
)

const (
	endpoint      = "https://esp-sandbox.api.gettyimages.com/esp"
	oauthEndpoint = "https://api.gettyimages.com/oauth2/token"
)

type Token string

type Credentials struct {
	APIKey      string
	APISecret   string
	ESPUsername string
	ESPPassword string
}

func (c *Credentials) areInvalid() bool {
	if len(c.APIKey) < 1 || len(c.APISecret) < 1 || len(c.ESPUsername) < 1 || len(c.ESPPassword) < 1 {
		return true
	}
	return false
}

func (c *Credentials) formValues() url.Values {
	v := url.Values{}
	v.Set("client_id", c.APIKey)
	v.Set("client_secret", c.APISecret)
	v.Set("username", c.ESPUsername)
	v.Set("password", c.ESPPassword)
	v.Set("grant_type", "password")
	return v
}

type Client struct {
	Credentials
	UploadBucket string
}

func (c Client) GetToken() Token {
	if c.Credentials.areInvalid() {
		log.Fatal("Not all required credentials were supplied.")
	}

	uri := oauthEndpoint
	log.Debugf("%s", uri)
	formValues := c.formValues()
	log.Debugf("%s", formValues.Encode())

	resp, err := http.PostForm(uri, formValues)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	payload, err := ioutil.ReadAll(resp.Body)
	log.Debugf("HTTP %d", resp.StatusCode)
	log.Debugf("%s", payload)
	return c.tokenFrom(payload)
}

func (c Client) tokenFrom(payload []byte) Token {
	var response map[string]string
	json.Unmarshal(payload, &response)
	return Token(response["access_token"])
}

// request performs a request using the provided HTTP verb and returns
// the response as a JSON payload. If the verb is POST, the optional
// serialized object will become the body of the HTTP request.
func (c Client) Request(verb string, path string, token Token, object []byte) ([]byte, error) {
	uri := endpoint + path
	log.Debug(uri)

	if (verb == "POST" || verb == "PUT") && object != nil {
		log.Debugf("Received serialized object: %s", object)
	}
	req, err := http.NewRequest(verb, uri, bytes.NewBuffer(object))
	httpClient := insecureClient()

	payload, err := getJSON(httpClient, req, token, c.APIKey)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	return payload, nil
}

// Private

func getJSON(c *http.Client, req *http.Request, token Token, apiKey string) ([]byte, error) {
	req.Header.Set("Authorization", "Token token="+string(token))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Api-Key", apiKey)

	resp, err := c.Do(req)
	defer resp.Body.Close()
	if err != nil {
		log.Fatal(err)
		return nil, err
	}

	payload, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Infof("HTTP %s", resp.Status)
	return payload, nil
}

// insecureClient returns an HTTP client that will not verify the validity
// of an SSL certificate when performing a request.
func insecureClient() *http.Client {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return &http.Client{Transport: tr}
}
<commit_msg>fix runtime bug<commit_after>package espsdk

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"io/ioutil"
	"net/http"
	"net/url"

	log "github.com/Sirupsen/logrus"
)

const (
	endpoint      = "https://esp-sandbox.api.gettyimages.com/esp"
	oauthEndpoint = "https://api.gettyimages.com/oauth2/token"
)

type Token string

type Credentials struct {
	APIKey      string
	APISecret   string
	ESPUsername string
	ESPPassword string
}

func (c *Credentials) areInvalid() bool {
	if len(c.APIKey) < 1 || len(c.APISecret) < 1 || len(c.ESPUsername) < 1 || len(c.ESPPassword) < 1 {
		return true
	}
	return false
}

func (c *Credentials) formValues() url.Values {
	v := url.Values{}
	v.Set("client_id", c.APIKey)
	v.Set("client_secret", c.APISecret)
	v.Set("username", c.ESPUsername)
	v.Set("password", c.ESPPassword)
	v.Set("grant_type", "password")
	return v
}

type Client struct {
	Credentials
	UploadBucket string
}

func (c Client) GetToken() Token {
	if c.Credentials.areInvalid() {
		log.Fatal("Not all required credentials were supplied.")
	}

	uri := oauthEndpoint
	log.Debugf("%s", uri)
	formValues := c.formValues()
	log.Debugf("%s", formValues.Encode())

	resp, err := http.PostForm(uri, formValues)
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	payload, err := ioutil.ReadAll(resp.Body)
	log.Debugf("HTTP %d", resp.StatusCode)
	log.Debugf("%s", payload)
	return c.tokenFrom(payload)
}

func (c Client) tokenFrom(payload []byte) Token {
	var response map[string]string
	json.Unmarshal(payload, &response)
	return Token(response["access_token"])
}

// request performs a request using the provided HTTP verb and returns
// the response as a JSON payload. If the verb is POST, the optional
// serialized object will become the body of the HTTP request.
func (c Client) Request(verb string, path string, token Token, object []byte) ([]byte, error) {
	uri := endpoint + path
	log.Debug(uri)

	if (verb == "POST" || verb == "PUT") && object != nil {
		log.Debugf("Received serialized object: %s", object)
	}
	req, err := http.NewRequest(verb, uri, bytes.NewBuffer(object))
	httpClient := insecureClient()

	payload, err := getJSON(httpClient, req, token, c.APIKey)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	return payload, nil
}

// Private

func getJSON(c *http.Client, req *http.Request, token Token, apiKey string) ([]byte, error) {
	req.Header.Set("Authorization", "Token token="+string(token))
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Api-Key", apiKey)

	resp, err := c.Do(req)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	defer resp.Body.Close()

	payload, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
		return nil, err
	}
	log.Infof("HTTP %s", resp.Status)
	return payload, nil
}

// insecureClient returns an HTTP client that will not verify the validity
// of an SSL certificate when performing a request.
func insecureClient() *http.Client {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	return &http.Client{Transport: tr}
}
<|endoftext|>


<commit_before>package pickett

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"strings"

	pickett_io "github.com/igneous-systems/pickett/io"
)

type Source struct {
	Tag       string
	Directory string
	DependsOn []string
}

type CodeVolume struct {
	Directory string
	MountedAt string
}

type GoBuild struct {
	RunIn                    string
	Tag                      string
	InstallGoPackages        []string
	InstallAndTestGoPackages []string
}

type GenericBuild struct {
	RunIn string
	Tag   string
	Run   []string
}

type ArtifactBuild struct {
	RunIn     string
	MergeWith string
	Tag       string
	Artifacts map[string]interface{}
}

type Config struct {
	DockerBuildOptions []string
	CodeVolume         CodeVolume
	Sources            []*Source
	GoBuilds           []*GoBuild
	ArtifactBuilds     []*ArtifactBuild
	GenericBuilds      []*GenericBuild
	nameToNode         map[string]Node
}

// NewCofingFile creates a new instance of configuration, including
// all the parsing of the config file and validation checking on the
// items therein.
func NewConfig(reader io.Reader, helper pickett_io.IOHelper) (*Config, error) {
	all, err := ioutil.ReadAll(reader)
	helper.CheckFatal(err, "could not read all of configuration file: %v")
	lines := strings.Split(string(all), "\n")
	var noComments bytes.Buffer
	for _, line := range lines {
		if index := strings.Index(line, "//"); index != -1 {
			if index == 0 {
				continue
			}
			line = line[:index]
		}
		noComments.WriteString(line)
	}

	//try to decode the json blob
	dec := json.NewDecoder(&noComments)
	conf := &Config{}
	err = dec.Decode(&conf)
	if err != nil {
		return nil, err
	}
	conf.nameToNode = make(map[string]Node)
	if err := conf.sourceNodes(helper); err != nil {
		return nil, err
	}
	if _, err := conf.goBuildNodes(); err != nil {
		return nil, err
	}
	if _, err := conf.artifactBuildNodes(); err != nil {
		return nil, err
	}
	return conf, nil
}

// sourceNodes walks all the nodes defined in the configuration file
// and returns them in a list.  The edges between the nodes are already
// in place when this function completes.
func (c *Config) sourceNodes(helper pickett_io.IOHelper) error {
	for _, img := range c.Sources {
		w, err := c.newSourceWorker(img, helper)
		if err != nil {
			return err
		}
		node := newNodeImpl(img.Tag, w)
		c.nameToNode[img.Tag] = node
	}
	//make a pass adding edges
	for _, img := range c.Sources {
		dest := c.nameToNode[img.Tag]
		work := dest.Worker().(*sourceWorker)
		for _, source := range img.DependsOn {
			node_source, ok := c.nameToNode[source]
			if !ok {
				return fmt.Errorf("image %s depends on %s, but %s not found",
					img.Tag, source, source)
			}
			node_source.AddOut(dest)
			work.inEdges = append(work.inEdges, node_source)
		}
	}
	return nil
}

// Sinks() return a list of the names of sinks you might want to build.
func (c *Config) Sinks() []string {
	result := []string{}
	for _, v := range c.nameToNode {
		if !v.IsSink() {
			continue
		}
		result = append(result, v.Name())
	}
	return result
}

// goBuildNodes returns all the go build nodes in the pickett file.  Note that
// this should not be called until after the dockerSourceNodes() have been
// extracted as it needs data structures built at that stage.
func (c *Config) goBuildNodes() ([]Node, error) {
	var result []Node
	for _, build := range c.GoBuilds {
		w, err := c.newGoWorker(build)
		if err != nil {
			return nil, err
		}
		r, found := c.nameToNode[build.RunIn]
		if !found {
			return nil, fmt.Errorf("Unable to find %s trying to build %s",
				build.RunIn, build.Tag)
		}

		//add edges
		w.runIn = r
		node := newNodeImpl(build.Tag, w)
		r.AddOut(node)

		//compute result
		result = append(result, node)
		c.nameToNode[build.Tag] = node
	}
	return result, nil
}

// artifactBuildNodes returns all theartifact build nodes in the pickett file.  Note that
// this should not be called until after the dockerSourceNodes() and goBuildNodes() have been
// extracted as it needs data structures built at that stage.
func (c *Config) artifactBuildNodes() ([]Node, error) {
	var result []Node
	for _, build := range c.ArtifactBuilds {
		w, err := c.newArtifactWorker(build)
		if err != nil {
			return nil, err
		}
		r, found := c.nameToNode[build.RunIn]
		if !found {
			return nil, fmt.Errorf("Unable to find '%s' trying to build RunIn '%s'",
				build.RunIn, build.Tag)
		}
		m, found := c.nameToNode[build.MergeWith]
		if !found {
			return nil, fmt.Errorf("Unable to find '%s' trying to build MergeWith of  '%s'",
				build.MergeWith, build.Tag)
		}
		// handle dependency edges
		if _, ok := r.Worker().(*goWorker); !ok {
			return nil, fmt.Errorf("Unable to create %s, right now artifacts must be derived from GoBuild nodes (%s is not GoBuild)",
				build.Tag, r.Name())
		}
		w.runIn = r
		w.mergeWith = m
		node := newNodeImpl(build.Tag, w)
		r.AddOut(node)
		m.AddOut(node)
		// now build the node and put it in the map + result
		result = append(result, node)
		c.nameToNode[build.Tag] = node
	}
	return result, nil
}

// newSourceWorker returns a sourceWorker from the configuration information
// provided in the pickett file.  Note that this does some sanity checking of
// the provided directory so this can fail.  It uses the path to the
// Pickett.json file to construct paths such that the directory is relative
// to the place where the Pickett.json is located.  This ignores the issue
// of edges.
func (c *Config) newSourceWorker(src *Source, helper pickett_io.IOHelper) (*sourceWorker, error) {
	node := &sourceWorker{
		tag: src.Tag,
		dir: src.Directory,
	}
	_, err := helper.OpenDockerfileRelative(src.Directory)
	if err != nil {
		return nil, fmt.Errorf("looked for %s/Dockerfile: %v",
			helper.DirectoryRelative(src.Directory), err)
	}
	return node, nil
}

// newGoWorker returns a goWorker from the configuration information
// provided in the pickett file. This sanity checks the config file, so it can
// fail.  It ignores dependency edges.
func (c *Config) newGoWorker(build *GoBuild) (*goWorker, error) {
	result := &goWorker{
		tag: build.Tag,
	}
	if len(build.InstallGoPackages) != 0 && len(build.InstallAndTestGoPackages) != 0 {
		return nil, fmt.Errorf("%s must define only one of InstallGoPackages and InstallAndTestGoPackages", build.Tag)
	}
	if len(build.InstallGoPackages) != 0 {
		result.pkgs = build.InstallGoPackages
		result.test = false
	}
	if len(build.InstallAndTestGoPackages) != 0 {
		result.test = true
		result.pkgs = build.InstallAndTestGoPackages
	}
	return result, nil
}

// newArtifactWorker returns a worker from the configuration information
// provided in the pickett file. This sanity checks the config file, so it can
// fail. It ignores dependency edges.
func (c *Config) newArtifactWorker(build *ArtifactBuild) (*artifactWorker, error) {
	if len(build.Artifacts) == 0 {
		return nil, fmt.Errorf("%s must define at least one artifact", build.Tag)
	}
	art := make(map[string]string)
	for k, v := range build.Artifacts {
		s, ok := v.(string)
		if !ok {
			return nil, fmt.Errorf("%v must be a string (in artifacts of %s)!", v, build.Tag)
		}
		art[k] = s
	}
	worker := &artifactWorker{
		artifacts: art,
		tag:       build.Tag,
	}
	return worker, nil
}

// Initiate does the work of running from creation to a particular tag being "born".
// Called by the "main()" of the pickett program if you provide a "target".
func (c *Config) Initiate(name string, helper pickett_io.IOHelper, cli pickett_io.DockerCli) error {
	node, isPresent := c.nameToNode[name]
	if !isPresent {
		return fmt.Errorf("no such target: %s", name)
	}
	return node.Build(c, helper, cli)
}
<commit_msg>undo last commit<commit_after>package pickett

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"strings"

	pickett_io "github.com/igneous-systems/pickett/io"
)

type Source struct {
	Tag       string
	Directory string
	DependsOn []string
}

type CodeVolume struct {
	Directory string
	MountedAt string
}

type GoBuild struct {
	RunIn                    string
	Tag                      string
	InstallGoPackages        []string
	InstallAndTestGoPackages []string
}

type GenericBuild struct {
	RunIn string
	Tag   string
	Run   []string
}

type ArtifactBuild struct {
	RunIn     string
	MergeWith string
	Tag       string
	Artifacts map[string]interface{}
}

type Config struct {
	DockerBuildOptions []string
	CodeVolume         CodeVolume
	Sources            []*Source
	GoBuilds           []*GoBuild
	ArtifactBuilds     []*ArtifactBuild
	GenericBuilds      []*GenericBuild
	nameToNode         map[string]Node
}

// NewCofingFile creates a new instance of configuration, including
// all the parsing of the config file and validation checking on the
// items therein.
func NewConfig(reader io.Reader, helper pickett_io.IOHelper) (*Config, error) {
	all, err := ioutil.ReadAll(reader)
	helper.CheckFatal(err, "could not read all of configuration file: %v")
	lines := strings.Split(string(all), "\n")
	var noComments bytes.Buffer
	for _, line := range lines {
		if index := strings.Index(line, "//"); index != -1 {
			if index == 0 {
				continue
			}
			line = line[:index]
		}
		noComments.WriteString(line)
	}

	//try to decode the json blob
	dec := json.NewDecoder(&noComments)
	conf := &Config{}
	err = dec.Decode(&conf)
	if err != nil {
		return nil, err
	}
	conf.nameToNode = make(map[string]Node)
	if err := conf.sourceNodes(helper); err != nil {
		return nil, err
	}
	if _, err := conf.goBuildNodes(); err != nil {
		return nil, err
	}
	if _, err := conf.artifactBuildNodes(); err != nil {
		return nil, err
	}
	return conf, nil
}

// sourceNodes walks all the nodes defined in the configuration file
// and returns them in a list.  The edges between the nodes are already
// in place when this function completes.
func (c *Config) sourceNodes(helper pickett_io.IOHelper) error {
	for _, img := range c.Sources {
		w, err := c.newSourceWorker(img, helper)
		if err != nil {
			return err
		}
		node := newNodeImpl(img.Tag, w)
		c.nameToNode[img.Tag] = node
	}
	//make a pass adding edges
	for _, img := range c.Sources {
		dest := c.nameToNode[img.Tag]
		work := dest.Worker().(*sourceWorker)
		for _, source := range img.DependsOn {
			node_source, ok := c.nameToNode[source]
			if !ok {
				return errors.New(fmt.Sprintf("image %s depends on %s, but %s not found",
					img.Tag, source, source))
			}
			node_source.AddOut(dest)
			work.inEdges = append(work.inEdges, node_source)
		}
	}
	return nil
}

// Sinks() return a list of the names of sinks you might want to build.
func (c *Config) Sinks() []string {
	result := []string{}
	for _, v := range c.nameToNode {
		if !v.IsSink() {
			continue
		}
		result = append(result, v.Name())
	}
	return result
}

// goBuildNodes returns all the go build nodes in the pickett file.  Note that
// this should not be called until after the dockerSourceNodes() have been
// extracted as it needs data structures built at that stage.
func (c *Config) goBuildNodes() ([]Node, error) {
	var result []Node
	for _, build := range c.GoBuilds {
		w, err := c.newGoWorker(build)
		if err != nil {
			return nil, err
		}
		r, found := c.nameToNode[build.RunIn]
		if !found {
			return nil, errors.New(fmt.Sprintf("Unable to find %s trying to build %s",
				build.RunIn, build.Tag))
		}

		//add edges
		w.runIn = r
		node := newNodeImpl(build.Tag, w)
		r.AddOut(node)

		//compute result
		result = append(result, node)
		c.nameToNode[build.Tag] = node
	}
	return result, nil
}

// artifactBuildNodes returns all theartifact build nodes in the pickett file.  Note that
// this should not be called until after the dockerSourceNodes() and goBuildNodes() have been
// extracted as it needs data structures built at that stage.
func (c *Config) artifactBuildNodes() ([]Node, error) {
	var result []Node
	for _, build := range c.ArtifactBuilds {
		w, err := c.newArtifactWorker(build)
		if err != nil {
			return nil, err
		}
		r, found := c.nameToNode[build.RunIn]
		if !found {
			return nil, errors.New(fmt.Sprintf("Unable to find '%s' trying to build RunIn '%s'",
				build.RunIn, build.Tag))
		}
		m, found := c.nameToNode[build.MergeWith]
		if !found {
			return nil, errors.New(fmt.Sprintf("Unable to find '%s' trying to build MergeWith of  '%s'",
				build.MergeWith, build.Tag))
		}
		// handle dependency edges
		if _, ok := r.Worker().(*goWorker); !ok {
			return nil, errors.New(fmt.Sprintf("Unable to create %s, right now artifacts must be derived from GoBuild nodes (%s is not GoBuild)",
				build.Tag, r.Name()))
		}
		w.runIn = r
		w.mergeWith = m
		node := newNodeImpl(build.Tag, w)
		r.AddOut(node)
		m.AddOut(node)
		// now build the node and put it in the map + result
		result = append(result, node)
		c.nameToNode[build.Tag] = node
	}
	return result, nil
}

// newSourceWorker returns a sourceWorker from the configuration information
// provided in the pickett file.  Note that this does some sanity checking of
// the provided directory so this can fail.  It uses the path to the
// Pickett.json file to construct paths such that the directory is relative
// to the place where the Pickett.json is located.  This ignores the issue
// of edges.
func (c *Config) newSourceWorker(src *Source, helper pickett_io.IOHelper) (*sourceWorker, error) {
	node := &sourceWorker{
		tag: src.Tag,
		dir: src.Directory,
	}
	_, err := helper.OpenDockerfileRelative(src.Directory)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("looked for %s/Dockerfile: %v",
			helper.DirectoryRelative(src.Directory), err))
	}
	return node, nil
}

// newGoWorker returns a goWorker from the configuration information
// provided in the pickett file. This sanity checks the config file, so it can
// fail.  It ignores dependency edges.
func (c *Config) newGoWorker(build *GoBuild) (*goWorker, error) {
	result := &goWorker{
		tag: build.Tag,
	}
	if len(build.InstallGoPackages) != 0 && len(build.InstallAndTestGoPackages) != 0 {
		return nil, errors.New(fmt.Sprintf("%s must define only one of InstallGoPackages and InstallAndTestGoPackages", build.Tag))
	}
	if len(build.InstallGoPackages) != 0 {
		result.pkgs = build.InstallGoPackages
		result.test = false
	}
	if len(build.InstallAndTestGoPackages) != 0 {
		result.test = true
		result.pkgs = build.InstallAndTestGoPackages
	}
	return result, nil
}

// newArtifactWorker returns a worker from the configuration information
// provided in the pickett file. This sanity checks the config file, so it can
// fail. It ignores dependency edges.
func (c *Config) newArtifactWorker(build *ArtifactBuild) (*artifactWorker, error) {
	if len(build.Artifacts) == 0 {
		return nil, errors.New(fmt.Sprintf("%s must define at least one artifact", build.Tag))
	}
	art := make(map[string]string)
	for k, v := range build.Artifacts {
		s, ok := v.(string)
		if !ok {
			return nil, errors.New(fmt.Sprintf("%v must be a string (in artifacts of %s)!", v, build.Tag))
		}
		art[k] = s
	}
	worker := &artifactWorker{
		artifacts: art,
		tag:       build.Tag,
	}
	return worker, nil
}

// Initiate does the work of running from creation to a particular tag being "born".
// Called by the "main()" of the pickett program if you provide a "target".
func (c *Config) Initiate(name string, helper pickett_io.IOHelper, cli pickett_io.DockerCli) error {
	node, isPresent := c.nameToNode[name]
	if !isPresent {
		return errors.New(fmt.Sprintf("no such target: %s", name))
	}
	return node.Build(c, helper, cli)
}
<|endoftext|>