<commit_before>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config     *pb.Config
	serving    bool
	LastIntent time.Time
}

type mainChecker struct {
	prev []string
}

func getIP(servertype, servername string) (string, int) {
	conn, err := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	log.Printf("Error? %v", err)
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	if err != nil {
		return list, conf
	}
	defer conn.Close()

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, _ := grpc.DialContext(ctx, entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	log.Printf("SETTING MASTER: %v", entry)
	_, err := server.Mote(ctx, &pbg.MoteRequest{Master: entry.GetMaster()}, grpc.FailFast(false))
	if err != nil {
		log.Printf("RESPONSE: %v", err)
	}
}

func runJob(job *pbs.JobSpec, server string) {
	log.Printf("Run %v on %v", job.Name, server)
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(ctx, job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	log.Printf("GETTING STATE: %p", &s)
	return []*pbg.State{&pbg.State{Key: "last_intent", TimeValue: s.LastIntent.Unix()}}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s *Server) MatchIntent() {
	checker := &mainChecker{}
	log.Printf("SERVING: %v", s.serving)
	for s.serving {
		time.Sleep(intentWait)
		s.LastIntent = time.Now()
		log.Printf("SETTING %p", s)

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
	}
}

// SetMaster sets up the master settings
func (s Server) SetMaster() {
	checker := &mainChecker{}
	for s.serving {
		time.Sleep(intentWait)

		log.Printf("Running SetMaster")

		fleet := checker.discover()
		matcher := make(map[string]*pbd.RegistryEntry)
		for _, entry := range fleet.GetServices() {
			if _, ok := matcher[entry.GetName()]; !ok {
				matcher[entry.GetName()] = entry
			} else {
				if entry.GetMaster() {
					matcher[entry.GetName()] = entry
				}
			}
		}

		for _, entry := range matcher {
			if !entry.GetMaster() {
				entry.Master = true
				checker.master(entry)
			}
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	var sync = flag.Bool("once", false, "One pass intent match")
	s := &Server{&goserver.GoServer{}, config, true, time.Now()}

	var quiet = flag.Bool("quiet", true, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	if *sync {
		s.MatchIntent()
	} else {
		s.Register = s
		s.PrepServer()
		s.GoServer.Killme = false
		s.RegisterServer("gobuildmaster", false)
		s.RegisterServingTask(s.MatchIntent)
		s.RegisterServingTask(s.SetMaster)
		s.Serve()
	}
}
<commit_msg>Logs intent run time<commit_after>package main

import (
	"flag"
	"io/ioutil"
	"log"
	"strconv"
	"time"

	"github.com/brotherlogic/goserver"
	"golang.org/x/net/context"
	"google.golang.org/grpc"

	pbd "github.com/brotherlogic/discovery/proto"
	pb "github.com/brotherlogic/gobuildmaster/proto"
	pbs "github.com/brotherlogic/gobuildslave/proto"
	pbg "github.com/brotherlogic/goserver/proto"
	"github.com/brotherlogic/goserver/utils"
)

const (
	intentWait = time.Second
)

// Server the main server type
type Server struct {
	*goserver.GoServer
	config     *pb.Config
	serving    bool
	LastIntent time.Time
}

type mainChecker struct {
	prev []string
}

func getIP(servertype, servername string) (string, int) {
	conn, err := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	log.Printf("Error? %v", err)
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err != nil {
		return "", -1
	}
	for _, s := range r.Services {
		if s.Name == servertype && s.Identifier == servername {
			return s.Ip, int(s.Port)
		}
	}

	return "", -1
}

func (t *mainChecker) getprev() []string {
	return t.prev
}
func (t *mainChecker) setprev(v []string) {
	t.prev = v
}

func (t *mainChecker) assess(server string) (*pbs.JobList, *pbs.Config) {
	list := &pbs.JobList{}
	conf := &pbs.Config{}

	ip, port := getIP("gobuildslave", server)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, err := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
	if err != nil {
		return list, conf
	}
	defer conn.Close()

	slave := pbs.NewGoBuildSlaveClient(conn)
	r, err := slave.List(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	r2, err := slave.GetConfig(ctx, &pbs.Empty{}, grpc.FailFast(false))
	if err != nil {
		return list, conf
	}

	return r, r2
}

func (t *mainChecker) master(entry *pbd.RegistryEntry) {
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	conn, _ := grpc.DialContext(ctx, entry.GetIp()+":"+strconv.Itoa(int(entry.GetPort())), grpc.WithInsecure())
	defer conn.Close()

	server := pbg.NewGoserverServiceClient(conn)
	log.Printf("SETTING MASTER: %v", entry)
	_, err := server.Mote(ctx, &pbg.MoteRequest{Master: entry.GetMaster()}, grpc.FailFast(false))
	if err != nil {
		log.Printf("RESPONSE: %v", err)
	}
}

func runJob(job *pbs.JobSpec, server string) {
	log.Printf("Run %v on %v", job.Name, server)
	if server != "" {
		ip, port := getIP("gobuildslave", server)
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		defer cancel()
		conn, _ := grpc.DialContext(ctx, ip+":"+strconv.Itoa(port), grpc.WithInsecure())
		defer conn.Close()

		slave := pbs.NewGoBuildSlaveClient(conn)
		job.Server = server
		slave.Run(ctx, job, grpc.FailFast(false))
	}
}

func (t *mainChecker) discover() *pbd.ServiceList {
	ret := &pbd.ServiceList{}

	conn, _ := grpc.Dial(utils.RegistryIP+":"+strconv.Itoa(utils.RegistryPort), grpc.WithInsecure())
	defer conn.Close()

	registry := pbd.NewDiscoveryServiceClient(conn)
	ctx, cancel := context.WithTimeout(context.Background(), time.Second)
	defer cancel()
	r, err := registry.ListAllServices(ctx, &pbd.Empty{}, grpc.FailFast(false))
	if err == nil {
		for _, s := range r.Services {
			ret.Services = append(ret.Services, s)
		}
	}

	return ret
}

// DoRegister Registers this server
func (s Server) DoRegister(server *grpc.Server) {
	pb.RegisterGoBuildMasterServer(server, &s)
}

// ReportHealth determines if the server is healthy
func (s Server) ReportHealth() bool {
	return true
}

// Mote promotes/demotes this server
func (s Server) Mote(master bool) error {
	return nil
}

//GetState gets the state of the server
func (s Server) GetState() []*pbg.State {
	log.Printf("GETTING STATE: %p", &s)
	return []*pbg.State{&pbg.State{Key: "last_intent", TimeValue: s.LastIntent.Unix()}}
}

//Compare compares current state to desired state
func (s Server) Compare(ctx context.Context, in *pb.Empty) (*pb.CompareResponse, error) {
	resp := &pb.CompareResponse{}
	list, _ := getFleetStatus(&mainChecker{})
	cc := &pb.Config{}
	for _, jlist := range list {
		for _, job := range jlist.GetDetails() {
			cc.Intents = append(cc.Intents, &pb.Intent{Spec: job.GetSpec()})
		}
	}
	resp.Current = cc
	resp.Desired = s.config

	return resp, nil
}

func getConfig(c checker) *pb.Config {
	list, _ := getFleetStatus(c)
	config := &pb.Config{}

	for _, jlist := range list {
		for _, job := range jlist.Details {
			found := false
			for _, ij := range config.Intents {
				if job.Spec.Name == ij.Spec.Name {
					ij.Count++
					found = true
				}
			}

			if !found {
				config.Intents = append(config.Intents, &pb.Intent{Spec: &pbs.JobSpec{Name: job.Spec.Name}, Count: 1})
			}
		}
	}

	return config
}

// MatchIntent tries to match the intent with the state of production
func (s *Server) MatchIntent() {
	checker := &mainChecker{}
	log.Printf("SERVING: %v", s.serving)
	for s.serving {
		time.Sleep(intentWait)
		t := time.Now()
		s.LastIntent = time.Now()
		log.Printf("SETTING %p", s)

		state := getConfig(checker)
		diff := configDiff(s.config, state)
		joblist := runJobs(diff)
		for _, job := range joblist {
			runJob(job, chooseServer(job, checker))
		}
		s.LogFunction("MatchIntentRun", t)
	}
}

// SetMaster sets up the master settings
func (s Server) SetMaster() {
	checker := &mainChecker{}
	for s.serving {
		time.Sleep(intentWait)

		log.Printf("Running SetMaster")

		fleet := checker.discover()
		matcher := make(map[string]*pbd.RegistryEntry)
		for _, entry := range fleet.GetServices() {
			if _, ok := matcher[entry.GetName()]; !ok {
				matcher[entry.GetName()] = entry
			} else {
				if entry.GetMaster() {
					matcher[entry.GetName()] = entry
				}
			}
		}

		for _, entry := range matcher {
			if !entry.GetMaster() {
				entry.Master = true
				checker.master(entry)
			}
		}
	}
}

func main() {
	config, err := loadConfig("config.pb")
	if err != nil {
		log.Fatalf("Fatal loading of config: %v", err)
	}

	var sync = flag.Bool("once", false, "One pass intent match")
	s := &Server{&goserver.GoServer{}, config, true, time.Now()}

	var quiet = flag.Bool("quiet", true, "Show all output")
	flag.Parse()

	if *quiet {
		log.SetFlags(0)
		log.SetOutput(ioutil.Discard)
	}

	if *sync {
		s.MatchIntent()
	} else {
		s.Register = s
		s.PrepServer()
		s.GoServer.Killme = false
		s.RegisterServer("gobuildmaster", false)
		s.RegisterServingTask(s.MatchIntent)
		s.RegisterServingTask(s.SetMaster)
		s.Serve()
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"go/build"
	"log"
	"net/http"
	"net/http/cgi"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strconv"
	"strings"
)

var (
	goroot  = ""
	srcDirs = []string{}
	godev   = flag.Bool("godev", false, "Set the command into GoDev CGI mode")
)

func init() {
	flag.Parse()

	goroot = runtime.GOROOT() + string(os.PathSeparator)

	dirs := build.Default.SrcDirs()

	for i := len(dirs) - 1; i >= 0; i-- {
		srcDir := dirs[i]

		if !strings.HasPrefix(srcDir, goroot) {
			srcDirs = append(srcDirs, srcDir)
		}
	}
}

const (
	SEV_ERR  = "Error"
	SEV_WARN = "Warning"
	SEV_INFO = "Info"
	SEV_CNCL = "Cancel"
	SEV_OK   = "Ok"
)

// Orion Status object
type Status struct {
	// One of SEV_ERR, SEV_WARN, SEV_INFO, SEV_CNCL, SEV_OK
	Severity        string
	HttpCode        uint
	Message         string
	DetailedMessage string
}

// Helper function to write an Orion-compatible error message with an optional error object
func ShowError(writer http.ResponseWriter, httpCode uint, message string, err error) {
	writer.Header().Add("Content-Type", "application/json")
	writer.WriteHeader(int(httpCode))
	errStr := ""
	if err != nil {
		errStr = err.Error()
	}
	status := Status{SEV_ERR, httpCode, message, errStr}
	bytes, err := json.Marshal(status)
	if err != nil {
		panic(err)
	}
	_, err = writer.Write(bytes)
	if err != nil {
		log.Printf("ERROR: %v\n", err)
	}
}

// Helper function to write an Orion-compatible JSON object
func ShowJson(writer http.ResponseWriter, httpCode uint, obj interface{}) {
	writer.Header().Add("Content-Type", "application/json")
	writer.WriteHeader(int(httpCode))

	bytes, err := json.Marshal(obj)
	if err != nil {
		panic(err)
	}
	_, err = writer.Write(bytes)
	if err != nil {
		log.Printf("ERROR %v\n", err)
	}
}

type ImplementsResult struct {
	Implements Implements `json:"implements"`
}

type Implements struct {
	FromPtr []ImplementsType `json:"fromptr,omitempty"`
	To      []ImplementsType `json:"to,omitempty"`
}

type ImplementsType struct {
	Name       string `json:"name"`
	Pos        string `json:"pos"`
	LogicalPos string `json:"logicalPos"`
	Kind       string `json:"kind"`
}

type ReferrersResult struct {
	Referrers Referrers `json:"referrers"`
}

type Referrers struct {
	LogicalRefs []string `json:"logicalRefs"`
	Refs        []string `json:"refs"`
}

type CallersResult struct {
	Callers []Caller `json:"callers"`
}

type Caller struct {
	Desc       string `json:"desc"`
	Pos        string `json:"pos"`
	LogicalPos string `json:"logicalPos"`
}

type PeersResult struct {
	Peers Peers `json:"peers"`
}

type Peers struct {
	LogicalAllocs   []string `json:"logicalAllocs"`
	Allocs          []string `json:"allocs"`
	LogicalReceives []string `json:"logicalReceives"`
	Receives        []string `json:"receives"`
	LogicalSends    []string `json:"logicalSends"`
	Sends           []string `json:"sends"`
}

func getLogicalPos(localPos string) (logicalPos string) {
	for _, path := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
		match := path
		if match[len(match)-1] != filepath.Separator {
			match = match + string(filepath.Separator)
		}

		if strings.HasPrefix(localPos, match) {
			logicalPos = localPos[len(match)-1:]

			if path == filepath.Join(goroot, "/src/pkg") {
				logicalPos = "/GOROOT" + logicalPos
			}

			// Replace any Windows back-slashes into forward slashes
			logicalPos = strings.Replace(logicalPos, "\\", "/", -1)
		}
	}

	if logicalPos == "" {
		logicalPos = localPos
	}

	return logicalPos
}

func main() {
	if *godev == false {
		fmt.Printf("This is a CGI program mean to be plugged into the godev IDE\n")
		return
	}

	req, err := cgi.Request()

	if err != nil {
		fmt.Printf("Error trying to set up the CGI request", err.Error())
		return
	}

	writer := &cgiWriter{file: os.Stdout, wroteHeader: false, header: make(http.Header)}

	if !oracleHandler(writer, req, req.URL.Path, strings.Split(req.URL.Path, "/")[2:]) {
		ShowError(writer, 400, "Request not handled", nil)
	}
}

type cgiWriter struct {
	file        *os.File
	header      http.Header
	wroteHeader bool
}

func (writer *cgiWriter) WriteHeader(status int) {
	if !writer.wroteHeader {
		statusStr := strconv.FormatInt(int64(status), 10)
		writer.file.Write([]byte("Status: " + statusStr + "\r\n"))
		writer.header.Write(writer.file)

		// Final blank line separates the headers from the content
		writer.file.Write([]byte("\r\n"))
	} else {
		fmt.Errorf("Multiple write header calls")
	}
	writer.wroteHeader = true
}

func (writer *cgiWriter) Header() http.Header {
	return writer.header
}

func (writer *cgiWriter) Write(bytes []byte) (n int, err error) {
	if !writer.wroteHeader {
		writer.WriteHeader(http.StatusOK)
	}

	return writer.file.Write(bytes)
}

func oracleHandler(writer http.ResponseWriter, req *http.Request, path string, pathSegs []string) bool {
	switch {
	case req.Method == "GET" && pathSegs[2] == "implements":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "implements", scope)
		output, err := cmd.Output()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return true
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return true
		}

		implements := &ImplementsResult{}
		json.Unmarshal(output, implements)

		for idx, _ := range implements.Implements.FromPtr {
			path := getLogicalPos(implements.Implements.FromPtr[idx].Pos)
			implements.Implements.FromPtr[idx].LogicalPos = path
		}

		for idx, _ := range implements.Implements.To {
			path := getLogicalPos(implements.Implements.To[idx].Pos)
			implements.Implements.To[idx].LogicalPos = path
		}

		ShowJson(writer, 200, implements)
		return true
	case req.Method == "GET" && pathSegs[2] == "referrers":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "referrers", scope)
		output, err := cmd.StdoutPipe()
		if err != nil {
			ShowError(writer, 500, "Error opening pipe", err)
			return true
		}
		defer output.Close()
		err = cmd.Start()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return true
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return true
		}

		decoder := json.NewDecoder(output)
		referrers := &ReferrersResult{}
		decoder.Decode(referrers)

		cmd.Wait()

		for idx, _ := range referrers.Referrers.Refs {
			path := getLogicalPos(referrers.Referrers.Refs[idx])
			referrers.Referrers.LogicalRefs = append(referrers.Referrers.LogicalRefs, path)
		}

		ShowJson(writer, 200, referrers)
		return true
	case req.Method == "GET" && pathSegs[2] == "callers":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "callers", scope)
		output, err := cmd.StdoutPipe()
		if err != nil {
			ShowError(writer, 500, "Error opening pipe", err)
			return true
		}
		defer output.Close()
		err = cmd.Start()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return true
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return true
		}

		decoder := json.NewDecoder(output)
		callers := &CallersResult{}
		decoder.Decode(callers)

		cmd.Wait()

		for idx, _ := range callers.Callers {
			callers.Callers[idx].LogicalPos = getLogicalPos(callers.Callers[idx].Pos)
		}

		ShowJson(writer, 200, callers)
		return true
	case req.Method == "GET" && pathSegs[2] == "peers":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "peers", scope)
		output, err := cmd.StdoutPipe()
		if err != nil {
			ShowError(writer, 500, "Error opening pipe", err)
			return true
		}
		defer output.Close()
		err = cmd.Start()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return true
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return true
		}

		decoder := json.NewDecoder(output)
		peers := &PeersResult{}
		decoder.Decode(peers)

		cmd.Wait()

		for idx, _ := range peers.Peers.Allocs {
			path := getLogicalPos(peers.Peers.Allocs[idx])
			peers.Peers.LogicalAllocs = append(peers.Peers.LogicalAllocs, path)
		}

		for idx, _ := range peers.Peers.Sends {
			path := getLogicalPos(peers.Peers.Sends[idx])
			peers.Peers.LogicalSends = append(peers.Peers.LogicalSends, path)
		}

		for idx, _ := range peers.Peers.Receives {
			path := getLogicalPos(peers.Peers.Receives[idx])
			peers.Peers.LogicalReceives = append(peers.Peers.LogicalReceives, path)
		}

		ShowJson(writer, 200, peers)
		return true
	}

	return false
}
<commit_msg>Use cgi.Serve() to get response writer for free.<commit_after>// Copyright 2013 Chris McGee <sirnewton_01@yahoo.ca>. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"go/build"
	"log"
	"net/http"
	"net/http/cgi"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
)

