<commit_before>// Package log provides a global logger for zerolog.
package log

import (
	"context"
	"io"
	"os"

	"github.com/rs/zerolog"
)

// Logger is the global logger.
var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()

// Output duplicates the global logger and sets w as its output.
func Output(w io.Writer) zerolog.Logger {
	return Logger.Output(w)
}

// With creates a child logger with the field added to its context.
func With() zerolog.Context {
	return Logger.With()
}

// Level crestes a child logger with the minium accepted level set to level.
func Level(level zerolog.Level) zerolog.Logger {
	return Logger.Level(level)
}

// Sample returns a logger with the s sampler.
func Sample(s zerolog.Sampler) zerolog.Logger {
	return Logger.Sample(s)
}

// Hook returns a logger with the h Hook.
func Hook(h zerolog.Hook) zerolog.Logger {
	return Logger.Hook(h)
}

// Debug starts a new message with debug level.
//
// You must call Msg on the returned event in order to send the event.
func Debug() *zerolog.Event {
	return Logger.Debug()
}

// Info starts a new message with info level.
//
// You must call Msg on the returned event in order to send the event.
func Info() *zerolog.Event {
	return Logger.Info()
}

// Warn starts a new message with warn level.
//
// You must call Msg on the returned event in order to send the event.
func Warn() *zerolog.Event {
	return Logger.Warn()
}

// Error starts a new message with error level.
//
// You must call Msg on the returned event in order to send the event.
func Error() *zerolog.Event {
	return Logger.Error()
}

// Fatal starts a new message with fatal level. The os.Exit(1) function
// is called by the Msg method.
//
// You must call Msg on the returned event in order to send the event.
func Fatal() *zerolog.Event {
	return Logger.Fatal()
}

// Panic starts a new message with panic level. The message is also sent
// to the panic function.
//
// You must call Msg on the returned event in order to send the event.
func Panic() *zerolog.Event {
	return Logger.Panic()
}

// Log starts a new message with no level. Setting zerolog.GlobalLevel to
// zerlog.Disabled will still disable events produced by this method.
//
// You must call Msg on the returned event in order to send the event.
func Log() *zerolog.Event {
	return Logger.Log()
}

// Print sends a log event using debug level and no extra field.
// Arguments are handled in the manner of fmt.Print.
func Print(v ...interface{}) {
	Logger.Print(v...)
}

// Printf sends a log event using debug level and no extra field.
// Arguments are handled in the manner of fmt.Printf.
func Printf(format string, v ...interface{}) {
	Logger.Printf(format, v...)
}

// Ctx returns the Logger associated with the ctx. If no logger
// is associated, a disabled logger is returned.
func Ctx(ctx context.Context) *zerolog.Logger {
	return zerolog.Ctx(ctx)
}
<commit_msg>Add missing WithLevel method to log package (#27)<commit_after>// Package log provides a global logger for zerolog.
package log

import (
	"context"
	"io"
	"os"

	"github.com/rs/zerolog"
)

// Logger is the global logger.
var Logger = zerolog.New(os.Stderr).With().Timestamp().Logger()

// Output duplicates the global logger and sets w as its output.
func Output(w io.Writer) zerolog.Logger {
	return Logger.Output(w)
}

// With creates a child logger with the field added to its context.
func With() zerolog.Context {
	return Logger.With()
}

// Level crestes a child logger with the minium accepted level set to level.
func Level(level zerolog.Level) zerolog.Logger {
	return Logger.Level(level)
}

// Sample returns a logger with the s sampler.
func Sample(s zerolog.Sampler) zerolog.Logger {
	return Logger.Sample(s)
}

// Hook returns a logger with the h Hook.
func Hook(h zerolog.Hook) zerolog.Logger {
	return Logger.Hook(h)
}

// Debug starts a new message with debug level.
//
// You must call Msg on the returned event in order to send the event.
func Debug() *zerolog.Event {
	return Logger.Debug()
}

// Info starts a new message with info level.
//
// You must call Msg on the returned event in order to send the event.
func Info() *zerolog.Event {
	return Logger.Info()
}

// Warn starts a new message with warn level.
//
// You must call Msg on the returned event in order to send the event.
func Warn() *zerolog.Event {
	return Logger.Warn()
}

// Error starts a new message with error level.
//
// You must call Msg on the returned event in order to send the event.
func Error() *zerolog.Event {
	return Logger.Error()
}

// Fatal starts a new message with fatal level. The os.Exit(1) function
// is called by the Msg method.
//
// You must call Msg on the returned event in order to send the event.
func Fatal() *zerolog.Event {
	return Logger.Fatal()
}

// Panic starts a new message with panic level. The message is also sent
// to the panic function.
//
// You must call Msg on the returned event in order to send the event.
func Panic() *zerolog.Event {
	return Logger.Panic()
}

// WithLevel starts a new message with level.
//
// You must call Msg on the returned event in order to send the event.
func WithLevel(level zerolog.Level) *zerolog.Event {
	return Logger.WithLevel(level)
}

// Log starts a new message with no level. Setting zerolog.GlobalLevel to
// zerlog.Disabled will still disable events produced by this method.
//
// You must call Msg on the returned event in order to send the event.
func Log() *zerolog.Event {
	return Logger.Log()
}

// Print sends a log event using debug level and no extra field.
// Arguments are handled in the manner of fmt.Print.
func Print(v ...interface{}) {
	Logger.Print(v...)
}

// Printf sends a log event using debug level and no extra field.
// Arguments are handled in the manner of fmt.Printf.
func Printf(format string, v ...interface{}) {
	Logger.Printf(format, v...)
}

// Ctx returns the Logger associated with the ctx. If no logger
// is associated, a disabled logger is returned.
func Ctx(ctx context.Context) *zerolog.Logger {
	return zerolog.Ctx(ctx)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bitly/nsq/nsq"
	"github.com/bmizerany/logplex"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

var (
	port             = flag.String("port", ":1514", "log reciever port")
	topic            = flag.String("topic", "nginx_log", "nsq topic")
	lookupdHTTPAddrs = flag.String("lookupd-http-address", "127.0.0.1:4161", "lookupd http")
)

func main() {
	flag.Parse()
	// get nsqd list
	lookupdlist := strings.Split(*lookupdHTTPAddrs, ",")
	// signal
	termchan := make(chan os.Signal, 1)
	exitChan := make(chan int)
	signal.Notify(termchan, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-termchan
		exitChan <- 1
	}()
	var wg sync.WaitGroup
	logchan := make(chan []byte)
	// tcp server
	wg.Add(1)
	go func() {
		run_server(*port, logchan, exitChan)
		wg.Done()
	}()
	nsqd_ch := make(chan string)
	go lookupnsqd(lookupdlist, nsqd_ch)
	go push_data(nsqd_ch, *topic, logchan)
	wg.Wait()
	log.Println("Server is going down")
	time.Sleep(time.Second * 2)
}

// run_server, listen tcp
func run_server(port string, logchan chan []byte, exitchan chan int) {
	server, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatal("server bind failed:", err)
	}
	defer server.Close()
	client_count := 0
	client_exit := make(chan int)
	stat := 0
	go func() {
		for {
			fd, err := server.Accept()
			if stat > 0 {
				break
			}
			client_count++
			if err != nil {
				log.Println("accept error", err)
			}
			go loghandle(fd, logchan, client_exit)
		}
	}()
	<-exitchan
	stat = 1
	for i := 0; i < client_count; i++ {
		client_exit <- 1
	}
	log.Println("All connection closed")
}

// receive log from tcp socket, encode json and send to logchan
func loghandle(fd net.Conn, logchan chan []byte, exitchan chan int) {
	defer fd.Close()
	rbuf := bufio.NewReader(fd)
	reader := logplex.NewReader(rbuf)
	go func() {
		for {
			msg, err := reader.ReadMsg()
			if err == io.EOF {
				break
			}
			if err != nil {
				log.Println("read log failed", err)
				break
			}
			if msg_json, err := json.Marshal(msg); err == nil {
				logchan <- msg_json
			} else {
				log.Println("json:", err)
			}
		}
	}()
	<-exitchan
}

// lookup allo nsqd node, send nsqd node via nsqd_ch
func lookupnsqd(lookupdaddrs []string, nsqd_ch chan string) {
	ticker := time.NewTicker(60 * time.Second)
	for {
		for _, addr := range lookupdaddrs {
			endpoint := fmt.Sprintf("http://%s/nodes", addr)
			log.Printf("LOOKUPD: querying %s", endpoint)
			data, err := nsq.ApiRequest(endpoint)
			if err != nil {
				log.Printf("ERROR: lookupd %s - %s", endpoint, err.Error())
				continue
			}
			producers := data.Get("producers")
			producersArray, _ := producers.Array()
			for i, _ := range producersArray {
				producer := producers.GetIndex(i)
				address := producer.Get("address").MustString()
				tcpPort := producer.Get("tcp_port").MustInt()
				port := strconv.Itoa(tcpPort)
				nsqd_ch <- address + ":" + port
				log.Println(address, ":", port)
			}
		}
		<-ticker.C
	}
}

type NsqdServer struct {
	Conn     net.Conn
	NsqdAddr string
}

// connect nsqd, receive nsqd's info from nsqd_ch. create connection
func push_data(nsqd_ch chan string, topic string, logchan chan []byte) {
	nsqd_list := make(map[string]*NsqdServer)
	done := make(chan string)
	for {
		select {
		case nsqd := <-nsqd_ch:
			if _, ok := nsqd_list[nsqd]; ok {
				continue
			}
			conn, err := net.DialTimeout("tcp", nsqd, time.Second)
			if err != nil {
				log.Println("connect failed:", err)
				continue
			}
			n := &NsqdServer{
				Conn:     conn,
				NsqdAddr: nsqd,
			}
			nsqd_list[nsqd] = n
			go message_handler(n, topic, logchan, done)
		case nsq := <-done:
			delete(nsqd_list, nsq)
			log.Println("disconnect:", nsq)
			nsqd_ch <- nsq
		}
	}
}

// send msg to nsqd node
func message_handler(nsqdserver *NsqdServer, topic string, logchan chan []byte, done chan string) {
	defer nsqdserver.Conn.Close()
	nsqdserver.Conn.Write(nsq.MagicV2)
	rwbuf := bufio.NewReadWriter(bufio.NewReader(nsqdserver.Conn), bufio.NewWriter(nsqdserver.Conn))
	for {
		var batch [][]byte
		for i := 0; i < 2; i++ {
			line := <-logchan
			if len(line) > 0 {
				batch = append(batch, line)
			}
		}
		cmd, _ := nsq.MultiPublish(topic, batch)
		err := cmd.Write(rwbuf)
		if err != nil {
			log.Println("write buf error", err)
			done <- nsqdserver.NsqdAddr
			break
		}
		err = rwbuf.Flush()
		if err != nil {
			log.Println("flush buf error", err)
			done <- nsqdserver.NsqdAddr
			break
		}
		resp, err := nsq.ReadResponse(rwbuf)
		if err != nil {
			log.Println("failed to read response", err)
			done <- nsqdserver.NsqdAddr
			break
		}
		_, data, _ := nsq.UnpackResponse(resp)
		if !bytes.Equal(data, []byte("OK")) {
			log.Println("invalid response", err)
			done <- nsqdserver.NsqdAddr
			break
		}
	}
}
<commit_msg>if read message error, it will cause client hang<commit_after>package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bitly/nsq/nsq"
	"github.com/bmizerany/logplex"
	"io"
	"log"
	"net"
	"os"
	"os/signal"
	"strconv"
	"strings"
	"sync"
	"syscall"
	"time"
)

var (
	port             = flag.String("port", ":1514", "log reciever port")
	topic            = flag.String("topic", "nginx_log", "nsq topic")
	lookupdHTTPAddrs = flag.String("lookupd-http-address", "127.0.0.1:4161", "lookupd http")
)

func main() {
	flag.Parse()
	// get nsqd list
	lookupdlist := strings.Split(*lookupdHTTPAddrs, ",")
	// signal
	termchan := make(chan os.Signal, 1)
	exitChan := make(chan int)
	signal.Notify(termchan, syscall.SIGINT, syscall.SIGTERM)
	go func() {
		<-termchan
		exitChan <- 1
	}()
	var wg sync.WaitGroup
	logchan := make(chan []byte)
	// tcp server
	wg.Add(1)
	go func() {
		run_server(*port, logchan, exitChan)
		wg.Done()
	}()
	nsqd_ch := make(chan string)
	go lookupnsqd(lookupdlist, nsqd_ch)
	go push_data(nsqd_ch, *topic, logchan)
	wg.Wait()
	log.Println("Server is going down")
	time.Sleep(time.Second * 2)
}

