<commit_before>package main

import (
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/gorilla/websocket"
	"github.com/mediocregopher/lever"
)

var (
	listenAddr      string
	etcdAPIs        []string
	dnsRoot         string
	defaultCategory string
	timeout         time.Duration
	etcdClient      *etcd.Client
)

func main() {
	l := lever.New("skyapi", nil)
	l.Add(lever.Param{
		Name:        "--listen-addr",
		Description: "Address to listen for websocket connections on",
		Default:     ":8053",
	})
	l.Add(lever.Param{
		Name:         "--etcd-api",
		Description:  "scheme://address an etcd node in the cluster can be found on. Can be specified multiple times",
		DefaultMulti: []string{"http://127.0.0.1:4001"},
	})
	l.Add(lever.Param{
		Name:        "--dns-root",
		Description: "Root domain that SkyDNS is serving on",
		Default:     "skydns.local.",
	})
	l.Add(lever.Param{
		Name:        "--timeout",
		Description: "The TTL for entries in SkyDNS, in seconds. The server will ping at half this value, the client should also",
		Default:     "30",
	})
	l.Add(lever.Param{
		Name:        "--default-category",
		Description: "The default category to file incoming connections over, if they don't specify",
		Default:     "services",
	})
	l.Parse()

	listenAddr, _ = l.ParamStr("--listen-addr")
	etcdAPIs, _ = l.ParamStrs("--etcd-api")
	dnsRoot, _ = l.ParamStr("--dns-root")
	timeoutSecs, _ := l.ParamInt("--timeout")
	timeout = time.Duration(timeoutSecs) * time.Second
	defaultCategory, _ = l.ParamStr("--default-category")

	etcdClient = etcd.NewClient(etcdAPIs)

	http.Handle("/provide", http.HandlerFunc(handler))
	log.Printf("listening on %s", listenAddr)
	log.Fatal(http.ListenAndServe(listenAddr, nil))
}

var upgrader = websocket.Upgrader{
	// Buffer sizes are 0 because nothing should ever be read or written
	ReadBufferSize:  0,
	WriteBufferSize: 0,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type connData struct {
	id                string
	service, category string
	Host              string `json:"host"`
	Port              int    `json:"port,omitempty"`
	Priority          int    `json:"priority,omitempty"`
	Weight            int    `json:"weight,omitempty"`
}

func (cd connData) sprintf(s string, args ...interface{}) string {
	realArgs := make([]interface{}, 0, len(args)+5)
	realArgs = append(realArgs, cd.id, cd.service, cd.category, cd.Host, cd.Port)
	realArgs = append(realArgs, args...)
	return fmt.Sprintf("[%s - %s.%s] %s:%d - "+s, realArgs...)
}

func (cd connData) toPath() (string, string) {
	parts := strings.Split(dnsRoot, ".")
	partsR := append(make([]string, 0, len(parts)+2), "/skydns")
	for i := len(parts) - 1; i >= 0; i-- {
		partsR = append(partsR, parts[i])
	}
	partsR = append(partsR, cd.category, cd.service)
	dir := path.Join(partsR...)
	file := path.Join(dir, cd.id)
	return dir, file
}

func errorMessagef(conn *websocket.Conn, s string, args ...interface{}) {
	msg := fmt.Sprintf("ERROR: "+s, args...)
	conn.WriteMessage(websocket.TextMessage, []byte(msg))
}

func handler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("error upgrading: %s", err)
		return
	}
	closeCh := make(chan struct{})
	go readDiscard(conn, closeCh)
	defer conn.Close()

	cd, err := parseConnData(r)
	if err != nil {
		log.Printf("parseConnData: %s", err)
		errorMessagef(conn, "parseConnData: %s", err)
		return
	}

	defer log.Print(cd.sprintf("closed"))
	defer etcdDelete(cd)
	log.Print(cd.sprintf("connected"))

	tick := time.Tick(timeout / 2)
	if !doTick(conn, cd) {
		return
	}
	for {
		select {
		case <-tick:
			if !doTick(conn, cd) {
				return
			}
		case <-closeCh:
			return
		}
	}
}

func doTick(conn *websocket.Conn, cd connData) bool {
	deadline := time.Now().Add(timeout / 2)
	err := conn.WriteControl(websocket.PingMessage, nil, deadline)
	if err != nil {
		log.Print(cd.sprintf("timedout"))
		return false
	}

	if err = etcdStore(cd); err != nil {
		log.Print(cd.sprintf("storing etcd data: %s", err))
		errorMessagef(conn, "storing etcd data: %s", err)
		return false
	}
	return true
}

func readDiscard(conn *websocket.Conn, closeCh chan struct{}) {
	for {
		if _, _, err := conn.NextReader(); err != nil {
			close(closeCh)
			return
		}
	}
}

func parseConnData(r *http.Request) (connData, error) {
	service := r.FormValue("service")
	category := r.FormValue("category")
	host := r.FormValue("host")
	portStr := r.FormValue("port")
	var port int
	var err error

	if service == "" {
		err = fmt.Errorf("service and port are required parameters")
		return connData{}, err
	}

	if category == "" {
		category = defaultCategory
	}

	if host == "" {
		host = r.RemoteAddr[:strings.Index(r.RemoteAddr, ":")]
	}

	if portStr != "" {
		port, err = strconv.Atoi(portStr)
		if err != nil {
			return connData{}, err
		}
	}

	var priority int
	priorityStr, weightStr := r.FormValue("priority"), r.FormValue("weight")
	if priorityStr != "" {
		if priority, err = strconv.Atoi(priorityStr); err != nil {
			return connData{}, err
		}
	}

	var weight int
	if weightStr != "" {
		if weight, err = strconv.Atoi(weightStr); err != nil {
			return connData{}, err
		}
	}

	sha := sha1.New()
	fmt.Fprint(sha, service)
	fmt.Fprint(sha, category)
	fmt.Fprint(sha, host)
	fmt.Fprint(sha, port)
	id := hex.EncodeToString(sha.Sum(nil))

	return connData{
		id:       id,
		service:  service,
		category: category,
		Host:     host,
		Port:     port,
		Priority: priority,
		Weight:   weight,
	}, nil
}

// Creates the given dir (and all of its parent directories if they don't
// already exist). Will not return an error if the given directory already
// exists
func MkDirP(ec *etcd.Client, dir string) error {
	parts := make([]string, 0, 4)
	for {
		parts = append(parts, dir)
		dir = path.Dir(dir)
		if dir == "/" {
			break
		}
	}

	for i := range parts {
		ai := len(parts) - i - 1
		_, err := ec.CreateDir(parts[ai], 0)
		if err != nil && err.(*etcd.EtcdError).ErrorCode != 105 {
			return err
		}
	}
	return nil
}

func etcdStore(cd connData) error {
	dir, file := cd.toPath()
	if err := MkDirP(etcdClient, dir); err != nil {
		return err
	}

	j, err := json.Marshal(cd)
	if err != nil {
		return err
	}

	_, err = etcdClient.Set(file, string(j), uint64(timeout.Seconds()))
	if err != nil {
		return err
	}

	return nil
}

func etcdDelete(cd connData) error {
	_, file := cd.toPath()
	_, err := etcdClient.Delete(file, false)
	return err
}
<commit_msg>etcdClient might not actually be thread-safe.... ooooops<commit_after>package main

import (
	"crypto/sha1"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"path"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/coreos/go-etcd/etcd"
	"github.com/gorilla/websocket"
	"github.com/mediocregopher/lever"
)

var (
	listenAddr      string
	etcdAPIs        []string
	dnsRoot         string
	defaultCategory string
	timeout         time.Duration
	etcdClient      *etcd.Client
	etcdClientLock  sync.Mutex
)

func main() {
	l := lever.New("skyapi", nil)
	l.Add(lever.Param{
		Name:        "--listen-addr",
		Description: "Address to listen for websocket connections on",
		Default:     ":8053",
	})
	l.Add(lever.Param{
		Name:         "--etcd-api",
		Description:  "scheme://address an etcd node in the cluster can be found on. Can be specified multiple times",
		DefaultMulti: []string{"http://127.0.0.1:4001"},
	})
	l.Add(lever.Param{
		Name:        "--dns-root",
		Description: "Root domain that SkyDNS is serving on",
		Default:     "skydns.local.",
	})
	l.Add(lever.Param{
		Name:        "--timeout",
		Description: "The TTL for entries in SkyDNS, in seconds. The server will ping at half this value, the client should also",
		Default:     "30",
	})
	l.Add(lever.Param{
		Name:        "--default-category",
		Description: "The default category to file incoming connections over, if they don't specify",
		Default:     "services",
	})
	l.Parse()

	listenAddr, _ = l.ParamStr("--listen-addr")
	etcdAPIs, _ = l.ParamStrs("--etcd-api")
	dnsRoot, _ = l.ParamStr("--dns-root")
	timeoutSecs, _ := l.ParamInt("--timeout")
	timeout = time.Duration(timeoutSecs) * time.Second
	defaultCategory, _ = l.ParamStr("--default-category")

	etcdClientLock.Lock()
	etcdClient = etcd.NewClient(etcdAPIs)
	etcdClientLock.Unlock()

	http.Handle("/provide", http.HandlerFunc(handler))
	log.Printf("listening on %s", listenAddr)
	log.Fatal(http.ListenAndServe(listenAddr, nil))
}

var upgrader = websocket.Upgrader{
	// Buffer sizes are 0 because nothing should ever be read or written
	ReadBufferSize:  0,
	WriteBufferSize: 0,
	CheckOrigin: func(r *http.Request) bool {
		return true
	},
}

type connData struct {
	id                string
	service, category string
	Host              string `json:"host"`
	Port              int    `json:"port,omitempty"`
	Priority          int    `json:"priority,omitempty"`
	Weight            int    `json:"weight,omitempty"`
}

func (cd connData) sprintf(s string, args ...interface{}) string {
	realArgs := make([]interface{}, 0, len(args)+5)
	realArgs = append(realArgs, cd.id, cd.service, cd.category, cd.Host, cd.Port)
	realArgs = append(realArgs, args...)
	return fmt.Sprintf("[%s - %s.%s] %s:%d - "+s, realArgs...)
}

func (cd connData) toPath() (string, string) {
	parts := strings.Split(dnsRoot, ".")
	partsR := append(make([]string, 0, len(parts)+2), "/skydns")
	for i := len(parts) - 1; i >= 0; i-- {
		partsR = append(partsR, parts[i])
	}
	partsR = append(partsR, cd.category, cd.service)
	dir := path.Join(partsR...)
	file := path.Join(dir, cd.id)
	return dir, file
}

func errorMessagef(conn *websocket.Conn, s string, args ...interface{}) {
	msg := fmt.Sprintf("ERROR: "+s, args...)
	conn.WriteMessage(websocket.TextMessage, []byte(msg))
}

func handler(w http.ResponseWriter, r *http.Request) {
	conn, err := upgrader.Upgrade(w, r, nil)
	if err != nil {
		log.Printf("error upgrading: %s", err)
		return
	}
	closeCh := make(chan struct{})
	go readDiscard(conn, closeCh)
	defer conn.Close()

	cd, err := parseConnData(r)
	if err != nil {
		log.Printf("parseConnData: %s", err)
		errorMessagef(conn, "parseConnData: %s", err)
		return
	}

	defer log.Print(cd.sprintf("closed"))
	defer etcdDelete(cd)
	log.Print(cd.sprintf("connected"))

	tick := time.Tick(timeout / 2)
	if !doTick(conn, cd) {
		return
	}
	for {
		select {
		case <-tick:
			if !doTick(conn, cd) {
				return
			}
		case <-closeCh:
			return
		}
	}
}

func doTick(conn *websocket.Conn, cd connData) bool {
	deadline := time.Now().Add(timeout / 2)
	err := conn.WriteControl(websocket.PingMessage, nil, deadline)
	if err != nil {
		log.Print(cd.sprintf("timedout"))
		return false
	}

	if err = etcdStore(cd); err != nil {
		log.Print(cd.sprintf("storing etcd data: %s", err))
		errorMessagef(conn, "storing etcd data: %s", err)
		return false
	}
	return true
}

func readDiscard(conn *websocket.Conn, closeCh chan struct{}) {
	for {
		if _, _, err := conn.NextReader(); err != nil {
			close(closeCh)
			return
		}
	}
}