var (
	goroot  = ""
	srcDirs = []string{}
	godev   = flag.Bool("godev", false, "Set the command into GoDev CGI mode")
)

func init() {
	flag.Parse()

	goroot = runtime.GOROOT() + string(os.PathSeparator)

	dirs := build.Default.SrcDirs()

	for i := len(dirs) - 1; i >= 0; i-- {
		srcDir := dirs[i]

		if !strings.HasPrefix(srcDir, goroot) {
			srcDirs = append(srcDirs, srcDir)
		}
	}
}

const (
	SEV_ERR  = "Error"
	SEV_WARN = "Warning"
	SEV_INFO = "Info"
	SEV_CNCL = "Cancel"
	SEV_OK   = "Ok"
)

// Orion Status object
type Status struct {
	// One of SEV_ERR, SEV_WARN, SEV_INFO, SEV_CNCL, SEV_OK
	Severity        string
	HttpCode        uint
	Message         string
	DetailedMessage string
}

// Helper function to write an Orion-compatible error message with an optional error object
func ShowError(writer http.ResponseWriter, httpCode uint, message string, err error) {
	writer.Header().Add("Content-Type", "application/json")
	writer.WriteHeader(int(httpCode))
	errStr := ""
	if err != nil {
		errStr = err.Error()
	}
	status := Status{SEV_ERR, httpCode, message, errStr}
	bytes, err := json.Marshal(status)
	if err != nil {
		panic(err)
	}
	_, err = writer.Write(bytes)
	if err != nil {
		log.Printf("ERROR: %v\n", err)
	}
}

// Helper function to write an Orion-compatible JSON object
func ShowJson(writer http.ResponseWriter, httpCode uint, obj interface{}) {
	writer.Header().Add("Content-Type", "application/json")
	writer.WriteHeader(int(httpCode))

	bytes, err := json.Marshal(obj)
	if err != nil {
		panic(err)
	}
	_, err = writer.Write(bytes)
	if err != nil {
		log.Printf("ERROR %v\n", err)
	}
}

type ImplementsResult struct {
	Implements Implements `json:"implements"`
}

type Implements struct {
	FromPtr []ImplementsType `json:"fromptr,omitempty"`
	To      []ImplementsType `json:"to,omitempty"`
}

type ImplementsType struct {
	Name       string `json:"name"`
	Pos        string `json:"pos"`
	LogicalPos string `json:"logicalPos"`
	Kind       string `json:"kind"`
}

type ReferrersResult struct {
	Referrers Referrers `json:"referrers"`
}

type Referrers struct {
	LogicalRefs []string `json:"logicalRefs"`
	Refs        []string `json:"refs"`
}

type CallersResult struct {
	Callers []Caller `json:"callers"`
}

type Caller struct {
	Desc       string `json:"desc"`
	Pos        string `json:"pos"`
	LogicalPos string `json:"logicalPos"`
}

type PeersResult struct {
	Peers Peers `json:"peers"`
}

type Peers struct {
	LogicalAllocs   []string `json:"logicalAllocs"`
	Allocs          []string `json:"allocs"`
	LogicalReceives []string `json:"logicalReceives"`
	Receives        []string `json:"receives"`
	LogicalSends    []string `json:"logicalSends"`
	Sends           []string `json:"sends"`
}

func getLogicalPos(localPos string) (logicalPos string) {
	for _, path := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
		match := path
		if match[len(match)-1] != filepath.Separator {
			match = match + string(filepath.Separator)
		}

		if strings.HasPrefix(localPos, match) {
			logicalPos = localPos[len(match)-1:]

			if path == filepath.Join(goroot, "/src/pkg") {
				logicalPos = "/GOROOT" + logicalPos
			}

			// Replace any Windows back-slashes into forward slashes
			logicalPos = strings.Replace(logicalPos, "\\", "/", -1)
		}
	}

	if logicalPos == "" {
		logicalPos = localPos
	}

	return logicalPos
}

func main() {
	if *godev == false {
		fmt.Printf("This is a CGI program mean to be plugged into the godev IDE\n")
		return
	}

	err := cgi.Serve(http.HandlerFunc(oracleHandler))

	if err != nil {
		fmt.Printf("Error trying to set up the CGI request", err.Error())
		return
	}
}