// run_server, listen tcp
func run_server(port string, logchan chan []byte, exitchan chan int) {
	server, err := net.Listen("tcp", port)
	if err != nil {
		log.Fatal("server bind failed:", err)
	}
	defer server.Close()
	client_count := 0
	client_exit := make(chan int)
	stat := 0
	go func() {
		for {
			fd, err := server.Accept()
			if stat > 0 {
				break
			}
			client_count++
			if err != nil {
				log.Println("accept error", err)
			}
			go loghandle(fd, logchan, client_exit)
		}
	}()
	<-exitchan
	stat = 1
	for i := 0; i < client_count; i++ {
		client_exit <- 1
	}
	log.Println("All connection closed")
}

// receive log from tcp socket, encode json and send to logchan
func loghandle(fd net.Conn, logchan chan []byte, exitchan chan int) {
	defer fd.Close()
	rbuf := bufio.NewReader(fd)
	reader := logplex.NewReader(rbuf)
	for {
		select {
		case <-exitchan:
			break
		default:
			msg, err := reader.ReadMsg()
			if err == io.EOF {
				break
			}
			if err != nil {
				log.Println("read log failed", err)
				break
			}
			if msg_json, err := json.Marshal(msg); err == nil {
				logchan <- msg_json
			} else {
				log.Println("json:", err)
			}
		}
	}
}

// lookup allo nsqd node, send nsqd node via nsqd_ch
func lookupnsqd(lookupdaddrs []string, nsqd_ch chan string) {
	ticker := time.NewTicker(60 * time.Second)
	for {
		for _, addr := range lookupdaddrs {
			endpoint := fmt.Sprintf("http://%s/nodes", addr)
			log.Printf("LOOKUPD: querying %s", endpoint)
			data, err := nsq.ApiRequest(endpoint)
			if err != nil {
				log.Printf("ERROR: lookupd %s - %s", endpoint, err.Error())
				continue
			}
			producers := data.Get("producers")
			producersArray, _ := producers.Array()
			for i, _ := range producersArray {
				producer := producers.GetIndex(i)
				address := producer.Get("address").MustString()
				tcpPort := producer.Get("tcp_port").MustInt()
				port := strconv.Itoa(tcpPort)
				nsqd_ch <- address + ":" + port
				log.Println(address, ":", port)
			}
		}
		<-ticker.C
	}
}

type NsqdServer struct {
	Conn     net.Conn
	NsqdAddr string
}

// connect nsqd, receive nsqd's info from nsqd_ch. create connection
func push_data(nsqd_ch chan string, topic string, logchan chan []byte) {
	nsqd_list := make(map[string]*NsqdServer)
	done := make(chan string)
	for {
		select {
		case nsqd := <-nsqd_ch:
			if _, ok := nsqd_list[nsqd]; ok {
				continue
			}
			conn, err := net.DialTimeout("tcp", nsqd, time.Second)
			if err != nil {
				log.Println("connect failed:", err)
				continue
			}
			n := &NsqdServer{
				Conn:     conn,
				NsqdAddr: nsqd,
			}
			nsqd_list[nsqd] = n
			go message_handler(n, topic, logchan, done)
		case nsq := <-done:
			delete(nsqd_list, nsq)
			log.Println("disconnect:", nsq)
			nsqd_ch <- nsq
		}
	}
}

