<commit_before>package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path"

	"github.com/boltdb/bolt"
	"github.com/gin-gonic/gin"
)

// AllowedIPs is a white/black list of
// IP addresses allowed to access cowyo
var AllowedIPs = map[string]bool{
	"192.168.1.13": true,
	"192.168.1.12": true,
	"192.168.1.2":  true,
}

// RuntimeArgs contains all runtime
// arguments available
var RuntimeArgs struct {
	WikiName         string
	ExternalIP       string
	Port             string
	DatabaseLocation string
	ServerCRT        string
	ServerKey        string
	SourcePath       string
	AdminKey         string
	Socket           string
	ForceWss         bool
}
var VersionNum string

func main() {
	VersionNum = "0.94"
	// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
	cwd, _ := os.Getwd()
	databaseFile := path.Join(cwd, "data.db")
	flag.StringVar(&RuntimeArgs.Port, "p", ":8003", "port to bind")
	flag.StringVar(&RuntimeArgs.DatabaseLocation, "db", databaseFile, "location of database file")
	flag.StringVar(&RuntimeArgs.AdminKey, "a", RandStringBytesMaskImprSrc(50), "key to access admin priveleges")
	flag.StringVar(&RuntimeArgs.ServerCRT, "crt", "", "location of ssl crt")
	flag.StringVar(&RuntimeArgs.ServerKey, "key", "", "location of ssl key")
	flag.StringVar(&RuntimeArgs.WikiName, "w", "cowyo", "custom name for wiki")
	flag.BoolVar(&RuntimeArgs.ForceWss, "e", false, "force encrypted sockets")
	dumpDataset := flag.Bool("dump", false, "flag to dump all data to 'dump' directory")
	flag.CommandLine.Usage = func() {
		fmt.Println(`cowyo (version ` + VersionNum + `): A Websocket Wiki and Kind Of A List Application
run this to start the server and then visit localhost at the port you specify
(see parameters).
Example: 'cowyo yourserver.com'
Example: 'cowyo -p :8080 localhost:8080'
Example: 'cowyo -db /var/lib/cowyo/db.bolt localhost:8003'
Example: 'cowyo -p :8080 -crt ssl/server.crt -key ssl/server.key localhost:8080'
Options:`)
		flag.CommandLine.PrintDefaults()
	}
	flag.Parse()

	if *dumpDataset {
		fmt.Println("Dumping data to 'dump' folder...")
		dumpEverything()
		os.Exit(1)
	}

	RuntimeArgs.ExternalIP = flag.Arg(0)
	if RuntimeArgs.ExternalIP == "" {
		RuntimeArgs.ExternalIP = GetLocalIP() + RuntimeArgs.Port
	}
	RuntimeArgs.SourcePath = cwd

	// create programdata bucket
	Open(RuntimeArgs.DatabaseLocation)

	err := db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte("programdata"))
		if err != nil {
			return fmt.Errorf("create bucket: %s", err)
		}
		return err
	})
	if err != nil {
		panic(err)
	}
	Close()

	// Default page
	aboutFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/aboutpage.md"))
	p := WikiData{"help", "", []string{}, []string{}, false, "zzz"}
	p.save(string(aboutFile))

	// var q WikiData
	// q.load("about")
	// fmt.Println(getImportantVersions(q))

	r := gin.Default()
	r.LoadHTMLGlob(path.Join(RuntimeArgs.SourcePath, "templates/*"))
	r.GET("/", newNote)
	r.HEAD("/", func(c *gin.Context) { c.Status(200) })
	r.GET("/:title", editNote)
	r.GET("/:title/*option", everythingElse)
	r.POST("/:title/*option", encryptionRoute)
	r.DELETE("/listitem", deleteListItem)
	r.DELETE("/deletepage", deletePage)
	if RuntimeArgs.ServerCRT != "" && RuntimeArgs.ServerKey != "" {
		RuntimeArgs.Socket = "wss"
		fmt.Println("--------------------------")
		fmt.Println("cowyo (version " + VersionNum + ") is up and running on https://" + RuntimeArgs.ExternalIP)
		fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
		fmt.Println("--------------------------")
		r.RunTLS(RuntimeArgs.Port, RuntimeArgs.ServerCRT, RuntimeArgs.ServerKey)
	} else {
		RuntimeArgs.Socket = "ws"
		if RuntimeArgs.ForceWss {
			RuntimeArgs.Socket = "wss"
		}
		fmt.Println("--------------------------")
		fmt.Println("cowyo (version " + VersionNum + ") is up and running on http://" + RuntimeArgs.ExternalIP)
		fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
		fmt.Println("--------------------------")
		r.Run(RuntimeArgs.Port)
	}
}
<commit_msg>Adding transfer ability<commit_after>package main

import (
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"path"

	"github.com/boltdb/bolt"
	"github.com/gin-gonic/gin"
)

// AllowedIPs is a white/black list of
// IP addresses allowed to access cowyo
var AllowedIPs = map[string]bool{
	"192.168.1.13": true,
	"192.168.1.12": true,
	"192.168.1.2":  true,
}

// RuntimeArgs contains all runtime
// arguments available
var RuntimeArgs struct {
	WikiName         string
	ExternalIP       string
	Port             string
	DatabaseLocation string
	ServerCRT        string
	ServerKey        string
	SourcePath       string
	AdminKey         string
	Socket           string
	ForceWss         bool
}
var VersionNum string

const _24K = (1 << 20) * 24

func main() {
	VersionNum = "0.94"
	// _, executableFile, _, _ := runtime.Caller(0) // get full path of this file
	cwd, _ := os.Getwd()
	databaseFile := path.Join(cwd, "data.db")
	flag.StringVar(&RuntimeArgs.Port, "p", ":8003", "port to bind")
	flag.StringVar(&RuntimeArgs.DatabaseLocation, "db", databaseFile, "location of database file")
	flag.StringVar(&RuntimeArgs.AdminKey, "a", RandStringBytesMaskImprSrc(50), "key to access admin priveleges")
	flag.StringVar(&RuntimeArgs.ServerCRT, "crt", "", "location of ssl crt")
	flag.StringVar(&RuntimeArgs.ServerKey, "key", "", "location of ssl key")
	flag.StringVar(&RuntimeArgs.WikiName, "w", "cowyo", "custom name for wiki")
	flag.BoolVar(&RuntimeArgs.ForceWss, "e", false, "force encrypted sockets")
	dumpDataset := flag.Bool("dump", false, "flag to dump all data to 'dump' directory")
	flag.CommandLine.Usage = func() {
		fmt.Println(`cowyo (version ` + VersionNum + `): A Websocket Wiki and Kind Of A List Application
run this to start the server and then visit localhost at the port you specify
(see parameters).
Example: 'cowyo yourserver.com'
Example: 'cowyo -p :8080 localhost:8080'
Example: 'cowyo -db /var/lib/cowyo/db.bolt localhost:8003'
Example: 'cowyo -p :8080 -crt ssl/server.crt -key ssl/server.key localhost:8080'
Options:`)
		flag.CommandLine.PrintDefaults()
	}
	flag.Parse()

	if *dumpDataset {
		fmt.Println("Dumping data to 'dump' folder...")
		dumpEverything()
		os.Exit(1)
	}

	RuntimeArgs.ExternalIP = flag.Arg(0)
	if RuntimeArgs.ExternalIP == "" {
		RuntimeArgs.ExternalIP = GetLocalIP() + RuntimeArgs.Port
	}
	RuntimeArgs.SourcePath = cwd

	// create programdata bucket
	Open(RuntimeArgs.DatabaseLocation)

	err := db.Update(func(tx *bolt.Tx) error {
		_, err := tx.CreateBucketIfNotExists([]byte("programdata"))
		if err != nil {
			return fmt.Errorf("create bucket: %s", err)
		}
		return err
	})
	if err != nil {
		panic(err)
	}
	Close()

	// Default page
	aboutFile, _ := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "templates/aboutpage.md"))
	p := WikiData{"help", "", []string{}, []string{}, false, "zzz"}
	p.save(string(aboutFile))

	// var q WikiData
	// q.load("about")
	// fmt.Println(getImportantVersions(q))

	r := gin.Default()
	r.LoadHTMLGlob(path.Join(RuntimeArgs.SourcePath, "templates/*"))
	r.GET("/", newNote)
	r.HEAD("/", func(c *gin.Context) { c.Status(200) })
	r.GET("/:title", editNote)
	r.PUT("/:title", func(c *gin.Context) {
		filename := c.Param("title")
		fmt.Println(filename)
		fmt.Println(c.Request.Body)
		fmt.Println(c.Request.ContentLength)
		fmt.Println(c.Request.Header)
		contentLength := c.Request.ContentLength
		var reader io.Reader
		reader = c.Request.Body
		if contentLength == -1 {
			// queue file to disk, because s3 needs content length
			var err error
			var f io.Reader

			f = reader

			var b bytes.Buffer

			n, err := io.CopyN(&b, f, _24K+1)
			if err != nil && err != io.EOF {
				log.Printf("%s", err.Error())
			}

			if n > _24K {
				file, err := ioutil.TempFile("./", "transfer-")
				if err != nil {
					log.Printf("%s", err.Error())
				}

				defer file.Close()

				n, err = io.Copy(file, io.MultiReader(&b, f))
				if err != nil {
					os.Remove(file.Name())
					log.Printf("%s", err.Error())
				}

				reader, err = os.Open(file.Name())
			} else {
				reader = bytes.NewReader(b.Bytes())
			}

			contentLength = n
		}
		buf := new(bytes.Buffer)
		buf.ReadFrom(reader)
		s := buf.String() // Does a complete copy of the bytes in the buffer.
		fmt.Println("---------------")
		fmt.Println(s)
		fmt.Println("---------------")
		fmt.Println(c.ContentType())
		fmt.Println(c.Request.Header)
		fmt.Println("---------------")
	})
	r.GET("/:title/*option", everythingElse)
	r.POST("/:title/*option", encryptionRoute)
	r.DELETE("/listitem", deleteListItem)
	r.DELETE("/deletepage", deletePage)
	if RuntimeArgs.ServerCRT != "" && RuntimeArgs.ServerKey != "" {
		RuntimeArgs.Socket = "wss"
		fmt.Println("--------------------------")
		fmt.Println("cowyo (version " + VersionNum + ") is up and running on https://" + RuntimeArgs.ExternalIP)
		fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
		fmt.Println("--------------------------")
		r.RunTLS(RuntimeArgs.Port, RuntimeArgs.ServerCRT, RuntimeArgs.ServerKey)
	} else {
		RuntimeArgs.Socket = "ws"
		if RuntimeArgs.ForceWss {
			RuntimeArgs.Socket = "wss"
		}
		fmt.Println("--------------------------")
		fmt.Println("cowyo (version " + VersionNum + ") is up and running on http://" + RuntimeArgs.ExternalIP)
		fmt.Println("Admin key: " + RuntimeArgs.AdminKey)
		fmt.Println("--------------------------")
		r.Run(RuntimeArgs.Port)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"flag"
	"runtime/pprof"

	"github.com/zhemao/glisp/interpreter"
	"github.com/zhemao/glisp/extensions"
)

func getLine(reader *bufio.Reader) (string, error) {
	line := make([]byte, 0)
	for {
		linepart, hasMore, err := reader.ReadLine()
		if err != nil {
			return "", err
		}
		line = append(line, linepart...)
		if !hasMore {
			break
		}
	}
	return string(line), nil
}

func isBalanced(str string) bool {
	parens := 0
	squares := 0

	for _, c := range str {
		switch c {
		case '(':
			parens++
		case ')':
			parens--
		case '[':
			squares++
		case ']':
			squares--
		}
	}

	return parens == 0 && squares == 0
}

func getExpression(reader *bufio.Reader) (string, error) {
	fmt.Printf("> ")
	line, err := getLine(reader)
	if err != nil {
		return "", err
	}
	for !isBalanced(line) {
		fmt.Printf(">> ")
		nextline, err := getLine(reader)
		if err != nil {
			return "", err
		}
		line += "\n" + nextline
	}
	return line, nil
}

func processDumpCommand(env *glisp.Glisp, args []string) {
	if len(args) == 0 {
		env.DumpEnvironment()
	} else {
		err := env.DumpFunctionByName(args[0])
		if err != nil {
			fmt.Println(err)
		}
	}
}

func repl(env *glisp.Glisp) {
	fmt.Printf("glisp version %s\n", glisp.Version())
	reader := bufio.NewReader(os.Stdin)

	for {
		line, err := getExpression(reader)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}

		parts := strings.Split(line, " ")
		if len(parts) == 0 {
			continue
		}

		if parts[0] == "quit" {
			break
		}

		if parts[0] == "dump" {
			processDumpCommand(env, parts[1:])
			continue
		}

		expr, err := env.EvalString(line)
		if err != nil {
			fmt.Print(env.GetStackTrace(err))
			env.Clear()
			continue
		}

		if expr != glisp.SexpNull {
			fmt.Println(expr.SexpString())
		}
	}
}