func oracleHandler(writer http.ResponseWriter, req *http.Request) {
	pathSegs := strings.Split(req.URL.Path, "/")[2:]

	switch {
	case req.Method == "GET" && pathSegs[2] == "implements":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "implements", scope)
		output, err := cmd.Output()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return
		}

		implements := &ImplementsResult{}
		json.Unmarshal(output, implements)

		for idx, _ := range implements.Implements.FromPtr {
			path := getLogicalPos(implements.Implements.FromPtr[idx].Pos)
			implements.Implements.FromPtr[idx].LogicalPos = path
		}

		for idx, _ := range implements.Implements.To {
			path := getLogicalPos(implements.Implements.To[idx].Pos)
			implements.Implements.To[idx].LogicalPos = path
		}

		ShowJson(writer, 200, implements)
		return
	case req.Method == "GET" && pathSegs[2] == "referrers":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "referrers", scope)
		output, err := cmd.StdoutPipe()
		if err != nil {
			ShowError(writer, 500, "Error opening pipe", err)
			return
		}
		defer output.Close()
		err = cmd.Start()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return
		}

		decoder := json.NewDecoder(output)
		referrers := &ReferrersResult{}
		decoder.Decode(referrers)

		cmd.Wait()

		for idx, _ := range referrers.Referrers.Refs {
			path := getLogicalPos(referrers.Referrers.Refs[idx])
			referrers.Referrers.LogicalRefs = append(referrers.Referrers.LogicalRefs, path)
		}

		ShowJson(writer, 200, referrers)
		return
	case req.Method == "GET" && pathSegs[2] == "callers":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "callers", scope)
		output, err := cmd.StdoutPipe()
		if err != nil {
			ShowError(writer, 500, "Error opening pipe", err)
			return
		}
		defer output.Close()
		err = cmd.Start()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return
		}

		decoder := json.NewDecoder(output)
		callers := &CallersResult{}
		decoder.Decode(callers)

		cmd.Wait()

		for idx, _ := range callers.Callers {
			callers.Callers[idx].LogicalPos = getLogicalPos(callers.Callers[idx].Pos)
		}

		ShowJson(writer, 200, callers)
		return
	case req.Method == "GET" && pathSegs[2] == "peers":
		localFilePath := ""
		pathToMatch := "/" + strings.Join(pathSegs[4:], "/")
		pathToMatch = strings.Replace(pathToMatch, "/GOROOT/", "/", 1)

		for _, srcDir := range append(srcDirs, filepath.Join(goroot, "/src/pkg")) {
			path := filepath.Join(srcDir, pathToMatch)

			_, err := os.Stat(path)

			if err == nil {
				localFilePath = path
				break
			}
		}

		pos := req.URL.Query().Get("pos")
		pos = localFilePath + ":#" + pos

		scope := req.URL.Query().Get("scope")

		cmd := exec.Command("oracle", "-format=json", "-pos="+pos, "peers", scope)
		output, err := cmd.StdoutPipe()
		if err != nil {
			ShowError(writer, 500, "Error opening pipe", err)
			return
		}
		defer output.Close()
		err = cmd.Start()

		if err != nil {
			if strings.Contains(strings.ToLower(err.Error()), "not found") {
				// Executable was not found, inform the user
				ShowError(writer, 500, "Oracle tool failed or is not installed.", err)
				return
			}

			// Tool returns no results basically
			writer.WriteHeader(204)
			return
		}

		decoder := json.NewDecoder(output)
		peers := &PeersResult{}
		decoder.Decode(peers)

		cmd.Wait()

		for idx, _ := range peers.Peers.Allocs {
			path := getLogicalPos(peers.Peers.Allocs[idx])
			peers.Peers.LogicalAllocs = append(peers.Peers.LogicalAllocs, path)
		}

		for idx, _ := range peers.Peers.Sends {
			path := getLogicalPos(peers.Peers.Sends[idx])
			peers.Peers.LogicalSends = append(peers.Peers.LogicalSends, path)
		}

		for idx, _ := range peers.Peers.Receives {
			path := getLogicalPos(peers.Peers.Receives[idx])
			peers.Peers.LogicalReceives = append(peers.Peers.LogicalReceives, path)
		}

		ShowJson(writer, 200, peers)
		return
	}

	ShowError(writer, 400, "Request not handled", nil)
}
<|endoftext|>


<commit_before>package qbs

import (
  "fmt"
	"strings"
	"time"
	"database/sql"
)

type oracle struct {
	base
}

func NewOracle() Dialect {
	d := &oracle{}
	d.base.Dialect = d
	return d
}

func (d oracle) quote(s string) string {
	sep := "."
	a := []string{}
	c := strings.Split(s, sep)
	for _, v := range c {
		a = append(a, fmt.Sprintf(`'%s'`, v))
	}
	return strings.Join(a, sep)
}

func (d oracle) sqlType(f interface{}, size int) string {
	switch f.(type) {
	case time.Time:
		return "DATE"
	/*
        case bool:
		return "boolean"
        */
	case int, int8, int16, int32, uint, uint8, uint16, uint32, int64, uint64:
		return "NUMBER"
	case float32, float64:
		return "NUMBER(16,2)"
    /*
	case []byte:
		return "bytea"
    */
	case []byte, string:
		if size > 0 && size < 4000 {
			return fmt.Sprintf("VARCHAR2(%d)", size)
		}
		return "CLOB"
	}
	panic("invalid sql type")
}

func (d oracle) insert(q *Qbs) (int64, error) {
	sql, args := d.Dialect.insertSql(q.criteria)
	row := q.QueryRow(sql, args...)
	value := q.criteria.model.pk.value
	var err error
	var id int64
	if _, ok := value.(int64); ok {
		err = row.Scan(&id)
	}else if _, ok := value.(string); ok {
		var str string
		err = row.Scan(&str)
	}
	return id, err
}

func (d oracle) insertSql(criteria *criteria) (string, []interface{}) {
	sql, values := d.base.insertSql(criteria)
	sql += " RETURNING " + d.Dialect.quote(criteria.model.pk.name)
	return sql, values
}

func (d oracle) indexExists(mg *Migration, tableName, indexName string) bool {
	var row *sql.Row
	var name string
	query := "SELECT INDEX_NAME FROM USER_INDEXES "
	query += "WHERE TABLE_NAME = ? AND INDEX_NAME = ?"
	query = d.substituteMarkers(query)
	row = mg.Db.QueryRow(query, tableName, indexName)
	row.Scan(&name)
	return name != ""
}

func (d oracle) substituteMarkers(query string) string {
	position := 1
	chunks := make([]string, 0, len(query)*2)
	for _, v := range query {
		if v == '?' {
			chunks = append(chunks, fmt.Sprintf("$%d", position))
			position++
		} else {
			chunks = append(chunks, string(v))
		}
	}
	return strings.Join(chunks, "")
}

func (d oracle) columnsInTable(mg *Migration, table interface{}) map[string]bool {
	tn := tableName(table)
	columns := make(map[string]bool)
	query := "SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME = ?"
	query = mg.Dialect.substituteMarkers(query)
	rows, err := mg.Db.Query(query, tn)
	defer rows.Close()
	if err != nil {
		panic(err)
	}
	for rows.Next() {
		column := ""
		err := rows.Scan(&column)
		if err == nil {
			columns[column] = true
		}
	}
	return columns
}

func (d oracle) primaryKeySql(isString bool, size int) string {
	if isString {
		return "text PRIMARY KEY"
	}
	return "bigserial PRIMARY KEY"
}

<commit_msg>update oracle dialect<commit_after>package qbs

import (
	"database/sql"
	"fmt"
	"strings"
	"time"
)

type oracle struct {
	base
}

func NewOracle() Dialect {
	d := &oracle{}
	d.base.Dialect = d
	return d
}

func (d oracle) quote(s string) string {
	sep := "."
	a := []string{}
	c := strings.Split(s, sep)
	for _, v := range c {
		a = append(a, fmt.Sprintf(`'%s'`, v))
	}
	return strings.Join(a, sep)
}

func (d oracle) sqlType(f interface{}, size int) string {
	switch f.(type) {
	case time.Time:
		return "DATE"
	/*
	        case bool:
			return "boolean"
	*/
	case int, int8, int16, int32, uint, uint8, uint16, uint32, int64, uint64:
		if size > 0 {
			return fmt.Sprintf("NUMBER(%d)", size)
		}
		return "NUMBER"
	case float32, float64:
		if size > 0 {
			return fmt.Sprintf("NUMBER(%d,%d)", size/10, size%10)
		}
		return "NUMBER(16,2)"
	case []byte, string:
		if size > 0 && size < 4000 {
			return fmt.Sprintf("VARCHAR2(%d)", size)
		}
		return "CLOB"
	}
	panic("invalid sql type")
}

func (d oracle) insert(q *Qbs) (int64, error) {
	sql, args := d.Dialect.insertSql(q.criteria)
	row := q.QueryRow(sql, args...)
	value := q.criteria.model.pk.value
	var err error
	var id int64
	if _, ok := value.(int64); ok {
		err = row.Scan(&id)
	} else if _, ok := value.(string); ok {
		var str string
		err = row.Scan(&str)
	}
	return id, err
}

func (d oracle) insertSql(criteria *criteria) (string, []interface{}) {
	sql, values := d.base.insertSql(criteria)
	sql += " RETURNING " + d.Dialect.quote(criteria.model.pk.name)
	return sql, values
}

func (d oracle) indexExists(mg *Migration, tableName, indexName string) bool {
	var row *sql.Row
	var name string
	query := "SELECT INDEX_NAME FROM USER_INDEXES "
	query += "WHERE TABLE_NAME = ? AND INDEX_NAME = ?"
	query = d.substituteMarkers(query)
	row = mg.Db.QueryRow(query, tableName, indexName)
	row.Scan(&name)
	return name != ""
}

func (d oracle) substituteMarkers(query string) string {
	position := 1
	chunks := make([]string, 0, len(query)*2)
	for _, v := range query {
		if v == '?' {
			chunks = append(chunks, fmt.Sprintf("$%d", position))
			position++
		} else {
			chunks = append(chunks, string(v))
		}
	}
	return strings.Join(chunks, "")
}

func (d oracle) columnsInTable(mg *Migration, table interface{}) map[string]bool {
	tn := tableName(table)
	columns := make(map[string]bool)
	query := "SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME = ?"
	query = mg.Dialect.substituteMarkers(query)
	rows, err := mg.Db.Query(query, tn)
	defer rows.Close()
	if err != nil {
		panic(err)
	}
	for rows.Next() {
		column := ""
		err := rows.Scan(&column)
		if err == nil {
			columns[column] = true
		}
	}
	return columns
}

func (d oracle) primaryKeySql(isString bool, size int) string {
	if isString {
		return fmt.Sprintf("VARCHAR2(%d) PRIMARY KEY NOT NULL", size)
	}
	return fmt.Sprintf("NUMBER(%d) PRIMARY KEY NOT NULL", size)
}

func (d oracle) createTableSql(model *model, ifNotExists bool) string {
	baseSql := d.base.createTableSql(model, ifNotExists)
	//sequence := "CREATE SEQENCE ....."
	//trigger := "CREATE TRIGGER ....."
	return baseSql //+ ":" + sequence + ";" + trigger
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"go/build"
	"go/types"
	"io"
	"os"
	"strings"

	"golang.org/x/tools/go/callgraph"
	"golang.org/x/tools/go/ssa"
)

var output io.Writer = os.Stdout