// send msg to nsqd node
func message_handler(nsqdserver *NsqdServer, topic string, logchan chan []byte, done chan string) {
	defer nsqdserver.Conn.Close()
	nsqdserver.Conn.Write(nsq.MagicV2)
	rwbuf := bufio.NewReadWriter(bufio.NewReader(nsqdserver.Conn), bufio.NewWriter(nsqdserver.Conn))
	for {
		var batch [][]byte
		for i := 0; i < 2; i++ {
			line := <-logchan
			if len(line) > 0 {
				batch = append(batch, line)
			}
		}
		cmd, _ := nsq.MultiPublish(topic, batch)
		err := cmd.Write(rwbuf)
		if err != nil {
			log.Println("write buf error", err)
			done <- nsqdserver.NsqdAddr
			break
		}
		err = rwbuf.Flush()
		if err != nil {
			log.Println("flush buf error", err)
			done <- nsqdserver.NsqdAddr
			break
		}
		resp, err := nsq.ReadResponse(rwbuf)
		if err != nil {
			log.Println("failed to read response", err)
			done <- nsqdserver.NsqdAddr
			break
		}
		_, data, _ := nsq.UnpackResponse(resp)
		if !bytes.Equal(data, []byte("OK")) {
			log.Println("invalid response", err)
			done <- nsqdserver.NsqdAddr
			break
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/Symantec/Dominator/lib/constants"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/flags/loadflags"
	"github.com/Symantec/Dominator/lib/flagutil"
	"github.com/Symantec/Dominator/lib/log"
	"github.com/Symantec/Dominator/lib/log/cmdlogger"
	"github.com/Symantec/Dominator/lib/mbr"
	objectclient "github.com/Symantec/Dominator/lib/objectserver/client"
	"github.com/Symantec/Dominator/lib/srpc"
	"github.com/Symantec/Dominator/lib/srpc/setupclient"
)

var (
	allocateBlocks = flag.Bool("allocateBlocks", false,
		"If true, allocate blocks when making raw image")
	buildLog = flag.String("buildLog", "",
		"Filename or URL containing build log")
	compress      = flag.Bool("compress", false, "If true, compress tar output")
	computedFiles = flag.String("computedFiles", "",
		"Name of file containing computed files list")
	computedFilesRoot = flag.String("computedFilesRoot", "",
		"Name of directory tree containing computed files to replace on unpack")
	copyMtimesFrom = flag.String("copyMtimesFrom", "",
		"Name of image to copy mtimes for otherwise unchanged files/devices")
	debug = flag.Bool("debug", false,
		"If true, show debugging output")
	deleteFilter = flag.String("deleteFilter", "",
		"Name of delete filter file for addi, adds and diff subcommands")
	expiresIn = flag.Duration("expiresIn", 0,
		"How long before the image expires (auto deletes). Default: never")
	filterFile = flag.String("filterFile", "",
		"Filter file to apply when adding images")
	ignoreExpiring = flag.Bool("ignoreExpiring", false,
		"If true, ignore expiring images when finding images")
	imageServerHostname = flag.String("imageServerHostname", "localhost",
		"Hostname of image server")
	imageServerPortNum = flag.Uint("imageServerPortNum",
		constants.ImageServerPortNumber,
		"Port number of image server")
	makeBootable = flag.Bool("makeBootable", true,
		"If true, make raw image bootable by installing GRUB")
	minFreeBytes = flag.Uint64("minFreeBytes", 4<<20,
		"minimum number of free bytes in raw image")
	releaseNotes = flag.String("releaseNotes", "",
		"Filename or URL containing release notes")
	requiredPaths = flagutil.StringToRuneMap(constants.RequiredPaths)
	roundupPower  = flag.Uint64("roundupPower", 24,
		"power of 2 to round up raw image size")
	skipFields = flag.String("skipFields", "",
		"Fields to skip when showing or diffing images")
	tableType mbr.TableType = mbr.TABLE_TYPE_MSDOS
	timeout                 = flag.Duration("timeout", 0,
		"Timeout for get subcommand")

	logger            log.DebugLogger
	minimumExpiration = 15 * time.Minute
)

func init() {
	flag.Var(&requiredPaths, "requiredPaths",
		"Comma separated list of required path:type entries")
	flag.Var(&tableType, "tableType", "partition table type for make-raw-image")
}

func printUsage() {
	fmt.Fprintln(os.Stderr,
		"Usage: imagetool [flags...] add|check|delete|list [args...]")
	fmt.Fprintln(os.Stderr, "Common flags:")
	flag.PrintDefaults()
	fmt.Fprintln(os.Stderr, "Commands:")
	fmt.Fprintln(os.Stderr, "  add    name imagefile filterfile triggerfile")
	fmt.Fprintln(os.Stderr, "  addi   name imagename filterfile triggerfile")
	fmt.Fprintln(os.Stderr, "  adds   name subname filterfile triggerfile")
	fmt.Fprintln(os.Stderr, "  addrep name baseimage layerimage...")
	fmt.Fprintln(os.Stderr, "  bulk-addrep layerimage...")
	fmt.Fprintln(os.Stderr, "  check  name")
	fmt.Fprintln(os.Stderr, "  check-directory dirname")
	fmt.Fprintln(os.Stderr, "  chown  dirname ownerGroup")
	fmt.Fprintln(os.Stderr, "  copy   name oldimagename")
	fmt.Fprintln(os.Stderr, "  delete name")
	fmt.Fprintln(os.Stderr, "  delunrefobj percentage bytes")
	fmt.Fprintln(os.Stderr, "  diff   tool left right")
	fmt.Fprintln(os.Stderr, "         left & right are image sources. Format:")
	fmt.Fprintln(os.Stderr, "         type:name where type is one of:")
	fmt.Fprintln(os.Stderr, "           d: name of directory tree to scan")
	fmt.Fprintln(os.Stderr, "           f: name of file containing a FileSystem")
	fmt.Fprintln(os.Stderr, "           i: name of an image on the imageserver")
	fmt.Fprintln(os.Stderr, "           l: name of file containing an Image")
	fmt.Fprintln(os.Stderr, "           s: name of sub to poll")
	fmt.Fprintln(os.Stderr, "  estimate-usage      name")
	fmt.Fprintln(os.Stderr, "  find-latest-image   directory")
	fmt.Fprintln(os.Stderr, "  get                 name directory")
	fmt.Fprintln(os.Stderr, "  list")
	fmt.Fprintln(os.Stderr, "  listdirs")
	fmt.Fprintln(os.Stderr, "  listunrefobj")
	fmt.Fprintln(os.Stderr, "  list-latest-image   directory")
	fmt.Fprintln(os.Stderr, "  make-raw-image      name rawfile")
	fmt.Fprintln(os.Stderr, "  match-triggers      name triggers-file")
	fmt.Fprintln(os.Stderr, "  merge-filters       filter-file...")
	fmt.Fprintln(os.Stderr, "  merge-triggers      triggers-file...")
	fmt.Fprintln(os.Stderr, "  mkdir               name")
	fmt.Fprintln(os.Stderr, "  show                name")
	fmt.Fprintln(os.Stderr, "  showunrefobj")
	fmt.Fprintln(os.Stderr, "  tar                 name [file]")
	fmt.Fprintln(os.Stderr, "  test-download-speed name")
	fmt.Fprintln(os.Stderr, "Fields:")
	fmt.Fprintln(os.Stderr, "  m: mode")
	fmt.Fprintln(os.Stderr, "  l: number of hardlinks")
	fmt.Fprintln(os.Stderr, "  u: UID")
	fmt.Fprintln(os.Stderr, "  g: GID")
	fmt.Fprintln(os.Stderr, "  s: size/Rdev")
	fmt.Fprintln(os.Stderr, "  t: time of last modification")
	fmt.Fprintln(os.Stderr, "  n: name")
	fmt.Fprintln(os.Stderr, "  d: data (hash or symlink target)")
}

type commandFunc func([]string)

type subcommand struct {
	command string
	minArgs int
	maxArgs int
	cmdFunc commandFunc
}

var subcommands = []subcommand{
	{"add", 4, 4, addImagefileSubcommand},
	{"adds", 4, 4, addImagesubSubcommand},
	{"addi", 4, 4, addImageimageSubcommand},
	{"addrep", 3, -1, addReplaceImageSubcommand},
	{"bulk-addrep", 1, -1, bulkAddReplaceImagesSubcommand},
	{"check", 1, 1, checkImageSubcommand},
	{"check-directory", 1, 1, checkDirectorySubcommand},
	{"chown", 2, 2, chownDirectorySubcommand},
	{"copy", 2, 2, copyImageSubcommand},
	{"delete", 1, 1, deleteImageSubcommand},
	{"delunrefobj", 2, 2, deleteUnreferencedObjectsSubcommand},
	{"diff", 3, 3, diffSubcommand},
	{"estimate-usage", 1, 1, estimateImageUsageSubcommand},
	{"find-latest-image", 1, 1, findLatestImageSubcommand},
	{"get", 2, 2, getImageSubcommand},
	{"list", 0, 0, listImagesSubcommand},
	{"listdirs", 0, 0, listDirectoriesSubcommand},
	{"listunrefobj", 0, 0, listUnreferencedObjectsSubcommand},
	{"list-latest-image", 1, 1, listLatestImageSubcommand},
	{"make-raw-image", 2, 2, makeRawImageSubcommand},
	{"match-triggers", 2, 2, matchTriggersSubcommand},
	{"merge-filters", 1, -1, mergeFiltersSubcommand},
	{"merge-triggers", 1, -1, mergeTriggersSubcommand},
	{"mkdir", 1, 1, makeDirectorySubcommand},
	{"show", 1, 1, showImageSubcommand},
	{"showunrefobj", 0, 0, showUnreferencedObjectsSubcommand},
	{"tar", 1, 2, tarImageSubcommand},
	{"test-download-speed", 1, 1, testDownloadSpeedSubcommand},
}

var imageSrpcClient *srpc.Client
var theObjectClient *objectclient.ObjectClient

var listSelector filesystem.ListSelector

func getClients() (*srpc.Client, *objectclient.ObjectClient) {
	if imageSrpcClient == nil {
		var err error
		clientName := fmt.Sprintf("%s:%d",
			*imageServerHostname, *imageServerPortNum)
		imageSrpcClient, err = srpc.DialHTTP("tcp", clientName, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error dialing\t%s\n", err)
			os.Exit(1)
		}
		theObjectClient = objectclient.NewObjectClient(clientName)
	}
	return imageSrpcClient, theObjectClient
}

func makeListSelector(arg string) filesystem.ListSelector {
	var mask filesystem.ListSelector = filesystem.ListSelectAll
	for _, char := range arg {
		switch char {
		case 'm':
			mask |= filesystem.ListSelectSkipMode
		case 'l':
			mask |= filesystem.ListSelectSkipNumLinks
		case 'u':
			mask |= filesystem.ListSelectSkipUid
		case 'g':
			mask |= filesystem.ListSelectSkipGid
		case 's':
			mask |= filesystem.ListSelectSkipSizeDevnum
		case 't':
			mask |= filesystem.ListSelectSkipMtime
		case 'n':
			mask |= filesystem.ListSelectSkipName
		case 'd':
			mask |= filesystem.ListSelectSkipData
		}
	}
	return mask
}

var listFilter *filter.Filter

func main() {
	if err := loadflags.LoadForCli("imagetool"); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	flag.Usage = printUsage
	flag.Parse()
	if flag.NArg() < 1 {
		printUsage()
		os.Exit(2)
	}
	logger = cmdlogger.New()
	if *expiresIn > 0 && *expiresIn < minimumExpiration {
		fmt.Fprintf(os.Stderr, "Minimum expiration: %s\n", minimumExpiration)
		os.Exit(2)
	}
	listSelector = makeListSelector(*skipFields)
	var err error
	if *filterFile != "" {
		listFilter, err = filter.Load(*filterFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(2)
		}
	}
	if err := setupclient.SetupTls(true); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	numSubcommandArgs := flag.NArg() - 1
	for _, subcommand := range subcommands {
		if flag.Arg(0) == subcommand.command {
			if numSubcommandArgs < subcommand.minArgs ||
				(subcommand.maxArgs >= 0 &&
					numSubcommandArgs > subcommand.maxArgs) {
				printUsage()
				os.Exit(2)
			}
			subcommand.cmdFunc(flag.Args()[1:])
			os.Exit(3)
		}
	}
	printUsage()
	os.Exit(2)
}
<commit_msg>Improve dialing error message in imagetool command.<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"time"

	"github.com/Symantec/Dominator/lib/constants"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/flags/loadflags"
	"github.com/Symantec/Dominator/lib/flagutil"
	"github.com/Symantec/Dominator/lib/log"
	"github.com/Symantec/Dominator/lib/log/cmdlogger"
	"github.com/Symantec/Dominator/lib/mbr"
	objectclient "github.com/Symantec/Dominator/lib/objectserver/client"
	"github.com/Symantec/Dominator/lib/srpc"
	"github.com/Symantec/Dominator/lib/srpc/setupclient"
)

var (
	allocateBlocks = flag.Bool("allocateBlocks", false,
		"If true, allocate blocks when making raw image")
	buildLog = flag.String("buildLog", "",
		"Filename or URL containing build log")
	compress      = flag.Bool("compress", false, "If true, compress tar output")
	computedFiles = flag.String("computedFiles", "",
		"Name of file containing computed files list")
	computedFilesRoot = flag.String("computedFilesRoot", "",
		"Name of directory tree containing computed files to replace on unpack")
	copyMtimesFrom = flag.String("copyMtimesFrom", "",
		"Name of image to copy mtimes for otherwise unchanged files/devices")
	debug = flag.Bool("debug", false,
		"If true, show debugging output")
	deleteFilter = flag.String("deleteFilter", "",
		"Name of delete filter file for addi, adds and diff subcommands")
	expiresIn = flag.Duration("expiresIn", 0,
		"How long before the image expires (auto deletes). Default: never")
	filterFile = flag.String("filterFile", "",
		"Filter file to apply when adding images")
	ignoreExpiring = flag.Bool("ignoreExpiring", false,
		"If true, ignore expiring images when finding images")
	imageServerHostname = flag.String("imageServerHostname", "localhost",
		"Hostname of image server")
	imageServerPortNum = flag.Uint("imageServerPortNum",
		constants.ImageServerPortNumber,
		"Port number of image server")
	makeBootable = flag.Bool("makeBootable", true,
		"If true, make raw image bootable by installing GRUB")
	minFreeBytes = flag.Uint64("minFreeBytes", 4<<20,
		"minimum number of free bytes in raw image")
	releaseNotes = flag.String("releaseNotes", "",
		"Filename or URL containing release notes")
	requiredPaths = flagutil.StringToRuneMap(constants.RequiredPaths)
	roundupPower  = flag.Uint64("roundupPower", 24,
		"power of 2 to round up raw image size")
	skipFields = flag.String("skipFields", "",
		"Fields to skip when showing or diffing images")
	tableType mbr.TableType = mbr.TABLE_TYPE_MSDOS
	timeout                 = flag.Duration("timeout", 0,
		"Timeout for get subcommand")

	logger            log.DebugLogger
	minimumExpiration = 15 * time.Minute
)

func init() {
	flag.Var(&requiredPaths, "requiredPaths",
		"Comma separated list of required path:type entries")
	flag.Var(&tableType, "tableType", "partition table type for make-raw-image")
}

func printUsage() {
	fmt.Fprintln(os.Stderr,
		"Usage: imagetool [flags...] add|check|delete|list [args...]")
	fmt.Fprintln(os.Stderr, "Common flags:")
	flag.PrintDefaults()
	fmt.Fprintln(os.Stderr, "Commands:")
	fmt.Fprintln(os.Stderr, "  add    name imagefile filterfile triggerfile")
	fmt.Fprintln(os.Stderr, "  addi   name imagename filterfile triggerfile")
	fmt.Fprintln(os.Stderr, "  adds   name subname filterfile triggerfile")
	fmt.Fprintln(os.Stderr, "  addrep name baseimage layerimage...")
	fmt.Fprintln(os.Stderr, "  bulk-addrep layerimage...")
	fmt.Fprintln(os.Stderr, "  check  name")
	fmt.Fprintln(os.Stderr, "  check-directory dirname")
	fmt.Fprintln(os.Stderr, "  chown  dirname ownerGroup")
	fmt.Fprintln(os.Stderr, "  copy   name oldimagename")
	fmt.Fprintln(os.Stderr, "  delete name")
	fmt.Fprintln(os.Stderr, "  delunrefobj percentage bytes")
	fmt.Fprintln(os.Stderr, "  diff   tool left right")
	fmt.Fprintln(os.Stderr, "         left & right are image sources. Format:")
	fmt.Fprintln(os.Stderr, "         type:name where type is one of:")
	fmt.Fprintln(os.Stderr, "           d: name of directory tree to scan")
	fmt.Fprintln(os.Stderr, "           f: name of file containing a FileSystem")
	fmt.Fprintln(os.Stderr, "           i: name of an image on the imageserver")
	fmt.Fprintln(os.Stderr, "           l: name of file containing an Image")
	fmt.Fprintln(os.Stderr, "           s: name of sub to poll")
	fmt.Fprintln(os.Stderr, "  estimate-usage      name")
	fmt.Fprintln(os.Stderr, "  find-latest-image   directory")
	fmt.Fprintln(os.Stderr, "  get                 name directory")
	fmt.Fprintln(os.Stderr, "  list")
	fmt.Fprintln(os.Stderr, "  listdirs")
	fmt.Fprintln(os.Stderr, "  listunrefobj")
	fmt.Fprintln(os.Stderr, "  list-latest-image   directory")
	fmt.Fprintln(os.Stderr, "  make-raw-image      name rawfile")
	fmt.Fprintln(os.Stderr, "  match-triggers      name triggers-file")
	fmt.Fprintln(os.Stderr, "  merge-filters       filter-file...")
	fmt.Fprintln(os.Stderr, "  merge-triggers      triggers-file...")
	fmt.Fprintln(os.Stderr, "  mkdir               name")
	fmt.Fprintln(os.Stderr, "  show                name")
	fmt.Fprintln(os.Stderr, "  showunrefobj")
	fmt.Fprintln(os.Stderr, "  tar                 name [file]")
	fmt.Fprintln(os.Stderr, "  test-download-speed name")
	fmt.Fprintln(os.Stderr, "Fields:")
	fmt.Fprintln(os.Stderr, "  m: mode")
	fmt.Fprintln(os.Stderr, "  l: number of hardlinks")
	fmt.Fprintln(os.Stderr, "  u: UID")
	fmt.Fprintln(os.Stderr, "  g: GID")
	fmt.Fprintln(os.Stderr, "  s: size/Rdev")
	fmt.Fprintln(os.Stderr, "  t: time of last modification")
	fmt.Fprintln(os.Stderr, "  n: name")
	fmt.Fprintln(os.Stderr, "  d: data (hash or symlink target)")
}

type commandFunc func([]string)

type subcommand struct {
	command string
	minArgs int
	maxArgs int
	cmdFunc commandFunc
}

var subcommands = []subcommand{
	{"add", 4, 4, addImagefileSubcommand},
	{"adds", 4, 4, addImagesubSubcommand},
	{"addi", 4, 4, addImageimageSubcommand},
	{"addrep", 3, -1, addReplaceImageSubcommand},
	{"bulk-addrep", 1, -1, bulkAddReplaceImagesSubcommand},
	{"check", 1, 1, checkImageSubcommand},
	{"check-directory", 1, 1, checkDirectorySubcommand},
	{"chown", 2, 2, chownDirectorySubcommand},
	{"copy", 2, 2, copyImageSubcommand},
	{"delete", 1, 1, deleteImageSubcommand},
	{"delunrefobj", 2, 2, deleteUnreferencedObjectsSubcommand},
	{"diff", 3, 3, diffSubcommand},
	{"estimate-usage", 1, 1, estimateImageUsageSubcommand},
	{"find-latest-image", 1, 1, findLatestImageSubcommand},
	{"get", 2, 2, getImageSubcommand},
	{"list", 0, 0, listImagesSubcommand},
	{"listdirs", 0, 0, listDirectoriesSubcommand},
	{"listunrefobj", 0, 0, listUnreferencedObjectsSubcommand},
	{"list-latest-image", 1, 1, listLatestImageSubcommand},
	{"make-raw-image", 2, 2, makeRawImageSubcommand},
	{"match-triggers", 2, 2, matchTriggersSubcommand},
	{"merge-filters", 1, -1, mergeFiltersSubcommand},
	{"merge-triggers", 1, -1, mergeTriggersSubcommand},
	{"mkdir", 1, 1, makeDirectorySubcommand},
	{"show", 1, 1, showImageSubcommand},
	{"showunrefobj", 0, 0, showUnreferencedObjectsSubcommand},
	{"tar", 1, 2, tarImageSubcommand},
	{"test-download-speed", 1, 1, testDownloadSpeedSubcommand},
}

var imageSrpcClient *srpc.Client
var theObjectClient *objectclient.ObjectClient

var listSelector filesystem.ListSelector

func getClients() (*srpc.Client, *objectclient.ObjectClient) {
	if imageSrpcClient == nil {
		var err error
		clientName := fmt.Sprintf("%s:%d",
			*imageServerHostname, *imageServerPortNum)
		imageSrpcClient, err = srpc.DialHTTP("tcp", clientName, 0)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error dialing: %s: %s\n", clientName, err)
			os.Exit(1)
		}
		theObjectClient = objectclient.NewObjectClient(clientName)
	}
	return imageSrpcClient, theObjectClient
}