func runScript(env *glisp.Glisp, fname string) {
	file, err := os.Open(fname)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
	defer file.Close()

	err = env.LoadFile(file)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	_, err = env.Run()
	if err != nil {
		fmt.Print(env.GetStackTrace(err))
		os.Exit(-1)
	}
}

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")

func main() {
	env := glisp.NewGlisp()
	env.ImportEval()
	glispext.ImportRandom(env)
	glispext.ImportTime(env)

	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}
		pprof.StartCPUProfile(f)
		defer pprof.StopCPUProfile()
	}

	args := flag.Args()
	if len(args) > 0 {
		runScript(env, args[0])
	} else {
		repl(env)
	}
}
<commit_msg>add memory profiling<commit_after>package main

import (
	"bufio"
	"fmt"
	"os"
	"strings"
	"flag"
	"runtime/pprof"

	"github.com/zhemao/glisp/interpreter"
	"github.com/zhemao/glisp/extensions"
)

func getLine(reader *bufio.Reader) (string, error) {
	line := make([]byte, 0)
	for {
		linepart, hasMore, err := reader.ReadLine()
		if err != nil {
			return "", err
		}
		line = append(line, linepart...)
		if !hasMore {
			break
		}
	}
	return string(line), nil
}

func isBalanced(str string) bool {
	parens := 0
	squares := 0

	for _, c := range str {
		switch c {
		case '(':
			parens++
		case ')':
			parens--
		case '[':
			squares++
		case ']':
			squares--
		}
	}

	return parens == 0 && squares == 0
}

func getExpression(reader *bufio.Reader) (string, error) {
	fmt.Printf("> ")
	line, err := getLine(reader)
	if err != nil {
		return "", err
	}
	for !isBalanced(line) {
		fmt.Printf(">> ")
		nextline, err := getLine(reader)
		if err != nil {
			return "", err
		}
		line += "\n" + nextline
	}
	return line, nil
}

func processDumpCommand(env *glisp.Glisp, args []string) {
	if len(args) == 0 {
		env.DumpEnvironment()
	} else {
		err := env.DumpFunctionByName(args[0])
		if err != nil {
			fmt.Println(err)
		}
	}
}

func repl(env *glisp.Glisp) {
	fmt.Printf("glisp version %s\n", glisp.Version())
	reader := bufio.NewReader(os.Stdin)

	for {
		line, err := getExpression(reader)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}

		parts := strings.Split(line, " ")
		if len(parts) == 0 {
			continue
		}

		if parts[0] == "quit" {
			break
		}

		if parts[0] == "dump" {
			processDumpCommand(env, parts[1:])
			continue
		}

		expr, err := env.EvalString(line)
		if err != nil {
			fmt.Print(env.GetStackTrace(err))
			env.Clear()
			continue
		}

		if expr != glisp.SexpNull {
			fmt.Println(expr.SexpString())
		}
	}
}

func runScript(env *glisp.Glisp, fname string) {
	file, err := os.Open(fname)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
	defer file.Close()

	err = env.LoadFile(file)
	if err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}

	_, err = env.Run()

	if err != nil {
		fmt.Print(env.GetStackTrace(err))
		os.Exit(-1)
	}
}

var cpuprofile = flag.String("cpuprofile", "", "write cpu profile to file")
var memprofile = flag.String("memprofile", "", "write mem profile to file")

func main() {
	env := glisp.NewGlisp()
	env.ImportEval()
	glispext.ImportRandom(env)
	glispext.ImportTime(env)

	flag.Parse()
	if *cpuprofile != "" {
		f, err := os.Create(*cpuprofile)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}
		err = pprof.StartCPUProfile(f)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}
		defer pprof.StopCPUProfile()
	}

	args := flag.Args()
	if len(args) > 0 {
		runScript(env, args[0])
	} else {
		repl(env)
	}

	if *memprofile != "" {
		f, err := os.Create(*memprofile)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}
		defer f.Close()

		err = pprof.Lookup("heap").WriteTo(f, 1)
		if err != nil {
			fmt.Println(err)
			os.Exit(-1)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"net"
    "golang.org/x/crypto/openpgp"
)

func main() {
	listen()
}

func handleConn(conn net.Conn) {
    fmt.Println("CONNECTION BABE")
    status, err := bufio.NewReader(conn).ReadString('\n')
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf(status)
}

func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Println(err)
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println(err)
		}
		go handleConn(conn)
	}
}
<commit_msg>import<commit_after>package main

import (
	"bufio"
	"fmt"
	"net"
    //"golang.org/x/crypto/openpgp"
)

func main() {
	listen()
}

func handleConn(conn net.Conn) {
    fmt.Println("CONNECTION BABE")
    status, err := bufio.NewReader(conn).ReadString('\n')
    if err != nil {
        fmt.Println(err)
    }
    fmt.Printf(status)
}

func listen() {
	ln, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Println(err)
	}
	for {
		conn, err := ln.Accept()
		if err != nil {
			fmt.Println(err)
		}
		go handleConn(conn)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"

	"crypto/tls"
	"crypto/x509"
	"github.com/prometheus/client_golang/prometheus"
	"io/ioutil"
)

const (
	namespace = "elasticsearch"
	indexHTML = `

			<title>Elasticsearch Exporter</title>

Elasticsearch Exporter

			Metrics

	`
)

func main() {
	var (
		listenAddress      = flag.String("web.listen-address", ":9108", "Address to listen on for web interface and telemetry.")
		metricsPath        = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		esHostname         = flag.String("es.hostname", "localhost", "hostname of an Elasticsearch node, where client http is enabled.")
		esProtocol         = flag.String("es.protocol", "http", "http/https protocol of an Elasticsearch node")
		esPort             = flag.String("es.port", "9200", "Port of an Elasticsearch node 9200 or 443")
		esUser             = flag.String("es.user", "username", "HTTP username for basic auth of an Elasticsearch node.")
		esPassword         = flag.String("es.password", "password", "HTTP password for basic auth of an Elasticsearch node.")
		esTimeout          = flag.Duration("es.timeout", 5*time.Second, "Timeout for trying to get stats from Elasticsearch.")
		esAllNodes         = flag.Bool("es.all", false, "Export stats for all nodes in the cluster.")
		esCA               = flag.String("es.ca", "", "Path to PEM file that conains trusted CAs for the Elasticsearch connection.")
		esClientPrivateKey = flag.String("es.client-private-key", "", "Path to PEM file that conains the private key for client auth when connecting to Elasticsearch.")
		esClientCert       = flag.String("es.client-cert", "", "Path to PEM file that conains the corresponding cert for the private key to connect to Elasticsearch.")
	)
	flag.Parse()

	nodesStatsURI := *esProtocol + "://" + *esUser + ":" + *esPassword + "@" + *esHostname + ":" + *esPort + "/_nodes/_local/stats"
	if *esAllNodes {
		nodesStatsURI = *esProtocol + "://" + *esUser + ":" + *esPassword + "@" + *esHostname + ":" + *esPort + "/_nodes/stats"
	}
	clusterHealthURI := *esProtocol + "://" + *esUser + ":" + *esPassword + "@" + *esHostname + ":" + *esPort + "/_cluster/health"

	exporter := NewExporter(nodesStatsURI, clusterHealthURI, *esTimeout, *esAllNodes, createElasticSearchTlsConfig(*esCA, *esClientCert, *esClientPrivateKey))
	prometheus.MustRegister(exporter)

	log.Println("Starting Server:", *listenAddress)
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fmt.Sprintf(indexHTML, *metricsPath)))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

func createElasticSearchTlsConfig(pemFile, pemCertFile, pemPrivateKeyFile string) *tls.Config {
	if len(pemFile) <= 0 {
		return nil
	}
	rootCerts, err := loadCertificatesFrom(pemFile)
	if err != nil {
		log.Fatalf("Couldn't load root certificate from %s. Got %s.", pemFile, err)
	}
	if len(pemCertFile) > 0 && len(pemPrivateKeyFile) > 0 {
		clientPrivateKey, err := loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile)
		if err != nil {
			log.Fatalf("Couldn't setup client authentication. Got %s.", err)
		}
		return &tls.Config{
			RootCAs:      rootCerts,
			Certificates: []tls.Certificate{*clientPrivateKey},
		}
	} else {
		return &tls.Config{
			RootCAs: rootCerts,
		}
	}
}

func loadCertificatesFrom(pemFile string) (*x509.CertPool, error) {
	caCert, err := ioutil.ReadFile(pemFile)
	if err != nil {
		return nil, err
	}
	certificates := x509.NewCertPool()
	certificates.AppendCertsFromPEM(caCert)
	return certificates, nil
}

func loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile string) (*tls.Certificate, error) {
	privateKey, err := tls.LoadX509KeyPair(pemCertFile, pemPrivateKeyFile)
	if err != nil {
		return nil, err
	}
	return &privateKey, nil
}
<commit_msg>check for empty username<commit_after>package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	_ "net/http/pprof"
	"time"

	"crypto/tls"
	"crypto/x509"
	"github.com/prometheus/client_golang/prometheus"
	"io/ioutil"
)

const (
	namespace = "elasticsearch"
	indexHTML = `

			<title>Elasticsearch Exporter</title>

Elasticsearch Exporter

			Metrics

	`
)

func main() {
	var (
		listenAddress      = flag.String("web.listen-address", ":9108", "Address to listen on for web interface and telemetry.")
		metricsPath        = flag.String("web.telemetry-path", "/metrics", "Path under which to expose metrics.")
		esHostname         = flag.String("es.hostname", "localhost", "hostname of an Elasticsearch node, where client http is enabled.")
		esProtocol         = flag.String("es.protocol", "http", "http/https protocol of an Elasticsearch node")
		esPort             = flag.String("es.port", "9200", "Port of an Elasticsearch node 9200 or 443")
		esUser             = flag.String("es.user", "", "HTTP username for basic auth of an Elasticsearch node.")
		esPassword         = flag.String("es.password", "", "HTTP password for basic auth of an Elasticsearch node.")
		esTimeout          = flag.Duration("es.timeout", 5*time.Second, "Timeout for trying to get stats from Elasticsearch.")
		esAllNodes         = flag.Bool("es.all", false, "Export stats for all nodes in the cluster.")
		esCA               = flag.String("es.ca", "", "Path to PEM file that conains trusted CAs for the Elasticsearch connection.")
		esClientPrivateKey = flag.String("es.client-private-key", "", "Path to PEM file that conains the private key for client auth when connecting to Elasticsearch.")
		esClientCert       = flag.String("es.client-cert", "", "Path to PEM file that conains the corresponding cert for the private key to connect to Elasticsearch.")
	)
	flag.Parse()

	var authString  string

	if *esUser != "" {
		authString = *esUser + ":" + *esPassword + "@"
	} else {
		authString = nil
	}

	nodesStatsURI := *esProtocol + "://" + authString + *esHostname + ":" + *esPort + "/_nodes/_local/stats"
	if *esAllNodes {
		nodesStatsURI = *esProtocol + "://" + authString + *esHostname + ":" + *esPort + "/_nodes/stats"
	}
	clusterHealthURI := *esProtocol + "://" + *esHostname + ":" + *esPort + "/_cluster/health"

	exporter := NewExporter(nodesStatsURI, clusterHealthURI, *esTimeout, *esAllNodes, createElasticSearchTlsConfig(*esCA, *esClientCert, *esClientPrivateKey))
	prometheus.MustRegister(exporter)

	log.Println("Starting Server:", *listenAddress)
	http.Handle(*metricsPath, prometheus.Handler())
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(fmt.Sprintf(indexHTML, *metricsPath)))
	})
	log.Fatal(http.ListenAndServe(*listenAddress, nil))
}

func createElasticSearchTlsConfig(pemFile, pemCertFile, pemPrivateKeyFile string) *tls.Config {
	if len(pemFile) <= 0 {
		return nil
	}
	rootCerts, err := loadCertificatesFrom(pemFile)
	if err != nil {
		log.Fatalf("Couldn't load root certificate from %s. Got %s.", pemFile, err)
	}
	if len(pemCertFile) > 0 && len(pemPrivateKeyFile) > 0 {
		clientPrivateKey, err := loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile)
		if err != nil {
			log.Fatalf("Couldn't setup client authentication. Got %s.", err)
		}
		return &tls.Config{
			RootCAs:      rootCerts,
			Certificates: []tls.Certificate{*clientPrivateKey},
		}
	} else {
		return &tls.Config{
			RootCAs: rootCerts,
		}
	}
}