func printOutput(mainPkg *types.Package, cg *callgraph.Graph, focusPkg *build.Package, limitPaths, ignorePaths []string, groupBy map[string]bool, nostd bool) error {
	groupType := groupBy["type"]
	groupPkg := groupBy["pkg"]

	cluster := NewDotCluster("focus")
	cluster.Attrs = dotAttrs{
		"bgcolor":   "white",
		"label":     "",
		"labelloc":  "t",
		"labeljust": "c",
		"fontsize":  "18",
	}
	if focusPkg != nil {
		cluster.Attrs["bgcolor"] = "#e6ecfa"
		cluster.Attrs["label"] = focusPkg.Name
	}

	nodes := []*dotNode{}
	edges := []*dotEdge{}

	nodeMap := make(map[string]*dotNode)
	edgeMap := make(map[string]*dotEdge)

	cg.DeleteSyntheticNodes()

	logf("%d limit prefixes: %v", len(limitPaths), limitPaths)
	logf("%d ignore prefixes: %v", len(ignorePaths), ignorePaths)
	logf("no std packages: %v", nostd)

	var inLimit = func(node *callgraph.Node) bool {
		pkgPath := node.Func.Pkg.Pkg.Path()
		for _, p := range limitPaths {
			if strings.HasPrefix(pkgPath, p) {
				return true
			}
		}
		return false
	}

	var isIgnored = func(node *callgraph.Node) bool {
		pkgPath := node.Func.Pkg.Pkg.Path()
		for _, p := range ignorePaths {
			if strings.HasPrefix(pkgPath, p) {
				return true
			}
		}
		return false
	}

	var isStd = func(node *callgraph.Node) bool {
		pkg, _ := build.Import(node.Func.Pkg.Pkg.Path(), "", 0)
		return pkg.Goroot
	}

	count := 0
	err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error {
		count++

		caller := edge.Caller
		callee := edge.Callee

		// omit synthetic calls
		if caller.Func.Pkg == nil || callee.Func.Synthetic != "" {
			return nil
		}

		callerPkg := caller.Func.Pkg.Pkg
		calleePkg := callee.Func.Pkg.Pkg

		// focus specific pkg
		if focusPkg != nil &&
			(callerPkg.Path() != focusPkg.ImportPath && calleePkg.Path() != focusPkg.ImportPath) {
			return nil
		}

		// limit path prefixes
		if len(limitPaths) > 0 &&
			(!inLimit(caller) || !inLimit(callee)) {
			logf("NOT in limit: %s -> %s", caller, callee)
			return nil
		}

		// ignore path prefixes
		if len(ignorePaths) > 0 &&
			(isIgnored(caller) || isIgnored(callee)) {
			return nil
		}

		// omit std
		if nostd && (isStd(caller) || isStd(callee)) {
			return nil
		}

		var sprintNode = func(node *callgraph.Node) *dotNode {
			// only once
			key := node.Func.String()
			if n, ok := nodeMap[key]; ok {
				return n
			}

			isFocused := focusPkg != nil &&
				node.Func.Pkg.Pkg.Path() == focusPkg.ImportPath
			attrs := make(dotAttrs)

			// node label
			label := node.Func.RelString(node.Func.Pkg.Pkg)

			// func signature
			sign := node.Func.Signature
			if node.Func.Parent() != nil {
				sign = node.Func.Parent().Signature
			}

			// omit type from label
			if groupType && sign.Recv() != nil {
				parts := strings.Split(label, ".")
				label = parts[len(parts)-1]
			}

			pkg, _ := build.Import(node.Func.Pkg.Pkg.Path(), "", 0)
			// set node color
			if isFocused {
				attrs["fillcolor"] = "lightblue"
			} else if pkg.Goroot {
				attrs["fillcolor"] = "#adedad"
			} else {
				attrs["fillcolor"] = "moccasin"
			}

			// include pkg name
			if !groupPkg && !isFocused {
				label = fmt.Sprintf("%s\n%s", node.Func.Pkg.Pkg.Name(), label)
			}

			attrs["label"] = label

			// func styles
			if node.Func.Parent() != nil {
				attrs["style"] = "dotted,filled"
			} else if node.Func.Object() != nil && node.Func.Object().Exported() {
				attrs["penwidth"] = "1.5"
			} else {
				attrs["penwidth"] = "0.5"
			}

			c := cluster

			// group by pkg
			if groupPkg && !isFocused {
				label := node.Func.Pkg.Pkg.Name()
				if pkg.Goroot {
					label = node.Func.Pkg.Pkg.Path()
				}
				key := node.Func.Pkg.Pkg.Path()
				if _, ok := c.Clusters[key]; !ok {
					c.Clusters[key] = &dotCluster{
						ID:       key,
						Clusters: make(map[string]*dotCluster),
						Attrs: dotAttrs{
							"penwidth":  "0.8",
							"fontsize":  "16",
							"label":     label,
							"style":     "filled",
							"fillcolor": "lightyellow",
						},
					}
					if pkg.Goroot {
						c.Clusters[key].Attrs["fillcolor"] = "#E0FFE1"
					}
				}
				c = c.Clusters[key]
			}

			// group by type
			if groupType && sign.Recv() != nil {
				label := strings.Split(node.Func.RelString(node.Func.Pkg.Pkg), ".")[0]
				key := sign.Recv().Type().String()
				if _, ok := c.Clusters[key]; !ok {
					c.Clusters[key] = &dotCluster{
						ID:       key,
						Clusters: make(map[string]*dotCluster),
						Attrs: dotAttrs{
							"penwidth":  "0.5",
							"fontsize":  "15",
							"fontcolor": "#222222",
							"label":     label,
							"labelloc":  "b",
							"style":     "rounded,filled",
							"fillcolor": "wheat2",
						},
					}
					if isFocused {
						c.Clusters[key].Attrs["fillcolor"] = "lightsteelblue"
					} else if pkg.Goroot {
						c.Clusters[key].Attrs["fillcolor"] = "#c2e3c2"
					}
				}
				c = c.Clusters[key]
			}

			n := &dotNode{
				ID:    node.Func.String(),
				Attrs: attrs,
			}

			if c != nil {
				c.Nodes = append(c.Nodes, n)
			} else {
				nodes = append(nodes, n)
			}

			nodeMap[key] = n
			return n
		}
		callerNode := sprintNode(edge.Caller)
		calleeNode := sprintNode(edge.Callee)

		// edges
		attrs := make(dotAttrs)

		// dynamic call
		if edge.Site != nil && edge.Site.Common().StaticCallee() == nil {
			attrs["style"] = "dashed"
		}

		// go & defer calls
		switch edge.Site.(type) {
		case *ssa.Go:
			attrs["arrowhead"] = "normalnoneodot"
		case *ssa.Defer:
			attrs["arrowhead"] = "normalnoneodiamond"
		}

		// colorize calls outside focused pkg
		if focusPkg != nil &&
			(calleePkg.Path() != focusPkg.ImportPath || callerPkg.Path() != focusPkg.ImportPath) {
			attrs["color"] = "saddlebrown"
		}

		e := &dotEdge{
			From:  callerNode,
			To:    calleeNode,
			Attrs: attrs,
		}

		// omit duplicate calls
		key := fmt.Sprintf("%s = %s => %s", caller.Func, edge.Description(), callee.Func)
		if _, ok := edgeMap[key]; !ok {
			edges = append(edges, e)
			edgeMap[key] = e
		}

		return nil
	})
	if err != nil {
		return err
	}

	logf("%d/%d edges", len(edges), count)

	dot := &dotGraph{
		Title:   mainPkg.Path(),
		Minlen:  minlen,
		Cluster: cluster,
		Nodes:   nodes,
		Edges:   edges,
		Options: map[string]string{
			"minlen":  fmt.Sprint(minlen),
			"nodesep": fmt.Sprint(nodesep),
		},
	}

	return WriteDot(output, dot)
}
<commit_msg>Fixes #4<commit_after>package main

import (
	"fmt"
	"go/build"
	"go/types"
	"io"
	"os"
	"strings"

	"golang.org/x/tools/go/callgraph"
	"golang.org/x/tools/go/ssa"
)

var output io.Writer = os.Stdout

func isSynthetic(edge *callgraph.Edge) bool {
	return edge.Caller.Func.Pkg == nil || edge.Callee.Func.Synthetic != ""
}

func inStd(node *callgraph.Node) bool {
	pkg, _ := build.Import(node.Func.Pkg.Pkg.Path(), "", 0)
	return pkg.Goroot
}

func printOutput(mainPkg *types.Package, cg *callgraph.Graph, focusPkg *build.Package, limitPaths, ignorePaths []string, groupBy map[string]bool, nostd bool) error {
	groupType := groupBy["type"]
	groupPkg := groupBy["pkg"]

	cluster := NewDotCluster("focus")
	cluster.Attrs = dotAttrs{
		"bgcolor":   "white",
		"label":     "",
		"labelloc":  "t",
		"labeljust": "c",
		"fontsize":  "18",
	}
	if focusPkg != nil {
		cluster.Attrs["bgcolor"] = "#e6ecfa"
		cluster.Attrs["label"] = focusPkg.Name
	}

	nodes := []*dotNode{}
	edges := []*dotEdge{}

	nodeMap := make(map[string]*dotNode)
	edgeMap := make(map[string]*dotEdge)

	cg.DeleteSyntheticNodes()

	logf("%d limit prefixes: %v", len(limitPaths), limitPaths)
	logf("%d ignore prefixes: %v", len(ignorePaths), ignorePaths)
	logf("no std packages: %v", nostd)

	var isFocused = func(edge *callgraph.Edge) bool {
		caller := edge.Caller
		callee := edge.Callee
		if caller.Func.Pkg.Pkg.Path() == focusPkg.ImportPath ||
			callee.Func.Pkg.Pkg.Path() == focusPkg.ImportPath {
			return true
		}
		fromFocused := false
		toFocused := false
		for _, e := range caller.In {
			if !isSynthetic(e) &&
				e.Caller.Func.Pkg.Pkg.Path() == focusPkg.ImportPath {
				fromFocused = true
				break
			}
		}
		for _, e := range callee.Out {
			if !isSynthetic(e) &&
				e.Callee.Func.Pkg.Pkg.Path() == focusPkg.ImportPath {
				toFocused = true
				break
			}
		}
		if fromFocused && toFocused {
			logf("edge semi-focus: %s", edge)
			return true
		}
		return false
	}

	var inLimits = func(node *callgraph.Node) bool {
		pkgPath := node.Func.Pkg.Pkg.Path()
		for _, p := range limitPaths {
			if strings.HasPrefix(pkgPath, p) {
				return true
			}
		}
		return false
	}

	var inIgnores = func(node *callgraph.Node) bool {
		pkgPath := node.Func.Pkg.Pkg.Path()
		for _, p := range ignorePaths {
			if strings.HasPrefix(pkgPath, p) {
				return true
			}
		}
		return false
	}

	count := 0
	err := callgraph.GraphVisitEdges(cg, func(edge *callgraph.Edge) error {
		count++

		caller := edge.Caller
		callee := edge.Callee

		// omit synthetic calls
		if isSynthetic(edge) {
			return nil
		}

		callerPkg := caller.Func.Pkg.Pkg
		calleePkg := callee.Func.Pkg.Pkg

		// focus specific pkg
		if focusPkg != nil &&
			!isFocused(edge) {
			return nil
		}

		// limit path prefixes
		if len(limitPaths) > 0 &&
			(!inLimits(caller) || !inLimits(callee)) {
			logf("NOT in limit: %s -> %s", caller, callee)
			return nil
		}

		// ignore path prefixes
		if len(ignorePaths) > 0 &&
			(inIgnores(caller) || inIgnores(callee)) {
			return nil
		}

		// omit std
		if nostd &&
			(inStd(caller) || inStd(callee)) {
			return nil
		}

		var sprintNode = func(node *callgraph.Node) *dotNode {
			// only once
			key := node.Func.String()
			if n, ok := nodeMap[key]; ok {
				return n
			}

			isFocused := focusPkg != nil &&
				node.Func.Pkg.Pkg.Path() == focusPkg.ImportPath
			attrs := make(dotAttrs)

			// node label
			label := node.Func.RelString(node.Func.Pkg.Pkg)

			// func signature
			sign := node.Func.Signature
			if node.Func.Parent() != nil {
				sign = node.Func.Parent().Signature
			}

			// omit type from label
			if groupType && sign.Recv() != nil {
				parts := strings.Split(label, ".")
				label = parts[len(parts)-1]
			}

			pkg, _ := build.Import(node.Func.Pkg.Pkg.Path(), "", 0)
			// set node color
			if isFocused {
				attrs["fillcolor"] = "lightblue"
			} else if pkg.Goroot {
				attrs["fillcolor"] = "#adedad"
			} else {
				attrs["fillcolor"] = "moccasin"
			}

			// include pkg name
			if !groupPkg && !isFocused {
				label = fmt.Sprintf("%s\n%s", node.Func.Pkg.Pkg.Name(), label)
			}

			attrs["label"] = label

			// func styles
			if node.Func.Parent() != nil {
				attrs["style"] = "dotted,filled"
			} else if node.Func.Object() != nil && node.Func.Object().Exported() {
				attrs["penwidth"] = "1.5"
			} else {
				attrs["penwidth"] = "0.5"
			}

			c := cluster

			// group by pkg
			if groupPkg && !isFocused {
				label := node.Func.Pkg.Pkg.Name()
				if pkg.Goroot {
					label = node.Func.Pkg.Pkg.Path()
				}
				key := node.Func.Pkg.Pkg.Path()
				if _, ok := c.Clusters[key]; !ok {
					c.Clusters[key] = &dotCluster{
						ID:       key,
						Clusters: make(map[string]*dotCluster),
						Attrs: dotAttrs{
							"penwidth":  "0.8",
							"fontsize":  "16",
							"label":     label,
							"style":     "filled",
							"fillcolor": "lightyellow",
						},
					}
					if pkg.Goroot {
						c.Clusters[key].Attrs["fillcolor"] = "#E0FFE1"
					}
				}
				c = c.Clusters[key]
			}

			// group by type
			if groupType && sign.Recv() != nil {
				label := strings.Split(node.Func.RelString(node.Func.Pkg.Pkg), ".")[0]
				key := sign.Recv().Type().String()
				if _, ok := c.Clusters[key]; !ok {
					c.Clusters[key] = &dotCluster{
						ID:       key,
						Clusters: make(map[string]*dotCluster),
						Attrs: dotAttrs{
							"penwidth":  "0.5",
							"fontsize":  "15",
							"fontcolor": "#222222",
							"label":     label,
							"labelloc":  "b",
							"style":     "rounded,filled",
							"fillcolor": "wheat2",
						},
					}
					if isFocused {
						c.Clusters[key].Attrs["fillcolor"] = "lightsteelblue"
					} else if pkg.Goroot {
						c.Clusters[key].Attrs["fillcolor"] = "#c2e3c2"
					}
				}
				c = c.Clusters[key]
			}

			n := &dotNode{
				ID:    node.Func.String(),
				Attrs: attrs,
			}

			if c != nil {
				c.Nodes = append(c.Nodes, n)
			} else {
				nodes = append(nodes, n)
			}

			nodeMap[key] = n
			return n
		}
		callerNode := sprintNode(edge.Caller)
		calleeNode := sprintNode(edge.Callee)

		// edges
		attrs := make(dotAttrs)

		// dynamic call
		if edge.Site != nil && edge.Site.Common().StaticCallee() == nil {
			attrs["style"] = "dashed"
		}

		// go & defer calls
		switch edge.Site.(type) {
		case *ssa.Go:
			attrs["arrowhead"] = "normalnoneodot"
		case *ssa.Defer:
			attrs["arrowhead"] = "normalnoneodiamond"
		}

		// colorize calls outside focused pkg
		if focusPkg != nil &&
			(calleePkg.Path() != focusPkg.ImportPath || callerPkg.Path() != focusPkg.ImportPath) {
			attrs["color"] = "saddlebrown"
		}

		e := &dotEdge{
			From:  callerNode,
			To:    calleeNode,
			Attrs: attrs,
		}

		// omit duplicate calls
		key := fmt.Sprintf("%s = %s => %s", caller.Func, edge.Description(), callee.Func)
		if _, ok := edgeMap[key]; !ok {
			edges = append(edges, e)
			edgeMap[key] = e
		}

		return nil
	})
	if err != nil {
		return err
	}

	logf("%d/%d edges", len(edges), count)

	dot := &dotGraph{
		Title:   mainPkg.Path(),
		Minlen:  minlen,
		Cluster: cluster,
		Nodes:   nodes,
		Edges:   edges,
		Options: map[string]string{
			"minlen":  fmt.Sprint(minlen),
			"nodesep": fmt.Sprint(nodesep),
		},
	}

	return WriteDot(output, dot)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"image"
	"image/draw"
	"image/png"
)

