<commit_before>package main

import (
	"bufio"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"github.com/dotcloud/docker/pkg/beam"
	"github.com/dotcloud/docker/pkg/beam/data"
	"github.com/dotcloud/docker/pkg/dockerscript"
	"github.com/dotcloud/docker/pkg/term"
	"io"
	"net"
	"net/url"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
)

func main() {
	devnull, err := Devnull()
	if err != nil {
		Fatal(err)
	}
	defer devnull.Close()
	if term.IsTerminal(0) {
		input := bufio.NewScanner(os.Stdin)
		for {
			os.Stdout.Write([]byte("beamsh> "))
			if !input.Scan() {
				break
			}
			line := input.Text()
			if len(line) != 0 {
				cmd, err := dockerscript.Parse(strings.NewReader(line))
				if err != nil {
					fmt.Fprintf(os.Stderr, "error: %v\n", err)
					continue
				}
				executeScript(devnull, cmd)
			}
			if err := input.Err(); err == io.EOF {
				break
			} else if err != nil {
				Fatal(err)
			}
		}
	} else {
		script, err := dockerscript.Parse(os.Stdin)
		if err != nil {
			Fatal("parse error: %v\n", err)
		}
		executeScript(devnull, script)
	}
}

func beamCopy(dst *net.UnixConn, src *net.UnixConn) (int, error) {
	var n int
	for {
		payload, attachment, err := beam.Receive(src)
		if err == io.EOF {
			return n, nil
		} else if err != nil {
			return n, err
		}
		if err := beam.Send(dst, payload, attachment); err != nil {
			if attachment != nil {
				attachment.Close()
			}
			return n, err
		}
		n++
	}
	panic("impossibru!")
	return n, nil
}

type Handler func([]string, *net.UnixConn, *net.UnixConn)

func Devnull() (*net.UnixConn, error) {
	priv, pub, err := beam.USocketPair()
	if err != nil {
		return nil, err
	}
	go func() {
		defer priv.Close()
		for {
			_, attachment, err := beam.Receive(priv)
			if err != nil {
				return
			}
			if attachment != nil {
				attachment.Close()
			}
		}
	}()
	return pub, nil
}

func scriptString(script []*dockerscript.Command) string {
	lines := make([]string, 0, len(script))
	for _, cmd := range script {
		line := strings.Join(cmd.Args, " ")
		if len(cmd.Children) > 0 {
			line += fmt.Sprintf(" { %s }", scriptString(cmd.Children))
		} else {
			line += " {}"
		}
		lines = append(lines, line)
	}
	return fmt.Sprintf("'%s'", strings.Join(lines, "; "))
}

func executeScript(client *net.UnixConn, script []*dockerscript.Command) error {
	Debugf("executeScript(%s)\n", scriptString(script))
	defer Debugf("executeScript(%s) DONE\n", scriptString(script))
	for _, cmd := range script {
		if err := executeCommand(client, cmd); err != nil {
			return err
		}
	}
	return nil
}

//	1) Find a handler for the command (if no handler, fail)
//	2) Attach new in & out pair to the handler
//	3) [in the background] Copy handler output to our own output
//	4) [in the background] Run the handler
//	5) Recursively executeScript() all children commands and wait for them to complete
//	6) Wait for handler to return and (shortly afterwards) output copy to complete
//	7)
func executeCommand(client *net.UnixConn, cmd *dockerscript.Command) error {
	Debugf("executeCommand(%s)\n", strings.Join(cmd.Args, " "))
	defer Debugf("executeCommand(%s) DONE\n", strings.Join(cmd.Args, " "))
	handler := GetHandler(cmd.Args[0])
	if handler == nil {
		return fmt.Errorf("no such command: %s", cmd.Args[0])
	}
	inPub, inPriv, err := beam.USocketPair()
	if err != nil {
		return err
	}
	// Don't close inPub here. We close it to signify the end of input once
	// all children are completed (guaranteeing that no more input will be sent
	// by children).
	// Otherwise we get a deadlock.
	defer inPriv.Close()
	outPub, outPriv, err := beam.USocketPair()
	if err != nil {
		return err
	}
	defer outPub.Close()
	// don't close outPriv here. It must be closed after the handler is called,
	// but before the copy tasks associated with it completes.
	// Otherwise we get a deadlock.
	var tasks sync.WaitGroup
	tasks.Add(2)
	go func() {
		handler(cmd.Args, inPriv, outPriv)
		// FIXME: do we need to outPriv.sync before closing it?
		Debugf("[%s] handler returned, closing output\n", strings.Join(cmd.Args, " "))
		outPriv.Close()
		tasks.Done()
	}()
	go func() {
		Debugf("[%s] copy start...\n", strings.Join(cmd.Args, " "))
		n, err := beamCopy(client, outPub)
		if err != nil {
			Fatal(err)
		}
		Debugf("[%s] copied %d messages\n", strings.Join(cmd.Args, " "), n)
		Debugf("[%s] copy done\n", strings.Join(cmd.Args, " "))
		tasks.Done()
	}()
	// depth-first execution of children commands
	// executeScript() blocks until all commands are completed
	executeScript(inPub, cmd.Children)
	inPub.Close()
	Debugf("[%s] waiting for handler and output copy to complete...\n", strings.Join(cmd.Args, " "))
	tasks.Wait()
	Debugf("[%s] handler and output copy complete!\n", strings.Join(cmd.Args, " "))
	return nil
}

func randomId() string {
	id := make([]byte, 4)
	io.ReadFull(rand.Reader, id)
	return hex.EncodeToString(id)
}

func GetHandler(name string) Handler {
	if name == "exec" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			cmd := exec.Command(args[1], args[2:]...)
			outR, outW, err := os.Pipe()
			if err != nil {
				return
			}
			cmd.Stdout = outW
			errR, errW, err := os.Pipe()
			if err != nil {
				return
			}
			cmd.Stderr = errW
			beam.Send(out, data.Empty().Set("cmd", "log", "stdout").Bytes(), outR)
			beam.Send(out, data.Empty().Set("cmd", "log", "stderr").Bytes(), errR)
			execErr := cmd.Run()
			var status string
			if execErr != nil {
				status = execErr.Error()
			} else {
				status = "ok"
			}
			beam.Send(out, data.Empty().Set("status", status).Set("cmd", args...).Bytes(), nil)
		}
	} else if name == "trace" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			for {
				p, a, err := beam.Receive(in)
				if err != nil {
					return
				}
				var msg string
				if pretty := data.Message(string(p)).Pretty(); pretty != "" {
					msg = pretty
				} else {
					msg = string(p)
				}
				if a != nil {
					msg = fmt.Sprintf("%s [%d]", msg, a.Fd())
				}
				fmt.Printf("===> %s\n", msg)
				beam.Send(out, p, a)
			}
		}
	} else if name == "emit" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			beam.Send(out, data.Empty().Set("foo", args[1:]...).Bytes(), nil)
		}
	} else if name == "print" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			for {
				_, a, err := beam.Receive(in)
				if err != nil {
					return
				}
				if a != nil {
					io.Copy(os.Stdout, a)
				}
			}
		}
	} else if name == "multiprint" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			var tasks sync.WaitGroup
			for {
				payload, a, err := beam.Receive(in)
				if err != nil {
					return
				}
				if a != nil {
					tasks.Add(1)
					go func(payload []byte, attachment *os.File) {
						defer tasks.Done()
						msg := data.Message(string(payload))
						input := bufio.NewScanner(attachment)
						for input.Scan() {
							fmt.Printf("[%s] %s\n", msg.Pretty(), input.Text())
						}
					}(payload, a)
				}
			}
			tasks.Wait()
		}
	} else if name == "listen" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			if len(args) != 2 {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", "wrong number of arguments").Bytes(), nil)
				return
			}
			u, err := url.Parse(args[1])
			if err != nil {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
				return
			}
			l, err := net.Listen(u.Scheme, u.Host)
			if err != nil {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
				return
			}
			for {
				conn, err := l.Accept()
				if err != nil {
					beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
					return
				}
				f, err := connToFile(conn)
				if err != nil {
					conn.Close()
					continue
				}
				beam.Send(out, data.Empty().Set("type", "socket").Set("remoteaddr", conn.RemoteAddr().String()).Bytes(), f)
			}
		}
	} else if name == "connect" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			if len(args) != 2 {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", "wrong number of arguments").Bytes(), nil)
				return
			}
			u, err := url.Parse(args[1])
			if err != nil {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
				return
			}
			var tasks sync.WaitGroup
			for {
				_, attachment, err := beam.Receive(in)
				if err != nil {
					break
				}
				if attachment == nil {
					continue
				}
				Logf("connecting to %s/%s\n", u.Scheme, u.Host)
				conn, err := net.Dial(u.Scheme, u.Host)
				if err != nil {
					beam.Send(out, data.Empty().Set("cmd", "msg", "connect error: " + err.Error()).Bytes(), nil)
					return
				}
				beam.Send(out, data.Empty().Set("cmd", "msg", "connection established").Bytes(), nil)
				tasks.Add(1)
				go func(attachment *os.File, conn net.Conn) {
					defer tasks.Done()
					// even when successful, conn.File() returns a duplicate,
					// so we must close the original
					var iotasks sync.WaitGroup
					iotasks.Add(2)
					go func(attachment *os.File, conn net.Conn) {
						defer iotasks.Done()
						io.Copy(attachment, conn)
					}(attachment, conn)
					go func(attachment *os.File, conn net.Conn) {
						defer iotasks.Done()
						io.Copy(conn, attachment)
					}(attachment, conn)
					iotasks.Wait()
					conn.Close()
					attachment.Close()
				}(attachment, conn)
			}
			tasks.Wait()
		}
	} else if name == "openfile" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			for _, name := range args {
				f, err := os.Open(name)
				if err != nil {
					continue
				}
				if err := beam.Send(out, data.Empty().Set("path", name).Set("type", "file").Bytes(), f); err != nil {
					f.Close()
				}
			}
		}
	}
	return nil
}

func connToFile(conn net.Conn) (f *os.File, err error) {
	if connWithFile, ok := conn.(interface { File() (*os.File, error) }); !ok {
		return nil, fmt.Errorf("no file descriptor available")
	} else {
		f, err = connWithFile.File()
		if err != nil {
			return nil, err
		}
	}
	return f, err
}

// 'status' is a notification of a job's status.
//
func parseEnv(args []string) ([]string, map[string]string) {
	var argsOut []string
	env := make(map[string]string)
	for _, word := range args[1:] {
		if strings.Contains(word, "=") {
			kv := strings.SplitN(word, "=", 2)
			key := kv[0]
			var val string
			if len(kv) == 2 {
				val = kv[1]
			}
			env[key] = val
		} else {
			argsOut = append(argsOut, word)
		}
	}
	return argsOut, env
}

type Msg struct {
	payload		[]byte
	attachment	*os.File
}

func Logf(msg string, args ...interface{}) (int, error) {
	if len(msg) == 0 || msg[len(msg) - 1] != '\n' {
		msg = msg + "\n"
	}
	msg = fmt.Sprintf("[%v] [%v] %s", os.Getpid(), path.Base(os.Args[0]), msg)
	return fmt.Printf(msg, args...)
}

func Debugf(msg string, args ...interface{}) {
	if os.Getenv("BEAMDEBUG") != "" {
		Logf(msg, args...)
	}
}

func Fatalf(msg string, args ...interface{})  {
	Logf(msg, args)
	os.Exit(1)
}

func Fatal(args ...interface{}) {
	Fatalf("%v", args[0])
}
<commit_msg>beam/examples/beamsh: 'exec' command correctly closes stdout and stderr when the process exists<commit_after>package main

import (
	"bufio"
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"github.com/dotcloud/docker/pkg/beam"
	"github.com/dotcloud/docker/pkg/beam/data"
	"github.com/dotcloud/docker/pkg/dockerscript"
	"github.com/dotcloud/docker/pkg/term"
	"io"
	"net"
	"net/url"
	"os"
	"os/exec"
	"path"
	"strings"
	"sync"
)

func main() {
	devnull, err := Devnull()
	if err != nil {
		Fatal(err)
	}
	defer devnull.Close()
	if term.IsTerminal(0) {
		input := bufio.NewScanner(os.Stdin)
		for {
			os.Stdout.Write([]byte("beamsh> "))
			if !input.Scan() {
				break
			}
			line := input.Text()
			if len(line) != 0 {
				cmd, err := dockerscript.Parse(strings.NewReader(line))
				if err != nil {
					fmt.Fprintf(os.Stderr, "error: %v\n", err)
					continue
				}
				executeScript(devnull, cmd)
			}
			if err := input.Err(); err == io.EOF {
				break
			} else if err != nil {
				Fatal(err)
			}
		}
	} else {
		script, err := dockerscript.Parse(os.Stdin)
		if err != nil {
			Fatal("parse error: %v\n", err)
		}
		executeScript(devnull, script)
	}
}

func beamCopy(dst *net.UnixConn, src *net.UnixConn) (int, error) {
	var n int
	for {
		payload, attachment, err := beam.Receive(src)
		if err == io.EOF {
			return n, nil
		} else if err != nil {
			return n, err
		}
		if err := beam.Send(dst, payload, attachment); err != nil {
			if attachment != nil {
				attachment.Close()
			}
			return n, err
		}
		n++
	}
	panic("impossibru!")
	return n, nil
}

type Handler func([]string, *net.UnixConn, *net.UnixConn)