func parseConnData(r *http.Request) (connData, error) {
	service := r.FormValue("service")
	category := r.FormValue("category")
	host := r.FormValue("host")
	portStr := r.FormValue("port")
	var port int
	var err error

	if service == "" {
		err = fmt.Errorf("service and port are required parameters")
		return connData{}, err
	}

	if category == "" {
		category = defaultCategory
	}

	if host == "" {
		host = r.RemoteAddr[:strings.Index(r.RemoteAddr, ":")]
	}

	if portStr != "" {
		port, err = strconv.Atoi(portStr)
		if err != nil {
			return connData{}, err
		}
	}

	var priority int
	priorityStr, weightStr := r.FormValue("priority"), r.FormValue("weight")
	if priorityStr != "" {
		if priority, err = strconv.Atoi(priorityStr); err != nil {
			return connData{}, err
		}
	}

	var weight int
	if weightStr != "" {
		if weight, err = strconv.Atoi(weightStr); err != nil {
			return connData{}, err
		}
	}

	sha := sha1.New()
	fmt.Fprint(sha, service)
	fmt.Fprint(sha, category)
	fmt.Fprint(sha, host)
	fmt.Fprint(sha, port)
	id := hex.EncodeToString(sha.Sum(nil))

	return connData{
		id:       id,
		service:  service,
		category: category,
		Host:     host,
		Port:     port,
		Priority: priority,
		Weight:   weight,
	}, nil
}

// Creates the given dir (and all of its parent directories if they don't
// already exist). Will not return an error if the given directory already
// exists
func MkDirP(ec *etcd.Client, dir string) error {
	parts := make([]string, 0, 4)
	for {
		parts = append(parts, dir)
		dir = path.Dir(dir)
		if dir == "/" {
			break
		}
	}

	for i := range parts {
		ai := len(parts) - i - 1
		_, err := ec.CreateDir(parts[ai], 0)
		if err != nil && err.(*etcd.EtcdError).ErrorCode != 105 {
			return err
		}
	}
	return nil
}

func etcdStore(cd connData) error {
	etcdClientLock.Lock()
	defer etcdClientLock.Unlock()

	dir, file := cd.toPath()
	if err := MkDirP(etcdClient, dir); err != nil {
		return err
	}

	j, err := json.Marshal(cd)
	if err != nil {
		return err
	}

	_, err = etcdClient.Set(file, string(j), uint64(timeout.Seconds()))
	if err != nil {
		return err
	}

	return nil
}

func etcdDelete(cd connData) error {
	_, file := cd.toPath()

	etcdClientLock.Lock()
	_, err := etcdClient.Delete(file, false)
	etcdClientLock.Unlock()

	return err
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"math/rand"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/jessevdk/go-flags"
	"github.com/yosisa/craft/config"
	"github.com/yosisa/craft/docker"
	"github.com/yosisa/craft/filter"
	"github.com/yosisa/craft/rpc"
)

type GlobalOptions struct {
	Config string `short:"c" long:"config" description:"Configuration file"`
	Filter string `short:"F" long:"filter" description:"Filter target agents"`
	conf   *config.Config
}

func (opts *GlobalOptions) ParseConfig() *config.Config {
	var err error
	if opts.conf, err = config.Parse(opts.Config); err != nil {
		log.WithField("error", err).Fatal("Could not parse config file")
	}
	return opts.conf
}

func (opts *GlobalOptions) agents() []string {
	if opts.conf == nil {
		opts.ParseConfig()
	}
	v, err := filterAgents(opts.conf.Agents, opts.Filter)
	if err != nil {
		log.WithField("error", err).Fatal("Failed to filter target agents")
	}
	return v
}

func filterAgents(agents []string, s string) ([]string, error) {
	if s == "" {
		return agents, nil
	}
	expr, err := filter.Parse(s)
	if err != nil {
		return nil, err
	}
	caps := gatherCapabilities(agents)
	caps.Filter(func(cap *rpc.Capability) bool {
		return expr.Eval(cap)
	})
	var out []string
	for agent := range caps {
		out = append(out, agent)
	}
	return out, nil
}

var (
	gopts  GlobalOptions
	parser = flags.NewParser(&gopts, flags.Default)
)

func main() {
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}
}

type Capabilities map[string]*rpc.Capability

func (c Capabilities) Filter(f func(*rpc.Capability) bool) {
	for agent, cap := range c {
		if !f(cap) {
			delete(c, agent)
		}
	}
}

func (c Capabilities) Copy() Capabilities {
	caps := make(Capabilities, len(c))
	for agent, cap := range c {
		caps[agent] = cap
	}
	return caps
}

func gatherCapabilities(agents []string) Capabilities {
	var wg sync.WaitGroup
	wg.Add(len(agents))
	caps := make(Capabilities)
	for _, agent := range agents {
		func(agent string) {
			defer wg.Done()
			c, err := rpc.Dial("tcp", agent)
			if err != nil {
				log.WithFields(log.Fields{"error": err, "agent": agent}).Error("Failed to connect agent")
				return
			}
			defer c.Close()

			var cap rpc.Capability
			if err = c.Call("Craft.Capability", rpc.Empty{}, &cap); err != nil {
				log.WithFields(log.Fields{"error": err, "method": "Craft.Capability"}).Error("RPC failed")
				return
			}
			if !cap.Available {
				log.WithField("agent", agent).Info("Temporary unavailable")
				return
			}
			caps[agent] = &cap
		}(agent)
	}
	wg.Wait()
	return caps
}

func findBestAgent(m *docker.Manifest, caps Capabilities) string {
	// Check existence of a container to be replaced
	if m.Replace != "" {
		caps.Filter(func(cap *rpc.Capability) bool {
			return stringSlice(cap.AllNames).Contains(m.Replace)
		})
	}

	// Check availability of name
	caps2 := caps.Copy()
	caps.Filter(func(cap *rpc.Capability) bool {
		return !stringSlice(cap.UsedNames).Contains(m.Name)
	})
	if len(caps) == 0 && m.Name == m.Replace {
		caps = caps2
	}

	// Check availability of ports
	caps.Filter(func(cap *rpc.Capability) bool {
		for _, p := range m.Ports {
			becomeAvailable, ok := cap.Containers[m.Replace]
			if ok && portSpecSlice(becomeAvailable.Ports).Contains(p.HostPort) {
				continue
			}
			if int64Slice(cap.UsedPorts).Contains(p.HostPort) {
				return false
			}
		}
		return true
	})

	// Check existence of containers to be linked
	caps.Filter(func(cap *rpc.Capability) bool {
		for _, link := range m.Links {
			if !stringSlice(cap.UsedNames).Contains(link.Name) {
				return false
			}
		}
		return true
	})

	// Check existence of a network container
	if strings.HasPrefix(m.NetworkMode, "container:") {
		name := m.NetworkMode[10:]
		caps.Filter(func(cap *rpc.Capability) bool {
			return stringSlice(cap.UsedNames).Contains(name)
		})
	}

	// Agent name restriction
	if m.Restrict.Agent != "" {
		re := regexp.MustCompile(m.Restrict.Agent)
		caps.Filter(func(cap *rpc.Capability) bool {
			return re.MatchString(cap.Agent)
		})
	}

	// Conflicts restriction
	for _, conflict := range m.Restrict.Conflicts {
		re := regexp.MustCompile(conflict)
		caps.Filter(func(cap *rpc.Capability) bool {
			return !stringSlice(cap.UsedNames).Match(re)
		})
	}

	// Choice agent that has least active containers
	running := 1024 * 1024 * 1024 // it's large enough
	var agent string
	for addr, cap := range caps {
		if n := len(cap.UsedNames); n < running {
			running = n
			agent = addr
		}
	}
	return agent
}

func resolveExLinks(m *docker.Manifest, caps Capabilities) ([]*rpc.ExLink, error) {
	var out []*rpc.ExLink
	for _, l := range m.ExLinks {
		caps2 := caps.Copy()
		caps2.Filter(func(cap *rpc.Capability) bool {
			return stringSlice(cap.UsedNames).Contains(l.Name) &&
				len(cap.IPAddrs) > 0
		})
		if len(caps2) == 0 {
			return nil, errors.New("No linkable containers")
		}
		_, cap := choice(caps2)
		ci := cap.Containers[l.Name]
		for _, port := range ci.Ports {
			addr := port.HostIP
			if addr == "0.0.0.0" {
				addr = cap.IPAddrs[0]
			}
			out = append(out, &rpc.ExLink{
				Name:    l.Alias,
				Exposed: string(port.Exposed),
				Addr:    addr,
				Port:    int(port.HostPort),
			})
		}
	}
	return out, nil
}