type Size struct {
	X, Y int
}

type sprite struct {
	Name   string
	Offset image.Point
	Size   Size
	img    image.Image
	area   int
}

type ByArea []sprite

func (a ByArea) Len() int           { return len(a) }
func (a ByArea) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByArea) Less(i, j int) bool { return a[i].area < a[j].area }

type Node struct {
	child [2]*Node
	rect  image.Rectangle
	img   *sprite
}

func (n *Node) size() Size {
	return Size{n.rect.Dx(), n.rect.Dy()}
}

func (n *Node) print() {
	fmt.Println(n)
	if n.child[0] != nil {
		n.child[0].print()
	}
	if n.child[1] != nil {
		n.child[1].print()
	}
}

func (n *Node) isLeaf() bool {
	return n.child[0] == nil || n.child[1] == nil
}

func (n *Node) insert(img *sprite) *Node {
	if !n.isLeaf() {
		node := n.child[0].insert(img)

		if node != nil {
			return node
		} else {
			return n.child[1].insert(img)
		}
	}

	// there is already an image in this node
	if n.img != nil {
		return nil
	}

	// space too small
	if n.rect.Dx() < img.Size.X || n.rect.Dy() < img.Size.Y {
		return nil
	}

	// just right
	if n.rect.Dx() == img.Size.X && n.rect.Dy() == img.Size.Y {
		img.Offset = n.rect.Min
		n.img = img
		return n
	}

	// the space that is left will be large enough to split
	n.split(img)

	return n.child[0].insert(img)
}

func (n *Node) split(img *sprite) {
	dx := n.rect.Dx() - img.Size.X
	dy := n.rect.Dy() - img.Size.Y

	if dx > dy {
		n.child[0] = &Node{rect: image.Rect(
			n.rect.Min.X,
			n.rect.Min.Y,
			n.rect.Min.X+img.Size.X,
			n.rect.Max.Y)}
		n.child[1] = &Node{rect: image.Rect(
			n.rect.Min.X+img.Size.X,
			n.rect.Min.Y,
			n.rect.Max.X,
			n.rect.Max.Y)}
	} else {
		n.child[0] = &Node{rect: image.Rect(
			n.rect.Min.X,
			n.rect.Min.Y,
			n.rect.Max.X,
			n.rect.Min.Y+img.Size.Y)}
		n.child[1] = &Node{rect: image.Rect(
			n.rect.Min.X,
			n.rect.Min.Y+img.Size.Y,
			n.rect.Max.X,
			n.rect.Max.Y)}
	}

}

func main() {
	var space int
	var dim string

	flag.IntVar(&space, "space", 1, "space added between images")
	flag.StringVar(&dim, "dimensions", "1024x1024", "atlas size")

	flag.Parse()

	args := flag.Args()

	inputDir := args[0]
	outputName := args[1]

	dimX, dimY, err := parseDimensions(dim)
	if err != nil {
		log.Fatal(err)
	}

	files, _ := ioutil.ReadDir(inputDir)
	sprites := make([]sprite, len(files))

	totalArea := 0

	for i := range sprites {
		s, err := readSprite(inputDir, files[i].Name(), space)
		if err != nil {
			log.Fatal(err)
		}
		totalArea += s.area
		sprites[i] = s
	}

	if totalArea > dimX*dimY {
		log.Fatal("atlas to small")
	}

	// we want to place the largest sprite first
	sort.Sort(sort.Reverse(ByArea(sprites)))

	// the final image
	dst := image.NewRGBA(image.Rect(0, 0, dimX, dimY))

	n := Node{rect: image.Rect(0, 0, dimX, dimY)}

	for i := range sprites {
		s := &sprites[i]
		node := n.insert(s)
		if node != nil {
			draw.Draw(dst, node.rect, s.img, image.ZP, draw.Src)
		} else {
			log.Fatalf("could not place %s\n", s.Name)
		}

	}

	writer, err := os.Create(outputName)
	if err != nil {
		log.Fatal(err)
	}
	defer writer.Close()

	err = png.Encode(writer, dst)
	if err != nil {
		log.Fatal(err)
	}
}

func parseDimensions(dim string) (dimX, dimY int, err error) {
	dims := strings.Split(dim, "x")
	if len(dims) != 2 {
		err = fmt.Errorf("couldn't parse dimension %s\n", dims)
		return
	}

	dimX, err = strconv.Atoi(dims[0])
	if err != nil {
		return
	}

	dimY, err = strconv.Atoi(dims[1])
	if err != nil {
		return
	}

	return
}

func readSprite(dir, name string, space int) (s sprite, err error) {
	path := path.Join(dir, name)
	reader, err := os.Open(path)
	if err != nil {
		return
	}
	defer reader.Close()

	img, _, err := image.Decode(reader)
	if err != nil {
		return
	}

	s.Name = strings.TrimSuffix(name, filepath.Ext(name))
	s.img = img
	rect := img.Bounds()
	s.Size = Size{rect.Dx() + space, rect.Dy() + space}
	s.area = s.Size.X * s.Size.Y
	return
}
<commit_msg>Move writing atlas to its own function<commit_after>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path"
	"path/filepath"
	"sort"
	"strconv"
	"strings"

	"image"
	"image/draw"
	"image/png"
)

type Size struct {
	X, Y int
}

type sprite struct {
	Name   string
	Offset image.Point
	Size   Size
	img    image.Image
	area   int
}

type ByArea []sprite

func (a ByArea) Len() int           { return len(a) }
func (a ByArea) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a ByArea) Less(i, j int) bool { return a[i].area < a[j].area }

type Node struct {
	child [2]*Node
	rect  image.Rectangle
	img   *sprite
}

func (n *Node) size() Size {
	return Size{n.rect.Dx(), n.rect.Dy()}
}

func (n *Node) print() {
	fmt.Println(n)
	if n.child[0] != nil {
		n.child[0].print()
	}
	if n.child[1] != nil {
		n.child[1].print()
	}
}

func (n *Node) isLeaf() bool {
	return n.child[0] == nil || n.child[1] == nil
}

func (n *Node) insert(img *sprite) *Node {
	if !n.isLeaf() {
		node := n.child[0].insert(img)

		if node != nil {
			return node
		} else {
			return n.child[1].insert(img)
		}
	}

	// there is already an image in this node
	if n.img != nil {
		return nil
	}

	// space too small
	if n.rect.Dx() < img.Size.X || n.rect.Dy() < img.Size.Y {
		return nil
	}

	// just right
	if n.rect.Dx() == img.Size.X && n.rect.Dy() == img.Size.Y {
		img.Offset = n.rect.Min
		n.img = img
		return n
	}

	// the space that is left will be large enough to split
	n.split(img)

	return n.child[0].insert(img)
}

func (n *Node) split(img *sprite) {
	dx := n.rect.Dx() - img.Size.X
	dy := n.rect.Dy() - img.Size.Y

	if dx > dy {
		n.child[0] = &Node{rect: image.Rect(
			n.rect.Min.X,
			n.rect.Min.Y,
			n.rect.Min.X+img.Size.X,
			n.rect.Max.Y)}
		n.child[1] = &Node{rect: image.Rect(
			n.rect.Min.X+img.Size.X,
			n.rect.Min.Y,
			n.rect.Max.X,
			n.rect.Max.Y)}
	} else {
		n.child[0] = &Node{rect: image.Rect(
			n.rect.Min.X,
			n.rect.Min.Y,
			n.rect.Max.X,
			n.rect.Min.Y+img.Size.Y)}
		n.child[1] = &Node{rect: image.Rect(
			n.rect.Min.X,
			n.rect.Min.Y+img.Size.Y,
			n.rect.Max.X,
			n.rect.Max.Y)}
	}

}

func main() {
	var space int
	var dim string

	flag.IntVar(&space, "space", 1, "space added between images")
	flag.StringVar(&dim, "dimensions", "1024x1024", "atlas size")

	flag.Parse()

	args := flag.Args()

	inputDir := args[0]
	outputName := args[1]

	dimX, dimY, err := parseDimensions(dim)
	if err != nil {
		log.Fatal(err)
	}

	files, _ := ioutil.ReadDir(inputDir)
	sprites := make([]sprite, len(files))

	totalArea := 0

	for i := range sprites {
		s, err := readSprite(inputDir, files[i].Name(), space)
		if err != nil {
			log.Fatal(err)
		}
		totalArea += s.area
		sprites[i] = s
	}

	if totalArea > dimX*dimY {
		log.Fatal("atlas to small")
	}

	// we want to place the largest sprite first
	sort.Sort(sort.Reverse(ByArea(sprites)))

	// the final image
	dst := image.NewRGBA(image.Rect(0, 0, dimX, dimY))

	n := Node{rect: image.Rect(0, 0, dimX, dimY)}

	for i := range sprites {
		s := &sprites[i]
		node := n.insert(s)
		if node != nil {
			draw.Draw(dst, node.rect, s.img, image.ZP, draw.Src)
		} else {
			log.Fatalf("could not place %s\n", s.Name)
		}

	}

	err = writeAtlas(outputName, dst)
	if err != nil {
		log.Fatal(err)
	}
}