func Devnull() (*net.UnixConn, error) {
	priv, pub, err := beam.USocketPair()
	if err != nil {
		return nil, err
	}
	go func() {
		defer priv.Close()
		for {
			_, attachment, err := beam.Receive(priv)
			if err != nil {
				return
			}
			if attachment != nil {
				attachment.Close()
			}
		}
	}()
	return pub, nil
}

func scriptString(script []*dockerscript.Command) string {
	lines := make([]string, 0, len(script))
	for _, cmd := range script {
		line := strings.Join(cmd.Args, " ")
		if len(cmd.Children) > 0 {
			line += fmt.Sprintf(" { %s }", scriptString(cmd.Children))
		} else {
			line += " {}"
		}
		lines = append(lines, line)
	}
	return fmt.Sprintf("'%s'", strings.Join(lines, "; "))
}

func executeScript(client *net.UnixConn, script []*dockerscript.Command) error {
	Debugf("executeScript(%s)\n", scriptString(script))
	defer Debugf("executeScript(%s) DONE\n", scriptString(script))
	for _, cmd := range script {
		if err := executeCommand(client, cmd); err != nil {
			return err
		}
	}
	return nil
}

//	1) Find a handler for the command (if no handler, fail)
//	2) Attach new in & out pair to the handler
//	3) [in the background] Copy handler output to our own output
//	4) [in the background] Run the handler
//	5) Recursively executeScript() all children commands and wait for them to complete
//	6) Wait for handler to return and (shortly afterwards) output copy to complete
//	7)
func executeCommand(client *net.UnixConn, cmd *dockerscript.Command) error {
	Debugf("executeCommand(%s)\n", strings.Join(cmd.Args, " "))
	defer Debugf("executeCommand(%s) DONE\n", strings.Join(cmd.Args, " "))
	handler := GetHandler(cmd.Args[0])
	if handler == nil {
		return fmt.Errorf("no such command: %s", cmd.Args[0])
	}
	inPub, inPriv, err := beam.USocketPair()
	if err != nil {
		return err
	}
	// Don't close inPub here. We close it to signify the end of input once
	// all children are completed (guaranteeing that no more input will be sent
	// by children).
	// Otherwise we get a deadlock.
	defer inPriv.Close()
	outPub, outPriv, err := beam.USocketPair()
	if err != nil {
		return err
	}
	defer outPub.Close()
	// don't close outPriv here. It must be closed after the handler is called,
	// but before the copy tasks associated with it completes.
	// Otherwise we get a deadlock.
	var tasks sync.WaitGroup
	tasks.Add(2)
	go func() {
		handler(cmd.Args, inPriv, outPriv)
		// FIXME: do we need to outPriv.sync before closing it?
		Debugf("[%s] handler returned, closing output\n", strings.Join(cmd.Args, " "))
		outPriv.Close()
		tasks.Done()
	}()
	go func() {
		Debugf("[%s] copy start...\n", strings.Join(cmd.Args, " "))
		n, err := beamCopy(client, outPub)
		if err != nil {
			Fatal(err)
		}
		Debugf("[%s] copied %d messages\n", strings.Join(cmd.Args, " "), n)
		Debugf("[%s] copy done\n", strings.Join(cmd.Args, " "))
		tasks.Done()
	}()
	// depth-first execution of children commands
	// executeScript() blocks until all commands are completed
	executeScript(inPub, cmd.Children)
	inPub.Close()
	Debugf("[%s] waiting for handler and output copy to complete...\n", strings.Join(cmd.Args, " "))
	tasks.Wait()
	Debugf("[%s] handler and output copy complete!\n", strings.Join(cmd.Args, " "))
	return nil
}

func randomId() string {
	id := make([]byte, 4)
	io.ReadFull(rand.Reader, id)
	return hex.EncodeToString(id)
}

func GetHandler(name string) Handler {
	if name == "exec" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			cmd := exec.Command(args[1], args[2:]...)
			outR, outW, err := os.Pipe()
			if err != nil {
				return
			}
			cmd.Stdout = outW
			errR, errW, err := os.Pipe()
			if err != nil {
				return
			}
			cmd.Stderr = errW
			beam.Send(out, data.Empty().Set("cmd", "log", "stdout").Bytes(), outR)
			beam.Send(out, data.Empty().Set("cmd", "log", "stderr").Bytes(), errR)
			execErr := cmd.Run()
			var status string
			if execErr != nil {
				status = execErr.Error()
			} else {
				status = "ok"
			}
			beam.Send(out, data.Empty().Set("status", status).Set("cmd", args...).Bytes(), nil)
			outW.Close()
			errW.Close()
		}
	} else if name == "trace" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			for {
				p, a, err := beam.Receive(in)
				if err != nil {
					return
				}
				var msg string
				if pretty := data.Message(string(p)).Pretty(); pretty != "" {
					msg = pretty
				} else {
					msg = string(p)
				}
				if a != nil {
					msg = fmt.Sprintf("%s [%d]", msg, a.Fd())
				}
				fmt.Printf("===> %s\n", msg)
				beam.Send(out, p, a)
			}
		}
	} else if name == "emit" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			beam.Send(out, data.Empty().Set("foo", args[1:]...).Bytes(), nil)
		}
	} else if name == "print" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			for {
				_, a, err := beam.Receive(in)
				if err != nil {
					return
				}
				if a != nil {
					io.Copy(os.Stdout, a)
				}
			}
		}
	} else if name == "multiprint" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			var tasks sync.WaitGroup
			for {
				payload, a, err := beam.Receive(in)
				if err != nil {
					return
				}
				if a != nil {
					tasks.Add(1)
					go func(payload []byte, attachment *os.File) {
						defer tasks.Done()
						msg := data.Message(string(payload))
						input := bufio.NewScanner(attachment)
						for input.Scan() {
							fmt.Printf("[%s] %s\n", msg.Pretty(), input.Text())
						}
					}(payload, a)
				}
			}
			tasks.Wait()
		}
	} else if name == "listen" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			if len(args) != 2 {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", "wrong number of arguments").Bytes(), nil)
				return
			}
			u, err := url.Parse(args[1])
			if err != nil {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
				return
			}
			l, err := net.Listen(u.Scheme, u.Host)
			if err != nil {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
				return
			}
			for {
				conn, err := l.Accept()
				if err != nil {
					beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
					return
				}
				f, err := connToFile(conn)
				if err != nil {
					conn.Close()
					continue
				}
				beam.Send(out, data.Empty().Set("type", "socket").Set("remoteaddr", conn.RemoteAddr().String()).Bytes(), f)
			}
		}
	} else if name == "connect" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			if len(args) != 2 {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", "wrong number of arguments").Bytes(), nil)
				return
			}
			u, err := url.Parse(args[1])
			if err != nil {
				beam.Send(out, data.Empty().Set("status", "1").Set("message", err.Error()).Bytes(), nil)
				return
			}
			var tasks sync.WaitGroup
			for {
				_, attachment, err := beam.Receive(in)
				if err != nil {
					break
				}
				if attachment == nil {
					continue
				}
				Logf("connecting to %s/%s\n", u.Scheme, u.Host)
				conn, err := net.Dial(u.Scheme, u.Host)
				if err != nil {
					beam.Send(out, data.Empty().Set("cmd", "msg", "connect error: " + err.Error()).Bytes(), nil)
					return
				}
				beam.Send(out, data.Empty().Set("cmd", "msg", "connection established").Bytes(), nil)
				tasks.Add(1)
				go func(attachment *os.File, conn net.Conn) {
					defer tasks.Done()
					// even when successful, conn.File() returns a duplicate,
					// so we must close the original
					var iotasks sync.WaitGroup
					iotasks.Add(2)
					go func(attachment *os.File, conn net.Conn) {
						defer iotasks.Done()
						io.Copy(attachment, conn)
					}(attachment, conn)
					go func(attachment *os.File, conn net.Conn) {
						defer iotasks.Done()
						io.Copy(conn, attachment)
					}(attachment, conn)
					iotasks.Wait()
					conn.Close()
					attachment.Close()
				}(attachment, conn)
			}
			tasks.Wait()
		}
	} else if name == "openfile" {
		return func(args []string, in *net.UnixConn, out *net.UnixConn) {
			for _, name := range args {
				f, err := os.Open(name)
				if err != nil {
					continue
				}
				if err := beam.Send(out, data.Empty().Set("path", name).Set("type", "file").Bytes(), f); err != nil {
					f.Close()
				}
			}
		}
	}
	return nil
}

func connToFile(conn net.Conn) (f *os.File, err error) {
	if connWithFile, ok := conn.(interface { File() (*os.File, error) }); !ok {
		return nil, fmt.Errorf("no file descriptor available")
	} else {
		f, err = connWithFile.File()
		if err != nil {
			return nil, err
		}
	}
	return f, err
}

// 'status' is a notification of a job's status.
//
func parseEnv(args []string) ([]string, map[string]string) {
	var argsOut []string
	env := make(map[string]string)
	for _, word := range args[1:] {
		if strings.Contains(word, "=") {
			kv := strings.SplitN(word, "=", 2)
			key := kv[0]
			var val string
			if len(kv) == 2 {
				val = kv[1]
			}
			env[key] = val
		} else {
			argsOut = append(argsOut, word)
		}
	}
	return argsOut, env
}

type Msg struct {
	payload		[]byte
	attachment	*os.File
}

func Logf(msg string, args ...interface{}) (int, error) {
	if len(msg) == 0 || msg[len(msg) - 1] != '\n' {
		msg = msg + "\n"
	}
	msg = fmt.Sprintf("[%v] [%v] %s", os.Getpid(), path.Base(os.Args[0]), msg)
	return fmt.Printf(msg, args...)
}

func Debugf(msg string, args ...interface{}) {
	if os.Getenv("BEAMDEBUG") != "" {
		Logf(msg, args...)
	}
}

func Fatalf(msg string, args ...interface{})  {
	Logf(msg, args)
	os.Exit(1)
}

func Fatal(args ...interface{}) {
	Fatalf("%v", args[0])
}
<|endoftext|>


<commit_before>package main

import (
	"io"
	"fmt"
	"os"
	"github.com/dotcloud/docker/pkg/dockerscript"
	"github.com/dotcloud/docker/pkg/beam"
	"github.com/dotcloud/docker/pkg/beam/data"
	"github.com/dotcloud/docker/pkg/term"
	"strings"
	"sync"
	"net"
	"path"
	"bufio"
	"strconv"
)

func main() {
	Debugf("Initializing engine\n")
	client, engine, err := beam.USocketPair()
	if err != nil {
		Fatal(err)
	}
	defer client.Close()
	go func() {
		Serve(engine, builtinsHandler)
		Debugf("Shutting down engine\n")
		engine.Close()
	}()
	if term.IsTerminal(0) {
		input := bufio.NewScanner(os.Stdin)
		for {
			os.Stdout.Write([]byte("beamsh> "))
			if !input.Scan() {
				break
			}
			line := input.Text()
			if len(line) != 0 {
				cmd, err := dockerscript.Parse(strings.NewReader(line))
				if err != nil {
					fmt.Fprintf(os.Stderr, "error: %v\n", err)
					continue
				}
				executeScript(client, cmd)
			}
			if err := input.Err(); err == io.EOF {
				break
			} else if err != nil {
				Fatal(err)
			}
		}
	} else {
		script, err := dockerscript.Parse(os.Stdin)
		if err != nil {
			Fatal("parse error: %v\n", err)
		}
		executeScript(client, script)
	}
}

func executeScript(client *net.UnixConn, script []*dockerscript.Command) {
	Debugf("%d commands:\n", len(script))
	for _, cmd := range script {
		job, err := beam.SendPipe(client, data.Empty().Set("cmd", cmd.Args...).Bytes())
		if err != nil {
			Fatal(err)
		}
		// TODO: pass a default handler to deal with 'status'
		// --> use beam chaining?
		Debugf("Listening for reply messages\n")
		Serve(job, builtinsHandler)
		Debugf("[%s] done\n", strings.Join(cmd.Args, " "))
	}
}

func parseMsgPayload(payload []byte) ([]string, error) {
	msg, err := data.Decode(string(payload))
	if err != nil {
		return nil, err
	}
	var cmd []string
	if c, exists := msg["cmd"]; exists {
		cmd = c
	}
	if len(cmd) == 0 {
		return nil, fmt.Errorf("empty command")
	}
	return cmd, nil
}

func CmdCat(args []string, f *os.File) {
	for _, name := range args[1:] {
		f, err := os.Open(name)
		if err != nil {
			continue
		}
		io.Copy(os.Stdout, f)
		f.Close()
	}
}

func CmdEcho(args []string, f *os.File) {
	resp, err := beam.FdConn(int(f.Fd()))
	if err != nil {
		Fatal(err)
		return
	}
	defer resp.Close()
	r, w, err := os.Pipe()
	if err != nil {
		return
	}
	Debugf("[CmdEcho] stdout pipe() r=%d w=%d\n", r.Fd(), w.Fd())
	if err := beam.Send(resp, data.Empty().Set("cmd", "log", "stdout").Bytes(), r); err != nil {
		return
	}
	fmt.Fprintln(w, strings.Join(args[1:], " "))
	w.Close()
}

func CmdExit(args []string, f *os.File) {
	var status int
	if len(args) > 1 {
		val, err := strconv.ParseInt(args[1], 10, 32)
		if err == nil {
			status = int(val)
		}
	}
	os.Exit(status)
}

