<commit_before>/*
 This package is used to implement a "line oriented command interpreter", inspired by the python package with
 the same name http://docs.python.org/2/library/cmd.html

 Usage:

	 commander := &Cmd{...}
	 commander.Init()

	 commander.Add(Command{...})
	 commander.Add(Command{...})

	 commander.CmdLoop()
*/
package cmd

import (
	"github.com/gobs/args"
	"github.com/gobs/pretty"

	"github.com/peterh/liner"

	"fmt"
	"os"
	"os/exec"
	"path"
	"sort"
	"strconv"
	"strings"
	"sync"
	"time"
)

//
// This is used to describe a new command
//
type Command struct {
	// command name
	Name string
	// command description
	Help string
	// the function to call to execute the command
	Call func(string) bool
	// the function to call to print the help string
	HelpFunc func()
}

func (c *Command) DefaultHelp() {
	if len(c.Help) > 0 {
		fmt.Println(c.Help)
	} else {
		fmt.Println("No help for ", c.Name)
	}
}

//
// The context for command completion
//
type Completer struct {
	// the list of words to match on
	Words []string
}

func (c *Completer) Complete(line string) (matches []string) {
	for _, w := range c.Words {
		if strings.HasPrefix(w, line) {
			matches = append(matches, w)
		}
	}

	return
}

//
// Create a Completer and initialize with list of words
//
func NewCompleter(words []string) (c *Completer) {
	return &Completer{Words: words}
}

//
// This the the "context" for the command interpreter
//
type Cmd struct {
	// the prompt string
	Prompt string

	// the history file
	HistoryFile string

	// this function is called before starting the command loop
	PreLoop func()

	// this function is called before terminating the command loop
	PostLoop func()

	// this function is called before executing the selected command
	PreCmd func(string)

	// this function is called after a command has been executed
	// return true to terminate the interpreter, false to continue
	PostCmd func(string, bool) bool

	// this function is called if the last typed command was an empty line
	EmptyLine func()

	// this function is called if the command line doesn't match any existing command
	// by default it displays an error message
	Default func(string)

	// this function is called to implement command completion.
	// it should return a list of words that match the input text
	Complete func(string, string) []string

	// if true, enable shell commands
	EnableShell bool

	// this is the list of available commands indexed by command name
	Commands map[string]Command

	///////// private stuff /////////////
	line         *liner.State
	completer    *Completer
	commandNames []string

	waitGroup          *sync.WaitGroup
	waitMax, waitCount int
}

func (cmd *Cmd) readHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	filepath := cmd.HistoryFile // start with current directory
	if f, err := os.Open(filepath); err == nil {
		cmd.line.ReadHistory(f)
		f.Close()

		cmd.HistoryFile = filepath
		return
	}

	filepath = path.Join(os.Getenv("HOME"), filepath) // then check home directory
	if f, err := os.Open(filepath); err == nil {
		cmd.line.ReadHistory(f)
		f.Close()

		cmd.HistoryFile = filepath
		return
	}
}

func (cmd *Cmd) writeHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	if f, err := os.Create(cmd.HistoryFile); err == nil {
		cmd.line.WriteHistory(f)
		f.Close()
	}
}

//
// Initialize the command interpreter context
//
func (cmd *Cmd) Init() {
	if cmd.PreLoop == nil {
		cmd.PreLoop = func() {}
	}
	if cmd.PostLoop == nil {
		cmd.PostLoop = func() {}
	}
	if cmd.PreCmd == nil {
		cmd.PreCmd = func(string) {}
	}
	if cmd.PostCmd == nil {
		cmd.PostCmd = func(line string, stop bool) bool { return stop }
	}
	if cmd.EmptyLine == nil {
		cmd.EmptyLine = func() {}
	}
	if cmd.Default == nil {
		cmd.Default = func(line string) { fmt.Printf("invalid command: %v\n", line) }
	}

	cmd.Commands = make(map[string]Command)
	cmd.Add(Command{"help", `list available commands`, cmd.Help, nil})
	cmd.Add(Command{"echo", `echo input line`, cmd.Echo, nil})
	cmd.Add(Command{"go", `go cmd: asynchronous execution of cmd, or 'go [--start|--wait]'`, cmd.Go, nil})
	cmd.Add(Command{"repeat", `repeat [--count=n] [--wait=ms] [--echo] command args`, cmd.Repeat, nil})
}

//
// Add a completer that matches on command names
//
func (cmd *Cmd) addCommandCompleter() {
	cmd.commandNames = make([]string, 0, len(cmd.Commands))

	for n, _ := range cmd.Commands {
		cmd.commandNames = append(cmd.commandNames, n)
	}

	// sorting for Help()
	sort.Strings(cmd.commandNames)

	cmd.completer = NewCompleter(cmd.commandNames)
	cmd.line.SetWordCompleter(cmd.wordCompleter)
}

func (cmd *Cmd) wordCompleter(line string, pos int) (head string, completions []string, tail string) {
	start := strings.LastIndex(line[:pos], " ")
	if start < 0 { // this is the command to match
		return "", cmd.completer.Complete(line), line[pos:]
	} else if strings.HasPrefix(line, "help ") {
		return line[:start+1], cmd.completer.Complete(line[start+1:]), line[pos:]
	} else if cmd.Complete != nil {
		return line[:start+1], cmd.Complete(line[start+1:], line), line[pos:]
	} else {
		return
	}
}

//
// execute shell command
//
func shellExec(command string) {
	args := args.GetArgs(command)
	if len(args) < 1 {
		fmt.Println("No command to exec")
	} else {
		cmd := exec.Command(args[0])
		cmd.Args = args
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			fmt.Println(err)
		}
	}
}

// Add a command to the command interpreter.
// Overrides a command with the same name, if there was one
//
func (cmd *Cmd) Add(command Command) {
	if command.HelpFunc == nil {
		command.HelpFunc = command.DefaultHelp
	}

	cmd.Commands[command.Name] = command
}

//
// Default help command.
// It lists all available commands or it displays the help for the specified command
//
func (cmd *Cmd) Help(line string) (stop bool) {
	fmt.Println("")

	if len(line) == 0 {
		fmt.Println("Available commands (use 'help <topic>'):")
		fmt.Println("================================================================")

		tp := pretty.NewTabPrinter(8)

		for _, c := range cmd.commandNames {
			tp.Print(c)
		}

		tp.Println()
	} else {
		c, ok := cmd.Commands[line]
		if ok {
			c.HelpFunc()
		} else {
			fmt.Println("unknown command")
		}
	}

	fmt.Println("")
	return
}

func (cmd *Cmd) Echo(line string) (stop bool) {
	fmt.Println(line)
	return
}

func (cmd *Cmd) Go(line string) (stop bool) {
	if strings.HasPrefix(line, "-") {
		// should be --start or --wait

		args := args.ParseArgs(line)

		if _, ok := args.Options["start"]; ok {
			cmd.waitGroup = new(sync.WaitGroup)
			cmd.waitCount = 0
			cmd.waitMax = 0

			if len(args.Arguments) > 0 {
				cmd.waitMax, _ = strconv.Atoi(args.Arguments[0])
			}

			return
		}

		if _, ok := args.Options["wait"]; ok {
			if cmd.waitGroup == nil {
				fmt.Println("nothing to wait on")
			} else {
				cmd.waitGroup.Wait()
				cmd.waitGroup = nil
			}

			return
		}
	}

	if strings.HasPrefix(line, "go ") {
		fmt.Println("Don't go go me!")
	} else {
		if cmd.waitGroup == nil {
			go cmd.OneCmd(line)
		} else {
			if cmd.waitMax > 0 {
				if cmd.waitCount >= cmd.waitMax {
					cmd.waitGroup.Wait()
					cmd.waitCount = 0
				}
			}

			cmd.waitCount++
			cmd.waitGroup.Add(1)
			go func() {
				defer cmd.waitGroup.Done()
				cmd.OneCmd(line)
			}()
		}
	}

	return
}

func (cmd *Cmd) Repeat(line string) (stop bool) {
	count := ^uint64(0) // almost forever
	wait := 0           // no wait
	echo := false
	arg := ""

	for {
		if strings.HasPrefix(line, "-") {
			// some options
			parts := strings.SplitN(line, " ", 2)
			if len(parts) < 2 {
				// no command
				fmt.Println("nothing to repeat")
				return
			}

			arg, line = parts[0], parts[1]
			if arg == "--" {
				break
			}

			if arg == "--echo" {
				echo = true
			} else if strings.HasPrefix(arg, "--count=") {
				count, _ = strconv.ParseUint(arg[8:], 10, 64)
				fmt.Println("count", count)
			} else if strings.HasPrefix(arg, "--wait=") {
				wait, _ = strconv.Atoi(arg[7:])
				fmt.Println("wait", wait)
			} else {
				// unknown option
				fmt.Println("invalid option", arg)
				return
			}
		} else {
			break
		}
	}

	formatted := strings.Contains(line, "%")

	for i := uint64(0); i < count; i++ {
		command := line
		if formatted {
			command = fmt.Sprintf(line, i)
		}

		if echo {
			fmt.Println(cmd.Prompt, command)
		}

		if cmd.OneCmd(command) {
			break
		}

		if wait > 0 && i < count-1 {
			time.Sleep(time.Duration(wait) * time.Millisecond)
		}
	}

	return
}

//
// This method executes one command
//
func (cmd *Cmd) OneCmd(line string) (stop bool) {

	if cmd.EnableShell && strings.HasPrefix(line, "!") {
		shellExec(line[1:])
		return
	}

	parts := strings.SplitN(line, " ", 2)
	cname := parts[0]

	command, ok := cmd.Commands[cname]

	if ok {
		var params string

		if len(parts) > 1 {
			params = strings.TrimSpace(parts[1])
		}

		stop = command.Call(params)
	} else {
		cmd.Default(line)
	}

	return
}

//
// This is the command interpreter entry point.
// It displays a prompt, waits for a command and executes it until the selected command returns true
//
func (cmd *Cmd) CmdLoop() {
	if len(cmd.Prompt) == 0 {
		cmd.Prompt = "> "
	}

	cmd.line = liner.NewLiner()
	defer cmd.line.Close()

	cmd.addCommandCompleter()

	cmd.PreLoop()
	cmd.readHistoryFile()

	defer func() {
		cmd.writeHistoryFile()
		cmd.PostLoop()
	}()

	// loop until ReadLine returns nil (signalling EOF)
	for {
		line, err := cmd.line.Prompt(cmd.Prompt)
		if err != nil {
			fmt.Println(err)
			break
		}

		line = strings.TrimSpace(line)

		if strings.HasPrefix(line, "#") || line == "" {
			cmd.EmptyLine()
			continue
		}

		cmd.line.AppendHistory(line) // allow user to recall this line

		cmd.PreCmd(line)

		stop := cmd.OneCmd(line)
		stop = cmd.PostCmd(line, stop)

		if stop {
			break
		}
	}
}
<commit_msg>liner now support reading from stdin and redirecting stdout<commit_after>/*
 This package is used to implement a "line oriented command interpreter", inspired by the python package with
 the same name http://docs.python.org/2/library/cmd.html

 Usage:

	 commander := &Cmd{...}
	 commander.Init()

	 commander.Add(Command{...})
	 commander.Add(Command{...})

	 commander.CmdLoop()
*/
package cmd

import (
	"github.com/gobs/args"
	"github.com/gobs/pretty"

	"github.com/peterh/liner"

	"fmt"
	"os"
	"os/exec"
	"os/signal"
	"path"
	"sort"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

//
// This is used to describe a new command
//
type Command struct {
	// command name
	Name string
	// command description
	Help string
	// the function to call to execute the command
	Call func(string) bool
	// the function to call to print the help string
	HelpFunc func()
}

func (c *Command) DefaultHelp() {
	if len(c.Help) > 0 {
		fmt.Println(c.Help)
	} else {
		fmt.Println("No help for ", c.Name)
	}
}

//
// The context for command completion
//
type Completer struct {
	// the list of words to match on
	Words []string
}

func (c *Completer) Complete(line string) (matches []string) {
	for _, w := range c.Words {
		if strings.HasPrefix(w, line) {
			matches = append(matches, w)
		}
	}

	return
}

//
// Create a Completer and initialize with list of words
//
func NewCompleter(words []string) (c *Completer) {
	return &Completer{Words: words}
}

//
// This the the "context" for the command interpreter
//
type Cmd struct {
	// the prompt string
	Prompt string

	// the history file
	HistoryFile string

	// this function is called before starting the command loop
	PreLoop func()

	// this function is called before terminating the command loop
	PostLoop func()

	// this function is called before executing the selected command
	PreCmd func(string)

	// this function is called after a command has been executed
	// return true to terminate the interpreter, false to continue
	PostCmd func(string, bool) bool

	// this function is called if the last typed command was an empty line
	EmptyLine func()

	// this function is called if the command line doesn't match any existing command
	// by default it displays an error message
	Default func(string)

	// this function is called to implement command completion.
	// it should return a list of words that match the input text
	Complete func(string, string) []string

	// if true, enable shell commands
	EnableShell bool

	// this is the list of available commands indexed by command name
	Commands map[string]Command

	///////// private stuff /////////////
	line         *liner.State
	completer    *Completer
	commandNames []string

	waitGroup          *sync.WaitGroup
	waitMax, waitCount int
}

func (cmd *Cmd) readHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	filepath := cmd.HistoryFile // start with current directory
	if f, err := os.Open(filepath); err == nil {
		cmd.line.ReadHistory(f)
		f.Close()

		cmd.HistoryFile = filepath
		return
	}

	filepath = path.Join(os.Getenv("HOME"), filepath) // then check home directory
	if f, err := os.Open(filepath); err == nil {
		cmd.line.ReadHistory(f)
		f.Close()

		cmd.HistoryFile = filepath
		return
	}
}