func makeListSelector(arg string) filesystem.ListSelector {
	var mask filesystem.ListSelector = filesystem.ListSelectAll
	for _, char := range arg {
		switch char {
		case 'm':
			mask |= filesystem.ListSelectSkipMode
		case 'l':
			mask |= filesystem.ListSelectSkipNumLinks
		case 'u':
			mask |= filesystem.ListSelectSkipUid
		case 'g':
			mask |= filesystem.ListSelectSkipGid
		case 's':
			mask |= filesystem.ListSelectSkipSizeDevnum
		case 't':
			mask |= filesystem.ListSelectSkipMtime
		case 'n':
			mask |= filesystem.ListSelectSkipName
		case 'd':
			mask |= filesystem.ListSelectSkipData
		}
	}
	return mask
}

var listFilter *filter.Filter

func main() {
	if err := loadflags.LoadForCli("imagetool"); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	flag.Usage = printUsage
	flag.Parse()
	if flag.NArg() < 1 {
		printUsage()
		os.Exit(2)
	}
	logger = cmdlogger.New()
	if *expiresIn > 0 && *expiresIn < minimumExpiration {
		fmt.Fprintf(os.Stderr, "Minimum expiration: %s\n", minimumExpiration)
		os.Exit(2)
	}
	listSelector = makeListSelector(*skipFields)
	var err error
	if *filterFile != "" {
		listFilter, err = filter.Load(*filterFile)
		if err != nil {
			fmt.Fprintln(os.Stderr, err)
			os.Exit(2)
		}
	}
	if err := setupclient.SetupTls(true); err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	numSubcommandArgs := flag.NArg() - 1
	for _, subcommand := range subcommands {
		if flag.Arg(0) == subcommand.command {
			if numSubcommandArgs < subcommand.minArgs ||
				(subcommand.maxArgs >= 0 &&
					numSubcommandArgs > subcommand.maxArgs) {
				printUsage()
				os.Exit(2)
			}
			subcommand.cmdFunc(flag.Args()[1:])
			os.Exit(3)
		}
	}
	printUsage()
	os.Exit(2)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"

	"github.com/pkg/errors"

	"github.com/spf13/cobra"

	"github.com/havoc-io/mutagen/cmd"
	"github.com/havoc-io/mutagen/pkg/filesystem"
	promptpkg "github.com/havoc-io/mutagen/pkg/prompt"
	sessionpkg "github.com/havoc-io/mutagen/pkg/session"
	sessionsvcpkg "github.com/havoc-io/mutagen/pkg/session/service"
	"github.com/havoc-io/mutagen/pkg/sync"
	"github.com/havoc-io/mutagen/pkg/url"
)

func createMain(command *cobra.Command, arguments []string) error {
	// Validate, extract, and parse URLs.
	if len(arguments) != 2 {
		return errors.New("invalid number of endpoint URLs provided")
	}
	alpha, err := url.Parse(arguments[0])
	if err != nil {
		return errors.Wrap(err, "unable to parse alpha URL")
	}
	beta, err := url.Parse(arguments[1])
	if err != nil {
		return errors.Wrap(err, "unable to parse beta URL")
	}

	// If either URL is a local path, make sure it's normalized.
	if alpha.Protocol == url.Protocol_Local {
		if alphaPath, err := filesystem.Normalize(alpha.Path); err != nil {
			return errors.Wrap(err, "unable to normalize alpha path")
		} else {
			alpha.Path = alphaPath
		}
	}
	if beta.Protocol == url.Protocol_Local {
		if betaPath, err := filesystem.Normalize(beta.Path); err != nil {
			return errors.Wrap(err, "unable to normalize beta path")
		} else {
			beta.Path = betaPath
		}
	}

	// Validate and convert the symlink mode specification.
	var symlinkMode sync.SymlinkMode
	if createConfiguration.symlinkMode != "" {
		if m, err := sync.NewSymlinkModeFromString(createConfiguration.symlinkMode); err != nil {
			return errors.Wrap(err, "unable to parse symlink mode")
		} else {
			symlinkMode = m
		}
	}

	// Connect to the daemon and defer closure of the connection.
	daemonConnection, err := createDaemonClientConnection()
	if err != nil {
		return errors.Wrap(err, "unable to connect to daemon")
	}
	defer daemonConnection.Close()

	// Create a session service client.
	sessionService := sessionsvcpkg.NewSessionClient(daemonConnection)

	// Invoke the session create method. The stream will close when the
	// associated context is cancelled.
	createContext, cancel := context.WithCancel(context.Background())
	defer cancel()
	stream, err := sessionService.Create(createContext)
	if err != nil {
		return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to invoke create")
	}

	// Send the initial request.
	request := &sessionsvcpkg.CreateRequest{
		Alpha: alpha,
		Beta:  beta,
		Configuration: &sessionpkg.Configuration{
			Ignores:     createConfiguration.ignores,
			SymlinkMode: symlinkMode,
		},
	}
	if err := stream.Send(request); err != nil {
		return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to send create request")
	}

	// Receive and process responses until we're done.
	for {
		// Receive the next response, watching for completion or another prompt.
		var prompt *promptpkg.Prompt
		if response, err := stream.Recv(); err != nil {
			return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to receive response")
		} else if response.Session != "" {
			if response.Prompt != nil {
				return errors.New("invalid create response received (session with prompt)")
			}
			fmt.Println("Created session", response.Session)
			return nil
		} else if response.Prompt == nil {
			return errors.New("invalid create response received (empty)")
		} else {
			prompt = response.Prompt
		}

		// Process the prompt.
		if response, err := promptpkg.PromptCommandLine(prompt.Message, prompt.Prompt); err != nil {
			return errors.Wrap(err, "unable to perform prompting")
		} else if err = stream.Send(&sessionsvcpkg.CreateRequest{Response: response}); err != nil {
			return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to send prompt response")
		}
	}
}

var createCommand = &cobra.Command{
	Use:   "create <alpha> <beta>",
	Short: "Creates and starts a new synchronization session",
	Run:   cmd.Mainify(createMain),
}

var createConfiguration struct {
	help        bool
	ignores     []string
	symlinkMode string
}

func init() {
	// Bind flags to configuration. We manually add help to override the default
	// message, but Cobra still implements it automatically.
	flags := createCommand.Flags()
	flags.BoolVarP(&createConfiguration.help, "help", "h", false, "Show help information")
	flags.StringSliceVarP(&createConfiguration.ignores, "ignore", "i", nil, "Specify ignore paths")
	flags.StringVarP(&createConfiguration.symlinkMode, "symlink-mode", "s", "", "Specify symlink mode (portable|ignore|posix-raw)")
}
<commit_msg>Added ability to specify watch mode and polling interval in create.<commit_after>package main

import (
	"context"
	"fmt"

	"github.com/pkg/errors"

	"github.com/spf13/cobra"

	"github.com/havoc-io/mutagen/cmd"
	"github.com/havoc-io/mutagen/pkg/filesystem"
	promptpkg "github.com/havoc-io/mutagen/pkg/prompt"
	sessionpkg "github.com/havoc-io/mutagen/pkg/session"
	sessionsvcpkg "github.com/havoc-io/mutagen/pkg/session/service"
	"github.com/havoc-io/mutagen/pkg/sync"
	"github.com/havoc-io/mutagen/pkg/url"
)

func createMain(command *cobra.Command, arguments []string) error {
	// Validate, extract, and parse URLs.
	if len(arguments) != 2 {
		return errors.New("invalid number of endpoint URLs provided")
	}
	alpha, err := url.Parse(arguments[0])
	if err != nil {
		return errors.Wrap(err, "unable to parse alpha URL")
	}
	beta, err := url.Parse(arguments[1])
	if err != nil {
		return errors.Wrap(err, "unable to parse beta URL")
	}

	// If either URL is a local path, make sure it's normalized.
	if alpha.Protocol == url.Protocol_Local {
		if alphaPath, err := filesystem.Normalize(alpha.Path); err != nil {
			return errors.Wrap(err, "unable to normalize alpha path")
		} else {
			alpha.Path = alphaPath
		}
	}
	if beta.Protocol == url.Protocol_Local {
		if betaPath, err := filesystem.Normalize(beta.Path); err != nil {
			return errors.Wrap(err, "unable to normalize beta path")
		} else {
			beta.Path = betaPath
		}
	}

	// We don't need to validate ignores here, that will happen on the session
	// service, so we'll save ourselves the time.

	// Validate and convert the symlink mode specification.
	var symlinkMode sync.SymlinkMode
	if createConfiguration.symlinkMode != "" {
		if m, err := sync.NewSymlinkModeFromString(createConfiguration.symlinkMode); err != nil {
			return errors.Wrap(err, "unable to parse symlink mode")
		} else {
			symlinkMode = m
		}
	}

	// Validate and convert the watch mode specification.
	var watchMode filesystem.WatchMode
	if createConfiguration.watchMode != "" {
		if m, err := filesystem.NewWatchModeFromString(createConfiguration.watchMode); err != nil {
			return errors.Wrap(err, "unable to parse watch mode")
		} else {
			watchMode = m
		}
	}

	// There's no need to validate the watch polling mode - any uint32 value is
	// valid.

	// Connect to the daemon and defer closure of the connection.
	daemonConnection, err := createDaemonClientConnection()
	if err != nil {
		return errors.Wrap(err, "unable to connect to daemon")
	}
	defer daemonConnection.Close()

	// Create a session service client.
	sessionService := sessionsvcpkg.NewSessionClient(daemonConnection)

	// Invoke the session create method. The stream will close when the
	// associated context is cancelled.
	createContext, cancel := context.WithCancel(context.Background())
	defer cancel()
	stream, err := sessionService.Create(createContext)
	if err != nil {
		return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to invoke create")
	}

	// Send the initial request.
	request := &sessionsvcpkg.CreateRequest{
		Alpha: alpha,
		Beta:  beta,
		Configuration: &sessionpkg.Configuration{
			Ignores:              createConfiguration.ignores,
			SymlinkMode:          symlinkMode,
			WatchMode:            watchMode,
			WatchPollingInterval: createConfiguration.watchPollingInterval,
		},
	}
	if err := stream.Send(request); err != nil {
		return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to send create request")
	}

	// Receive and process responses until we're done.
	for {
		// Receive the next response, watching for completion or another prompt.
		var prompt *promptpkg.Prompt
		if response, err := stream.Recv(); err != nil {
			return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to receive response")
		} else if response.Session != "" {
			if response.Prompt != nil {
				return errors.New("invalid create response received (session with prompt)")
			}
			fmt.Println("Created session", response.Session)
			return nil
		} else if response.Prompt == nil {
			return errors.New("invalid create response received (empty)")
		} else {
			prompt = response.Prompt
		}

		// Process the prompt.
		if response, err := promptpkg.PromptCommandLine(prompt.Message, prompt.Prompt); err != nil {
			return errors.Wrap(err, "unable to perform prompting")
		} else if err = stream.Send(&sessionsvcpkg.CreateRequest{Response: response}); err != nil {
			return errors.Wrap(peelAwayRPCErrorLayer(err), "unable to send prompt response")
		}
	}
}

var createCommand = &cobra.Command{
	Use:   "create <alpha> <beta>",
	Short: "Creates and starts a new synchronization session",
	Run:   cmd.Mainify(createMain),
}

var createConfiguration struct {
	help                 bool
	ignores              []string
	symlinkMode          string
	watchMode            string
	watchPollingInterval uint32
}

func init() {
	// Bind flags to configuration. We manually add help to override the default
	// message, but Cobra still implements it automatically.
	flags := createCommand.Flags()
	flags.BoolVarP(&createConfiguration.help, "help", "h", false, "Show help information")
	flags.StringSliceVarP(&createConfiguration.ignores, "ignore", "i", nil, "Specify ignore paths")
	flags.StringVar(&createConfiguration.symlinkMode, "symlink-mode", "", "Specify symlink mode (portable|ignore|posix-raw)")
	flags.StringVar(&createConfiguration.watchMode, "watch-mode", "", "Specify watch mode (recursive-home|poll)")
	flags.Uint32Var(&createConfiguration.watchPollingInterval, "watch-polling-interval", 0, "Specify watch polling interval")
}
<|endoftext|>


<commit_before>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to discordgo package logging

package discordgo

import (
	"fmt"
	"log"
	"runtime"
	"strings"
)

const (

	// LogError level is used for critical errors that could lead to data loss
	// or panic that would not be returned to a calling function.
	LogError int = iota

	// LogWarning level is used for very abnormal events and errors that are
	// also returned to a calling function.
	LogWarning

	// LogInformational level is used for normal non-error activity
	LogInformational

	// LogDebug level is for very detailed non-error activity.  This is
	// very spammy and will impact performance.
	LogDebug
)

// Logger can be used to replace the standard logging for discordgo
var Logger func(msgL, caller int, format string, a ...interface{})