// 'status' is a notification of a job's status.
//
func parseEnv(args []string) ([]string, map[string]string) {
	var argsOut []string
	env := make(map[string]string)
	for _, word := range args[1:] {
		if strings.Contains(word, "=") {
			kv := strings.SplitN(word, "=", 2)
			key := kv[0]
			var val string
			if len(kv) == 2 {
				val = kv[1]
			}
			env[key] = val
		} else {
			argsOut = append(argsOut, word)
		}
	}
	return argsOut, env
}

func CmdLog(args []string, f *os.File) {
	defer Debugf("CmdLog done\n")
	name := args[1]
	input := bufio.NewScanner(f)
	for input.Scan() {
		line := input.Text()
		if len(line) > 0 {
			fmt.Printf("[%s] %s\n", name, line)
		}
		if err := input.Err(); err != nil {
			fmt.Printf("[%s:%s]\n", name, err)
			break
		}
	}
}

func Serve(endpoint *net.UnixConn, handler func([]string, *os.File)) error {
	Debugf("[Serve %#v]\n", handler)
	defer Debugf("[Serve %#v] done\n", handler)
	var tasks sync.WaitGroup
	defer tasks.Wait()
	for {
		payload, attachment, err := beam.Receive(endpoint)
		if err != nil {
			return err
		}
		tasks.Add(1)
		// Handle new message
		go func(payload []byte, attachment *os.File) {
			Debugf("---> Handling '%s' [fd=%d]\n", payload, attachment.Fd())
			defer tasks.Done()
			if attachment != nil {
				defer func() {
					Debugf("---> Closing attachment [fd=%d] for msg '%s'\n", attachment.Fd(), payload)
					attachment.Close()
				}()
			} else {
				defer Debugf("---> No attachment to close for msg '%s'\n", payload)
			}
			args, err := parseMsgPayload(payload)
			if err != nil {
				Logf("error parsing beam message: %s\n", err)
				if attachment != nil {
					attachment.Close()
				}
				return
			}
			Debugf("---> calling handler for '%s'\n", args[0])
			handler(args, attachment)
			Debugf("---> handler returned for '%s'\n", args[0])
		}(payload, attachment)
	}
	return nil
}

func builtinsHandler(args []string, attachment *os.File) {
	if args[0] == "exit" {
		CmdExit(args, attachment)
	} else if args[0] == "cat" {
		CmdCat(args, attachment)
	} else if args[0] == "echo" {
		CmdEcho(args, attachment)
	} else if args[0] == "log" {
		CmdLog(args, attachment)
	}
}

func Logf(msg string, args ...interface{}) (int, error) {
	if len(msg) == 0 || msg[len(msg) - 1] != '\n' {
		msg = msg + "\n"
	}
	msg = fmt.Sprintf("[%v] [%v] %s", os.Getpid(), path.Base(os.Args[0]), msg)
	return fmt.Printf(msg, args...)
}

func Debugf(msg string, args ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		Logf(msg, args...)
	}
}

func Fatalf(msg string, args ...interface{})  {
	Logf(msg, args)
	os.Exit(1)
}

func Fatal(args ...interface{}) {
	Fatalf("%v", args[0])
}
<commit_msg>beam/examples/beamsh: fix a bug in the log command<commit_after>package main

import (
	"io"
	"fmt"
	"os"
	"github.com/dotcloud/docker/pkg/dockerscript"
	"github.com/dotcloud/docker/pkg/beam"
	"github.com/dotcloud/docker/pkg/beam/data"
	"github.com/dotcloud/docker/pkg/term"
	"strings"
	"sync"
	"net"
	"path"
	"bufio"
	"strconv"
)

func main() {
	Debugf("Initializing engine\n")
	client, engine, err := beam.USocketPair()
	if err != nil {
		Fatal(err)
	}
	defer client.Close()
	go func() {
		Serve(engine, builtinsHandler)
		Debugf("Shutting down engine\n")
		engine.Close()
	}()
	if term.IsTerminal(0) {
		input := bufio.NewScanner(os.Stdin)
		for {
			os.Stdout.Write([]byte("beamsh> "))
			if !input.Scan() {
				break
			}
			line := input.Text()
			if len(line) != 0 {
				cmd, err := dockerscript.Parse(strings.NewReader(line))
				if err != nil {
					fmt.Fprintf(os.Stderr, "error: %v\n", err)
					continue
				}
				executeScript(client, cmd)
			}
			if err := input.Err(); err == io.EOF {
				break
			} else if err != nil {
				Fatal(err)
			}
		}
	} else {
		script, err := dockerscript.Parse(os.Stdin)
		if err != nil {
			Fatal("parse error: %v\n", err)
		}
		executeScript(client, script)
	}
}

func executeScript(client *net.UnixConn, script []*dockerscript.Command) {
	Debugf("%d commands:\n", len(script))
	for _, cmd := range script {
		job, err := beam.SendPipe(client, data.Empty().Set("cmd", cmd.Args...).Bytes())
		if err != nil {
			Fatal(err)
		}
		// TODO: pass a default handler to deal with 'status'
		// --> use beam chaining?
		Debugf("Listening for reply messages\n")
		Serve(job, builtinsHandler)
		Debugf("[%s] done\n", strings.Join(cmd.Args, " "))
	}
}

func parseMsgPayload(payload []byte) ([]string, error) {
	msg, err := data.Decode(string(payload))
	if err != nil {
		return nil, err
	}
	var cmd []string
	if c, exists := msg["cmd"]; exists {
		cmd = c
	}
	if len(cmd) == 0 {
		return nil, fmt.Errorf("empty command")
	}
	return cmd, nil
}

func CmdCat(args []string, f *os.File) {
	for _, name := range args[1:] {
		f, err := os.Open(name)
		if err != nil {
			continue
		}
		io.Copy(os.Stdout, f)
		f.Close()
	}
}

func CmdEcho(args []string, f *os.File) {
	resp, err := beam.FdConn(int(f.Fd()))
	if err != nil {
		Fatal(err)
		return
	}
	defer resp.Close()
	r, w, err := os.Pipe()
	if err != nil {
		return
	}
	Debugf("[CmdEcho] stdout pipe() r=%d w=%d\n", r.Fd(), w.Fd())
	if err := beam.Send(resp, data.Empty().Set("cmd", "log", "stdout").Bytes(), r); err != nil {
		return
	}
	fmt.Fprintln(w, strings.Join(args[1:], " "))
	w.Close()
}

func CmdExit(args []string, f *os.File) {
	var status int
	if len(args) > 1 {
		val, err := strconv.ParseInt(args[1], 10, 32)
		if err == nil {
			status = int(val)
		}
	}
	os.Exit(status)
}

// 'status' is a notification of a job's status.
//
func parseEnv(args []string) ([]string, map[string]string) {
	var argsOut []string
	env := make(map[string]string)
	for _, word := range args[1:] {
		if strings.Contains(word, "=") {
			kv := strings.SplitN(word, "=", 2)
			key := kv[0]
			var val string
			if len(kv) == 2 {
				val = kv[1]
			}
			env[key] = val
		} else {
			argsOut = append(argsOut, word)
		}
	}
	return argsOut, env
}

func CmdLog(args []string, f *os.File) {
	defer Debugf("CmdLog done\n")
	var name string
	if len(args) > 0 {
		name = args[1]
	}
	input := bufio.NewScanner(f)
	for input.Scan() {
		line := input.Text()
		if len(line) > 0 {
			fmt.Printf("[%s] %s\n", name, line)
		}
		if err := input.Err(); err != nil {
			fmt.Printf("[%s:%s]\n", name, err)
			break
		}
	}
}

func Serve(endpoint *net.UnixConn, handler func([]string, *os.File)) error {
	Debugf("[Serve %#v]\n", handler)
	defer Debugf("[Serve %#v] done\n", handler)
	var tasks sync.WaitGroup
	defer tasks.Wait()
	for {
		payload, attachment, err := beam.Receive(endpoint)
		if err != nil {
			return err
		}
		tasks.Add(1)
		// Handle new message
		go func(payload []byte, attachment *os.File) {
			Debugf("---> Handling '%s' [fd=%d]\n", payload, attachment.Fd())
			defer tasks.Done()
			if attachment != nil {
				defer func() {
					Debugf("---> Closing attachment [fd=%d] for msg '%s'\n", attachment.Fd(), payload)
					attachment.Close()
				}()
			} else {
				defer Debugf("---> No attachment to close for msg '%s'\n", payload)
			}
			args, err := parseMsgPayload(payload)
			if err != nil {
				Logf("error parsing beam message: %s\n", err)
				if attachment != nil {
					attachment.Close()
				}
				return
			}
			Debugf("---> calling handler for '%s'\n", args[0])
			handler(args, attachment)
			Debugf("---> handler returned for '%s'\n", args[0])
		}(payload, attachment)
	}
	return nil
}

func builtinsHandler(args []string, attachment *os.File) {
	if args[0] == "exit" {
		CmdExit(args, attachment)
	} else if args[0] == "cat" {
		CmdCat(args, attachment)
	} else if args[0] == "echo" {
		CmdEcho(args, attachment)
	} else if args[0] == "log" {
		CmdLog(args, attachment)
	}
}

func Logf(msg string, args ...interface{}) (int, error) {
	if len(msg) == 0 || msg[len(msg) - 1] != '\n' {
		msg = msg + "\n"
	}
	msg = fmt.Sprintf("[%v] [%v] %s", os.Getpid(), path.Base(os.Args[0]), msg)
	return fmt.Printf(msg, args...)
}

func Debugf(msg string, args ...interface{}) {
	if os.Getenv("DEBUG") != "" {
		Logf(msg, args...)
	}
}

func Fatalf(msg string, args ...interface{})  {
	Logf(msg, args)
	os.Exit(1)
}

func Fatal(args ...interface{}) {
	Fatalf("%v", args[0])
}
<|endoftext|>


<commit_before>package main

import (
  "net/http"
  "github.com/pilu/traffic"
  "fmt"
  "log"
  "os"
)

type AppLogger struct {
  Name string
  *log.Logger
}

func (appLogger *AppLogger) requestLogFunc(statusCode int, r *http.Request) {
  appLogger.Printf("%s: %d - %s\n", appLogger.Name, statusCode, r.URL)
}

var logger *AppLogger

func rootHandler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "Hello World\n")
}

func pageHandler(w http.ResponseWriter, r *http.Request) {
  params := r.URL.Query()
  fmt.Fprintf(w, "Category ID: %s\n", params.Get("category_id"))
  fmt.Fprintf(w, "Page ID: %s\n", params.Get("id"))
}

func customNotFoundHandler(w http.ResponseWriter, r *http.Request) {
  w.WriteHeader(http.StatusNotFound)
  fmt.Fprintf(w, "Page not found: %s\n", r.URL.Path)
}

func customBeforeFilter(w http.ResponseWriter, r *http.Request) {
  w.Header().Add("X-APP-NAME", "My App")
}

func init() {
  logger = &AppLogger{
    Name: "Test App",
    Logger: log.New(os.Stderr, "", log.LstdFlags),
  }
}

func main() {
  router := traffic.New()

  // Logger
  router.RequestLogFunc = logger.requestLogFunc

  // Routes
  router.Get("/", rootHandler)
  router.Get("/categories/:category_id/pages/:id", pageHandler)

  // Custom not found handler
  router.NotFoundHandler = customNotFoundHandler

  // Executed before all handlers
  router.BeforeFilter = customBeforeFilter

  http.Handle("/", router)
  http.ListenAndServe(":7000", nil)
}
<commit_msg>fixed examples<commit_after>package main

import (
  "net/http"
  "github.com/pilu/traffic"
  "fmt"
  "log"
  "os"
)

type AppLogger struct {
  Name string
  *log.Logger
}

func (appLogger *AppLogger) requestLogFunc(statusCode int, r *http.Request) {
  appLogger.Printf("%s: %d - %s\n", appLogger.Name, statusCode, r.URL)
}

var logger *AppLogger

func rootHandler(w http.ResponseWriter, r *http.Request) {
  fmt.Fprint(w, "Hello World\n")
}

func pageHandler(w http.ResponseWriter, r *http.Request) {
  params := r.URL.Query()
  fmt.Fprintf(w, "Category ID: %s\n", params.Get("category_id"))
  fmt.Fprintf(w, "Page ID: %s\n", params.Get("id"))
}

func customNotFoundHandler(w http.ResponseWriter, r *http.Request) {
  w.WriteHeader(http.StatusNotFound)
  fmt.Fprintf(w, "Page not found: %s\n", r.URL.Path)
}

func customBeforeFilter(w http.ResponseWriter, r *http.Request) bool {
  w.Header().Add("X-APP-NAME", "My App")

  return true
}

func init() {
  logger = &AppLogger{
    Name: "Test App",
    Logger: log.New(os.Stderr, "", log.LstdFlags),
  }
}

func main() {
  router := traffic.New()

  // Logger
  router.RequestLogFunc = logger.requestLogFunc

  // Routes
  router.Get("/", rootHandler)
  router.Get("/categories/:category_id/pages/:id", pageHandler)

  // Custom not found handler
  router.NotFoundHandler = customNotFoundHandler

  // Executed before all handlers
  router.AddBeforeFilter(customBeforeFilter)

  http.Handle("/", router)
  http.ListenAndServe(":7000", nil)
}
<|endoftext|>


<commit_before>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package input

import (
	"path/filepath"
	"testing"

	"github.com/caixw/apidoc/doc"
	"github.com/issue9/assert"
)