func choice(caps Capabilities) (string, *rpc.Capability) {
	var keys []string
	for k := range caps {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	n := rand.Intn(len(keys))
	return keys[n], caps[keys[n]]
}

type stringSlice []string

func (ss stringSlice) Contains(s string) bool {
	for _, v := range ss {
		if v == s {
			return true
		}
	}
	return false
}

func (ss stringSlice) Match(re *regexp.Regexp) bool {
	for _, v := range ss {
		if re.MatchString(v) {
			return true
		}
	}
	return false
}

type int64Slice []int64

func (is int64Slice) Contains(n int64) bool {
	for _, v := range is {
		if v == n {
			return true
		}
	}
	return false
}

type portSpecSlice []*docker.PortSpec

func (ps portSpecSlice) Contains(n int64) bool {
	for _, v := range ps {
		if v.HostPort == n {
			return true
		}
	}
	return false
}

func logRPCError(err error) {
	if err == nil {
		return
	}
	if re, ok := err.(rpc.Error); ok {
		re.Each(func(addr string, err error) {
			fields := log.Fields{"error": strings.TrimSpace(err.Error()), "agent": addr}
			log.WithFields(fields).Error("RPC error")
		})
	} else {
		log.WithField("error", err).Error("RPC error")
	}
}
<commit_msg>Agents and filter options can be set via env<commit_after>package main

import (
	"errors"
	"math/rand"
	"os"
	"regexp"
	"runtime"
	"sort"
	"strings"
	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/jessevdk/go-flags"
	"github.com/yosisa/craft/config"
	"github.com/yosisa/craft/docker"
	"github.com/yosisa/craft/filter"
	"github.com/yosisa/craft/rpc"
)

type GlobalOptions struct {
	Config string `short:"c" long:"config" description:"Configuration file"`
	Agents string `long:"agents" description:"Comma separated agent list" env:"CRAFT_AGENTS"`
	Filter string `short:"F" long:"filter" description:"Filter target agents" env:"CRAFT_FILTER"`
	conf   *config.Config
}

func (opts *GlobalOptions) ParseConfig() *config.Config {
	var err error
	if opts.conf, err = config.Parse(opts.Config); err != nil {
		log.WithField("error", err).Fatal("Could not parse config file")
	}
	if opts.Agents != "" {
		opts.conf.Agents = strings.Split(opts.Agents, ",")
	}
	return opts.conf
}

func (opts *GlobalOptions) agents() []string {
	if opts.conf == nil {
		opts.ParseConfig()
	}
	v, err := filterAgents(opts.conf.Agents, opts.Filter)
	if err != nil {
		log.WithField("error", err).Fatal("Failed to filter target agents")
	}
	return v
}

func filterAgents(agents []string, s string) ([]string, error) {
	if s == "" {
		return agents, nil
	}
	expr, err := filter.Parse(s)
	if err != nil {
		return nil, err
	}
	caps := gatherCapabilities(agents)
	caps.Filter(func(cap *rpc.Capability) bool {
		return expr.Eval(cap)
	})
	var out []string
	for agent := range caps {
		out = append(out, agent)
	}
	return out, nil
}

var (
	gopts  GlobalOptions
	parser = flags.NewParser(&gopts, flags.Default)
)

func main() {
	if os.Getenv("GOMAXPROCS") == "" {
		runtime.GOMAXPROCS(runtime.NumCPU())
	}
	if _, err := parser.Parse(); err != nil {
		os.Exit(1)
	}
}

type Capabilities map[string]*rpc.Capability

func (c Capabilities) Filter(f func(*rpc.Capability) bool) {
	for agent, cap := range c {
		if !f(cap) {
			delete(c, agent)
		}
	}
}

func (c Capabilities) Copy() Capabilities {
	caps := make(Capabilities, len(c))
	for agent, cap := range c {
		caps[agent] = cap
	}
	return caps
}

func gatherCapabilities(agents []string) Capabilities {
	var wg sync.WaitGroup
	wg.Add(len(agents))
	caps := make(Capabilities)
	for _, agent := range agents {
		func(agent string) {
			defer wg.Done()
			c, err := rpc.Dial("tcp", agent)
			if err != nil {
				log.WithFields(log.Fields{"error": err, "agent": agent}).Error("Failed to connect agent")
				return
			}
			defer c.Close()

			var cap rpc.Capability
			if err = c.Call("Craft.Capability", rpc.Empty{}, &cap); err != nil {
				log.WithFields(log.Fields{"error": err, "method": "Craft.Capability"}).Error("RPC failed")
				return
			}
			if !cap.Available {
				log.WithField("agent", agent).Info("Temporary unavailable")
				return
			}
			caps[agent] = &cap
		}(agent)
	}
	wg.Wait()
	return caps
}

func findBestAgent(m *docker.Manifest, caps Capabilities) string {
	// Check existence of a container to be replaced
	if m.Replace != "" {
		caps.Filter(func(cap *rpc.Capability) bool {
			return stringSlice(cap.AllNames).Contains(m.Replace)
		})
	}

	// Check availability of name
	caps2 := caps.Copy()
	caps.Filter(func(cap *rpc.Capability) bool {
		return !stringSlice(cap.UsedNames).Contains(m.Name)
	})
	if len(caps) == 0 && m.Name == m.Replace {
		caps = caps2
	}

	// Check availability of ports
	caps.Filter(func(cap *rpc.Capability) bool {
		for _, p := range m.Ports {
			becomeAvailable, ok := cap.Containers[m.Replace]
			if ok && portSpecSlice(becomeAvailable.Ports).Contains(p.HostPort) {
				continue
			}
			if int64Slice(cap.UsedPorts).Contains(p.HostPort) {
				return false
			}
		}
		return true
	})

	// Check existence of containers to be linked
	caps.Filter(func(cap *rpc.Capability) bool {
		for _, link := range m.Links {
			if !stringSlice(cap.UsedNames).Contains(link.Name) {
				return false
			}
		}
		return true
	})

	// Check existence of a network container
	if strings.HasPrefix(m.NetworkMode, "container:") {
		name := m.NetworkMode[10:]
		caps.Filter(func(cap *rpc.Capability) bool {
			return stringSlice(cap.UsedNames).Contains(name)
		})
	}

	// Agent name restriction
	if m.Restrict.Agent != "" {
		re := regexp.MustCompile(m.Restrict.Agent)
		caps.Filter(func(cap *rpc.Capability) bool {
			return re.MatchString(cap.Agent)
		})
	}

	// Conflicts restriction
	for _, conflict := range m.Restrict.Conflicts {
		re := regexp.MustCompile(conflict)
		caps.Filter(func(cap *rpc.Capability) bool {
			return !stringSlice(cap.UsedNames).Match(re)
		})
	}

	// Choice agent that has least active containers
	running := 1024 * 1024 * 1024 // it's large enough
	var agent string
	for addr, cap := range caps {
		if n := len(cap.UsedNames); n < running {
			running = n
			agent = addr
		}
	}
	return agent
}

func resolveExLinks(m *docker.Manifest, caps Capabilities) ([]*rpc.ExLink, error) {
	var out []*rpc.ExLink
	for _, l := range m.ExLinks {
		caps2 := caps.Copy()
		caps2.Filter(func(cap *rpc.Capability) bool {
			return stringSlice(cap.UsedNames).Contains(l.Name) &&
				len(cap.IPAddrs) > 0
		})
		if len(caps2) == 0 {
			return nil, errors.New("No linkable containers")
		}
		_, cap := choice(caps2)
		ci := cap.Containers[l.Name]
		for _, port := range ci.Ports {
			addr := port.HostIP
			if addr == "0.0.0.0" {
				addr = cap.IPAddrs[0]
			}
			out = append(out, &rpc.ExLink{
				Name:    l.Alias,
				Exposed: string(port.Exposed),
				Addr:    addr,
				Port:    int(port.HostPort),
			})
		}
	}
	return out, nil
}

func choice(caps Capabilities) (string, *rpc.Capability) {
	var keys []string
	for k := range caps {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	n := rand.Intn(len(keys))
	return keys[n], caps[keys[n]]
}

type stringSlice []string

func (ss stringSlice) Contains(s string) bool {
	for _, v := range ss {
		if v == s {
			return true
		}
	}
	return false
}

func (ss stringSlice) Match(re *regexp.Regexp) bool {
	for _, v := range ss {
		if re.MatchString(v) {
			return true
		}
	}
	return false
}

type int64Slice []int64

func (is int64Slice) Contains(n int64) bool {
	for _, v := range is {
		if v == n {
			return true
		}
	}
	return false
}

type portSpecSlice []*docker.PortSpec

func (ps portSpecSlice) Contains(n int64) bool {
	for _, v := range ps {
		if v.HostPort == n {
			return true
		}
	}
	return false
}

func logRPCError(err error) {
	if err == nil {
		return
	}
	if re, ok := err.(rpc.Error); ok {
		re.Each(func(addr string, err error) {
			fields := log.Fields{"error": strings.TrimSpace(err.Error()), "agent": addr}
			log.WithFields(fields).Error("RPC error")
		})
	} else {
		log.WithField("error", err).Error("RPC error")
	}
}
<|endoftext|>


<commit_before>package main

import (
	"runtime"

	"fmt"

	"github.com/getfider/fider/app/models"
	"github.com/getfider/fider/app/pkg/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

var buildtime string
var version = "0.9.0-beta"

func main() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())

	settings := &models.AppSettings{
		BuildTime:       buildtime,
		Version:         version,
		Compiler:        runtime.Version(),
		Environment:     env.Current(),
		GoogleAnalytics: env.GetEnvOrDefault("GOOGLE_ANALYTICS", ""),
		Mode:            env.Mode(),
		Domain:          env.MultiTenantDomain(),
	}

	e := GetMainEngine(settings)
	e.Start(":" + env.GetEnvOrDefault("PORT", "3000"))
}
<commit_msg>update version to 0.9.0 (#252)<commit_after>package main

import (
	"runtime"

	"fmt"

	"github.com/getfider/fider/app/models"
	"github.com/getfider/fider/app/pkg/env"
	_ "github.com/lib/pq"
	_ "github.com/mattes/migrate/database/postgres"
	_ "github.com/mattes/migrate/source/file"
)

var buildtime string
var version = "0.9.0"

func main() {
	fmt.Printf("Application is starting...\n")
	fmt.Printf("GO_ENV: %s\n", env.Current())

	settings := &models.AppSettings{
		BuildTime:       buildtime,
		Version:         version,
		Compiler:        runtime.Version(),
		Environment:     env.Current(),
		GoogleAnalytics: env.GetEnvOrDefault("GOOGLE_ANALYTICS", ""),
		Mode:            env.Mode(),
		Domain:          env.MultiTenantDomain(),
	}

	e := GetMainEngine(settings)
	e.Start(":" + env.GetEnvOrDefault("PORT", "3000"))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"

	flag "github.com/ogier/pflag"
)

func main() {
	// debug := flag.BoolP("debug", "d", false, "Print ")
	useUDP := flag.BoolP("udp", "u", false, "Send via UDP (will ignore TLS)")
	useTLS := flag.BoolP("tls", "s", true, "Connect with TLS")
	doTee := flag.BoolP("tee", "t", true, "Tee stdin to stdout")
	command := "forward [OPTIONS] host:port"
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "%s\n", command)
		fmt.Fprintf(os.Stderr, "Options:\n")
		flag.PrintDefaults()
	}
	flag.Parse()

	if len(flag.Args()) == 0 || !validDestination(flag.Arg(0)) {
		flag.Usage()
		return
	}

	destination := flag.Arg(0)

	var conn net.Conn
	var err error
	switch {
	case *useUDP:
		conn, err = net.Dial("udp", destination)
	case !*useTLS:
		conn, err = net.Dial("tcp", destination)
	default:
		conn, err = tls.Dial("tcp", destination, &tls.Config{})
	}
	connected := false
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could not connect to %s: %v\n", destination, err)
	} else {
		connected = true
		defer conn.Close()
	}

	writers := make([]*io.PipeWriter, 0, 2)
	wg := sync.WaitGroup{}
	if *doTee {
		stdOutReader, stdOutWriter := io.Pipe()
		writers = append(writers, stdOutWriter)
		go func() {
			wg.Add(1)
			io.Copy(os.Stdout, stdOutReader)
			wg.Done()
		}()
	}
	if connected {
		netReader, netWriter := io.Pipe()
		writers = append(writers, netWriter)
		go func() {
			wg.Add(1)
			reader := bufio.NewReader(netReader)
			for {
				data, err := reader.ReadBytes('\n')
				if err != nil {
					break
				}
				conn.Write(data)
			}
			io.Copy(ioutil.Discard, netReader)
			wg.Done()
		}()
	}

	if len(writers) == 0 {
		io.Copy(ioutil.Discard, os.Stdin)
		return
	}

	writeOnly := make([]io.Writer, 0, len(writers))
	for _, w := range writers {
		writeOnly = append(writeOnly, w)
	}
	mw := io.MultiWriter(writeOnly...)
	io.Copy(mw, os.Stdin)
	for _, w := range writers {
		w.Close()
	}

	wg.Wait()
}

// Basic validation test
func validDestination(d string) bool {
	if len(d) == 0 {
		return false
	}

	s := strings.Split(d, ":")
	if len(s) != 2 || len(s[0]) == 0 || len(s[1]) == 0 {
		return false
	}

	if _, err := strconv.Atoi(s[1]); err != nil {
		return false
	}

	return true
}
<commit_msg>Changed flag package, added basic syslog wrapping<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/codegangsta/cli"
)

var (
	useUDP bool
	useTLS bool
	doTee  bool

	syslog         bool
	syslogHostname string
	syslogApp      string
)

func main() {
	app := cli.NewApp()
	app.Name = "forward"
	app.Usage = "Transport StdIn lines to a remote destination over UDP, TCP, or TCP+TLS"
	app.UsageText = "forward [global options] [syslog [syslog options]] address:port"
	app.Flags = []cli.Flag{
		cli.BoolFlag{
			Name:        "udp, u",
			Usage:       "Send via UDP (will ignore TLS)",
			Destination: &useUDP,
		},
		cli.BoolTFlag{
			Name:        "tls, s",
			Usage:       "TLS-secured TCP connection",
			Destination: &useTLS,
		},
		cli.BoolTFlag{
			Name:        "tee, t",
			Usage:       "Tee stdin to stdout",
			Destination: &doTee,
		},
	}
	app.Action = func(c *cli.Context) {
		forward(c.Args().First())
	}

	h, _ := os.Hostname()
	app.Commands = []cli.Command{
		{
			Name:      "syslog",
			Aliases:   []string{"log"},
			Usage:     "Wrap lines in RFC-5424 Syslog format",
			ArgsUsage: "address:port",
			Action: func(c *cli.Context) {
				syslog = true
				forward(c.Args().First())
			},
			Flags: []cli.Flag{
				cli.StringFlag{
					Name:        "hostname, n",
					Value:       h,
					Destination: &syslogHostname,
				},
				cli.StringFlag{
					Name:        "app, a",
					Value:       "app",
					Destination: &syslogApp,
				},
			},
		},
	}

	app.Run(os.Args)
}

func forward(destination string) {
	if !validDestination(destination) {
		return
	}

	var conn net.Conn
	var err error
	switch {
	case useUDP:
		conn, err = net.Dial("udp", destination)
	case !useTLS:
		conn, err = net.Dial("tcp", destination)
	default:
		conn, err = tls.Dial("tcp", destination, &tls.Config{})
	}
	connected := false
	if err != nil {
		fmt.Fprintf(os.Stderr, "Could not connect to %s: %v\n", destination, err)
	} else {
		connected = true
		defer conn.Close()
	}

	writers := make([]*io.PipeWriter, 0, 2)
	wg := sync.WaitGroup{}
	if doTee {
		stdOutReader, stdOutWriter := io.Pipe()
		writers = append(writers, stdOutWriter)
		go func() {
			io.Copy(os.Stdout, stdOutReader)
		}()
	}
	if connected {
		netReader, netWriter := io.Pipe()
		writers = append(writers, netWriter)
		go func() {
			wg.Add(1)
			reader := bufio.NewReader(netReader)
			byteBuffer := bytes.NewBuffer([]byte{})
			for {
				data, err := reader.ReadBytes('\n')
				if err != nil {
					break
				}

				if syslog {
					data = toSyslog(byteBuffer, data)
				}

				if _, err = conn.Write(data); err != nil {
					break
				}
				byteBuffer.Reset()
			}
			io.Copy(ioutil.Discard, netReader)
			wg.Done()
		}()
	}

	if len(writers) == 0 {
		io.Copy(ioutil.Discard, os.Stdin)
		return
	}

	writeOnly := make([]io.Writer, 0, len(writers))
	for _, w := range writers {
		writeOnly = append(writeOnly, w)
	}
	mw := io.MultiWriter(writeOnly...)
	io.Copy(mw, os.Stdin)
	for _, w := range writers {
		w.Close()
	}

	wg.Wait()
}

func toSyslog(b *bytes.Buffer, line []byte) []byte {
	//<22>1 2016-06-18T09:56:21Z sendername programname - - - the log message
	b.WriteString("<22>1 ")
	b.WriteString(time.Now().UTC().Format(time.RFC3339) + " ")
	b.WriteString(syslogHostname + " ")
	b.WriteString(syslogApp + " ")
	b.WriteString("- - - ")
	b.Write(line)

	return b.Bytes()
}