func parseDimensions(dim string) (dimX, dimY int, err error) {
	dims := strings.Split(dim, "x")
	if len(dims) != 2 {
		err = fmt.Errorf("couldn't parse dimension %s\n", dims)
		return
	}

	dimX, err = strconv.Atoi(dims[0])
	if err != nil {
		return
	}

	dimY, err = strconv.Atoi(dims[1])
	if err != nil {
		return
	}

	return
}

func readSprite(dir, name string, space int) (s sprite, err error) {
	path := path.Join(dir, name)
	reader, err := os.Open(path)
	if err != nil {
		return
	}
	defer reader.Close()

	img, _, err := image.Decode(reader)
	if err != nil {
		return
	}

	s.Name = strings.TrimSuffix(name, filepath.Ext(name))
	s.img = img
	rect := img.Bounds()
	s.Size = Size{rect.Dx() + space, rect.Dy() + space}
	s.area = s.Size.X * s.Size.Y
	return
}

func writeAtlas(filename string, data *image.RGBA) (err error) {
	writer, err := os.Create(filename)
	if err != nil {
		return
	}
	defer writer.Close()

	err = png.Encode(writer, data)
	if err != nil {
		return
	}

	return
}
<|endoftext|>


<commit_before>package sphere

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
)

// Packet indicates the data of the message
type Packet struct {
	Type      PacketType
	Namespace string
	Room      string
	Cid       int
	Error     error
	Message   *Message
	Machine   string
}

// ParsePacket returns Packet from bytes
func ParsePacket(data []byte) (*Packet, error) {
	var p *Packet
	if err := json.Unmarshal(data, &p); err != nil {
		return nil, errors.New("packet format is invalid")
	}
	return p, nil
}

// Packet.toJSON returns json byte array from Packet
func (p *Packet) toJSON() ([]byte, error) {
	return json.Marshal(p)
}

// MarshalJSON handler
func (p *Packet) MarshalJSON() ([]byte, error) {
	var err string
	if p.Error != nil {
		err = p.Error.Error()
	}
	return json.Marshal(&struct {
		Type      PacketType `json:"type"`
		Namespace string     `json:"namespace,omitempty"`
		Room      string     `json:"room,omitempty"`
		Cid       int        `json:"cid"`
		Error     string     `json:"error,omitempty"`
		Message   *Message   `json:"message,omitempty"`
		Machine   string     `json:"-"`
	}{p.Type, p.Namespace, p.Room, p.Cid, err, p.Message, p.Machine})
}

// Packet.toBytes returns byte array from Packet
func (p *Packet) toBytes() ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(p)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

func (p *Packet) String() string {
	if json, err := p.toJSON(); err == nil {
		return string(json[:])
	}
	return ""
}

// Response return response packet
func (p *Packet) Response() *Packet {
	r := *p
	switch r.Type {
	case PacketTypeSubscribe:
		r.Type = PacketTypeSubscribed
	case PacketTypeUnsubscribe:
		r.Type = PacketTypeUnsubscribed
	case PacketTypePing:
		r.Type = PacketTypePong
	}
	return &r
}

// SetError set error message
func (p *Packet) SetError(err error) *Packet {
	p.Error = err
	return p
}
<commit_msg>Export ToJSON, ToBytes<commit_after>package sphere

import (
	"bytes"
	"encoding/gob"
	"encoding/json"
	"errors"
)

// Packet indicates the data of the message
type Packet struct {
	Type      PacketType
	Namespace string
	Room      string
	Cid       int
	Error     error
	Message   *Message
	Machine   string
}

// ParsePacket returns Packet from bytes
func ParsePacket(data []byte) (*Packet, error) {
	var p *Packet
	if err := json.Unmarshal(data, &p); err != nil {
		return nil, errors.New("packet format is invalid")
	}
	return p, nil
}

// ToJSON returns json byte array from Packet
func (p *Packet) ToJSON() ([]byte, error) {
	return json.Marshal(p)
}

// ToBytes returns byte array from Packet
func (p *Packet) ToBytes() ([]byte, error) {
	var buf bytes.Buffer
	enc := gob.NewEncoder(&buf)
	err := enc.Encode(p)
	if err != nil {
		return nil, err
	}
	return buf.Bytes(), nil
}

// String returns Packet in string format
func (p *Packet) String() string {
	if json, err := p.ToJSON(); err == nil {
		return string(json[:])
	}
	return ""
}

// MarshalJSON handler
func (p *Packet) MarshalJSON() ([]byte, error) {
	var err string
	if p.Error != nil {
		err = p.Error.Error()
	}
	return json.Marshal(&struct {
		Type      PacketType `json:"type"`
		Namespace string     `json:"namespace,omitempty"`
		Room      string     `json:"room,omitempty"`
		Cid       int        `json:"cid"`
		Error     string     `json:"error,omitempty"`
		Message   *Message   `json:"message,omitempty"`
		Machine   string     `json:"-"`
	}{p.Type, p.Namespace, p.Room, p.Cid, err, p.Message, p.Machine})
}

// Response return response packet
func (p *Packet) Response() *Packet {
	r := *p
	switch r.Type {
	case PacketTypeSubscribe:
		r.Type = PacketTypeSubscribed
	case PacketTypeUnsubscribe:
		r.Type = PacketTypeUnsubscribed
	case PacketTypePing:
		r.Type = PacketTypePong
	}
	return &r
}

// SetError set error message
func (p *Packet) SetError(err error) *Packet {
	p.Error = err
	return p
}
<|endoftext|>


<commit_before>package river

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"hawx.me/code/riviera/river/data/memdata"
	"hawx.me/code/riviera/river/internal/persistence"
	"hawx.me/code/riviera/river/models"
)

func TestConfluence(t *testing.T) {
	db := memdata.Open()
	river, _ := persistence.NewRiver(db, -time.Minute)

	c := newConfluence(river, newEvents(3))

	assert.Empty(t, c.Latest())
}

type dummyTrib struct {
	name    string
	feed    models.Feed
	stopped bool
	feeds   chan models.Feed
	fetches chan int
}

func (d *dummyTrib) Name() string { return d.name }

func (d *dummyTrib) push() {
	d.feeds <- d.feed
}

func (d *dummyTrib) Feeds() <-chan models.Feed {
	return d.feeds
}

func (d *dummyTrib) Fetches() <-chan int {
	return d.fetches
}

func (d *dummyTrib) Start() {
	d.feeds = make(chan models.Feed)
	d.fetches = make(chan int)
	d.stopped = false
	d.push()
}

func (d *dummyTrib) Stop() {
	d.stopped = true
	close(d.feeds)
	close(d.fetches)
}

func TestConfluenceWithTributary(t *testing.T) {
	db := memdata.Open()
	river, _ := persistence.NewRiver(db, -time.Minute)

	c := newConfluence(river, newEvents(3))

	now := time.Now().Local().Round(time.Second)

	feed := models.Feed{
		FeedTitle:      "hey",
		WhenLastUpdate: models.RssTime{now.Add(-time.Second)},
	}
	feed2 := models.Feed{
		FeedTitle:      "cool",
		WhenLastUpdate: models.RssTime{now.Add(-5 * time.Second)},
	}

	trib := &dummyTrib{feed: feed, name: "dummy1"}
	trib2 := &dummyTrib{feed: feed2, name: "dummy2"}

	c.Add(trib)
	trib.Start()
	assert.Equal(t, []models.Feed{feed}, c.Latest())

	c.Add(trib2)
	trib2.Start()
	assert.Equal(t, []models.Feed{feed, feed2}, c.Latest())

	c.Remove(trib.Name())
	assert.True(t, trib.stopped)

	c.Add(trib)
	trib.Start()
	assert.Equal(t, []models.Feed{feed, feed2}, c.Latest())
}

func TestConfluenceWithTributaryWhenTooOld(t *testing.T) {
	db := memdata.Open()
	river, _ := persistence.NewRiver(db, -time.Minute)

	c := newConfluence(river, newEvents(3))

	feed := models.Feed{
		FeedTitle:      "hey",
		WhenLastUpdate: models.RssTime{time.Now().Add(-5 * time.Minute)},
	}

	trib := &dummyTrib{name: "dummy3", feed: feed}
	c.Add(trib)
	trib.Start()

	assert.Empty(t, c.Latest())
}
<commit_msg>Prevent races in confluence tests<commit_after>package river

import (
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"hawx.me/code/riviera/river/data/memdata"
	"hawx.me/code/riviera/river/internal/persistence"
	"hawx.me/code/riviera/river/models"
)

func TestConfluence(t *testing.T) {
	db := memdata.Open()
	river, _ := persistence.NewRiver(db, -time.Minute)

	c := newConfluence(river, newEvents(3))

	assert.Empty(t, c.Latest())
}

type dummyTrib struct {
	name    string
	feed    models.Feed
	stopped bool
	feeds   chan models.Feed
	fetches chan int
}

func newDummyTrib(feed models.Feed, name string) *dummyTrib {
	return &dummyTrib{
		name:    name,
		feed:    feed,
		stopped: true,
		feeds:   make(chan models.Feed),
		fetches: make(chan int),
	}
}

func (d *dummyTrib) Name() string { return d.name }

func (d *dummyTrib) push() {
	d.feeds <- d.feed
}

func (d *dummyTrib) Feeds() <-chan models.Feed {
	return d.feeds
}

func (d *dummyTrib) Fetches() <-chan int {
	return d.fetches
}

func (d *dummyTrib) Start() {
	d.stopped = false
	d.push()
}

func (d *dummyTrib) Stop() {
	d.stopped = true
}

func TestConfluenceWithTributary(t *testing.T) {
	db := memdata.Open()
	river, _ := persistence.NewRiver(db, -time.Minute)

	c := newConfluence(river, newEvents(3))

	now := time.Now().Local().Round(time.Second)

	feed := models.Feed{
		FeedTitle:      "hey",
		WhenLastUpdate: models.RssTime{now.Add(-time.Second)},
	}
	feed2 := models.Feed{
		FeedTitle:      "cool",
		WhenLastUpdate: models.RssTime{now.Add(-5 * time.Second)},
	}

	trib := newDummyTrib(feed, "dummy1")
	trib2 := newDummyTrib(feed2, "dummy2")

	c.Add(trib)
	trib.Start()
	assert.Equal(t, []models.Feed{feed}, c.Latest())

	c.Add(trib2)
	trib2.Start()
	assert.Equal(t, []models.Feed{feed, feed2}, c.Latest())

	c.Remove(trib.Name())
	assert.True(t, trib.stopped)

	c.Add(trib)
	trib.Start()
	assert.Equal(t, []models.Feed{feed, feed2}, c.Latest())
}

func TestConfluenceWithTributaryWhenTooOld(t *testing.T) {
	db := memdata.Open()
	river, _ := persistence.NewRiver(db, -time.Minute)

	c := newConfluence(river, newEvents(3))

	feed := models.Feed{
		FeedTitle:      "hey",
		WhenLastUpdate: models.RssTime{time.Now().Add(-5 * time.Minute)},
	}

	trib := newDummyTrib(feed, "dummy3")
	c.Add(trib)
	trib.Start()

	assert.Empty(t, c.Latest())
}
<|endoftext|>


<commit_before>package database