func TestParse(t *testing.T) {
	a := assert.New(t)

	testParse(a, "cpp")
	testParse(a, "go")
	testParse(a, "java")
	testParse(a, "javascript")
	testParse(a, "perl")
	testParse(a, "php")
	testParse(a, "python")
	testParse(a, "ruby")
	testParse(a, "rust")
}

func testParse(a *assert.Assertion, lang string) {
	o := &Options{
		Lang:      lang,
		Dir:       "./testdata/" + lang,
		Recursive: true,
	}
	a.NotError(o.Init()) // 初始化扩展名信息

	docs, err := Parse(o)
	a.NotError(err).
		NotNil(docs).
		Equal(len(docs.Apis), 2)

	// test1.xx
	api0 := docs.Apis[0]
	api1 := docs.Apis[1]
	a.Equal(api0.URL, "/users/login").
		Equal(api1.URL, "/users/login").
		Equal(api0.Group, "users").
		Equal(api1.Group, "users")

		// doc.xx
	a.Equal(docs.Title, "title of api").
		Equal(docs.Version, "2.9").
		Equal(docs.Content, "\n\n line1\n line2\n")
}

func TestParseFile(t *testing.T) {
	a := assert.New(t)

	testParseFile(a, "cpp", "./testdata/cpp/test1.c")
	testParseFile(a, "go", "./testdata/go/test1.go")
	testParseFile(a, "java", "./testdata/java/test1.java")
	testParseFile(a, "javascript", "./testdata/javascript/test1.js")
	testParseFile(a, "perl", "./testdata/perl/test1.pl")
	testParseFile(a, "php", "./testdata/php/test1.php")
	testParseFile(a, "python", "./testdata/python/test1.py")
	testParseFile(a, "ruby", "./testdata/ruby/test1.rb")
	testParseFile(a, "rust", "./testdata/rust/test1.rs")
}

func testParseFile(a *assert.Assertion, lang string, path string) {
	docs := doc.New()
	a.NotNil(docs)

	b, found := langs[lang]
	if !found {
		a.T().Error("不支持该语言")
	}

	parseFile(docs, path, b)
	a.Equal(2, len(docs.Apis))

	api0 := docs.Apis[0]
	api1 := docs.Apis[1]

	a.T().Log(api0.Success.Examples[0])
	a.T().Log(api1.Success.Examples[0])

	a.Equal(api0.URL, "/users/login").
		Equal(api1.URL, "/users/login").
		Equal(api0.Group, "users").
		Equal(api1.Group, "users")

	if api0.Method == "POST" {
		a.Equal(api1.Method, "DELETE").
			Equal(1, len(api1.Request.Headers))
	} else {
		a.Equal(api0.Method, "DELETE").
			Equal(api1.Method, "POST").
			Equal(1, len(api0.Request.Headers))
	}
}

func TestRecursivePath(t *testing.T) {
	a := assert.New(t)

	opt := &Options{Dir: "./testdir", Recursive: false, Exts: []string{".c", ".h"}}
	paths, err := recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testfile.c"),
		filepath.Join("testdir", "testfile.h"),
	})

	opt.Dir = "./testdir"
	opt.Recursive = true
	opt.Exts = []string{".1", ".2"}
	paths, err = recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testdir1", "testfile.1"),
		filepath.Join("testdir", "testdir1", "testfile.2"),
		filepath.Join("testdir", "testdir2", "testfile.1"),
		filepath.Join("testdir", "testfile.1"),
	})

	opt.Dir = "./testdir/testdir1"
	opt.Recursive = true
	opt.Exts = []string{".1", ".2"}
	paths, err = recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testdir1", "testfile.1"),
		filepath.Join("testdir", "testdir1", "testfile.2"),
	})

	opt.Dir = "./testdir"
	opt.Recursive = true
	opt.Exts = []string{".1"}
	paths, err = recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testdir1", "testfile.1"),
		filepath.Join("testdir", "testdir2", "testfile.1"),
		filepath.Join("testdir", "testfile.1"),
	})
}

func TestOptions_Init(t *testing.T) {
	a := assert.New(t)

	o := &Options{Dir: "not exists"}
	a.Error(o.Init())

	o.Dir = "./"
	o.Lang = "not exists"
	a.Error(o.Init())

	// 未指定扩展名，则使用系统默认的
	o.Lang = "cpp"
	a.NotError(o.Init())
	a.Equal(o.Exts, langExts["cpp"])

	// 指定了 Exts，自动调整扩展名样式。
	o.Lang = "cpp"
	o.Exts = []string{"c1", ".c2"}
	a.NotError(o.Init())
	a.Equal(o.Exts, []string{".c1", ".c2"})
}
<commit_msg>从master分支合并<commit_after>// Copyright 2016 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

package input

import (
	"path/filepath"
	"testing"

	"github.com/caixw/apidoc/doc"
	"github.com/issue9/assert"
)

func TestParse(t *testing.T) {
	a := assert.New(t)

	testParse(a, "cpp")
	testParse(a, "go")
	testParse(a, "java")
	testParse(a, "javascript")
	testParse(a, "perl")
	testParse(a, "php")
	testParse(a, "python")
	testParse(a, "ruby")
	testParse(a, "rust")
}

func testParse(a *assert.Assertion, lang string) {
	o := &Options{
		Lang:      lang,
		Dir:       "./testdata/" + lang,
		Recursive: true,
	}
	a.NotError(o.Init()) // 初始化扩展名信息

	docs, err := Parse(o)
	a.NotError(err).
		NotNil(docs).
		Equal(len(docs.Apis), 2)

	// test1.xx
	api0 := docs.Apis[0]
	api1 := docs.Apis[1]
	a.Equal(api0.URL, "/users/login").
		Equal(api1.URL, "/users/login").
		Equal(api0.Group, "users").
		Equal(api1.Group, "users")

		// doc.xx
	a.Equal(docs.Title, "title of api").
		Equal(docs.Version, "2.9").
		Equal(docs.Content, "\n\n line1\n line2\n")
}

func TestParseFile(t *testing.T) {
	a := assert.New(t)

	testParseFile(a, "cpp", "./testdata/cpp/test1.c")
	testParseFile(a, "go", "./testdata/go/test1.go")
	testParseFile(a, "java", "./testdata/java/test1.java")
	testParseFile(a, "javascript", "./testdata/javascript/test1.js")
	testParseFile(a, "perl", "./testdata/perl/test1.pl")
	testParseFile(a, "php", "./testdata/php/test1.php")
	testParseFile(a, "python", "./testdata/python/test1.py")
	testParseFile(a, "ruby", "./testdata/ruby/test1.rb")
	testParseFile(a, "rust", "./testdata/rust/test1.rs")
}

func testParseFile(a *assert.Assertion, lang string, path string) {
	docs := doc.New()
	a.NotNil(docs)

	b, found := langs[lang]
	if !found {
		a.T().Error("不支持该语言")
	}

	parseFile(docs, path, b, nil)
	a.Equal(2, len(docs.Apis))

	api0 := docs.Apis[0]
	api1 := docs.Apis[1]

	a.T().Log(api0.Success.Examples[0])
	a.T().Log(api1.Success.Examples[0])

	a.Equal(api0.URL, "/users/login").
		Equal(api1.URL, "/users/login").
		Equal(api0.Group, "users").
		Equal(api1.Group, "users")

	if api0.Method == "POST" {
		a.Equal(api1.Method, "DELETE").
			Equal(1, len(api1.Request.Headers))
	} else {
		a.Equal(api0.Method, "DELETE").
			Equal(api1.Method, "POST").
			Equal(1, len(api0.Request.Headers))
	}
}

func TestRecursivePath(t *testing.T) {
	a := assert.New(t)

	opt := &Options{Dir: "./testdir", Recursive: false, Exts: []string{".c", ".h"}}
	paths, err := recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testfile.c"),
		filepath.Join("testdir", "testfile.h"),
	})

	opt.Dir = "./testdir"
	opt.Recursive = true
	opt.Exts = []string{".1", ".2"}
	paths, err = recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testdir1", "testfile.1"),
		filepath.Join("testdir", "testdir1", "testfile.2"),
		filepath.Join("testdir", "testdir2", "testfile.1"),
		filepath.Join("testdir", "testfile.1"),
	})

	opt.Dir = "./testdir/testdir1"
	opt.Recursive = true
	opt.Exts = []string{".1", ".2"}
	paths, err = recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testdir1", "testfile.1"),
		filepath.Join("testdir", "testdir1", "testfile.2"),
	})

	opt.Dir = "./testdir"
	opt.Recursive = true
	opt.Exts = []string{".1"}
	paths, err = recursivePath(opt)
	a.NotError(err)
	a.Contains(paths, []string{
		filepath.Join("testdir", "testdir1", "testfile.1"),
		filepath.Join("testdir", "testdir2", "testfile.1"),
		filepath.Join("testdir", "testfile.1"),
	})
}

func TestOptions_Init(t *testing.T) {
	a := assert.New(t)

	o := &Options{Dir: "not exists"}
	a.Error(o.Init())

	o.Dir = "./"
	o.Lang = "not exists"
	a.Error(o.Init())

	// 未指定扩展名，则使用系统默认的
	o.Lang = "cpp"
	a.NotError(o.Init())
	a.Equal(o.Exts, langExts["cpp"])

	// 指定了 Exts，自动调整扩展名样式。
	o.Lang = "cpp"
	o.Exts = []string{"c1", ".c2"}
	a.NotError(o.Init())
	a.Equal(o.Exts, []string{".c1", ".c2"})
}
<|endoftext|>


<commit_before>package user

import (
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"

	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/validate"
)

func TestProtect(t *testing.T) {

	var err error

	Secret = "secret"

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(validate.ValidateParams())
	router.Use(Auth(true))
	router.Use(Protect())

	router.GET("/important/:ib", func(c *gin.Context) {
		c.String(200, "OK")
		return
	})

	first := performRequest(router, "GET", "/important/1")

	assert.Equal(t, first.Code, 401, "HTTP request code should match")

	user := DefaultUser()
	user.SetID(2)
	user.SetAuthenticated()

	user.hash, err = HashPassword("testpassword")
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotNil(t, user.hash, "password should be returned")
	}

	assert.True(t, user.ComparePassword("testpassword"), "Password should validate")

	token, err := user.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	firstrows := sqlmock.NewRows([]string{"role"}).AddRow(1)

	mock.ExpectQuery(`SELECT COALESCE`).WillReturnRows(firstrows)

	second := performJwtHeaderRequest(router, "GET", "/important/1", token)

	assert.Equal(t, second.Code, 403, "HTTP request code should match")

	secondrows := sqlmock.NewRows([]string{"role"}).AddRow(3)

	mock.ExpectQuery(`SELECT COALESCE`).WillReturnRows(secondrows)

	third := performJwtHeaderRequest(router, "GET", "/important/1", token)

	assert.Equal(t, third.Code, 200, "HTTP request code should match")

	assert.NoError(t, mock.ExpectationsWereMet(), "An error was not expected")

}
<commit_msg>fix tests<commit_after>package user

import (
	"testing"

	"github.com/gin-gonic/gin"
	"github.com/stretchr/testify/assert"
	"gopkg.in/DATA-DOG/go-sqlmock.v1"

	"github.com/eirka/eirka-libs/db"
	"github.com/eirka/eirka-libs/validate"
)

func TestProtect(t *testing.T) {

	var err error

	Secret = "secret"

	mock, err := db.NewTestDb()
	assert.NoError(t, err, "An error was not expected")

	gin.SetMode(gin.ReleaseMode)

	router := gin.New()

	router.Use(validate.ValidateParams())
	router.Use(Auth(true))
	router.Use(Protect())

	router.GET("/important/:ib", func(c *gin.Context) {
		c.String(200, "OK")
		return
	})

	first := performRequest(router, "GET", "/important/1")

	assert.Equal(t, first.Code, 403, "HTTP request code should match")

	user := DefaultUser()
	user.SetID(2)
	user.SetAuthenticated()

	user.hash, err = HashPassword("testpassword")
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotNil(t, user.hash, "password should be returned")
	}

	assert.True(t, user.ComparePassword("testpassword"), "Password should validate")

	token, err := user.CreateToken()
	if assert.NoError(t, err, "An error was not expected") {
		assert.NotEmpty(t, token, "token should be returned")
	}

	firstrows := sqlmock.NewRows([]string{"role"}).AddRow(1)

	mock.ExpectQuery(`SELECT COALESCE`).WillReturnRows(firstrows)

	second := performJwtHeaderRequest(router, "GET", "/important/1", token)

	assert.Equal(t, second.Code, 403, "HTTP request code should match")

	secondrows := sqlmock.NewRows([]string{"role"}).AddRow(3)

	mock.ExpectQuery(`SELECT COALESCE`).WillReturnRows(secondrows)

	third := performJwtHeaderRequest(router, "GET", "/important/1", token)

	assert.Equal(t, third.Code, 200, "HTTP request code should match")

	assert.NoError(t, mock.ExpectationsWereMet(), "An error was not expected")

}
<|endoftext|>


<commit_before>package storage

import (
	"bytes"
	"io"
	"path"
	"sort"
	"strconv"

	"github.com/anacrolix/missinggo/v2/resource"

	"github.com/anacrolix/torrent/metainfo"
)

type piecePerResource struct {
	p PieceProvider
}

