<commit_before>/*
   Copyright 2016 GitHub Inc.
	 See https://github.com/github/gh-ost/blob/master/LICENSE
*/

package logic

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"sync/atomic"

	"github.com/github/gh-ost/go/base"
	"github.com/outbrain/golib/log"
)

type printStatusFunc func(PrintStatusRule, io.Writer)

// Server listens for requests on a socket file or via TCP
type Server struct {
	migrationContext *base.MigrationContext
	unixListener     net.Listener
	tcpListener      net.Listener
	hooksExecutor    *HooksExecutor
	printStatus      printStatusFunc
}

func NewServer(migrationContext *base.MigrationContext, hooksExecutor *HooksExecutor, printStatus printStatusFunc) *Server {
	return &Server{
		migrationContext: migrationContext,
		hooksExecutor:    hooksExecutor,
		printStatus:      printStatus,
	}
}

func (this *Server) BindSocketFile() (err error) {
	if this.migrationContext.ServeSocketFile == "" {
		return nil
	}
	if this.migrationContext.DropServeSocket && base.FileExists(this.migrationContext.ServeSocketFile) {
		os.Remove(this.migrationContext.ServeSocketFile)
	}
	this.unixListener, err = net.Listen("unix", this.migrationContext.ServeSocketFile)
	if err != nil {
		return err
	}
	log.Infof("Listening on unix socket file: %s", this.migrationContext.ServeSocketFile)
	return nil
}

func (this *Server) RemoveSocketFile() (err error) {
	log.Infof("Removing socket file: %s", this.migrationContext.ServeSocketFile)
	return os.Remove(this.migrationContext.ServeSocketFile)
}

func (this *Server) BindTCPPort() (err error) {
	if this.migrationContext.ServeTCPPort == 0 {
		return nil
	}
	this.tcpListener, err = net.Listen("tcp", fmt.Sprintf(":%d", this.migrationContext.ServeTCPPort))
	if err != nil {
		return err
	}
	log.Infof("Listening on tcp port: %d", this.migrationContext.ServeTCPPort)
	return nil
}

// Serve begins listening & serving on whichever device was configured
func (this *Server) Serve() (err error) {
	go func() {
		for {
			conn, err := this.unixListener.Accept()
			if err != nil {
				log.Errore(err)
			}
			go this.handleConnection(conn)
		}
	}()
	go func() {
		if this.tcpListener == nil {
			return
		}
		for {
			conn, err := this.tcpListener.Accept()
			if err != nil {
				log.Errore(err)
			}
			go this.handleConnection(conn)
		}
	}()

	return nil
}

func (this *Server) handleConnection(conn net.Conn) (err error) {
	if conn != nil {
		defer conn.Close()
	}
	command, _, err := bufio.NewReader(conn).ReadLine()
	if err != nil {
		return err
	}
	return this.onServerCommand(string(command), bufio.NewWriter(conn))
}

// onServerCommand responds to a user's interactive command
func (this *Server) onServerCommand(command string, writer *bufio.Writer) (err error) {
	defer writer.Flush()

	printStatusRule, err := this.applyServerCommand(command, writer)
	if err == nil {
		this.printStatus(printStatusRule, writer)
	} else {
		fmt.Fprintf(writer, "%s\n", err.Error())
	}
	return log.Errore(err)
}

// applyServerCommand parses and executes commands by user
func (this *Server) applyServerCommand(command string, writer *bufio.Writer) (printStatusRule PrintStatusRule, err error) {
	printStatusRule = NoPrintStatusRule

	tokens := strings.SplitN(command, "=", 2)
	command = strings.TrimSpace(tokens[0])
	arg := ""
	if len(tokens) > 1 {
		arg = strings.TrimSpace(tokens[1])
	}
	argIsQuestion := (arg == "?")
	throttleHint := "# Note: you may only throttle for as long as your binary logs are not purged\n"

	if err := this.hooksExecutor.onInteractiveCommand(command); err != nil {
		return NoPrintStatusRule, err
	}

	switch command {
	case "help":
		{
			fmt.Fprintln(writer, `available commands:
status                               # Print a detailed status message
sup                                  # Print a short status message
coordinates													 # Print the currently inspected coordinates
chunk-size=<newsize>                 # Set a new chunk-size
dml-batch-size=<newsize>             # Set a new dml-batch-size
nice-ratio=<ratio>                   # Set a new nice-ratio, immediate sleep after each row-copy operation, float (examples: 0 is aggressive, 0.7 adds 70% runtime, 1.0 doubles runtime, 2.0 triples runtime, ...)
critical-load=<load>                 # Set a new set of max-load thresholds
max-lag-millis=<max-lag>             # Set a new replication lag threshold
replication-lag-query=<query>        # Set a new query that determines replication lag (no quotes)
max-load=<load>                      # Set a new set of max-load thresholds
throttle-query=<query>               # Set a new throttle-query (no quotes)
throttle-http=<URL>                  # Set a new throttle URL
throttle-control-replicas=<replicas> # Set a new comma delimited list of throttle control replicas
throttle                             # Force throttling
no-throttle                          # End forced throttling (other throttling may still apply)
unpostpone                           # Bail out a cut-over postpone; proceed to cut-over
panic                                # panic and quit without cleanup
help                                 # This message
- use '?' (question mark) as argument to get info rather than set. e.g. "max-load=?" will just print out current max-load.
`)
		}
	case "sup":
		return ForcePrintStatusOnlyRule, nil
	case "info", "status":
		return ForcePrintStatusAndHintRule, nil
	case "coordinates":
		{
			if argIsQuestion || arg == "" {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetRecentBinlogCoordinates())
				return NoPrintStatusRule, nil
			}
			return NoPrintStatusRule, fmt.Errorf("coordinates are read-only")
		}
	case "chunk-size":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", atomic.LoadInt64(&this.migrationContext.ChunkSize))
				return NoPrintStatusRule, nil
			}
			if chunkSize, err := strconv.Atoi(arg); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetChunkSize(int64(chunkSize))
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "dml-batch-size":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", atomic.LoadInt64(&this.migrationContext.DMLBatchSize))
				return NoPrintStatusRule, nil
			}
			if dmlBatchSize, err := strconv.Atoi(arg); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetDMLBatchSize(int64(dmlBatchSize))
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "max-lag-millis":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", atomic.LoadInt64(&this.migrationContext.MaxLagMillisecondsThrottleThreshold))
				return NoPrintStatusRule, nil
			}
			if maxLagMillis, err := strconv.Atoi(arg); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetMaxLagMillisecondsThrottleThreshold(int64(maxLagMillis))
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "replication-lag-query":
		{
			return NoPrintStatusRule, fmt.Errorf("replication-lag-query is deprecated. gh-ost uses an internal, subsecond resolution query")
		}
	case "nice-ratio":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetNiceRatio())
				return NoPrintStatusRule, nil
			}
			if niceRatio, err := strconv.ParseFloat(arg, 64); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetNiceRatio(niceRatio)
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "max-load":
		{
			if argIsQuestion {
				maxLoad := this.migrationContext.GetMaxLoad()
				fmt.Fprintf(writer, "%s\n", maxLoad.String())
				return NoPrintStatusRule, nil
			}
			if err := this.migrationContext.ReadMaxLoad(arg); err != nil {
				return NoPrintStatusRule, err
			}
			return ForcePrintStatusAndHintRule, nil
		}
	case "critical-load":
		{
			if argIsQuestion {
				criticalLoad := this.migrationContext.GetCriticalLoad()
				fmt.Fprintf(writer, "%s\n", criticalLoad.String())
				return NoPrintStatusRule, nil
			}
			if err := this.migrationContext.ReadCriticalLoad(arg); err != nil {
				return NoPrintStatusRule, err
			}
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle-query":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetThrottleQuery())
				return NoPrintStatusRule, nil
			}
			this.migrationContext.SetThrottleQuery(arg)
			fmt.Fprintf(writer, throttleHint)
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle-http":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetThrottleHTTP())
				return NoPrintStatusRule, nil
			}
			this.migrationContext.SetThrottleHTTP(arg)
			fmt.Fprintf(writer, throttleHint)
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle-control-replicas":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%s\n", this.migrationContext.GetThrottleControlReplicaKeys().ToCommaDelimitedList())
				return NoPrintStatusRule, nil
			}
			if err := this.migrationContext.ReadThrottleControlReplicaKeys(arg); err != nil {
				return NoPrintStatusRule, err
			}
			fmt.Fprintf(writer, "%s\n", this.migrationContext.GetThrottleControlReplicaKeys().ToCommaDelimitedList())
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle", "pause", "suspend":
		{
			atomic.StoreInt64(&this.migrationContext.ThrottleCommandedByUser, 1)
			fmt.Fprintf(writer, throttleHint)
			return ForcePrintStatusAndHintRule, nil
		}
	case "no-throttle", "unthrottle", "resume", "continue":
		{
			atomic.StoreInt64(&this.migrationContext.ThrottleCommandedByUser, 0)
			return ForcePrintStatusAndHintRule, nil
		}
	case "unpostpone", "no-postpone", "cut-over":
		{
			if arg == "" && this.migrationContext.ForceNamedCutOverCommand {
				err := fmt.Errorf("User commanded 'unpostpone' without specifying table name, but --force-named-cut-over is set")
				return NoPrintStatusRule, err
			}
			if arg != "" && arg != this.migrationContext.OriginalTableName {
				// User explicitly provided table name. This is a courtesy protection mechanism
				err := fmt.Errorf("User commanded 'unpostpone' on %s, but migrated table is %s; ignoring request.", arg, this.migrationContext.OriginalTableName)
				return NoPrintStatusRule, err
			}
			if atomic.LoadInt64(&this.migrationContext.IsPostponingCutOver) > 0 {
				atomic.StoreInt64(&this.migrationContext.UserCommandedUnpostponeFlag, 1)
				fmt.Fprintf(writer, "Unpostponed\n")
				return ForcePrintStatusAndHintRule, nil
			}
			fmt.Fprintf(writer, "You may only invoke this when gh-ost is actively postponing migration. At this time it is not.\n")
			return NoPrintStatusRule, nil
		}
	case "panic":
		{
			err := fmt.Errorf("User commanded 'panic'. I will now panic, without cleanup. PANIC!")
			this.migrationContext.PanicAbort <- err
			return NoPrintStatusRule, err
		}
	default:
		err = fmt.Errorf("Unknown command: %s", command)
		return NoPrintStatusRule, err
	}
	return NoPrintStatusRule, nil
}
<commit_msg>support quoted values in interactive commands<commit_after>/*
   Copyright 2016 GitHub Inc.
	 See https://github.com/github/gh-ost/blob/master/LICENSE
*/

package logic

import (
	"bufio"
	"fmt"
	"io"
	"net"
	"os"
	"strconv"
	"strings"
	"sync/atomic"

	"github.com/github/gh-ost/go/base"
	"github.com/outbrain/golib/log"
)

type printStatusFunc func(PrintStatusRule, io.Writer)

// Server listens for requests on a socket file or via TCP
type Server struct {
	migrationContext *base.MigrationContext
	unixListener     net.Listener
	tcpListener      net.Listener
	hooksExecutor    *HooksExecutor
	printStatus      printStatusFunc
}

func NewServer(migrationContext *base.MigrationContext, hooksExecutor *HooksExecutor, printStatus printStatusFunc) *Server {
	return &Server{
		migrationContext: migrationContext,
		hooksExecutor:    hooksExecutor,
		printStatus:      printStatus,
	}
}

func (this *Server) BindSocketFile() (err error) {
	if this.migrationContext.ServeSocketFile == "" {
		return nil
	}
	if this.migrationContext.DropServeSocket && base.FileExists(this.migrationContext.ServeSocketFile) {
		os.Remove(this.migrationContext.ServeSocketFile)
	}
	this.unixListener, err = net.Listen("unix", this.migrationContext.ServeSocketFile)
	if err != nil {
		return err
	}
	log.Infof("Listening on unix socket file: %s", this.migrationContext.ServeSocketFile)
	return nil
}

func (this *Server) RemoveSocketFile() (err error) {
	log.Infof("Removing socket file: %s", this.migrationContext.ServeSocketFile)
	return os.Remove(this.migrationContext.ServeSocketFile)
}

func (this *Server) BindTCPPort() (err error) {
	if this.migrationContext.ServeTCPPort == 0 {
		return nil
	}
	this.tcpListener, err = net.Listen("tcp", fmt.Sprintf(":%d", this.migrationContext.ServeTCPPort))
	if err != nil {
		return err
	}
	log.Infof("Listening on tcp port: %d", this.migrationContext.ServeTCPPort)
	return nil
}

// Serve begins listening & serving on whichever device was configured
func (this *Server) Serve() (err error) {
	go func() {
		for {
			conn, err := this.unixListener.Accept()
			if err != nil {
				log.Errore(err)
			}
			go this.handleConnection(conn)
		}
	}()
	go func() {
		if this.tcpListener == nil {
			return
		}
		for {
			conn, err := this.tcpListener.Accept()
			if err != nil {
				log.Errore(err)
			}
			go this.handleConnection(conn)
		}
	}()

	return nil
}