// Basic validation test
func validDestination(d string) bool {
	if len(d) == 0 {
		return false
	}

	s := strings.Split(d, ":")
	if len(s) != 2 || len(s[0]) == 0 || len(s[1]) == 0 {
		return false
	}

	if _, err := strconv.Atoi(s[1]); err != nil {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/slyrz/newscat/html"
	"github.com/slyrz/newscat/model"
	"github.com/slyrz/newscat/util"
	"io"
	"net/http"
	"os"
	"strings"
)

// Input stores the user-provided HTML data and its location.
type Input struct {
	Location string    // either file path or URL or empty if data was read from stdin
	Data     io.Reader // the HTML data (hopefully)
}

var (
	// highlight indicates whether newscat should use ANSI escape codes
	// to print headings and emphasized text in bold type. The default value of this flag
	// depends on the type of stdout - it's set to false if newscat isn't printing
	// onto a terminal.
	highlight = flag.Bool("highlight", util.IsTerminal(os.Stdout), "highlight headings and emphasized text")

	// newscat extracts either article content or links.
	extractContent = flag.Bool("content", true, "extract article content")
	extractLinks   = flag.Bool("links", false, "extract links")
)

func printChunks(chunks []*html.Chunk) {
	var last *html.Chunk = nil
	for _, chunk := range chunks {
		delim, pre, pos := "", "", ""
		// If the last chunk and the current chunk are part of the same HTML block,
		// separate them by a space character. Otherwise use two newline characters
		// to create a new paragraph.
		if last != nil {
			switch {
			case last.Block != chunk.Block:
				delim = "\n\n"
			case last.Block == chunk.Block:
				delim = " "
			}
		}
		if *highlight {
			// Print headings and emphasized text bold.
			switch chunk.Base.Data {
			case "h1", "h2", "h3", "h4", "h5", "h6", "em", "strong", "b":
				pre, pos = "\x1b[1m", "\x1b[0m"
			default:
				pre, pos = "", ""
			}
		}
		fmt.Printf("%s%s%s%s", delim, pre, chunk.Text, pos)
		last = chunk
	}
	fmt.Println()
}

func printLinks(links []*html.Link) {
	for _, link := range links {
		fmt.Println(link.URL)
	}
}

func main() {
	flag.Parse()

	inputChannel := make(chan Input, 4)
	// Open all input (file paths or URLs) or read from stdin and write the
	// corresponding Input structs to the inputChannel channel.
	go func() {
		if flag.NArg() > 0 {
			for _, arg := range flag.Args() {
				if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
					if resp, err := http.Get(arg); err == nil {
						inputChannel <- Input{arg, resp.Body}
					}
				} else {
					if file, err := os.Open(arg); err == nil {
						inputChannel <- Input{arg, file}
					}
				}
			}
		} else {
			inputChannel <- Input{"", os.Stdin}
		}
		close(inputChannel)
	}()

	switch {
	case *extractLinks:
		ext := model.NewLinkExtractor()
		for input := range inputChannel {
			if website, err := html.NewWebsite(input.Data); err == nil {
				// Add protocol and domain to relative links before we perform
				// link extraction. Works only if input.Location is a URL.
				website.ResolveBase(input.Location)
				if links := ext.Extract(website); len(links) > 0 {
					printLinks(links)
				}
			}
		}
	case *extractContent:
		ext := model.NewChunkExtractor()
		for input := range inputChannel {
			if article, err := html.NewArticle(input.Data); err == nil {
				if chunks := ext.Extract(article); len(chunks) > 0 {
					printChunks(chunks)
				}
			}
		}
	}
}
<commit_msg>added custom usage output<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/slyrz/newscat/html"
	"github.com/slyrz/newscat/model"
	"github.com/slyrz/newscat/util"
	"io"
	"net/http"
	"os"
	"strings"
)

// Input stores the user-provided HTML data and its location.
type Input struct {
	Location string    // either file path or URL or empty if data was read from stdin
	Data     io.Reader // the HTML data (hopefully)
}

var (
	// highlight indicates whether newscat should use ANSI escape codes
	// to print headings and emphasized text in bold type. The default value of this flag
	// depends on the type of stdout - it's set to false if newscat isn't printing
	// onto a terminal.
	highlight = flag.Bool("highlight", util.IsTerminal(os.Stdout), "highlight headings and emphasized text")

	// newscat extracts either article content or links.
	extractContent = flag.Bool("content", true, "extract article content")
	extractLinks   = flag.Bool("links", false, "extract links")
)

func init() {
	flag.Usage = func() {
		fmt.Println(`newscat [OPTION]... [PATH|URL]...

Options:
  -content    Extract article content (default).
  -links      Extract links.
  -highlight  Use ANSI escape codes to format output.`)
	}
}

func printChunks(chunks []*html.Chunk) {
	var last *html.Chunk = nil
	for _, chunk := range chunks {
		delim, pre, pos := "", "", ""
		// If the last chunk and the current chunk are part of the same HTML block,
		// separate them by a space character. Otherwise use two newline characters
		// to create a new paragraph.
		if last != nil {
			switch {
			case last.Block != chunk.Block:
				delim = "\n\n"
			case last.Block == chunk.Block:
				delim = " "
			}
		}
		if *highlight {
			// Print headings and emphasized text bold.
			switch chunk.Base.Data {
			case "h1", "h2", "h3", "h4", "h5", "h6", "em", "strong", "b":
				pre, pos = "\x1b[1m", "\x1b[0m"
			default:
				pre, pos = "", ""
			}
		}
		fmt.Printf("%s%s%s%s", delim, pre, chunk.Text, pos)
		last = chunk
	}
	fmt.Println()
}

func printLinks(links []*html.Link) {
	for _, link := range links {
		fmt.Println(link.URL)
	}
}