func NewResourcePieces(p PieceProvider) ClientImpl {
	return &piecePerResource{
		p: p,
	}
}

type piecePerResourceTorrentImpl struct {
	piecePerResource
}

func (piecePerResourceTorrentImpl) Close() error {
	return nil
}

func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
	return piecePerResourceTorrentImpl{s}, nil
}

func (s piecePerResource) Piece(p metainfo.Piece) PieceImpl {
	return piecePerResourcePiece{
		mp: p,
		rp: s.p,
	}
}

type PieceProvider interface {
	resource.Provider
}

type ConsecutiveChunkWriter interface {
	WriteConsecutiveChunks(prefix string, _ io.Writer) (int64, error)
}

type piecePerResourcePiece struct {
	mp metainfo.Piece
	rp resource.Provider
}

var _ io.WriterTo = piecePerResourcePiece{}

func (s piecePerResourcePiece) WriteTo(w io.Writer) (int64, error) {
	if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
		if s.mustIsComplete() {
			return ccw.WriteConsecutiveChunks(s.completedInstancePath(), w)
		} else {
			return ccw.WriteConsecutiveChunks(s.incompleteDirPath()+"/", w)
		}
	}
	return io.Copy(w, io.NewSectionReader(s, 0, s.mp.Length()))
}

// Returns if the piece is complete. Ok should be true, because we are the definitive source of
// truth here.
func (s piecePerResourcePiece) mustIsComplete() bool {
	completion := s.Completion()
	if !completion.Ok {
		panic("must know complete definitively")
	}
	return completion.Complete
}

func (s piecePerResourcePiece) Completion() Completion {
	fi, err := s.completed().Stat()
	return Completion{
		Complete: err == nil && fi.Size() == s.mp.Length(),
		Ok:       true,
	}
}

func (s piecePerResourcePiece) MarkComplete() error {
	incompleteChunks := s.getChunks()
	err := s.completed().Put(io.NewSectionReader(incompleteChunks, 0, s.mp.Length()))
	if err == nil {
		for _, c := range incompleteChunks {
			c.instance.Delete()
		}
	}
	return err
}

func (s piecePerResourcePiece) MarkNotComplete() error {
	return s.completed().Delete()
}

func (s piecePerResourcePiece) ReadAt(b []byte, off int64) (int, error) {
	if s.mustIsComplete() {
		return s.completed().ReadAt(b, off)
	}
	return s.getChunks().ReadAt(b, off)
}

func (s piecePerResourcePiece) WriteAt(b []byte, off int64) (n int, err error) {
	i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), strconv.FormatInt(off, 10)))
	if err != nil {
		panic(err)
	}
	r := bytes.NewReader(b)
	err = i.Put(r)
	n = len(b) - r.Len()
	return
}

type chunk struct {
	offset   int64
	instance resource.Instance
}

type chunks []chunk

func (me chunks) ReadAt(b []byte, off int64) (int, error) {
	for {
		if len(me) == 0 {
			return 0, io.EOF
		}
		if me[0].offset <= off {
			break
		}
		me = me[1:]
	}
	n, err := me[0].instance.ReadAt(b, off-me[0].offset)
	if n == len(b) {
		return n, nil
	}
	if err == nil || err == io.EOF {
		n_, err := me[1:].ReadAt(b[n:], off+int64(n))
		return n + n_, err
	}
	return n, err
}

func (s piecePerResourcePiece) getChunks() (chunks chunks) {
	names, err := s.incompleteDir().Readdirnames()
	if err != nil {
		return
	}
	for _, n := range names {
		offset, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			panic(err)
		}
		i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), n))
		if err != nil {
			panic(err)
		}
		chunks = append(chunks, chunk{offset, i})
	}
	sort.Slice(chunks, func(i, j int) bool {
		return chunks[i].offset < chunks[j].offset
	})
	return
}

func (s piecePerResourcePiece) completedInstancePath() string {
	return path.Join("completed", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) completed() resource.Instance {
	i, err := s.rp.NewInstance(s.completedInstancePath())
	if err != nil {
		panic(err)
	}
	return i
}

func (s piecePerResourcePiece) incompleteDirPath() string {
	return path.Join("incompleted", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) incompleteDir() resource.DirInstance {
	i, err := s.rp.NewInstance(s.incompleteDirPath())
	if err != nil {
		panic(err)
	}
	return i.(resource.DirInstance)
}
<commit_msg>Use ConsecutiveChunkWriter in MarkComplete for piece resource storage if available<commit_after>package storage

import (
	"bytes"
	"io"
	"path"
	"sort"
	"strconv"

	"github.com/anacrolix/missinggo/v2/resource"

	"github.com/anacrolix/torrent/metainfo"
)

type piecePerResource struct {
	p PieceProvider
}

func NewResourcePieces(p PieceProvider) ClientImpl {
	return &piecePerResource{
		p: p,
	}
}

type piecePerResourceTorrentImpl struct {
	piecePerResource
}

func (piecePerResourceTorrentImpl) Close() error {
	return nil
}

func (s piecePerResource) OpenTorrent(info *metainfo.Info, infoHash metainfo.Hash) (TorrentImpl, error) {
	return piecePerResourceTorrentImpl{s}, nil
}

func (s piecePerResource) Piece(p metainfo.Piece) PieceImpl {
	return piecePerResourcePiece{
		mp: p,
		rp: s.p,
	}
}

type PieceProvider interface {
	resource.Provider
}

type ConsecutiveChunkWriter interface {
	WriteConsecutiveChunks(prefix string, _ io.Writer) (int64, error)
}

type piecePerResourcePiece struct {
	mp metainfo.Piece
	rp resource.Provider
}

var _ io.WriterTo = piecePerResourcePiece{}

func (s piecePerResourcePiece) WriteTo(w io.Writer) (int64, error) {
	if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
		if s.mustIsComplete() {
			return ccw.WriteConsecutiveChunks(s.completedInstancePath(), w)
		} else {
			return s.writeConsecutiveIncompleteChunks(ccw, w)
		}
	}
	return io.Copy(w, io.NewSectionReader(s, 0, s.mp.Length()))
}

func (s piecePerResourcePiece) writeConsecutiveIncompleteChunks(ccw ConsecutiveChunkWriter, w io.Writer) (int64, error) {
	return ccw.WriteConsecutiveChunks(s.incompleteDirPath()+"/", w)
}

// Returns if the piece is complete. Ok should be true, because we are the definitive source of
// truth here.
func (s piecePerResourcePiece) mustIsComplete() bool {
	completion := s.Completion()
	if !completion.Ok {
		panic("must know complete definitively")
	}
	return completion.Complete
}

func (s piecePerResourcePiece) Completion() Completion {
	fi, err := s.completed().Stat()
	return Completion{
		Complete: err == nil && fi.Size() == s.mp.Length(),
		Ok:       true,
	}
}

func (s piecePerResourcePiece) MarkComplete() error {
	incompleteChunks := s.getChunks()
	r, w := io.Pipe()
	go func() {
		var err error
		if ccw, ok := s.rp.(ConsecutiveChunkWriter); ok {
			_, err = s.writeConsecutiveIncompleteChunks(ccw, w)
		} else {
			_, err = io.Copy(w, io.NewSectionReader(incompleteChunks, 0, s.mp.Length()))
		}
		w.CloseWithError(err)
	}()
	err := s.completed().Put(r)
	if err == nil {
		for _, c := range incompleteChunks {
			c.instance.Delete()
		}
	}
	return err
}

func (s piecePerResourcePiece) MarkNotComplete() error {
	return s.completed().Delete()
}

func (s piecePerResourcePiece) ReadAt(b []byte, off int64) (int, error) {
	if s.mustIsComplete() {
		return s.completed().ReadAt(b, off)
	}
	return s.getChunks().ReadAt(b, off)
}

func (s piecePerResourcePiece) WriteAt(b []byte, off int64) (n int, err error) {
	i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), strconv.FormatInt(off, 10)))
	if err != nil {
		panic(err)
	}
	r := bytes.NewReader(b)
	err = i.Put(r)
	n = len(b) - r.Len()
	return
}

type chunk struct {
	offset   int64
	instance resource.Instance
}

type chunks []chunk

func (me chunks) ReadAt(b []byte, off int64) (int, error) {
	for {
		if len(me) == 0 {
			return 0, io.EOF
		}
		if me[0].offset <= off {
			break
		}
		me = me[1:]
	}
	n, err := me[0].instance.ReadAt(b, off-me[0].offset)
	if n == len(b) {
		return n, nil
	}
	if err == nil || err == io.EOF {
		n_, err := me[1:].ReadAt(b[n:], off+int64(n))
		return n + n_, err
	}
	return n, err
}

func (s piecePerResourcePiece) getChunks() (chunks chunks) {
	names, err := s.incompleteDir().Readdirnames()
	if err != nil {
		return
	}
	for _, n := range names {
		offset, err := strconv.ParseInt(n, 10, 64)
		if err != nil {
			panic(err)
		}
		i, err := s.rp.NewInstance(path.Join(s.incompleteDirPath(), n))
		if err != nil {
			panic(err)
		}
		chunks = append(chunks, chunk{offset, i})
	}
	sort.Slice(chunks, func(i, j int) bool {
		return chunks[i].offset < chunks[j].offset
	})
	return
}

func (s piecePerResourcePiece) completedInstancePath() string {
	return path.Join("completed", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) completed() resource.Instance {
	i, err := s.rp.NewInstance(s.completedInstancePath())
	if err != nil {
		panic(err)
	}
	return i
}

func (s piecePerResourcePiece) incompleteDirPath() string {
	return path.Join("incompleted", s.mp.Hash().HexString())
}

func (s piecePerResourcePiece) incompleteDir() resource.DirInstance {
	i, err := s.rp.NewInstance(s.incompleteDirPath())
	if err != nil {
		panic(err)
	}
	return i.(resource.DirInstance)
}
<|endoftext|>


<commit_before>// Stocks daily price update

// Usage: go run orca.go [stock_A,stock_B,stock_C, ...]
//						 [price_A,price_B,price_C, ...]
//						 [update time]
//						 [debug]

// TODO: Handle case when user has multiple shares of the same symbol -
//			1st buy: MSFT at $37 for 1000 stocks
//			2nd buy: MSFT at $50 for  300 stocks

// TODO: Support Thailand stocks

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/jasonlvhit/gocron"
	"github.com/mailgun/mailgun-go"
)

const MAILGUN_URL = "___URL___"
const MAILGUN_PRIVATE_KEY = "___PRIVATE_API_KEY___"
const MAILGUN_PUBLIC_KEY = "___PUBLIC_API_KEY___"
const USER_EMAIL = "___EMAIL___"
const QUOTE_BASE_URL = "http://dev.markitondemand.com/MODApis/Api/v2/Quote/json?symbol="
const DEBUG = false

var user_stocks map[string]float64
var update_time string

type Quote struct {
	Status           string
	Name             string
	Symbol           string
	LastPrice        float64
	Change           float64
	ChangePercent    float64
	Timestamp        string
	MSDate           float64
	MarketCap        float64
	Volume           float64
	ChangeYTD        float64
	ChangePercentYTD float64
	High             float64
	Low              float64
	Open             float64
	Message          string // Error message
}

// DEBUG: Print message to console.
func debugMessage(message string) {
	if DEBUG {
		fmt.Println(message)
	}
}

// Check if an error has occured.
func checkError(e error) {
	if e != nil {
		fmt.Printf("Error: %s\n", e)
		os.Exit(1)
	}
}

// Parse input data into user_stocks.
func populateUserStocks(input_stocks string, input_prices string) {
	debugMessage(fmt.Sprint("\npopulateUserStocks args: ", input_stocks, " ,", input_prices))

	stocks := strings.Split(input_stocks, ",")
	prices := strings.Split(input_prices, ",")

	for i := range stocks {
		var err error
		user_stocks[stocks[i]], err = strconv.ParseFloat(prices[i], 64)
		checkError(err)
	}

	for k, v := range user_stocks {
		debugMessage(fmt.Sprint("key: ", k, " val: ", v))
	}
}

func main() {
	if len(os.Args) < 4 {
		fmt.Printf("Usage: ./orca [stocks] [prices] [update_time]\n")
		os.Exit(1)
	}

	user_stocks = make(map[string]float64)
	populateUserStocks(os.Args[1], os.Args[2])
	update_time = os.Args[3]

	gocron.Clear()
	gocron.Every(1).Day().At(update_time).Do(fetchAndSendUpdate)
	<-gocron.Start()
}

// Fetch updated stocks and send email.
func fetchAndSendUpdate() {
	update := getUserStockPriceResult()
	debugMessage(update)

	mg := mailgun.NewMailgun(
		MAILGUN_URL,
		MAILGUN_PRIVATE_KEY,
		MAILGUN_PUBLIC_KEY,
	)

	email := mg.NewMessage(
		"ORCA PROJECT <mailgun@"+MAILGUN_URL+">",
		"ORCA Daily Update",
		update,
		USER_EMAIL,
	)

	_, _, err := mg.Send(email)
	checkError(err)
}