// msglog provides package wide logging consistancy for discordgo
// the format, a...  portion this command follows that of fmt.Printf
//   msgL   : LogLevel of the message
//   caller : 1 + the number of callers away from the message source
//   format : Printf style message format
//   a ...  : comma seperated list of values to pass
func msglog(msgL, caller int, format string, a ...interface{}) {

	if Logger != nil {
		Logger(msgL, caller, format, a)
	} else {

		pc, file, line, _ := runtime.Caller(caller)

		files := strings.Split(file, "/")
		file = files[len(files)-1]

		name := runtime.FuncForPC(pc).Name()
		fns := strings.Split(name, ".")
		name = fns[len(fns)-1]

		msg := fmt.Sprintf(format, a...)

		log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
	}
}

// helper function that wraps msglog for the Session struct
// This adds a check to insure the message is only logged
// if the session log level is equal or higher than the
// message log level
func (s *Session) log(msgL int, format string, a ...interface{}) {

	if msgL > s.LogLevel {
		return
	}

	msglog(msgL, 2, format, a...)
}

// helper function that wraps msglog for the VoiceConnection struct
// This adds a check to insure the message is only logged
// if the voice connection log level is equal or higher than the
// message log level
func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) {

	if msgL > v.LogLevel {
		return
	}

	msglog(msgL, 2, format, a...)
}

// printJSON is a helper function to display JSON data in a easy to read format.
/* NOT USED ATM
func printJSON(body []byte) {
	var prettyJSON bytes.Buffer
	error := json.Indent(&prettyJSON, body, "", "\t")
	if error != nil {
		log.Print("JSON parse error: ", error)
	}
	log.Println(string(prettyJSON.Bytes()))
}
*/
<commit_msg>Forgot ... (#473)<commit_after>// Discordgo - Discord bindings for Go
// Available at https://github.com/bwmarrin/discordgo

// Copyright 2015-2016 Bruce Marriner <bruce@sqls.net>.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This file contains code related to discordgo package logging

package discordgo

import (
	"fmt"
	"log"
	"runtime"
	"strings"
)

const (

	// LogError level is used for critical errors that could lead to data loss
	// or panic that would not be returned to a calling function.
	LogError int = iota

	// LogWarning level is used for very abnormal events and errors that are
	// also returned to a calling function.
	LogWarning

	// LogInformational level is used for normal non-error activity
	LogInformational

	// LogDebug level is for very detailed non-error activity.  This is
	// very spammy and will impact performance.
	LogDebug
)

// Logger can be used to replace the standard logging for discordgo
var Logger func(msgL, caller int, format string, a ...interface{})

// msglog provides package wide logging consistancy for discordgo
// the format, a...  portion this command follows that of fmt.Printf
//   msgL   : LogLevel of the message
//   caller : 1 + the number of callers away from the message source
//   format : Printf style message format
//   a ...  : comma seperated list of values to pass
func msglog(msgL, caller int, format string, a ...interface{}) {

	if Logger != nil {
		Logger(msgL, caller, format, a...)
	} else {

		pc, file, line, _ := runtime.Caller(caller)

		files := strings.Split(file, "/")
		file = files[len(files)-1]

		name := runtime.FuncForPC(pc).Name()
		fns := strings.Split(name, ".")
		name = fns[len(fns)-1]

		msg := fmt.Sprintf(format, a...)

		log.Printf("[DG%d] %s:%d:%s() %s\n", msgL, file, line, name, msg)
	}
}

// helper function that wraps msglog for the Session struct
// This adds a check to insure the message is only logged
// if the session log level is equal or higher than the
// message log level
func (s *Session) log(msgL int, format string, a ...interface{}) {

	if msgL > s.LogLevel {
		return
	}

	msglog(msgL, 2, format, a...)
}

// helper function that wraps msglog for the VoiceConnection struct
// This adds a check to insure the message is only logged
// if the voice connection log level is equal or higher than the
// message log level
func (v *VoiceConnection) log(msgL int, format string, a ...interface{}) {

	if msgL > v.LogLevel {
		return
	}

	msglog(msgL, 2, format, a...)
}

// printJSON is a helper function to display JSON data in a easy to read format.
/* NOT USED ATM
func printJSON(body []byte) {
	var prettyJSON bytes.Buffer
	error := json.Indent(&prettyJSON, body, "", "\t")
	if error != nil {
		log.Print("JSON parse error: ", error)
	}
	log.Println(string(prettyJSON.Bytes()))
}
*/
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"go/format"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type generateType struct {
	Name    string
	Initial string
	Fields  []generateField
}

type generateField struct {
	Name     string
	Initial  string
	TypeName string
	JSONName string
	View     string
}

var reservedFiledNames = map[string]string{
	"uuid":      "UUID",
	"item":      "Item",
	"id":        "ID",
	"slug":      "Slug",
	"timestamp": "Timestamp",
	"updated":   "Updated",
}

func legalFieldNames(fields ...generateField) (bool, map[string]string) {
	conflicts := make(map[string]string)
	for _, field := range fields {
		for jsonName, fieldName := range reservedFiledNames {
			if field.JSONName == jsonName || field.Name == fieldName {
				conflicts[jsonName] = fieldName
			}
		}
	}

	if len(conflicts) > 0 {
		return false, conflicts
	}

	return true, conflicts
}

// blog title:string Author:string PostCategory:string content:string some_thing:int
func parseType(args []string) (generateType, error) {
	t := generateType{
		Name: fieldName(args[0]),
	}
	t.Initial = strings.ToLower(string(t.Name[0]))

	fields := args[1:]
	for _, field := range fields {
		f, err := parseField(field, t)
		if err != nil {
			return generateType{}, err
		}

		// set initial (1st character of the type's name) on field so we don't need
		// to set the template variable like was done in prior version
		f.Initial = t.Initial

		t.Fields = append(t.Fields, f)
	}

	if ok, nameConflicts := legalFieldNames(t.Fields...); !ok {
		for jsonName, fieldName := range nameConflicts {
			fmt.Println(fmt.Sprintf("reserved field name: %s (%s)", jsonName, fieldName))
		}

		count := len(nameConflicts)
		var c = "conflict"
		if count > 1 {
			c = "conflicts"
		}

		return generateType{}, fmt.Errorf("You have (%d) naming %s - please rename and try again", count, c)
	}

	return t, nil
}

func parseField(raw string, gt generateType) (generateField, error) {
	// contents:string or // contents:string:richtext
	if !strings.Contains(raw, ":") {
		return generateField{}, fmt.Errorf("Invalid generate argument. [%s]", raw)
	}

	data := strings.Split(raw, ":")

	field := generateField{
		Name:     fieldName(data[0]),
		Initial:  gt.Initial,
		TypeName: strings.ToLower(data[1]),
		JSONName: fieldJSONName(data[0]),
	}

	fieldType := "input"
	if len(data) == 3 {
		fieldType = data[2]
	}

	err := setFieldView(&field, fieldType)
	if err != nil {
		return generateField{}, err
	}

	return field, nil
}

// get the initial field name passed and check it for all possible cases
// MyTitle:string myTitle:string my_title:string -> MyTitle
// error-message:string -> ErrorMessage
func fieldName(name string) string {
	// remove _ or - if first character
	if name[0] == '-' || name[0] == '_' {
		name = name[1:]
	}

	// remove _ or - if last character
	if name[len(name)-1] == '-' || name[len(name)-1] == '_' {
		name = name[:len(name)-1]
	}

	// upcase the first character
	name = strings.ToUpper(string(name[0])) + name[1:]

	// remove _ or - character, and upcase the character immediately following
	for i := 0; i < len(name); i++ {
		r := rune(name[i])
		if isUnderscore(r) || isHyphen(r) {
			up := strings.ToUpper(string(name[i+1]))
			name = name[:i] + up + name[i+2:]
		}
	}

	return name
}

// get the initial field name passed and convert to json-like name
// MyTitle:string myTitle:string my_title:string -> my_title
// error-message:string -> error-message
func fieldJSONName(name string) string {
	// remove _ or - if first character
	if name[0] == '-' || name[0] == '_' {
		name = name[1:]
	}

	// downcase the first character
	name = strings.ToLower(string(name[0])) + name[1:]

	// check for uppercase character, downcase and insert _ before it if i-1
	// isn't already _ or -
	for i := 0; i < len(name); i++ {
		r := rune(name[i])
		if isUpper(r) {
			low := strings.ToLower(string(r))
			if name[i-1] == '_' || name[i-1] == '-' {
				name = name[:i] + low + name[i+1:]
			} else {
				name = name[:i] + "_" + low + name[i+1:]
			}
		}
	}

	return name
}

// set the specified view inside the editor field for a generated field for a type
func setFieldView(field *generateField, viewType string) error {
	var err error
	var tmpl *template.Template
	buf := &bytes.Buffer{}

	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	tmplDir := filepath.Join(pwd, "cmd", "ponzu", "templates")
	tmplFrom := func(filename string) (*template.Template, error) {
		return template.ParseFiles(filepath.Join(tmplDir, filename))
	}

	viewType = strings.ToLower(viewType)
	switch viewType {
	case "checkbox":
		tmpl, err = tmplFrom("gen-checkbox.tmpl")
	case "custom":
		tmpl, err = tmplFrom("gen-custom.tmpl")
	case "file":
		tmpl, err = tmplFrom("gen-file.tmpl")
	case "hidden":
		tmpl, err = tmplFrom("gen-hidden.tmpl")
	case "input", "text":
		tmpl, err = tmplFrom("gen-input.tmpl")
	case "richtext":
		tmpl, err = tmplFrom("gen-richtext.tmpl")
	case "select":
		tmpl, err = tmplFrom("gen-select.tmpl")
	case "textarea":
		tmpl, err = tmplFrom("gen-textarea.tmpl")
	case "tags":
		tmpl, err = tmplFrom("gen-tags.tmpl")
	default:
		msg := fmt.Sprintf("'%s' is not a recognized view type. Using 'input' instead.", viewType)
		fmt.Println(msg)
		tmpl, err = tmplFrom("gen-input.tmpl")
	}

	if err != nil {
		return err
	}

	err = tmpl.Execute(buf, field)
	if err != nil {
		return err
	}

	field.View = buf.String()

	return nil
}

func isUpper(char rune) bool {
	if char >= 'A' && char <= 'Z' {
		return true
	}

	return false
}

func isUnderscore(char rune) bool {
	return char == '_'
}

func isHyphen(char rune) bool {
	return char == '-'
}

func generateContentType(args []string) error {
	name := args[0]
	fileName := strings.ToLower(name) + ".go"

	// open file in ./content/ dir
	// if exists, alert user of conflict
	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	contentDir := filepath.Join(pwd, "content")
	filePath := filepath.Join(contentDir, fileName)

	if _, err := os.Stat(filePath); !os.IsNotExist(err) {
		localFile := filepath.Join("content", fileName)
		return fmt.Errorf("Please remove '%s' before executing this command", localFile)
	}

	// parse type info from args
	gt, err := parseType(args)
	if err != nil {
		return fmt.Errorf("Failed to parse type args: %s", err.Error())
	}

	tmplPath := filepath.Join(pwd, "cmd", "ponzu", "templates", "gen-content.tmpl")
	tmpl, err := template.ParseFiles(tmplPath)
	if err != nil {
		return fmt.Errorf("Failed to parse template: %s", err.Error())
	}

	buf := &bytes.Buffer{}
	err = tmpl.Execute(buf, gt)
	if err != nil {
		return fmt.Errorf("Failed to execute template: %s", err.Error())
	}

	fmtBuf, err := format.Source(buf.Bytes())
	if err != nil {
		return fmt.Errorf("Failed to format template: %s", err.Error())
	}

	// no file exists.. ok to write new one
	file, err := os.Create(filePath)
	defer file.Close()
	if err != nil {
		return err
	}

	_, err = file.Write(fmtBuf)
	if err != nil {
		return fmt.Errorf("Failed to write generated file buffer: %s", err.Error())
	}

	return nil
}
<commit_msg>fix typo<commit_after>package main

import (
	"bytes"
	"fmt"
	"go/format"
	"os"
	"path/filepath"
	"strings"
	"text/template"
)

type generateType struct {
	Name    string
	Initial string
	Fields  []generateField
}

type generateField struct {
	Name     string
	Initial  string
	TypeName string
	JSONName string
	View     string
}

var reservedFieledNames = map[string]string{
	"uuid":      "UUID",
	"item":      "Item",
	"id":        "ID",
	"slug":      "Slug",
	"timestamp": "Timestamp",
	"updated":   "Updated",
}

func legalFieldNames(fields ...generateField) (bool, map[string]string) {
	conflicts := make(map[string]string)
	for _, field := range fields {
		for jsonName, fieldName := range reservedFieledNames {
			if field.JSONName == jsonName || field.Name == fieldName {
				conflicts[jsonName] = fieldName
			}
		}
	}

	if len(conflicts) > 0 {
		return false, conflicts
	}

	return true, conflicts
}

// blog title:string Author:string PostCategory:string content:string some_thing:int
func parseType(args []string) (generateType, error) {
	t := generateType{
		Name: fieldName(args[0]),
	}
	t.Initial = strings.ToLower(string(t.Name[0]))

	fields := args[1:]
	for _, field := range fields {
		f, err := parseField(field, t)
		if err != nil {
			return generateType{}, err
		}

		// set initial (1st character of the type's name) on field so we don't need
		// to set the template variable like was done in prior version
		f.Initial = t.Initial

		t.Fields = append(t.Fields, f)
	}

	if ok, nameConflicts := legalFieldNames(t.Fields...); !ok {
		for jsonName, fieldName := range nameConflicts {
			fmt.Println(fmt.Sprintf("reserved field name: %s (%s)", jsonName, fieldName))
		}

		count := len(nameConflicts)
		var c = "conflict"
		if count > 1 {
			c = "conflicts"
		}

		return generateType{}, fmt.Errorf("You have (%d) naming %s - please rename and try again", count, c)
	}

	return t, nil
}

func parseField(raw string, gt generateType) (generateField, error) {
	// contents:string or // contents:string:richtext
	if !strings.Contains(raw, ":") {
		return generateField{}, fmt.Errorf("Invalid generate argument. [%s]", raw)
	}

	data := strings.Split(raw, ":")

	field := generateField{
		Name:     fieldName(data[0]),
		Initial:  gt.Initial,
		TypeName: strings.ToLower(data[1]),
		JSONName: fieldJSONName(data[0]),
	}

	fieldType := "input"
	if len(data) == 3 {
		fieldType = data[2]
	}

	err := setFieldView(&field, fieldType)
	if err != nil {
		return generateField{}, err
	}

	return field, nil
}

// get the initial field name passed and check it for all possible cases
// MyTitle:string myTitle:string my_title:string -> MyTitle
// error-message:string -> ErrorMessage
func fieldName(name string) string {
	// remove _ or - if first character
	if name[0] == '-' || name[0] == '_' {
		name = name[1:]
	}

	// remove _ or - if last character
	if name[len(name)-1] == '-' || name[len(name)-1] == '_' {
		name = name[:len(name)-1]
	}

	// upcase the first character
	name = strings.ToUpper(string(name[0])) + name[1:]

	// remove _ or - character, and upcase the character immediately following
	for i := 0; i < len(name); i++ {
		r := rune(name[i])
		if isUnderscore(r) || isHyphen(r) {
			up := strings.ToUpper(string(name[i+1]))
			name = name[:i] + up + name[i+2:]
		}
	}

	return name
}

// get the initial field name passed and convert to json-like name
// MyTitle:string myTitle:string my_title:string -> my_title
// error-message:string -> error-message
func fieldJSONName(name string) string {
	// remove _ or - if first character
	if name[0] == '-' || name[0] == '_' {
		name = name[1:]
	}

	// downcase the first character
	name = strings.ToLower(string(name[0])) + name[1:]

	// check for uppercase character, downcase and insert _ before it if i-1
	// isn't already _ or -
	for i := 0; i < len(name); i++ {
		r := rune(name[i])
		if isUpper(r) {
			low := strings.ToLower(string(r))
			if name[i-1] == '_' || name[i-1] == '-' {
				name = name[:i] + low + name[i+1:]
			} else {
				name = name[:i] + "_" + low + name[i+1:]
			}
		}
	}

	return name
}

// set the specified view inside the editor field for a generated field for a type
func setFieldView(field *generateField, viewType string) error {
	var err error
	var tmpl *template.Template
	buf := &bytes.Buffer{}

	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	tmplDir := filepath.Join(pwd, "cmd", "ponzu", "templates")
	tmplFrom := func(filename string) (*template.Template, error) {
		return template.ParseFiles(filepath.Join(tmplDir, filename))
	}

	viewType = strings.ToLower(viewType)
	switch viewType {
	case "checkbox":
		tmpl, err = tmplFrom("gen-checkbox.tmpl")
	case "custom":
		tmpl, err = tmplFrom("gen-custom.tmpl")
	case "file":
		tmpl, err = tmplFrom("gen-file.tmpl")
	case "hidden":
		tmpl, err = tmplFrom("gen-hidden.tmpl")
	case "input", "text":
		tmpl, err = tmplFrom("gen-input.tmpl")
	case "richtext":
		tmpl, err = tmplFrom("gen-richtext.tmpl")
	case "select":
		tmpl, err = tmplFrom("gen-select.tmpl")
	case "textarea":
		tmpl, err = tmplFrom("gen-textarea.tmpl")
	case "tags":
		tmpl, err = tmplFrom("gen-tags.tmpl")
	default:
		msg := fmt.Sprintf("'%s' is not a recognized view type. Using 'input' instead.", viewType)
		fmt.Println(msg)
		tmpl, err = tmplFrom("gen-input.tmpl")
	}

	if err != nil {
		return err
	}

	err = tmpl.Execute(buf, field)
	if err != nil {
		return err
	}

	field.View = buf.String()

	return nil
}

func isUpper(char rune) bool {
	if char >= 'A' && char <= 'Z' {
		return true
	}

	return false
}

func isUnderscore(char rune) bool {
	return char == '_'
}

func isHyphen(char rune) bool {
	return char == '-'
}

func generateContentType(args []string) error {
	name := args[0]
	fileName := strings.ToLower(name) + ".go"

	// open file in ./content/ dir
	// if exists, alert user of conflict
	pwd, err := os.Getwd()
	if err != nil {
		return err
	}

	contentDir := filepath.Join(pwd, "content")
	filePath := filepath.Join(contentDir, fileName)

	if _, err := os.Stat(filePath); !os.IsNotExist(err) {
		localFile := filepath.Join("content", fileName)
		return fmt.Errorf("Please remove '%s' before executing this command", localFile)
	}

	// parse type info from args
	gt, err := parseType(args)
	if err != nil {
		return fmt.Errorf("Failed to parse type args: %s", err.Error())
	}

	tmplPath := filepath.Join(pwd, "cmd", "ponzu", "templates", "gen-content.tmpl")
	tmpl, err := template.ParseFiles(tmplPath)
	if err != nil {
		return fmt.Errorf("Failed to parse template: %s", err.Error())
	}

	buf := &bytes.Buffer{}
	err = tmpl.Execute(buf, gt)
	if err != nil {
		return fmt.Errorf("Failed to execute template: %s", err.Error())
	}

	fmtBuf, err := format.Source(buf.Bytes())
	if err != nil {
		return fmt.Errorf("Failed to format template: %s", err.Error())
	}

	// no file exists.. ok to write new one
	file, err := os.Create(filePath)
	defer file.Close()
	if err != nil {
		return err
	}

	_, err = file.Write(fmtBuf)
	if err != nil {
		return fmt.Errorf("Failed to write generated file buffer: %s", err.Error())
	}

	return nil
}
<|endoftext|>


<commit_before>package main

//go:generate esc -o templates.go ../../templates

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/fsnotify/fsnotify"
	"github.com/subosito/snowboard"
	"github.com/subosito/snowboard/engines/drafter"
	"github.com/subosito/snowboard/engines/drafterc"
)