import (
	"github.com/boltdb/bolt"
	rss "github.com/hawx/go-pkg-rss"
)

type Bucket interface {
	rss.Database
}

type bucket struct {
	name string
	db   *bolt.DB
}

var in []byte = []byte("in")

func (d *bucket) Get(key string) bool {
	ok := false

	d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(d.name))
		if b.Get([]byte(key)) != nil {
			ok = true
		}
		return nil
	})

	return ok
}

func (d *bucket) Set(key string) {
	d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(d.name))
		return b.Put([]byte(key), in)
	})
}
<commit_msg>Modify to use simpler database interface<commit_after>package database

import (
	"github.com/boltdb/bolt"
	rss "github.com/hawx/go-pkg-rss"
)

type Bucket interface {
	rss.Database
}

type bucket struct {
	name string
	db   *bolt.DB
}

var in []byte = []byte("in")

func (d *bucket) Contains(key string) bool {
	ok := false

	d.db.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(d.name))
		if b.Get([]byte(key)) != nil {
			ok = true
		}
		return nil
	})

	if ok {
		return true
	}

	d.db.Update(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(d.name))
		return b.Put([]byte(key), in)
	})

	return false
}
<|endoftext|>


<commit_before>package polo

import (
	"fmt"
	"html/template"
	"log"
	"os"
)

type Site struct {
	db DB

	Config     Config
	outputPath string

	Article, Page ParsedFile
}

func NewSite(db DB, config Config, outputPath string) *Site {
	return &Site{db: db, Config: config, outputPath: outputPath}
}

// TODO (agonzalezro): this needs to be merged with Pages() and used in writeXXXs
func (site Site) Articles() (articles []*ParsedFile) {
	query := `
		SELECT title, slug, content, tags, date, is_page
		FROM files
		WHERE is_page = 0
		`
	rows, err := site.db.connection.Query(query)
	if err != nil {
		log.Panic(err)
	}
	for rows.Next() {
		var title, slug, content, tags, date string
		var isPage int
		rows.Scan(&title, &slug, &content, &tags, &date, isPage)
		article := ParsedFile{Title: title, Slug: slug, Content: content, tags: tags, Date: date}
		articles = append(articles, &article)
	}
	return articles
}

// TODO (agonzalezro): use this function from write*
func (site Site) Pages() (pages []*ParsedFile) {
	query := `
		SELECT title, slug, content, tags, date, is_page
		FROM files
		WHERE is_page =1
		`
	rows, err := site.db.connection.Query(query)
	if err != nil {
		log.Panic(err)
	}
	for rows.Next() {
		var title, slug, content, tags, date string
		var isPage int
		rows.Scan(&title, &slug, &content, &tags, &date, isPage)
		page := ParsedFile{Title: title, Slug: slug, Content: content, tags: tags, Date: date}
		pages = append(pages, &page)
	}
	return pages
}

// Dump all the site content to the disk
func (site Site) Write() {
	site.writeIndex()
	site.writeArticles()
	site.writePages()
}

func (site Site) writeIndex() {
	indexFile := fmt.Sprintf("%s/index.html", site.outputPath)

	template := template.Must(template.ParseFiles("templates/index.html", "templates/base.html"))

	file, err := os.Create(indexFile)
	if err != nil {
		log.Panic(err)
	}
	template.ExecuteTemplate(file, "base", site)
}

// Write articles in different files
func (site Site) writeArticles() {
	query := `
    SELECT title, slug, content, tags, date, is_page
    FROM files
	WHERE is_page = 0
    `
	rows, err := site.db.connection.Query(query)
	if err != nil {
		log.Panic(err)
	}
	for rows.Next() {
		var title, slug, content, tags, date string
		var isPage int
		rows.Scan(&title, &slug, &content, &tags, &date, isPage)
		article := ParsedFile{Title: title, Slug: slug, Content: content, tags: tags, Date: date}
		filePath := fmt.Sprintf("%s/%s.html", site.outputPath, article.Slug)

		template := template.Must(template.ParseFiles("templates/article.html", "templates/base.html"))

		file, err := os.Create(filePath)
		if err != nil {
			log.Panic(err)
		}
		site.Article = article
		if err := template.ExecuteTemplate(file, "base", site); err != nil {
			log.Panic(err)
		}
	}

}

func (site Site) writePages() {
	query := `
	SELECT title, slug, content, tags, date, is_page
	FROM files
	WHERE is_page = 1
	`

	// First of all create the pages/ folder if it doesn't exist
	pagesPath := fmt.Sprintf("%s/pages", site.outputPath)
	if _, err := os.Stat(pagesPath); os.IsNotExist(err) {
		os.Mkdir(pagesPath, 0777)
	}

	rows, err := site.db.connection.Query(query)
	if err != nil {
		log.Panic(err)
	}

	for rows.Next() {
		var title, slug, content, tags, date string
		var isPage int
		rows.Scan(&title, &slug, &content, &tags, &date, &isPage)
		page := ParsedFile{Title: title, Slug: slug, Content: content, tags: tags, Date: date}

		filePath := fmt.Sprintf("%s/%s.html", pagesPath, page.Slug)

		template := template.Must(template.ParseFiles("templates/page.html", "templates/base.html"))

		file, err := os.Create(filePath)
		if err != nil {
			log.Panic(err)
		}
		site.Page = page
		if err := template.ExecuteTemplate(file, "base", site); err != nil {
			log.Panic(err)
		}
	}
}
<commit_msg>Duplication on site.go removed<commit_after>package polo

import (
	"fmt"
	"html/template"
	"log"
	"os"
)

type Site struct {
	db DB

	Config     Config
	outputPath string

	Article, Page ParsedFile
}

func NewSite(db DB, config Config, outputPath string) *Site {
	return &Site{db: db, Config: config, outputPath: outputPath}
}

// Dump all the site content to the disk
func (site Site) Write() {
	site.writeIndex()
	site.writeArticles()
	site.writePages()
}

func (site Site) writeIndex() {
	indexFile := fmt.Sprintf("%s/index.html", site.outputPath)

	template := template.Must(template.ParseFiles("templates/index.html", "templates/base.html"))

	file, err := os.Create(indexFile)
	if err != nil {
		log.Panic(err)
	}

	if err := template.ExecuteTemplate(file, "base", site); err != nil {
		log.Panic(err)
	}
}

func (site Site) Articles() (articles []*ParsedFile) {
	query := `
		SELECT title, slug, content, tags, date
		FROM files
		WHERE is_page = 0
		`
	rows, err := site.db.connection.Query(query)
	if err != nil {
		log.Panic(err)
	}
	for rows.Next() {
		article := ParsedFile{isPage: false}
		rows.Scan(&article.Title, &article.Slug, &article.Content, &article.tags, &article.Date)
		articles = append(articles, &article)
	}
	return articles
}

func (site Site) writeArticles() {
	for _, article := range site.Articles() {
		filePath := fmt.Sprintf("%s/%s.html", site.outputPath, article.Slug)

		template := template.Must(template.ParseFiles("templates/article.html", "templates/base.html"))

		file, err := os.Create(filePath)
		if err != nil {
			log.Panic(err)
		}
		site.Article = *article
		if err := template.ExecuteTemplate(file, "base", site); err != nil {
			log.Panic(err)
		}
	}

}

func (site Site) Pages() (pages []*ParsedFile) {
	query := `
		SELECT title, slug, content, tags, date
		FROM files
		WHERE is_page =1
		`
	rows, err := site.db.connection.Query(query)
	if err != nil {
		log.Panic(err)
	}
	for rows.Next() {
		page := ParsedFile{isPage: true}
		rows.Scan(&page.Title, &page.Slug, &page.Content, &page.tags, &page.Date)
		pages = append(pages, &page)
	}
	return pages
}