// Return a well formatted result of current market prices.
func getUserStockPriceResult() string {
	investment := 0.0
	returnInvestment := 0.0
	result := "Stocks Update " + time.Now().Format("Tue Jan 12") + "\n"
	result += "-----------------------------\n"
	result += fmt.Sprintf("%-*s %-*s %-*s\n", 10, "Symbol", 15, "P.Price", 15, "M.Price")
	for symbol, uPrice := range user_stocks {
		quote := fetchQuoteForStock(symbol)
		price := quote.LastPrice
		result += fmt.Sprintf("%-*s %-*.2f %-*.2f\n", 10, strings.ToUpper(symbol), 15, uPrice, 15, price)
		investment += uPrice
		returnInvestment += price
	}
	result += "-----------------------------\n"
	result += "Total amount invested: " + fmt.Sprintf("%.2f\n", investment)
	result += "Current amount: " + fmt.Sprintf("%.2f\n", returnInvestment)
	result += "Gain/Loss = " + fmt.Sprintf("%.2f\n", returnInvestment-investment)
	return result
}

// Return market price of a stock.
func fetchQuoteForStock(code string) *Quote {
	url := QUOTE_BASE_URL + strings.ToLower(code)
	httpResponse := getHttpResponse(url)

	var quote Quote
	err := json.Unmarshal(httpResponse, &quote)
	checkError(err)
	return &quote
}

// Return byte array response of an API call.
func getHttpResponse(url string) []byte {
	resp, err := http.Get(url)
	checkError(err)

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	checkError(err)
	return body
}
<commit_msg>Removed unused debug arg<commit_after>// Stocks daily price update

// Usage: go run orca.go [stock_A,stock_B,stock_C, ...]
//			 [price_A,price_B,price_C, ...]
//			 [update time]

// TODO: Handle case when user has multiple shares of the same symbol -
//			1st buy: MSFT at $37 for 1000 stocks
//			2nd buy: MSFT at $50 for  300 stocks

// TODO: Support Thailand stocks

package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"strconv"
	"strings"
	"time"

	"github.com/jasonlvhit/gocron"
	"github.com/mailgun/mailgun-go"
)

const MAILGUN_URL = "___URL___"
const MAILGUN_PRIVATE_KEY = "___PRIVATE_API_KEY___"
const MAILGUN_PUBLIC_KEY = "___PUBLIC_API_KEY___"
const USER_EMAIL = "___EMAIL___"
const QUOTE_BASE_URL = "http://dev.markitondemand.com/MODApis/Api/v2/Quote/json?symbol="
const DEBUG = false

var user_stocks map[string]float64
var update_time string

type Quote struct {
	Status           string
	Name             string
	Symbol           string
	LastPrice        float64
	Change           float64
	ChangePercent    float64
	Timestamp        string
	MSDate           float64
	MarketCap        float64
	Volume           float64
	ChangeYTD        float64
	ChangePercentYTD float64
	High             float64
	Low              float64
	Open             float64
	Message          string // Error message
}

// DEBUG: Print message to console.
func debugMessage(message string) {
	if DEBUG {
		fmt.Println(message)
	}
}

// Check if an error has occured.
func checkError(e error) {
	if e != nil {
		fmt.Printf("Error: %s\n", e)
		os.Exit(1)
	}
}

// Parse input data into user_stocks.
func populateUserStocks(input_stocks string, input_prices string) {
	debugMessage(fmt.Sprint("\npopulateUserStocks args: ", input_stocks, " ,", input_prices))

	stocks := strings.Split(input_stocks, ",")
	prices := strings.Split(input_prices, ",")

	for i := range stocks {
		var err error
		user_stocks[stocks[i]], err = strconv.ParseFloat(prices[i], 64)
		checkError(err)
	}

	for k, v := range user_stocks {
		debugMessage(fmt.Sprint("key: ", k, " val: ", v))
	}
}

func main() {
	if len(os.Args) < 4 {
		fmt.Printf("Usage: ./orca [stocks] [prices] [update_time]\n")
		os.Exit(1)
	}

	user_stocks = make(map[string]float64)
	populateUserStocks(os.Args[1], os.Args[2])
	update_time = os.Args[3]

	gocron.Clear()
	gocron.Every(1).Day().At(update_time).Do(fetchAndSendUpdate)
	<-gocron.Start()
}

// Fetch updated stocks and send email.
func fetchAndSendUpdate() {
	update := getUserStockPriceResult()
	debugMessage(update)

	mg := mailgun.NewMailgun(
		MAILGUN_URL,
		MAILGUN_PRIVATE_KEY,
		MAILGUN_PUBLIC_KEY,
	)

	email := mg.NewMessage(
		"ORCA PROJECT <mailgun@"+MAILGUN_URL+">",
		"ORCA Daily Update",
		update,
		USER_EMAIL,
	)

	_, _, err := mg.Send(email)
	checkError(err)
}

// Return a well formatted result of current market prices.
func getUserStockPriceResult() string {
	investment := 0.0
	returnInvestment := 0.0
	result := "Stocks Update " + time.Now().Format("Tue Jan 12") + "\n"
	result += "-----------------------------\n"
	result += fmt.Sprintf("%-*s %-*s %-*s\n", 10, "Symbol", 15, "P.Price", 15, "M.Price")
	for symbol, uPrice := range user_stocks {
		quote := fetchQuoteForStock(symbol)
		price := quote.LastPrice
		result += fmt.Sprintf("%-*s %-*.2f %-*.2f\n", 10, strings.ToUpper(symbol), 15, uPrice, 15, price)
		investment += uPrice
		returnInvestment += price
	}
	result += "-----------------------------\n"
	result += "Total amount invested: " + fmt.Sprintf("%.2f\n", investment)
	result += "Current amount: " + fmt.Sprintf("%.2f\n", returnInvestment)
	result += "Gain/Loss = " + fmt.Sprintf("%.2f\n", returnInvestment-investment)
	return result
}

// Return market price of a stock.
func fetchQuoteForStock(code string) *Quote {
	url := QUOTE_BASE_URL + strings.ToLower(code)
	httpResponse := getHttpResponse(url)

	var quote Quote
	err := json.Unmarshal(httpResponse, &quote)
	checkError(err)
	return &quote
}