const versionStr = "v0.2.0"

var (
	version  = flag.Bool("v", false, "Display version information")
	input    = flag.String("i", "", "API Blueprint file")
	output   = flag.String("o", "index.html", "HTML output file")
	watch    = flag.Bool("w", false, "Watch input (and template, if any) file for changes")
	serve    = flag.Bool("s", false, "Serve HTML via 0.0.0.0:8088")
	tplFile  = flag.String("t", "alpha", "Custom template for documentation")
	engineF  = flag.String("e", "cgo", "Use different engine. Supported engines: cgo, cli")
	validate = flag.Bool("l", false, "Validate input only")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage:\n  snowboard [OPTIONS]\n\nOptions:\n")
		flag.PrintDefaults()
		os.Exit(0)
	}

	flag.Parse()

	engine := parserEngine()

	if *version {
		fmt.Printf("Snowboard version: %s\n", versionStr)
		fmt.Println("Engine:")

		for name, v := range engine.Version() {
			fmt.Printf("  %s version: %s\n", name, v)
		}

		os.Exit(0)
	}

	if *input == "" {
		flag.Usage()
	}

	if *validate {
		cEngine := checkerEngine()

		b, err := readFile(*input)
		checkErr(err)

		bf := bytes.NewReader(b)

		out, err := snowboard.Validate(bf, cEngine)
		if err == nil && out == nil {
			fmt.Fprintf(os.Stdout, "OK\n")
			os.Exit(0)
		}

		if err != nil {
			checkErr(err)
		}

		s := "--------"
		w := tabwriter.NewWriter(os.Stdout, 8, 0, 0, ' ', tabwriter.Debug)
		fmt.Fprintln(w, "Row\tCol\tDescription")
		fmt.Fprintf(w, "%s\t%s\t%s\n", s, s, strings.Repeat(s, 15))

		for _, n := range out.Annotations {
			for _, m := range n.SourceMaps {
				fmt.Fprintf(w, "%d\t%d\t%s\n", m.Row, m.Col, n.Description)
			}
		}

		w.Flush()
		os.Exit(1)
	}

	if *watch {
		watcher, err := fsnotify.NewWatcher()
		checkErr(err)
		defer watcher.Close()

		done := make(chan bool)
		go func() {
			for {
				select {
				case event := <-watcher.Events:
					if event.Op&fsnotify.Write == fsnotify.Write {
						renderHTML(engine)
					}
				case err := <-watcher.Errors:
					log.Println("Error:", err)
				}
			}
		}()

		err = watcher.Add(*input)
		checkErr(err)

		_, err = os.Stat(*tplFile)
		if err == nil {
			err = watcher.Add(*tplFile)
			checkErr(err)
		}

		renderHTML(engine)
		serveHTML()

		<-done
	} else {
		renderHTML(engine)
		serveHTML()
	}
}

func readFile(fn string) ([]byte, error) {
	info, err := os.Stat(fn)
	if err != nil {
		return nil, errors.New("File is not exist")
	}

	if info.IsDir() {
		return nil, errors.New("File is a directory")
	}

	return ioutil.ReadFile(fn)
}

func readTemplate(fn string) ([]byte, error) {
	tf, err := readFile(fn)
	if err == nil {
		return tf, nil
	}

	fs := FS(false)
	ff, err := fs.Open("/templates/" + fn + ".html")
	if err != nil {
		return nil, err
	}

	defer ff.Close()
	return ioutil.ReadAll(ff)
}

func checkErr(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}
}

func renderHTML(engine snowboard.Parser) {
	b, err := readFile(*input)
	logErr(err)

	log.Println("Generate HTML... START")

	bf := bytes.NewReader(b)
	bp, err := snowboard.Parse(bf, engine)
	logErr(err)

	of, err := os.Create(*output)
	logErr(err)
	defer of.Close()

	tf, err := readTemplate(*tplFile)
	logErr(err)

	err = snowboard.HTML(string(tf), of, bp)
	logErr(err)

	log.Println("Generate HTML... DONE")
}

func logErr(err error) {
	if err != nil {
		log.Fatalln("Error: ", err)
	}
}

func serveHTML() {
	if !*serve {
		return
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, *output)
	})

	err := http.ListenAndServe(":8088", nil)
	logErr(err)
}

func parserEngine() snowboard.Parser {
	switch *engineF {
	case "cli":
		return drafterc.Engine{}
	}

	return drafter.Engine{}
}

func checkerEngine() snowboard.Checker {
	return drafter.Engine{}
}
<commit_msg>bump version<commit_after>package main

//go:generate esc -o templates.go ../../templates

import (
	"bytes"
	"errors"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"strings"
	"text/tabwriter"

	"github.com/fsnotify/fsnotify"
	"github.com/subosito/snowboard"
	"github.com/subosito/snowboard/engines/drafter"
	"github.com/subosito/snowboard/engines/drafterc"
)

const versionStr = "v0.2.1"

var (
	version  = flag.Bool("v", false, "Display version information")
	input    = flag.String("i", "", "API Blueprint file")
	output   = flag.String("o", "index.html", "HTML output file")
	watch    = flag.Bool("w", false, "Watch input (and template, if any) file for changes")
	serve    = flag.Bool("s", false, "Serve HTML via 0.0.0.0:8088")
	tplFile  = flag.String("t", "alpha", "Custom template for documentation")
	engineF  = flag.String("e", "cgo", "Use different engine. Supported engines: cgo, cli")
	validate = flag.Bool("l", false, "Validate input only")
)