func main() {
	flag.Parse()

	inputChannel := make(chan Input, 4)
	// Open all input (file paths or URLs) or read from stdin and write the
	// corresponding Input structs to the inputChannel channel.
	go func() {
		if flag.NArg() > 0 {
			for _, arg := range flag.Args() {
				if strings.HasPrefix(arg, "http://") || strings.HasPrefix(arg, "https://") {
					if resp, err := http.Get(arg); err == nil {
						inputChannel <- Input{arg, resp.Body}
					}
				} else {
					if file, err := os.Open(arg); err == nil {
						inputChannel <- Input{arg, file}
					}
				}
			}
		} else {
			inputChannel <- Input{"", os.Stdin}
		}
		close(inputChannel)
	}()

	switch {
	case *extractLinks:
		ext := model.NewLinkExtractor()
		for input := range inputChannel {
			if website, err := html.NewWebsite(input.Data); err == nil {
				// Add protocol and domain to relative links before we perform
				// link extraction. Works only if input.Location is a URL.
				website.ResolveBase(input.Location)
				if links := ext.Extract(website); len(links) > 0 {
					printLinks(links)
				}
			}
		}
	case *extractContent:
		ext := model.NewChunkExtractor()
		for input := range inputChannel {
			if article, err := html.NewArticle(input.Data); err == nil {
				if chunks := ext.Extract(article); len(chunks) > 0 {
					printChunks(chunks)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"strings"

	"./bookmark"
	"./htmlhelper"
	"./lisp"
	"./tour"
)

var AppList = map[string]func(){
	"lisp":       lisp.Main,
	"tour":       tour.Main,
	"bookmark":   bookmark.Main,
	"htmlhelper": htmlhelper.Main,
}

func appNames() (names []string) {
	for name, _ := range AppList {
		names = append(names, name)
	}
	return
}

func main() {
	var appname string
	usage := fmt.Sprintf("app you want to run: %s", strings.Join(appNames(), " | "))
	flag.StringVar(&appname, "appname", "", usage)
	flag.Parse()

	f := AppList[appname]
	if f == nil {
		flag.Usage()
		return
	}
	f()
}
<commit_msg>remove local import<commit_after>package main

import (
	"flag"
	"fmt"
	"strings"

	"github.com/siteshen/go-practice/bookmark"
	"github.com/siteshen/go-practice/htmlhelper"
	"github.com/siteshen/go-practice/lisp"
	"github.com/siteshen/go-practice/tour"
)

var AppList = map[string]func(){
	"lisp":       lisp.Main,
	"tour":       tour.Main,
	"bookmark":   bookmark.Main,
	"htmlhelper": htmlhelper.Main,
}

func appNames() (names []string) {
	for name, _ := range AppList {
		names = append(names, name)
	}
	return
}

func main() {
	var appname string
	usage := fmt.Sprintf("app you want to run: %s", strings.Join(appNames(), " | "))
	flag.StringVar(&appname, "appname", "", usage)
	flag.Parse()

	f := AppList[appname]
	if f == nil {
		flag.Usage()
		return
	}
	f()
}
<|endoftext|>


<commit_before>package main

import (
  "fmt"
  "os"
  "log"
  "errors"
  "strconv"
  "path/filepath"
  tag "github.com/dhowden/tag"
  color "github.com/fatih/color"
)

func openFolder (folder string) *os.File {
  d, err := os.Open(folder)

  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }

  return d
}

func getNewFileName (m tag.Metadata) (string, error) {
  artist := m.AlbumArtist()

  if artist == "" {
    artist = m.Artist()
  }

  year := strconv.Itoa(m.Year())
  album := m.Album()

  if (len(artist) == 0) {
    return "", errors.New("Artist not found")
  }

  if (year == "0") {
    return "", errors.New("Year not found")
  }

  if (len(album) == 0) {
    return "", errors.New("Album not found")
  }

  folder := artist + " - " + year + " - " + album

  return folder, nil
}

func main () {
  // Get the folders passed as arguments
  folders := os.Args[1:]

  green := color.New(color.FgGreen).SprintFunc()
  red := color.New(color.FgRed).SprintFunc()

  for _,folder := range folders {
    d := openFolder(folder);

    defer d.Close()

    files, err := d.Readdir(-1)
    if err != nil {
      fmt.Println(err)
      os.Exit(1)
    }

    for _, file := range files {
      if file.Mode().IsRegular() && filepath.Ext(file.Name()) == ".mp3" {
        var f, err = os.Open(folder + "/" + file.Name())

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

        defer f.Close()

        m, err := tag.ReadFrom(f)

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

        newFolderName, err := getNewFileName(m)

        fmt.Printf("Renaming \"" + folder + "\" ")

        if err != nil {
          fmt.Printf("%s\n", red("Error: " + err.Error()))
        } else {
          os.Rename(folder, newFolderName)
          fmt.Printf("%s\n", green("Success ✔"))
        }

        break
      }
    }
  }
}
<commit_msg>Added a function to get the folder files<commit_after>package main

import (
  "fmt"
  "os"
  "log"
  "errors"
  "strconv"
  "path/filepath"
  tag "github.com/dhowden/tag"
  color "github.com/fatih/color"
)

func openFolder (folder string) *os.File {
  d, err := os.Open(folder)

  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }

  return d
}

func getFolderFiles (d *os.File) []os.FileInfo {
  files, err := d.Readdir(-1)

  if err != nil {
    fmt.Println(err)
    os.Exit(1)
  }

  return files
}

func getNewFileName (m tag.Metadata) (string, error) {
  artist := m.AlbumArtist()

  if artist == "" {
    artist = m.Artist()
  }

  year := strconv.Itoa(m.Year())
  album := m.Album()

  if (len(artist) == 0) {
    return "", errors.New("Artist not found")
  }

  if (year == "0") {
    return "", errors.New("Year not found")
  }

  if (len(album) == 0) {
    return "", errors.New("Album not found")
  }

  folder := artist + " - " + year + " - " + album

  return folder, nil
}

func main () {
  // Get the folders passed as arguments
  folders := os.Args[1:]

  green := color.New(color.FgGreen).SprintFunc()
  red := color.New(color.FgRed).SprintFunc()

  for _,folder := range folders {
    d := openFolder(folder)
    defer d.Close()

    files := getFolderFiles(d)

    for _, file := range files {
      if file.Mode().IsRegular() && filepath.Ext(file.Name()) == ".mp3" {
        var f, err = os.Open(folder + "/" + file.Name())

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

        defer f.Close()

        m, err := tag.ReadFrom(f)

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

        newFolderName, err := getNewFileName(m)

        fmt.Printf("Renaming \"" + folder + "\" ")

        if err != nil {
          fmt.Printf("%s\n", red("Error: " + err.Error()))
        } else {
          os.Rename(folder, newFolderName)
          fmt.Printf("%s\n", green("Success ✔"))
        }

        break
      }
    }
  }
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"time"

	"context"
)

const retryDelay = 500 * time.Millisecond

type Resource interface {
	fmt.Stringer
	Await(context.Context) error
}

type unavailableError struct {
	Reason error
}

// Error implements the error interface.
func (e *unavailableError) Error() string {
	return e.Reason.Error()
}

func main() {
	var (
		forceFlag   = flag.Bool("f", false, "Force running the command even after giving up")
		timeoutFlag = flag.Duration("t", 1*time.Minute, "Timeout duration before giving up")
		verboseFlag = flag.Bool("v", false, "Set verbose output")
		quietFlag   = flag.Bool("q", false, "Set quiet mode")
	)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: await [options...] <res>... [ -- <cmd>]")
		fmt.Fprintln(os.Stderr, "Await availability of resources.")
		fmt.Fprintln(os.Stderr)
		flag.PrintDefaults()
	}
	flag.Parse()

	logLevel := errorLevel
	switch {
	case *quietFlag:
		logLevel = silentLevel
	case *verboseFlag:
		logLevel = infoLevel
	}
	log := NewLogger(logLevel)

	resArgs, cmdArgs := splitArgs(flag.Args())
	ress, err := parseResources(resArgs)
	if err != nil {
		log.Fatalln("Error: failed to parse resources: %v", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), *timeoutFlag)
	go func() {
		for i := 0; i < len(ress); {
			select {
			case <-ctx.Done(): // Exceeded timeout
				return
			default:
				res, err := identifyResource(ress[i])
				if err != nil {
					log.Fatalf("Error: %v", err)
				}

				log.Infof("Awaiting resource: %s", res)
				if err := res.Await(ctx); err != nil {
					if e, ok := err.(*unavailableError); ok { // transient error
						log.Infof("Resource unavailable: %v", e)
					} else {
						log.Errorf("Error: awaiting resource: %v", err)
					}
					time.Sleep(retryDelay)
				} else {
					i++
				}
			}
		}

		cancel() // All resources are available
	}()

	switch <-ctx.Done(); ctx.Err() {
	case context.Canceled:
		log.Infoln("All resources available")
	case context.DeadlineExceeded:
		log.Infoln("Error: timeout exceeded")
		if !*forceFlag {
			os.Exit(1)
		}
	}

	if len(cmdArgs) > 0 {
		log.Infof("Runnning command: %v", cmdArgs)
		if err := execCmd(cmdArgs); err != nil {
			log.Fatalf("Error: %v", err)
		}
	}
}

func splitArgs(args []string) ([]string, []string) {
	for i, a := range args {
		if a == "--" {
			return args[0:i], args[i+1:]
		}
	}
	return args, []string{}
}

func parseResources(urlArgs []string) ([]url.URL, error) {
	var urls []url.URL
	for _, urlArg := range urlArgs {
		// Leveraging the fact the Go's URL parser matches e.g. `curl -s
		// http://example.com` as url.Path instead of throwing an error.
		u, err := url.Parse(urlArg)
		if err != nil {
			return urls, err
		}
		urls = append(urls, *u)
	}
	return urls, nil
}

func identifyResource(u url.URL) (Resource, error) {
	switch u.Scheme {
	case "http", "https":
		return &httpResource{u}, nil
	case "ws", "wss":
		return &websocketResource{u}, nil
	case "tcp", "tcp4", "tcp6":
		return &tcpResource{u}, nil
	case "file":
		return &fileResource{u}, nil
	case "postgres":
		return &postgresqlResource{u}, nil
	case "mysql":
		return &mysqlResource{u}, nil
	case "":
		return &commandResource{u}, nil
	default:
		return nil, fmt.Errorf("unsupported resource scheme: %v", u.Scheme)
	}
}

func execCmd(cmdArgs []string) error {
	path, err := exec.LookPath(cmdArgs[0])
	if err != nil {
		return err
	}
	return syscall.Exec(path, cmdArgs, os.Environ())
}

func parseTags(tag string) map[string]string {
	tags := map[string]string{}
	tagParts := strings.Split(tag, ",")
	for _, t := range tagParts {
		kv := strings.SplitN(t, "=", 2)
		k := kv[0]
		if len(kv) == 1 {
			tags[k] = ""
		} else {
			tags[k] = kv[1]
		}
	}
	return tags
}
<commit_msg>Improve error messages<commit_after>package main

import (
	"flag"
	"fmt"
	"net/url"
	"os"
	"os/exec"
	"strings"
	"syscall"
	"time"

	"context"
)

const retryDelay = 500 * time.Millisecond

type Resource interface {
	fmt.Stringer
	Await(context.Context) error
}

type unavailableError struct {
	Reason error
}

// Error implements the error interface.
func (e *unavailableError) Error() string {
	return e.Reason.Error()
}

func main() {
	var (
		forceFlag   = flag.Bool("f", false, "Force running the command even after giving up")
		timeoutFlag = flag.Duration("t", 1*time.Minute, "Timeout duration before giving up")
		verboseFlag = flag.Bool("v", false, "Set verbose output")
		quietFlag   = flag.Bool("q", false, "Set quiet mode")
	)
	flag.Usage = func() {
		fmt.Fprintln(os.Stderr, "Usage: await [options...] <res>... [ -- <cmd>]")
		fmt.Fprintln(os.Stderr, "Await availability of resources.")
		fmt.Fprintln(os.Stderr)
		flag.PrintDefaults()
	}
	flag.Parse()

	logLevel := errorLevel
	switch {
	case *quietFlag:
		logLevel = silentLevel
	case *verboseFlag:
		logLevel = infoLevel
	}
	log := NewLogger(logLevel)

	resArgs, cmdArgs := splitArgs(flag.Args())
	ress, err := parseResources(resArgs)
	if err != nil {
		log.Fatalln("Error: failed to parse resources: %v", err)
	}

	ctx, cancel := context.WithTimeout(context.Background(), *timeoutFlag)
	go func() {
		for i := 0; i < len(ress); {
			select {
			case <-ctx.Done(): // Exceeded timeout
				return
			default:
				res, err := identifyResource(ress[i])
				if err != nil {
					log.Fatalf("Error: %v", err)
				}

				log.Infof("Awaiting resource: %s", res)
				if err := res.Await(ctx); err != nil {
					if e, ok := err.(*unavailableError); ok { // transient error
						log.Infof("Resource unavailable: %v", e)
					} else {
						log.Errorf("Error: failed to await resource: %v", err)
					}
					time.Sleep(retryDelay)
				} else {
					i++
				}
			}
		}

		cancel() // All resources are available
	}()

	switch <-ctx.Done(); ctx.Err() {
	case context.Canceled:
		log.Infoln("All resources available")
	case context.DeadlineExceeded:
		log.Infoln("Timeout exceeded")
		if !*forceFlag {
			os.Exit(1)
		}
	}

	if len(cmdArgs) > 0 {
		log.Infof("Runnning command: %v", cmdArgs)
		if err := execCmd(cmdArgs); err != nil {
			log.Fatalf("Error: failed to execute command: %v", err)
		}
	}
}

func splitArgs(args []string) ([]string, []string) {
	for i, a := range args {
		if a == "--" {
			return args[0:i], args[i+1:]
		}
	}
	return args, []string{}
}

func parseResources(urlArgs []string) ([]url.URL, error) {
	var urls []url.URL
	for _, urlArg := range urlArgs {
		// Leveraging the fact the Go's URL parser matches e.g. `curl -s
		// http://example.com` as url.Path instead of throwing an error.
		u, err := url.Parse(urlArg)
		if err != nil {
			return urls, err
		}
		urls = append(urls, *u)
	}
	return urls, nil
}

func identifyResource(u url.URL) (Resource, error) {
	switch u.Scheme {
	case "http", "https":
		return &httpResource{u}, nil
	case "ws", "wss":
		return &websocketResource{u}, nil
	case "tcp", "tcp4", "tcp6":
		return &tcpResource{u}, nil
	case "file":
		return &fileResource{u}, nil
	case "postgres":
		return &postgresqlResource{u}, nil
	case "mysql":
		return &mysqlResource{u}, nil
	case "":
		return &commandResource{u}, nil
	default:
		return nil, fmt.Errorf("unsupported resource scheme: %v", u.Scheme)
	}
}

func execCmd(cmdArgs []string) error {
	path, err := exec.LookPath(cmdArgs[0])
	if err != nil {
		return err
	}
	return syscall.Exec(path, cmdArgs, os.Environ())
}

func parseTags(tag string) map[string]string {
	tags := map[string]string{}
	tagParts := strings.Split(tag, ",")
	for _, t := range tagParts {
		kv := strings.SplitN(t, "=", 2)
		k := kv[0]
		if len(kv) == 1 {
			tags[k] = ""
		} else {
			tags[k] = kv[1]
		}
	}
	return tags
}
<|endoftext|>


<commit_before>package main

import (
	"log"
	"math/rand"
	"time"
)

func main() {
	/*
		log.Print("started.")
		// チャネル
		sleep1_finished := make(chan bool)

		go func() {
			// 0.2秒かかるコマンド
			log.Print("sleep1 started.")
			time.Sleep(200 * 1000 * 1000 * time.Nanosecond)
			log.Print("sleep1 finished.")
			sleep1_finished <- true
		}()

		// 終わるまで待つ
		<- sleep1_finished
	*/

	// 136枚の中から14枚返したい
	list := shuffled_cards()

	// 出力
	// digit_output(list)
	solve(list)
	string_output(list)
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(list []int) {
	for i := len(list); i > 1; i-- {
		j := rand.Intn(i) // 0　.. i-1 の乱数発生
		list[i-1], list[j] = list[j], list[i-1]
	}
}

func shuffled_cards() []int {
	rand.Seed(time.Now().UnixNano())

	// データ要素数指定、および初期データ作成
	size := 136
	list := make([]int, size, size)
	for i := 0; i < size; i++ {
		list[i] = i
	}

	// シャッフル
	shuffle(list)

	return list[:14]
}

// 単純出力
func digit_output(list []int) {
	for j := 0; j < 14; j++ {
		log.Println(list[j])
	}
}

// 牌の出力
func string_output(list []int) {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < 14; j++ {
		// 126976 is 'ton'
		// https://codepoints.net/U+1F000
		b = append(b, string(list[j]/4+126976)...) // ...が必要
		b = append(b, string(32)...)               // ...が必要
	}
	log.Print(string(b))
}

// solve
// 字マンソーピンのリストをつくる
func solve(list []int) bool {
	if is_chitoitsu(list) {
		return true
	}
	matrix := [][]int{{}, {}, {}, {}}
	for _, value := range list {
		group(matrix, value)
	}
	log.Println(matrix)

	group_scan(matrix)
	return false
}

// スート分類してくれる
func group(m [][]int, j int) {
	// 4で割ると本来のインデックスに
	i := j / 4
	switch {
	case i < 7:
		m[0] = append(m[0], i)
		log.Println(i)
	case i < 7+(9*1):
		m[1] = append(m[1], i-7)
		log.Println(i)
	case i < 7+(9*2):
		m[2] = append(m[2], i-7-(9*1))
		log.Println(i)
	case i < 7+(9*3):
		m[3] = append(m[3], i-7-(9*2))
		log.Println(i)
	}
}

func group_scan(m [][]int) bool {
	if !validate_mod3(m) {
		return false
	}
	return false
}

func validate_mod3(m [][]int) bool {
	//スートのサイズを3で割った時
	//あまりが2であるすーとグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a) % 3 {
		case 0:
			// noop
		case 1:
			log.Println("immi")
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func is_chitoitsu(list []int) bool {
	//カウンタ
	c := map[int]int{}

	//コピー
	l := list

	for _, v := range l {
		// 4で割ると本来のインデックスに
		v = v / 4
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}
<commit_msg>Update<commit_after>package main

import (
	"log"
	"math/rand"
	"sort"
	"time"
)

func main() {
	/*
		log.Print("started.")
		// チャネル
		sleep1_finished := make(chan bool)

		go func() {
			// 0.2秒かかるコマンド
			log.Print("sleep1 started.")
			time.Sleep(200 * 1000 * 1000 * time.Nanosecond)
			log.Print("sleep1 finished.")
			sleep1_finished <- true
		}()

		// 終わるまで待つ
		<- sleep1_finished
	*/

	// 136枚の中から14枚返したい
	list := shuffled_cards()

	// 出力
	// digit_output(list)
	solve(list)
	string_output(list)
}

// http://d.hatena.ne.jp/hake/20150930/p1
func shuffle(list []int) {
	for i := len(list); i > 1; i-- {
		j := rand.Intn(i) // 0　.. i-1 の乱数発生
		list[i-1], list[j] = list[j], list[i-1]
	}
}

func shuffled_cards() []int {
	rand.Seed(time.Now().UnixNano())

	// データ要素数指定、および初期データ作成
	size := 136
	list := make([]int, size, size)
	for i := 0; i < size; i++ {
		list[i] = i
	}

	// シャッフル
	shuffle(list)

	return list[:14]
}

// 単純出力
func digit_output(list []int) {
	for j := 0; j < 14; j++ {
		log.Println(list[j])
	}
}

// 牌の出力
func string_output(list []int) {
	// http://qiita.com/ruiu/items/2bb83b29baeae2433a79
	// サイズ0、内部バッファの長さ69の[]byteの値を割り当てる
	b := make([]byte, 0, 70)

	// bに文字列を追加
	for j := 0; j < 14; j++ {
		// 126976 is 'ton'
		// https://codepoints.net/U+1F000
		b = append(b, string(list[j]/4+126976)...) // ...が必要
		b = append(b, string(32)...)               // ...が必要
	}
	log.Print(string(b))
}

// solve
// 字マンソーピンのリストをつくる
func solve(list []int) bool {
	if is_chitoitsu(list) {
		return true
	}
	matrix := [][]int{{}, {}, {}, {}}
	for _, value := range list {
		group(matrix, value)
	}
	log.Println(matrix)

	group_scan(matrix)
	return false
}

// スート分類してくれる
func group(m [][]int, j int) {
	// 4で割ると本来のインデックスに
	i := j / 4
	switch {
	case i < 7:
		m[0] = append(m[0], i)
		log.Println(i)
	case i < 7+(9*1):
		m[1] = append(m[1], i-7)
		log.Println(i)
	case i < 7+(9*2):
		m[2] = append(m[2], i-7-(9*1))
		log.Println(i)
	case i < 7+(9*3):
		m[3] = append(m[3], i-7-(9*2))
		log.Println(i)
	}
}

func group_scan(m [][]int) bool {
	if !validate_mod3(m) {
		return false
	}
	if !validate_33332(m) {
		return false
	}
	return true
}

func validate_mod3(m [][]int) bool {
	//スートのサイズを3で割った時
	//あまりが2であるすーとグループが1つであること
	c := 0
	for _, a := range m {
		switch len(a) % 3 {
		case 0:
			// noop
		case 1:
			log.Println("immi")
			return false
		case 2:
			c++
		}
	}
	return c == 1
}

func validate_33332(m [][]int) bool {
	for _, a := range m {
		if !validate_suit_group(a) {
			return false
		}
	}
	return true
}

func validate_suit_group(a []int) bool {
	//TODO: implement
	for _, v := range a {
		// 4で割ると本来のインデックスに
		v = v / 4
	}

	//ソート
	sort.Ints(a)
	if len(a)%3 == 2 {
		//ペアを探す
		pair_numbers := pairable_numbers(a)
		//ペア候補がなかったらぬける
		if len(pair_numbers) == 0 {
			return false
		}
	} else if len(a)%3 == 0 {
		return true
	}
	// 来ないはず
	return false
}

func pairable_numbers(sorted []int) []int {
	//カウンタ
	retval := []int{}
	a := 999 // 2つ前
	b := 999 // 1つ前
	for _, v := range sorted {
		if b == v && a != v {
			retval = append(retval, v)
		} else {
			b = v
		}
	}
	return retval
}

func is_chitoitsu(list []int) bool {
	//カウンタ
	c := map[int]int{}

	//コピー
	l := list

	for _, v := range l {
		// 4で割ると本来のインデックスに
		v = v / 4
		count, ok := c[v]
		if ok {
			if count == 1 {
				c[v] = 2
			} else {
				// c[v] == 2
				return false
			}
		} else {
			c[v] = 1
		}
		//8個チェック
		if len(c) >= 8 {
			return false
		}
	}
	return true
}
<|endoftext|>


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

package main

import (
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"os"
)

const (
	version = "0.8.10.1"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(m *cmd.Manager, args []string) error {
		context := cmd.Context{Args: args}
		client := cmd.NewClient(nil, nil, m)
		command := plugin{}
		return command.Run(&context, client)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&tsuru.AppRun{})
	m.Register(&tsuru.AppInfo{})
	m.Register(&AppCreate{})
	m.Register(&AppRemove{})
	m.Register(&UnitAdd{})
	m.Register(&UnitRemove{})
	m.Register(tsuru.AppList{})
	m.Register(&tsuru.AppLog{})
	m.Register(&tsuru.AppGrant{})
	m.Register(&tsuru.AppRevoke{})
	m.Register(&tsuru.AppRestart{})
	m.Register(&tsuru.AppStart{})
	m.Register(&tsuru.SetCName{})
	m.Register(&tsuru.UnsetCName{})
	m.Register(&tsuru.EnvGet{})
	m.Register(&tsuru.EnvSet{})
	m.Register(&tsuru.EnvUnset{})
	m.Register(&KeyAdd{})
	m.Register(&KeyRemove{})
	m.Register(tsuru.ServiceList{})
	m.Register(tsuru.ServiceAdd{})
	m.Register(tsuru.ServiceRemove{})
	m.Register(tsuru.ServiceDoc{})
	m.Register(tsuru.ServiceInfo{})
	m.Register(tsuru.ServiceInstanceStatus{})
	m.Register(&tsuru.ServiceBind{})
	m.Register(&tsuru.ServiceUnbind{})
	m.Register(platformList{})
	m.Register(&pluginInstall{})
	m.Register(&pluginRemove{})
	m.Register(&pluginList{})
	m.Register(swap{})
	return m
}

func main() {
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	manager.Run(os.Args[1:])
}
<commit_msg>bump tsuru to 0.8.11.<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/cmd/tsuru-base"
	"os"
)

const (
	version = "0.8.11"
	header  = "Supported-Tsuru"
)

func buildManager(name string) *cmd.Manager {
	lookup := func(m *cmd.Manager, args []string) error {
		context := cmd.Context{Args: args}
		client := cmd.NewClient(nil, nil, m)
		command := plugin{}
		return command.Run(&context, client)
	}
	m := cmd.BuildBaseManager(name, version, header, lookup)
	m.Register(&tsuru.AppRun{})
	m.Register(&tsuru.AppInfo{})
	m.Register(&AppCreate{})
	m.Register(&AppRemove{})
	m.Register(&UnitAdd{})
	m.Register(&UnitRemove{})
	m.Register(tsuru.AppList{})
	m.Register(&tsuru.AppLog{})
	m.Register(&tsuru.AppGrant{})
	m.Register(&tsuru.AppRevoke{})
	m.Register(&tsuru.AppRestart{})
	m.Register(&tsuru.AppStart{})
	m.Register(&tsuru.SetCName{})
	m.Register(&tsuru.UnsetCName{})
	m.Register(&tsuru.EnvGet{})
	m.Register(&tsuru.EnvSet{})
	m.Register(&tsuru.EnvUnset{})
	m.Register(&KeyAdd{})
	m.Register(&KeyRemove{})
	m.Register(tsuru.ServiceList{})
	m.Register(tsuru.ServiceAdd{})
	m.Register(tsuru.ServiceRemove{})
	m.Register(tsuru.ServiceDoc{})
	m.Register(tsuru.ServiceInfo{})
	m.Register(tsuru.ServiceInstanceStatus{})
	m.Register(&tsuru.ServiceBind{})
	m.Register(&tsuru.ServiceUnbind{})
	m.Register(platformList{})
	m.Register(&pluginInstall{})
	m.Register(&pluginRemove{})
	m.Register(&pluginList{})
	m.Register(swap{})
	return m
}

func main() {
	name := cmd.ExtractProgramName(os.Args[0])
	manager := buildManager(name)
	manager.Run(os.Args[1:])
}
<|endoftext|>


<commit_before>package raft

import (
	"sync"
	"time"
)

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

// A peer is a reference to another server involved in the consensus protocol.
type Peer struct {
	server           *server
	Name             string `json:"name"`
	ConnectionString string `json:"connectionString"`
	prevLogIndex     uint64
	mutex            sync.RWMutex
	stopChan         chan bool
	heartbeatTimeout time.Duration
}

//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------

// Creates a new peer.
func newPeer(server *server, name string, connectionString string, heartbeatTimeout time.Duration) *Peer {
	return &Peer{
		server:           server,
		Name:             name,
		ConnectionString: connectionString,
		heartbeatTimeout: heartbeatTimeout,
	}
}

//------------------------------------------------------------------------------
//
// Accessors
//
//------------------------------------------------------------------------------

// Sets the heartbeat timeout.
func (p *Peer) setHeartbeatTimeout(duration time.Duration) {
	p.heartbeatTimeout = duration
}

//--------------------------------------
// Prev log index
//--------------------------------------

// Retrieves the previous log index.
func (p *Peer) getPrevLogIndex() uint64 {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return p.prevLogIndex
}

// Sets the previous log index.
func (p *Peer) setPrevLogIndex(value uint64) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.prevLogIndex = value
}

//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------

//--------------------------------------
// Heartbeat
//--------------------------------------

// Starts the peer heartbeat.
func (p *Peer) startHeartbeat() {
	p.stopChan = make(chan bool, 1)
	c := make(chan bool)
	go p.heartbeat(c)
	<-c
}

// Stops the peer heartbeat.
func (p *Peer) stopHeartbeat(flush bool) {
	// here is a problem
	// the previous stop is no buffer leader may get blocked
	// when heartbeat returns
	// I make the channel with 1 buffer
	// and try to panic here
	select {
	case p.stopChan <- flush:

	default:
		panic("[" + p.server.Name() + "] cannot stop [" + p.Name + "] heartbeat")
	}
}

//--------------------------------------
// Copying
//--------------------------------------

// Clones the state of the peer. The clone is not attached to a server and
// the heartbeat timer will not exist.
func (p *Peer) clone() *Peer {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return &Peer{
		Name:             p.Name,
		ConnectionString: p.ConnectionString,
		prevLogIndex:     p.prevLogIndex,
	}
}

//--------------------------------------
// Heartbeat
//--------------------------------------

// Listens to the heartbeat timeout and flushes an AppendEntries RPC.
func (p *Peer) heartbeat(c chan bool) {
	stopChan := p.stopChan

	c <- true

	debugln("peer.heartbeat: ", p.Name, p.heartbeatTimeout)

	for {
		select {
		case flush := <-stopChan:
			if !flush {
				debugln("peer.heartbeat.stop: ", p.Name)
				return
			} else {
				// before we can safely remove a node
				// we must flush the remove command to the node first
				p.flush()
				debugln("peer.heartbeat.stop: ", p.Name)
				return
			}

		case <-time.After(p.heartbeatTimeout):
			p.flush()
		}
	}
}

func (p *Peer) flush() {
	debugln("peer.heartbeat.run: ", p.Name)
	prevLogIndex := p.getPrevLogIndex()
	entries, prevLogTerm := p.server.log.getEntriesAfter(prevLogIndex, p.server.maxLogEntriesPerRequest)

	if p.server.State() != Leader {
		return
	}

	if entries != nil {
		p.sendAppendEntriesRequest(newAppendEntriesRequest(p.server.currentTerm, prevLogIndex, prevLogTerm, p.server.log.CommitIndex(), p.server.name, entries))
	} else {
		p.sendSnapshotRequest(newSnapshotRequest(p.server.name, p.server.lastSnapshot))
	}
}

//--------------------------------------
// Append Entries
//--------------------------------------

// Sends an AppendEntries request to the peer through the transport.
func (p *Peer) sendAppendEntriesRequest(req *AppendEntriesRequest) {
	traceln("peer.flush.send: ", p.server.Name(), "->", p.Name, " ", len(req.Entries))

	resp := p.server.Transporter().SendAppendEntriesRequest(p.server, p, req)
	if resp == nil {
		debugln("peer.flush.timeout: ", p.server.Name(), "->", p.Name)
		return
	}
	traceln("peer.flush.recv: ", p.Name)

	// If successful then update the previous log index.
	p.mutex.Lock()
	if resp.Success {
		if len(req.Entries) > 0 {
			p.prevLogIndex = req.Entries[len(req.Entries)-1].Index

			// if peer append a log entry from the current term
			// we set append to true
			if req.Entries[len(req.Entries)-1].Term == p.server.currentTerm {
				resp.append = true
			}
		}
		traceln("peer.flush.success: ", p.server.Name(), "->", p.Name, "; idx =", p.prevLogIndex)

		// If it was unsuccessful then decrement the previous log index and
		// we'll try again next time.
	} else {
		if resp.CommitIndex >= p.prevLogIndex {

			// we may miss a response from peer
			// so maybe the peer has commited the logs we sent
			// but we did not receive the success reply and did not increase
			// the prevLogIndex

			p.prevLogIndex = resp.CommitIndex

			debugln("peer.flush.commitIndex: ", p.server.Name(), "->", p.Name, " idx =", p.prevLogIndex)
		} else if p.prevLogIndex > 0 {
			// Decrement the previous log index down until we find a match. Don't
			// let it go below where the peer's commit index is though. That's a
			// problem.
			p.prevLogIndex--
			// if it not enough, we directly decrease to the index of the
			if p.prevLogIndex > resp.Index {
				p.prevLogIndex = resp.Index
			}

			debugln("peer.flush.decrement: ", p.server.Name(), "->", p.Name, " idx =", p.prevLogIndex)
		}
	}
	p.mutex.Unlock()

	// Attach the peer to resp, thus server can know where it comes from
	resp.peer = p.Name
	// Send response to server for processing.
	p.server.send(resp)
}

// Sends an Snapshot request to the peer through the transport.
func (p *Peer) sendSnapshotRequest(req *SnapshotRequest) {
	debugln("peer.snap.send: ", p.Name)

	resp := p.server.Transporter().SendSnapshotRequest(p.server, p, req)
	if resp == nil {
		debugln("peer.snap.timeout: ", p.Name)
		return
	}

	debugln("peer.snap.recv: ", p.Name)

	// If successful, the peer should have been to snapshot state
	// Send it the snapshot!
	if resp.Success {
		p.sendSnapshotRecoveryRequest()
	} else {
		debugln("peer.snap.failed: ", p.Name)
		return
	}

}

// Sends an Snapshot Recovery request to the peer through the transport.
func (p *Peer) sendSnapshotRecoveryRequest() {
	req := newSnapshotRecoveryRequest(p.server.name, p.server.lastSnapshot)
	debugln("peer.snap.recovery.send: ", p.Name)
	resp := p.server.Transporter().SendSnapshotRecoveryRequest(p.server, p, req)

	if resp == nil {
		debugln("peer.snap.recovery.timeout: ", p.Name)
		return
	}

	if resp.Success {
		p.prevLogIndex = req.LastIndex
	} else {
		debugln("peer.snap.recovery.failed: ", p.Name)
		return
	}
	// Send response to server for processing.
	p.server.send(&AppendEntriesResponse{Term: resp.Term, Success: resp.Success, append: (resp.Term == p.server.currentTerm)})
}

//--------------------------------------
// Vote Requests
//--------------------------------------

// send VoteRequest Request
func (p *Peer) sendVoteRequest(req *RequestVoteRequest, c chan *RequestVoteResponse) {
	debugln("peer.vote: ", p.server.Name(), "->", p.Name)
	req.peer = p
	if resp := p.server.Transporter().SendVoteRequest(p.server, p, req); resp != nil {
		debugln("peer.vote: recv", p.server.Name(), "<-", p.Name)
		resp.peer = p
		c <- resp
	}
}
<commit_msg>refactor(peer.go) use ticker<commit_after>package raft

import (
	"sync"
	"time"
)

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

// A peer is a reference to another server involved in the consensus protocol.
type Peer struct {
	server           *server
	Name             string `json:"name"`
	ConnectionString string `json:"connectionString"`
	prevLogIndex     uint64
	mutex            sync.RWMutex
	stopChan         chan bool
	heartbeatTimeout time.Duration
}

//------------------------------------------------------------------------------
//
// Constructor
//
//------------------------------------------------------------------------------

// Creates a new peer.
func newPeer(server *server, name string, connectionString string, heartbeatTimeout time.Duration) *Peer {
	return &Peer{
		server:           server,
		Name:             name,
		ConnectionString: connectionString,
		heartbeatTimeout: heartbeatTimeout,
	}
}

//------------------------------------------------------------------------------
//
// Accessors
//
//------------------------------------------------------------------------------

// Sets the heartbeat timeout.
func (p *Peer) setHeartbeatTimeout(duration time.Duration) {
	p.heartbeatTimeout = duration
}

//--------------------------------------
// Prev log index
//--------------------------------------

// Retrieves the previous log index.
func (p *Peer) getPrevLogIndex() uint64 {
	p.mutex.RLock()
	defer p.mutex.RUnlock()
	return p.prevLogIndex
}

// Sets the previous log index.
func (p *Peer) setPrevLogIndex(value uint64) {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	p.prevLogIndex = value
}

//------------------------------------------------------------------------------
//
// Methods
//
//------------------------------------------------------------------------------

//--------------------------------------
// Heartbeat
//--------------------------------------

// Starts the peer heartbeat.
func (p *Peer) startHeartbeat() {
	p.stopChan = make(chan bool, 1)
	c := make(chan bool)
	go p.heartbeat(c)
	<-c
}

// Stops the peer heartbeat.
func (p *Peer) stopHeartbeat(flush bool) {
	// here is a problem
	// the previous stop is no buffer leader may get blocked
	// when heartbeat returns
	// I make the channel with 1 buffer
	// and try to panic here
	select {
	case p.stopChan <- flush:

	default:
		panic("[" + p.server.Name() + "] cannot stop [" + p.Name + "] heartbeat")
	}
}

//--------------------------------------
// Copying
//--------------------------------------

// Clones the state of the peer. The clone is not attached to a server and
// the heartbeat timer will not exist.
func (p *Peer) clone() *Peer {
	p.mutex.Lock()
	defer p.mutex.Unlock()
	return &Peer{
		Name:             p.Name,
		ConnectionString: p.ConnectionString,
		prevLogIndex:     p.prevLogIndex,
	}
}

//--------------------------------------
// Heartbeat
//--------------------------------------

// Listens to the heartbeat timeout and flushes an AppendEntries RPC.
func (p *Peer) heartbeat(c chan bool) {
	stopChan := p.stopChan

	c <- true

	ticker := time.Tick(p.heartbeatTimeout)

	debugln("peer.heartbeat: ", p.Name, p.heartbeatTimeout)

	for {
		select {
		case flush := <-stopChan:
			if !flush {
				debugln("peer.heartbeat.stop: ", p.Name)
				return
			} else {
				// before we can safely remove a node
				// we must flush the remove command to the node first
				p.flush()
				debugln("peer.heartbeat.stop: ", p.Name)
				return
			}

		case <-ticker:
			p.flush()
		}
	}
}

func (p *Peer) flush() {
	debugln("peer.heartbeat.run: ", p.Name)
	prevLogIndex := p.getPrevLogIndex()
	entries, prevLogTerm := p.server.log.getEntriesAfter(prevLogIndex, p.server.maxLogEntriesPerRequest)

	if p.server.State() != Leader {
		return
	}

	if entries != nil {
		p.sendAppendEntriesRequest(newAppendEntriesRequest(p.server.currentTerm, prevLogIndex, prevLogTerm, p.server.log.CommitIndex(), p.server.name, entries))
	} else {
		p.sendSnapshotRequest(newSnapshotRequest(p.server.name, p.server.lastSnapshot))
	}
}

//--------------------------------------
// Append Entries
//--------------------------------------

// Sends an AppendEntries request to the peer through the transport.
func (p *Peer) sendAppendEntriesRequest(req *AppendEntriesRequest) {
	traceln("peer.flush.send: ", p.server.Name(), "->", p.Name, " ", len(req.Entries))

	resp := p.server.Transporter().SendAppendEntriesRequest(p.server, p, req)
	if resp == nil {
		debugln("peer.flush.timeout: ", p.server.Name(), "->", p.Name)
		return
	}
	traceln("peer.flush.recv: ", p.Name)

	// If successful then update the previous log index.
	p.mutex.Lock()
	if resp.Success {
		if len(req.Entries) > 0 {
			p.prevLogIndex = req.Entries[len(req.Entries)-1].Index

			// if peer append a log entry from the current term
			// we set append to true
			if req.Entries[len(req.Entries)-1].Term == p.server.currentTerm {
				resp.append = true
			}
		}
		traceln("peer.flush.success: ", p.server.Name(), "->", p.Name, "; idx =", p.prevLogIndex)

		// If it was unsuccessful then decrement the previous log index and
		// we'll try again next time.
	} else {
		if resp.CommitIndex >= p.prevLogIndex {

			// we may miss a response from peer
			// so maybe the peer has commited the logs we sent
			// but we did not receive the success reply and did not increase
			// the prevLogIndex

			p.prevLogIndex = resp.CommitIndex

			debugln("peer.flush.commitIndex: ", p.server.Name(), "->", p.Name, " idx =", p.prevLogIndex)
		} else if p.prevLogIndex > 0 {
			// Decrement the previous log index down until we find a match. Don't
			// let it go below where the peer's commit index is though. That's a
			// problem.
			p.prevLogIndex--
			// if it not enough, we directly decrease to the index of the
			if p.prevLogIndex > resp.Index {
				p.prevLogIndex = resp.Index
			}

			debugln("peer.flush.decrement: ", p.server.Name(), "->", p.Name, " idx =", p.prevLogIndex)
		}
	}
	p.mutex.Unlock()

	// Attach the peer to resp, thus server can know where it comes from
	resp.peer = p.Name
	// Send response to server for processing.
	p.server.send(resp)
}

// Sends an Snapshot request to the peer through the transport.
func (p *Peer) sendSnapshotRequest(req *SnapshotRequest) {
	debugln("peer.snap.send: ", p.Name)

	resp := p.server.Transporter().SendSnapshotRequest(p.server, p, req)
	if resp == nil {
		debugln("peer.snap.timeout: ", p.Name)
		return
	}

	debugln("peer.snap.recv: ", p.Name)

	// If successful, the peer should have been to snapshot state
	// Send it the snapshot!
	if resp.Success {
		p.sendSnapshotRecoveryRequest()
	} else {
		debugln("peer.snap.failed: ", p.Name)
		return
	}

}

// Sends an Snapshot Recovery request to the peer through the transport.
func (p *Peer) sendSnapshotRecoveryRequest() {
	req := newSnapshotRecoveryRequest(p.server.name, p.server.lastSnapshot)
	debugln("peer.snap.recovery.send: ", p.Name)
	resp := p.server.Transporter().SendSnapshotRecoveryRequest(p.server, p, req)

	if resp == nil {
		debugln("peer.snap.recovery.timeout: ", p.Name)
		return
	}

	if resp.Success {
		p.prevLogIndex = req.LastIndex
	} else {
		debugln("peer.snap.recovery.failed: ", p.Name)
		return
	}
	// Send response to server for processing.
	p.server.send(&AppendEntriesResponse{Term: resp.Term, Success: resp.Success, append: (resp.Term == p.server.currentTerm)})
}

//--------------------------------------
// Vote Requests
//--------------------------------------

// send VoteRequest Request
func (p *Peer) sendVoteRequest(req *RequestVoteRequest, c chan *RequestVoteResponse) {
	debugln("peer.vote: ", p.server.Name(), "->", p.Name)
	req.peer = p
	if resp := p.server.Transporter().SendVoteRequest(p.server, p, req); resp != nil {
		debugln("peer.vote: recv", p.server.Name(), "<-", p.Name)
		resp.peer = p
		c <- resp
	}
}
<|endoftext|>


<commit_before>/*
 * Mini Copy, (C) 2014, 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"runtime"
	"strconv"

	"github.com/cheggaaa/pb"
	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/console"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/utils/log"
)

func checkConfig() {
	// Check for the environment early on and gracefuly report.
	_, err := user.Current()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to obtain user's home directory")
	}
	// Ensures config file is sane
	_, err = getMcConfig()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalf("mc: Unable to read config file: %s\n", mustGetMcConfigPath())
	}
}

// Tries to get os/arch/platform specific information
// Returns a map of current os/arch/platform/memstats
func getSystemData() map[string]string {
	host, err := os.Hostname()
	if err != nil {
		host = ""
	}
	memstats := &runtime.MemStats{}
	runtime.ReadMemStats(memstats)
	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s",
		pb.FormatBytes(int64(memstats.Alloc)),
		pb.FormatBytes(int64(memstats.TotalAlloc)),
		pb.FormatBytes(int64(memstats.HeapAlloc)),
		pb.FormatBytes(int64(memstats.HeapSys)))
	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
		host,
		runtime.GOOS,
		runtime.GOARCH)
	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU()))
	return map[string]string{
		"PLATFORM": platform,
		"RUNTIME":  goruntime,
		"MEM":      mem,
	}
}

func main() {
	app := cli.NewApp()
	app.Usage = "Mini Copy for object storage and filesystems"
	app.Version = mcGitCommitHash
	app.Commands = options
	app.Flags = flags
	app.Author = "Minio.io"
	app.Before = func(ctx *cli.Context) error {
		globalQuietFlag = ctx.GlobalBool("quiet")
		globalDebugFlag = ctx.GlobalBool("debug")
		globalMaxRetryFlag = ctx.GlobalInt("retry")
		if globalDebugFlag {
			app.ExtraInfo = getSystemData()
		} else {
			log.Debug = log.New(ioutil.Discard, "", 0)
		}

		theme := ctx.GlobalString("theme")
		if theme != "" {
			err := console.SetTheme(theme)
			if err != nil {
				console.Fatalf("mc: Unable to set theme [%s]\n", theme)
			}
		}
		checkConfig()
		return nil
	}
	app.After = func(ctx *cli.Context) error {
		if !isMcConfigExist() && ctx.Command.Name != "config" {
			console.Fatalln("mc: error ``mc`` is not configured. Please run \"mc config generate\".")
		}

		return nil
	}
	app.RunAndExitOnError()
}
<commit_msg>Fix a bug introduced in qdb migration commit 27d87fdd8ec268446c238009cb86af1b255582dc when config file doesn't exist<commit_after>/*
 * Mini Copy, (C) 2014, 2015 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/user"
	"runtime"
	"strconv"

	"github.com/cheggaaa/pb"
	"github.com/minio-io/cli"
	"github.com/minio-io/mc/pkg/console"
	"github.com/minio-io/minio/pkg/iodine"
	"github.com/minio-io/minio/pkg/utils/log"
)

func checkConfig() {
	// Check for the environment early on and gracefuly report.
	_, err := user.Current()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalln("mc: Unable to obtain user's home directory")
	}

	// If config doesn't exist, do not attempt to read it
	if !isMcConfigExist() {
		return
	}

	// Ensures config file is sane
	_, err = getMcConfig()
	if err != nil {
		log.Debug.Println(iodine.New(err, nil))
		console.Fatalf("mc: Unable to read config file: %s\n", mustGetMcConfigPath())
	}
}

// Tries to get os/arch/platform specific information
// Returns a map of current os/arch/platform/memstats
func getSystemData() map[string]string {
	host, err := os.Hostname()
	if err != nil {
		host = ""
	}
	memstats := &runtime.MemStats{}
	runtime.ReadMemStats(memstats)
	mem := fmt.Sprintf("Used: %s | Allocated: %s | Used-Heap: %s | Allocated-Heap: %s",
		pb.FormatBytes(int64(memstats.Alloc)),
		pb.FormatBytes(int64(memstats.TotalAlloc)),
		pb.FormatBytes(int64(memstats.HeapAlloc)),
		pb.FormatBytes(int64(memstats.HeapSys)))
	platform := fmt.Sprintf("Host: %s | OS: %s | Arch: %s",
		host,
		runtime.GOOS,
		runtime.GOARCH)
	goruntime := fmt.Sprintf("Version: %s | CPUs: %s", runtime.Version(), strconv.Itoa(runtime.NumCPU()))
	return map[string]string{
		"PLATFORM": platform,
		"RUNTIME":  goruntime,
		"MEM":      mem,
	}
}

func main() {
	app := cli.NewApp()
	app.Usage = "Mini Copy for object storage and filesystems"
	app.Version = mcGitCommitHash
	app.Commands = options
	app.Flags = flags
	app.Author = "Minio.io"
	app.Before = func(ctx *cli.Context) error {
		globalQuietFlag = ctx.GlobalBool("quiet")
		globalDebugFlag = ctx.GlobalBool("debug")
		globalMaxRetryFlag = ctx.GlobalInt("retry")
		if globalDebugFlag {
			app.ExtraInfo = getSystemData()
		} else {
			log.Debug = log.New(ioutil.Discard, "", 0)
		}

		theme := ctx.GlobalString("theme")
		if theme != "" {
			err := console.SetTheme(theme)
			if err != nil {
				console.Fatalf("mc: Unable to set theme [%s]\n", theme)
			}
		}
		checkConfig()
		return nil
	}
	app.After = func(ctx *cli.Context) error {
		if !isMcConfigExist() && ctx.Command.Name != "config" {
			console.Fatalln("mc: error ``mc`` is not configured. Please run \"mc config generate\".")
		}

		return nil
	}
	app.RunAndExitOnError()
}
<|endoftext|>


<commit_before>package gorequest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type Options struct {
	Url    string
	Method string
	Body   string
	Json   string
	Header string
}

type Response *http.Response

type SuperAgent struct {
	Url       string
	Method    string
	Header    map[string]string
	Type      string
	Data      map[string]interface{}
	QueryData url.Values
}

func New() *SuperAgent {
	s := SuperAgent{
		Type:   "json",
		Data:   make(map[string]interface{}),
		Header: make(map[string]string)}
	return &s
}

func Get(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "GET",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		QueryData: url.Values{}}
	return newReq
}

// TODO: query func for get
func (s *SuperAgent) Query(content string) *SuperAgent {
	_ = content
	return s
}

func Post(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "POST",
		Type:      "json",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		QueryData: url.Values{}}
	return newReq
}

func (s *SuperAgent) Set(param string, value string) *SuperAgent {
	s.Header[param] = value
	return s
}

func (s *SuperAgent) Send(content string) *SuperAgent {
	var val map[string]interface{}
	// check if it is json format
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		s.Type = "json"
		for k, v := range val {
			s.Data[k] = v
		}
	} else {
		// not json format (just normal string)
		s.Type = "form"
		queryVal, _ := url.ParseQuery(content)
		for k, _ := range queryVal {
			s.QueryData.Add(k, queryVal.Get(k))
		}
	}

	return s
}

func (s *SuperAgent) End(callback ...func(response Response)) (error, *http.Response, string) {
	var (
		req  *http.Request
		err  error
		resp *http.Response
	)
	client := &http.Client{}
	if s.Method == "POST" {
		if s.Type == "json" {
			contentJson, _ := json.Marshal(s.Data)
			contentReader := bytes.NewReader(contentJson)
			req, err = http.NewRequest(s.Method, s.Url, contentReader)
			req.Header.Set("Content-Type", "application/json")
		} else if s.Type == "form" {
			req, err = http.NewRequest(s.Method, s.Url, strings.NewReader(s.QueryData.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
	} else if s.Method == "GET" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	}
	for k, v := range s.Header {
		req.Header.Set(k, v)
	}
	resp, err = client.Do(req)
	if err != nil {
		return err, nil, ""
	}
	defer resp.Body.Close()

	// test callback func
	// TODO: be able to return body and using callback together. Right now, ifwe use ioutil.ReadAll data disappear
	if len(callback) != 0 {
		callback[0](resp)
		return nil, resp, ""
	} else {
		body, _ := ioutil.ReadAll(resp.Body)
		return nil, resp, string(body)
	}
}

func CustomRequest(options Options) (error, *http.Response, string) {
	fmt.Println(options)
	var (
		req  *http.Request
		err  error
		resp *http.Response
	)
	client := &http.Client{}
	if options.Method == "POST" {
		if options.Json != "" {
			content := strings.NewReader(options.Json)
			req, err = http.NewRequest(options.Method, options.Url, content)
			req.Header.Set("Content-Type", "application/json")
		}
	}
	resp, err = client.Do(req)
	if err != nil {
		return err, nil, ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	return nil, resp, string(body)
}

func main() {
	/*err, response, body:= Get("http://localhost:1337")
	  if err==nil && response.StatusCode == 200 {
	    fmt.Println(body)
	  }
	  fmt.Println(err, response, body)*/
	options := Options{Url: "http://localhost:1337", Method: "POST", Body: "hello", Json: `{ "hello":"hello"}`}
	CustomRequest(options)

	//s.post("/api/pet").send(`{"name":"tg"}`).end()
	Post("http://requestb.in/1f7ur5s1").
		Send(`nickname=a`).
		Set("Accept", "application/json").
		End(func(response Response) {
		fmt.Println(response)
	})
	/*client:= &http.Client{}
	  req,_ := http.NewRequest("GET", "http://localhost:1337", nil)
	  req.Header.Add("Content-Type","application/json")
	  fmt.Println("main",req)
	  res, _ :=  client.Do(req)
	  fmt.Println(res.Body)
	  /*const jsonStream =`{"sn":"sn1"}`
	  reader:=strings.NewReader(jsonStream)
	  resp,_ := http.Post("http://localhost:1337", "application/json", reader)
	  defer resp.Body.Close()
	  body,_ :=ioutil.ReadAll(resp.Body)
	  fmt.Println(resp)
	  fmt.Println(string(body))*/
}
<commit_msg>Arranged return parameters by putting error at the end<commit_after>package gorequest

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

type Options struct {
	Url    string
	Method string
	Body   string
	Json   string
	Header string
}

type Response *http.Response

type SuperAgent struct {
	Url       string
	Method    string
	Header    map[string]string
	Type      string
	Data      map[string]interface{}
	QueryData url.Values
}

func New() *SuperAgent {
	s := SuperAgent{
		Type:   "json",
		Data:   make(map[string]interface{}),
		Header: make(map[string]string)}
	return &s
}

func Get(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "GET",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		QueryData: url.Values{}}
	return newReq
}