// Return byte array response of an API call.
func getHttpResponse(url string) []byte {
	resp, err := http.Get(url)
	checkError(err)

	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	checkError(err)
	return body
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

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 vfs

import (
	"bytes"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/golang/glog"
	"golang.org/x/net/context"
	"google.golang.org/api/googleapi"
	storage "google.golang.org/api/storage/v1"
	"io/ioutil"
	"k8s.io/kops/util/pkg/hashing"
	"net/http"
	"os"
	"path"
	"strings"
	"sync"
)

// GSPath is a vfs path for Google Cloud Storage
type GSPath struct {
	client  *storage.Service
	bucket  string
	key     string
	md5Hash string
}

var _ Path = &GSPath{}
var _ HasHash = &GSPath{}

func NewGSPath(client *storage.Service, bucket string, key string) *GSPath {
	bucket = strings.TrimSuffix(bucket, "/")
	key = strings.TrimPrefix(key, "/")

	return &GSPath{
		client: client,
		bucket: bucket,
		key:    key,
	}
}

func (p *GSPath) Path() string {
	return "gs://" + p.bucket + "/" + p.key
}

func (p *GSPath) Bucket() string {
	return p.bucket
}

func (p *GSPath) String() string {
	return p.Path()
}

func (p *GSPath) Remove() error {
	err := p.client.Objects.Delete(p.bucket, p.key).Do()
	if err != nil {
		// TODO: Check for not-exists, return os.NotExist

		return fmt.Errorf("error deleting %s: %v", p, err)
	}

	return nil
}

func (p *GSPath) Join(relativePath ...string) Path {
	args := []string{p.key}
	args = append(args, relativePath...)
	joined := path.Join(args...)
	return &GSPath{
		client: p.client,
		bucket: p.bucket,
		key:    joined,
	}
}

func (p *GSPath) WriteFile(data []byte) error {
	glog.V(4).Infof("Writing file %q", p)

	md5Hash, err := hashing.HashAlgorithmMD5.Hash(bytes.NewReader(data))
	if err != nil {
		return err
	}

	obj := &storage.Object{
		Name:    p.key,
		Md5Hash: base64.StdEncoding.EncodeToString(md5Hash.HashValue),
	}
	r := bytes.NewReader(data)
	_, err = p.client.Objects.Insert(p.bucket, obj).Media(r).Do()
	if err != nil {
		return fmt.Errorf("error writing %s: %v", p, err)
	}

	return nil
}

// To prevent concurrent creates on the same file while maintaining atomicity of writes,
// we take a process-wide lock during the operation.
// Not a great approach, but fine for a single process (with low concurrency)
// TODO: should we enable versioning?
var createFileLockGCS sync.Mutex

func (p *GSPath) CreateFile(data []byte) error {
	createFileLockGCS.Lock()
	defer createFileLockGCS.Unlock()

	// Check if exists
	_, err := p.ReadFile()
	if err == nil {
		return os.ErrExist
	}

	if !os.IsNotExist(err) {
		return err
	}

	return p.WriteFile(data)
}

func (p *GSPath) ReadFile() ([]byte, error) {
	glog.V(4).Infof("Reading file %q", p)

	response, err := p.client.Objects.Get(p.bucket, p.key).Download()
	if err != nil {
		if isGCSNotFound(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("error reading %s: %v", p, err)
	}
	if response == nil {
		return nil, fmt.Errorf("no response returned from reading %s", p)
	}
	defer response.Body.Close()

	d, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading %s: %v", p, err)
	}
	return d, nil
}

func (p *GSPath) ReadDir() ([]Path, error) {
	prefix := p.key
	if !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	ctx := context.Background()
	var paths []Path
	err := p.client.Objects.List(p.bucket).Delimiter("/").Prefix(prefix).Pages(ctx, func(page *storage.Objects) error {
		for _, o := range page.Items {
			child := &GSPath{
				client:  p.client,
				bucket:  p.bucket,
				key:     o.Name,
				md5Hash: o.Md5Hash,
			}
			paths = append(paths, child)
		}
		return nil
	})
	if err != nil {
		if isGCSNotFound(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("error listing %s: %v", p, err)
	}
	glog.V(8).Infof("Listed files in %v: %v", p, paths)
	return paths, nil
}

func (p *GSPath) ReadTree() ([]Path, error) {
	// No delimiter for recursive search

	prefix := p.key
	if prefix != "" && !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	ctx := context.Background()
	var paths []Path
	err := p.client.Objects.List(p.bucket).Prefix(prefix).Pages(ctx, func(page *storage.Objects) error {
		for _, o := range page.Items {
			key := o.Name
			child := &GSPath{
				client:  p.client,
				bucket:  p.bucket,
				key:     key,
				md5Hash: o.Md5Hash,
			}
			paths = append(paths, child)
		}
		return nil
	})
	if err != nil {
		if isGCSNotFound(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("error listing %s: %v", p, err)
	}
	return paths, nil
}

func (p *GSPath) Base() string {
	return path.Base(p.key)
}

func (p *GSPath) PreferredHash() (*hashing.Hash, error) {
	return p.Hash(hashing.HashAlgorithmMD5)
}

func (p *GSPath) Hash(a hashing.HashAlgorithm) (*hashing.Hash, error) {
	if a != hashing.HashAlgorithmMD5 {
		return nil, nil
	}

	md5 := p.md5Hash
	if md5 == "" {
		return nil, nil
	}

	md5Bytes, err := hex.DecodeString(md5)
	if err != nil {
		return nil, fmt.Errorf("Etag was not a valid MD5 sum: %q", md5)
	}

	return &hashing.Hash{Algorithm: hashing.HashAlgorithmMD5, HashValue: md5Bytes}, nil
}

func isGCSNotFound(err error) bool {
	if err == nil {
		return false
	}
	ae, ok := err.(*googleapi.Error)
	return ok && ae.Code == http.StatusNotFound
}
<commit_msg>GCS: Don't reuse same error message<commit_after>/*
Copyright 2016 The Kubernetes Authors.

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 vfs

import (
	"bytes"
	"encoding/base64"
	"encoding/hex"
	"fmt"
	"github.com/golang/glog"
	"golang.org/x/net/context"
	"google.golang.org/api/googleapi"
	storage "google.golang.org/api/storage/v1"
	"io/ioutil"
	"k8s.io/kops/util/pkg/hashing"
	"net/http"
	"os"
	"path"
	"strings"
	"sync"
)

// GSPath is a vfs path for Google Cloud Storage
type GSPath struct {
	client  *storage.Service
	bucket  string
	key     string
	md5Hash string
}

var _ Path = &GSPath{}
var _ HasHash = &GSPath{}

func NewGSPath(client *storage.Service, bucket string, key string) *GSPath {
	bucket = strings.TrimSuffix(bucket, "/")
	key = strings.TrimPrefix(key, "/")

	return &GSPath{
		client: client,
		bucket: bucket,
		key:    key,
	}
}

func (p *GSPath) Path() string {
	return "gs://" + p.bucket + "/" + p.key
}

func (p *GSPath) Bucket() string {
	return p.bucket
}

func (p *GSPath) String() string {
	return p.Path()
}

func (p *GSPath) Remove() error {
	err := p.client.Objects.Delete(p.bucket, p.key).Do()
	if err != nil {
		// TODO: Check for not-exists, return os.NotExist

		return fmt.Errorf("error deleting %s: %v", p, err)
	}

	return nil
}

func (p *GSPath) Join(relativePath ...string) Path {
	args := []string{p.key}
	args = append(args, relativePath...)
	joined := path.Join(args...)
	return &GSPath{
		client: p.client,
		bucket: p.bucket,
		key:    joined,
	}
}

func (p *GSPath) WriteFile(data []byte) error {
	glog.V(4).Infof("Writing file %q", p)

	md5Hash, err := hashing.HashAlgorithmMD5.Hash(bytes.NewReader(data))
	if err != nil {
		return err
	}

	obj := &storage.Object{
		Name:    p.key,
		Md5Hash: base64.StdEncoding.EncodeToString(md5Hash.HashValue),
	}
	r := bytes.NewReader(data)
	_, err = p.client.Objects.Insert(p.bucket, obj).Media(r).Do()
	if err != nil {
		return fmt.Errorf("error writing %s: %v", p, err)
	}

	return nil
}

// To prevent concurrent creates on the same file while maintaining atomicity of writes,
// we take a process-wide lock during the operation.
// Not a great approach, but fine for a single process (with low concurrency)
// TODO: should we enable versioning?
var createFileLockGCS sync.Mutex

func (p *GSPath) CreateFile(data []byte) error {
	createFileLockGCS.Lock()
	defer createFileLockGCS.Unlock()

	// Check if exists
	_, err := p.ReadFile()
	if err == nil {
		return os.ErrExist
	}

	if !os.IsNotExist(err) {
		return err
	}

	return p.WriteFile(data)
}

func (p *GSPath) ReadFile() ([]byte, error) {
	glog.V(4).Infof("Reading file %q", p)

	response, err := p.client.Objects.Get(p.bucket, p.key).Download()
	if err != nil {
		if isGCSNotFound(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("error reading %s: %v", p, err)
	}
	if response == nil {
		return nil, fmt.Errorf("no response returned from reading %s", p)
	}
	defer response.Body.Close()

	d, err := ioutil.ReadAll(response.Body)
	if err != nil {
		return nil, fmt.Errorf("error reading %s: %v", p, err)
	}
	return d, nil
}

func (p *GSPath) ReadDir() ([]Path, error) {
	prefix := p.key
	if !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	ctx := context.Background()
	var paths []Path
	err := p.client.Objects.List(p.bucket).Delimiter("/").Prefix(prefix).Pages(ctx, func(page *storage.Objects) error {
		for _, o := range page.Items {
			child := &GSPath{
				client:  p.client,
				bucket:  p.bucket,
				key:     o.Name,
				md5Hash: o.Md5Hash,
			}
			paths = append(paths, child)
		}
		return nil
	})
	if err != nil {
		if isGCSNotFound(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("error listing %s: %v", p, err)
	}
	glog.V(8).Infof("Listed files in %v: %v", p, paths)
	return paths, nil
}

func (p *GSPath) ReadTree() ([]Path, error) {
	// No delimiter for recursive search

	prefix := p.key
	if prefix != "" && !strings.HasSuffix(prefix, "/") {
		prefix += "/"
	}

	ctx := context.Background()
	var paths []Path
	err := p.client.Objects.List(p.bucket).Prefix(prefix).Pages(ctx, func(page *storage.Objects) error {
		for _, o := range page.Items {
			key := o.Name
			child := &GSPath{
				client:  p.client,
				bucket:  p.bucket,
				key:     key,
				md5Hash: o.Md5Hash,
			}
			paths = append(paths, child)
		}
		return nil
	})
	if err != nil {
		if isGCSNotFound(err) {
			return nil, os.ErrNotExist
		}
		return nil, fmt.Errorf("error listing tree %s: %v", p, err)
	}
	return paths, nil
}

func (p *GSPath) Base() string {
	return path.Base(p.key)
}

func (p *GSPath) PreferredHash() (*hashing.Hash, error) {
	return p.Hash(hashing.HashAlgorithmMD5)
}

func (p *GSPath) Hash(a hashing.HashAlgorithm) (*hashing.Hash, error) {
	if a != hashing.HashAlgorithmMD5 {
		return nil, nil
	}

	md5 := p.md5Hash
	if md5 == "" {
		return nil, nil
	}

	md5Bytes, err := hex.DecodeString(md5)
	if err != nil {
		return nil, fmt.Errorf("Etag was not a valid MD5 sum: %q", md5)
	}

	return &hashing.Hash{Algorithm: hashing.HashAlgorithmMD5, HashValue: md5Bytes}, nil
}

func isGCSNotFound(err error) bool {
	if err == nil {
		return false
	}
	ae, ok := err.(*googleapi.Error)
	return ok && ae.Code == http.StatusNotFound
}
<|endoftext|>


<commit_before>package migrations

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/fnproject/fn/api/datastore/sql/migratex"
	"github.com/fnproject/fn/api/id"
	"github.com/fnproject/fn/api/models"
	"github.com/jmoiron/sqlx"
)

func up10(ctx context.Context, tx *sqlx.Tx) error {
	addAppIDStatements := []string{
		"ALTER TABLE apps ADD id VARCHAR(256);",
		"ALTER TABLE calls ADD app_id VARCHAR(256);",
		"ALTER TABLE logs ADD app_id VARCHAR(256);",
		"ALTER TABLE routes ADD app_id VARCHAR(256);",
	}
	for _, statement := range addAppIDStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}

	rows, err := tx.QueryxContext(ctx, "SELECT DISTINCT name FROM apps;")
	if err != nil {
		return err
	}

	res := []*models.App{}
	for rows.Next() {

		var app models.App
		err := rows.StructScan(&app)
		if err != nil {
			if err == sql.ErrNoRows {
				return nil
			}
			return err
		}
		app.ID = id.New().String()
		res = append(res, &app)
	}
	err = rows.Close()
	if err != nil {
		return err
	}

	if err := rows.Err(); err != nil {
		return err
	}

	// it is required for some reason, can't do this within the rows iteration.
	for _, app := range res {
		query := tx.Rebind(`UPDATE apps SET id=:id WHERE name=:name`)
		_, err = tx.NamedExecContext(ctx, query, app)
		if err != nil {
			return err
		}

		for _, t := range []string{"routes", "calls", "logs"} {
			q := fmt.Sprintf(`UPDATE %s SET app_id=:id WHERE app_name=:name;`, t)
			_, err = tx.NamedExecContext(ctx, tx.Rebind(q), app)
			if err != nil {
				return err
			}
		}
	}

	dropAppNameStatements := []string{
		"ALTER TABLE routes DROP COLUMN app_name;",
		"ALTER TABLE calls DROP COLUMN app_name;",
		"ALTER TABLE logs DROP COLUMN app_name;",
	}
	for _, statement := range dropAppNameStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}

	return nil
}

func down10(ctx context.Context, tx *sqlx.Tx) error {

	addAppNameStatements := []string{
		"ALTER TABLE calls ADD app_name VARCHAR(256);",
		"ALTER TABLE logs ADD app_name VARCHAR(256);",
		"ALTER TABLE routes ADD app_name VARCHAR(256);",
	}
	for _, statement := range addAppNameStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}

	rows, err := tx.QueryxContext(ctx, "SELECT DISTINCT id, name FROM apps;")
	if err != nil {
		return err
	}

	res := []*models.App{}
	for rows.Next() {
		var app models.App
		err := rows.StructScan(&app)
		if err != nil {
			if err == sql.ErrNoRows {
				return nil
			}
			return err
		}
		res = append(res, &app)
	}
	err = rows.Close()
	if err != nil {
		return err
	}

	if err := rows.Err(); err != nil {
		return err
	}

	// it is required for some reason, can't do this within the rows iteration.
	for _, app := range res {
		for _, t := range []string{"routes", "calls", "logs"} {
			q := "UPDATE " + t + " SET app_name=:name WHERE app_id=:id;"
			_, err = tx.NamedExecContext(ctx, tx.Rebind(q), app)
			if err != nil {
				return err
			}
		}
	}

	removeAppIDStatements := []string{
		"ALTER TABLE logs DROP COLUMN app_id;",
		"ALTER TABLE calls DROP COLUMN app_id;",
		"ALTER TABLE routes DROP COLUMN app_id;",
		"ALTER TABLE apps DROP COLUMN id;",
	}
	for _, statement := range removeAppIDStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}
	return nil
}

func init() {
	Migrations = append(Migrations, &migratex.MigFields{
		VersionFunc: vfunc(10),
		UpFunc:      up10,
		DownFunc:    down10,
	})
}
<commit_msg>Fix SQL UP migration (#895)<commit_after>package migrations

import (
	"context"
	"database/sql"
	"fmt"
	"github.com/fnproject/fn/api/datastore/sql/migratex"
	"github.com/fnproject/fn/api/id"
	"github.com/fnproject/fn/api/models"
	"github.com/jmoiron/sqlx"
)

var sqlStatements = [...]string{`CREATE TABLE IF NOT EXISTS routes (
	app_id varchar(256) NOT NULL,
	path varchar(256) NOT NULL,
	image varchar(256) NOT NULL,
	format varchar(16) NOT NULL,
	memory int NOT NULL,
	cpus int,
	timeout int NOT NULL,
	idle_timeout int NOT NULL,
	type varchar(16) NOT NULL,
	headers text NOT NULL,
	config text NOT NULL,
	annotations text NOT NULL,
	created_at text,
	updated_at varchar(256),
	PRIMARY KEY (app_id, path)
);`,

	`CREATE TABLE IF NOT EXISTS calls (
	created_at varchar(256) NOT NULL,
	started_at varchar(256) NOT NULL,
	completed_at varchar(256) NOT NULL,
	status varchar(256) NOT NULL,
	id varchar(256) NOT NULL,
	app_id varchar(256) NOT NULL,
	path varchar(256) NOT NULL,
	stats text,
	error text,
	PRIMARY KEY (id)
);`,

	`CREATE TABLE IF NOT EXISTS logs (
	id varchar(256) NOT NULL PRIMARY KEY,
	app_id varchar(256) NOT NULL,
	log text NOT NULL
);`,
}

var tables = [...]map[string]string{
	{"table": "routes", "statement": sqlStatements[0],
		"columns": "app_id,path,image,format,memory,cpus,timeout," +
			"idle_timeout,type,headers,config,annotations,created_at,updated_at"},

	{"table": "calls", "statement": sqlStatements[1],
		"columns": "created_at,started_at,completed_at,status,id,app_id,path,stats,error"},

	{"table": "logs", "statement": sqlStatements[2],
		"columns": "id,app_id,log"},
}

func up10(ctx context.Context, tx *sqlx.Tx) error {
	addAppIDStatements := []string{
		"ALTER TABLE apps ADD id VARCHAR(256);",
		"ALTER TABLE calls ADD app_id VARCHAR(256);",
		"ALTER TABLE logs ADD app_id VARCHAR(256);",
		"ALTER TABLE routes ADD app_id VARCHAR(256);",
	}
	for _, statement := range addAppIDStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}

	rows, err := tx.QueryxContext(ctx, "SELECT DISTINCT name FROM apps;")
	if err != nil {
		return err
	}

	res := []*models.App{}
	for rows.Next() {

		var app models.App
		err := rows.StructScan(&app)
		if err != nil {
			if err == sql.ErrNoRows {
				return nil
			}
			return err
		}
		app.ID = id.New().String()
		res = append(res, &app)
	}
	err = rows.Close()
	if err != nil {
		return err
	}

	if err := rows.Err(); err != nil {
		return err
	}

	// it is required for some reason, can't do this within the rows iteration.
	for _, app := range res {
		query := tx.Rebind(`UPDATE apps SET id=:id WHERE name=:name`)
		_, err = tx.NamedExecContext(ctx, query, app)
		if err != nil {
			return err
		}

		for _, t := range []string{"routes", "calls", "logs"} {
			q := fmt.Sprintf(`UPDATE %s SET app_id=:id WHERE app_name=:name;`, t)
			_, err = tx.NamedExecContext(ctx, tx.Rebind(q), app)
			if err != nil {
				return err
			}
		}
	}

	if tx.DriverName() != "sqlite3" {
		dropAppNameStatements := []string{
			"ALTER TABLE routes DROP COLUMN app_name;",
			"ALTER TABLE calls DROP COLUMN app_name;",
			"ALTER TABLE logs DROP COLUMN app_name;",
		}
		for _, statement := range dropAppNameStatements {
			_, err := tx.ExecContext(ctx, statement)
			if err != nil {
				return err
			}
		}
	} else {
		for _, t := range tables {
			statement := t["statement"]
			tableName := t["table"]
			columns := t["columns"]
			_, err = tx.ExecContext(ctx, tx.Rebind(fmt.Sprintf("ALTER TABLE %s RENAME TO old_%s;", tableName, tableName)))
			if err != nil {
				return err
			}
			_, err = tx.ExecContext(ctx, tx.Rebind(statement))
			if err != nil {
				return err
			}
			_, err = tx.ExecContext(ctx, tx.Rebind(fmt.Sprintf("INSERT INTO %s (%s) SELECT %s FROM old_%s;", tableName, columns, columns, tableName)))
			if err != nil {
				return err
			}

			_, err = tx.ExecContext(ctx, tx.Rebind(fmt.Sprintf("DROP TABLE old_%s;", tableName)))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

func down10(ctx context.Context, tx *sqlx.Tx) error {

	addAppNameStatements := []string{
		"ALTER TABLE calls ADD app_name VARCHAR(256);",
		"ALTER TABLE logs ADD app_name VARCHAR(256);",
		"ALTER TABLE routes ADD app_name VARCHAR(256);",
	}
	for _, statement := range addAppNameStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}

	rows, err := tx.QueryxContext(ctx, "SELECT DISTINCT id, name FROM apps;")
	if err != nil {
		return err
	}

	res := []*models.App{}
	for rows.Next() {
		var app models.App
		err := rows.StructScan(&app)
		if err != nil {
			if err == sql.ErrNoRows {
				return nil
			}
			return err
		}
		res = append(res, &app)
	}
	err = rows.Close()
	if err != nil {
		return err
	}

	if err := rows.Err(); err != nil {
		return err
	}

	// it is required for some reason, can't do this within the rows iteration.
	for _, app := range res {
		for _, t := range []string{"routes", "calls", "logs"} {
			q := "UPDATE " + t + " SET app_name=:name WHERE app_id=:id;"
			_, err = tx.NamedExecContext(ctx, tx.Rebind(q), app)
			if err != nil {
				return err
			}
		}
	}

	removeAppIDStatements := []string{
		"ALTER TABLE logs DROP COLUMN app_id;",
		"ALTER TABLE calls DROP COLUMN app_id;",
		"ALTER TABLE routes DROP COLUMN app_id;",
		"ALTER TABLE apps DROP COLUMN id;",
	}
	for _, statement := range removeAppIDStatements {
		_, err := tx.ExecContext(ctx, statement)
		if err != nil {
			return err
		}
	}
	return nil
}

func init() {
	Migrations = append(Migrations, &migratex.MigFields{
		VersionFunc: vfunc(10),
		UpFunc:      up10,
		DownFunc:    down10,
	})
}
<|endoftext|>


<commit_before>package glubcms

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	bm "github.com/microcosm-cc/bluemonday"
	bf "github.com/russross/blackfriday"
)

type Entry interface {
	Active() bool
	Author() string
	Date() time.Time
	HTML() template.HTML
	IsArticle() bool
	Link() string
	Title() string
}

type Entries []Entry

func (e Entries) Len() int {
	return len(e)
}
func (e Entries) Less(i, j int) bool {
	return e[i].Date().After(e[j].Date())
}
func (e Entries) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}
func (e Entries) Split() (Menu, Articles Entries) {
	for _, v := range e {
		if v.IsArticle() {
			Articles = append(Articles, v)
		} else {
			Menu = append(Menu, v)
		}
	}

	return
}