func (cmd *Cmd) writeHistoryFile() {
	if len(cmd.HistoryFile) == 0 {
		// no history file
		return
	}

	if f, err := os.Create(cmd.HistoryFile); err == nil {
		cmd.line.WriteHistory(f)
		f.Close()
	}
}

//
// Initialize the command interpreter context
//
func (cmd *Cmd) Init() {
	if cmd.PreLoop == nil {
		cmd.PreLoop = func() {}
	}
	if cmd.PostLoop == nil {
		cmd.PostLoop = func() {}
	}
	if cmd.PreCmd == nil {
		cmd.PreCmd = func(string) {}
	}
	if cmd.PostCmd == nil {
		cmd.PostCmd = func(line string, stop bool) bool { return stop }
	}
	if cmd.EmptyLine == nil {
		cmd.EmptyLine = func() {}
	}
	if cmd.Default == nil {
		cmd.Default = func(line string) { fmt.Printf("invalid command: %v\n", line) }
	}

	cmd.Commands = make(map[string]Command)
	cmd.Add(Command{"help", `list available commands`, cmd.Help, nil})
	cmd.Add(Command{"echo", `echo input line`, cmd.Echo, nil})
	cmd.Add(Command{"go", `go cmd: asynchronous execution of cmd, or 'go [--start|--wait]'`, cmd.Go, nil})
	cmd.Add(Command{"repeat", `repeat [--count=n] [--wait=ms] [--echo] command args`, cmd.Repeat, nil})
}

//
// Add a completer that matches on command names
//
func (cmd *Cmd) addCommandCompleter() {
	cmd.commandNames = make([]string, 0, len(cmd.Commands))

	for n, _ := range cmd.Commands {
		cmd.commandNames = append(cmd.commandNames, n)
	}

	// sorting for Help()
	sort.Strings(cmd.commandNames)

	cmd.completer = NewCompleter(cmd.commandNames)
	cmd.line.SetWordCompleter(cmd.wordCompleter)
}

func (cmd *Cmd) wordCompleter(line string, pos int) (head string, completions []string, tail string) {
	start := strings.LastIndex(line[:pos], " ")
	if start < 0 { // this is the command to match
		return "", cmd.completer.Complete(line), line[pos:]
	} else if strings.HasPrefix(line, "help ") {
		return line[:start+1], cmd.completer.Complete(line[start+1:]), line[pos:]
	} else if cmd.Complete != nil {
		return line[:start+1], cmd.Complete(line[start+1:], line), line[pos:]
	} else {
		return
	}
}

//
// execute shell command
//
func shellExec(command string) {
	args := args.GetArgs(command)
	if len(args) < 1 {
		fmt.Println("No command to exec")
	} else {
		cmd := exec.Command(args[0])
		cmd.Args = args
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			fmt.Println(err)
		}
	}
}

// Add a command to the command interpreter.
// Overrides a command with the same name, if there was one
//
func (cmd *Cmd) Add(command Command) {
	if command.HelpFunc == nil {
		command.HelpFunc = command.DefaultHelp
	}

	cmd.Commands[command.Name] = command
}

//
// Default help command.
// It lists all available commands or it displays the help for the specified command
//
func (cmd *Cmd) Help(line string) (stop bool) {
	fmt.Println("")

	if len(line) == 0 {
		fmt.Println("Available commands (use 'help <topic>'):")
		fmt.Println("================================================================")

		tp := pretty.NewTabPrinter(8)

		for _, c := range cmd.commandNames {
			tp.Print(c)
		}

		tp.Println()
	} else {
		c, ok := cmd.Commands[line]
		if ok {
			c.HelpFunc()
		} else {
			fmt.Println("unknown command")
		}
	}

	fmt.Println("")
	return
}

func (cmd *Cmd) Echo(line string) (stop bool) {
	fmt.Println(line)
	return
}

func (cmd *Cmd) Go(line string) (stop bool) {
	if strings.HasPrefix(line, "-") {
		// should be --start or --wait

		args := args.ParseArgs(line)

		if _, ok := args.Options["start"]; ok {
			cmd.waitGroup = new(sync.WaitGroup)
			cmd.waitCount = 0
			cmd.waitMax = 0

			if len(args.Arguments) > 0 {
				cmd.waitMax, _ = strconv.Atoi(args.Arguments[0])
			}

			return
		}

		if _, ok := args.Options["wait"]; ok {
			if cmd.waitGroup == nil {
				fmt.Println("nothing to wait on")
			} else {
				cmd.waitGroup.Wait()
				cmd.waitGroup = nil
			}

			return
		}
	}

	if strings.HasPrefix(line, "go ") {
		fmt.Println("Don't go go me!")
	} else {
		if cmd.waitGroup == nil {
			go cmd.OneCmd(line)
		} else {
			if cmd.waitMax > 0 {
				if cmd.waitCount >= cmd.waitMax {
					cmd.waitGroup.Wait()
					cmd.waitCount = 0
				}
			}

			cmd.waitCount++
			cmd.waitGroup.Add(1)
			go func() {
				defer cmd.waitGroup.Done()
				cmd.OneCmd(line)
			}()
		}
	}

	return
}

func (cmd *Cmd) Repeat(line string) (stop bool) {
	count := ^uint64(0) // almost forever
	wait := 0           // no wait
	echo := false
	arg := ""

	for {
		if strings.HasPrefix(line, "-") {
			// some options
			parts := strings.SplitN(line, " ", 2)
			if len(parts) < 2 {
				// no command
				fmt.Println("nothing to repeat")
				return
			}

			arg, line = parts[0], parts[1]
			if arg == "--" {
				break
			}

			if arg == "--echo" {
				echo = true
			} else if strings.HasPrefix(arg, "--count=") {
				count, _ = strconv.ParseUint(arg[8:], 10, 64)
				fmt.Println("count", count)
			} else if strings.HasPrefix(arg, "--wait=") {
				wait, _ = strconv.Atoi(arg[7:])
				fmt.Println("wait", wait)
			} else {
				// unknown option
				fmt.Println("invalid option", arg)
				return
			}
		} else {
			break
		}
	}

	formatted := strings.Contains(line, "%")

	for i := uint64(0); i < count; i++ {
		command := line
		if formatted {
			command = fmt.Sprintf(line, i)
		}

		if echo {
			fmt.Println(cmd.Prompt, command)
		}

		if cmd.OneCmd(command) {
			break
		}

		if wait > 0 && i < count-1 {
			time.Sleep(time.Duration(wait) * time.Millisecond)
		}
	}

	return
}

//
// This method executes one command
//
func (cmd *Cmd) OneCmd(line string) (stop bool) {

	if cmd.EnableShell && strings.HasPrefix(line, "!") {
		shellExec(line[1:])
		return
	}

	parts := strings.SplitN(line, " ", 2)
	cname := parts[0]

	command, ok := cmd.Commands[cname]

	if ok {
		var params string

		if len(parts) > 1 {
			params = strings.TrimSpace(parts[1])
		}

		stop = command.Call(params)
	} else {
		cmd.Default(line)
	}

	return
}

//
// This is the command interpreter entry point.
// It displays a prompt, waits for a command and executes it until the selected command returns true
//
func (cmd *Cmd) CmdLoop() {
	if len(cmd.Prompt) == 0 {
		cmd.Prompt = "> "
	}

	cmd.line = liner.NewLiner()
	defer cmd.line.Close()

	cmd.addCommandCompleter()
	cmd.PreLoop()
	cmd.readHistoryFile()

	defer func() {
		if cmd.line != nil {
			cmd.writeHistoryFile()
			cmd.line.Close()
		}

		cmd.PostLoop()
	}()

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

	go func() {
		sig := <-sigc

		// restore terminal
		if cmd.line != nil {
			cmd.line.Close()
		}

		// rethrow signal
		signal.Stop(sigc)
		p, _ := os.FindProcess(os.Getpid())
		p.Signal(sig)
	}()

	// loop until ReadLine returns nil (signalling EOF)
	for {
		line, err := cmd.line.Prompt(cmd.Prompt)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			break
		}

		line = strings.TrimSpace(line)

		if strings.HasPrefix(line, "#") || line == "" {
			cmd.EmptyLine()
			continue
		}

		if cmd.line != nil {
			cmd.line.AppendHistory(line) // allow user to recall this line
		}

		cmd.PreCmd(line)
		stop := cmd.OneCmd(line)
		stop = cmd.PostCmd(line, stop)

		if stop {
			break
		}
	}
}
<|endoftext|>


<commit_before>package cronsifter

import (
	"bufio"
	"log"
	"os"
	"os/exec"
	"time"
)

var execCommand = exec.Command

func handleOutput(s *bufio.Scanner, out chan<- string) {
	defer close(out)
	for s.Scan() {
		text := s.Text()
		out <- text
	}
}

// ExecCommand take a slice of string to be executed as the
// command. stdout and stderr are passed to the channels handed
// to the function.
func ExecCommand(a []string, stdout chan<- string, stderr chan<- string) {
	cmd := execCommand(a[0], a[1:]...)
	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatalf("RunCommand: cmd.StdoutPipe(): %v", err)
	}
	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		log.Fatalf("RunCommand: cmd.StderrPipe(): %v", err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatalf("RunCommand: cmd.Start(): %v", err)
	}

	scanner := bufio.NewScanner(stdoutPipe)
	go handleOutput(scanner, stdout)

	errScanner := bufio.NewScanner(stderrPipe)
	go handleOutput(errScanner, stderr)

	if err := cmd.Wait(); err != nil {
		log.Fatalf("RunCommand: cmd.Wait(): %v", err)
	}
}

// Process representing the process that is going to be run.
type Process struct {
	Command  string
	Args     []string
	Delay    string
	Ping     string
	Pid      int
	Status   string
	OsP      *os.Process
	respawns int
}

// RunIt runs the Process and makes sure it keeps running.
func RunIt(p *Process) chan *Process {
	ch := make(chan *Process)
	go func() {
		p.run()
		go p.monitor()
		ch <- p
	}()

	return ch
}

func (p *Process) run() {
	wd, _ := os.Getwd()
	proc := &os.ProcAttr{
		Dir: wd,
		Env: os.Environ(),
		Files: []*os.File{
			os.Stdin,
			os.Stdout,
			os.Stderr,
		},
	}

	process, err := os.StartProcess(p.Command, p.Args, proc)
	if err != nil {
		log.Fatalf("%s failed. %s\n", p.Command, err)
		return
	}
	p.OsP = process
	p.Pid = process.Pid
	p.Status = "started"

}

func (p *Process) monitor() {
	if p.OsP == nil {
		return
	}
	status := make(chan *os.ProcessState)
	died := make(chan error)
	go func() {
		state, err := p.OsP.Wait()
		if err != nil {
			died <- err
			return
		}
		status <- state
	}()
	select {
	case s := <-status:
		log.Printf("%s exit=%s, success=%#v, exited=%#v, respawn_count=%#v\n", p.Command, s, s.Success(), s.Exited(), p.respawns)
		// log.Printf("%s success = %#v\n", p.Command, s.Success())
		// log.Printf("%s exited = %#v\n", p.Command, s.Exited())
		p.respawns++
		// log.Printf("%s respawns = %#v\n", p.Command, p.respawns)
		if p.Delay != "" {
			log.Printf("%s sleeping for %#v before restarting\n", p.Command, p.Delay)
			t, _ := time.ParseDuration(p.Delay)
			time.Sleep(t)
		}
		RunIt(p)
		p.Status = "restarted"
	case err := <-died:
		log.Printf("%d %s killed = %#v\n", p.OsP.Pid, p.Command, err)
	}
}
<commit_msg>More cleanup<commit_after>package cronsifter

import (
	"bufio"
	"log"
	"os"
	"os/exec"
	"time"
)

var execCommand = exec.Command

func handleOutput(s *bufio.Scanner, out chan<- string) {
	defer close(out)
	for s.Scan() {
		text := s.Text()
		out <- text
	}
}

// ExecCommand take a slice of string to be executed as the
// command. stdout and stderr are passed to the channels handed
// to the function.
func ExecCommand(a []string, stdout chan<- string, stderr chan<- string) {
	cmd := execCommand(a[0], a[1:]...)
	stdoutPipe, err := cmd.StdoutPipe()
	if err != nil {
		log.Fatalf("RunCommand: cmd.StdoutPipe(): %v", err)
	}
	stderrPipe, err := cmd.StderrPipe()
	if err != nil {
		log.Fatalf("RunCommand: cmd.StderrPipe(): %v", err)
	}

	if err := cmd.Start(); err != nil {
		log.Fatalf("RunCommand: cmd.Start(): %v", err)
	}

	scanner := bufio.NewScanner(stdoutPipe)
	go handleOutput(scanner, stdout)

	errScanner := bufio.NewScanner(stderrPipe)
	go handleOutput(errScanner, stderr)

	if err := cmd.Wait(); err != nil {
		log.Fatalf("RunCommand: cmd.Wait(): %v", err)
	}
}