// TODO: query func for get
func (s *SuperAgent) Query(content string) *SuperAgent {
	_ = content
	return s
}

func Post(targetUrl string) *SuperAgent {
	newReq := &SuperAgent{
		Url:       targetUrl,
		Method:    "POST",
		Type:      "json",
		Header:    make(map[string]string),
		Data:      make(map[string]interface{}),
		QueryData: url.Values{}}
	return newReq
}

func (s *SuperAgent) Set(param string, value string) *SuperAgent {
	s.Header[param] = value
	return s
}

func (s *SuperAgent) Send(content string) *SuperAgent {
	var val map[string]interface{}
	// check if it is json format
	if err := json.Unmarshal([]byte(content), &val); err == nil {
		s.Type = "json"
		for k, v := range val {
			s.Data[k] = v
		}
	} else {
		// not json format (just normal string)
		s.Type = "form"
		queryVal, _ := url.ParseQuery(content)
		for k, _ := range queryVal {
			s.QueryData.Add(k, queryVal.Get(k))
		}
	}

	return s
}

func (s *SuperAgent) End(callback ...func(response Response, body []byte)) (*http.Response, string, error) {
	var (
		req  *http.Request
		err  error
		resp *http.Response
	)
	client := &http.Client{}
	if s.Method == "POST" {
		if s.Type == "json" {
			contentJson, _ := json.Marshal(s.Data)
			contentReader := bytes.NewReader(contentJson)
			req, err = http.NewRequest(s.Method, s.Url, contentReader)
			req.Header.Set("Content-Type", "application/json")
		} else if s.Type == "form" {
			req, err = http.NewRequest(s.Method, s.Url, strings.NewReader(s.QueryData.Encode()))
			req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		}
	} else if s.Method == "GET" {
		req, err = http.NewRequest(s.Method, s.Url, nil)
	}
	for k, v := range s.Header {
		req.Header.Set(k, v)
	}
	resp, err = client.Do(req)
	if err != nil {
		return nil, "", err
	}
	defer resp.Body.Close()

	// test callback func
	// TODO: be able to return body and using callback together. Right now, if we use ioutil.ReadAll data disappear
	body, _ := ioutil.ReadAll(resp.Body)
	if len(callback) != 0 {
		callback[0](resp, body)
	}
	return resp, string(body), nil
}