func main() {
	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage:\n  snowboard [OPTIONS]\n\nOptions:\n")
		flag.PrintDefaults()
		os.Exit(0)
	}

	flag.Parse()

	engine := parserEngine()

	if *version {
		fmt.Printf("Snowboard version: %s\n", versionStr)
		fmt.Println("Engine:")

		for name, v := range engine.Version() {
			fmt.Printf("  %s version: %s\n", name, v)
		}

		os.Exit(0)
	}

	if *input == "" {
		flag.Usage()
	}

	if *validate {
		cEngine := checkerEngine()

		b, err := readFile(*input)
		checkErr(err)

		bf := bytes.NewReader(b)

		out, err := snowboard.Validate(bf, cEngine)
		if err == nil && out == nil {
			fmt.Fprintf(os.Stdout, "OK\n")
			os.Exit(0)
		}

		if err != nil {
			checkErr(err)
		}

		s := "--------"
		w := tabwriter.NewWriter(os.Stdout, 8, 0, 0, ' ', tabwriter.Debug)
		fmt.Fprintln(w, "Row\tCol\tDescription")
		fmt.Fprintf(w, "%s\t%s\t%s\n", s, s, strings.Repeat(s, 15))

		for _, n := range out.Annotations {
			for _, m := range n.SourceMaps {
				fmt.Fprintf(w, "%d\t%d\t%s\n", m.Row, m.Col, n.Description)
			}
		}

		w.Flush()
		os.Exit(1)
	}

	if *watch {
		watcher, err := fsnotify.NewWatcher()
		checkErr(err)
		defer watcher.Close()

		done := make(chan bool)
		go func() {
			for {
				select {
				case event := <-watcher.Events:
					if event.Op&fsnotify.Write == fsnotify.Write {
						renderHTML(engine)
					}
				case err := <-watcher.Errors:
					log.Println("Error:", err)
				}
			}
		}()

		err = watcher.Add(*input)
		checkErr(err)

		_, err = os.Stat(*tplFile)
		if err == nil {
			err = watcher.Add(*tplFile)
			checkErr(err)
		}

		renderHTML(engine)
		serveHTML()

		<-done
	} else {
		renderHTML(engine)
		serveHTML()
	}
}

func readFile(fn string) ([]byte, error) {
	info, err := os.Stat(fn)
	if err != nil {
		return nil, errors.New("File is not exist")
	}

	if info.IsDir() {
		return nil, errors.New("File is a directory")
	}

	return ioutil.ReadFile(fn)
}

func readTemplate(fn string) ([]byte, error) {
	tf, err := readFile(fn)
	if err == nil {
		return tf, nil
	}

	fs := FS(false)
	ff, err := fs.Open("/templates/" + fn + ".html")
	if err != nil {
		return nil, err
	}

	defer ff.Close()
	return ioutil.ReadAll(ff)
}

func checkErr(err error) {
	if err != nil {
		fmt.Fprintf(os.Stderr, "Error: %s\n", err)
		os.Exit(1)
	}
}

func renderHTML(engine snowboard.Parser) {
	b, err := readFile(*input)
	logErr(err)

	log.Println("Generate HTML... START")

	bf := bytes.NewReader(b)
	bp, err := snowboard.Parse(bf, engine)
	logErr(err)

	of, err := os.Create(*output)
	logErr(err)
	defer of.Close()

	tf, err := readTemplate(*tplFile)
	logErr(err)

	err = snowboard.HTML(string(tf), of, bp)
	logErr(err)

	log.Println("Generate HTML... DONE")
}

func logErr(err error) {
	if err != nil {
		log.Fatalln("Error: ", err)
	}
}

func serveHTML() {
	if !*serve {
		return
	}

	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, *output)
	})

	err := http.ListenAndServe(":8088", nil)
	logErr(err)
}

func parserEngine() snowboard.Parser {
	switch *engineF {
	case "cli":
		return drafterc.Engine{}
	}

	return drafter.Engine{}
}

func checkerEngine() snowboard.Checker {
	return drafter.Engine{}
}
<|endoftext|>


<commit_before>package cli

import (
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/tus/tusd/pkg/handler"
)

// Setups the different components, starts a Listener and give it to
// http.Serve().
//
// By default it will bind to the specified host/port, unless a UNIX socket is
// specified, in which case a different socket creation and binding mechanism
// is put in place.
func Serve() {
	config := handler.Config{
		MaxSize:                 Flags.MaxSize,
		BasePath:                Flags.Basepath,
		RespectForwardedHeaders: Flags.BehindProxy,
		StoreComposer:           Composer,
		NotifyCompleteUploads:   true,
		NotifyTerminatedUploads: true,
		NotifyUploadProgress:    true,
		NotifyCreatedUploads:    true,
	}

	if err := SetupPreHooks(&config); err != nil {
		stderr.Fatalf("Unable to setup hooks for handler: %s", err)
	}

	handler, err := handler.NewHandler(config)
	if err != nil {
		stderr.Fatalf("Unable to create handler: %s", err)
	}

	basepath := Flags.Basepath
	address := ""

	if Flags.HttpSock != "" {
		address = Flags.HttpSock
		stdout.Printf("Using %s as socket to listen.\n", address)
	} else {
		address = Flags.HttpHost + ":" + Flags.HttpPort
		stdout.Printf("Using %s as address to listen.\n", address)
	}

	stdout.Printf("Using %s as the base path.\n", basepath)

	SetupPostHooks(handler)

	if Flags.ExposeMetrics {
		SetupMetrics(handler)
		SetupHookMetrics()
	}

	stdout.Printf("Supported tus extensions: %s\n", handler.SupportedExtensions())

	// Do not display the greeting if the tusd handler will be mounted at the root
	// path. Else this would cause a "multiple registrations for /" panic.
	if basepath != "/" {
		http.HandleFunc("/", DisplayGreeting)
	}

	http.Handle(basepath, http.StripPrefix(basepath, handler))

	// Also register a route without the trailing slash, so we can handle uploads
	// for /files/ and /files, for example.
	if strings.HasSuffix(basepath, "/") {
		basepathWithoutSlash := strings.TrimSuffix(basepath, "/")
		http.Handle(basepathWithoutSlash, http.StripPrefix(basepathWithoutSlash, handler))
	}

	var listener net.Listener
	timeoutDuration := time.Duration(Flags.Timeout) * time.Millisecond

	if Flags.HttpSock != "" {
		listener, err = NewUnixListener(address, timeoutDuration, timeoutDuration)
	} else {
		listener, err = NewListener(address, timeoutDuration, timeoutDuration)
	}

	if err != nil {
		stderr.Fatalf("Unable to create listener: %s", err)
	}

	if Flags.HttpSock == "" {
		stdout.Printf("You can now upload files to: http://%s%s", address, basepath)
	}

	if err = http.Serve(listener, nil); err != nil {
		stderr.Fatalf("Unable to serve: %s", err)
	}
}
<commit_msg>cli: Fix error when setting basepath to /<commit_after>package cli

import (
	"net"
	"net/http"
	"strings"
	"time"

	"github.com/tus/tusd/pkg/handler"
)

// Setups the different components, starts a Listener and give it to
// http.Serve().
//
// By default it will bind to the specified host/port, unless a UNIX socket is
// specified, in which case a different socket creation and binding mechanism
// is put in place.
func Serve() {
	config := handler.Config{
		MaxSize:                 Flags.MaxSize,
		BasePath:                Flags.Basepath,
		RespectForwardedHeaders: Flags.BehindProxy,
		StoreComposer:           Composer,
		NotifyCompleteUploads:   true,
		NotifyTerminatedUploads: true,
		NotifyUploadProgress:    true,
		NotifyCreatedUploads:    true,
	}

	if err := SetupPreHooks(&config); err != nil {
		stderr.Fatalf("Unable to setup hooks for handler: %s", err)
	}

	handler, err := handler.NewHandler(config)
	if err != nil {
		stderr.Fatalf("Unable to create handler: %s", err)
	}

	basepath := Flags.Basepath
	address := ""

	if Flags.HttpSock != "" {
		address = Flags.HttpSock
		stdout.Printf("Using %s as socket to listen.\n", address)
	} else {
		address = Flags.HttpHost + ":" + Flags.HttpPort
		stdout.Printf("Using %s as address to listen.\n", address)
	}

	stdout.Printf("Using %s as the base path.\n", basepath)

	SetupPostHooks(handler)

	if Flags.ExposeMetrics {
		SetupMetrics(handler)
		SetupHookMetrics()
	}

	stdout.Printf("Supported tus extensions: %s\n", handler.SupportedExtensions())

	if basepath == "/" {
		// If the basepath is set to the root path, only install the tusd handler
		// and do not show a greeting.
		http.Handle("/", handler)
	} else {
		// If a custom basepath is defined, we show a greeting at the root path...
		http.HandleFunc("/", DisplayGreeting)

		// ... and register a route with and without the trailing slash, so we can
		// handle uploads for /files/ and /files, for example.
		basepathWithoutSlash := strings.TrimSuffix(basepath, "/")
		basepathWithSlash := basepathWithoutSlash + "/"

		http.Handle(basepathWithSlash, http.StripPrefix(basepathWithSlash, handler))
		http.Handle(basepathWithoutSlash, http.StripPrefix(basepathWithoutSlash, handler))
	}

	var listener net.Listener
	timeoutDuration := time.Duration(Flags.Timeout) * time.Millisecond

	if Flags.HttpSock != "" {
		listener, err = NewUnixListener(address, timeoutDuration, timeoutDuration)
	} else {
		listener, err = NewListener(address, timeoutDuration, timeoutDuration)
	}

	if err != nil {
		stderr.Fatalf("Unable to create listener: %s", err)
	}

	if Flags.HttpSock == "" {
		stdout.Printf("You can now upload files to: http://%s%s", address, basepath)
	}

	if err = http.Serve(listener, nil); err != nil {
		stderr.Fatalf("Unable to serve: %s", err)
	}
}
<|endoftext|>


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

package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/banzaicloud/bank-vaults/pkg/sdk/vault"
	vaultapi "github.com/hashicorp/vault/api"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cast"
)

// The special value for VAULT_ENV which marks that the login token needs to be passed through to the application
// which was acquired during the new Vault client creation
const vaultLogin = "vault:login"

type sanitizedEnviron []string

var sanitizeEnvmap = map[string]bool{
	"VAULT_TOKEN":                  true,
	"VAULT_ADDR":                   true,
	"VAULT_CACERT":                 true,
	"VAULT_CAPATH":                 true,
	"VAULT_CLIENT_CERT":            true,
	"VAULT_CLIENT_KEY":             true,
	"VAULT_CLIENT_TIMEOUT":         true,
	"VAULT_CLUSTER_ADDR":           true,
	"VAULT_MAX_RETRIES":            true,
	"VAULT_REDIRECT_ADDR":          true,
	"VAULT_SKIP_VERIFY":            true,
	"VAULT_TLS_SERVER_NAME":        true,
	"VAULT_CLI_NO_COLOR":           true,
	"VAULT_RATE_LIMIT":             true,
	"VAULT_NAMESPACE":              true,
	"VAULT_MFA":                    true,
	"VAULT_ROLE":                   true,
	"VAULT_PATH":                   true,
	"VAULT_IGNORE_MISSING_SECRETS": true,
	"VAULT_ENV_PASSTHROUGH":        true,
	"VAULT_JSON_LOG":               true,
}

var logger *log.Logger

// GlobalHook struct used for adding additional fields to the log
type GlobalHook struct {
}

// Levels returning all log levels
func (h *GlobalHook) Levels() []log.Level {
	return log.AllLevels
}

// Fire adding the additional fields to all log entries
func (h *GlobalHook) Fire(e *log.Entry) error {
	e.Data["app"] = "vault-env"
	return nil
}

// Appends variable an entry (name=value) into the environ list.
// VAULT_* variables are not populated into this list.
func (environ *sanitizedEnviron) append(iname interface{}, ivalue interface{}) {
	name, value := iname.(string), ivalue.(string)
	if _, ok := sanitizeEnvmap[name]; !ok {
		*environ = append(*environ, fmt.Sprintf("%s=%s", name, value))
	}
}