func (this *Server) handleConnection(conn net.Conn) (err error) {
	if conn != nil {
		defer conn.Close()
	}
	command, _, err := bufio.NewReader(conn).ReadLine()
	if err != nil {
		return err
	}
	return this.onServerCommand(string(command), bufio.NewWriter(conn))
}

// onServerCommand responds to a user's interactive command
func (this *Server) onServerCommand(command string, writer *bufio.Writer) (err error) {
	defer writer.Flush()

	printStatusRule, err := this.applyServerCommand(command, writer)
	if err == nil {
		this.printStatus(printStatusRule, writer)
	} else {
		fmt.Fprintf(writer, "%s\n", err.Error())
	}
	return log.Errore(err)
}

// applyServerCommand parses and executes commands by user
func (this *Server) applyServerCommand(command string, writer *bufio.Writer) (printStatusRule PrintStatusRule, err error) {
	printStatusRule = NoPrintStatusRule

	tokens := strings.SplitN(command, "=", 2)
	command = strings.TrimSpace(tokens[0])
	arg := ""
	if len(tokens) > 1 {
		arg = strings.TrimSpace(tokens[1])
		if unquoted, err := strconv.Unquote(arg); err == nil {
			arg = unquoted
		}
	}
	argIsQuestion := (arg == "?")
	throttleHint := "# Note: you may only throttle for as long as your binary logs are not purged\n"

	if err := this.hooksExecutor.onInteractiveCommand(command); err != nil {
		return NoPrintStatusRule, err
	}

	switch command {
	case "help":
		{
			fmt.Fprintln(writer, `available commands:
status                               # Print a detailed status message
sup                                  # Print a short status message
coordinates													 # Print the currently inspected coordinates
chunk-size=<newsize>                 # Set a new chunk-size
dml-batch-size=<newsize>             # Set a new dml-batch-size
nice-ratio=<ratio>                   # Set a new nice-ratio, immediate sleep after each row-copy operation, float (examples: 0 is aggressive, 0.7 adds 70% runtime, 1.0 doubles runtime, 2.0 triples runtime, ...)
critical-load=<load>                 # Set a new set of max-load thresholds
max-lag-millis=<max-lag>             # Set a new replication lag threshold
replication-lag-query=<query>        # Set a new query that determines replication lag (no quotes)
max-load=<load>                      # Set a new set of max-load thresholds
throttle-query=<query>               # Set a new throttle-query (no quotes)
throttle-http=<URL>                  # Set a new throttle URL
throttle-control-replicas=<replicas> # Set a new comma delimited list of throttle control replicas
throttle                             # Force throttling
no-throttle                          # End forced throttling (other throttling may still apply)
unpostpone                           # Bail out a cut-over postpone; proceed to cut-over
panic                                # panic and quit without cleanup
help                                 # This message
- use '?' (question mark) as argument to get info rather than set. e.g. "max-load=?" will just print out current max-load.
`)
		}
	case "sup":
		return ForcePrintStatusOnlyRule, nil
	case "info", "status":
		return ForcePrintStatusAndHintRule, nil
	case "coordinates":
		{
			if argIsQuestion || arg == "" {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetRecentBinlogCoordinates())
				return NoPrintStatusRule, nil
			}
			return NoPrintStatusRule, fmt.Errorf("coordinates are read-only")
		}
	case "chunk-size":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", atomic.LoadInt64(&this.migrationContext.ChunkSize))
				return NoPrintStatusRule, nil
			}
			if chunkSize, err := strconv.Atoi(arg); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetChunkSize(int64(chunkSize))
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "dml-batch-size":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", atomic.LoadInt64(&this.migrationContext.DMLBatchSize))
				return NoPrintStatusRule, nil
			}
			if dmlBatchSize, err := strconv.Atoi(arg); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetDMLBatchSize(int64(dmlBatchSize))
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "max-lag-millis":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", atomic.LoadInt64(&this.migrationContext.MaxLagMillisecondsThrottleThreshold))
				return NoPrintStatusRule, nil
			}
			if maxLagMillis, err := strconv.Atoi(arg); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetMaxLagMillisecondsThrottleThreshold(int64(maxLagMillis))
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "replication-lag-query":
		{
			return NoPrintStatusRule, fmt.Errorf("replication-lag-query is deprecated. gh-ost uses an internal, subsecond resolution query")
		}
	case "nice-ratio":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetNiceRatio())
				return NoPrintStatusRule, nil
			}
			if niceRatio, err := strconv.ParseFloat(arg, 64); err != nil {
				return NoPrintStatusRule, err
			} else {
				this.migrationContext.SetNiceRatio(niceRatio)
				return ForcePrintStatusAndHintRule, nil
			}
		}
	case "max-load":
		{
			if argIsQuestion {
				maxLoad := this.migrationContext.GetMaxLoad()
				fmt.Fprintf(writer, "%s\n", maxLoad.String())
				return NoPrintStatusRule, nil
			}
			if err := this.migrationContext.ReadMaxLoad(arg); err != nil {
				return NoPrintStatusRule, err
			}
			return ForcePrintStatusAndHintRule, nil
		}
	case "critical-load":
		{
			if argIsQuestion {
				criticalLoad := this.migrationContext.GetCriticalLoad()
				fmt.Fprintf(writer, "%s\n", criticalLoad.String())
				return NoPrintStatusRule, nil
			}
			if err := this.migrationContext.ReadCriticalLoad(arg); err != nil {
				return NoPrintStatusRule, err
			}
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle-query":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetThrottleQuery())
				return NoPrintStatusRule, nil
			}
			this.migrationContext.SetThrottleQuery(arg)
			fmt.Fprintf(writer, throttleHint)
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle-http":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%+v\n", this.migrationContext.GetThrottleHTTP())
				return NoPrintStatusRule, nil
			}
			this.migrationContext.SetThrottleHTTP(arg)
			fmt.Fprintf(writer, throttleHint)
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle-control-replicas":
		{
			if argIsQuestion {
				fmt.Fprintf(writer, "%s\n", this.migrationContext.GetThrottleControlReplicaKeys().ToCommaDelimitedList())
				return NoPrintStatusRule, nil
			}
			if err := this.migrationContext.ReadThrottleControlReplicaKeys(arg); err != nil {
				return NoPrintStatusRule, err
			}
			fmt.Fprintf(writer, "%s\n", this.migrationContext.GetThrottleControlReplicaKeys().ToCommaDelimitedList())
			return ForcePrintStatusAndHintRule, nil
		}
	case "throttle", "pause", "suspend":
		{
			atomic.StoreInt64(&this.migrationContext.ThrottleCommandedByUser, 1)
			fmt.Fprintf(writer, throttleHint)
			return ForcePrintStatusAndHintRule, nil
		}
	case "no-throttle", "unthrottle", "resume", "continue":
		{
			atomic.StoreInt64(&this.migrationContext.ThrottleCommandedByUser, 0)
			return ForcePrintStatusAndHintRule, nil
		}
	case "unpostpone", "no-postpone", "cut-over":
		{
			if arg == "" && this.migrationContext.ForceNamedCutOverCommand {
				err := fmt.Errorf("User commanded 'unpostpone' without specifying table name, but --force-named-cut-over is set")
				return NoPrintStatusRule, err
			}
			if arg != "" && arg != this.migrationContext.OriginalTableName {
				// User explicitly provided table name. This is a courtesy protection mechanism
				err := fmt.Errorf("User commanded 'unpostpone' on %s, but migrated table is %s; ignoring request.", arg, this.migrationContext.OriginalTableName)
				return NoPrintStatusRule, err
			}
			if atomic.LoadInt64(&this.migrationContext.IsPostponingCutOver) > 0 {
				atomic.StoreInt64(&this.migrationContext.UserCommandedUnpostponeFlag, 1)
				fmt.Fprintf(writer, "Unpostponed\n")
				return ForcePrintStatusAndHintRule, nil
			}
			fmt.Fprintf(writer, "You may only invoke this when gh-ost is actively postponing migration. At this time it is not.\n")
			return NoPrintStatusRule, nil
		}
	case "panic":
		{
			err := fmt.Errorf("User commanded 'panic'. I will now panic, without cleanup. PANIC!")
			this.migrationContext.PanicAbort <- err
			return NoPrintStatusRule, err
		}
	default:
		err = fmt.Errorf("Unknown command: %s", command)
		return NoPrintStatusRule, err
	}
	return NoPrintStatusRule, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"runtime"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

const (
	helloVersion = "0.5"
)

var knownPaths []string
var boottime time.Time
var banner string
var requests int64
var quota int64
var usr *user.User

func inc() int64 {
	return atomic.AddInt64(&requests, 1)
}

func get() int64 {
	return atomic.LoadInt64(&requests)
}

func getVersion() string {
	return fmt.Sprintf("version=%s runtime=%s pid=%d host=%s GOMAXPROCS=%d", helloVersion, runtime.Version(), os.Getpid(), getHostname(), runtime.GOMAXPROCS(0))
}

func getHostname() string {
	host, errHost := os.Hostname()
	if errHost != nil {
		host = host + " (host error: " + errHost.Error() + ")"
	}
	return host
}

func dumpVersion(path string) {
	log.Printf("dumpVersion: writing to touch=%s", path)
	v := getVersion()
	err := ioutil.WriteFile(path, []byte(v), 0640)
	if err != nil {
		log.Printf("dumpVersion: touch=%s: %v", path, err)
	}
}

func main() {

	boottime = time.Now()

	tls := true

	log.Print(getVersion())

	defaultBanner := "banner default"
	if envBanner := os.Getenv("GWH_BANNER"); envBanner != "" {
		defaultBanner = envBanner
	}

	var errUser error
	usr, errUser = user.Current()
	if errUser != nil {
		log.Printf("current user error: %v", errUser)
	}
	if usr != nil {
		log.Printf("user: %s (uid: %s)", usr.Username, usr.Uid)
	}

	currDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Getwd: %s", err)
	}

	var addr, httpsAddr, key, cert string
	var disableKeepalive bool
	var touch string
	var showVersion bool

	flag.StringVar(&key, "key", "key.pem", "TLS key file")
	flag.StringVar(&cert, "cert", "cert.pem", "TLS cert file")
	flag.StringVar(&addr, "addr", ":8080", "HTTP listen address")
	flag.StringVar(&httpsAddr, "httpsAddr", ":8443", "HTTPS listen address")
	flag.StringVar(&banner, "banner", defaultBanner, "banner will be displayed (env var GWH_BANNER sets default banner)")
	flag.StringVar(&touch, "touch", "", "write version info into this file, if specified")
	flag.BoolVar(&showVersion, "version", false, "does not actually run")
	flag.BoolVar(&disableKeepalive, "disableKeepalive", false, "disable keepalive")
	flag.Int64Var(&quota, "quota", 0, "if defined, service is terminated after serving that amount of requests")
	flag.Parse()

	if touch != "" {
		dumpVersion(touch)
	}

	keepalive := !disableKeepalive

	log.Print("banner: ", banner)
	log.Print("keepalive: ", keepalive)

	if !fileExists(key) {
		log.Printf("TLS key file not found: %s - disabling TLS", key)
		tls = false
	}

	if !fileExists(cert) {
		log.Printf("TLS cert file not found: %s - disabling TLS", cert)
		tls = false
	}

	if showVersion {
		return
	}

	// default handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { rootHandler(w, r, keepalive) })

	registerStatic("/www/", currDir)

	log.Printf("using TCP ports HTTP=%s HTTPS=%s TLS=%v", addr, httpsAddr, tls)

	log.Printf("requests quota: %d (0=unlimited)", quota)

	if tls {
		serveHTTPS(addr, httpsAddr, cert, key, keepalive)
		return
	}

	log.Printf("serving HTTP on TCP %s", addr)
	if err := listenAndServe(addr, nil, keepalive); err != nil {
		log.Fatalf("listenAndServe: %s: %v", addr, err)
	}
}

func serveHTTPS(addr, httpsAddr, cert, key string, keepalive bool) {
	httpPort := "80"
	h := strings.Split(addr, ":")
	if len(h) > 1 {
		httpPort = h[1]
	}

	httpsPort := "443"
	hs := strings.Split(httpsAddr, ":")
	if len(hs) > 1 {
		httpsPort = hs[1]
	}

	if httpPort != httpsPort {
		log.Printf("installing redirect from HTTP=%s to HTTPS=%s", addr, httpsPort)

		redirectTLS := func(w http.ResponseWriter, r *http.Request) {
			host := strings.Split(r.Host, ":")[0]
			//code := http.StatusMovedPermanently // 301 permanent
			code := http.StatusFound // 302 temporary
			http.Redirect(w, r, "https://"+host+":"+httpsPort+r.RequestURI, code)
		}

		// http-to-https redirect server
		go func() {
			if err := listenAndServe(addr, http.HandlerFunc(redirectTLS), keepalive); err != nil {
				log.Fatalf("redirect: listenAndServe: %s: %v", addr, err)
			}
		}()
	}

	log.Printf("serving HTTPS on TCP %s", httpsAddr)
	if err := listenAndServeTLS(httpsAddr, cert, key, nil, keepalive); err != nil {
		log.Fatalf("listenAndServeTLS: %s: %v", httpsAddr, err)
	}
}