func loadCertificatesFrom(pemFile string) (*x509.CertPool, error) {
	caCert, err := ioutil.ReadFile(pemFile)
	if err != nil {
		return nil, err
	}
	certificates := x509.NewCertPool()
	certificates.AppendCertsFromPEM(caCert)
	return certificates, nil
}

func loadPrivateKeyFrom(pemCertFile, pemPrivateKeyFile string) (*tls.Certificate, error) {
	privateKey, err := tls.LoadX509KeyPair(pemCertFile, pemPrivateKeyFile)
	if err != nil {
		return nil, err
	}
	return &privateKey, nil
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/sha512"
	//"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"ponydownloader/settings"
	"strconv"

	"github.com/vaughan0/go-ini"
)

//	default variables
var (
	QDEPTH    int64       = 20    //Depth of the queue buffer - how many images are enqueued
	IMGDIR    string      = "img" //Default download directory
	TAG       string      = ""    //Default tag string is empty, it should be extracted from command line and only command line
	STARTPAGE int         = 1     //Default start page, derpiboo.ru 1-indexed
	STOPPAGE  int         = 0     //Default stop page, would stop parsing json when stop page is reached or site reaches the end of search
	elog      *log.Logger         //The logger for errors
)

func main() {

	fmt.Println("Derpiboo.ru Downloader version 0.2.0")

	elog, logfile := settings.SetLog() //setting up logging of errors

	defer logfile.Close() //Almost forgot. Always close the file in the end.

	config, err := ini.LoadFile("config.ini") // Loading default config file and checking for various errors.

	if os.IsNotExist(err) {
		elog.Fatalln("Config.ini does not exist, create it") //We can not live without config. We could, in theory, but writing default config if none exist can wait
	}

	if err != nil {
		elog.Panicln(err) //Oh, something is broken beyond my understanding. Sorry.
	}

	//Getting stuff from config, overwriting hardwired defaults when needed

	key, ok := config.Get("main", "key")

	if !ok || key == "" {
		elog.Println("'key' variable missing from 'main' section. It is vital for server-side filtering") //Empty key or key does not exist. Derpibooru works with this, but default image filter filters too much. Use key to set your own!
	}

	Q_temp, _ := config.Get("main", "workers")

	if Q_temp != "" {
		QDEPTH, err = strconv.ParseInt(Q_temp, 10, 0)

		if err != nil {
			elog.Fatalln("Wrong configuration: Depth of the buffer queue is not a number")

		}
	}

	ID_temp, _ := config.Get("main", "downdir")

	if ID_temp != "" {
		IMGDIR = ID_temp
	}

	//Here we are parsing all the flags. Command line argument hold priority to config. Except for 'key'. API-key is config-only

	flag.StringVar(&TAG, "t", TAG, "Tags to download")
	flag.IntVar(&STARTPAGE, "p", STARTPAGE, "Starting page for search")
	flag.IntVar(&STOPPAGE, "sp", STOPPAGE, "Stopping page for search, 0 - parse all all search pages")

	flag.Parse()

	if flag.NArg() == 0 && TAG == "" { //If no arguments after flags and empty/unchanged tag, what we should download? Sane end of line.
		log.SetPrefix("Done at ") //We can not do this with elog!
		log.Println("Nothing to download, bye!")
		os.Exit(0)
	}

	//Creating directory for downloads if it does not yet exist
	if err := os.MkdirAll(IMGDIR, 0644); err != nil { //Execute? No need to execute any image. Also, all those other users can not do anything beyond enjoying our images.
		elog.Fatalln(err) //We can not create folder for images, end of line.
	}

	//	Creating channels to pass info to downloader and to signal job well done
	imgdat := make(chan Image, QDEPTH) //Better leave default queue depth. Experiment shown that depth about 20 provides optimal perfomance on my system
	done := make(chan bool)

	if TAG == "" { //Because we can put imgid with flags. Why not?

		//	Checking argument for being a number and then getting image data

		imgid := flag.Arg(0) //0-indexed, unlike os.Args. os.Args[0] is path to program. It needs to be used later, when we are searching for what directory we are writing in
		_, err = strconv.Atoi(imgid)

		if err != nil {
			elog.Fatalln("Wrong input: can not parse", imgid, "as a number")
		}

		log.Println("Processing image No", imgid)

		go parseImg(imgdat, imgid, key) // Sending imgid to parser. Here validity is our problem

	} else {

		//	and here we send tags to getter/parser. Validity is server problem, mostly

		log.Println("Processing tags", TAG)
		go ParseTag(imgdat, TAG, key)
	}

	log.Println("Starting worker") //It would be funny if worker goroutine does not start
	go DlImg(imgdat, done)

	<-done
	log.SetPrefix("Done at ")
	log.Println("Finised")
	//And we are done here! Hooray!
	return
}

type Image struct {
	imgid    int
	url      string
	filename string
	hash     string
}

func ParseImg(imgchan chan<- Image, imgid string, key string) {

	source := "http://derpiboo.ru/images/" + imgid + ".json?nofav=&nocomments="
	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Getting image info at:", source)

	resp, err := http.Get(source) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
	if err != nil {
		elog.Println(err)
		return
	}

	defer resp.Body.Close() //and not forgetting to close it when it's done

	var dat map[string]interface{}

	body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
	if err != nil {
		elog.Println(err)
		return
	}

	if err := json.Unmarshal(body, &dat); //transforming json into native map

	err != nil {
		elog.Println(err)
		return
	}

	InfoToChannel(dat, imgchan)

	close(imgchan) //closing channel, we are done here

	return
}

func DlImg(imgchan <-chan Image, done chan bool) {

	fmt.Println("Worker started; reading channel") //nice notification that we are not forgotten

	for {

		imgdata, more := <-imgchan

		if more { //checking that there is an image in channel

			if imgdata.filename == "" {
				elog.Println("Empty filename. Oops?") //something somewhere had gone wrong, going to the next image
			} else {

				fmt.Println("Saving as", imgdata.filename)

				func() { // to not hold all the files open when there is no need

					output, err := os.Create(IMGDIR + string(os.PathSeparator) + imgdata.filename) //And now, THE FILE!
					if err != err {
						elog.Println("Error when creating file for image" + strconv.Itoa(imgdata.imgid))
						elog.Println(err)
						return
					}
					defer output.Close() //Not forgetting to deal with it after completing download

					response, err := http.Get(imgdata.url)
					if err != nil {
						elog.Println("Error when getting image", imgdata.imgid)
						elog.Println(err)
						return
					}
					defer response.Body.Close() //Same, we shall not listen to the void when we finished getting image

					hash := sha512.New()

					io.Copy(io.MultiWriter(output, hash), response.Body) //	Writing things we got from Derpibooru into the

					b := make([]byte, hash.Size())
					hash.Sum(b[:0])

					//	fmt.Println("\n", hex.EncodeToString(b), "\n", imgdata.hash )

					//if hex.EncodeToString(b) != imgdata.hash {
						//elog.Println("Hash wrong with imageid", imgdata.imgid)
					//}
				}()
			}

			//fmt.Println("\n", hex.EncodeToString(hash.Sum(nil)), "\n", imgdata.hash )

		} else {
			done <- true //well, there is no images in channel, it means we got them all, so synchronization is kicking in and ending the process
			break        //Just in case, so it would not stupidly die when program finishes - it will die smartly

		}
	}
}

func ParseTag(imgchan chan<- Image, tag string, key string) {

	source := "http://derpiboo.ru/search.json?nofav=&nocomments=" //yay hardwiring url strings!

	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Searching as", source+"&q="+tag)
	var working bool = true
	i := STARTPAGE
	for working {
		func() {
			fmt.Println("Searching page", i)
			resp, err := http.Get(source + "&q=" + tag + "&page=" + strconv.Itoa(i)) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
			defer resp.Body.Close()                                                  //and not forgetting to close it when it's done. And before we panic and die horribly.
			if err != nil {
				elog.Println("Error while getting search page", i)
				elog.Println(err)
				return
			}

			var dats []map[string]interface{} //Because we got array incoming instead of single object, we using an slive of maps!

			//fmt.Println(resp)

			body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
			if err != nil {
				elog.Println("Error while reading search page", i)
				elog.Println(err)
				return
			}

			//fmt.Println(body)

			if err := json.Unmarshal(body, &dats); //transforming json into native slice of maps

			err != nil {
				elog.Println("Error while parsing search page", i)
				elog.Println(err)
				return

			}

			if len(dats) == 0 {
				fmt.Println("Pages are over")
				working = false
				return
			} //exit due to finishing all pages

			for _, dat := range dats {
				InfoToChannel(dat, imgchan)
			}
			if i == STOPPAGE {
				working = false
				return
			}
			i++

		}()
	}

	close(imgchan)
}

func InfoToChannel(dat map[string]interface{}, imgchan chan<- Image) {

	var imgdata Image

	imgdata.url = "http:" + dat["image"].(string)
	imgdata.hash = dat["sha512_hash"].(string)
	imgdata.filename = (strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + "." + dat["file_name"].(string) + "." + dat["original_format"].(string))
	imgdata.imgid = int(dat["id_number"].(float64))

	//	for troubleshooting - possibly debug flag?
	//	fmt.Println(dat)
	//	fmt.Println(imgdata.url)
	//	fmt.Println(imgdata.hash)
	//	fmt.Println(imgdata.filename)

	imgchan <- imgdata
}
<commit_msg>ouch, forgot in the merge<commit_after>package main

import (
	"crypto/sha512"
	//"encoding/hex"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"ponydownloader/settings"
	"strconv"

	"github.com/vaughan0/go-ini"
)

//	default variables
var (
	QDEPTH    int64       = 20    //Depth of the queue buffer - how many images are enqueued
	IMGDIR    string      = "img" //Default download directory
	TAG       string      = ""    //Default tag string is empty, it should be extracted from command line and only command line
	STARTPAGE int         = 1     //Default start page, derpiboo.ru 1-indexed
	STOPPAGE  int         = 0     //Default stop page, would stop parsing json when stop page is reached or site reaches the end of search
	elog      *log.Logger         //The logger for errors
)

func main() {

	fmt.Println("Derpiboo.ru Downloader version 0.2.0")

	elog, logfile := settings.SetLog() //setting up logging of errors

	defer logfile.Close() //Almost forgot. Always close the file in the end.

	config, err := ini.LoadFile("config.ini") // Loading default config file and checking for various errors.

	if os.IsNotExist(err) {
		elog.Fatalln("Config.ini does not exist, create it") //We can not live without config. We could, in theory, but writing default config if none exist can wait
	}

	if err != nil {
		elog.Panicln(err) //Oh, something is broken beyond my understanding. Sorry.
	}

	//Getting stuff from config, overwriting hardwired defaults when needed

	key, ok := config.Get("main", "key")

	if !ok || key == "" {
		elog.Println("'key' variable missing from 'main' section. It is vital for server-side filtering") //Empty key or key does not exist. Derpibooru works with this, but default image filter filters too much. Use key to set your own!
	}

	Q_temp, _ := config.Get("main", "workers")

	if Q_temp != "" {
		QDEPTH, err = strconv.ParseInt(Q_temp, 10, 0)

		if err != nil {
			elog.Fatalln("Wrong configuration: Depth of the buffer queue is not a number")

		}
	}

	ID_temp, _ := config.Get("main", "downdir")

	if ID_temp != "" {
		IMGDIR = ID_temp
	}

	//Here we are parsing all the flags. Command line argument hold priority to config. Except for 'key'. API-key is config-only

	flag.StringVar(&TAG, "t", TAG, "Tags to download")
	flag.IntVar(&STARTPAGE, "p", STARTPAGE, "Starting page for search")
	flag.IntVar(&STOPPAGE, "sp", STOPPAGE, "Stopping page for search, 0 - parse all all search pages")

	flag.Parse()

	if flag.NArg() == 0 && TAG == "" { //If no arguments after flags and empty/unchanged tag, what we should download? Sane end of line.
		log.SetPrefix("Done at ") //We can not do this with elog!
		log.Println("Nothing to download, bye!")
		os.Exit(0)
	}

	//Creating directory for downloads if it does not yet exist
	if err := os.MkdirAll(IMGDIR, 0644); err != nil { //Execute? No need to execute any image. Also, all those other users can not do anything beyond enjoying our images.
		elog.Fatalln(err) //We can not create folder for images, end of line.
	}

	//	Creating channels to pass info to downloader and to signal job well done
	imgdat := make(chan Image, QDEPTH) //Better leave default queue depth. Experiment shown that depth about 20 provides optimal perfomance on my system
	done := make(chan bool)

	if TAG == "" { //Because we can put imgid with flags. Why not?

		//	Checking argument for being a number and then getting image data

		imgid := flag.Arg(0) //0-indexed, unlike os.Args. os.Args[0] is path to program. It needs to be used later, when we are searching for what directory we are writing in
		_, err = strconv.Atoi(imgid)

		if err != nil {
			elog.Fatalln("Wrong input: can not parse", imgid, "as a number")
		}

		log.Println("Processing image No", imgid)

		go ParseImg(imgdat, imgid, key) // Sending imgid to parser. Here validity is our problem

	} else {

		//	and here we send tags to getter/parser. Validity is server problem, mostly

		log.Println("Processing tags", TAG)
		go ParseTag(imgdat, TAG, key)
	}

	log.Println("Starting worker") //It would be funny if worker goroutine does not start
	go DlImg(imgdat, done)

	<-done
	log.SetPrefix("Done at ")
	log.Println("Finised")
	//And we are done here! Hooray!
	return
}