func main() {
	enableJSONLog := os.Getenv("VAULT_JSON_LOG")

	logger = log.New()
	// Add additonal fields to all log messages
	logger.AddHook(&GlobalHook{})
	if enableJSONLog == "true" {
		logger.SetFormatter(&log.JSONFormatter{})
	}

	ignoreMissingSecrets := os.Getenv("VAULT_IGNORE_MISSING_SECRETS") == "true"

	// The login procedure takes the token from a file (if using Vault Agent)
	// or requests one for itself (Kubernetes Auth), so if we got a VAULT_TOKEN
	// for the special value with "vault:login"
	originalVaultTokenEnvVar := os.Getenv("VAULT_TOKEN")
	if originalVaultTokenEnvVar == vaultLogin {
		os.Unsetenv("VAULT_TOKEN")
	}

	client, err := vault.NewClientWithOptions(
		vault.ClientRole(os.Getenv("VAULT_ROLE")),
		vault.ClientAuthPath(os.Getenv("VAULT_PATH")),
	)
	if err != nil {
		logger.Fatal("failed to create vault client", err.Error())
	}

	passthroughEnvVars := strings.Split(os.Getenv("VAULT_ENV_PASSTHROUGH"), ",")

	if originalVaultTokenEnvVar == vaultLogin {
		os.Setenv("VAULT_TOKEN", vaultLogin)
		passthroughEnvVars = append(passthroughEnvVars, "VAULT_TOKEN")
	}

	// do not sanitize env vars specified in VAULT_ENV_PASSTHROUGH
	for _, envVar := range passthroughEnvVars {
		if trimmed := strings.TrimSpace(envVar); trimmed != "" {
			delete(sanitizeEnvmap, trimmed)
		}
	}

	secretCache := map[string]*vaultapi.Secret{}

	// initial and sanitized environs
	environ := syscall.Environ()
	sanitized := make(sanitizedEnviron, 0, len(environ))

	for _, env := range environ {
		split := strings.SplitN(env, "=", 2)
		name := split[0]
		value := split[1]
		var update bool
		if strings.HasPrefix(value, ">>") {
			value = strings.TrimPrefix(value, ">>")
			update = true
		} else {
			update = false
		}
		if strings.HasPrefix(value, "vault:") {
			path := strings.TrimPrefix(value, "vault:")

			// handle special case for vault:login env value
			// namely pass through the the VAULT_TOKEN received from the Vault login procedure
			if name == "VAULT_TOKEN" && path == "login" {
				value = client.RawClient().Token()
				sanitized.append(name, value)
				continue
			}

			split := strings.SplitN(path, "#", 3)
			path = split[0]

			var key string
			if len(split) > 1 {
				key = split[1]
			}

			version := "-1"
			if len(split) == 3 {
				version = split[2]
			}

			var secret *vaultapi.Secret
			var err error

			if secret = secretCache[path]; secret == nil {
				if update {
					secret, err = client.RawClient().Logical().Write(path, map[string]interface{}{})
					if err != nil {
						logger.Fatalln("failed to write secret to path:", path, err.Error())
					} else {
						secretCache[path] = secret
					}
				} else {
					secret, err = client.RawClient().Logical().ReadWithData(path, map[string][]string{"version": {version}})
					if err != nil {
						if ignoreMissingSecrets {
							logger.Errorln("failed to read secret from path:", path, err.Error())
						} else {
							logger.Fatalln("failed to read secret from path:", path, err.Error())
						}
					} else {
						secretCache[path] = secret
					}
				}
			}

			if secret == nil {
				if ignoreMissingSecrets {
					logger.Warnln("path not found:", path)
				} else {
					logger.Fatalln("path not found:", path)
				}
			} else {
				var data map[string]interface{}
				v2Data, ok := secret.Data["data"]
				if ok {
					data = cast.ToStringMap(v2Data)

					// Check if a given version of a path is destroyed
					metadata := secret.Data["metadata"].(map[string]interface{})
					if metadata["destroyed"].(bool) {
						logger.Warnln("version of secret has been permanently destroyed version:", version, "path:", path)
					}

					// Check if a given version of a path still exists
					if deletionTime, ok := metadata["deletion_time"].(string); ok && deletionTime != "" {
						logger.Warnln("cannot find data for path, given version has been deleted",
							"path:", path, "version:", version,
							"deletion_time", deletionTime)
					}
				} else {
					data = cast.ToStringMap(secret.Data)
				}
				if value, ok := data[key]; ok {
					sanitized.append(name, value)
				} else {
					logger.Fatalln("key not found:", key)
				}
			}
		} else {
			sanitized.append(name, value)
		}
	}

	var entrypointCmd []string
	if len(os.Args) == 1 {
		logger.Fatalln("no command is given, vault-env can't determine the entrypoint (command), please specify it explicitly or let the webhook query it (see documentation)")
	} else {
		entrypointCmd = os.Args[1:]
	}
	binary, err := exec.LookPath(entrypointCmd[0])
	if err != nil {
		logger.Fatalln("binary not found", entrypointCmd[0])
	}
	err = syscall.Exec(binary, entrypointCmd, sanitized)
	if err != nil {
		logger.Fatalln("failed to exec process", binary, entrypointCmd, err.Error())
	}
}
<commit_msg>[vault-env] Revoke Vault token<commit_after>// Copyright © 2018 Banzai Cloud
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"
	"os/exec"
	"strings"
	"syscall"

	"github.com/banzaicloud/bank-vaults/pkg/sdk/vault"
	vaultapi "github.com/hashicorp/vault/api"
	log "github.com/sirupsen/logrus"
	"github.com/spf13/cast"
)

// The special value for VAULT_ENV which marks that the login token needs to be passed through to the application
// which was acquired during the new Vault client creation
const vaultLogin = "vault:login"

type sanitizedEnviron []string

var sanitizeEnvmap = map[string]bool{
	"VAULT_TOKEN":                  true,
	"VAULT_ADDR":                   true,
	"VAULT_CACERT":                 true,
	"VAULT_CAPATH":                 true,
	"VAULT_CLIENT_CERT":            true,
	"VAULT_CLIENT_KEY":             true,
	"VAULT_CLIENT_TIMEOUT":         true,
	"VAULT_CLUSTER_ADDR":           true,
	"VAULT_MAX_RETRIES":            true,
	"VAULT_REDIRECT_ADDR":          true,
	"VAULT_SKIP_VERIFY":            true,
	"VAULT_TLS_SERVER_NAME":        true,
	"VAULT_CLI_NO_COLOR":           true,
	"VAULT_RATE_LIMIT":             true,
	"VAULT_NAMESPACE":              true,
	"VAULT_MFA":                    true,
	"VAULT_ROLE":                   true,
	"VAULT_PATH":                   true,
	"VAULT_IGNORE_MISSING_SECRETS": true,
	"VAULT_ENV_PASSTHROUGH":        true,
	"VAULT_JSON_LOG":               true,
}

var logger *log.Logger

// GlobalHook struct used for adding additional fields to the log
type GlobalHook struct {
}

// Levels returning all log levels
func (h *GlobalHook) Levels() []log.Level {
	return log.AllLevels
}

// Fire adding the additional fields to all log entries
func (h *GlobalHook) Fire(e *log.Entry) error {
	e.Data["app"] = "vault-env"
	return nil
}

// Appends variable an entry (name=value) into the environ list.
// VAULT_* variables are not populated into this list.
func (environ *sanitizedEnviron) append(iname interface{}, ivalue interface{}) {
	name, value := iname.(string), ivalue.(string)
	if _, ok := sanitizeEnvmap[name]; !ok {
		*environ = append(*environ, fmt.Sprintf("%s=%s", name, value))
	}
}

func main() {
	enableJSONLog := os.Getenv("VAULT_JSON_LOG")

	logger = log.New()
	// Add additonal fields to all log messages
	logger.AddHook(&GlobalHook{})
	if enableJSONLog == "true" {
		logger.SetFormatter(&log.JSONFormatter{})
	}

	var entrypointCmd []string
	if len(os.Args) == 1 {
		logger.Fatalln("no command is given, vault-env can't determine the entrypoint (command), please specify it explicitly or let the webhook query it (see documentation)")
	} else {
		entrypointCmd = os.Args[1:]
	}

	binary, err := exec.LookPath(entrypointCmd[0])
	if err != nil {
		logger.Fatalln("binary not found", entrypointCmd[0])
	}

	ignoreMissingSecrets := os.Getenv("VAULT_IGNORE_MISSING_SECRETS") == "true"

	// The login procedure takes the token from a file (if using Vault Agent)
	// or requests one for itself (Kubernetes Auth), so if we got a VAULT_TOKEN
	// for the special value with "vault:login"
	originalVaultTokenEnvVar := os.Getenv("VAULT_TOKEN")
	if originalVaultTokenEnvVar == vaultLogin {
		os.Unsetenv("VAULT_TOKEN")
	}

	client, err := vault.NewClientWithOptions(
		vault.ClientRole(os.Getenv("VAULT_ROLE")),
		vault.ClientAuthPath(os.Getenv("VAULT_PATH")),
	)
	if err != nil {
		logger.Fatal("failed to create vault client", err.Error())
	}

	passthroughEnvVars := strings.Split(os.Getenv("VAULT_ENV_PASSTHROUGH"), ",")

	if originalVaultTokenEnvVar == vaultLogin {
		os.Setenv("VAULT_TOKEN", vaultLogin)
		passthroughEnvVars = append(passthroughEnvVars, "VAULT_TOKEN")
	}

	// do not sanitize env vars specified in VAULT_ENV_PASSTHROUGH
	for _, envVar := range passthroughEnvVars {
		if trimmed := strings.TrimSpace(envVar); trimmed != "" {
			delete(sanitizeEnvmap, trimmed)
		}
	}

	secretCache := map[string]*vaultapi.Secret{}

	// initial and sanitized environs
	environ := syscall.Environ()
	sanitized := make(sanitizedEnviron, 0, len(environ))

	for _, env := range environ {
		split := strings.SplitN(env, "=", 2)
		name := split[0]
		value := split[1]
		var update bool
		if strings.HasPrefix(value, ">>") {
			value = strings.TrimPrefix(value, ">>")
			update = true
		} else {
			update = false
		}
		if strings.HasPrefix(value, "vault:") {
			path := strings.TrimPrefix(value, "vault:")

			// handle special case for vault:login env value
			// namely pass through the the VAULT_TOKEN received from the Vault login procedure
			if name == "VAULT_TOKEN" && path == "login" {
				value = client.RawClient().Token()
				sanitized.append(name, value)
				continue
			}

			split := strings.SplitN(path, "#", 3)
			path = split[0]

			var key string
			if len(split) > 1 {
				key = split[1]
			}

			version := "-1"
			if len(split) == 3 {
				version = split[2]
			}

			var secret *vaultapi.Secret
			var err error

			if secret = secretCache[path]; secret == nil {
				if update {
					secret, err = client.RawClient().Logical().Write(path, map[string]interface{}{})
					if err != nil {
						logger.Fatalln("failed to write secret to path:", path, err.Error())
					} else {
						secretCache[path] = secret
					}
				} else {
					secret, err = client.RawClient().Logical().ReadWithData(path, map[string][]string{"version": {version}})
					if err != nil {
						if ignoreMissingSecrets {
							logger.Errorln("failed to read secret from path:", path, err.Error())
						} else {
							logger.Fatalln("failed to read secret from path:", path, err.Error())
						}
					} else {
						secretCache[path] = secret
					}
				}
			}

			if secret == nil {
				if ignoreMissingSecrets {
					logger.Warnln("path not found:", path)
				} else {
					logger.Fatalln("path not found:", path)
				}
			} else {
				var data map[string]interface{}
				v2Data, ok := secret.Data["data"]
				if ok {
					data = cast.ToStringMap(v2Data)

					// Check if a given version of a path is destroyed
					metadata := secret.Data["metadata"].(map[string]interface{})
					if metadata["destroyed"].(bool) {
						logger.Warnln("version of secret has been permanently destroyed version:", version, "path:", path)
					}

					// Check if a given version of a path still exists
					if deletionTime, ok := metadata["deletion_time"].(string); ok && deletionTime != "" {
						logger.Warnln("cannot find data for path, given version has been deleted",
							"path:", path, "version:", version,
							"deletion_time", deletionTime)
					}
				} else {
					data = cast.ToStringMap(secret.Data)
				}
				if value, ok := data[key]; ok {
					sanitized.append(name, value)
				} else {
					logger.Fatalln("key not found:", key)
				}
			}
		} else {
			sanitized.append(name, value)
		}
	}

	err = client.RawClient().Auth().Token().RevokeSelf(client.RawClient().Token())
	if err != nil {
		logger.Warnln("failed to revoke token")
	}

	err = syscall.Exec(binary, entrypointCmd, sanitized)
	if err != nil {
		logger.Fatalln("failed to exec process", binary, entrypointCmd, err.Error())
	}
}
<|endoftext|>