func listenAndServe(addr string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServe()
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServeTLS(certFile, keyFile)
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

type staticHandler struct {
	innerHandler http.Handler
}

func registerStatic(path, dir string) {
	http.Handle(path, staticHandler{http.StripPrefix(path, http.FileServer(http.Dir(dir)))})
	knownPaths = append(knownPaths, path)
	log.Printf("mapping www path %s to directory %s", path, dir)
}

func (handler staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	count := inc()
	log.Printf("staticHandler.ServeHTTP req=%d url=%s from=%s", count, r.URL.Path, r.RemoteAddr)
	handler.innerHandler.ServeHTTP(w, r)
	checkQuota("staticHandler.ServeHTTP", count)
}

func checkQuota(label string, count int64) {
	if quota > 0 && count > quota {
		log.Printf("%s req=%d quota=%d EXITING", label, count, quota)
		os.Exit(0)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request, keepalive bool) {
	count := inc()
	log.Printf("rootHandler: req=%d from=%s", count, r.RemoteAddr)
	log.Printf("rootHandler: req=%d method=%s host=%s path=%s", count, r.Method, r.Host, r.URL.Path)
	log.Printf("rootHandler: req=%d query=%s", count, r.URL.RawQuery)

	var paths string
	for _, p := range knownPaths {
		paths += fmt.Sprintf("%s
", p, p)
	}

	var errMsg string
	if r.URL.Path != "/" {
		format := `

Path not found!

Method: %s

Host: %s

Path not found: [%s]

Query: [%s]

`
		errMsg = fmt.Sprintf(format, r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
	}

	header :=
		`<!DOCTYPE html>

    <title>gowebhello root page</title>

gowebhello root page

    https://github.com/udhos/gowebhello is a simple golang replacement for 'python -m SimpleHTTPServer'.

`
	bodyTempl :=
		`
Welcome!

	gowebhello version %s runtime %s

        Keepalive: %v

	Application banner: %s

	Application arguments: %v

	Application dir: %s

	Process: %d

	User: %s (uid: %s)

	Server hostname: %s

	Your address: %s

	Request method=%s host=%s path=[%s] query=[%s]

	Current time: %s

	Uptime: %s

	Requests: %d

    %s

All known paths:

    %s
`

	footer :=
		`

`

	cwd, errCwd := os.Getwd()
	if errCwd != nil {
		cwd = cwd + " (error: " + errCwd.Error() + ")"
	}

	host := getHostname()

	now := time.Now()

	username := "?"
	uid := "?"

	if usr != nil {
		username = usr.Username
		uid = usr.Uid
	}

	body := fmt.Sprintf(bodyTempl, helloVersion, runtime.Version(), keepalive, banner, os.Args, cwd, os.Getpid(), username, uid, host, r.RemoteAddr, r.Method, r.Host, r.URL.Path, r.URL.RawQuery, now, time.Since(boottime), get(), errMsg, paths)

	if !keepalive {
		w.Header().Set("Connection", "close")
	}

	io.WriteString(w, header)
	io.WriteString(w, body)
	showHeaders(w, r)
	showReqBody(w, r)
	io.WriteString(w, footer)

	checkQuota("rootHandler", count)
}

func showReqBody(w http.ResponseWriter, r *http.Request) {

	io.WriteString(w, "
Request Body
\n")

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		m := fmt.Sprintf("request body: %v", err)
		log.Print(m)
		io.WriteString(w, m)
		return
	}

	bodySize := len(buf)

	log.Printf("body size: %d", bodySize)

	io.WriteString(w, fmt.Sprintf("
request body size: %d
\n", bodySize))

	io.WriteString(w, "
\n")
	io.WriteString(w, string(buf))
	io.WriteString(w, "\n")
	io.WriteString(w, "
\n")
}

func showHeaders(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "
Headers
\n")

	var headers []string
	for k, v := range r.Header {
		for _, vv := range v {
			headers = append(headers, k+": "+vv+"
\n")
		}
	}

	sort.Strings(headers)

	for _, h := range headers {
		io.WriteString(w, h)
	}
}
<commit_msg>Display quota.<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/user"
	"runtime"
	"sort"
	"strings"
	"sync/atomic"
	"time"
)

const (
	helloVersion = "0.5"
)

var knownPaths []string
var boottime time.Time
var banner string
var requests int64
var quota int64
var usr *user.User

func inc() int64 {
	return atomic.AddInt64(&requests, 1)
}

func get() int64 {
	return atomic.LoadInt64(&requests)
}

func getVersion() string {
	return fmt.Sprintf("version=%s runtime=%s pid=%d host=%s GOMAXPROCS=%d", helloVersion, runtime.Version(), os.Getpid(), getHostname(), runtime.GOMAXPROCS(0))
}

func getHostname() string {
	host, errHost := os.Hostname()
	if errHost != nil {
		host = host + " (host error: " + errHost.Error() + ")"
	}
	return host
}

func dumpVersion(path string) {
	log.Printf("dumpVersion: writing to touch=%s", path)
	v := getVersion()
	err := ioutil.WriteFile(path, []byte(v), 0640)
	if err != nil {
		log.Printf("dumpVersion: touch=%s: %v", path, err)
	}
}

func main() {

	boottime = time.Now()

	tls := true

	log.Print(getVersion())

	defaultBanner := "banner default"
	if envBanner := os.Getenv("GWH_BANNER"); envBanner != "" {
		defaultBanner = envBanner
	}

	var errUser error
	usr, errUser = user.Current()
	if errUser != nil {
		log.Printf("current user error: %v", errUser)
	}
	if usr != nil {
		log.Printf("user: %s (uid: %s)", usr.Username, usr.Uid)
	}

	currDir, err := os.Getwd()
	if err != nil {
		log.Fatalf("Getwd: %s", err)
	}

	var addr, httpsAddr, key, cert string
	var disableKeepalive bool
	var touch string
	var showVersion bool

	flag.StringVar(&key, "key", "key.pem", "TLS key file")
	flag.StringVar(&cert, "cert", "cert.pem", "TLS cert file")
	flag.StringVar(&addr, "addr", ":8080", "HTTP listen address")
	flag.StringVar(&httpsAddr, "httpsAddr", ":8443", "HTTPS listen address")
	flag.StringVar(&banner, "banner", defaultBanner, "banner will be displayed (env var GWH_BANNER sets default banner)")
	flag.StringVar(&touch, "touch", "", "write version info into this file, if specified")
	flag.BoolVar(&showVersion, "version", false, "does not actually run")
	flag.BoolVar(&disableKeepalive, "disableKeepalive", false, "disable keepalive")
	flag.Int64Var(&quota, "quota", 0, "if defined, service is terminated after serving that amount of requests")
	flag.Parse()

	if touch != "" {
		dumpVersion(touch)
	}

	keepalive := !disableKeepalive

	log.Print("banner: ", banner)
	log.Print("keepalive: ", keepalive)

	if !fileExists(key) {
		log.Printf("TLS key file not found: %s - disabling TLS", key)
		tls = false
	}

	if !fileExists(cert) {
		log.Printf("TLS cert file not found: %s - disabling TLS", cert)
		tls = false
	}

	if showVersion {
		return
	}

	// default handler
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) { rootHandler(w, r, keepalive) })

	registerStatic("/www/", currDir)

	log.Printf("using TCP ports HTTP=%s HTTPS=%s TLS=%v", addr, httpsAddr, tls)

	log.Printf("requests quota: %d (0=unlimited)", quota)

	if tls {
		serveHTTPS(addr, httpsAddr, cert, key, keepalive)
		return
	}

	log.Printf("serving HTTP on TCP %s", addr)
	if err := listenAndServe(addr, nil, keepalive); err != nil {
		log.Fatalf("listenAndServe: %s: %v", addr, err)
	}
}

func serveHTTPS(addr, httpsAddr, cert, key string, keepalive bool) {
	httpPort := "80"
	h := strings.Split(addr, ":")
	if len(h) > 1 {
		httpPort = h[1]
	}

	httpsPort := "443"
	hs := strings.Split(httpsAddr, ":")
	if len(hs) > 1 {
		httpsPort = hs[1]
	}

	if httpPort != httpsPort {
		log.Printf("installing redirect from HTTP=%s to HTTPS=%s", addr, httpsPort)

		redirectTLS := func(w http.ResponseWriter, r *http.Request) {
			host := strings.Split(r.Host, ":")[0]
			//code := http.StatusMovedPermanently // 301 permanent
			code := http.StatusFound // 302 temporary
			http.Redirect(w, r, "https://"+host+":"+httpsPort+r.RequestURI, code)
		}

		// http-to-https redirect server
		go func() {
			if err := listenAndServe(addr, http.HandlerFunc(redirectTLS), keepalive); err != nil {
				log.Fatalf("redirect: listenAndServe: %s: %v", addr, err)
			}
		}()
	}

	log.Printf("serving HTTPS on TCP %s", httpsAddr)
	if err := listenAndServeTLS(httpsAddr, cert, key, nil, keepalive); err != nil {
		log.Fatalf("listenAndServeTLS: %s: %v", httpsAddr, err)
	}
}

func listenAndServe(addr string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServe()
}

func listenAndServeTLS(addr, certFile, keyFile string, handler http.Handler, keepalive bool) error {
	server := &http.Server{Addr: addr, Handler: handler}
	server.SetKeepAlivesEnabled(keepalive)
	return server.ListenAndServeTLS(certFile, keyFile)
}

func fileExists(path string) bool {
	_, err := os.Stat(path)
	return err == nil
}

type staticHandler struct {
	innerHandler http.Handler
}

func registerStatic(path, dir string) {
	http.Handle(path, staticHandler{http.StripPrefix(path, http.FileServer(http.Dir(dir)))})
	knownPaths = append(knownPaths, path)
	log.Printf("mapping www path %s to directory %s", path, dir)
}

func (handler staticHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	count := inc()
	log.Printf("staticHandler.ServeHTTP req=%d url=%s from=%s", count, r.URL.Path, r.RemoteAddr)
	handler.innerHandler.ServeHTTP(w, r)
	checkQuota("staticHandler.ServeHTTP", count)
}

func checkQuota(label string, count int64) {
	if quota > 0 && count > quota {
		log.Printf("%s req=%d quota=%d EXITING", label, count, quota)
		os.Exit(0)
	}
}

func rootHandler(w http.ResponseWriter, r *http.Request, keepalive bool) {
	count := inc()
	log.Printf("rootHandler: req=%d from=%s", count, r.RemoteAddr)
	log.Printf("rootHandler: req=%d method=%s host=%s path=%s", count, r.Method, r.Host, r.URL.Path)
	log.Printf("rootHandler: req=%d query=%s", count, r.URL.RawQuery)

	var paths string
	for _, p := range knownPaths {
		paths += fmt.Sprintf("%s
", p, p)
	}

	var errMsg string
	if r.URL.Path != "/" {
		format := `

Path not found!

Method: %s

Host: %s

Path not found: [%s]

Query: [%s]

`
		errMsg = fmt.Sprintf(format, r.Method, r.Host, r.URL.Path, r.URL.RawQuery)
	}

	header :=
		`<!DOCTYPE html>

    <title>gowebhello root page</title>

gowebhello root page

    https://github.com/udhos/gowebhello is a simple golang replacement for 'python -m SimpleHTTPServer'.

`
	bodyTempl :=
		`
Welcome!

	gowebhello version %s runtime %s

        Keepalive: %v

	Application banner: %s

	Application arguments: %v

	Application dir: %s

	Process: %d

	User: %s (uid: %s)

	Server hostname: %s

	Your address: %s

	Request method=%s host=%s path=[%s] query=[%s]

	Current time: %s

	Uptime: %s

	Requests: %d (Quota: %d)

    %s

All known paths:

    %s
`

	footer :=
		`

`

	cwd, errCwd := os.Getwd()
	if errCwd != nil {
		cwd = cwd + " (error: " + errCwd.Error() + ")"
	}

	host := getHostname()

	now := time.Now()

	username := "?"
	uid := "?"

	if usr != nil {
		username = usr.Username
		uid = usr.Uid
	}

	body := fmt.Sprintf(bodyTempl, helloVersion, runtime.Version(), keepalive, banner, os.Args, cwd, os.Getpid(), username, uid, host, r.RemoteAddr, r.Method, r.Host, r.URL.Path, r.URL.RawQuery, now, time.Since(boottime), get(), quota, errMsg, paths)

	if !keepalive {
		w.Header().Set("Connection", "close")
	}

	io.WriteString(w, header)
	io.WriteString(w, body)
	showHeaders(w, r)
	showReqBody(w, r)
	io.WriteString(w, footer)

	checkQuota("rootHandler", count)
}

func showReqBody(w http.ResponseWriter, r *http.Request) {

	io.WriteString(w, "
Request Body
\n")

	buf, err := ioutil.ReadAll(r.Body)
	if err != nil {
		m := fmt.Sprintf("request body: %v", err)
		log.Print(m)
		io.WriteString(w, m)
		return
	}

	bodySize := len(buf)

	log.Printf("body size: %d", bodySize)

	io.WriteString(w, fmt.Sprintf("
request body size: %d
\n", bodySize))

	io.WriteString(w, "
\n")
	io.WriteString(w, string(buf))
	io.WriteString(w, "\n")
	io.WriteString(w, "
\n")
}

func showHeaders(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "
Headers
\n")

	var headers []string
	for k, v := range r.Header {
		for _, vv := range v {
			headers = append(headers, k+": "+vv+"
\n")
		}
	}

	sort.Strings(headers)

	for _, h := range headers {
		io.WriteString(w, h)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/cenkalti/backoff"
	dockerapi "github.com/fsouza/go-dockerclient"
)

var hostIp = flag.String("ip", "", "IP for ports mapped to the host")
var refreshInterval = flag.Int("ttl-refresh", 0, "Frequency with which service TTLs are refreshed")
var refreshTtl = flag.Int("ttl", 0, "TTL for services (default is no expiry)")

func getopt(name, def string) string {
	if env := os.Getenv(name); env != "" {
		return env
	}
	return def
}

func assert(err error) {
	if err != nil {
		log.Fatal("registrator: ", err)
	}
}

func retry(fn func() error) error {
	return backoff.Retry(fn, backoff.NewExponentialBackOff())
}

func mapdefault(m map[string]string, key, default_ string) string {
	v, ok := m[key]
	if !ok {
		return default_
	}
	return v
}

type ServiceRegistry interface {
	Register(service *Service) error
	Deregister(service *Service) error
	Refresh(service *Service) error
}

func NewServiceRegistry(uri *url.URL) ServiceRegistry {
	factory := map[string]func(*url.URL) ServiceRegistry{
		"consul":  NewConsulRegistry,
		"etcd":    NewEtcdRegistry,
		"skydns2": NewSkydns2Registry,
	}[uri.Scheme]
	if factory == nil {
		log.Fatal("unrecognized registry backend: ", uri.Scheme)
	}
	log.Println("registrator: Using", uri.Scheme, "registry backend at", uri)
	return factory(uri)
}

func main() {

	flag.Parse()

	if *hostIp != "" {
		log.Println("registrator: Forcing host IP to", *hostIp)
	}
	if (*refreshTtl == 0 && *refreshInterval > 0) || (*refreshTtl > 0 && *refreshInterval == 0) {
		assert(errors.New("-ttl and -ttl-refresh must be specified together or not at all"))
	} else if *refreshTtl <= *refreshInterval {
		assert(errors.New("-ttl must be greater than -ttl-refresh"))
	}

	docker, err := dockerapi.NewClient(getopt("DOCKER_HOST", "unix:///var/run/docker.sock"))
	assert(err)

	uri, err := url.Parse(flag.Arg(0))
	assert(err)
	registry := NewServiceRegistry(uri)

	bridge := &RegistryBridge{
		docker:   docker,
		registry: registry,
		services: make(map[string][]*Service),
	}

	containers, err := docker.ListContainers(dockerapi.ListContainersOptions{})
	assert(err)
	for _, listing := range containers {
		bridge.Add(listing.ID)
	}

	quit := make(chan struct{})
	if *refreshInterval > 0 {
		ticker := time.NewTicker(time.Duration(*refreshInterval) * time.Second)
		go func() {
			for {
				select {
				case <-ticker.C:
					bridge.Refresh()
				case <-quit:
					ticker.Stop()
					return
				}
			}
		}()
	}

	events := make(chan *dockerapi.APIEvents)
	assert(docker.AddEventListener(events))
	log.Println("registrator: Listening for Docker events...")
	for msg := range events {
		switch msg.Status {
		case "start":
			go bridge.Add(msg.ID)
		case "die":
			go bridge.Remove(msg.ID)
		}
	}

	close(quit)
	log.Fatal("registrator: docker event loop closed") // todo: reconnect?
}
<commit_msg>Fix non-ttl case<commit_after>package main

import (
	"errors"
	"flag"
	"log"
	"net/url"
	"os"
	"time"

	"github.com/cenkalti/backoff"
	dockerapi "github.com/fsouza/go-dockerclient"
)

var hostIp = flag.String("ip", "", "IP for ports mapped to the host")
var refreshInterval = flag.Int("ttl-refresh", 0, "Frequency with which service TTLs are refreshed")
var refreshTtl = flag.Int("ttl", 0, "TTL for services (default is no expiry)")

func getopt(name, def string) string {
	if env := os.Getenv(name); env != "" {
		return env
	}
	return def
}

func assert(err error) {
	if err != nil {
		log.Fatal("registrator: ", err)
	}
}

func retry(fn func() error) error {
	return backoff.Retry(fn, backoff.NewExponentialBackOff())
}

func mapdefault(m map[string]string, key, default_ string) string {
	v, ok := m[key]
	if !ok {
		return default_
	}
	return v
}

type ServiceRegistry interface {
	Register(service *Service) error
	Deregister(service *Service) error
	Refresh(service *Service) error
}

func NewServiceRegistry(uri *url.URL) ServiceRegistry {
	factory := map[string]func(*url.URL) ServiceRegistry{
		"consul":  NewConsulRegistry,
		"etcd":    NewEtcdRegistry,
		"skydns2": NewSkydns2Registry,
	}[uri.Scheme]
	if factory == nil {
		log.Fatal("unrecognized registry backend: ", uri.Scheme)
	}
	log.Println("registrator: Using", uri.Scheme, "registry backend at", uri)
	return factory(uri)
}

func main() {

	flag.Parse()

	if *hostIp != "" {
		log.Println("registrator: Forcing host IP to", *hostIp)
	}
	if (*refreshTtl == 0 && *refreshInterval > 0) || (*refreshTtl > 0 && *refreshInterval == 0) {
		assert(errors.New("-ttl and -ttl-refresh must be specified together or not at all"))
	} else if *refreshTtl > 0 && *refreshTtl <= *refreshInterval {
		assert(errors.New("-ttl must be greater than -ttl-refresh"))
	}

	docker, err := dockerapi.NewClient(getopt("DOCKER_HOST", "unix:///var/run/docker.sock"))
	assert(err)

	uri, err := url.Parse(flag.Arg(0))
	assert(err)
	registry := NewServiceRegistry(uri)

	bridge := &RegistryBridge{
		docker:   docker,
		registry: registry,
		services: make(map[string][]*Service),
	}

	containers, err := docker.ListContainers(dockerapi.ListContainersOptions{})
	assert(err)
	for _, listing := range containers {
		bridge.Add(listing.ID)
	}

	quit := make(chan struct{})
	if *refreshInterval > 0 {
		ticker := time.NewTicker(time.Duration(*refreshInterval) * time.Second)
		go func() {
			for {
				select {
				case <-ticker.C:
					bridge.Refresh()
				case <-quit:
					ticker.Stop()
					return
				}
			}
		}()
	}

	events := make(chan *dockerapi.APIEvents)
	assert(docker.AddEventListener(events))
	log.Println("registrator: Listening for Docker events...")
	for msg := range events {
		switch msg.Status {
		case "start":
			go bridge.Add(msg.ID)
		case "die":
			go bridge.Remove(msg.ID)
		}
	}

	close(quit)
	log.Fatal("registrator: docker event loop closed") // todo: reconnect?
}
<|endoftext|>


<commit_before>package render

import (
	"encoding/json"
	"io"
)

func Bind(r io.Reader, v interface{}) error {
	return json.NewDecoder(r).Decode(v)
}
<commit_msg>Drain response body in render Bind<commit_after>package render

import (
	"encoding/json"
	"io"
	"io/ioutil"
)

func Bind(r io.Reader, v interface{}) error {
	err := json.NewDecoder(r).Decode(v)
	io.Copy(ioutil.Discard, r)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package repo

import (
	"time"
)

type SettingsData struct {
	PaymentDataInQR    *bool              `json:"paymentDataInQR"`
	ShowNotifications  *bool              `json:"showNotifications"`
	ShowNsfw           *bool              `json:"showNsfw"`
	ShippingAddresses  *[]ShippingAddress `json:"shippingAddresses"`
	LocalCurrency      *string            `json:"localCurrency"`
	Country            *string            `json:"country"`
	Language           *string            `json:"language"`
	TermsAndConditions *string            `json:"termsAndConditions"`
	RefundPolicy       *string            `json:"refundPolicy"`
	BlockedNodes       *[]string          `json:"blockedNodes"`
	StoreModerators    *[]string          `json:"storeModerators"`
	MisPaymentBuffer   *float32           `json:"mispaymentBuffer"`
	SMTPSettings       *SMTPSettings      `json:"smtpSettings"`
	Version            *string            `json:"version"`
}

type ShippingAddress struct {
	Name           string `json:"name"`
	Company        string `json:"company"`
	AddressLineOne string `json:"addressLineOne"`
	AddressLineTwo string `json:"addressLineTwo"`
	City           string `json:"city"`
	State          string `json:"state"`
	Country        string `json:"country"`
	PostalCode     string `json:"postalCode"`
	AddressNotes   string `json:"addressNotes"`
}

type SMTPSettings struct {
	Notifications  bool   `json:"notifications"`
	ServerAddress  string `json:"serverAddress"`
	Username       string `json:"username"`
	Password       string `json:"password"`
	SenderEmail    string `json:"senderEmail"`
	RecipientEmail string `json:"recipientEmail"`
}

type Coupon struct {
	Slug string
	Code string
	Hash string
}

type ChatMessage struct {
	MessageId string    `json:"messageId"`
	PeerId    string    `json:"peerId"`
	Subject   string    `json:"subject"`
	Message   string    `json:"message"`
	Read      bool      `json:"read"`
	Outgoing  bool      `json:"outgoing"`
	Timestamp time.Time `json:"timestamp"`
}

type ChatConversation struct {
	PeerId    string    `json:"peerId"`
	Unread    int       `json:"unread"`
	Last      string    `json:"lastMessage"`
	Timestamp time.Time `json:"timestamp"`
	Outgoing  bool      `json:"outgoing"`
}

type Metadata struct {
	Txid      string
	Address   string
	Memo      string
	OrderId   string
	Thumbnail string
}

type Purchase struct {
	OrderId            string    `json:"orderId"`
	Timestamp          time.Time `json:"timestamp"`
	Title              string    `json:"title"`
	Thumbnail          string    `json:"thumbnail"`
	Total              uint64    `json:"total"`
	VendorId           string    `json:"vendorId"`
	VendorHandle       string    `json:"vendorHandle"`
	ShippingName       string    `json:"shippingName"`
	ShippingAddress    string    `json:"shippingAddress"`
	State              string    `json:"status"`
	Read               bool      `json:"read"`
	UnreadChatMessages int       `json:"unreadChatMessages"`
}

type Sale struct {
	OrderId            string    `json:"orderId"`
	Timestamp          time.Time `json:"timestamp"`
	Title              string    `json:"title"`
	Thumbnail          string    `json:"thumbnail"`
	Total              uint64    `json:"total"`
	BuyerId            string    `json:"buyerId"`
	BuyerHandle        string    `json:"buyerHandle"`
	ShippingName       string    `json:"shippingName"`
	ShippingAddress    string    `json:"shippingAddress"`
	State              string    `json:"state"`
	Read               bool      `json:"read"`
	UnreadChatMessages int       `json:"unreadChatMessages"`
}

type Case struct {
	CaseId             string    `json:"caseId"`
	Timestamp          time.Time `json:"timestamp"`
	Title              string    `json:"title"`
	Thumbnail          string    `json:"thumbnail"`
	Total              uint64    `json:"total"`
	BuyerId            string    `json:"buyerId"`
	BuyerHandle        string    `json:"buyerHandle"`
	VendorId           string    `json:"vendorId"`
	VendorHandle       string    `json:"vendorHandle"`
	BuyerOpened        bool      `json:"buyerOpened"`
	State              string    `json:"state"`
	Read               bool      `json:"read"`
	UnreadChatMessages int       `json:"unreadChatMessages"`
}
<commit_msg>Change state to status in GET purchases<commit_after>package repo

import (
	"time"
)

type SettingsData struct {
	PaymentDataInQR    *bool              `json:"paymentDataInQR"`
	ShowNotifications  *bool              `json:"showNotifications"`
	ShowNsfw           *bool              `json:"showNsfw"`
	ShippingAddresses  *[]ShippingAddress `json:"shippingAddresses"`
	LocalCurrency      *string            `json:"localCurrency"`
	Country            *string            `json:"country"`
	Language           *string            `json:"language"`
	TermsAndConditions *string            `json:"termsAndConditions"`
	RefundPolicy       *string            `json:"refundPolicy"`
	BlockedNodes       *[]string          `json:"blockedNodes"`
	StoreModerators    *[]string          `json:"storeModerators"`
	MisPaymentBuffer   *float32           `json:"mispaymentBuffer"`
	SMTPSettings       *SMTPSettings      `json:"smtpSettings"`
	Version            *string            `json:"version"`
}

type ShippingAddress struct {
	Name           string `json:"name"`
	Company        string `json:"company"`
	AddressLineOne string `json:"addressLineOne"`
	AddressLineTwo string `json:"addressLineTwo"`
	City           string `json:"city"`
	State          string `json:"state"`
	Country        string `json:"country"`
	PostalCode     string `json:"postalCode"`
	AddressNotes   string `json:"addressNotes"`
}

type SMTPSettings struct {
	Notifications  bool   `json:"notifications"`
	ServerAddress  string `json:"serverAddress"`
	Username       string `json:"username"`
	Password       string `json:"password"`
	SenderEmail    string `json:"senderEmail"`
	RecipientEmail string `json:"recipientEmail"`
}

type Coupon struct {
	Slug string
	Code string
	Hash string
}

type ChatMessage struct {
	MessageId string    `json:"messageId"`
	PeerId    string    `json:"peerId"`
	Subject   string    `json:"subject"`
	Message   string    `json:"message"`
	Read      bool      `json:"read"`
	Outgoing  bool      `json:"outgoing"`
	Timestamp time.Time `json:"timestamp"`
}

type ChatConversation struct {
	PeerId    string    `json:"peerId"`
	Unread    int       `json:"unread"`
	Last      string    `json:"lastMessage"`
	Timestamp time.Time `json:"timestamp"`
	Outgoing  bool      `json:"outgoing"`
}

type Metadata struct {
	Txid      string
	Address   string
	Memo      string
	OrderId   string
	Thumbnail string
}

type Purchase struct {
	OrderId            string    `json:"orderId"`
	Timestamp          time.Time `json:"timestamp"`
	Title              string    `json:"title"`
	Thumbnail          string    `json:"thumbnail"`
	Total              uint64    `json:"total"`
	VendorId           string    `json:"vendorId"`
	VendorHandle       string    `json:"vendorHandle"`
	ShippingName       string    `json:"shippingName"`
	ShippingAddress    string    `json:"shippingAddress"`
	State              string    `json:"state"`
	Read               bool      `json:"read"`
	UnreadChatMessages int       `json:"unreadChatMessages"`
}

type Sale struct {
	OrderId            string    `json:"orderId"`
	Timestamp          time.Time `json:"timestamp"`
	Title              string    `json:"title"`
	Thumbnail          string    `json:"thumbnail"`
	Total              uint64    `json:"total"`
	BuyerId            string    `json:"buyerId"`
	BuyerHandle        string    `json:"buyerHandle"`
	ShippingName       string    `json:"shippingName"`
	ShippingAddress    string    `json:"shippingAddress"`
	State              string    `json:"state"`
	Read               bool      `json:"read"`
	UnreadChatMessages int       `json:"unreadChatMessages"`
}

type Case struct {
	CaseId             string    `json:"caseId"`
	Timestamp          time.Time `json:"timestamp"`
	Title              string    `json:"title"`
	Thumbnail          string    `json:"thumbnail"`
	Total              uint64    `json:"total"`
	BuyerId            string    `json:"buyerId"`
	BuyerHandle        string    `json:"buyerHandle"`
	VendorId           string    `json:"vendorId"`
	VendorHandle       string    `json:"vendorHandle"`
	BuyerOpened        bool      `json:"buyerOpened"`
	State              string    `json:"state"`
	Read               bool      `json:"read"`
	UnreadChatMessages int       `json:"unreadChatMessages"`
}
<|endoftext|>


<commit_before>package pd

import (
	"sync"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"golang.org/x/net/context"
)

const requestTimeout = 3 * time.Second

// Client is a PD (Placement Driver) client.
// It should not be used after calling Close().
type Client struct {
	clusterID   uint64
	etcdClient  *clientv3.Client
	workerMutex sync.RWMutex
	worker      *rpcWorker
	wg          sync.WaitGroup
	quit        chan struct{}
}

// NewClient creates a PD client.
func NewClient(etcdAddrs []string, leaderPath string, clusterID uint64) (*Client, error) {
	log.Infof("[pd] create etcd client with endpoints %v", etcdAddrs)
	etcdClient, err := clientv3.New(clientv3.Config{
		Endpoints:   etcdAddrs,
		DialTimeout: requestTimeout,
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	leaderAddr, revision, err := getLeader(etcdClient, leaderPath)
	if err != nil {
		return nil, errors.Trace(err)
	}

	client := &Client{
		clusterID:  clusterID,
		etcdClient: etcdClient,
		worker:     newRPCWorker(leaderAddr, clusterID),
		quit:       make(chan struct{}),
	}

	client.wg.Add(1)
	go client.watchLeader(leaderPath, revision)

	return client, nil
}

// Close stops the client.
func (c *Client) Close() {
	close(c.quit)
	c.worker.stop(errors.New("[pd] pd-client closing"))
	c.wg.Wait()
}

// GetTS get a timestamp from PD.
func (c *Client) GetTS() (int64, int64, error) {
	req := &tsoRequest{
		done: make(chan error),
	}
	c.workerMutex.RLock()
	c.worker.requests <- req
	c.workerMutex.RUnlock()
	err := <-req.done
	return req.physical, req.logical, err
}

// GetRegion get a region from PD by key.
// The region may expire after split. Caller is responsible for caching and
// take care of region change.
func (c *Client) GetRegion(key []byte) (*metapb.Region, error) {
	req := &regionRequest{
		key:  key,
		done: make(chan error),
	}
	c.workerMutex.RLock()
	c.worker.requests <- req
	c.workerMutex.RUnlock()
	err := <-req.done
	return req.region, err
}

func (c *Client) watchLeader(leaderPath string, revision int64) {
	defer c.wg.Done()
WATCH:
	for {
		log.Infof("[pd] start watch pd leader on path %v, revision %v", leaderPath, revision)
		rch := c.etcdClient.Watch(context.Background(), leaderPath)
		select {
		case resp := <-rch:
			if resp.Canceled {
				log.Warn("[pd] leader watcher canceled")
				continue WATCH
			}
			leaderAddr, rev, err := getLeader(c.etcdClient, leaderPath)
			if err != nil {
				log.Warn(err)
				continue WATCH
			}
			log.Infof("[pd] found new pd-server leader addr: %v", leaderAddr)
			c.workerMutex.Lock()
			c.worker.stop(errors.Errorf("[pd] leader change"))
			c.worker = newRPCWorker(leaderAddr, c.clusterID)
			c.workerMutex.Unlock()
			revision = rev
		case <-c.quit:
			return
		}
	}
}

func getLeader(etcdClient *clientv3.Client, path string) (string, int64, error) {
	kv := clientv3.NewKV(etcdClient)
	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	resp, err := kv.Get(ctx, path)
	cancel()
	if err != nil {
		return "", 0, errors.Trace(err)
	}
	if len(resp.Kvs) != 1 {
		return "", 0, errors.Errorf("invalid getLeader resp: %v", resp)
	}

	var leader pdpb.Leader
	if err = proto.Unmarshal(resp.Kvs[0].Value, &leader); err != nil {
		return "", 0, errors.Trace(err)
	}
	return leader.GetAddr(), resp.Header.Revision, nil
}
<commit_msg>pd-client: fix test panic.<commit_after>package pd

import (
	"sync"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/golang/protobuf/proto"
	"github.com/juju/errors"
	"github.com/ngaut/log"
	"github.com/pingcap/kvproto/pkg/metapb"
	"github.com/pingcap/kvproto/pkg/pdpb"
	"golang.org/x/net/context"
)

const requestTimeout = 3 * time.Second

// Client is a PD (Placement Driver) client.
// It should not be used after calling Close().
type Client struct {
	clusterID   uint64
	etcdClient  *clientv3.Client
	workerMutex sync.RWMutex
	worker      *rpcWorker
	wg          sync.WaitGroup
	quit        chan struct{}
}

// NewClient creates a PD client.
func NewClient(etcdAddrs []string, leaderPath string, clusterID uint64) (*Client, error) {
	log.Infof("[pd] create etcd client with endpoints %v", etcdAddrs)
	etcdClient, err := clientv3.New(clientv3.Config{
		Endpoints:   etcdAddrs,
		DialTimeout: requestTimeout,
	})
	if err != nil {
		return nil, errors.Trace(err)
	}
	leaderAddr, revision, err := getLeader(etcdClient, leaderPath)
	if err != nil {
		return nil, errors.Trace(err)
	}

	client := &Client{
		clusterID:  clusterID,
		etcdClient: etcdClient,
		worker:     newRPCWorker(leaderAddr, clusterID),
		quit:       make(chan struct{}),
	}

	client.wg.Add(1)
	go client.watchLeader(leaderPath, revision)

	return client, nil
}

// Close stops the client.
func (c *Client) Close() {
	close(c.quit)
	// Must wait watchLeader done.
	c.wg.Wait()
	c.worker.stop(errors.New("[pd] pd-client closing"))
}

// GetTS get a timestamp from PD.
func (c *Client) GetTS() (int64, int64, error) {
	req := &tsoRequest{
		done: make(chan error),
	}
	c.workerMutex.RLock()
	c.worker.requests <- req
	c.workerMutex.RUnlock()
	err := <-req.done
	return req.physical, req.logical, err
}

// GetRegion get a region from PD by key.
// The region may expire after split. Caller is responsible for caching and
// take care of region change.
func (c *Client) GetRegion(key []byte) (*metapb.Region, error) {
	req := &regionRequest{
		key:  key,
		done: make(chan error),
	}
	c.workerMutex.RLock()
	c.worker.requests <- req
	c.workerMutex.RUnlock()
	err := <-req.done
	return req.region, err
}

func (c *Client) watchLeader(leaderPath string, revision int64) {
	defer c.wg.Done()
WATCH:
	for {
		log.Infof("[pd] start watch pd leader on path %v, revision %v", leaderPath, revision)
		rch := c.etcdClient.Watch(context.Background(), leaderPath)
		select {
		case resp := <-rch:
			if resp.Canceled {
				log.Warn("[pd] leader watcher canceled")
				continue WATCH
			}
			leaderAddr, rev, err := getLeader(c.etcdClient, leaderPath)
			if err != nil {
				log.Warn(err)
				continue WATCH
			}
			log.Infof("[pd] found new pd-server leader addr: %v", leaderAddr)
			c.workerMutex.Lock()
			c.worker.stop(errors.Errorf("[pd] leader change"))
			c.worker = newRPCWorker(leaderAddr, c.clusterID)
			c.workerMutex.Unlock()
			revision = rev
		case <-c.quit:
			return
		}
	}
}

func getLeader(etcdClient *clientv3.Client, path string) (string, int64, error) {
	kv := clientv3.NewKV(etcdClient)
	ctx, cancel := context.WithTimeout(context.Background(), requestTimeout)
	resp, err := kv.Get(ctx, path)
	cancel()
	if err != nil {
		return "", 0, errors.Trace(err)
	}
	if len(resp.Kvs) != 1 {
		return "", 0, errors.Errorf("invalid getLeader resp: %v", resp)
	}

	var leader pdpb.Leader
	if err = proto.Unmarshal(resp.Kvs[0].Value, &leader); err != nil {
		return "", 0, errors.Trace(err)
	}
	return leader.GetAddr(), resp.Header.Revision, nil
}
<|endoftext|>


<commit_before>package auth

import (
	"net/url"
	"os"

	"koding/kites/config"
	"koding/kites/config/configstore"
	"koding/kites/kloud/stack"
	"koding/klientctl/endpoint/kloud"
	"koding/klientctl/endpoint/kontrol"
	"koding/klientctl/endpoint/team"

	"github.com/koding/logging"
)

type Facade struct {
	*Client

	Konfig *config.Konfig
	Kloud  *kloud.Client
	Team   *team.Client
	Log    logging.Logger
}

type FacadeOpts struct {
	Base *url.URL
	Log  logging.Logger
}

func NewFacade(opts *FacadeOpts) (*Facade, error) {
	k, err := newKonfig(opts.Base)
	if err != nil {
		return nil, err
	}

	kloud := &kloud.Client{
		Transport: &kloud.KiteTransport{
			Konfig: k,
			Log:    opts.Log,
		},
	}

	return &Facade{
		Client: &Client{
			Kloud: kloud,
			Kontrol: &kontrol.Client{
				Kloud:  kloud,
				Konfig: k,
			},
		},
		Konfig: k,
		Kloud:  kloud,
		Team: &team.Client{
			Kloud: kloud,
		},
		Log: opts.Log,
	}, nil
}

func (f *Facade) Login(opts *LoginOptions) (*stack.PasswordLoginResponse, error) {
	// If we already own a valid kite.key, it means we were already
	// authenticated and we just call kloud using kite.key authentication.
	err := f.Kloud.Transport.(stack.Validator).Valid()

	f.log().Debug("auth: transport test: %s", err)

	if err != nil && opts.Token == "" {
		if err = opts.AskUserPass(); err != nil {
			return nil, err
		}
	}

	resp, err := f.Client.Login(opts)
	if err != nil {
		return nil, err
	}

	if resp.KiteKey != "" {
		f.Konfig.KiteKey = resp.KiteKey
		if resp.Metadata != nil {
			f.Konfig.Endpoints = resp.Metadata.Endpoints
		}

		if err := configstore.Use(f.Konfig); err != nil {
			return nil, err
		}

		if kt, ok := f.Kloud.Transport.(*kloud.KiteTransport); ok {
			kt.SetKiteKey(resp.KiteKey)
		}
	}

	if resp.GroupName != "" {
		f.Team.Use(&team.Team{Name: resp.GroupName})
	}

	return resp, nil

}

func (f *Facade) log() logging.Logger {
	if f.Log != nil {
		return f.Log
	}
	return kloud.DefaultLog
}

func newKonfig(base *url.URL) (*config.Konfig, error) {
	k, ok := configstore.List()[config.ID(base.String())]
	if !ok {
		k = &config.Konfig{
			Endpoints: &config.Endpoints{
				Koding: config.NewEndpointURL(base),
			},
			Debug: os.Getenv("KD_DEBUG") == "1",
		}
	}

	if err := configstore.Use(k); err != nil {
		return nil, err
	}

	return k, nil
}
<commit_msg>kd/auth: ensure klient is fetched from managed channel<commit_after>package auth

import (
	"net/url"
	"os"

	"koding/kites/config"
	"koding/kites/config/configstore"
	"koding/kites/kloud/stack"
	conf "koding/klientctl/config"
	"koding/klientctl/endpoint/kloud"
	"koding/klientctl/endpoint/kontrol"
	"koding/klientctl/endpoint/team"

	"github.com/koding/logging"
)

type Facade struct {
	*Client

	Konfig *config.Konfig
	Kloud  *kloud.Client
	Team   *team.Client
	Log    logging.Logger
}

type FacadeOpts struct {
	Base *url.URL
	Log  logging.Logger
}

func NewFacade(opts *FacadeOpts) (*Facade, error) {
	k, err := newKonfig(opts.Base)
	if err != nil {
		return nil, err
	}

	kloud := &kloud.Client{
		Transport: &kloud.KiteTransport{
			Konfig: k,
			Log:    opts.Log,
		},
	}

	return &Facade{
		Client: &Client{
			Kloud: kloud,
			Kontrol: &kontrol.Client{
				Kloud:  kloud,
				Konfig: k,
			},
		},
		Konfig: k,
		Kloud:  kloud,
		Team: &team.Client{
			Kloud: kloud,
		},
		Log: opts.Log,
	}, nil
}

func (f *Facade) Login(opts *LoginOptions) (*stack.PasswordLoginResponse, error) {
	// If we already own a valid kite.key, it means we were already
	// authenticated and we just call kloud using kite.key authentication.
	err := f.Kloud.Transport.(stack.Validator).Valid()

	f.log().Debug("auth: transport test: %s", err)

	if err != nil && opts.Token == "" {
		if err = opts.AskUserPass(); err != nil {
			return nil, err
		}
	}

	resp, err := f.Client.Login(opts)
	if err != nil {
		return nil, err
	}

	if resp.KiteKey != "" {
		f.Konfig.KiteKey = resp.KiteKey
		if resp.Metadata != nil {
			f.Konfig.Endpoints = resp.Metadata.Endpoints
		}

		if err := configstore.Use(f.Konfig); err != nil {
			return nil, err
		}

		if kt, ok := f.Kloud.Transport.(*kloud.KiteTransport); ok {
			kt.SetKiteKey(resp.KiteKey)
		}
	}

	if resp.GroupName != "" {
		f.Team.Use(&team.Team{Name: resp.GroupName})
	}

	return resp, nil

}

func (f *Facade) log() logging.Logger {
	if f.Log != nil {
		return f.Log
	}
	return kloud.DefaultLog
}

func newKonfig(base *url.URL) (*config.Konfig, error) {
	k, ok := configstore.List()[config.ID(base.String())]
	if !ok {
		k = &config.Konfig{
			Endpoints: &config.Endpoints{
				Koding: config.NewEndpointURL(base),
			},
			Debug: os.Getenv("KD_DEBUG") == "1",
		}
	}

	if err := configstore.Use(k); err != nil {
		return nil, err
	}

	return k, nil
}

// fixKlientEndpoint fixes klient latest endpoint - kloud always installs
// klient from development/production channels, however kd needs to use
// managed/devmanaed ones.
//
// This is a hack that eventually needs to be removed.
func fixKlientEndpoint(e *config.Endpoints) {
	if !e.KlientLatest.IsNil() {
		e.KlientLatest = config.ReplaceCustomEnv(e.KlientLatest, conf.Environments.Env,
			conf.Environments.KlientEnv)
	}
}
<|endoftext|>


<commit_before>package phraseapp

import (
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"os"

	"github.com/bgentry/speakeasy"
)

var Debug bool

func EnableDebug() {
	Debug = true
}

type DefaultParams Action
type Action map[string]map[string]interface{}

type Client struct {
	http.Client
	Credentials *Credentials
}

type Credentials struct {
	Username string
	Token    string
	TFA      bool
	Host     string
	Debug    bool
}

func NewClient(credentials Credentials, defaultCredentials *Credentials) (*Client, error) {
	client := &Client{Credentials: &Credentials{}}

	envToken := os.Getenv("PHRASEAPP_ACCESS_TOKEN")

	if credentials.Token != "" && client.Credentials.Token == "" && client.Credentials.Username == "" {
		client.Credentials.Token = credentials.Token
	} else if credentials.Username != "" && client.Credentials.Username == "" {
		client.Credentials.Username = credentials.Username
	} else if envToken != "" && credentials.Token == "" && credentials.Username == "" && client.Credentials.Username == "" {
		client.Credentials.Token = envToken
	}

	if credentials.TFA && client.Credentials.Username == "" {
		client.Credentials.TFA = credentials.TFA
	}

	if credentials.Debug == true || ((defaultCredentials != nil) && defaultCredentials.Debug == true) {
		EnableDebug()
	}

	if credentials.Host != "" {
		client.Credentials.Host = credentials.Host
	} else {
		if defaultCredentials != nil && defaultCredentials.Host != "" {
			client.Credentials.Host = defaultCredentials.Host
		}
	}

	if client.Credentials.Host == "" {
		client.Credentials.Host = "https://api.phraseapp.com"
	}

	notSet := client.Credentials.Token == "" && client.Credentials.Username == ""
	if notSet && defaultCredentials != nil && defaultCredentials.Token != "" {
		client.Credentials.Token = defaultCredentials.Token
	}
	if notSet && defaultCredentials != nil && defaultCredentials.Username != "" {
		client.Credentials.Username = defaultCredentials.Username
	}

	return client, nil
}

func (client *Client) authenticate(req *http.Request) error {
	if client.Credentials == nil {
		return fmt.Errorf("no auth handler registered")
	}

	if err := client.Credentials.validate(); err != nil {
		return err
	}

	req.Header.Set("User-Agent", GetUserAgent())
	switch {
	case client.Credentials.Token != "":
		req.Header.Set("Authorization", "token "+client.Credentials.Token)
	case client.Credentials.Username != "":
		pwd, err := speakeasy.Ask("Password: ")
		if err != nil {
			return err
		}
		req.SetBasicAuth(client.Credentials.Username, pwd)

		if client.Credentials.TFA { // TFA only required for username+password based login.
			token, err := speakeasy.Ask("TFA-Token: ")
			if err != nil {
				return err
			}
			req.Header.Set("X-PhraseApp-OTP", token)
		}
	}

	return nil
}

func (ah *Credentials) validate() error {
	switch {
	case ah.Username == "" && ah.Token == "":
		return fmt.Errorf("either username or token must be given")
	default:
		return nil
	}
}

func (client *Client) sendRequestPaginated(method, rawurl, ctype string, r io.Reader, status, page, perPage int) (io.ReadCloser, error) {
	endpointUrl := client.Credentials.Host + rawurl
	u, err := url.Parse(endpointUrl)
	if err != nil {
		return nil, err
	}

	query := u.Query()
	query.Add("page", strconv.Itoa(page))
	query.Add("per_page", strconv.Itoa(perPage))

	u.RawQuery = query.Encode()

	if Debug {
		fmt.Fprintln(os.Stderr, method, u.String())
	}

	req, err := http.NewRequest(method, u.String(), r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := client.send(req, status)
	if err != nil {
		return nil, err
	}

	return resp.Body, nil
}

func (client *Client) sendRequest(method, url, ctype string, r io.Reader, status int) (io.ReadCloser, error) {
	endpointUrl := client.Credentials.Host + url
	if Debug {
		fmt.Fprintln(os.Stderr, method, url)
		bytes, err := ioutil.ReadAll(r)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
		}
		str := string(bytes)
		fmt.Fprintln(os.Stderr, str)
		r = strings.NewReader(str)
	}
	req, err := http.NewRequest(method, endpointUrl, r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := client.send(req, status)
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func (client *Client) send(req *http.Request, status int) (*http.Response, error) {
	err := client.authenticate(req)
	if err != nil {
		return nil, err
	}

	resp, err := client.Client.Do(req)
	if err != nil {
		return nil, err
	}

	err = handleResponseStatus(resp, status)
	if err != nil {
		resp.Body.Close()
	}
	return resp, err
}
<commit_msg>adds further debugging<commit_after>package phraseapp

import (
	"bytes"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strconv"
	"strings"

	"os"

	"github.com/bgentry/speakeasy"
)

var Debug bool

func EnableDebug() {
	Debug = true
}

type DefaultParams Action
type Action map[string]map[string]interface{}

type Client struct {
	http.Client
	Credentials *Credentials
}

type Credentials struct {
	Username string
	Token    string
	TFA      bool
	Host     string
	Debug    bool
}

func NewClient(credentials Credentials, defaultCredentials *Credentials) (*Client, error) {
	client := &Client{Credentials: &Credentials{}}

	envToken := os.Getenv("PHRASEAPP_ACCESS_TOKEN")

	if credentials.Token != "" && client.Credentials.Token == "" && client.Credentials.Username == "" {
		client.Credentials.Token = credentials.Token
	} else if credentials.Username != "" && client.Credentials.Username == "" {
		client.Credentials.Username = credentials.Username
	} else if envToken != "" && credentials.Token == "" && credentials.Username == "" && client.Credentials.Username == "" {
		client.Credentials.Token = envToken
	}

	if credentials.TFA && client.Credentials.Username == "" {
		client.Credentials.TFA = credentials.TFA
	}

	if credentials.Debug == true || ((defaultCredentials != nil) && defaultCredentials.Debug == true) {
		EnableDebug()
	}

	if credentials.Host != "" {
		client.Credentials.Host = credentials.Host
	} else {
		if defaultCredentials != nil && defaultCredentials.Host != "" {
			client.Credentials.Host = defaultCredentials.Host
		}
	}

	if client.Credentials.Host == "" {
		client.Credentials.Host = "https://api.phraseapp.com"
	}

	notSet := client.Credentials.Token == "" && client.Credentials.Username == ""
	if notSet && defaultCredentials != nil && defaultCredentials.Token != "" {
		client.Credentials.Token = defaultCredentials.Token
	}
	if notSet && defaultCredentials != nil && defaultCredentials.Username != "" {
		client.Credentials.Username = defaultCredentials.Username
	}

	return client, nil
}

func (client *Client) authenticate(req *http.Request) error {
	if client.Credentials == nil {
		return fmt.Errorf("no auth handler registered")
	}

	if err := client.Credentials.validate(); err != nil {
		return err
	}

	req.Header.Set("User-Agent", GetUserAgent())
	switch {
	case client.Credentials.Token != "":
		req.Header.Set("Authorization", "token "+client.Credentials.Token)
	case client.Credentials.Username != "":
		pwd, err := speakeasy.Ask("Password: ")
		if err != nil {
			return err
		}
		req.SetBasicAuth(client.Credentials.Username, pwd)

		if client.Credentials.TFA { // TFA only required for username+password based login.
			token, err := speakeasy.Ask("TFA-Token: ")
			if err != nil {
				return err
			}
			req.Header.Set("X-PhraseApp-OTP", token)
		}
	}

	return nil
}

func (ah *Credentials) validate() error {
	switch {
	case ah.Username == "" && ah.Token == "":
		return fmt.Errorf("either username or token must be given")
	default:
		return nil
	}
}

func (client *Client) sendRequestPaginated(method, rawurl, ctype string, r io.Reader, status, page, perPage int) (io.ReadCloser, error) {
	endpointUrl := client.Credentials.Host + rawurl
	u, err := url.Parse(endpointUrl)
	if err != nil {
		return nil, err
	}

	query := u.Query()
	query.Add("page", strconv.Itoa(page))
	query.Add("per_page", strconv.Itoa(perPage))

	u.RawQuery = query.Encode()

	if Debug {
		fmt.Fprintln(os.Stderr, method, u.String())
	}

	req, err := http.NewRequest(method, u.String(), r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := client.send(req, status)
	if err != nil {
		return nil, err
	}

	return resp.Body, nil
}

func (client *Client) sendRequest(method, url, ctype string, r io.Reader, status int) (io.ReadCloser, error) {
	endpointUrl := client.Credentials.Host + url
	if Debug {
		fmt.Fprintln(os.Stderr, method, url)
		bytes, err := ioutil.ReadAll(r)
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error: %s", err.Error())
		}
		str := string(bytes)
		fmt.Fprintln(os.Stderr, str)
		r = strings.NewReader(str)
	}
	req, err := http.NewRequest(method, endpointUrl, r)
	if err != nil {
		return nil, err
	}

	if ctype != "" {
		req.Header.Add("Content-Type", ctype)
	}

	resp, err := client.send(req, status)
	if err != nil {
		return nil, err
	}
	return resp.Body, nil
}

func (client *Client) send(req *http.Request, status int) (*http.Response, error) {
	err := client.authenticate(req)
	if err != nil {
		return nil, err
	}

	if Debug {
		b := new(bytes.Buffer)
		err = req.Header.Write(b)
		if err != nil {
			return nil, err
		}

		bytes, err := ioutil.ReadAll(b)
		if err != nil {
			return nil, err
		}
		fmt.Fprintln(os.Stderr, string(bytes))

		r := req.Body
		if r != nil {
			var by []byte
			_, err = r.Read(by)
			fmt.Fprintln(os.Stderr, string(by))
		}
	}
	resp, err := client.Client.Do(req)
	if err != nil {
		return nil, err
	}

	err = handleResponseStatus(resp, status)
	if err != nil {
		resp.Body.Close()
	}
	return resp, err
}
<|endoftext|>


<commit_before>package mixcoin

import (
	"btcnet"
	"btcrpcclient"
	"btcutil"
	"btcwire"
	"btcws"

	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

type chunk struct {
	val      int
	sendBy   int
	returnBy int
	outAddr  *btcutil.AddressPubKeyHash
	fee      int
	nonce    int
	confirm  int
}

type chunkTable struct {
	receivable map[string]*chunk
	pool       map[string]*chunk
	retained   map[string]*chunk
}

type ServerConfig struct {
	// host:port for btcwallet instance
	RpcAddress string
	// username for btcwallet instance
	RpcUser string
	// password for btcwallet instance
	RpcPass string

	// min confirmations we require
	MinConfirmations int
	// standard chunk size
	ChunkSize int

	// path to pubring
	PubRingFile string
	// path to privring
	PrivRingFile string
	// password for privring
	Passphrase string

	NetParams *btcnet.Params
}

func NewChunkTable() *chunkTable {
	table := &chunkTable{
		receivable: make(map[string]*chunk),
		pool:       make(map[string]*chunk),
		retained:   make(map[string]*chunk),
	}
	return table
}

type Server struct {
	config *ServerConfig
	chunks *chunkTable
	rpc    *btcrpcclient.Client
}

func NewServer(config *ServerConfig) (*Server, error) {

	//btcdHomeDir := btcutil.AppDataDir("btcd", false)
	certs, err := ioutil.ReadFile("/Users/cguo/server.crt")
	//certs, err := ioutil.ReadFile(filepath.Join(btcdHomeDir, "rpc.cert"))
	if err != nil {
		panic("couldn't read btcd certs")
	}
	connCfg := &btcrpcclient.ConnConfig{
		Host:         config.RpcAddress,
		Endpoint:     "ws",
		User:         config.RpcUser,
		Pass:         config.RpcPass,
		Certificates: certs,
		DisableTLS:   false,
	}

	client, err := btcrpcclient.New(connCfg, &ntfnHandlers)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	// Register for block connect and disconnect notifications.
	if err = client.NotifyBlocks(); err != nil {
		fmt.Println(err)
		return nil, err
	}

	server := &Server{
		config: config,
		chunks: NewChunkTable(),
		rpc:    client,
	}

	ntfnHandlers := btcrpcclient.NotificationHandlers{
		OnBlockConnected: server.onNewBlock,
		OnBlockDisconnected: func(hash *btcwire.ShaHash, height int32) {
			log.Printf("Block disconnected: %v (%d)", hash, height)
		},
	}

	return server, nil
}

func (self *Server) HandleChunkRequest(chunkReq *ChunkRequest) (*ChunkRequest, error) {
	fmt.Println("handling chunk request")

	escrowAddr, err := self.getNewAddress()
	if err != nil {
		fmt.Println(err)
		panic("unable to create new address")
	}
	encodedAddr := (*escrowAddr).EncodeAddress()

	chunkReq.EscrowAddr = encodedAddr

	err = self.signChunk(chunkReq)
	if err != nil {
		fmt.Println(err)
		panic(err)
	}

	self.registerNewChunk(encodedAddr, chunkReq)
	self.registerAddress(encodedAddr)

	return chunkReq, nil
}

func (self *Server) getNewAddress() (*btcutil.Address, error) {
	addr, err := self.rpc.GetNewAddress()
	if err != nil {
		self.rpc.CreateEncryptedWallet("Thereis1")
	}
	addr, err = self.rpc.GetNewAddress()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	return &addr, nil
}

func (self *Server) signChunk(chunk *ChunkRequest) error {
	fmt.Println("signing chunk")
	marshaledBytes, _ := json.Marshal(chunk)
	marshaledBuf := bytes.NewBuffer(marshaledBytes)

	keyringFileBuffer, err := os.Open(self.config.PrivRingFile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer keyringFileBuffer.Close()
	entityList, err := openpgp.ReadKeyRing(keyringFileBuffer)
	if err != nil {
		fmt.Println(err)
		return err
	}
	entity := entityList[0]
	passphrasebyte := []byte(self.config.Passphrase)
	entity.PrivateKey.Decrypt(passphrasebyte)
	for _, subkey := range entity.Subkeys {
		subkey.PrivateKey.Decrypt(passphrasebyte)
	}
	armoredSigBuf := new(bytes.Buffer)
	err = openpgp.ArmoredDetachSign(armoredSigBuf, entity, marshaledBuf, nil)
	if err != nil {
		fmt.Println(err)
		return err
	}
	armoredSigEnc, err := ioutil.ReadAll(armoredSigBuf)
	if err != nil {
		fmt.Println(err)
		return err
	}
	armoredSig := base64.StdEncoding.EncodeToString(armoredSigEnc)

	chunk.Warrant = armoredSig

	return nil
}

/*
* TODO: ChunkRequest -> Chunk
 */
func (self *Server) registerNewChunk(encodedAddr string, chunk *ChunkRequest) error {
	return nil
}

func (self *Server) registerAddress(encodedAddr string) error {
	addr, err := btcutil.DecodeAddress(encodedAddr, self.config.NetParams)
	self.rpc.NotifyReceived([]*btcutil.Address{addr})
}

func (self *Server) onNewBlock(hash *btcwire.ShaHash, height int32) {
	minConf := self.config.MinConfirmations
	stdChunkSize := self.config.ChunkSize

	for encodedAddr, chunk := range self.chunks.receivable {
		addr := btcutil.DecodeAddress(encod, self.config.NetParams)
		amount, err := self.rpc.GetReceivedByAddressMinConf(addr, minConf)
		// TODO check that the time is before receivedBy
		if amount >= stdChunkSize {
			// check random beacon to see if we should retain as fee
			// move chunk from receivable into pool
		}
	}
}
<commit_msg>look for incoming txns<commit_after>package mixcoin

import (
	"btcnet"
	"btcrpcclient"
	"btcutil"
	"btcwire"
	"btcws"

	"bytes"
	"code.google.com/p/go.crypto/openpgp"
	"encoding/base64"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"os"
)

type chunk struct {
	val      int
	sendBy   int
	returnBy int
	outAddr  *btcutil.AddressPubKeyHash
	fee      int
	nonce    int
	confirm  int

	txInfo btcjson.TransactionInput
}

type chunkTable struct {
	receivable map[string]*chunk
	pool       map[string]*chunk
	retained   map[string]*chunk
}

type ServerConfig struct {
	// host:port for btcwallet instance
	RpcAddress string
	// username for btcwallet instance
	RpcUser string
	// password for btcwallet instance
	RpcPass string

	// min confirmations we require
	MinConfirmations int
	// standard chunk size
	ChunkSize int

	// path to pubring
	PubRingFile string
	// path to privring
	PrivRingFile string
	// password for privring
	Passphrase string

	NetParams *btcnet.Params
}

func NewChunkTable() *chunkTable {
	table := &chunkTable{
		receivable: make(map[string]*chunk),
		pool:       make(map[string]*chunk),
		retained:   make(map[string]*chunk),
	}
	return table
}

type Server struct {
	config *ServerConfig
	chunks *chunkTable
	rpc    *btcrpcclient.Client
}

func NewServer(config *ServerConfig) (*Server, error) {

	//btcdHomeDir := btcutil.AppDataDir("btcd", false)
	certs, err := ioutil.ReadFile("/Users/cguo/server.crt")
	//certs, err := ioutil.ReadFile(filepath.Join(btcdHomeDir, "rpc.cert"))
	if err != nil {
		panic("couldn't read btcd certs")
	}
	connCfg := &btcrpcclient.ConnConfig{
		Host:         config.RpcAddress,
		Endpoint:     "ws",
		User:         config.RpcUser,
		Pass:         config.RpcPass,
		Certificates: certs,
		DisableTLS:   false,
	}

	client, err := btcrpcclient.New(connCfg, &ntfnHandlers)
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	// Register for block connect and disconnect notifications.
	if err = client.NotifyBlocks(); err != nil {
		fmt.Println(err)
		return nil, err
	}

	server := &Server{
		config: config,
		chunks: NewChunkTable(),
		rpc:    client,
	}

	ntfnHandlers := btcrpcclient.NotificationHandlers{
		OnBlockConnected: server.onNewBlock,
		OnRecvTx:         server.onReceivedTx,
		OnBlockDisconnected: func(hash *btcwire.ShaHash, height int32) {
			log.Printf("Block disconnected: %v (%d)", hash, height)
		},
	}

	return server, nil
}

func (self *Server) HandleChunkRequest(chunkReq *ChunkRequest) (*ChunkRequest, error) {
	fmt.Println("handling chunk request")

	escrowAddr, err := self.getNewAddress()
	if err != nil {
		fmt.Println(err)
		panic("unable to create new address")
	}
	encodedAddr := (*escrowAddr).EncodeAddress()

	chunkReq.EscrowAddr = encodedAddr

	err = self.signChunk(chunkReq)
	if err != nil {
		fmt.Println(err)
		panic(err)
	}

	self.registerNewChunk(encodedAddr, chunkReq)
	self.registerAddress(encodedAddr)

	return chunkReq, nil
}

func (self *Server) getNewAddress() (*btcutil.Address, error) {
	addr, err := self.rpc.GetNewAddress()
	if err != nil {
		self.rpc.CreateEncryptedWallet("Thereis1")
	}
	addr, err = self.rpc.GetNewAddress()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}
	return &addr, nil
}

func (self *Server) signChunk(chunk *ChunkRequest) error {
	fmt.Println("signing chunk")
	marshaledBytes, _ := json.Marshal(chunk)
	marshaledBuf := bytes.NewBuffer(marshaledBytes)

	keyringFileBuffer, err := os.Open(self.config.PrivRingFile)
	if err != nil {
		fmt.Println(err)
		return err
	}
	defer keyringFileBuffer.Close()
	entityList, err := openpgp.ReadKeyRing(keyringFileBuffer)
	if err != nil {
		fmt.Println(err)
		return err
	}
	entity := entityList[0]
	passphrasebyte := []byte(self.config.Passphrase)
	entity.PrivateKey.Decrypt(passphrasebyte)
	for _, subkey := range entity.Subkeys {
		subkey.PrivateKey.Decrypt(passphrasebyte)
	}
	armoredSigBuf := new(bytes.Buffer)
	err = openpgp.ArmoredDetachSign(armoredSigBuf, entity, marshaledBuf, nil)
	if err != nil {
		fmt.Println(err)
		return err
	}
	armoredSigEnc, err := ioutil.ReadAll(armoredSigBuf)
	if err != nil {
		fmt.Println(err)
		return err
	}
	armoredSig := base64.StdEncoding.EncodeToString(armoredSigEnc)

	chunk.Warrant = armoredSig

	return nil
}

/*
* TODO: ChunkRequest -> Chunk
 */
func (self *Server) registerNewChunk(encodedAddr string, chunk *ChunkRequest) error {
	return nil
}

func (self *Server) registerAddress(encodedAddr string) error {
	addr, err := btcutil.DecodeAddress(encodedAddr, self.config.NetParams)
	self.rpc.NotifyReceived([]*btcutil.Address{addr})
}

func (self *Server) onReceivedTx(tx *btcutil.Tx, details *btcws.BlockDetails) {
	blockHash := details.Hash
}

func (self *Server) extractReceivedVouts(tx *btcjson.TxRawResult) map[string]*btcjson.Vout, error {
	ret = make(map[string]*btcjson.Vout)

	for voutIndex, vout := range tx.Vout {
		lockScript := vout.ScriptPubKey
		if vout.Value >= self.config.ChunkSize &&
			lockScript.type == "scriptpubkey" &&
			len(lockScript.addresses == 1) && lockScript.addresses[0] == "one of the receivable addresses" {
				ret[lockScript.addresses[0].EncodeAddress()] = vout
			}
	}

	return ret, nil
}

func (self *Server) onNewBlock(blockHash *btcwire.ShaHash, height int32) {
	minConf := self.config.MinConfirmations
	stdChunkSize := self.config.ChunkSize

	blockVerbose, err := self.rpc.GetBlockVerbose(blockHash, true)

	for x, rawTx := range blockVerbose.RawTx {
		for voutIndex, vout := range rawTx.Vout {
			if vout.Value >= stdChunkSize && len()
		}
	}

	for encodedAddr, chunk := range self.chunks.receivable {
		addr := btcutil.DecodeAddress(encod, self.config.NetParams)
		amount, err := self.rpc.GetReceivedByAddressMinConf(addr, minConf)
		if err != nil {
			return err
		}
		account, err := self.rpc.GetAccount(addr)
		if err != nil {
			return err
		}
		// TODO check that the time is before receivedBy
		if amount >= stdChunkSize {
			// check random beacon to see if we should retain as fee
			// move chunk from receivable into pool
		}
	}
}
<|endoftext|>


<commit_before>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package cmd

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	figure "github.com/admpub/go-figure"
	"github.com/admpub/log"
	"github.com/admpub/nging/application/cmd/event"
	"github.com/admpub/nging/application/handler/setup"
	"github.com/admpub/nging/application/library/config"
	"github.com/admpub/nging/application/library/license"
	"github.com/admpub/nging/application/library/msgbox"
	"github.com/mholt/certmagic"
	"github.com/spf13/cobra"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/engine/standard"
	"github.com/webx-top/echo/middleware"
	"github.com/webx-top/echo/subdomains"
)

// Nging 启动入口

// rootCmd represents the base command when called without any subcommands
var rootCmd = NewRoot()

func NewRoot() *cobra.Command {
	// rootCmd represents the base command when called without any subcommands
	return &cobra.Command{
		Use:          filepath.Base(os.Args[0]),
		Short:        ``,
		Long:         ``,
		SilenceUsage: true,
		RunE:         rootRunE,
	}
}

func rootRunE(cmd *cobra.Command, args []string) error {
	if !event.Licensed {
		machineID, _ := license.MachineID()
		message := `Invalid license!
授权无效!

If you have already purchased a license, please place the ` + license.FileName() + ` to:
如果您已经购买了授权，请将授权证书` + license.FileName() + `放到：

%s`
		msgbox.Error("WARNING",
			message,
			license.FilePath())

		fmt.Println(``)
		fmt.Println(`To purchase a license, please go to our official website:`)
		fmt.Println(`购买授权请前往官方网站：`)
		fmt.Println(``)
		fmt.Println(license.ProductURL() + `?version=` + config.Version.Number + `&machineID=` + machineID)
		if event.MustLicensed {
			return nil
		}
	}

	//独立模块
	if config.DefaultCLIConfig.OnlyRunServer() {
		return nil
	}

	//Manager
	config.DefaultCLIConfig.RunStartup()

	if config.IsInstalled() {
		if err := setup.Upgrade(); err != nil && os.ErrNotExist != err {
			log.Error(`upgrade.sql: `, err)
		}
	}

	// LOGO
	fmt.Println(strings.TrimSuffix(figure.NewFigure(event.SofewareName, `big`, false).String(), "\n"), config.Version.VString()+"\n")

	event.Start()

	c := &engine.Config{
		ReusePort:   true,
		TLSAuto:     config.DefaultConfig.Sys.SSLAuto,
		TLSEmail:    config.DefaultConfig.Sys.SSLEmail,
		TLSHosts:    config.DefaultConfig.Sys.SSLHosts,
		TLSCacheDir: config.DefaultConfig.Sys.SSLCacheDir,
		TLSCertFile: config.DefaultConfig.Sys.SSLCertFile,
		TLSKeyFile:  config.DefaultConfig.Sys.SSLKeyFile,
	}
	c.Address = fmt.Sprintf(`%s:%v`, config.DefaultCLIConfig.Address, config.DefaultCLIConfig.Port)
	hasCert := (len(c.TLSCertFile) > 0 && len(c.TLSKeyFile) > 0)
	//c.TLSAuto = true
	if c.TLSAuto || hasCert {
		if config.DefaultCLIConfig.Port == 80 {
			if c.TLSAuto {
				if err := initCertMagic(c); err != nil {
					panic(err)
				}
				//c.SupportAutoTLS(nil, config.DefaultConfig.Sys.SSLHosts...)
			} else {
				c.Address = fmt.Sprintf(`%s:443`, config.DefaultCLIConfig.Address)
				e2 := echo.New()
				e2.Use(middleware.HTTPSRedirect(), middleware.Log(), middleware.Recover())
				go e2.Run(standard.New(fmt.Sprintf(`%s:80`, config.DefaultCLIConfig.Address)))
			}
			subdomains.Default.Protocol = `https`
		}
	}
	if len(event.Welcome) > 0 {
		now := time.Now()
		msgbox.Success(`Welcome`,
			event.Welcome,
			config.Version.VString(),
			now.Format("Monday, 02 Jan 2006"))
	}
	subdomains.Default.Run(standard.NewWithConfig(c))
	return c.Listener.Close()
}

func initCertMagic(c *engine.Config) error {
	fileStorage := &certmagic.FileStorage{
		Path: filepath.Join(echo.Wd(), `data`, `cache`, `certmagic`),
	}
	if !com.IsExist(fileStorage.Path) {
		if err := os.MkdirAll(fileStorage.Path, 0777); err != nil {
			return err
		}
	}
	if event.Develop { // use the staging endpoint while we're developing
		certmagic.Default.CA = certmagic.LetsEncryptStagingCA
	} else {
		certmagic.Default.CA = certmagic.LetsEncryptProductionCA
	}
	certmagic.Default.Email = c.TLSEmail
	certmagic.Default.Agreed = true
	certmagic.Default.Storage = fileStorage
	ln, err := certmagic.Listen(c.TLSHosts)
	if err == nil {
		c.SetListener(ln)
	}
	return err
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	Init()
	if len(rootCmd.Use) == 0 {
		rootCmd.Use = os.Args[0]
	}
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	config.DefaultCLIConfig.InitFlag(rootCmd.PersistentFlags())
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
}
<commit_msg>update<commit_after>/*
   Nging is a toolbox for webmasters
   Copyright (C) 2018-present  Wenhui Shen <swh@admpub.com>

   This program is free software: you can redistribute it and/or modify
   it under the terms of the GNU Affero General Public License as published
   by the Free Software Foundation, either version 3 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU Affero General Public License for more details.

   You should have received a copy of the GNU Affero General Public License
   along with this program.  If not, see <https://www.gnu.org/licenses/>.
*/

package cmd

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	figure "github.com/admpub/go-figure"
	"github.com/admpub/log"
	"github.com/admpub/nging/application/cmd/event"
	"github.com/admpub/nging/application/handler/setup"
	"github.com/admpub/nging/application/library/config"
	"github.com/admpub/nging/application/library/license"
	"github.com/admpub/nging/application/library/msgbox"
	"github.com/mholt/certmagic"
	"github.com/spf13/cobra"
	"github.com/webx-top/com"
	"github.com/webx-top/echo"
	"github.com/webx-top/echo/engine"
	"github.com/webx-top/echo/engine/standard"
	"github.com/webx-top/echo/middleware"
	"github.com/webx-top/echo/subdomains"
)

// Nging 启动入口

// rootCmd represents the base command when called without any subcommands
var rootCmd = NewRoot()

func NewRoot() *cobra.Command {
	// rootCmd represents the base command when called without any subcommands
	return &cobra.Command{
		Use:          filepath.Base(os.Args[0]),
		Short:        ``,
		Long:         ``,
		SilenceUsage: true,
		RunE:         rootRunE,
	}
}

func rootRunE(cmd *cobra.Command, args []string) error {
	if !event.Licensed {
		machineID, _ := license.MachineID()
		message := `Invalid license!
授权无效!

If you have already purchased a license, please place the ` + license.FileName() + ` to:
如果您已经购买了授权，请将授权证书` + license.FileName() + `放到：

%s`
		msgbox.Error("WARNING",
			message,
			license.FilePath())

		fmt.Println(``)
		fmt.Println(`To purchase a license, please go to our official website:`)
		fmt.Println(`购买授权请前往官方网站：`)
		fmt.Println(``)
		fmt.Println(license.ProductURL() + `?version=` + config.Version.Number + `&machineID=` + machineID)
		if event.MustLicensed {
			return nil
		}
	}

	//独立模块
	if config.DefaultCLIConfig.OnlyRunServer() {
		return nil
	}

	//Manager
	config.DefaultCLIConfig.RunStartup()

	if config.IsInstalled() {
		if err := setup.Upgrade(); err != nil && os.ErrNotExist != err {
			log.Error(`upgrade.sql: `, err)
		}
	}

	// LOGO
	fmt.Println(strings.TrimSuffix(figure.NewFigure(event.SofewareName, `big`, false).String(), "\n"), config.Version.VString()+"\n")

	event.Start()

	c := &engine.Config{
		ReusePort:   true,
		TLSAuto:     config.DefaultConfig.Sys.SSLAuto,
		TLSEmail:    config.DefaultConfig.Sys.SSLEmail,
		TLSHosts:    config.DefaultConfig.Sys.SSLHosts,
		TLSCacheDir: config.DefaultConfig.Sys.SSLCacheDir,
		TLSCertFile: config.DefaultConfig.Sys.SSLCertFile,
		TLSKeyFile:  config.DefaultConfig.Sys.SSLKeyFile,
	}
	c.Address = fmt.Sprintf(`%s:%v`, config.DefaultCLIConfig.Address, config.DefaultCLIConfig.Port)
	hasCert := (len(c.TLSCertFile) > 0 && len(c.TLSKeyFile) > 0)
	//c.TLSAuto = true
	if c.TLSAuto || hasCert {
		if config.DefaultCLIConfig.Port == 80 {
			if c.TLSAuto {
				if err := initCertMagic(c); err != nil {
					panic(err)
				}
				//c.SupportAutoTLS(nil, config.DefaultConfig.Sys.SSLHosts...)
			} else {
				c.Address = fmt.Sprintf(`%s:443`, config.DefaultCLIConfig.Address)
				e2 := echo.New()
				e2.Use(middleware.HTTPSRedirect(), middleware.Log(), middleware.Recover())
				go e2.Run(standard.New(fmt.Sprintf(`%s:80`, config.DefaultCLIConfig.Address)))
			}
			subdomains.Default.Protocol = `https`
		}
	}
	if len(event.Welcome) > 0 {
		now := time.Now()
		msgbox.Success(`Welcome`,
			event.Welcome,
			config.Version.VString(),
			now.Format("Monday, 02 Jan 2006"))
	}
	subdomains.Default.SetDebug(config.DefaultConfig.Debug)
	subdomains.Default.Run(standard.NewWithConfig(c))
	return c.Listener.Close()
}

func initCertMagic(c *engine.Config) error {
	fileStorage := &certmagic.FileStorage{
		Path: filepath.Join(echo.Wd(), `data`, `cache`, `certmagic`),
	}
	if !com.IsExist(fileStorage.Path) {
		if err := os.MkdirAll(fileStorage.Path, 0777); err != nil {
			return err
		}
	}
	if event.Develop { // use the staging endpoint while we're developing
		certmagic.Default.CA = certmagic.LetsEncryptStagingCA
	} else {
		certmagic.Default.CA = certmagic.LetsEncryptProductionCA
	}
	certmagic.Default.Email = c.TLSEmail
	certmagic.Default.Agreed = true
	certmagic.Default.Storage = fileStorage
	ln, err := certmagic.Listen(c.TLSHosts)
	if err == nil {
		c.SetListener(ln)
	}
	return err
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	Init()
	if len(rootCmd.Use) == 0 {
		rootCmd.Use = os.Args[0]
	}
	if err := rootCmd.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)

	config.DefaultCLIConfig.InitFlag(rootCmd.PersistentFlags())
}

// initConfig reads in config file and ENV variables if set.
func initConfig() {
}
<|endoftext|>