type Image struct {
	imgid    int
	url      string
	filename string
	hash     string
}

func ParseImg(imgchan chan<- Image, imgid string, key string) {

	source := "http://derpiboo.ru/images/" + imgid + ".json?nofav=&nocomments="
	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Getting image info at:", source)

	resp, err := http.Get(source) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
	if err != nil {
		elog.Println(err)
		return
	}

	defer resp.Body.Close() //and not forgetting to close it when it's done

	var dat map[string]interface{}

	body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
	if err != nil {
		elog.Println(err)
		return
	}

	if err := json.Unmarshal(body, &dat); //transforming json into native map

	err != nil {
		elog.Println(err)
		return
	}

	InfoToChannel(dat, imgchan)

	close(imgchan) //closing channel, we are done here

	return
}

func DlImg(imgchan <-chan Image, done chan bool) {

	fmt.Println("Worker started; reading channel") //nice notification that we are not forgotten

	for {

		imgdata, more := <-imgchan

		if more { //checking that there is an image in channel

			if imgdata.filename == "" {
				elog.Println("Empty filename. Oops?") //something somewhere had gone wrong, going to the next image
			} else {

				fmt.Println("Saving as", imgdata.filename)

				func() { // to not hold all the files open when there is no need

					output, err := os.Create(IMGDIR + string(os.PathSeparator) + imgdata.filename) //And now, THE FILE!
					if err != err {
						elog.Println("Error when creating file for image" + strconv.Itoa(imgdata.imgid))
						elog.Println(err)
						return
					}
					defer output.Close() //Not forgetting to deal with it after completing download

					response, err := http.Get(imgdata.url)
					if err != nil {
						elog.Println("Error when getting image", imgdata.imgid)
						elog.Println(err)
						return
					}
					defer response.Body.Close() //Same, we shall not listen to the void when we finished getting image

					hash := sha512.New()

					io.Copy(io.MultiWriter(output, hash), response.Body) //	Writing things we got from Derpibooru into the

					b := make([]byte, hash.Size())
					hash.Sum(b[:0])

					//	fmt.Println("\n", hex.EncodeToString(b), "\n", imgdata.hash )

					//if hex.EncodeToString(b) != imgdata.hash {
						//elog.Println("Hash wrong with imageid", imgdata.imgid)
					//}
				}()
			}

			//fmt.Println("\n", hex.EncodeToString(hash.Sum(nil)), "\n", imgdata.hash )

		} else {
			done <- true //well, there is no images in channel, it means we got them all, so synchronization is kicking in and ending the process
			break        //Just in case, so it would not stupidly die when program finishes - it will die smartly

		}
	}
}

func ParseTag(imgchan chan<- Image, tag string, key string) {

	source := "http://derpiboo.ru/search.json?nofav=&nocomments=" //yay hardwiring url strings!

	if key != "" {
		source = source + "&key=" + key
	}

	fmt.Println("Searching as", source+"&q="+tag)
	var working bool = true
	i := STARTPAGE
	for working {
		func() {
			fmt.Println("Searching page", i)
			resp, err := http.Get(source + "&q=" + tag + "&page=" + strconv.Itoa(i)) //Getting our nice http response. Needs checking for 404 and other responses that are... less expected
			defer resp.Body.Close()                                                  //and not forgetting to close it when it's done. And before we panic and die horribly.
			if err != nil {
				elog.Println("Error while getting search page", i)
				elog.Println(err)
				return
			}

			var dats []map[string]interface{} //Because we got array incoming instead of single object, we using an slive of maps!

			//fmt.Println(resp)

			body, err := ioutil.ReadAll(resp.Body) //stolen from official documentation
			if err != nil {
				elog.Println("Error while reading search page", i)
				elog.Println(err)
				return
			}

			//fmt.Println(body)

			if err := json.Unmarshal(body, &dats); //transforming json into native slice of maps

			err != nil {
				elog.Println("Error while parsing search page", i)
				elog.Println(err)
				return

			}

			if len(dats) == 0 {
				fmt.Println("Pages are over")
				working = false
				return
			} //exit due to finishing all pages

			for _, dat := range dats {
				InfoToChannel(dat, imgchan)
			}
			if i == STOPPAGE {
				working = false
				return
			}
			i++

		}()
	}

	close(imgchan)
}

func InfoToChannel(dat map[string]interface{}, imgchan chan<- Image) {

	var imgdata Image

	imgdata.url = "http:" + dat["image"].(string)
	imgdata.hash = dat["sha512_hash"].(string)
	imgdata.filename = (strconv.FormatFloat(dat["id_number"].(float64), 'f', -1, 64) + "." + dat["file_name"].(string) + "." + dat["original_format"].(string))
	imgdata.imgid = int(dat["id_number"].(float64))

	//	for troubleshooting - possibly debug flag?
	//	fmt.Println(dat)
	//	fmt.Println(imgdata.url)
	//	fmt.Println(imgdata.hash)
	//	fmt.Println(imgdata.filename)

	imgchan <- imgdata
}
<|endoftext|>


<commit_before>// Serve Directory
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"

	"github.com/gorilla/mux"
)

type HumanReadableSize int64

func (hrSize HumanReadableSize) String() string {
	unit := ""
	fltSize := float64(hrSize)
	if fltSize > 1024 {
		unit = "K"
		fltSize /= 1024.0
		if fltSize > 1024 {
			unit = "M"
			fltSize /= 1024.0
			if fltSize > 1024 {
				unit = "G"
				fltSize /= 1024.0
			}
		}
	}
	return fmt.Sprintf("%0.1f%s", fltSize, unit)
}