func CustomRequest(options Options) (error, *http.Response, string) {
	fmt.Println(options)
	var (
		req  *http.Request
		err  error
		resp *http.Response
	)
	client := &http.Client{}
	if options.Method == "POST" {
		if options.Json != "" {
			content := strings.NewReader(options.Json)
			req, err = http.NewRequest(options.Method, options.Url, content)
			req.Header.Set("Content-Type", "application/json")
		}
	}
	resp, err = client.Do(req)
	if err != nil {
		return err, nil, ""
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	return nil, resp, string(body)
}

func main() {
	/*err, response, body:= Get("http://localhost:1337")
	  if err==nil && response.StatusCode == 200 {
	    fmt.Println(body)
	  }
	  fmt.Println(err, response, body)*/
	options := Options{Url: "http://localhost:1337", Method: "POST", Body: "hello", Json: `{ "hello":"hello"}`}
	CustomRequest(options)

	//s.post("/api/pet").send(`{"name":"tg"}`).end()
	Post("http://requestb.in/1f7ur5s1").
		Send(`nickname=a`).
		Set("Accept", "application/json").
		End(func(response Response, body []byte) {
		fmt.Println(response)
	})
	/*client:= &http.Client{}
	  req,_ := http.NewRequest("GET", "http://localhost:1337", nil)
	  req.Header.Add("Content-Type","application/json")
	  fmt.Println("main",req)
	  res, _ :=  client.Do(req)
	  fmt.Println(res.Body)
	  /*const jsonStream =`{"sn":"sn1"}`
	  reader:=strings.NewReader(jsonStream)
	  resp,_ := http.Post("http://localhost:1337", "application/json", reader)
	  defer resp.Body.Close()
	  body,_ :=ioutil.ReadAll(resp.Body)
	  fmt.Println(resp)
	  fmt.Println(string(body))*/
}
<|endoftext|>