type GCTime time.Time

const GCTimeLayout = "2006-01-02 15:04"

func (t *GCTime) UnmarshalJSON(b []byte) error {
	tmp, err := time.Parse(GCTimeLayout, strings.Trim(string(b), "\""))
	*t = GCTime(tmp)
	return err
}

func (t GCTime) String() string {
	return time.Time(t).Format(GCTimeLayout)
}
func (t GCTime) MarshalJSON() ([]byte, error) {
	return []byte(`"` + t.String() + `"`), nil
}

type Meta struct {
	Author string
	Date   GCTime
	Title  string
}

type entry struct {
	meta      Meta
	active    bool
	html      []byte
	isarticle bool
	link      url.URL
}

func (e entry) Active() bool {
	return e.active
}
func (a entry) Author() string {
	return a.meta.Author
}
func (e entry) Date() time.Time {
	return time.Time(e.meta.Date)
}
func (e entry) HTML() template.HTML {
	return template.HTML(e.html)
}
func (e entry) IsArticle() bool {
	return e.isarticle
}
func (e entry) Link() string {
	return e.link.String()
}
func (e entry) Title() string {
	return e.meta.Title
}

func entryFromMeta(path string) (*entry, error) {
	ret := entry{}
	f, err := os.Open(filepath.Join(path))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&ret.meta)
	if err != nil {
		err = &os.PathError{
			Op:   "Parsing json in",
			Path: path,
			Err:  err,
		}
	}
	return &ret, err
}

func entryFromDir(prefix, path, activepath string) Entry {
	ret, err := entryFromMeta(filepath.Join(prefix, path, "meta.json"))
	if err != nil {
		log.Println(err)
		return nil
	}

	ret.link = url.URL{Path: path}
	if strings.HasPrefix(activepath, path) {
		ret.active = true
	}

	md, err := os.Open(filepath.Join(prefix, path, "article.md"))
	if err != nil {
		if !os.IsNotExist(err) {
			log.Println(err)
		}
		return ret
	}
	defer md.Close()

	// only attempt to convert the markdown if it's the active path
	if activepath == path {
		b, err := ioutil.ReadAll(md)
		if err != nil {
			log.Println(err)
			return ret
		}
		ret.html = bf.MarkdownBasic(b)
		ret.html = bm.UGCPolicy().SanitizeBytes(ret.html)
	}

	ret.isarticle = true

	return ret
}

func entriesFromDir(prefix, path, activepath string) Entries {
	var ret Entries

	dir, err := os.Open(filepath.Join(prefix, path))
	if err != nil {
		log.Println(err)
		return nil
	}

	dirlist, err := dir.Readdir(0)
	if err != nil {
		log.Println(err)
	}
	dir.Close()

	for _, fi := range dirlist {
		if !fi.IsDir() {
			continue
		}
		entry := entryFromDir(prefix, filepath.Join(path, fi.Name()), activepath)
		if entry != nil {
			ret = append(ret, entry)
		}
	}

	sort.Sort(ret)
	return ret
}

type Page struct {
	Menu     []Entries
	Articles Entries
	Content  Entry
}

func PageFromDir(prefix, path string) Page {
	var p Page
	path = filepath.Clean(path)
	activepath := path

	// look for an article in current path
	if c := entryFromDir(prefix, path, activepath); c != nil && c.IsArticle() {
		p.Content = c
	}

	for {
		menu, articles := entriesFromDir(prefix, path, activepath).Split()
		if len(menu) > 0 {
			sort.Sort(menu)
			p.Menu = append(p.Menu, menu)
		}
		if p.Articles == nil && len(articles) > 0 {
			sort.Sort(articles)
			p.Articles = articles
			if p.Content == nil {
				// Parse again with activepath set, to get the markdown
				cpath := p.Articles[0].Link()
				p.Content = entryFromDir(prefix, cpath, cpath)
				p.Articles[0] = p.Content
			}
		}
		if path == "." || path == "/" {
			break
		}
		path = filepath.Dir(path)
	}

	// building from deepest path to /, need to reverse
	for l, r := 0, len(p.Menu)-1; l < r; l, r = l+1, r-1 {
		p.Menu[l], p.Menu[r] = p.Menu[r], p.Menu[l]
	}

	return p
}

// For debugging
func (p Page) Outline() string {
	buf := bytes.Buffer{}

	fmt.Fprintln(&buf, "Menu:")
	for level, ms := range p.Menu {
		for _, m := range ms {
			fmt.Fprintf(&buf, "%s%q", strings.Repeat("\t", level), m.Title())
			if m.Active() {
				fmt.Fprintf(&buf, " (active)")
			}
			fmt.Fprintln(&buf)
		}
	}

	fmt.Fprintln(&buf, "Articles:")
	for _, a := range p.Articles {
		fmt.Fprintf(&buf, "%q", a.Title())
		if a.Active() {
			fmt.Fprintf(&buf, " (active)")
		}
		fmt.Fprintln(&buf)
	}
	fmt.Fprintln(&buf, "Content:")
	if p.Content != nil {
		fmt.Fprint(&buf, p.Content.Title())
	}

	return buf.String()
}

func init() {
	log.SetFlags(log.Flags() | log.Lshortfile)
}
<commit_msg>add priority field to metadata<commit_after>package glubcms

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/url"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	bm "github.com/microcosm-cc/bluemonday"
	bf "github.com/russross/blackfriday"
)

type Entry interface {
	Active() bool
	Author() string
	Date() time.Time
	HTML() template.HTML
	IsArticle() bool
	Link() string
	Priority() int
	Title() string
}

type Entries []Entry

func (e Entries) Len() int {
	return len(e)
}
func (e Entries) Less(i, j int) bool {
	ei, ej := e[i], e[j]
	if ei.Priority() != ej.Priority() {
		return ei.Priority() > ej.Priority()
	}
	return e[i].Date().After(e[j].Date())
}
func (e Entries) Swap(i, j int) {
	e[i], e[j] = e[j], e[i]
}
func (e Entries) Split() (Menu, Articles Entries) {
	for _, v := range e {
		if v.IsArticle() {
			Articles = append(Articles, v)
		} else {
			Menu = append(Menu, v)
		}
	}

	return
}

type GCTime time.Time

const GCTimeLayout = "2006-01-02 15:04"

func (t *GCTime) UnmarshalJSON(b []byte) error {
	tmp, err := time.Parse(GCTimeLayout, strings.Trim(string(b), "\""))
	*t = GCTime(tmp)
	return err
}

func (t GCTime) String() string {
	return time.Time(t).Format(GCTimeLayout)
}
func (t GCTime) MarshalJSON() ([]byte, error) {
	return []byte(`"` + t.String() + `"`), nil
}

type Meta struct {
	Author   string
	Date     GCTime
	Title    string
	Priority int
}

type entry struct {
	meta      Meta
	active    bool
	html      []byte
	isarticle bool
	link      url.URL
}

func (e entry) Active() bool {
	return e.active
}
func (a entry) Author() string {
	return a.meta.Author
}
func (e entry) Date() time.Time {
	return time.Time(e.meta.Date)
}
func (e entry) HTML() template.HTML {
	return template.HTML(e.html)
}
func (e entry) IsArticle() bool {
	return e.isarticle
}
func (e entry) Link() string {
	return e.link.String()
}
func (e entry) Priority() int {
	return e.meta.Priority
}
func (e entry) Title() string {
	return e.meta.Title
}

func entryFromMeta(path string) (*entry, error) {
	ret := entry{}
	f, err := os.Open(filepath.Join(path))
	if err != nil {
		return nil, err
	}
	defer f.Close()

	err = json.NewDecoder(f).Decode(&ret.meta)
	if err != nil {
		err = &os.PathError{
			Op:   "Parsing json in",
			Path: path,
			Err:  err,
		}
	}
	return &ret, err
}

func entryFromDir(prefix, path, activepath string) Entry {
	ret, err := entryFromMeta(filepath.Join(prefix, path, "meta.json"))
	if err != nil {
		log.Println(err)
		return nil
	}

	ret.link = url.URL{Path: path}
	if strings.HasPrefix(activepath, path) {
		ret.active = true
	}

	md, err := os.Open(filepath.Join(prefix, path, "article.md"))
	if err != nil {
		if !os.IsNotExist(err) {
			log.Println(err)
		}
		return ret
	}
	defer md.Close()

	// only attempt to convert the markdown if it's the active path
	if activepath == path {
		b, err := ioutil.ReadAll(md)
		if err != nil {
			log.Println(err)
			return ret
		}
		ret.html = bf.MarkdownBasic(b)
		ret.html = bm.UGCPolicy().SanitizeBytes(ret.html)
	}

	ret.isarticle = true

	return ret
}

func entriesFromDir(prefix, path, activepath string) Entries {
	var ret Entries

	dir, err := os.Open(filepath.Join(prefix, path))
	if err != nil {
		log.Println(err)
		return nil
	}

	dirlist, err := dir.Readdir(0)
	if err != nil {
		log.Println(err)
	}
	dir.Close()

	for _, fi := range dirlist {
		if !fi.IsDir() {
			continue
		}
		entry := entryFromDir(prefix, filepath.Join(path, fi.Name()), activepath)
		if entry != nil {
			ret = append(ret, entry)
		}
	}

	sort.Sort(ret)
	return ret
}

type Page struct {
	Menu     []Entries
	Articles Entries
	Content  Entry
}

func PageFromDir(prefix, path string) Page {
	var p Page
	path = filepath.Clean(path)
	activepath := path

	// look for an article in current path
	if c := entryFromDir(prefix, path, activepath); c != nil && c.IsArticle() {
		p.Content = c
	}

	for {
		menu, articles := entriesFromDir(prefix, path, activepath).Split()
		if len(menu) > 0 {
			sort.Sort(menu)
			p.Menu = append(p.Menu, menu)
		}
		if p.Articles == nil && len(articles) > 0 {
			sort.Sort(articles)
			p.Articles = articles
			if p.Content == nil {
				// Parse again with activepath set, to get the markdown
				cpath := p.Articles[0].Link()
				p.Content = entryFromDir(prefix, cpath, cpath)
				p.Articles[0] = p.Content
			}
		}
		if path == "." || path == "/" {
			break
		}
		path = filepath.Dir(path)
	}

	// building from deepest path to /, need to reverse
	for l, r := 0, len(p.Menu)-1; l < r; l, r = l+1, r-1 {
		p.Menu[l], p.Menu[r] = p.Menu[r], p.Menu[l]
	}

	return p
}

// For debugging
func (p Page) Outline() string {
	buf := bytes.Buffer{}

	fmt.Fprintln(&buf, "Menu:")
	for level, ms := range p.Menu {
		for _, m := range ms {
			fmt.Fprintf(&buf, "%s%q", strings.Repeat("\t", level), m.Title())
			if m.Active() {
				fmt.Fprintf(&buf, " (active)")
			}
			fmt.Fprintln(&buf)
		}
	}

	fmt.Fprintln(&buf, "Articles:")
	for _, a := range p.Articles {
		fmt.Fprintf(&buf, "%q", a.Title())
		if a.Active() {
			fmt.Fprintf(&buf, " (active)")
		}
		fmt.Fprintln(&buf)
	}
	fmt.Fprintln(&buf, "Content:")
	if p.Content != nil {
		fmt.Fprint(&buf, p.Content.Title())
	}

	return buf.String()
}

func init() {
	log.SetFlags(log.Flags() | log.Lshortfile)
}
<|endoftext|>