// Process representing the process that is going to be run.
type Process struct {
	Command  string
	Args     []string
	Delay    string
	Pid      int
	Status   string
	OsP      *os.Process
	respawns int
}

// RunIt runs the Process and makes sure it keeps running.
func RunIt(p *Process) chan *Process {
	ch := make(chan *Process)
	go func() {
		p.run()
		go p.monitor()
		ch <- p
	}()

	return ch
}

func (p *Process) run() {
	wd, _ := os.Getwd()
	proc := &os.ProcAttr{
		Dir: wd,
		Env: os.Environ(),
		Files: []*os.File{
			os.Stdin,
			os.Stdout,
			os.Stderr,
		},
	}

	process, err := os.StartProcess(p.Command, p.Args, proc)
	if err != nil {
		log.Fatalf("%s failed. %s\n", p.Command, err)
		return
	}
	p.OsP = process
	p.Pid = process.Pid
	p.Status = "started"

}

func (p *Process) monitor() {
	if p.OsP == nil {
		return
	}
	status := make(chan *os.ProcessState)
	died := make(chan error)
	go func() {
		state, err := p.OsP.Wait()
		if err != nil {
			died <- err
			return
		}
		status <- state
	}()
	select {
	case s := <-status:
		log.Printf("%s exit=%s, success=%#v, exited=%#v, respawn_count=%#v\n", p.Command, s, s.Success(), s.Exited(), p.respawns)
		p.respawns++
		if p.Delay != "" {
			log.Printf("%s sleeping for %#v before restarting\n", p.Command, p.Delay)
			t, _ := time.ParseDuration(p.Delay)
			time.Sleep(t)
		}
		RunIt(p)
		p.Status = "restarted"
	case err := <-died:
		log.Printf("%d %s killed = %#v\n", p.OsP.Pid, p.Command, err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/spf13/cobra"
)

type Options struct {
	verbose  bool
	force    bool
	kill     bool
	config   string
	manifest string
	group    string
}

var options = Options{
	false,
	false,
	false,
	"",
	"",
	"",
}
var defaultManifests = []string{"crane.json", "crane.yaml", "crane.yml", "Cranefile"}

func manifestFiles() []string {
	var result = []string(nil)
	if len(options.manifest) > 0 {
		result = []string{options.manifest}
	} else {
		result = defaultManifests
	}
	return result
}

func isVerbose() bool {
	return options.verbose
}

func handleCmd() {
	var cmdLift = &cobra.Command{
		Use:   "lift",
		Short: "Build or pull images, then run or start the containers",
		Long: `
provision will use specified Dockerfiles to build the images.
If no Dockerfile is given, it will pull the image from the index.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.lift(options.force, options.kill)
		},
	}

	var cmdProvision = &cobra.Command{
		Use:   "provision",
		Short: "Build or pull images",
		Long: `
provision will use specified Dockerfiles to build the images.
If no Dockerfile is given, it will pull the image from the index.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.provision(options.force)
		},
	}

	var cmdRun = &cobra.Command{
		Use:   "run",
		Short: "Run the containers",
		Long:  `run will call docker run on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.run(options.force, options.kill)
		},
	}

	var cmdRm = &cobra.Command{
		Use:   "rm",
		Short: "Remove the containers",
		Long:  `rm will call docker rm on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.rm(options.force, options.kill)
		},
	}

	var cmdKill = &cobra.Command{
		Use:   "kill",
		Short: "Kill the containers",
		Long:  `kill will call docker kill on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.kill()
		},
	}

	var cmdStart = &cobra.Command{
		Use:   "start",
		Short: "Start the containers",
		Long:  `start will call docker start on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.start()
		},
	}

	var cmdStop = &cobra.Command{
		Use:   "stop",
		Short: "Stop the containers",
		Long:  `stop will call docker stop on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.stop()
		},
	}

	var cmdStatus = &cobra.Command{
		Use:   "status",
		Short: "Displays status of containers",
		Long:  `Displays the current status of the containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.status()
		},
	}

	var cmdVersion = &cobra.Command{
		Use:   "version",
		Short: "Display version",
		Long:  `Displays the version of Crane.`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("v0.7.0")
		},
	}

	var craneCmd = &cobra.Command{
		Use:   "crane",
		Short: "crane - Lift containers with ease",
		Long: `
Crane is a little tool to orchestrate Docker containers.
It works by reading in JSON or YAML (either from crane.json, crane.yaml, the string specified in --config, or a json or yml file specified by --manifest) which describes how to obtain container images and how to run them.
See the corresponding docker commands for more information.`,
	}

	craneCmd.PersistentFlags().BoolVarP(&options.verbose, "verbose", "v", false, "verbose output")
	craneCmd.PersistentFlags().StringVarP(&options.config, "config", "c", "", "config to read from")
	craneCmd.PersistentFlags().StringVarP(&options.manifest, "manifest", "m", "", "config file to read from")
	craneCmd.PersistentFlags().StringVarP(&options.group, "group", "g", "", "group or container to restrict the command to")
	cmdLift.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdLift.Flags().BoolVarP(&options.kill, "kill", "k", false, "kill containers")
	cmdProvision.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdRun.Flags().BoolVarP(&options.force, "force", "f", false, "stop and remove running containers first")
	cmdRun.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	cmdRm.Flags().BoolVarP(&options.force, "force", "f", false, "stop running containers first")
	cmdRm.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	craneCmd.AddCommand(cmdLift, cmdProvision, cmdRun, cmdRm, cmdKill, cmdStart, cmdStop, cmdStatus, cmdVersion)
	craneCmd.Execute()
}
<commit_msg>fix copy/paste<commit_after>package main

import (
	"fmt"
	"github.com/spf13/cobra"
)

type Options struct {
	verbose  bool
	force    bool
	kill     bool
	config   string
	manifest string
	group    string
}

var options = Options{
	false,
	false,
	false,
	"",
	"",
	"",
}
var defaultManifests = []string{"crane.json", "crane.yaml", "crane.yml", "Cranefile"}

func manifestFiles() []string {
	var result = []string(nil)
	if len(options.manifest) > 0 {
		result = []string{options.manifest}
	} else {
		result = defaultManifests
	}
	return result
}

func isVerbose() bool {
	return options.verbose
}