func (site Site) writePages() {
	// First of all create the pages/ folder if it doesn't exist
	pagesPath := fmt.Sprintf("%s/pages", site.outputPath)
	if _, err := os.Stat(pagesPath); os.IsNotExist(err) {
		os.Mkdir(pagesPath, 0777)
	}

	for _, page := range site.Pages() {
		filePath := fmt.Sprintf("%s/%s.html", pagesPath, page.Slug)

		template := template.Must(template.ParseFiles("templates/page.html", "templates/base.html"))

		file, err := os.Create(filePath)
		if err != nil {
			log.Panic(err)
		}
		site.Page = *page
		if err := template.ExecuteTemplate(file, "base", site); err != nil {
			log.Panic(err)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
  "errors"
  "strconv"
  "encoding/json"
  "github.com/parnurzeal/gorequest"
)

/*
 * {
 *   "name": "howdy-lb",
 *   "major_version": 1,
 *   "datacenter": "us-east-1",
 *   "namespace": "dev"
 * }
 */
type LoadbalancerCreate struct {
  Name string `json:"name"`
  MajorVersion int `json:"major_version"`
  Datacenter string `json:"datacenter"`
  Namespace string `json:"namespace"`
}

/*
 * {
 *   "name": "howdy-lb--1--974u8r6v",
 *   "routes": [
 *     ...
 *   ],
 *   "guid": "b74b8209468b",
 *   "deploy_time": 1481065235649,
 *   "datacenter": "us-east-1",
 *   "namespace": "dev"
 * }
 */
type Loadbalancer struct {
  Name string `json:"name"`
  Routes []LoadbalancerRoute `json:"routes"`
  Guid string `json:"guid"`
  DeployTime int64 `json:"deploy_time"`
  Datacenter string `json:"datacenter"`
  NamespaceRef string `json:"namespace"`
}

/*
 * {
 *   "backend_port_reference": "default",
 *   "backend_major_version": 1,
 *   "backend_name": "howdy-http",
 *   "lb_port": 8444
 * }
*/
type LoadbalancerRoute struct {
  BackendPortReference string `json:"backend_port_reference"`
  BackendMajorVersion int `json:"backend_major_version"`
  BackendName string `json:"backend_name"`
  LBPort int `json:"lb_port"`
}

//////////////////////// LIST ////////////////////////

func ListLoadbalancers(delimitedDcs string, delimitedNamespaces string, delimitedStatuses string, http *gorequest.SuperAgent, cfg *Config) (list []Loadbalancer, err []error){
  uri := "/v1/loadbalancers?"
  // set the datacenters if specified
  if (isValidCommaDelimitedList(delimitedDcs)){
    uri = uri+"dc="+delimitedDcs+"&"
  }
  if (isValidCommaDelimitedList(delimitedNamespaces)){
    uri = uri+"ns="+delimitedNamespaces
  } else {
    uri = uri+"ns=dev,qa,prod"
  }

  r, bytes, errs := AugmentRequest(
    http.Get(cfg.Endpoint+uri), cfg).EndBytes()

  if r != nil {
    if (r.StatusCode / 100 != 2){
      errs = append(errs, errors.New("bad response from Nelson server"))
      return nil, errs
    } else {
      var list []Loadbalancer
      if err := json.Unmarshal(bytes, &list); err != nil {
        panic(err)
      }
      return list, errs
    }
  } else {
    errs = append(errs, errors.New("No response from the Nelson server, aborting."))
    return nil, errs
  }
}

func PrintListLoadbalancers(lb []Loadbalancer){
  var tabulized = [][]string {}
  for _,l := range lb {
    routes := ""
    for i,r := range l.Routes {
      // 8443 ~> howdy-http@1->default
      routes = routes + strconv.Itoa(r.LBPort)+" ~> "+r.BackendName+"@"+strconv.Itoa(r.BackendMajorVersion)+"->"+r.BackendPortReference

      // if not the last element, lets bang on a comma
      if(i == len(l.Routes)){ routes = routes + ", " }
    }
    tabulized = append(tabulized,[]string{ l.Guid, l.Datacenter, l.NamespaceRef, l.Name, routes })
  }

  RenderTableToStdout([]string{ "GUID",  "Datacenter", "Namespace", "Name", "Routes"}, tabulized)
}

//////////////////////// REMOVE ////////////////////////

func RemoveLoadBalancer(guid string, http *gorequest.SuperAgent, cfg *Config) (str string, err []error){
  r, body, errs := AugmentRequest(
    http.Delete(cfg.Endpoint+"/v1/loadbalancers/"+guid), cfg).EndBytes()

  if (r.StatusCode / 100 != 2){
    resp := string(body[:])
    errs = append(errs, errors.New("Unexpected response from Nelson server"))
    return resp, errs
  } else {
    return "Requested removal of "+guid, errs
  }
}

//////////////////////// CREATE ////////////////////////

func CreateLoadBalancer(req LoadbalancerCreate, http *gorequest.SuperAgent, cfg *Config) (str string, err []error){
  r, body, errs := AugmentRequest(
    http.Post(cfg.Endpoint+"/v1/loadbalancers"), cfg).Send(req).EndBytes()

  if (r.StatusCode / 100 != 2){
    resp := string(body[:])
    errs = append(errs, errors.New("Unexpected response from Nelson server"))
    return resp, errs
  } else {
    return "Loadbalancer has been created.", errs
  }
}
<commit_msg>add support for LB addresses in the response <commit_after>package main

import (
  "errors"
  "strconv"
  "encoding/json"
  "github.com/parnurzeal/gorequest"
)

/*
 * {
 *   "name": "howdy-lb",
 *   "major_version": 1,
 *   "datacenter": "us-east-1",
 *   "namespace": "dev"
 * }
 */
type LoadbalancerCreate struct {
  Name string `json:"name"`
  MajorVersion int `json:"major_version"`
  Datacenter string `json:"datacenter"`
  Namespace string `json:"namespace"`
}

/*
 * {
 *   "name": "howdy-lb--1--974u8r6v",
 *   "routes": [
 *     ...
 *   ],
 *   "guid": "b74b8209468b",
 *   "deploy_time": 1481065235649,
 *   "datacenter": "us-east-1",
 *   "namespace": "dev"
 * }
 */
type Loadbalancer struct {
  Name string `json:"name"`
  Routes []LoadbalancerRoute `json:"routes"`
  Guid string `json:"guid"`
  DeployTime int64 `json:"deploy_time"`
  Datacenter string `json:"datacenter"`
  NamespaceRef string `json:"namespace"`
  Address string `json:"address"`
}

/*
 * {
 *   "backend_port_reference": "default",
 *   "backend_major_version": 1,
 *   "backend_name": "howdy-http",
 *   "lb_port": 8444
 * }
*/
type LoadbalancerRoute struct {
  BackendPortReference string `json:"backend_port_reference"`
  BackendMajorVersion int `json:"backend_major_version"`
  BackendName string `json:"backend_name"`
  LBPort int `json:"lb_port"`
}

//////////////////////// LIST ////////////////////////

func ListLoadbalancers(delimitedDcs string, delimitedNamespaces string, delimitedStatuses string, http *gorequest.SuperAgent, cfg *Config) (list []Loadbalancer, err []error){
  uri := "/v1/loadbalancers?"
  // set the datacenters if specified
  if (isValidCommaDelimitedList(delimitedDcs)){
    uri = uri+"dc="+delimitedDcs+"&"
  }
  if (isValidCommaDelimitedList(delimitedNamespaces)){
    uri = uri+"ns="+delimitedNamespaces
  } else {
    uri = uri+"ns=dev,qa,prod"
  }

  r, bytes, errs := AugmentRequest(
    http.Get(cfg.Endpoint+uri), cfg).EndBytes()

  if r != nil {
    if (r.StatusCode / 100 != 2){
      errs = append(errs, errors.New("bad response from Nelson server"))
      return nil, errs
    } else {
      var list []Loadbalancer
      if err := json.Unmarshal(bytes, &list); err != nil {
        panic(err)
      }
      return list, errs
    }
  } else {
    errs = append(errs, errors.New("No response from the Nelson server, aborting."))
    return nil, errs
  }
}

func PrintListLoadbalancers(lb []Loadbalancer){
  var tabulized = [][]string {}
  for _,l := range lb {
    routes := ""
    for i,r := range l.Routes {
      // 8443 ~> howdy-http@1->default
      routes = routes + strconv.Itoa(r.LBPort)+" ~> "+r.BackendName+"@"+strconv.Itoa(r.BackendMajorVersion)+"->"+r.BackendPortReference

      // if not the last element, lets bang on a comma
      if(i == len(l.Routes)){ routes = routes + ", " }
    }
    tabulized = append(tabulized,[]string{ l.Guid, l.Datacenter, l.NamespaceRef, l.Name, routes, l.Address })
  }

  RenderTableToStdout([]string{ "GUID",  "Datacenter", "Namespace", "Name", "Routes", "Address"}, tabulized)
}

//////////////////////// REMOVE ////////////////////////

func RemoveLoadBalancer(guid string, http *gorequest.SuperAgent, cfg *Config) (str string, err []error){
  r, body, errs := AugmentRequest(
    http.Delete(cfg.Endpoint+"/v1/loadbalancers/"+guid), cfg).EndBytes()

  if (r.StatusCode / 100 != 2){
    resp := string(body[:])
    errs = append(errs, errors.New("Unexpected response from Nelson server"))
    return resp, errs
  } else {
    return "Requested removal of "+guid, errs
  }
}

//////////////////////// CREATE ////////////////////////

func CreateLoadBalancer(req LoadbalancerCreate, http *gorequest.SuperAgent, cfg *Config) (str string, err []error){
  r, body, errs := AugmentRequest(
    http.Post(cfg.Endpoint+"/v1/loadbalancers"), cfg).Send(req).EndBytes()

  if (r.StatusCode / 100 != 2){
    resp := string(body[:])
    errs = append(errs, errors.New("Unexpected response from Nelson server"))
    return resp, errs
  } else {
    return "Loadbalancer has been created.", errs
  }
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"fmt"
	"os"
	"path"

	"github.com/codegangsta/cli"
	"github.com/rackspace/gophercloud"
)

// Name is the name of the CLI
var name = "rack"

// Version is the current CLI version
var Version = "0.0.0-dev"

// Usage return a string that specifies how to call a particular command.
func Usage(commandPrefix, action, mandatoryFlags string) string {
	return fmt.Sprintf("%s [GLOBALS] %s %s %s [OPTIONS]", name, commandPrefix, action, mandatoryFlags)
}

// Contains checks whether a given string is in a provided slice of strings.
func Contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// RackDir returns the location of the `rack` directory. This directory is for
// storing `rack`-specific information such as the cache or a config file.
func RackDir() (string, error) {
	homeDir := os.Getenv("HOME") // *nix
	if homeDir == "" {           // Windows
		homeDir = os.Getenv("USERPROFILE")
	}
	if homeDir == "" {
		return "", errors.New("User home directory not found.")
	}

	return path.Join(homeDir, ".rack"), nil
}

// CheckArgNum checks that the provided number of arguments has the same
// cardinality as the expected number of arguments.
func CheckArgNum(c *cli.Context, expected int) error {
	argsLen := len(c.Args())
	if argsLen != expected {
		return fmt.Errorf("Expected %d args but got %d\nUsage: %s", expected, argsLen, c.Command.Usage)
	}
	return nil
}

// CheckFlagsSet checks that the given flag names are set for the command.
func CheckFlagsSet(c *cli.Context, flagNames []string) error {
	for _, flagName := range flagNames {
		if !c.IsSet(flagName) {
			return Error(c, ErrMissingFlag{
				Msg: fmt.Sprintf("--%s is required.", flagName),
			})
		}
	}
	return nil
}

// IDOrName is a function for retrieving a resources unique identifier based on
// whether he or she passed an `id` or a `name` flag.
func IDOrName(c *cli.Context, client *gophercloud.ServiceClient, idFromName func(*gophercloud.ServiceClient, string) (string, error)) (string, error) {
	if c.IsSet("id") {
		return c.String("id"), nil
	} else if c.IsSet("name") {
		name := c.String("name")
		id, err := idFromName(client, name)
		if err != nil {
			return "", fmt.Errorf("Error converting name [%s] to ID: %s", name, err)
		}
		return id, nil
	} else {
		return "", Error(c, ErrMissingFlag{
			Msg: "One of either --id or --name must be provided.",
		})
	}
}

// IDAndNameFlags are flags for commands that allow either an ID or a name.
var IDAndNameFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "id",
		Usage: "[optional; required if 'name' is not provided] The ID of the resource",
	},
	cli.StringFlag{
		Name:  "name",
		Usage: "[optional; required if 'id' is not provided] The name of the resource",
	},
}

// IDOrNameUsage returns flag usage information for resources that allow either
// an ID or a name.
func IDOrNameUsage(resource string) string {
	return fmt.Sprintf("[--id <%sID> | --name <%sName>]", resource, resource)
}
<commit_msg>create .rack directory if not exists<commit_after>package util

import (
	"errors"
	"fmt"
	"os"
	"path"

	"github.com/codegangsta/cli"
	"github.com/rackspace/gophercloud"
)

// Name is the name of the CLI
var name = "rack"

// Version is the current CLI version
var Version = "0.0.0-dev"

// Usage return a string that specifies how to call a particular command.
func Usage(commandPrefix, action, mandatoryFlags string) string {
	return fmt.Sprintf("%s [GLOBALS] %s %s %s [OPTIONS]", name, commandPrefix, action, mandatoryFlags)
}

// Contains checks whether a given string is in a provided slice of strings.
func Contains(s []string, e string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// RackDir returns the location of the `rack` directory. This directory is for
// storing `rack`-specific information such as the cache or a config file.
func RackDir() (string, error) {
	homeDir := os.Getenv("HOME") // *nix
	if homeDir == "" {           // Windows
		homeDir = os.Getenv("USERPROFILE")
	}
	if homeDir == "" {
		return "", errors.New("User home directory not found.")
	}
	dirpath := path.Join(homeDir, ".rack")
	err := os.MkdirAll(dirpath, 0644)
	return dirpath, err
}

// CheckArgNum checks that the provided number of arguments has the same
// cardinality as the expected number of arguments.
func CheckArgNum(c *cli.Context, expected int) error {
	argsLen := len(c.Args())
	if argsLen != expected {
		return fmt.Errorf("Expected %d args but got %d\nUsage: %s", expected, argsLen, c.Command.Usage)
	}
	return nil
}

// CheckFlagsSet checks that the given flag names are set for the command.
func CheckFlagsSet(c *cli.Context, flagNames []string) error {
	for _, flagName := range flagNames {
		if !c.IsSet(flagName) {
			return Error(c, ErrMissingFlag{
				Msg: fmt.Sprintf("--%s is required.", flagName),
			})
		}
	}
	return nil
}

// IDOrName is a function for retrieving a resources unique identifier based on
// whether he or she passed an `id` or a `name` flag.
func IDOrName(c *cli.Context, client *gophercloud.ServiceClient, idFromName func(*gophercloud.ServiceClient, string) (string, error)) (string, error) {
	if c.IsSet("id") {
		return c.String("id"), nil
	} else if c.IsSet("name") {
		name := c.String("name")
		id, err := idFromName(client, name)
		if err != nil {
			return "", fmt.Errorf("Error converting name [%s] to ID: %s", name, err)
		}
		return id, nil
	} else {
		return "", Error(c, ErrMissingFlag{
			Msg: "One of either --id or --name must be provided.",
		})
	}
}

// IDAndNameFlags are flags for commands that allow either an ID or a name.
var IDAndNameFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "id",
		Usage: "[optional; required if 'name' is not provided] The ID of the resource",
	},
	cli.StringFlag{
		Name:  "name",
		Usage: "[optional; required if 'id' is not provided] The name of the resource",
	},
}

// IDOrNameUsage returns flag usage information for resources that allow either
// an ID or a name.
func IDOrNameUsage(resource string) string {
	return fmt.Sprintf("[--id <%sID> | --name <%sName>]", resource, resource)
}
<|endoftext|>