func BrowseDirectory(w http.ResponseWriter, r *http.Request) {
	upath, _ := url.QueryUnescape(mux.Vars(r)["directory"])
	if upath == "" {
		upath = "."
	}
	if pinfo, sErr := os.Stat(upath); sErr != nil {
		http.Error(w, sErr.Error(), http.StatusNotFound)
	} else if pinfo.IsDir() {
		entries, err := ioutil.ReadDir(upath)
		if err != nil {
			http.Error(w, err.Error(), http.StatusNotFound)
		}
		fmt.Fprintf(w, "<title>Index of %s</title>\n", upath)
		fmt.Fprintln(w, "")
		fmt.Fprintf(w, "
Index of %s
\n
\n", upath)
		fmt.Fprintln(w, "
")
		fmt.Fprintln(w, "
Name	Last Modified	Size
")
		if upath != "." {
			parent, _ := filepath.Split(upath[:len(upath)-1])
			fmt.Fprintf(w, "
Parent Directory		 -
", parent)
		}
		for _, info := range entries {
			hrSize := HumanReadableSize(info.Size())
			dir := ""
			if info.IsDir() {
				dir = "/"
			}
			fmt.Fprintf(w, "
%s	%s	%s
", info.Name()+dir, info.Name()+dir, info.ModTime().Format("2006-01-02 15:04:05 -0700"), hrSize.String())
		}
		fmt.Fprintln(w, "

Golang servedir")
	} else {
		http.ServeFile(w, r, upath)
	}
}

func main() {
	r := mux.NewRouter()
	r.Path("/{directory:.*}").HandlerFunc(BrowseDirectory)
	http.Handle("/", r)

	var port int
	var localhost bool
	flag.IntVar(&port, "port", 8080, "Port number.")
	flag.BoolVar(&localhost, "localhost", false, "Bind to 127.0.0.1 only.")
	flag.Parse()

	serveIP := ""
	if localhost {
		serveIP = "127.0.0.1"
	}
	http.ListenAndServe(fmt.Sprintf("%s:%d", serveIP, port), nil)
}
<commit_msg>Use the whole width of window.<commit_after>// Serve Directory
package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"

	"github.com/gorilla/mux"
)

type HumanReadableSize int64

func (hrSize HumanReadableSize) String() string {
	unit := ""
	fltSize := float64(hrSize)
	if fltSize > 1024 {
		unit = "K"
		fltSize /= 1024.0
		if fltSize > 1024 {
			unit = "M"
			fltSize /= 1024.0
			if fltSize > 1024 {
				unit = "G"
				fltSize /= 1024.0
			}
		}
	}
	return fmt.Sprintf("%0.1f%s", fltSize, unit)
}

func BrowseDirectory(w http.ResponseWriter, r *http.Request) {
	upath, _ := url.QueryUnescape(mux.Vars(r)["directory"])
	if upath == "" {
		upath = "."
	}
	if pinfo, sErr := os.Stat(upath); sErr != nil {
		http.Error(w, sErr.Error(), http.StatusNotFound)
	} else if pinfo.IsDir() {
		entries, err := ioutil.ReadDir(upath)
		if err != nil {
			http.Error(w, err.Error(), http.StatusNotFound)
		}
		fmt.Fprintf(w, "<title>Index of %s</title>\n", upath)
		fmt.Fprintln(w, "")
		fmt.Fprintf(w, "
Index of %s
\n
\n", upath)
		fmt.Fprintln(w, "
")
		fmt.Fprintln(w, "
Name	Last Modified	Size
")
		if upath != "." {
			parent, _ := filepath.Split(upath[:len(upath)-1])
			fmt.Fprintf(w, "
Parent Directory		 -
", parent)
		}
		for _, info := range entries {
			hrSize := HumanReadableSize(info.Size())
			if info.IsDir() {
				fmt.Fprintf(w, "
%s/	%s	%s
", info.Name(), info.Name(), info.ModTime().Format("2006-01-02 15:04:05 -0700"), " - ")
			} else {
				fmt.Fprintf(w, "
%s	%s	%s
", info.Name(), info.Name(), info.ModTime().Format("2006-01-02 15:04:05 -0700"), hrSize.String())
			}
		}
		fmt.Fprintln(w, "

Golang servedir")
	} else {
		http.ServeFile(w, r, upath)
	}
}

func main() {
	r := mux.NewRouter()
	r.Path("/{directory:.*}").HandlerFunc(BrowseDirectory)
	http.Handle("/", r)

	var port int
	var localhost bool
	flag.IntVar(&port, "port", 8080, "Port number.")
	flag.BoolVar(&localhost, "localhost", false, "Bind to 127.0.0.1 only.")
	flag.Parse()

	serveIP := ""
	if localhost {
		serveIP = "127.0.0.1"
	}
	http.ListenAndServe(fmt.Sprintf("%s:%d", serveIP, port), nil)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/koding/klient/collaboration"
	"github.com/koding/klient/command"
	"github.com/koding/klient/fs"
	"github.com/koding/klient/protocol"
	"github.com/koding/klient/terminal"
	"github.com/koding/klient/usage"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite/config"
)

var (
	flagIP          = flag.String("ip", "", "Change public ip")
	flagPort        = flag.Int("port", 56789, "Change running port")
	flagVersion     = flag.Bool("version", false, "Show version and exit")
	flagEnvironment = flag.String("env", protocol.Environment, "Change environment")
	flagRegion      = flag.String("region", protocol.Region, "Change region")
	flagRegisterURL = flag.String("register-url", "", "Change register URL to kontrol")
	flagDebug       = flag.Bool("debug", false, "Debug mode")

	// update parameters
	flagUpdateInterval = flag.Duration("update-interval", time.Minute*5,
		"Change interval for checking for new updates")
	flagUpdateURL = flag.String("update-url",
		"https://s3.amazonaws.com/koding-klient/"+protocol.Environment+"/latest-version.txt",
		"Change update endpoint for latest version")

	VERSION = protocol.Version
	NAME    = protocol.Name

	// this is our main reference to count and measure metrics for the klient
	// we count only those methods, please add/remove methods here that will
	// reset the timer of a klient.
	usg = usage.NewUsage(map[string]bool{
		"fs.readDirectory":    true,
		"fs.glob":             true,
		"fs.readFile":         true,
		"fs.writeFile":        true,
		"fs.uniquePath":       true,
		"fs.getInfo":          true,
		"fs.setPermissions":   true,
		"fs.remove":           true,
		"fs.rename":           true,
		"fs.createDirectory":  true,
		"fs.move":             true,
		"fs.copy":             true,
		"webterm.getSessions": true,
		"webterm.connect":     true,
		"webterm.killSession": true,
		"exec":                true,
		"klient.share":        true,
		"klient.unshare":      true,
		"klient.shared":       true,
		// Disabled until we have Docker support, no need to bloat the binary :)
		// "docker.create":       true,
		// "docker.connect":      true,
		// "docker.stop":         true,
		// "docker.start":        true,
		// "docker.remove":       true,
		// "docker.list":         true,
	})

	// this is used to allow other users to call any klient method.
	collab = collaboration.New()

	// we also could use an atomic boolean this is simple for now.
	updating   = false
	updatingMu sync.Mutex // protects updating
)

func main() {
	flag.Parse()
	if *flagVersion {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	k := newKite()

	// Close the klient.db in any case. Corrupt db would be catastrophic
	defer collab.Close()

	k.Log.Info("Running as version %s", VERSION)
	k.Run()
}

func newKite() *kite.Kite {
	k := kite.New(NAME, VERSION)

	if *flagDebug {
		k.SetLogLevel(kite.DEBUG)
	}

	conf := config.MustGet()
	k.Config = conf
	k.Config.Port = *flagPort
	k.Config.Environment = *flagEnvironment
	k.Config.Region = *flagRegion
	k.Id = conf.Id // always boot up with the same id in the kite.key

	if *flagUpdateInterval < time.Minute {
		k.Log.Warning("Update interval can't be less than one minute. Setting to one minute.")
		*flagUpdateInterval = time.Minute
	}

	updater := &Updater{
		Endpoint: *flagUpdateURL,
		Interval: *flagUpdateInterval,
		Log:      k.Log,
	}

	// before we register check for latest update and re-update itself before
	// we continue
	k.Log.Info("Checking for new updates")
	if err := updater.checkAndUpdate(); err != nil {
		k.Log.Warning("Self-update: %s", err)
	}

	go updater.Run()

	userIn := func(user string, users ...string) bool {
		for _, u := range users {
			if u == user {
				return true
			}
		}
		return false
	}

	// don't pass any request if the caller is outside of our scope.
	// don't allow anyone to call a method if we are during an update.
	k.PreHandleFunc(func(r *kite.Request) (interface{}, error) {
		// only authenticated methods have correct username. For example
		// kite.ping has authentication disabled so username can be empty.
		if r.Auth != nil {
			// Koding (kloud) connects to much, don't display it.
			if r.Username != "koding" {
				k.Log.Info("Kite '%s/%s/%s' called method: '%s'",
					r.Username, r.Client.Environment, r.Client.Name, r.Method)
			}

			// Allow these users by default
			allowedUsers := []string{k.Config.Username, "koding"}

			// Allow collaboration users as well
			sharedUsers, err := collab.GetAll()
			if err != nil {
				return nil, fmt.Errorf("Can't read shared users from the storage. Err: %v", err)
			}

			sharedUsernames := make([]string, 0)
			for username := range sharedUsers {
				sharedUsernames = append(sharedUsernames, username)
			}

			allowedUsers = append(allowedUsers, sharedUsernames...)

			if !userIn(r.Username, allowedUsers...) {
				return nil, fmt.Errorf("User '%s' is not allowed to make a call to us.", r.Username)
			}
		}

		updatingMu.Lock()
		defer updatingMu.Unlock()

		if updating {
			return nil, errors.New("Updating klient. Can't accept any method.")
		}

		return true, nil
	})

	// Metrics, is used by Kloud to get usage so Kloud can stop free VMs
	k.PreHandleFunc(usg.Counter) // we measure every incoming request
	k.HandleFunc("klient.usage", usg.Current)

	// Collaboration, is used by our Koding.com browser client.
	k.HandleFunc("klient.share", collab.Share)
	k.HandleFunc("klient.unshare", collab.Unshare)
	k.HandleFunc("klient.shared", collab.Shared)

	// Filesystem
	k.HandleFunc("fs.readDirectory", fs.ReadDirectory)
	k.HandleFunc("fs.glob", fs.Glob)
	k.HandleFunc("fs.readFile", fs.ReadFile)
	k.HandleFunc("fs.writeFile", fs.WriteFile)
	k.HandleFunc("fs.uniquePath", fs.UniquePath)
	k.HandleFunc("fs.getInfo", fs.GetInfo)
	k.HandleFunc("fs.setPermissions", fs.SetPermissions)
	k.HandleFunc("fs.remove", fs.Remove)
	k.HandleFunc("fs.rename", fs.Rename)
	k.HandleFunc("fs.createDirectory", fs.CreateDirectory)
	k.HandleFunc("fs.move", fs.Move)
	k.HandleFunc("fs.copy", fs.Copy)

	// // Docker
	// Disabled until we have Docker support, no need to bloat the binary :)
	// dock := docker.New("unix:///var/run/docker.sock", k.Log)
	// k.HandleFunc("docker.create", dock.Create)
	// k.HandleFunc("docker.connect", dock.Connect)
	// k.HandleFunc("docker.stop", dock.Stop)
	// k.HandleFunc("docker.start", dock.Start)
	// k.HandleFunc("docker.remove", dock.RemoveContainer)
	// k.HandleFunc("docker.list", dock.List)

	// Execution
	k.HandleFunc("exec", command.Exec)

	// Terminal
	term := terminal.New(k.Log)
	term.InputHook = usg.Reset
	k.HandleFunc("webterm.getSessions", term.GetSessions)
	k.HandleFunc("webterm.connect", term.Connect)
	k.HandleFunc("webterm.killSession", term.KillSession)
	k.HandleFunc("webterm.killSessions", term.KillSessions)

	var disconnectTimer *time.Timer

	k.OnFirstRequest(func(c *kite.Client) {
		// Koding (kloud) connects to much, don't display it.
		if c.Username != "koding" {
			k.Log.Info("Kite '%s/%s/%s' is connected", c.Username, c.Environment, c.Name)
		}

		if c.Username != k.Config.Username {
			return // we don't care for others
		}

		// it's still not initialized, so don't do anything
		if disconnectTimer != nil {
			// stop previously started disconnect timer.
			k.Log.Info("Disconnection timer is cancelled.")
			disconnectTimer.Stop()
		}

	})

	// Unshare collab users if the klient owner disconnects
	k.OnDisconnect(func(c *kite.Client) {
		// Koding (kloud) connects to much, don't display it.
		if c.Username != "koding" {
			k.Log.Info("Kite '%s/%s/%s' is disconnected", c.Username, c.Environment, c.Name)
		}

		if c.Username != k.Config.Username {
			return // we don't care for others
		}

		// if there is any previously created timers stop them so we don't leak
		// goroutines
		if disconnectTimer != nil {
			disconnectTimer.Stop()
		}

		k.Log.Info("Disconnection timer of 1 minutes is fired.")
		disconnectTimer = time.NewTimer(time.Minute * 1)

		// Close all active sessions of the current. Do not close it
		// immediately, instead of give some time so users can safely exit. If
		// the user reconnects again the timer will be stopped so we don't
		// unshare for network hiccups accidentally.
		go func() {
			select {
			case <-disconnectTimer.C:
				sharedUsers, err := collab.GetAll()
				if err != nil {
					k.Log.Warning("Couldn't unshare users: '%s'", err)
					return
				}

				if len(sharedUsers) == 0 {
					return // nothing to do ...
				}

				k.Log.Info("Unsharing users '%s'", sharedUsers)
				for user, option := range sharedUsers {
					// dont touch permanent users
					if option.Permanent {
						k.Log.Info("User is permanent, avoiding it: '%s'", user)
						continue
					}

					if err := collab.Delete(user); err != nil {
						k.Log.Warning("Couldn't delete user from storage: '%s'", err)
					}
					term.CloseSessions(user)
				}
			}
		}()
	})

	if err := register(k); err != nil {
		panic(err)
	}

	return k
}

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func HasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Given a string of the form "host", "port", returns "host:port"
func AddPort(host, port string) string {
	if ok := HasPort(host); ok {
		return host
	}

	return host + ":" + port
}
<commit_msg>main: do not update immediately, refactor auth check<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"os"
	"strings"
	"sync"
	"time"

	"github.com/koding/klient/collaboration"
	"github.com/koding/klient/command"
	"github.com/koding/klient/fs"
	"github.com/koding/klient/protocol"
	"github.com/koding/klient/terminal"
	"github.com/koding/klient/usage"

	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite"
	"github.com/koding/klient/Godeps/_workspace/src/github.com/koding/kite/config"
)

var (
	flagIP          = flag.String("ip", "", "Change public ip")
	flagPort        = flag.Int("port", 56789, "Change running port")
	flagVersion     = flag.Bool("version", false, "Show version and exit")
	flagEnvironment = flag.String("env", protocol.Environment, "Change environment")
	flagRegion      = flag.String("region", protocol.Region, "Change region")
	flagRegisterURL = flag.String("register-url", "", "Change register URL to kontrol")
	flagDebug       = flag.Bool("debug", false, "Debug mode")

	// update parameters
	flagUpdateInterval = flag.Duration("update-interval", time.Minute*5,
		"Change interval for checking for new updates")
	flagUpdateURL = flag.String("update-url",
		"https://s3.amazonaws.com/koding-klient/"+protocol.Environment+"/latest-version.txt",
		"Change update endpoint for latest version")

	VERSION = protocol.Version
	NAME    = protocol.Name

	// this is our main reference to count and measure metrics for the klient
	// we count only those methods, please add/remove methods here that will
	// reset the timer of a klient.
	usg = usage.NewUsage(map[string]bool{
		"fs.readDirectory":    true,
		"fs.glob":             true,
		"fs.readFile":         true,
		"fs.writeFile":        true,
		"fs.uniquePath":       true,
		"fs.getInfo":          true,
		"fs.setPermissions":   true,
		"fs.remove":           true,
		"fs.rename":           true,
		"fs.createDirectory":  true,
		"fs.move":             true,
		"fs.copy":             true,
		"webterm.getSessions": true,
		"webterm.connect":     true,
		"webterm.killSession": true,
		"exec":                true,
		"klient.share":        true,
		"klient.unshare":      true,
		"klient.shared":       true,
		// Disabled until we have Docker support, no need to bloat the binary :)
		// "docker.create":       true,
		// "docker.connect":      true,
		// "docker.stop":         true,
		// "docker.start":        true,
		// "docker.remove":       true,
		// "docker.list":         true,
	})

	// this is used to allow other users to call any klient method.
	collab = collaboration.New()

	// we also could use an atomic boolean this is simple for now.
	updating   = false
	updatingMu sync.Mutex // protects updating
)

func main() {
	flag.Parse()
	if *flagVersion {
		fmt.Println(VERSION)
		os.Exit(0)
	}

	k := newKite()

	// Close the klient.db in any case. Corrupt db would be catastrophic
	defer collab.Close()

	k.Log.Info("Running as version %s", VERSION)
	k.Run()
}

func newKite() *kite.Kite {
	k := kite.New(NAME, VERSION)

	if *flagDebug {
		k.SetLogLevel(kite.DEBUG)
	}

	conf := config.MustGet()
	k.Config = conf
	k.Config.Port = *flagPort
	k.Config.Environment = *flagEnvironment
	k.Config.Region = *flagRegion
	k.Id = conf.Id // always boot up with the same id in the kite.key

	if *flagUpdateInterval < time.Minute {
		k.Log.Warning("Update interval can't be less than one minute. Setting to one minute.")
		*flagUpdateInterval = time.Minute
	}

	// start our updater in the background
	updater := &Updater{
		Endpoint: *flagUpdateURL,
		Interval: *flagUpdateInterval,
		Log:      k.Log,
	}
	go updater.Run()

	// don't allow anyone to call a method if we are during an update.
	k.PreHandleFunc(func(r *kite.Request) (interface{}, error) {
		// Koding (kloud) connects to much, don't display it.
		if r.Username != "koding" {
			k.Log.Info("Kite '%s/%s/%s' called method: '%s'",
				r.Username, r.Client.Environment, r.Client.Name, r.Method)
		}

		updatingMu.Lock()
		defer updatingMu.Unlock()

		if updating {
			return nil, errors.New("Updating klient. Can't accept any method.")
		}

		return true, nil
	})

	k.PreHandleFunc(checkAuth(k.Config.Username))

	// Metrics, is used by Kloud to get usage so Kloud can stop free VMs
	k.PreHandleFunc(usg.Counter) // we measure every incoming request
	k.HandleFunc("klient.usage", usg.Current)

	// Collaboration, is used by our Koding.com browser client.
	k.HandleFunc("klient.share", collab.Share)
	k.HandleFunc("klient.unshare", collab.Unshare)
	k.HandleFunc("klient.shared", collab.Shared)

	// Filesystem
	k.HandleFunc("fs.readDirectory", fs.ReadDirectory)
	k.HandleFunc("fs.glob", fs.Glob)
	k.HandleFunc("fs.readFile", fs.ReadFile)
	k.HandleFunc("fs.writeFile", fs.WriteFile)
	k.HandleFunc("fs.uniquePath", fs.UniquePath)
	k.HandleFunc("fs.getInfo", fs.GetInfo)
	k.HandleFunc("fs.setPermissions", fs.SetPermissions)
	k.HandleFunc("fs.remove", fs.Remove)
	k.HandleFunc("fs.rename", fs.Rename)
	k.HandleFunc("fs.createDirectory", fs.CreateDirectory)
	k.HandleFunc("fs.move", fs.Move)
	k.HandleFunc("fs.copy", fs.Copy)

	// // Docker
	// Disabled until we have Docker support, no need to bloat the binary :)
	// dock := docker.New("unix:///var/run/docker.sock", k.Log)
	// k.HandleFunc("docker.create", dock.Create)
	// k.HandleFunc("docker.connect", dock.Connect)
	// k.HandleFunc("docker.stop", dock.Stop)
	// k.HandleFunc("docker.start", dock.Start)
	// k.HandleFunc("docker.remove", dock.RemoveContainer)
	// k.HandleFunc("docker.list", dock.List)

	// Execution
	k.HandleFunc("exec", command.Exec)

	// Terminal
	term := terminal.New(k.Log)
	term.InputHook = usg.Reset
	k.HandleFunc("webterm.getSessions", term.GetSessions)
	k.HandleFunc("webterm.connect", term.Connect)
	k.HandleFunc("webterm.killSession", term.KillSession)
	k.HandleFunc("webterm.killSessions", term.KillSessions)

	var disconnectTimer *time.Timer

	k.OnFirstRequest(func(c *kite.Client) {
		// Koding (kloud) connects to much, don't display it.
		if c.Username != "koding" {
			k.Log.Info("Kite '%s/%s/%s' is connected", c.Username, c.Environment, c.Name)
		}

		if c.Username != k.Config.Username {
			return // we don't care for others
		}

		// it's still not initialized, so don't do anything
		if disconnectTimer != nil {
			// stop previously started disconnect timer.
			k.Log.Info("Disconnection timer is cancelled.")
			disconnectTimer.Stop()
		}

	})

	// Unshare collab users if the klient owner disconnects
	k.OnDisconnect(func(c *kite.Client) {
		// Koding (kloud) connects to much, don't display it.
		if c.Username != "koding" {
			k.Log.Info("Kite '%s/%s/%s' is disconnected", c.Username, c.Environment, c.Name)
		}

		if c.Username != k.Config.Username {
			return // we don't care for others
		}

		// if there is any previously created timers stop them so we don't leak
		// goroutines
		if disconnectTimer != nil {
			disconnectTimer.Stop()
		}

		k.Log.Info("Disconnection timer of 1 minutes is fired.")
		disconnectTimer = time.NewTimer(time.Minute * 1)

		// Close all active sessions of the current. Do not close it
		// immediately, instead of give some time so users can safely exit. If
		// the user reconnects again the timer will be stopped so we don't
		// unshare for network hiccups accidentally.
		go func() {
			select {
			case <-disconnectTimer.C:
				sharedUsers, err := collab.GetAll()
				if err != nil {
					k.Log.Warning("Couldn't unshare users: '%s'", err)
					return
				}

				if len(sharedUsers) == 0 {
					return // nothing to do ...
				}

				k.Log.Info("Unsharing users '%s'", sharedUsers)
				for user, option := range sharedUsers {
					// dont touch permanent users
					if option.Permanent {
						k.Log.Info("User is permanent, avoiding it: '%s'", user)
						continue
					}

					if err := collab.Delete(user); err != nil {
						k.Log.Warning("Couldn't delete user from storage: '%s'", err)
					}
					term.CloseSessions(user)
				}
			}
		}()
	})

	if err := register(k); err != nil {
		panic(err)
	}

	return k
}

// checkAuth checks whether the given incoming request is authenticated or not.
// It don't pass any request if the caller is outside of our scope.
func checkAuth(klientUser string) kite.HandlerFunc {
	return func(r *kite.Request) (interface{}, error) {
		if r.Auth != nil {
			return true, nil
		}

		// lazy return for those, no need to fetch from the DB
		if userIn(r.Username, []string{klientUser, "koding"}...) {
			return true, nil
		}

		// Allow collaboration users as well
		sharedUsers, err := collab.GetAll()
		if err != nil {
			return nil, fmt.Errorf("Can't read shared users from the storage. Err: %v", err)
		}

		sharedUsernames := make([]string, 0)
		for username := range sharedUsers {
			sharedUsernames = append(sharedUsernames, username)
		}

		if !userIn(r.Username, sharedUsernames...) {
			return nil, fmt.Errorf("User '%s' is not allowed to make a call to us.", r.Username)
		}

		return true, nil

	}
}

// userIn checks whether the given user exists in the users list or not. It
// returns true if the user exists.
func userIn(user string, users ...string) bool {
	for _, u := range users {
		if u == user {
			return true
		}
	}
	return false
}

// Given a string of the form "host", "host:port", or "[ipv6::address]:port",
// return true if the string includes a port.
func HasPort(s string) bool { return strings.LastIndex(s, ":") > strings.LastIndex(s, "]") }

// Given a string of the form "host", "port", returns "host:port"
func AddPort(host, port string) string {
	if ok := HasPort(host); ok {
		return host
	}

	return host + ":" + port
}
<|endoftext|>


<commit_before>package main

// copyright(c) 2014, Jason E. Aten
//
// goq : a simple queueing system in go; qsub replacement.
//

import (
	"fmt"
	"github.com/smallnest/rpcx/log"
	"os"
	"regexp"
	"strconv"
	"strings"

	schema "github.com/glycerine/goq/schema"
)

var timeoutRx = regexp.MustCompile("resource temporarily unavailable")

var LASTGITCOMMITHASH string

var _ = log.SetDummyLogger

func main() {

	// to quiet down the rpcx logging.
	//log.SetDummyLogger()

	pid := os.Getpid()
	home, err := FindGoqHome()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: please set env var GOQ_HOME to point to your Goq installation: %s\n", err)
		os.Exit(1)
	}

	//ShowRlimit()
	//fmt.Printf("errno = %p\n", unsafe.Pointer(GetAddrErrno()))

	/*
		// debug, SIGQUIT -> stacktrace
		sigChan := make(chan os.Signal)
		go func() {
			stacktrace := make([]byte, 8192)
			for _ = range sigChan {
				length := runtime.Stack(stacktrace, true)
				fmt.Println(string(stacktrace[:length]))
			}
		}()
		signal.Notify(sigChan, syscall.SIGQUIT)
	*/

	if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "--version") {
		fmt.Printf("%s\n", goq_version())
		os.Exit(0)
	}

	var isServer bool
	if len(os.Args) > 1 && (os.Args[1] == "serve" || os.Args[1] == "server") {
		isServer = true
	}

	var isInit bool
	if len(os.Args) > 1 && os.Args[1] == "init" {
		isInit = true
	}

	var isSubmitter bool
	if len(os.Args) > 1 && os.Args[1] == "sub" {
		isSubmitter = true
	}

	var isWorker bool
	if len(os.Args) > 1 && os.Args[1] == "work" {
		isWorker = true
	}

	var isKill bool
	if len(os.Args) > 1 && os.Args[1] == "kill" {
		isKill = true
	}

	var isShutdown bool
	if len(os.Args) > 1 && os.Args[1] == "shutdown" {
		isShutdown = true
	}

	var isStat bool
	var maxShow int = 10
	if len(os.Args) > 1 && os.Args[1] == "stat" {
		isStat = true
		if len(os.Args) > 2 {
			m, err := strconv.Atoi(os.Args[2])
			if err == nil {
				maxShow = m
				//fmt.Printf("main sub is setting maxShow = %d\n", maxShow)
			} else {
				fmt.Fprintf(os.Stderr, "%s sub could not parse stat maxShow argument '%s', err = %v\n", GoqExeName, os.Args[2], err)
				os.Exit(1)
			}
		}
	}

	var isWait bool
	if len(os.Args) > 1 && os.Args[1] == "wait" {
		isWait = true
	}

	// Brutally tell all workers to kill themselves off. In firey smoke.
	var isImmo bool
	if len(os.Args) > 1 && os.Args[1] == "immolateworkers" {
		isImmo = true
	}

	// deafWorker is for testing the behavior
	// of the jobserver when the worker dies or
	// doesn't answer after requesting a job.
	var isDeafWorker bool
	if len(os.Args) > 1 && os.Args[1] == "deafworker" {
		isDeafWorker = true
	}

	cfg, err := DiskThenEnvConfig(home)
	if !isInit {
		if err != nil {
			fmt.Fprintf(os.Stderr, "[pid %d] error on trying to read GOQ_HOME dir %s/.goq: '%s'. Did you forget to do 'goq init' ?\n", pid, home, err)
			os.Exit(1)
		}
	}

	switch {
	case isInit:
		if KeyExists(cfg) {
			fmt.Printf("[pid %d] goq init: key already exists in '%s'; delete .goq manually if you want to re-init. Warning: you will have to redistribute the .goq auth creds to your cluster.\n", pid, cfg.Home+"/.goq")
			os.Exit(1)
		}
		ServerInit(cfg)
		fmt.Printf("[pid %d] goq init: key created in '%s'.\n", pid, cfg.Home+"/.goq")
		os.Exit(0)

	case isServer:
		VPrintf("[pid %d] making new external job server, listening on %s:%d\n", pid, cfg.JservIP, cfg.JservPort)

		serv, err := NewJobServ(cfg)
		if err != nil {
			panic(err)
		}

		VPrintf("[pid %d] job server made, now handling requests.\n", pid)
		// wait till done, serving requests
		<-serv.Done

	case isSubmitter:
		args := os.Args[2:]
		if len(args) == 0 {
			fmt.Printf("[pid %d] cowardly refusing to submit empty job.\n", pid)
			os.Exit(1)
		}
		sub, err := NewSubmitter(cfg, false)
		if err != nil {
			panic(err)
		}
		// try really hard to cleanup, so no leftover sockets to fill up our file handle table.
		// It is okay to call sub.Bye() more than once.
		defer sub.Bye()
		todojob := MakeActualJob(args, cfg)
		VPrintf("[pid %d] submitter instantiated, make testjob to submit over nanomsg: %s.\n", pid, todojob)

		reply, _, err := sub.SubmitJobGetReply(todojob)
		if err != nil {
			match := timeoutRx.FindStringSubmatch(err.Error())
			if match != nil {
				fmt.Printf("[pid %d] sub timed-out after %d msec trying to contact server at '%s'.\n", pid, cfg.SendTimeoutMsec, cfg.JservAddr())
				sub.Bye()
				os.Exit(1)
			}
			fmt.Printf("[pid %d] goq sub: unknown error trying to contact server at '%s': '%s'.\n", pid, cfg.JservAddr(), err)
			sub.Bye()
			os.Exit(1)
		}
		if reply != nil && reply.Aboutjid != 0 {
			fmt.Printf("[pid %d] submitted job %d to server at '%s'.\n", pid, reply.Aboutjid, cfg.JservAddr())
			sub.Bye()
			os.Exit(0)
		}
		fmt.Printf("[pid %d] submitted job to server over nanomsg, got unexpected msg '%s', reply: %#v.\n", pid, reply.Msg, reply)
		sub.Bye()
		os.Exit(1)

	case isImmo:
		sub, err := NewSubmitter(cfg, false)
		if err != nil {
			panic(err)
		}

		err = sub.SubmitImmoJob()
		if err != nil {
			fmt.Printf("[pid %d] error while submitting ImmolateWorkers command to server '%s': %s\n", pid, cfg.JservAddr(), err)
			os.Exit(1)
		}

		fmt.Printf("[pid %d] immolate workers command submitted to server '%s':\n", pid, cfg.JservAddr())
		os.Exit(0)

	case isWorker:
		// client code, connects to the bus.

		// set a small, 1 seecond, timeout
		cpcfg := CopyConfig(cfg)
		cpcfg.SendTimeoutMsec = 1000
		worker, err := NewWorker(cpcfg, nil)
		if err != nil {
			panic(err)
		}

		VPrintf("[pid %d] worker instantiated, asking for work.\n", os.Getpid())

		worker.StandaloneExeStart()
		//<-worker.Done

	case isDeafWorker:
		worker, err := NewWorker(cfg, &WorkOpts{IsDeaf: true})
		if err != nil {
			panic(err)
		}

		VPrintf("[pid %d] worker instantiated, asking for work.\n", os.Getpid())

		worker.StandaloneExeStart()

	case isKill:
		if len(os.Args) < 3 {
			fmt.Fprintf(os.Stderr, "error in kill invocation. Expected: %s kill {jobid}, but jobid is missing.\n", GoqExeName)
			os.Exit(1)
		}
		jid, err := strconv.ParseInt(os.Args[2], 10, 64)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error in kill invocation. Expected: %s kill {jobid}, but jobid is not numeric.\n", GoqExeName)
			os.Exit(1)
		}

		SendKill(cfg, jid)
		fmt.Printf("[pid %d] sent kill %d request to jobserver at '%s'.\n", pid, jid, cfg.JservAddr())
		//  (no ack required on kill)

	case isShutdown:
		SendShutdown(cfg)
		fmt.Printf("[pid %d] sent shutdown request to jobserver at '%s'.\n", pid, cfg.JservAddr())

	case isWait:
		if len(os.Args) < 3 {
			fmt.Fprintf(os.Stderr, "error in wait invocation. Expected: %s wait {jobid}, but jobid is missing.\n", GoqExeName)
			os.Exit(1)
		}
		jid, err := strconv.Atoi(os.Args[2])
		if err != nil {
			fmt.Fprintf(os.Stderr, "error in wait invocation. Expected: %s wait {jobid}, but jobid is not numeric.\n", GoqExeName)
			os.Exit(1)
		}
		sub, err := NewSubmitter(cfg, true) // true to wait forever for it (no timeout)
		if err != nil {
			panic(err)
		}
		fmt.Printf("[pid %d; %s] waiting for jobid %d to finish at server '%s'.\n", pid, sub.Addr, jid, cfg.JservAddr())

		waitchan, err := sub.WaitForJob(int64(jid))
		if err != nil {
			if strings.HasSuffix(err.Error(), "resource temporarily unavailable\n") {
				fmt.Printf("[pid %d] wait timed-out after %d msec trying to contact server at '%s'.\n", pid, cfg.SendTimeoutMsec, cfg.JservAddr())
				os.Exit(1)
			}
			panic(err)
		}
		waitres := <-waitchan
		if waitres.Id == -1 {
			if len(waitres.Out) > 0 {
				fmt.Printf("[pid %d] wait on jobid %d result: error while waiting to finish: %#v.\n", pid, jid, waitres.Out[0])
			} else {
				fmt.Printf("[pid %d] wait on jobid %d result: error while waiting to finish.\n", pid, jid)
			}
			os.Exit(1)
		}
		switch waitres.Msg {
		case schema.JOBMSG_JOBNOTKNOWN:
			fmt.Printf("[pid %d] wait on jobid %d result: error: server says jobid-unknown.\n", pid, jid)
			os.Exit(1)

		case schema.JOBMSG_JOBFINISHEDNOTICE:
			fmt.Printf("[pid %d] wait on jobid %d result: success, job was completed.\n", pid, jid)
			os.Exit(0)

		case schema.JOBMSG_CANCELSUBMIT:
			fmt.Printf("[pid %d] wait on jobid %d result: error: job cancelled.\n", pid, jid)
			os.Exit(1)

		default:
			fmt.Printf("[pid %d] wait on jobid %d result: done with unrecognized Msg '%s': %#v.\n", pid, jid, waitres.Msg, waitres)
			os.Exit(1)
		}

	case isStat:
		sub, err := NewSubmitter(cfg, false)
		if err != nil {
			panic(err)
		}

		o, err := sub.SubmitSnapJob(maxShow)
		if err != nil {
			fmt.Printf("[pid %d] error while trying to get stats from server '%s': %s\n", pid, cfg.JservAddr(), err)
			os.Exit(1)
		}

		fmt.Printf("[pid %d] stats for job server '%s':\n", pid, cfg.JservAddr())
		for i := range o {
			fmt.Printf("%s\n", o[i])
		}
		sub.Bye()
	default:
		fmt.Printf("err: only recognized goq commands: init, sub, work, kill (jobid), stat, wait (jobid), immolateworkers, serve, shutdown\n")
		os.Exit(1)
	}

	VPrintf("[pid %d] done.\n", pid)
}
<commit_msg>atg. quiet down<commit_after>package main

// copyright(c) 2014, Jason E. Aten
//
// goq : a simple queueing system in go; qsub replacement.
//

import (
	"fmt"
	"github.com/smallnest/rpcx/log"
	"os"
	"regexp"
	"strconv"
	"strings"

	schema "github.com/glycerine/goq/schema"
)

var timeoutRx = regexp.MustCompile("resource temporarily unavailable")

var LASTGITCOMMITHASH string

var _ = log.SetDummyLogger

func main() {

	// to quiet down the rpcx logging.
	log.SetDummyLogger()

	pid := os.Getpid()
	home, err := FindGoqHome()
	if err != nil {
		fmt.Fprintf(os.Stderr, "Fatal error: please set env var GOQ_HOME to point to your Goq installation: %s\n", err)
		os.Exit(1)
	}

	//ShowRlimit()
	//fmt.Printf("errno = %p\n", unsafe.Pointer(GetAddrErrno()))

	/*
		// debug, SIGQUIT -> stacktrace
		sigChan := make(chan os.Signal)
		go func() {
			stacktrace := make([]byte, 8192)
			for _ = range sigChan {
				length := runtime.Stack(stacktrace, true)
				fmt.Println(string(stacktrace[:length]))
			}
		}()
		signal.Notify(sigChan, syscall.SIGQUIT)
	*/

	if len(os.Args) > 1 && (os.Args[1] == "version" || os.Args[1] == "--version") {
		fmt.Printf("%s\n", goq_version())
		os.Exit(0)
	}

	var isServer bool
	if len(os.Args) > 1 && (os.Args[1] == "serve" || os.Args[1] == "server") {
		isServer = true
	}

	var isInit bool
	if len(os.Args) > 1 && os.Args[1] == "init" {
		isInit = true
	}

	var isSubmitter bool
	if len(os.Args) > 1 && os.Args[1] == "sub" {
		isSubmitter = true
	}

	var isWorker bool
	if len(os.Args) > 1 && os.Args[1] == "work" {
		isWorker = true
	}

	var isKill bool
	if len(os.Args) > 1 && os.Args[1] == "kill" {
		isKill = true
	}

	var isShutdown bool
	if len(os.Args) > 1 && os.Args[1] == "shutdown" {
		isShutdown = true
	}

	var isStat bool
	var maxShow int = 10
	if len(os.Args) > 1 && os.Args[1] == "stat" {
		isStat = true
		if len(os.Args) > 2 {
			m, err := strconv.Atoi(os.Args[2])
			if err == nil {
				maxShow = m
				//fmt.Printf("main sub is setting maxShow = %d\n", maxShow)
			} else {
				fmt.Fprintf(os.Stderr, "%s sub could not parse stat maxShow argument '%s', err = %v\n", GoqExeName, os.Args[2], err)
				os.Exit(1)
			}
		}
	}

	var isWait bool
	if len(os.Args) > 1 && os.Args[1] == "wait" {
		isWait = true
	}

	// Brutally tell all workers to kill themselves off. In firey smoke.
	var isImmo bool
	if len(os.Args) > 1 && os.Args[1] == "immolateworkers" {
		isImmo = true
	}

	// deafWorker is for testing the behavior
	// of the jobserver when the worker dies or
	// doesn't answer after requesting a job.
	var isDeafWorker bool
	if len(os.Args) > 1 && os.Args[1] == "deafworker" {
		isDeafWorker = true
	}

	cfg, err := DiskThenEnvConfig(home)
	if !isInit {
		if err != nil {
			fmt.Fprintf(os.Stderr, "[pid %d] error on trying to read GOQ_HOME dir %s/.goq: '%s'. Did you forget to do 'goq init' ?\n", pid, home, err)
			os.Exit(1)
		}
	}

	switch {
	case isInit:
		if KeyExists(cfg) {
			fmt.Printf("[pid %d] goq init: key already exists in '%s'; delete .goq manually if you want to re-init. Warning: you will have to redistribute the .goq auth creds to your cluster.\n", pid, cfg.Home+"/.goq")
			os.Exit(1)
		}
		ServerInit(cfg)
		fmt.Printf("[pid %d] goq init: key created in '%s'.\n", pid, cfg.Home+"/.goq")
		os.Exit(0)

	case isServer:
		VPrintf("[pid %d] making new external job server, listening on %s:%d\n", pid, cfg.JservIP, cfg.JservPort)

		serv, err := NewJobServ(cfg)
		if err != nil {
			panic(err)
		}

		VPrintf("[pid %d] job server made, now handling requests.\n", pid)
		// wait till done, serving requests
		<-serv.Done

	case isSubmitter:
		args := os.Args[2:]
		if len(args) == 0 {
			fmt.Printf("[pid %d] cowardly refusing to submit empty job.\n", pid)
			os.Exit(1)
		}
		sub, err := NewSubmitter(cfg, false)
		if err != nil {
			panic(err)
		}
		// try really hard to cleanup, so no leftover sockets to fill up our file handle table.
		// It is okay to call sub.Bye() more than once.
		defer sub.Bye()
		todojob := MakeActualJob(args, cfg)
		VPrintf("[pid %d] submitter instantiated, make testjob to submit over nanomsg: %s.\n", pid, todojob)

		reply, _, err := sub.SubmitJobGetReply(todojob)
		if err != nil {
			match := timeoutRx.FindStringSubmatch(err.Error())
			if match != nil {
				fmt.Printf("[pid %d] sub timed-out after %d msec trying to contact server at '%s'.\n", pid, cfg.SendTimeoutMsec, cfg.JservAddr())
				sub.Bye()
				os.Exit(1)
			}
			fmt.Printf("[pid %d] goq sub: unknown error trying to contact server at '%s': '%s'.\n", pid, cfg.JservAddr(), err)
			sub.Bye()
			os.Exit(1)
		}
		if reply != nil && reply.Aboutjid != 0 {
			fmt.Printf("[pid %d] submitted job %d to server at '%s'.\n", pid, reply.Aboutjid, cfg.JservAddr())
			sub.Bye()
			os.Exit(0)
		}
		fmt.Printf("[pid %d] submitted job to server over nanomsg, got unexpected msg '%s', reply: %#v.\n", pid, reply.Msg, reply)
		sub.Bye()
		os.Exit(1)

	case isImmo:
		sub, err := NewSubmitter(cfg, false)
		if err != nil {
			panic(err)
		}

		err = sub.SubmitImmoJob()
		if err != nil {
			fmt.Printf("[pid %d] error while submitting ImmolateWorkers command to server '%s': %s\n", pid, cfg.JservAddr(), err)
			os.Exit(1)
		}

		fmt.Printf("[pid %d] immolate workers command submitted to server '%s':\n", pid, cfg.JservAddr())
		os.Exit(0)

	case isWorker:
		// client code, connects to the bus.

		// set a small, 1 seecond, timeout
		cpcfg := CopyConfig(cfg)
		cpcfg.SendTimeoutMsec = 1000
		worker, err := NewWorker(cpcfg, nil)
		if err != nil {
			panic(err)
		}

		VPrintf("[pid %d] worker instantiated, asking for work.\n", os.Getpid())

		worker.StandaloneExeStart()
		//<-worker.Done

	case isDeafWorker:
		worker, err := NewWorker(cfg, &WorkOpts{IsDeaf: true})
		if err != nil {
			panic(err)
		}

		VPrintf("[pid %d] worker instantiated, asking for work.\n", os.Getpid())

		worker.StandaloneExeStart()

	case isKill:
		if len(os.Args) < 3 {
			fmt.Fprintf(os.Stderr, "error in kill invocation. Expected: %s kill {jobid}, but jobid is missing.\n", GoqExeName)
			os.Exit(1)
		}
		jid, err := strconv.ParseInt(os.Args[2], 10, 64)
		if err != nil {
			fmt.Fprintf(os.Stderr, "error in kill invocation. Expected: %s kill {jobid}, but jobid is not numeric.\n", GoqExeName)
			os.Exit(1)
		}

		SendKill(cfg, jid)
		fmt.Printf("[pid %d] sent kill %d request to jobserver at '%s'.\n", pid, jid, cfg.JservAddr())
		//  (no ack required on kill)

	case isShutdown:
		SendShutdown(cfg)
		fmt.Printf("[pid %d] sent shutdown request to jobserver at '%s'.\n", pid, cfg.JservAddr())

	case isWait:
		if len(os.Args) < 3 {
			fmt.Fprintf(os.Stderr, "error in wait invocation. Expected: %s wait {jobid}, but jobid is missing.\n", GoqExeName)
			os.Exit(1)
		}
		jid, err := strconv.Atoi(os.Args[2])
		if err != nil {
			fmt.Fprintf(os.Stderr, "error in wait invocation. Expected: %s wait {jobid}, but jobid is not numeric.\n", GoqExeName)
			os.Exit(1)
		}
		sub, err := NewSubmitter(cfg, true) // true to wait forever for it (no timeout)
		if err != nil {
			panic(err)
		}
		fmt.Printf("[pid %d; %s] waiting for jobid %d to finish at server '%s'.\n", pid, sub.Addr, jid, cfg.JservAddr())

		waitchan, err := sub.WaitForJob(int64(jid))
		if err != nil {
			if strings.HasSuffix(err.Error(), "resource temporarily unavailable\n") {
				fmt.Printf("[pid %d] wait timed-out after %d msec trying to contact server at '%s'.\n", pid, cfg.SendTimeoutMsec, cfg.JservAddr())
				os.Exit(1)
			}
			panic(err)
		}
		waitres := <-waitchan
		if waitres.Id == -1 {
			if len(waitres.Out) > 0 {
				fmt.Printf("[pid %d] wait on jobid %d result: error while waiting to finish: %#v.\n", pid, jid, waitres.Out[0])
			} else {
				fmt.Printf("[pid %d] wait on jobid %d result: error while waiting to finish.\n", pid, jid)
			}
			os.Exit(1)
		}
		switch waitres.Msg {
		case schema.JOBMSG_JOBNOTKNOWN:
			fmt.Printf("[pid %d] wait on jobid %d result: error: server says jobid-unknown.\n", pid, jid)
			os.Exit(1)

		case schema.JOBMSG_JOBFINISHEDNOTICE:
			fmt.Printf("[pid %d] wait on jobid %d result: success, job was completed.\n", pid, jid)
			os.Exit(0)

		case schema.JOBMSG_CANCELSUBMIT:
			fmt.Printf("[pid %d] wait on jobid %d result: error: job cancelled.\n", pid, jid)
			os.Exit(1)

		default:
			fmt.Printf("[pid %d] wait on jobid %d result: done with unrecognized Msg '%s': %#v.\n", pid, jid, waitres.Msg, waitres)
			os.Exit(1)
		}

	case isStat:
		sub, err := NewSubmitter(cfg, false)
		if err != nil {
			panic(err)
		}

		o, err := sub.SubmitSnapJob(maxShow)
		if err != nil {
			fmt.Printf("[pid %d] error while trying to get stats from server '%s': %s\n", pid, cfg.JservAddr(), err)
			os.Exit(1)
		}

		fmt.Printf("[pid %d] stats for job server '%s':\n", pid, cfg.JservAddr())
		for i := range o {
			fmt.Printf("%s\n", o[i])
		}
		sub.Bye()
	default:
		fmt.Printf("err: only recognized goq commands: init, sub, work, kill (jobid), stat, wait (jobid), immolateworkers, serve, shutdown\n")
		os.Exit(1)
	}

	VPrintf("[pid %d] done.\n", pid)
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"os"

	k8sclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	"github.com/bradfitz/http2"
	flags "github.com/jessevdk/go-flags"
)

var logger *log.Logger = log.New(os.Stdout, "", 0)

const prefix = "/api"

type Options struct {
	Port                 uint16 `short:"p" long:"port" description:"The port to listen on" default:"9090"`
	KubernetesMaster     string `short:"k" long:"kubernetes-master" description:"The URL to the Kubernetes master"`
	KubernetesApiVersion string `short:"v" long:"kubernetes-api-version" description:"The version of the Kubernetes API to use" default:"v1beta2"`
	Insecure             bool   `long:"insecure" description:"Trust all server certificates" default:"false"`
	StaticDir            string `short:"w" long:"www" description:"Optional directory to serve static files from" default:"."`
	StaticPrefix         string `long:"www-prefix" description:"Prefix to serve static files on" default:"/"`
	ApiPrefix            string `long:"api-prefix" description:"Prefix to serve Kubernetes API on" default:"/api/"`
	Error404             string `long:"404" description:"Page to send on 404 (useful for e.g. Angular html5mode default page)"`
	TlsCertFile          string `long:"tls-cert" description:"TLS cert file"`
	TlsKeyFile           string `long:"tls-key" description:"TLS key file"`
}

func main() {
	var options Options
	var parser = flags.NewParser(&options, flags.Default)

	if _, err := parser.Parse(); err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			parser.WriteHelp(os.Stderr)
			os.Exit(1)
		}
		os.Exit(0)
	}

	if len(options.KubernetesMaster) == 0 && len(os.Getenv("KUBERNETES_SERVICE_HOST")) > 0 {
		options.KubernetesMaster = "https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"
	}

	options.KubernetesMaster = os.ExpandEnv(options.KubernetesMaster)

	k8sConfig := &k8sclient.Config{
		Host:     options.KubernetesMaster,
		Version:  options.KubernetesApiVersion,
		Insecure: options.Insecure,
	}

	k8sClient, err := k8sclient.New(k8sConfig)
	if err != nil {
		log.Panic(err)
	}

	if serverVersion, err := k8sClient.ServerVersion(); err != nil {
		log.Panic("Couldn't retrieve Kubernetes server version - incorrect URL?", err)
	} else {
		log.Printf("Connecting to Kubernetes master at %v running version %v", options.KubernetesMaster, serverVersion.String())
	}

	// Add SVG mimetype...
	mime.AddExtensionType(".svg", "image/svg+xml")

	_, err = kubectl.NewProxyServer(options.StaticDir, options.ApiPrefix, options.StaticPrefix, k8sConfig)

	log.Printf("Listening on port %d", options.Port)

	http2.VerboseLogs = true

	srv := &http.Server{
		Addr: fmt.Sprintf(":%d", options.Port),
	}

	http2.ConfigureServer(srv, &http2.Server{})

	if len(options.Error404) > 0 {
		srv.Handler = Handle404(http.DefaultServeMux, http.Dir(options.StaticDir), options.Error404)
	}

	if len(options.TlsCertFile) > 0 && len(options.TlsKeyFile) > 0 {
		log.Fatal(srv.ListenAndServeTLS(options.TlsCertFile, options.TlsKeyFile))
	} else {
		log.Fatal(srv.ListenAndServe())
	}
}

type hijack404 struct {
	http.ResponseWriter
	r            *http.Request
	fs           http.FileSystem
	error404Page string
	handled      bool
}

func (h *hijack404) Write(p []byte) (int, error) {
	if h.handled {
		f, err := h.fs.Open(h.error404Page)
		if err != nil {
			h.ResponseWriter.Write([]byte("404 page not found"))
			return 0, errors.New("404 page not found")
		}
		_, err = f.Stat()
		if err != nil {
			h.ResponseWriter.Write([]byte("404 page not found"))
			return 0, errors.New("404 page not found")
		}
		contents, err := ioutil.ReadAll(f)
		ctype := http.DetectContentType(contents)
		h.ResponseWriter.Header().Set("Content-Type", ctype)
		h.ResponseWriter.Write(contents)
		return 0, nil
	}
	return h.ResponseWriter.Write(p)
}

func (h *hijack404) WriteHeader(code int) {
	if code == http.StatusNotFound {
		h.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
		h.handled = true
	}
	h.ResponseWriter.WriteHeader(code)
}

// Handle404 will pass any 404's from the handler to the handle404
// function. If handle404 returns true, the response is considered complete,
// and the processing by handler is aborted.
func Handle404(handler http.Handler, fs http.FileSystem, error404Page string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hijack := &hijack404{ResponseWriter: w, r: r, fs: fs, error404Page: error404Page}
		handler.ServeHTTP(hijack, r)
	})
}
<commit_msg>Remove verbose http2 logs<commit_after>package main

import (
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"mime"
	"net/http"
	"os"

	k8sclient "github.com/GoogleCloudPlatform/kubernetes/pkg/client"
	"github.com/GoogleCloudPlatform/kubernetes/pkg/kubectl"
	"github.com/bradfitz/http2"
	flags "github.com/jessevdk/go-flags"
)

var logger *log.Logger = log.New(os.Stdout, "", 0)

const prefix = "/api"

type Options struct {
	Port                 uint16 `short:"p" long:"port" description:"The port to listen on" default:"9090"`
	KubernetesMaster     string `short:"k" long:"kubernetes-master" description:"The URL to the Kubernetes master"`
	KubernetesApiVersion string `short:"v" long:"kubernetes-api-version" description:"The version of the Kubernetes API to use" default:"v1beta2"`
	Insecure             bool   `long:"insecure" description:"Trust all server certificates" default:"false"`
	StaticDir            string `short:"w" long:"www" description:"Optional directory to serve static files from" default:"."`
	StaticPrefix         string `long:"www-prefix" description:"Prefix to serve static files on" default:"/"`
	ApiPrefix            string `long:"api-prefix" description:"Prefix to serve Kubernetes API on" default:"/api/"`
	Error404             string `long:"404" description:"Page to send on 404 (useful for e.g. Angular html5mode default page)"`
	TlsCertFile          string `long:"tls-cert" description:"TLS cert file"`
	TlsKeyFile           string `long:"tls-key" description:"TLS key file"`
}

func main() {
	var options Options
	var parser = flags.NewParser(&options, flags.Default)

	if _, err := parser.Parse(); err != nil {
		if e, ok := err.(*flags.Error); !ok || e.Type != flags.ErrHelp {
			parser.WriteHelp(os.Stderr)
			os.Exit(1)
		}
		os.Exit(0)
	}

	if len(options.KubernetesMaster) == 0 && len(os.Getenv("KUBERNETES_SERVICE_HOST")) > 0 {
		options.KubernetesMaster = "https://${KUBERNETES_SERVICE_HOST}:${KUBERNETES_SERVICE_PORT}"
	}

	options.KubernetesMaster = os.ExpandEnv(options.KubernetesMaster)

	k8sConfig := &k8sclient.Config{
		Host:     options.KubernetesMaster,
		Version:  options.KubernetesApiVersion,
		Insecure: options.Insecure,
	}

	k8sClient, err := k8sclient.New(k8sConfig)
	if err != nil {
		log.Panic(err)
	}

	if serverVersion, err := k8sClient.ServerVersion(); err != nil {
		log.Panic("Couldn't retrieve Kubernetes server version - incorrect URL?", err)
	} else {
		log.Printf("Connecting to Kubernetes master at %v running version %v", options.KubernetesMaster, serverVersion.String())
	}

	// Add SVG mimetype...
	mime.AddExtensionType(".svg", "image/svg+xml")

	_, err = kubectl.NewProxyServer(options.StaticDir, options.ApiPrefix, options.StaticPrefix, k8sConfig)

	log.Printf("Listening on port %d", options.Port)

	srv := &http.Server{
		Addr: fmt.Sprintf(":%d", options.Port),
	}

	http2.ConfigureServer(srv, &http2.Server{})

	if len(options.Error404) > 0 {
		srv.Handler = Handle404(http.DefaultServeMux, http.Dir(options.StaticDir), options.Error404)
	}

	if len(options.TlsCertFile) > 0 && len(options.TlsKeyFile) > 0 {
		log.Fatal(srv.ListenAndServeTLS(options.TlsCertFile, options.TlsKeyFile))
	} else {
		log.Fatal(srv.ListenAndServe())
	}
}

type hijack404 struct {
	http.ResponseWriter
	r            *http.Request
	fs           http.FileSystem
	error404Page string
	handled      bool
}

func (h *hijack404) Write(p []byte) (int, error) {
	if h.handled {
		f, err := h.fs.Open(h.error404Page)
		if err != nil {
			h.ResponseWriter.Write([]byte("404 page not found"))
			return 0, errors.New("404 page not found")
		}
		_, err = f.Stat()
		if err != nil {
			h.ResponseWriter.Write([]byte("404 page not found"))
			return 0, errors.New("404 page not found")
		}
		contents, err := ioutil.ReadAll(f)
		ctype := http.DetectContentType(contents)
		h.ResponseWriter.Header().Set("Content-Type", ctype)
		h.ResponseWriter.Write(contents)
		return 0, nil
	}
	return h.ResponseWriter.Write(p)
}

func (h *hijack404) WriteHeader(code int) {
	if code == http.StatusNotFound {
		h.ResponseWriter.Header().Set("Content-Type", "text/html; charset=utf-8")
		h.handled = true
	}
	h.ResponseWriter.WriteHeader(code)
}

// Handle404 will pass any 404's from the handler to the handle404
// function. If handle404 returns true, the response is considered complete,
// and the processing by handler is aborted.
func Handle404(handler http.Handler, fs http.FileSystem, error404Page string) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		hijack := &hijack404{ResponseWriter: w, r: r, fs: fs, error404Page: error404Page}
		handler.ServeHTTP(hijack, r)
	})
}
<|endoftext|>