func handleCmd() {
	var cmdLift = &cobra.Command{
		Use:   "lift",
		Short: "Build or pull images, then run or start the containers",
		Long: `
lift will use specified Dockerfiles to build the images.
If no Dockerfile is given, it will pull the image from the index.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.lift(options.force, options.kill)
		},
	}

	var cmdProvision = &cobra.Command{
		Use:   "provision",
		Short: "Build or pull images",
		Long: `
provision will use specified Dockerfiles to build the images.
If no Dockerfile is given, it will pull the image from the index.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.provision(options.force)
		},
	}

	var cmdRun = &cobra.Command{
		Use:   "run",
		Short: "Run the containers",
		Long:  `run will call docker run on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.run(options.force, options.kill)
		},
	}

	var cmdRm = &cobra.Command{
		Use:   "rm",
		Short: "Remove the containers",
		Long:  `rm will call docker rm on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.rm(options.force, options.kill)
		},
	}

	var cmdKill = &cobra.Command{
		Use:   "kill",
		Short: "Kill the containers",
		Long:  `kill will call docker kill on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.kill()
		},
	}

	var cmdStart = &cobra.Command{
		Use:   "start",
		Short: "Start the containers",
		Long:  `start will call docker start on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.start()
		},
	}

	var cmdStop = &cobra.Command{
		Use:   "stop",
		Short: "Stop the containers",
		Long:  `stop will call docker stop on all containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.stop()
		},
	}

	var cmdStatus = &cobra.Command{
		Use:   "status",
		Short: "Displays status of containers",
		Long:  `Displays the current status of the containers.`,
		Run: func(cmd *cobra.Command, args []string) {
			containers := getContainers(options)
			containers.status()
		},
	}

	var cmdVersion = &cobra.Command{
		Use:   "version",
		Short: "Display version",
		Long:  `Displays the version of Crane.`,
		Run: func(cmd *cobra.Command, args []string) {
			fmt.Println("v0.7.0")
		},
	}

	var craneCmd = &cobra.Command{
		Use:   "crane",
		Short: "crane - Lift containers with ease",
		Long: `
Crane is a little tool to orchestrate Docker containers.
It works by reading in JSON or YAML (either from crane.json, crane.yaml, the string specified in --config, or a json or yml file specified by --manifest) which describes how to obtain container images and how to run them.
See the corresponding docker commands for more information.`,
	}

	craneCmd.PersistentFlags().BoolVarP(&options.verbose, "verbose", "v", false, "verbose output")
	craneCmd.PersistentFlags().StringVarP(&options.config, "config", "c", "", "config to read from")
	craneCmd.PersistentFlags().StringVarP(&options.manifest, "manifest", "m", "", "config file to read from")
	craneCmd.PersistentFlags().StringVarP(&options.group, "group", "g", "", "group or container to restrict the command to")
	cmdLift.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdLift.Flags().BoolVarP(&options.kill, "kill", "k", false, "kill containers")
	cmdProvision.Flags().BoolVarP(&options.force, "force", "f", false, "rebuild all images")
	cmdRun.Flags().BoolVarP(&options.force, "force", "f", false, "stop and remove running containers first")
	cmdRun.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	cmdRm.Flags().BoolVarP(&options.force, "force", "f", false, "stop running containers first")
	cmdRm.Flags().BoolVarP(&options.kill, "kill", "k", false, "when using --force, kill containers instead of stopping them")
	craneCmd.AddCommand(cmdLift, cmdProvision, cmdRun, cmdRm, cmdKill, cmdStart, cmdStop, cmdStatus, cmdVersion)
	craneCmd.Execute()
}
<|endoftext|>


<commit_before>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/tfdiags"
)

func buildProviderConfig(ctx EvalContext, addr addrs.ProviderConfig, body hcl.Body) hcl.Body {
	// If we have an Input configuration set, then merge that in
	if input := ctx.ProviderInput(addr); input != nil {
		// "input" is a map of the subset of config values that were known
		// during the input walk, set by EvalInputProvider. Note that
		// in particular it does *not* include attributes that had
		// computed values at input time.

		inputBody := configs.SynthBody("<input prompt>", input)
		body = configs.MergeBodies(body, inputBody)
	}

	return body
}

// EvalConfigProvider is an EvalNode implementation that configures
// a provider that is already initialized and retrieved.
type EvalConfigProvider struct {
	Addr     addrs.ProviderConfig
	Provider *ResourceProvider
	Config   *configs.Provider
}

func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
	if n.Provider == nil {
		return nil, fmt.Errorf("EvalConfigProvider Provider is nil")
	}
	if n.Config == nil {
		return nil, nil
	}

	var diags tfdiags.Diagnostics
	provider := *n.Provider
	config := n.Config

	if config == nil {
		// If we have no explicit configuration, just write an empty
		// configuration into the provider.
		configDiags := ctx.ConfigureProvider(n.Addr, cty.EmptyObjectVal)
		return nil, configDiags.ErrWithWarnings()
	}

	schema, err := provider.GetSchema(&ProviderSchemaRequest{})
	if err != nil {
		diags = diags.Append(err)
		return nil, diags.NonFatalErr()
	}
	if schema == nil {
		return nil, fmt.Errorf("schema not available for %s", n.Addr)
	}

	configSchema := schema.Provider
	configBody := buildProviderConfig(ctx, n.Addr, config.Config)
	configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, addrs.NoKey)
	diags = diags.Append(evalDiags)
	if evalDiags.HasErrors() {
		return nil, diags.NonFatalErr()
	}

	configDiags := ctx.ConfigureProvider(n.Addr, configVal)
	configDiags = configDiags.InConfigBody(configBody)

	return nil, configDiags.ErrWithWarnings()
}

// EvalInitProvider is an EvalNode implementation that initializes a provider
// and returns nothing. The provider can be retrieved again with the
// EvalGetProvider node.
type EvalInitProvider struct {
	TypeName string
	Addr     addrs.ProviderConfig
}

func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) {
	return ctx.InitProvider(n.TypeName, n.Addr)
}

// EvalCloseProvider is an EvalNode implementation that closes provider
// connections that aren't needed anymore.
type EvalCloseProvider struct {
	Addr addrs.ProviderConfig
}

func (n *EvalCloseProvider) Eval(ctx EvalContext) (interface{}, error) {
	ctx.CloseProvider(n.Addr)
	return nil, nil
}

// EvalGetProvider is an EvalNode implementation that retrieves an already
// initialized provider instance for the given name.
//
// Unlike most eval nodes, this takes an _absolute_ provider configuration,
// because providers can be passed into and inherited between modules.
// Resource nodes must therefore know the absolute path of the provider they
// will use, which is usually accomplished by implementing
// interface GraphNodeProviderConsumer.
type EvalGetProvider struct {
	Addr   addrs.AbsProviderConfig
	Output *ResourceProvider

	// If non-nil, Schema will be updated after eval to refer to the
	// schema of the provider.
	Schema **ProviderSchema
}

func (n *EvalGetProvider) Eval(ctx EvalContext) (interface{}, error) {
	result := ctx.Provider(n.Addr)
	if result == nil {
		return nil, fmt.Errorf("provider %s not initialized", n.Addr)
	}

	if n.Output != nil {
		*n.Output = result
	}

	if n.Schema != nil {
		*n.Schema = ctx.ProviderSchema(n.Addr)
	}

	return nil, nil
}

// EvalInputProvider is an EvalNode implementation that asks for input
// for the given provider configurations.
type EvalInputProvider struct {
	Addr     addrs.ProviderConfig
	Provider *ResourceProvider
	Config   *configs.Provider
}

func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
	// This is currently disabled. It used to interact with a provider method
	// called Input, allowing the provider to capture input interactively
	// itself, but once re-implemented we'll have this instead use the
	// provider's configuration schema to automatically infer what we need
	// to prompt for.
	var diags tfdiags.Diagnostics
	diags = diags.Append(tfdiags.SimpleWarning(fmt.Sprintf("%s: provider input is temporarily disabled", n.Addr)))
	return nil, diags.ErrWithWarnings()
}
<commit_msg>it is valid to have no provider config<commit_after>package terraform

import (
	"fmt"

	"github.com/hashicorp/hcl2/hcl"
	"github.com/zclconf/go-cty/cty"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/configs"
	"github.com/hashicorp/terraform/tfdiags"
)

func buildProviderConfig(ctx EvalContext, addr addrs.ProviderConfig, body hcl.Body) hcl.Body {
	// If we have an Input configuration set, then merge that in
	if input := ctx.ProviderInput(addr); input != nil {
		// "input" is a map of the subset of config values that were known
		// during the input walk, set by EvalInputProvider. Note that
		// in particular it does *not* include attributes that had
		// computed values at input time.

		inputBody := configs.SynthBody("<input prompt>", input)
		body = configs.MergeBodies(body, inputBody)
	}

	return body
}

// EvalConfigProvider is an EvalNode implementation that configures
// a provider that is already initialized and retrieved.
type EvalConfigProvider struct {
	Addr     addrs.ProviderConfig
	Provider *ResourceProvider
	Config   *configs.Provider
}

func (n *EvalConfigProvider) Eval(ctx EvalContext) (interface{}, error) {
	if n.Provider == nil {
		return nil, fmt.Errorf("EvalConfigProvider Provider is nil")
	}

	var diags tfdiags.Diagnostics
	provider := *n.Provider
	config := n.Config

	if config == nil {
		// If we have no explicit configuration, just write an empty
		// configuration into the provider.
		configDiags := ctx.ConfigureProvider(n.Addr, cty.EmptyObjectVal)
		return nil, configDiags.ErrWithWarnings()
	}

	schema, err := provider.GetSchema(&ProviderSchemaRequest{})
	if err != nil {
		diags = diags.Append(err)
		return nil, diags.NonFatalErr()
	}
	if schema == nil {
		return nil, fmt.Errorf("schema not available for %s", n.Addr)
	}

	configSchema := schema.Provider
	configBody := buildProviderConfig(ctx, n.Addr, config.Config)
	configVal, configBody, evalDiags := ctx.EvaluateBlock(configBody, configSchema, nil, addrs.NoKey)
	diags = diags.Append(evalDiags)
	if evalDiags.HasErrors() {
		return nil, diags.NonFatalErr()
	}

	configDiags := ctx.ConfigureProvider(n.Addr, configVal)
	configDiags = configDiags.InConfigBody(configBody)

	return nil, configDiags.ErrWithWarnings()
}

// EvalInitProvider is an EvalNode implementation that initializes a provider
// and returns nothing. The provider can be retrieved again with the
// EvalGetProvider node.
type EvalInitProvider struct {
	TypeName string
	Addr     addrs.ProviderConfig
}

func (n *EvalInitProvider) Eval(ctx EvalContext) (interface{}, error) {
	return ctx.InitProvider(n.TypeName, n.Addr)
}

// EvalCloseProvider is an EvalNode implementation that closes provider
// connections that aren't needed anymore.
type EvalCloseProvider struct {
	Addr addrs.ProviderConfig
}

func (n *EvalCloseProvider) Eval(ctx EvalContext) (interface{}, error) {
	ctx.CloseProvider(n.Addr)
	return nil, nil
}

// EvalGetProvider is an EvalNode implementation that retrieves an already
// initialized provider instance for the given name.
//
// Unlike most eval nodes, this takes an _absolute_ provider configuration,
// because providers can be passed into and inherited between modules.
// Resource nodes must therefore know the absolute path of the provider they
// will use, which is usually accomplished by implementing
// interface GraphNodeProviderConsumer.
type EvalGetProvider struct {
	Addr   addrs.AbsProviderConfig
	Output *ResourceProvider

	// If non-nil, Schema will be updated after eval to refer to the
	// schema of the provider.
	Schema **ProviderSchema
}

func (n *EvalGetProvider) Eval(ctx EvalContext) (interface{}, error) {
	result := ctx.Provider(n.Addr)
	if result == nil {
		return nil, fmt.Errorf("provider %s not initialized", n.Addr)
	}

	if n.Output != nil {
		*n.Output = result
	}

	if n.Schema != nil {
		*n.Schema = ctx.ProviderSchema(n.Addr)
	}

	return nil, nil
}

// EvalInputProvider is an EvalNode implementation that asks for input
// for the given provider configurations.
type EvalInputProvider struct {
	Addr     addrs.ProviderConfig
	Provider *ResourceProvider
	Config   *configs.Provider
}

func (n *EvalInputProvider) Eval(ctx EvalContext) (interface{}, error) {
	// This is currently disabled. It used to interact with a provider method
	// called Input, allowing the provider to capture input interactively
	// itself, but once re-implemented we'll have this instead use the
	// provider's configuration schema to automatically infer what we need
	// to prompt for.
	var diags tfdiags.Diagnostics
	diags = diags.Append(tfdiags.SimpleWarning(fmt.Sprintf("%s: provider input is temporarily disabled", n.Addr)))
	return nil, diags.ErrWithWarnings()
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd 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 cni

import (
	"fmt"
	"sync"

	cnilibrary "github.com/containernetworking/cni/libcni"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/pkg/errors"
)

type CNI interface {
	// Setup setup the network for the namespace
	Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
	// Remove tears down the network of the namespace.
	Remove(id string, path string, opts ...NamespaceOpts) error
	// Load loads the cni network config
	Load(opts ...CNIOpt) error
	// Status checks the status of the cni initialization
	Status() error
}

type libcni struct {
	config

	cniConfig    cnilibrary.CNI
	networkCount int // minimum network plugin configurations needed to initialize cni
	networks     []*Network
	sync.RWMutex
}

func defaultCNIConfig() *libcni {
	return &libcni{
		config: config{
			pluginDirs:    []string{DefaultCNIDir},
			pluginConfDir: DefaultNetDir,
			prefix:        DefaultPrefix,
		},
		cniConfig: &cnilibrary.CNIConfig{
			Path: []string{DefaultCNIDir},
		},
		networkCount: 1,
	}
}

func New(config ...CNIOpt) (CNI, error) {
	cni := defaultCNIConfig()
	var err error
	for _, c := range config {
		if err = c(cni); err != nil {
			return nil, err
		}
	}
	return cni, nil
}

func (c *libcni) Load(opts ...CNIOpt) error {
	var err error
	c.Lock()
	defer c.Unlock()
	// Reset the networks on a load operation to ensure
	// config happens on a clean slate
	c.reset()

	for _, o := range opts {
		if err = o(c); err != nil {
			return errors.Wrapf(ErrLoad, fmt.Sprintf("cni config load failed: %v", err))
		}
	}
	return nil
}

func (c *libcni) Status() error {
	c.RLock()
	defer c.RUnlock()
	return c.status()
}

// Setup setups the network in the namespace
func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.status(); err != nil {
		return nil, err
	}
	ns, err := newNamespace(id, path, opts...)
	if err != nil {
		return nil, err
	}
	var results []*current.Result
	for _, network := range c.networks {
		r, err := network.Attach(ns)
		if err != nil {
			return nil, err
		}
		results = append(results, r)
	}
	return c.GetCNIResultFromResults(results)
}

// Remove removes the network config from the namespace
func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.status(); err != nil {
		return err
	}
	ns, err := newNamespace(id, path, opts...)
	if err != nil {
		return err
	}
	for _, network := range c.networks {
		if err := network.Remove(ns); err != nil {
			return err
		}
	}
	return nil
}

func (c *libcni) reset() {
	c.networks = nil
}

func (c *libcni) status() error {
	if len(c.networks) < c.networkCount {
		return ErrCNINotInitialized
	}
	return nil
}
<commit_msg>Ignore "no such file or directory" error.<commit_after>/*
   Copyright The containerd 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 cni

import (
	"fmt"
	"strings"
	"sync"

	cnilibrary "github.com/containernetworking/cni/libcni"
	"github.com/containernetworking/cni/pkg/types/current"
	"github.com/pkg/errors"
)

type CNI interface {
	// Setup setup the network for the namespace
	Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error)
	// Remove tears down the network of the namespace.
	Remove(id string, path string, opts ...NamespaceOpts) error
	// Load loads the cni network config
	Load(opts ...CNIOpt) error
	// Status checks the status of the cni initialization
	Status() error
}

type libcni struct {
	config

	cniConfig    cnilibrary.CNI
	networkCount int // minimum network plugin configurations needed to initialize cni
	networks     []*Network
	sync.RWMutex
}

func defaultCNIConfig() *libcni {
	return &libcni{
		config: config{
			pluginDirs:    []string{DefaultCNIDir},
			pluginConfDir: DefaultNetDir,
			prefix:        DefaultPrefix,
		},
		cniConfig: &cnilibrary.CNIConfig{
			Path: []string{DefaultCNIDir},
		},
		networkCount: 1,
	}
}

func New(config ...CNIOpt) (CNI, error) {
	cni := defaultCNIConfig()
	var err error
	for _, c := range config {
		if err = c(cni); err != nil {
			return nil, err
		}
	}
	return cni, nil
}

func (c *libcni) Load(opts ...CNIOpt) error {
	var err error
	c.Lock()
	defer c.Unlock()
	// Reset the networks on a load operation to ensure
	// config happens on a clean slate
	c.reset()

	for _, o := range opts {
		if err = o(c); err != nil {
			return errors.Wrapf(ErrLoad, fmt.Sprintf("cni config load failed: %v", err))
		}
	}
	return nil
}

func (c *libcni) Status() error {
	c.RLock()
	defer c.RUnlock()
	return c.status()
}

// Setup setups the network in the namespace
func (c *libcni) Setup(id string, path string, opts ...NamespaceOpts) (*CNIResult, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.status(); err != nil {
		return nil, err
	}
	ns, err := newNamespace(id, path, opts...)
	if err != nil {
		return nil, err
	}
	var results []*current.Result
	for _, network := range c.networks {
		r, err := network.Attach(ns)
		if err != nil {
			return nil, err
		}
		results = append(results, r)
	}
	return c.GetCNIResultFromResults(results)
}

// Remove removes the network config from the namespace
func (c *libcni) Remove(id string, path string, opts ...NamespaceOpts) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.status(); err != nil {
		return err
	}
	ns, err := newNamespace(id, path, opts...)
	if err != nil {
		return err
	}
	for _, network := range c.networks {
		if err := network.Remove(ns); err != nil && !strings.Contains(err.Error(), "no such file or directory") {
			return err
		}
	}
	return nil
}

func (c *libcni) reset() {
	c.networks = nil
}

func (c *libcni) status() error {
	if len(c.networks) < c.networkCount {
		return ErrCNINotInitialized
	}
	return nil
}
<|endoftext|>


<commit_before>package transmogrifier

import (
	"encoding/csv"
	_ "fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	_ "strconv"
	"strings"
)

// CSV is a struct for representing and working with csv data.
type CSV struct {
	// source information.
	source resource
	// sink information
	sink resource
	// format information
	format resource
	// Automatically set the format source-or not.
	formatSourceAutoSet bool
	// useFormat use a format file
	useFormat bool
	// formatType is the type of format being used
	formatType string
	// Variables consistent with stdlib's Reader struct in the csv package,
	// with the exception of csv.Reader.TrailingComma, which is ommitted
	// since it is ignored.
	//
	// Anything set here will override the Reader's default value set by
	// csv.NewReader(). Please check golang.org/pkg/encoding/csv for more
	// info about the variables.
	comma            rune
	comment          rune
	fieldsPerRecord  int
	lazyQuotes       bool
	trimLeadingSpace bool
	// format
	hasFormat bool
	// hasHeaderRows: whether the csv data includes a header row as its
	// first row. If the csv data does not include header data, the header
	// data must be provided via template, e.g. false implies
	// 'useFormat' == true. True does not have any implications on using
	// the format file.
	hasHeaderRow bool
	// The csv file data:
	// headerRow contains the header row information. This is when a format
	// has been supplied, the header row information is set.
	headerRow []string
	// table is the parsed csv data
	table [][]string
}

// NewCSV returns an initialize CSV object. It still needs to be configured
// for use.
func NewCSV() *CSV {
	C := &CSV{
		source:       resource{},
		sink:         resource{},
		format:       resource{},
		hasHeaderRow: true,
		table:        [][]string{},
	}
	return C
}

// NewSourceCSV creates a new *CSV with its source set and initialized.
func NewSourcesCSV(t, s string, b bool) *CSV {
	c := NewCSV()
	c.useFormat = b
	// currently anything that's not file uses the default "", which
	// means set it yourself to use it.
	switch t {
	case "bytes":
	case "file":
		c.formatType = "file"
	}
	c.SetSource(s)
	return c
}

// ReadCSV takes a reader, and reads the data connected with it as CSV data.
// A slice of slice of type string, or an error, are returned. This reads the
// entire file, so if the file is very large and you don't have sufficent RAM
// you will not like the results. There may be a row or chunk oriented
// implementation in the future.
func ReadCSV(r io.Reader) ([][]string, error) {
	cr := csv.NewReader(r)
	rows, err := cr.ReadAll()
	if err != nil {
		//		logger.Error(err)
		return nil, err
	}
	return rows, nil
}

// ReadCSVFile takes a path, reads the contents of the file and returns int.
func ReadCSVFile(f string) ([][]string, error) {
	file, err := os.Open(f)
	if err != nil {
		//		logger.Error(err)
		return nil, err
	}
	// because we don't want to forget or worry about hanldling close prior
	// to every return.
	defer file.Close()
	// Read the file into csv
	csv, err := ReadCSV(file)
	return csv, nil
}

// SetSource sets the source and has the formatFile updated, if applicable.
func (c *CSV) SetSource(s string) {
	c.source = resource{Name: s, Path: s}
	c.autoSetFormatFile()
}

// autoSetFormatSource sets the formatSource if it is not already set or if the
// previously set value was set by setFormatSource. The latter allows auto-
// generated default source name to be updated when the source is while
// preserving overrides.
func (c *CSV) autoSetFormatFile() error {
	// if the source isn't set, nothing to do.
	if c.source.Name == "" {
		log.Printf("setFormatSource exit: source not set")
		return nil
	}
	// if formatSource isn't empty and wasn't set by setFormatSource,
	// nothing to do
	if c.source.Format != "" && !c.formatSourceAutoSet {
		log.Printf("setFormatSource exit: formatSource was already set to %s", c.source.Format)
		return nil
	}
	if c.source.Type != "file" {
		log.Printf("setFormatSource exit: not using format file, format type is %s", c.source.Type)
		return nil
	}
	// Figure out the filename
	dir, file := filepath.Split(c.source.Name)
	// break up the filename into its part, the last is extension.
	var fname string
	fParts := strings.Split(file, ".")
	if len(fParts) <= 2 {
		fname = fParts[0]
	} else {
		// Join all but the last part together for the name
		// This handles names with multiple `.`
		fname = strings.Join(fParts[0:len(fParts)-2], ".")
	}
	fname += ".md"
	c.source.Path = filepath.Join(dir, fname)
	c.formatSourceAutoSet = true
	return nil
}
<commit_msg>refactor csv part of csv to md handling so that CSV drives it<commit_after>package transmogrifier

import (
	"encoding/csv"
	_ "fmt"
	"io"
	"log"
	"os"
	"path/filepath"
	_ "strconv"
	"strings"
)

// CSV is a struct for representing and working with csv data.
type CSV struct {
	// source information.
	source resource
	// sink information
	sink resource
	// format information
	format resource
	// Automatically set the format source-or not.
	formatSourceAutoSet bool
	// useFormat use a format file
	useFormat bool
	// formatType is the type of format being used
	formatType string
	// Variables consistent with stdlib's Reader struct in the csv package,
	// with the exception of csv.Reader.TrailingComma, which is ommitted
	// since it is ignored.
	//
	// Anything set here will override the Reader's default value set by
	// csv.NewReader(). Please check golang.org/pkg/encoding/csv for more
	// info about the variables.
	comma            rune
	comment          rune
	fieldsPerRecord  int
	lazyQuotes       bool
	trimLeadingSpace bool
	// format
	hasFormat bool
	// hasHeaderRows: whether the csv data includes a header row as its
	// first row. If the csv data does not include header data, the header
	// data must be provided via template, e.g. false implies
	// 'useFormat' == true. True does not have any implications on using
	// the format file.
	hasHeaderRow bool
	// The csv file data:
	// headerRow contains the header row information. This is when a format
	// has been supplied, the header row information is set.
	headerRow []string
	// table is the parsed csv data
	table [][]string
}

// NewCSV returns an initialize CSV object. It still needs to be configured
// for use.
func NewCSV() *CSV {
	C := &CSV{
		source:       resource{},
		sink:         resource{},
		format:       resource{},
		hasHeaderRow: true,
		table:        [][]string{},
	}
	return C
}

// NewSourceCSV creates a new *CSV with its source set and initialized.
func NewSourcesCSV(t, s string, b bool) *CSV {
	c := NewCSV()
	c.useFormat = b
	// currently anything that's not file uses the default "", which
	// means set it yourself to use it.
	switch t {
	case "bytes":
	case "file":
		c.formatType = "file"
	}
	c.SetSource(s)
	return c
}

// ReadCSV takes a reader, and reads the data connected with it as CSV data.
// A slice of slice of type string, or an error, are returned. This reads the
// entire file, so if the file is very large and you don't have sufficent RAM
// you will not like the results. There may be a row or chunk oriented
// implementation in the future.
func ReadCSV(r io.Reader) ([][]string, error) {
	cr := csv.NewReader(r)
	rows, err := cr.ReadAll()
	if err != nil {
		//		logger.Error(err)
		return nil, err
	}
	return rows, nil
}

// ReadCSVFile takes a path, reads the contents of the file and returns int.
func ReadCSVFile(f string) ([][]string, error) {
	file, err := os.Open(f)
	if err != nil {
		//		logger.Error(err)
		return nil, err
	}
	// because we don't want to forget or worry about hanldling close prior
	// to every return.
	defer file.Close()
	// Read the file into csv
	csv, err := ReadCSV(file)
	return csv, nil
}

// SetSource sets the source and has the formatFile updated, if applicable.
func (c *CSV) SetSource(s string) {
	c.source = resource{Name: s, Path: s}
	c.autoSetFormatFile()
}

// autoSetFormatSource sets the formatSource if it is not already set or if the
// previously set value was set by setFormatSource. The latter allows auto-
// generated default source name to be updated when the source is while
// preserving overrides.
func (c *CSV) autoSetFormatFile() error {
	// if the source isn't set, nothing to do.
	if c.source.Name == "" {
		log.Printf("setFormatSource exit: source not set")
		return nil
	}
	// if formatSource isn't empty and wasn't set by setFormatSource,
	// nothing to do
	if c.source.Format != "" && !c.formatSourceAutoSet {
		log.Printf("setFormatSource exit: formatSource was already set to %s", c.source.Format)
		return nil
	}
	if c.source.Type != "file" {
		log.Printf("setFormatSource exit: not using format file, format type is %s", c.source.Type)
		return nil
	}
	// Figure out the filename
	dir, file := filepath.Split(c.source.Name)
	// break up the filename into its part, the last is extension.
	var fname string
	fParts := strings.Split(file, ".")
	if len(fParts) <= 2 {
		fname = fParts[0]
	} else {
		// Join all but the last part together for the name
		// This handles names with multiple `.`
		fname = strings.Join(fParts[0:len(fParts)-2], ".")
	}
	fname += ".md"
	c.source.Path = filepath.Join(dir, fname)
	c.formatSourceAutoSet = true
	return nil
}

// TableToMD creates a MD table out of the CSV data
func (c *CSV) toMDTable() error {
	// Try to read the source
	c.Table, err := ReadCSVFile(m.Source.Path)
	if err != nil {
		return err
	}
	var formatName string
	// otherwise see if  HasFormat
	if c.UseFormat {
		//		c.setFormatFile()
		if c.FormatType == "file" {
			//derive the format filename
			filename := filepath.Base(c.Source.Path)
			if filename == "." {
				err = fmt.Errorf("unable to determine format filename")
				return err
			}
			dir := filepath.Dir(c.Source.Path)
			parts := strings.Split(filename, ".")
			formatName = parts[0] + ".fmt"
			if dir != "." {
				formatName = dir + formatName
			}
			err := c.formatFromFile()
			if err != nil {
				return err
			}
		}
	}
	// Now convert the data to md
	md := NewMDTable()
	err = md.CSVToMD(c)
	return err
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"log"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/miekg/dns"
)

var dnsBinding = flag.String("dnsbind", "",
	"address to bind dns server to")
var dnsZone = flag.String("dnszone", "cbfs.",
	"DNS Zone for msgs/responses")

const cbfsSvc = "_cbfs._tcp"

const maxDnsResponses = 8

type dnsService struct{}

func (d dnsService) serviceDomain() string {
	return cbfsSvc + "." + *dnsZone
}

func (d dnsService) writeLogErr(w dns.ResponseWriter, msg *dns.Msg) {
	err := w.Write(msg)
	if err != nil {
		log.Printf("Error writing response: %v\n%v", err, msg)
	}
}

func (d dnsService) srvList(w dns.ResponseWriter, r *dns.Msg) {
	msg := &dns.Msg{}

	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Error finding nodes: %v", err)
		return
	}

	for i, n := range nl {
		_, p, err := net.SplitHostPort(n.Address())
		if err != nil {
			continue
		}
		port := 8484
		tmp, err := strconv.Atoi(p)
		if err == nil {
			port = tmp
		}

		rr := &dns.RR_SRV{
			Hdr: dns.RR_Header{
				Name:   d.serviceDomain(),
				Rrtype: dns.TypeSRV,
				Class:  dns.ClassINET,
				Ttl:    5},
			Priority: uint16(i),
			Weight:   uint16(time.Since(n.Time).Seconds()),
			Port:     uint16(port),
			Target:   n.name + "." + *dnsZone,
		}
		msg.Answer = append(msg.Answer, rr)

		arr := &dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   n.name + "." + *dnsZone,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    60,
			},
			A: net.ParseIP(n.Addr),
		}

		msg.Extra = append(msg.Extra, arr)

		if len(msg.Answer) > maxDnsResponses {
			break
		}
	}

	msg.SetReply(r)
	d.writeLogErr(w, msg)
}

func (d dnsService) hostLookup(w dns.ResponseWriter, r *dns.Msg) {
	msg := &dns.Msg{}

	name := r.Question[0].Name
	name = name[:len(name)-len(*dnsZone)-1]

	node, err := findNode(name)
	if err == nil {
		msg.Answer = []dns.RR{&dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   r.Question[0].Name,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    60,
			},
			A: net.ParseIP(node.Addr),
		}}
		msg.SetReply(r)
	} else {
		msg.SetRcode(r, dns.RcodeNameError)
	}

	d.writeLogErr(w, msg)
}

func (d dnsService) listHosts(w dns.ResponseWriter, r *dns.Msg) {
	msg := &dns.Msg{}

	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Error finding nodes: %v", err)
		return
	}

	for _, n := range nl {
		if time.Since(n.Time) > (3 * globalConfig.HeartbeatFreq) {
			continue
		}
		rr := &dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   *dnsZone,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    5},
			A: net.ParseIP(n.Addr),
		}

		msg.Answer = append(msg.Answer, rr)
		if len(msg.Answer) > maxDnsResponses {
			break
		}
	}

	msg.SetReply(r)
	d.writeLogErr(w, msg)
}

func (d dnsService) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	if *verbose {
		log.Printf("Incoming DNS Query: %v", r)
	}
	q := dns.Question{}

	if len(r.Question) == 1 &&
		strings.HasSuffix(r.Question[0].Name, *dnsZone) {
		q = r.Question[0]
	}

	switch q.Qtype {
	case dns.TypeSRV:
		d.srvList(w, r)
	case dns.TypeA, dns.TypeANY:
		if q.Name == *dnsZone {
			d.listHosts(w, r)
		} else {
			d.hostLookup(w, r)
		}
	case dns.TypeAAAA:
		msg := &dns.Msg{}
		msg.SetRcode(r, dns.RcodeSuccess)
		d.writeLogErr(w, msg)
	default:
		msg := &dns.Msg{}
		msg.SetRcode(r, dns.RcodeNotImplemented)
		d.writeLogErr(w, msg)
	}
}

func dnsServices() {
	if *dnsBinding == "" {
		return
	}

	log.Printf("Starting DNS services on %v.", *dnsBinding)

	d := dnsService{}

	serv := dns.Server{
		Net:     "udp",
		Addr:    *dnsBinding,
		Handler: d,
	}

	err := serv.ListenAndServe()
	if err != nil {
		log.Fatalf("DNS server failure: %v", err)
	}
}
<commit_msg>DNS API Update.<commit_after>package main

import (
	"flag"
	"log"
	"net"
	"strconv"
	"strings"
	"time"

	"github.com/miekg/dns"
)

var dnsBinding = flag.String("dnsbind", "",
	"address to bind dns server to")
var dnsZone = flag.String("dnszone", "cbfs.",
	"DNS Zone for msgs/responses")

const cbfsSvc = "_cbfs._tcp"

const maxDnsResponses = 8

type dnsService struct{}

func (d dnsService) serviceDomain() string {
	return cbfsSvc + "." + *dnsZone
}

func (d dnsService) writeLogErr(w dns.ResponseWriter, msg *dns.Msg) {
	err := w.WriteMsg(msg)
	if err != nil {
		log.Printf("Error writing response: %v\n%v", err, msg)
	}
}

func (d dnsService) srvList(w dns.ResponseWriter, r *dns.Msg) {
	msg := &dns.Msg{}

	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Error finding nodes: %v", err)
		return
	}

	for i, n := range nl {
		_, p, err := net.SplitHostPort(n.Address())
		if err != nil {
			continue
		}
		port := 8484
		tmp, err := strconv.Atoi(p)
		if err == nil {
			port = tmp
		}

		rr := &dns.RR_SRV{
			Hdr: dns.RR_Header{
				Name:   d.serviceDomain(),
				Rrtype: dns.TypeSRV,
				Class:  dns.ClassINET,
				Ttl:    5},
			Priority: uint16(i),
			Weight:   uint16(time.Since(n.Time).Seconds()),
			Port:     uint16(port),
			Target:   n.name + "." + *dnsZone,
		}
		msg.Answer = append(msg.Answer, rr)

		arr := &dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   n.name + "." + *dnsZone,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    60,
			},
			A: net.ParseIP(n.Addr),
		}

		msg.Extra = append(msg.Extra, arr)

		if len(msg.Answer) > maxDnsResponses {
			break
		}
	}

	msg.SetReply(r)
	d.writeLogErr(w, msg)
}

func (d dnsService) hostLookup(w dns.ResponseWriter, r *dns.Msg) {
	msg := &dns.Msg{}

	name := r.Question[0].Name
	name = name[:len(name)-len(*dnsZone)-1]

	node, err := findNode(name)
	if err == nil {
		msg.Answer = []dns.RR{&dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   r.Question[0].Name,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    60,
			},
			A: net.ParseIP(node.Addr),
		}}
		msg.SetReply(r)
	} else {
		msg.SetRcode(r, dns.RcodeNameError)
	}

	d.writeLogErr(w, msg)
}

func (d dnsService) listHosts(w dns.ResponseWriter, r *dns.Msg) {
	msg := &dns.Msg{}

	nl, err := findAllNodes()
	if err != nil {
		log.Printf("Error finding nodes: %v", err)
		return
	}

	for _, n := range nl {
		if time.Since(n.Time) > (3 * globalConfig.HeartbeatFreq) {
			continue
		}
		rr := &dns.RR_A{
			Hdr: dns.RR_Header{
				Name:   *dnsZone,
				Rrtype: dns.TypeA,
				Class:  dns.ClassINET,
				Ttl:    5},
			A: net.ParseIP(n.Addr),
		}

		msg.Answer = append(msg.Answer, rr)
		if len(msg.Answer) > maxDnsResponses {
			break
		}
	}

	msg.SetReply(r)
	d.writeLogErr(w, msg)
}

func (d dnsService) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
	if *verbose {
		log.Printf("Incoming DNS Query: %v", r)
	}
	q := dns.Question{}

	if len(r.Question) == 1 &&
		strings.HasSuffix(r.Question[0].Name, *dnsZone) {
		q = r.Question[0]
	}

	switch q.Qtype {
	case dns.TypeSRV:
		d.srvList(w, r)
	case dns.TypeA, dns.TypeANY:
		if q.Name == *dnsZone {
			d.listHosts(w, r)
		} else {
			d.hostLookup(w, r)
		}
	case dns.TypeAAAA:
		msg := &dns.Msg{}
		msg.SetRcode(r, dns.RcodeSuccess)
		d.writeLogErr(w, msg)
	default:
		msg := &dns.Msg{}
		msg.SetRcode(r, dns.RcodeNotImplemented)
		d.writeLogErr(w, msg)
	}
}

func dnsServices() {
	if *dnsBinding == "" {
		return
	}

	log.Printf("Starting DNS services on %v.", *dnsBinding)

	d := dnsService{}

	serv := dns.Server{
		Net:     "udp",
		Addr:    *dnsBinding,
		Handler: d,
	}

	err := serv.ListenAndServe()
	if err != nil {
		log.Fatalf("DNS server failure: %v", err)
	}
}
<|endoftext|>


<commit_before>package godns

import (
	"fmt"
	"log"
	"net"
	"strconv"
	"time"

	"github.com/miekg/dns"
)

// DefaultNameServer - Google
const DefaultNameServer = "8.8.8.8:53"

// Pool - for caching DNS records and easy retrieval
type Pool struct {
	NameServer                      string
	Randomize                       bool
	records                         TCPMap
	Timeout, StepTimeout, RetryWait time.Duration
}

// New - returns new dns records pool
func New() *Pool {
	return &Pool{
		NameServer:  DefaultNameServer,
		Randomize:   true,
		Timeout:     5 * time.Second,
		StepTimeout: 2 * time.Second,
		RetryWait:   2 * time.Second,
		records:     make(TCPMap, 5),
	}
}

// Get - retuns first or random IP assigned to hostport
func (p *Pool) Get(hostport string) (*net.TCPAddr, error) {
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		return nil, err
	}
	if p.records.Exist(hostport) {
		return p.records.Get(hostport, p.Randomize)
	}
	ips, duration, err := p.ResolveName(host, p.NameServer)
	if err != nil {
		return nil, err
	}
	log.Printf("%s took %f, to resolve %s", hostport, duration.Seconds(), ips)
	if len(ips) == 0 {
		return nil, ErrEmptyIPS
	}
	portNum, _ := strconv.Atoi(port)
	p.records.BulkAdd(hostport, ips, portNum)
	return p.records.Get(hostport, p.Randomize)
}

// ResolveName - resolves name for given host and returns array of IP's
func (p *Pool) ResolveName(name, nameserver string) (addrs []net.IP, dur time.Duration, err error) {
	dnsClient := &dns.Client{
		Net:          "tcp",
		ReadTimeout:  p.StepTimeout,
		WriteTimeout: p.StepTimeout,
	}
	dnsMessage := new(dns.Msg)
	dnsMessage.MsgHdr.RecursionDesired = true
	dnsMessage.SetQuestion(dns.Fqdn(name), dns.TypeA)
	addrs = make([]net.IP, 0, 5)
	retryWait := p.RetryWait

Redo:
	var reply *dns.Msg
	var rtt time.Duration
	reply, rtt, err = dnsClient.Exchange(dnsMessage, nameserver)
	dur += rtt
	if err != nil {
		if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
			if dur+retryWait < p.Timeout {
				time.Sleep(retryWait)
				retryWait *= 2
				goto Redo
			}
		}
		return nil, dur, err
	}
	if reply.Rcode != dns.RcodeSuccess {
		err = fmt.Errorf(`ResolveName(%s, %s): %s`, name, nameserver, dns.RcodeToString[reply.Rcode])
		return nil, dur, err
	}
	for _, a := range reply.Answer {
		if rra, ok := a.(*dns.A); ok {
			addrs = append(addrs, rra.A)
		}
		if rra6, ok := a.(*dns.AAAA); ok {
			addrs = append(addrs, rra6.AAAA)
		}
	}
	if reply.MsgHdr.Truncated {
		goto Redo
	}
	return
}
<commit_msg>Use glog instead of log<commit_after>package godns

import (
	"fmt"
	"net"
	"strconv"
	"time"

	"github.com/golang/glog"
	"github.com/miekg/dns"
)

// DefaultNameServer - Google
const DefaultNameServer = "8.8.8.8:53"

// Pool - for caching DNS records and easy retrieval
type Pool struct {
	NameServer                      string
	Randomize                       bool
	records                         TCPMap
	Timeout, StepTimeout, RetryWait time.Duration
}

// New - returns new dns records pool
func New() *Pool {
	return &Pool{
		NameServer:  DefaultNameServer,
		Randomize:   true,
		Timeout:     5 * time.Second,
		StepTimeout: 2 * time.Second,
		RetryWait:   2 * time.Second,
		records:     make(TCPMap, 5),
	}
}

// Get - retuns first or random IP assigned to hostport
func (p *Pool) Get(hostport string) (*net.TCPAddr, error) {
	host, port, err := net.SplitHostPort(hostport)
	if err != nil {
		return nil, err
	}
	if p.records.Exist(hostport) {
		return p.records.Get(hostport, p.Randomize)
	}
	ips, duration, err := p.ResolveName(host, p.NameServer)
	if err != nil {
		return nil, err
	}
	glog.Warningf("%s took %f, to resolve %s", hostport, duration.Seconds(), ips)
	if len(ips) == 0 {
		return nil, ErrEmptyIPS
	}
	portNum, _ := strconv.Atoi(port)
	p.records.BulkAdd(hostport, ips, portNum)
	return p.records.Get(hostport, p.Randomize)
}

// ResolveName - resolves name for given host and returns array of IP's
func (p *Pool) ResolveName(name, nameserver string) (addrs []net.IP, dur time.Duration, err error) {
	dnsClient := &dns.Client{
		Net:          "tcp",
		ReadTimeout:  p.StepTimeout,
		WriteTimeout: p.StepTimeout,
	}
	dnsMessage := new(dns.Msg)
	dnsMessage.MsgHdr.RecursionDesired = true
	dnsMessage.SetQuestion(dns.Fqdn(name), dns.TypeA)
	addrs = make([]net.IP, 0, 5)
	retryWait := p.RetryWait

Redo:
	var reply *dns.Msg
	var rtt time.Duration
	reply, rtt, err = dnsClient.Exchange(dnsMessage, nameserver)
	dur += rtt
	if err != nil {
		if netErr, ok := err.(net.Error); ok && netErr.Temporary() {
			if dur+retryWait < p.Timeout {
				time.Sleep(retryWait)
				retryWait *= 2
				goto Redo
			}
		}
		return nil, dur, err
	}
	if reply.Rcode != dns.RcodeSuccess {
		err = fmt.Errorf(`ResolveName(%s, %s): %s`, name, nameserver, dns.RcodeToString[reply.Rcode])
		return nil, dur, err
	}
	for _, a := range reply.Answer {
		if rra, ok := a.(*dns.A); ok {
			addrs = append(addrs, rra.A)
		}
		if rra6, ok := a.(*dns.AAAA); ok {
			addrs = append(addrs, rra6.AAAA)
		}
	}
	if reply.MsgHdr.Truncated {
		goto Redo
	}
	return
}
<|endoftext|>


<commit_before>// Package static generates websites from any Go web app that uses `net/http`.
package static
<commit_msg>Add package import comment<commit_after>// Package static generates websites from any Go web app that uses `net/http`.
package static // import "4d63.com/static"
<|endoftext|>


<commit_before>/*
Package core provides a pure handlers (or middlewares) stack so you can perform actions downstream, then filter and manipulate the response upstream.

The handlers stack

A handler is a function that receives a context.
It can be registered with Use and has the possibility to break the sream or to continue with the next handler in the stack.

Example of a logger followed by the response writing:

	// Log
	core.Use(func(c *core.Context) {
		start := time.Now()
		c.Next()
		log.Printf(" %s  %s  %s", c.Request.Method, c.Request.URL, time.Since(start))
	})

	// Response
	core.Use(func(c *core.Context) {
		fmt.Fprint(c.ResponseWriter, "Hello, World!")
	})

	// Run server
	core.Run()

When using Run, your app is reachable at http://localhost:8080 by default.

If you need more flexibility, you can make a new handlers stack, which is fully compatible with the net/http.Handler interface:

	hs := core.NewHandlersStack()

	hs.Use(func(c *core.Context) {
		fmt.Fprint(c.ResponseWriter, "Hello, World!")
	})

	http.ListenAndServe(":8080", hs)

Flags

These flags are preset:

	-address	The address to listen and serving on.
				Value is saved in Address.
	-production	Run the server in production environment.
				Some third-party handlers may have different behaviors depending on the environment.
				Value is saved in Production.

It's up to you to call

	flag.Parse()

in your main function if you want to use them.

Panic recovering

When using Run, your server always recovers from panics, logs the error with stack, and sends a 500 Internal Server Error.
If you want to use a custom handler on panic, give one to HandlePanic.

Handlers & Helpers

No handlers or helpers are bundled in the core: it does one thing and does it well.
That's why you need to import all and only the handlers or helpers you need:

	compress	Clever response compressing
				https://godoc.org/github.com/volatile/compress
	cors		Cross-Origin Resource Sharing support
				https://godoc.org/github.com/volatile/cors
	i18n		Simple internationalization
				https://godoc.org/github.com/volatile/i18n
	log			Requests logging
				https://godoc.org/github.com/volatile/log
	response	Readable response helper
				https://godoc.org/github.com/volatile/response
	route		Flexible routing helper
				https://godoc.org/github.com/volatile/route
	secure 		Quick security wins
				https://godoc.org/github.com/volatile/secure
	static		Simple assets serving
				https://godoc.org/github.com/volatile/static
*/
package core
<commit_msg>Fix doc<commit_after>/*
Package core provides a pure handlers (or middlewares) stack so you can perform actions downstream, then filter and manipulate the response upstream.

The handlers stack

A handler is a function that receives a context.
It can be registered with Use and has the possibility to break the sream or to continue with the next handler in the stack.

Example of a logger followed by the response writing:

	// Log
	core.Use(func(c *core.Context) {
		start := time.Now()
		c.Next()
		log.Printf(" %s  %s  %s", c.Request.Method, c.Request.URL, time.Since(start))
	})

	// Response
	core.Use(func(c *core.Context) {
		fmt.Fprint(c.ResponseWriter, "Hello, World!")
	})

	// Run server
	core.Run()

When using Run, your app is reachable at http://localhost:8080 by default.

If you need more flexibility, you can make a new handlers stack, which is fully compatible with the net/http.Handler interface:

	hs := core.NewHandlersStack()

	hs.Use(func(c *core.Context) {
		fmt.Fprint(c.ResponseWriter, "Hello, World!")
	})

	http.ListenAndServe(":8080", hs)

Flags

These flags are preset:

	-address	The address to listen and serving on.
				Value is saved in Address.
	-production	Run the server in production environment.
				Some third-party handlers may have different behaviors depending on the environment.
				Value is saved in Production.

It's up to you to call
	flag.Parse()
in your main function if you want to use them.

Panic recovering

When using Run, your server always recovers from panics, logs the error with stack, and sends a 500 Internal Server Error.
If you want to use a custom handler on panic, give one to HandlePanic.

Handlers & Helpers

No handlers or helpers are bundled in the core: it does one thing and does it well.
That's why you need to import all and only the handlers or helpers you need:

	compress	Clever response compressing
	        	https://godoc.org/github.com/volatile/compress
	cors		Cross-Origin Resource Sharing support
	    		https://godoc.org/github.com/volatile/cors
	i18n		Simple internationalization
	    		https://godoc.org/github.com/volatile/i18n
	log			Requests logging
	   			https://godoc.org/github.com/volatile/log
	response	Readable response helper
	        	https://godoc.org/github.com/volatile/response
	route		Flexible routing helper
	     		https://godoc.org/github.com/volatile/route
	secure 		Quick security wins
	      		https://godoc.org/github.com/volatile/secure
	static		Simple assets serving
	      		https://godoc.org/github.com/volatile/static
*/
package core
<|endoftext|>


<commit_before>// gddoexp indicate if a package from GoDoc should be archived
package gddoexp
<commit_msg>Fix package description<commit_after>// Package gddoexp indicate if a package from GoDoc should be archived
package gddoexp
<|endoftext|>


<commit_before>/*
Package pi is a powerful toolkit to fasten the writing of web services in Go.

It has been written in top of the router from the pat toolkit (http://gorillatoolkit.org/pkg/pat).

Simple example:

		import "github.com/gocarina/pi"

		func main() {
			p := pi.New()
			p.Router("/",

				p.Route("/products",

						// "/products/{id}"
						p.Route("/{id}").
						Delete(DeleteProductHandler).
						Put(EditProductHandler).
						BeforeFunc(AuthorizeUser)).

				// "/products"
				Get(GetProductsHandler).
				Post(AddProductHandler))

				p.ListenAndServe(":8080")
		}

This example registers two different routes :
- "/products" with two methods: GetProductsHandler (GET) and AddProductHandler (POST)
- "/products/{id}" with also two methods: DeleteProductHandler (GET) and EditProductHandler (PUT)

There is also an interceptor Before in the route "/products/{id}", that means that the function AuthorizeUser is called before calling either
DeleteProductHandler or EditProductHandler.

If there was interceptors on the route "/products", it would also apply for the child routes.
There is 3 kind of interceptor:

- Before: called before the request is handled by the handler. If there is an error in a Before interceptor, the request flow is stopped and
 Error interceptors are called.
- After: called after the request has been handled by the handled. Errors are ignored (print to the error output).
- Error: called when an error occurs in any Before interceptor or in the request handler. Errors are completely ignored.

Interceptors and Handler are both HandlerFunction.
Here is a typical example of HandlerFunction:

		func SomeHandlerOrInterceptor(c *pi.RequestContext) (err error) {
			// Do something
			if err != nil {
				return pi.NewError(http.StatusInternalServerError, err) // Output: { "errorCode": 500, "errorMessage": "Message of the error" }
			}
			return c.WriteJSON(pi.J{"status": "OK"}) // Output: { "status": "OK" }
		}

The RequestContext has some useful functions each one of them are documented here.

If you have any problems or questions, create an issue to our github page project: https://github.com/gocarina/pi

*/
package pi
<commit_msg>Updated doc<commit_after>/*
Package pi is a powerful toolkit to fasten the writing of web services in Go.

It has been written in top of the router from the pat toolkit (http://gorillatoolkit.org/pkg/pat).

Simple example:

		import "github.com/gocarina/pi"

		func main() {
			p := pi.New()
			p.Router("/",

				p.Route("/products",

						// "/products/{id}"
						p.Route("/{id}").
						Delete(DeleteProductHandler).
						Put(EditProductHandler).
						BeforeFunc(AuthorizeUser)).

				// "/products"
				Get(GetProductsHandler).
				Post(AddProductHandler))

				p.ListenAndServe(":8080")
		}

This example registers two different routes :
- "/products" with two methods: GetProductsHandler (GET) and AddProductHandler (POST)
- "/products/{id}" with also two methods: DeleteProductHandler (GET) and EditProductHandler (PUT)

There is also an interceptor Before in the route "/products/{id}", that means that the function AuthorizeUser is called before calling either
DeleteProductHandler or EditProductHandler.

If there was interceptors on the route "/products", it would also apply for the child routes.
There is 3 kind of interceptor:

- Before: called before the request is handled by the handler. If there is an error in a Before interceptor, the request flow is stopped and
Error interceptors are called.
- After: called after the request has been handled by the handled. Errors are ignored (print to the error output).
- Error: called when an error occurs in any Before interceptor or in the request handler. Errors are completely ignored.

Interceptors and Handlers are both HandlerFunction.
Here is a typical example of HandlerFunction:

		func SomeHandlerOrInterceptor(c *pi.RequestContext) (err error) {
			// Do something
			if err != nil {
				return pi.NewError(http.StatusInternalServerError, err) // Output: { "errorCode": 500, "errorMessage": "Message of the error" }
			}
			return c.WriteJSON(pi.J{"status": "OK"}) // Output: { "status": "OK" }
		}

The RequestContext has some useful methods, please, see below for more information.

If you have any problems or questions, create an issue to our github page project: https://github.com/gocarina/pi

*/
package pi
<|endoftext|>


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

// 一个简单小巧的orm实现方案。
//
// 目前内置了对以下数据库的支持：
//  1. sqlite3:  github.com/mattn/go-sqlite3
//  2. mysql:    github.com/go-sql-driver/mysql
//  3. postgres: github.com/lib/pq
// 其它数据库，用户可以通过实现Dialect接口，来实现相应的支持。
//
//
//
// 初始化：
//
// 默认情况下，orm包并不会加载任何数据库的实例。所以想要用哪个数据库，需要手动初始化：
//  import (
//      _ github.com/mattn/go-sqlite3    // 加载数据库驱动
//      _ github.com/issue9/orm/dialect  // sqlite3的dialect声明在此处
//  )
//
//  // 初始化一个DB，表前缀为prefix_
//  db1 := orm.NewDB("sqlite3", "./db1", "prefix_", dialect.Sqlite3())
//
//  // 另一个DB实例
//  db2 := orm.NewDB("sqlite3", "./db2", "db2_", dialect.Sqlite3())
//
//
//
// 占位符
//
//
// SQL语句可以使用'#'字符在语句中暂替真实的表名前缀，也可以使用{}
// 包含一个关键字，使其它成为普通列名，如：
//  select * from #user where {group}=1
// 在实际执行时，如DB.Query()，将第一个参数replace指定为true，
// 相关的占位符就会被替换成与当前环境想容的实例，如在表名前缀为p_，
// 数据库为mysql时，会被替换成以下语句，然后再执行：
//  select * from p_user where `group`=1
// DB.Query(),DB.Exec(),DB.Prepare().DB.Where()及Tx与之对应的函数都可以使用占位符。
//
// Model不能指定占位符，它们默认总会使用占位符，且无法取消。
//
//
//
// Model:
//
// orm包通过struct tag来描述model在数据库中的结构。大概格式如下：
//  type User struct {
//      Id          int64      `orm:"name(id);ai;"`
//      FirstName   string     `orm:"name(first_name);index(index_name)"`
//      LastName    string     `orm:"name(first_name);index(index_name)"`
//
//      // 此处group会自动加上引号，无须担心是否为关键字
//      Group       string	   `orm:"name(group)"`
//  }
//
//  // 通过orm.Metaer接口，指定表的额外数据。若不需要，可不用实现该接口
//  // 表名user会被自动加上表名前缀。
//  func(u *User) Meta() string {
//      return "name(user);engine(innodb);charset(utf-8)"
//  }
//
// 目前支持以下的struct tag：
//
//  name(fieldName): 将当前的字段映射到数据表中的fieldName字段。
//
//  len(l1, l2): 指定字段的长度。比如mysql中的int(5),varchar(255),double(1,2),
//  不支持该特性的数据，将会忽略该标签的内容，比如sqlite3。
//  NOTE:字符串类型必须指定长度，若长度过大或是将长度设置了-1，
//  想使用类似于TEXT等不定长的形式表达。
//
//  nullable(true|false): 相当于定义表结构时的NULL，建议尽量少用该属性，
//  若非用不可的话，与之对应的Go属性必须声明为NullString之类的结构。
//
//  pk: 主键，支持联合主键，给多个字段加上pk的struct tag即可。
//
//  ai: 自增，若指定了自增列，则将自动取消其它的pk设置。无法指定起始值和步长。
//  可手动设置一个非零值来更改某条数据的AI行为。
//
//  unique(index_name): 唯一索引，支持联合索引，index_name为约束名，
//  会将index_name为一样的字段定义为一个联合索引。
//
//  index(index_name): 普通的关键字索引，同unique一样会将名称相同的索引定义为一个联合索引。
//
//  default(value): 指定默认值。相当于定义表结构时的DEFAULT。
//  当一个字段如果是个零值(reflect.Zero())时，将会使用它的默认值，
//  但是系统无法判断该零值是人为指定，还是未指定被默认初始化零值的，
//  所以在需要用到零值的字段，最好不要用default的struct tag。
//
//  fk(fk_name,refTable,refColName,updateRule,deleteRule):
//  定义物理外键，最少需要指定fk_name,refTabl,refColName三个值。分别对应约束名，
//  引用的表和引用的字段，updateRule,deleteRule，在不指定的情况下，使用数据库的默认值。
//
//  check(chk_name, expr): check约束。chk_name为约束名，expr为该约束的表达式。
//  check约束只能在core.Metaer接口中指定，而不是像其它约束一样，通过字段的struct tag指定。
//  因为check约束的表达式可以通过and或是or等符号连接多条基本表达式，
//  在字段struct tag中指定会显得有点怪异。
//
// 关于core.Metaer接口。
//
// 在go不能将struct tag作用于结构体，所以为了指定一些表级别的属性，
// 只能通过接口的形式，在接口方法中返回一段类似于struct tag的字符串，
// 以达到相同的目的。
//
// 在core.Metaer中除了可以指定name(table_name)和check(name,expr)两个属性之外，
// 还可指定一些自定义的属性，这些属性都将会被保存到Model.Meta中。
//
//
// 约束名：
//
// index,unique,check,fk都是可以指定约束名的，在表中，约束名必须是唯一的，
// 即便是不同类型的约束，比如已经有一个unique的约束名叫作name，
// 那么其它类型的约束，就不能再取这个名称了。
//
//
//
// 如何使用：
//
// Create:
// 可以通过Engine.Create()或是Tx.Create()创建一张表。
//  // 创建表
//  e.Create(&User{})
//  // 创建多个表
//  e.Create(&User{},&Email{})
//
// Update:
//  // 将id为1的记录的FirstName更改为abc；对象中的零值不会被提交。
//  e.Update(&User{Id:1,FirstName:"abc"})
//  e.Where("id=?",1).Table("#table").Set("FirstName", "abc").Update(nil)
//
// Delete:
//  // 删除id为1的记录
//  e.Delete(&User{Id:1})
//  e.Where("id=?",1).Table("#table").Delete(nil)
//
// Insert:
//  // 插入一条数据
//  e.Insert(&User{Id:1,FirstName:"abc"})
//  // 一次性插入多条数据
//  e.Insert(&User{Id:1,FirstName:"abc"},&User{Id:1,FirstName:"abc"})
//
// Select:
//  // 导出id=1的数据
//  m, err := e.Where("id=1").Table("#table").FetchMap(nil)
//  // 导出id为1的数据，并回填到user实例中
//  user := &User{Id:1}
//  err := e.Find(u)
//
//  Query/Exec:
//  // Query返回参数与sql.Query是相同的
//  sql := "select * from #tbl_name where id=?"
//  rows, err := e.Query(true, sql, []interface{}{5})
//  // Exec返回参数与sql.Exec是相同的
//  sql = "update #tbl_name set name=? where id=?"
//  r, err := e.Exec(true, sql, []interface{}{"name1", 5})
//
// 事务：
//
// 默认的DB是不支持事务的，若需要事务支持，则需要调用DB.Begin()
// 返回事务对象Tx，当然并不是所有的数据库都支持事务操作的。
// Tx拥有与DB相似的接口。
package orm

// 数据表的更改，涉及到很多方面：
//  1.字段名更改；
//  2.字段类型更改，且不兼容旧类型；
//  3.名称加了特殊修饰符：比如sqlite中的[group]与`group`为同一字段名，但表示形式不同；
// 所有这一切都让UpgradeTable()这一功能的实现变得很糟糕，
// 在没有比较完美的方法之前，不准备实现这个功能。

// 版本号
const Version = "0.14.35.150807"
<commit_msg>删除无用的包版本号<commit_after>// Copyright 2014 by caixw, All rights reserved.
// Use of this source code is governed by a MIT
// license that can be found in the LICENSE file.

// 一个简单小巧的orm实现方案。
//
// 目前内置了对以下数据库的支持：
//  1. sqlite3:  github.com/mattn/go-sqlite3
//  2. mysql:    github.com/go-sql-driver/mysql
//  3. postgres: github.com/lib/pq
// 其它数据库，用户可以通过实现Dialect接口，来实现相应的支持。
//
//
//
// 初始化：
//
// 默认情况下，orm包并不会加载任何数据库的实例。所以想要用哪个数据库，需要手动初始化：
//  import (
//      _ github.com/mattn/go-sqlite3    // 加载数据库驱动
//      _ github.com/issue9/orm/dialect  // sqlite3的dialect声明在此处
//  )
//
//  // 初始化一个DB，表前缀为prefix_
//  db1 := orm.NewDB("sqlite3", "./db1", "prefix_", dialect.Sqlite3())
//
//  // 另一个DB实例
//  db2 := orm.NewDB("sqlite3", "./db2", "db2_", dialect.Sqlite3())
//
//
//
// 占位符
//
//
// SQL语句可以使用'#'字符在语句中暂替真实的表名前缀，也可以使用{}
// 包含一个关键字，使其它成为普通列名，如：
//  select * from #user where {group}=1
// 在实际执行时，如DB.Query()，将第一个参数replace指定为true，
// 相关的占位符就会被替换成与当前环境想容的实例，如在表名前缀为p_，
// 数据库为mysql时，会被替换成以下语句，然后再执行：
//  select * from p_user where `group`=1
// DB.Query(),DB.Exec(),DB.Prepare().DB.Where()及Tx与之对应的函数都可以使用占位符。
//
// Model不能指定占位符，它们默认总会使用占位符，且无法取消。
//
//
//
// Model:
//
// orm包通过struct tag来描述model在数据库中的结构。大概格式如下：
//  type User struct {
//      Id          int64      `orm:"name(id);ai;"`
//      FirstName   string     `orm:"name(first_name);index(index_name)"`
//      LastName    string     `orm:"name(first_name);index(index_name)"`
//
//      // 此处group会自动加上引号，无须担心是否为关键字
//      Group       string	   `orm:"name(group)"`
//  }
//
//  // 通过orm.Metaer接口，指定表的额外数据。若不需要，可不用实现该接口
//  // 表名user会被自动加上表名前缀。
//  func(u *User) Meta() string {
//      return "name(user);engine(innodb);charset(utf-8)"
//  }
//
// 目前支持以下的struct tag：
//
//  name(fieldName): 将当前的字段映射到数据表中的fieldName字段。
//
//  len(l1, l2): 指定字段的长度。比如mysql中的int(5),varchar(255),double(1,2),
//  不支持该特性的数据，将会忽略该标签的内容，比如sqlite3。
//  NOTE:字符串类型必须指定长度，若长度过大或是将长度设置了-1，
//  想使用类似于TEXT等不定长的形式表达。
//
//  nullable(true|false): 相当于定义表结构时的NULL，建议尽量少用该属性，
//  若非用不可的话，与之对应的Go属性必须声明为NullString之类的结构。
//
//  pk: 主键，支持联合主键，给多个字段加上pk的struct tag即可。
//
//  ai: 自增，若指定了自增列，则将自动取消其它的pk设置。无法指定起始值和步长。
//  可手动设置一个非零值来更改某条数据的AI行为。
//
//  unique(index_name): 唯一索引，支持联合索引，index_name为约束名，
//  会将index_name为一样的字段定义为一个联合索引。
//
//  index(index_name): 普通的关键字索引，同unique一样会将名称相同的索引定义为一个联合索引。
//
//  default(value): 指定默认值。相当于定义表结构时的DEFAULT。
//  当一个字段如果是个零值(reflect.Zero())时，将会使用它的默认值，
//  但是系统无法判断该零值是人为指定，还是未指定被默认初始化零值的，
//  所以在需要用到零值的字段，最好不要用default的struct tag。
//
//  fk(fk_name,refTable,refColName,updateRule,deleteRule):
//  定义物理外键，最少需要指定fk_name,refTabl,refColName三个值。分别对应约束名，
//  引用的表和引用的字段，updateRule,deleteRule，在不指定的情况下，使用数据库的默认值。
//
//  check(chk_name, expr): check约束。chk_name为约束名，expr为该约束的表达式。
//  check约束只能在core.Metaer接口中指定，而不是像其它约束一样，通过字段的struct tag指定。
//  因为check约束的表达式可以通过and或是or等符号连接多条基本表达式，
//  在字段struct tag中指定会显得有点怪异。
//
// 关于core.Metaer接口。
//
// 在go不能将struct tag作用于结构体，所以为了指定一些表级别的属性，
// 只能通过接口的形式，在接口方法中返回一段类似于struct tag的字符串，
// 以达到相同的目的。
//
// 在core.Metaer中除了可以指定name(table_name)和check(name,expr)两个属性之外，
// 还可指定一些自定义的属性，这些属性都将会被保存到Model.Meta中。
//
//
// 约束名：
//
// index,unique,check,fk都是可以指定约束名的，在表中，约束名必须是唯一的，
// 即便是不同类型的约束，比如已经有一个unique的约束名叫作name，
// 那么其它类型的约束，就不能再取这个名称了。
//
//
//
// 如何使用：
//
// Create:
// 可以通过Engine.Create()或是Tx.Create()创建一张表。
//  // 创建表
//  e.Create(&User{})
//  // 创建多个表
//  e.Create(&User{},&Email{})
//
// Update:
//  // 将id为1的记录的FirstName更改为abc；对象中的零值不会被提交。
//  e.Update(&User{Id:1,FirstName:"abc"})
//  e.Where("id=?",1).Table("#table").Set("FirstName", "abc").Update(nil)
//
// Delete:
//  // 删除id为1的记录
//  e.Delete(&User{Id:1})
//  e.Where("id=?",1).Table("#table").Delete(nil)
//
// Insert:
//  // 插入一条数据
//  e.Insert(&User{Id:1,FirstName:"abc"})
//  // 一次性插入多条数据
//  e.Insert(&User{Id:1,FirstName:"abc"},&User{Id:1,FirstName:"abc"})
//
// Select:
//  // 导出id=1的数据
//  m, err := e.Where("id=1").Table("#table").FetchMap(nil)
//  // 导出id为1的数据，并回填到user实例中
//  user := &User{Id:1}
//  err := e.Find(u)
//
//  Query/Exec:
//  // Query返回参数与sql.Query是相同的
//  sql := "select * from #tbl_name where id=?"
//  rows, err := e.Query(true, sql, []interface{}{5})
//  // Exec返回参数与sql.Exec是相同的
//  sql = "update #tbl_name set name=? where id=?"
//  r, err := e.Exec(true, sql, []interface{}{"name1", 5})
//
// 事务：
//
// 默认的DB是不支持事务的，若需要事务支持，则需要调用DB.Begin()
// 返回事务对象Tx，当然并不是所有的数据库都支持事务操作的。
// Tx拥有与DB相似的接口。
package orm

// 数据表的更改，涉及到很多方面：
//  1.字段名更改；
//  2.字段类型更改，且不兼容旧类型；
//  3.名称加了特殊修饰符：比如sqlite中的[group]与`group`为同一字段名，但表示形式不同；
// 所有这一切都让UpgradeTable()这一功能的实现变得很糟糕，
// 在没有比较完美的方法之前，不准备实现这个功能。
<|endoftext|>


<commit_before>// Package enmime implements a MIME parsing library for Go.  It's built ontop of Go's included
// mime/multipart support, but is geared towards parsing MIME encoded emails.
//
// The Basics
//
// Calling EnvelopeFromMessage causes enmime to parse the body of the message object into a tree of
// Part objects, each of which is aware of its content type, filename and headers.  If the part was
// encoded in quoted-printable or base64, it is decoded before being stored in the Part object.
//
// EnvelopeFromMessage returns an Envelope struct.  The struct contains both the plain text and HTML
// portions of the email (if available).  The root of the tree, as well as slices of the email's
// inlines and attachments are also available.
//
// If you need to locate a particular Part, you can pass a custom PartMatcher function into
// BreadthMatchFirst() or DepthMatchFirst() to search the Part tree.  BreadthMatchAll() and
// DepthMatchAll() will collect all matching parts.
//
// Please note that enmime parses messages into memory, so it is not likely to perform well with
// multi-gigabyte attachments.
//
// enmime is open source software released under the MIT License.  The latest version can be found
// at https://github.com/jhillyerd/go.enmime
package enmime
<commit_msg>Package doc introduction rewritten<commit_after>// Package enmime implements a MIME parsing library for Go.  It's built on top of Go's included
// mime/multipart support, but is geared towards parsing MIME encoded emails.
//
// Overview
//
// The enmime API has two conceptual layers.  The lower layer is a tree of Part structs,
// representing each component of a decoded MIME message.  The upper layer, called an Envelope
// provides an intuitive way to interact with a MIME message.
//
// Part Tree
//
// Calling ReadParts causes enmime to parse the body of a MIME message into a tree of Part objects,
// each of which is aware of its content type, filename and headers.  Each Part implements
// io.Reader, providing access to the content it represents.  If the part was encoded in
// quoted-printable or base64, it is decoded prior to being accessed by the Reader.
//
// If you need to locate a particular Part, you can pass a custom PartMatcher function into the
// BreadthMatchFirst() or DepthMatchFirst() methods to search the Part tree.  BreadthMatchAll() and
// DepthMatchAll() will collect all Parts matching your criteria.
//
// The Envelope
//
// EnvelopeFromMessage returns an Envelope struct.  Behind the scenes a Part tree is constructed,
// and then sorted into the correct fields of the Envelope.
//
// The Envelope contains both the plain text and HTML portions of the email.  If there was no plain
// text Part available, the HTML Part will be downconverted using the html2text library[1].  The
// root of the Part tree, as well as slices of the inline and attachment Parts are also available.
//
// Please note that enmime parses messages into memory, so it is not likely to perform well with
// multi-gigabyte attachments.
//
// enmime is open source software released under the MIT License.  The latest version can be found
// at https://github.com/jhillyerd/go.enmime
//
// [1]: https://github.com/jaytaylor/html2text
package enmime
<|endoftext|>


<commit_before>/*
Package scl is an implementation of a parser for the Sepia Configuration
Language.

SCL is a simple, declarative, self-documenting, semi-functional language that
extends HCL (as in https://github.com/hashicorp/hcl) in the same way that Sass
extends CSS.  What that means is, any properly formatted HCL is valid SCL. If
you really enjoy HCL, you can keep using it exclusively: under the hood, SCL
‘compiles’ to HCL.  The difference is that now you can explicitly include
files, use ‘mixins’ to quickly inject boilerplate code, and use properly
scoped, natural variables.  The language is designed to accompany Sepia (and,
specifically, Sepia plugins) but it's a general purpose language, and can be
used for pretty much any configurational purpose.

Full documenation for SCL, including a language specification, tutorials and
examples, is available at http://sepia-docs.us-east-1.elasticbeanstalk.com/scl.
*/
package scl

/*
MixinDoc documents a mixin from a particular SCL file. Since mixins can be nested, it
also includes a tree of all child mixins.
*/
type MixinDoc struct {
	Name      string
	File      string
	Line      int
	Reference string
	Signature string
	Docs      string
	Children  MixinDocs
}

/*
MixinDocs is a slice of MixinDocs, for convenience.
*/
type MixinDocs []MixinDoc
<commit_msg>Wiki location<commit_after>/*
Package scl is an implementation of a parser for the Sepia Configuration
Language.

SCL is a simple, declarative, self-documenting, semi-functional language that
extends HCL (as in https://github.com/hashicorp/hcl) in the same way that Sass
extends CSS.  What that means is, any properly formatted HCL is valid SCL. If
you really enjoy HCL, you can keep using it exclusively: under the hood, SCL
‘compiles’ to HCL.  The difference is that now you can explicitly include
files, use ‘mixins’ to quickly inject boilerplate code, and use properly
scoped, natural variables.  The language is designed to accompany Sepia (and,
specifically, Sepia plugins) but it's a general purpose language, and can be
used for pretty much any configurational purpose.

Full documenation for the language itself, including a language specification,
tutorials and examples, is available at https://github.com/homemade/scl/wiki.
*/
package scl

/*
MixinDoc documents a mixin from a particular SCL file. Since mixins can be nested, it
also includes a tree of all child mixins.
*/
type MixinDoc struct {
	Name      string
	File      string
	Line      int
	Reference string
	Signature string
	Docs      string
	Children  MixinDocs
}

/*
MixinDocs is a slice of MixinDocs, for convenience.
*/
type MixinDocs []MixinDoc
<|endoftext|>