<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"strconv"
)

const MAXLEN = 10 * 1024 * 1024 // 10 MB

// ScanNetStrings is a bufio.ScanFunc that can be used with bufio.Scanner to
// Scan() netstrings from a Reader.
// Netstring information here: http://cr.yp.to/proto/netstrings.txt
func ScanNetStrings(data []byte, atEOF bool) (advance int, token []byte, err error) {

	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// find out how much to read
	dataLen := 0
	lenLen := bytes.IndexByte(data, ':')
	if lenLen <= 0 {
		err = fmt.Errorf("Missing Length")
		return 0, nil, err
	}

	// No leading zeroes allowed, per spec
	lenStr := string(data[:lenLen])
	if len(lenStr) > 1 && lenStr[0] == '0' {
		err = fmt.Errorf("Leading zero in length")
		return 0, nil, err
	}

	i, err := strconv.ParseInt(lenStr, 10, 0)
	dataLen = int(i) // 0 on error, safe to check this first
	if dataLen > MAXLEN {
		err = fmt.Errorf("Proposed Length > MAXLEN")
		return 0, nil, err
	}
	if err != nil {
		err = fmt.Errorf("Error Parsing Length")
		return 0, nil, err
	}

	// one extra byte for ':'' and ',''
	if len(data) >= lenLen+1+dataLen+1 {
		// possibly complete!
		idxComma := lenLen + 1 + dataLen
		if data[idxComma] != ',' {
			err = fmt.Errorf("Missing Terminator (,)")
			return len(data), nil, err
		}
		// return the data between the : and ,
		return idxComma + 1, data[lenLen+1 : idxComma], nil
	}

	if atEOF {
		err = fmt.Errorf("Unexpected EOF")
		return len(data), nil, err
	}

	// No complete netstring in buffer yet - request more data.
	return 0, nil, nil
}

func usage() {
	fmt.Fprintf(
		os.Stderr,
		"  Usage: AFL_FIX_SOCK=/path/to/sockfile %s\n",
		path.Base(os.Args[0]),
	)
}

func main() {

	sockName := os.Getenv("AFL_FIX_SOCK")
	if sockName == "" {
		fmt.Fprintf(os.Stderr, "No socket variable in ENV")
		usage()
		os.Exit(1)
	}

	os.Remove(sockName)
	s, err := net.Listen("unix", sockName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to create socket %s: %s", sockName, err)
		usage()
		os.Exit(1)
	}
	log.Printf("Listening on %s...", sockName)
	defer os.Remove(sockName)
	defer s.Close()

	for {
		conn, err := s.Accept()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error accepting connection: %s", err)
			usage()
			os.Exit(1)
		}
		log.Printf("Accepted connection!")

		scanner := bufio.NewScanner(conn)
		scanner.Split(ScanNetStrings)
		for scanner.Scan() {
			// read a netstring
			in := scanner.Bytes()
			/*

				  MAKE YOUR CHANGES HERE!
				  			|
						   \ /
						    v
			*/
			fixed := bytes.Replace(in, []byte("Hello"), []byte("A MUCH LONGER THING"), -1)
			// write a netstring
			conn.Write([]byte(fmt.Sprintf("%d:%s,", len(fixed), string(fixed))))
		}

		if scanner.Err() != nil {
			log.Printf("Error during scanning: %s\n", scanner.Err().Error())
		} else {
			log.Printf("Interrupted. Waiting for new connection (^C to abort)\n")
		}
	}

}
<commit_msg>comments<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"strconv"
)

const MAXLEN = 10 * 1024 * 1024 // 10 MB

// ScanNetStrings is a bufio.ScanFunc that can be used with bufio.Scanner to
// Scan() netstrings from a Reader.
// Netstring information here: http://cr.yp.to/proto/netstrings.txt
func ScanNetStrings(data []byte, atEOF bool) (advance int, token []byte, err error) {

	if atEOF && len(data) == 0 {
		return 0, nil, nil
	}

	// find out how much to read
	dataLen := 0
	lenLen := bytes.IndexByte(data, ':')
	if lenLen <= 0 {
		err = fmt.Errorf("Missing Length")
		return 0, nil, err
	}

	// No leading zeroes allowed, per spec
	lenStr := string(data[:lenLen])
	if len(lenStr) > 1 && lenStr[0] == '0' {
		err = fmt.Errorf("Leading zero in length")
		return 0, nil, err
	}

	i, err := strconv.ParseInt(lenStr, 10, 0)
	dataLen = int(i) // 0 on error, safe to check this first
	if dataLen > MAXLEN {
		err = fmt.Errorf("Proposed Length > MAXLEN")
		return 0, nil, err
	}
	if err != nil {
		err = fmt.Errorf("Error Parsing Length")
		return 0, nil, err
	}

	// one extra byte for ':'' and ',''
	if len(data) >= lenLen+1+dataLen+1 {
		// possibly complete!
		idxComma := lenLen + 1 + dataLen
		if data[idxComma] != ',' {
			err = fmt.Errorf("Missing Terminator (,)")
			return len(data), nil, err
		}
		// return the data between the : and ,
		return idxComma + 1, data[lenLen+1 : idxComma], nil
	}

	if atEOF {
		err = fmt.Errorf("Unexpected EOF")
		return len(data), nil, err
	}

	// No complete netstring in buffer yet - request more data.
	return 0, nil, nil
}

func usage() {
	fmt.Fprintf(
		os.Stderr,
		"  Usage: AFL_FIX_SOCK=/path/to/sockfile %s\n",
		path.Base(os.Args[0]),
	)
}

func main() {

	sockName := os.Getenv("AFL_FIX_SOCK")
	if sockName == "" {
		fmt.Fprintf(os.Stderr, "No socket variable in ENV")
		usage()
		os.Exit(1)
	}

	os.Remove(sockName)
	s, err := net.Listen("unix", sockName)
	if err != nil {
		fmt.Fprintf(os.Stderr, "Unable to create socket %s: %s", sockName, err)
		usage()
		os.Exit(1)
	}
	log.Printf("Listening on %s...", sockName)
	defer os.Remove(sockName) // not guaranteed to run
	defer s.Close()

	for {
		conn, err := s.Accept()
		if err != nil {
			fmt.Fprintf(os.Stderr, "Error accepting connection: %s", err)
			usage()
			os.Exit(1)
		}
		log.Printf("Accepted connection!")

		scanner := bufio.NewScanner(conn)
		scanner.Split(ScanNetStrings)
		for scanner.Scan() {
			// read a netstring
			in := scanner.Bytes()
			/*

				  MAKE YOUR CHANGES HERE!
				  			|
						   \ /
						    v
			*/
			fixed := bytes.Replace(in, []byte("Hello"), []byte("A MUCH LONGER THING"), -1)
			// write a netstring
			conn.Write([]byte(fmt.Sprintf("%d:%s,", len(fixed), string(fixed))))
		}

		if scanner.Err() != nil {
			log.Printf("Error during scanning: %s\n", scanner.Err().Error())
		} else {
			log.Printf("Interrupted. Waiting for new connection (^C to abort)\n")
		}
	}

}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strconv"

	"github.com/oklog/run"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	vpaclientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
	clientset "k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/klog"

	"k8s.io/kube-state-metrics/internal/store"
	"k8s.io/kube-state-metrics/pkg/allowdenylist"
	"k8s.io/kube-state-metrics/pkg/metricshandler"
	"k8s.io/kube-state-metrics/pkg/options"
	"k8s.io/kube-state-metrics/pkg/util/proc"
	"k8s.io/kube-state-metrics/pkg/version"
)

const (
	metricsPath = "/metrics"
	healthzPath = "/healthz"
)

// promLogger implements promhttp.Logger
type promLogger struct{}

func (pl promLogger) Println(v ...interface{}) {
	klog.Error(v...)
}

func main() {
	opts := options.NewOptions()
	opts.AddFlags()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	err := opts.Parse()
	if err != nil {
		klog.Fatalf("Error: %s", err)
	}

	if opts.Version {
		fmt.Printf("%#v\n", version.GetVersion())
		os.Exit(0)
	}

	if opts.Help {
		opts.Usage()
		os.Exit(0)
	}
	storeBuilder := store.NewBuilder()

	ksmMetricsRegistry := prometheus.NewRegistry()
	storeBuilder.WithMetrics(ksmMetricsRegistry)

	var resources []string
	if len(opts.Resources) == 0 {
		klog.Info("Using default resources")
		resources = options.DefaultResources.AsSlice()
	} else {
		klog.Infof("Using resources %s", opts.Resources.String())
		resources = opts.Resources.AsSlice()
	}

	if err := storeBuilder.WithEnabledResources(resources); err != nil {
		klog.Fatalf("Failed to set up resources: %v", err)
	}

	if len(opts.Namespaces) == 0 {
		klog.Info("Using all namespace")
		storeBuilder.WithNamespaces(options.DefaultNamespaces)
	} else {
		if opts.Namespaces.IsAllNamespaces() {
			klog.Info("Using all namespace")
		} else {
			klog.Infof("Using %s namespaces", opts.Namespaces)
		}
		storeBuilder.WithNamespaces(opts.Namespaces)
	}

	allowDenyList, err := allowdenylist.New(opts.MetricAllowlist, opts.MetricDenylist)
	if err != nil {
		klog.Fatal(err)
	}

	err = allowDenyList.Parse()
	if err != nil {
		klog.Fatalf("error initializing the allowdeny list : %v", err)
	}

	klog.Infof("metric allow-denylisting: %v", allowDenyList.Status())

	storeBuilder.WithAllowDenyList(allowDenyList)

	storeBuilder.WithGenerateStoreFunc(storeBuilder.DefaultGenerateStoreFunc())

	proc.StartReaper()

	kubeClient, vpaClient, err := createKubeClient(opts.Apiserver, opts.Kubeconfig)
	if err != nil {
		klog.Fatalf("Failed to create client: %v", err)
	}
	storeBuilder.WithKubeClient(kubeClient)
	storeBuilder.WithVPAClient(vpaClient)
	storeBuilder.WithSharding(opts.Shard, opts.TotalShards)

	ksmMetricsRegistry.MustRegister(
		prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
		prometheus.NewGoCollector(),
	)

	var g run.Group
	m := metricshandler.New(
		opts,
		kubeClient,
		storeBuilder,
		opts.EnableGZIPEncoding,
	)

	// Run MetricsHandler
	{
		g.Add(func() error {
			klog.Errorf("metricshandler error: %v", m.Run(ctx))
			return ctx.Err()
		}, func(error) {
			//cancel()
		})
	}

	telemetryMux := buildTelemetryServer(ksmMetricsRegistry)
	telemetryServer := http.Server{Handler: telemetryMux}
	telemetryListenAddress := net.JoinHostPort(opts.TelemetryHost, strconv.Itoa(opts.TelemetryPort))
	telemetryLn, err := net.Listen("tcp", telemetryListenAddress)
	if err != nil {
		klog.Fatalf("Failed to create Telemetry Listener: %v", err)
	}
	metricsMux := buildMetricsServer(kubeClient, storeBuilder, m, opts)
	metricsServer := http.Server{Handler: metricsMux}
	metricsServerListenAddress := net.JoinHostPort(opts.Host, strconv.Itoa(opts.Port))
	metricsServerLn, err := net.Listen("tcp", metricsServerListenAddress)
	if err != nil {
		klog.Fatalf("Failed to create MetricsServer Listener: %v", err)
	}

	// Run Telemetry server
	{
		g.Add(func() error {
			klog.Infof("Starting kube-state-metrics self metrics server: %s", telemetryListenAddress)
			err = telemetryServer.Serve(telemetryLn)
			klog.Errorf("kube-state-metrics self metrics server error: %v", err)
			return err
		}, func(error) {
			telemetryServer.Shutdown(ctx)
		})
	}
	// Run Metrics server
	{
		g.Add(func() error {
			klog.Infof("Starting metrics server: %s", metricsServerListenAddress)
			err = metricsServer.Serve(metricsServerLn)
			klog.Errorf("metrics server error: %v", err)
			return err
		}, func(error) {
			metricsServer.Shutdown(ctx)
		})
	}

	if err := g.Run(); err != nil {
		klog.Fatalf("Failed to run Run Group: %v", err)
	}
	klog.Info("Exiting")
}

func createKubeClient(apiserver string, kubeconfig string) (clientset.Interface, vpaclientset.Interface, error) {
	config, err := clientcmd.BuildConfigFromFlags(apiserver, kubeconfig)
	if err != nil {
		return nil, nil, err
	}

	config.UserAgent = version.GetVersion().String()
	config.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
	config.ContentType = "application/vnd.kubernetes.protobuf"

	kubeClient, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, nil, err
	}

	vpaClient, err := vpaclientset.NewForConfig(config)
	if err != nil {
		return nil, nil, err
	}
	// Informers don't seem to do a good job logging error messages when it
	// can't reach the server, making debugging hard. This makes it easier to
	// figure out if apiserver is configured incorrectly.
	klog.Infof("Testing communication with server")
	v, err := kubeClient.Discovery().ServerVersion()
	if err != nil {
		return nil, nil, errors.Wrap(err, "error while trying to communicate with apiserver")
	}
	klog.Infof("Running with Kubernetes cluster version: v%s.%s. git version: %s. git tree state: %s. commit: %s. platform: %s",
		v.Major, v.Minor, v.GitVersion, v.GitTreeState, v.GitCommit, v.Platform)
	klog.Infof("Communication with server successful")

	return kubeClient, vpaClient, nil
}

func buildTelemetryServer(registry prometheus.Gatherer) *http.ServeMux {
	mux := http.NewServeMux()

	// Add metricsPath
	mux.Handle(metricsPath, promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: promLogger{}}))
	// Add index
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Kube-State-Metrics Metrics Server</title>

Kube-State-Metrics Metrics

<a href='` + metricsPath + `'>metrics

             `))
	})
	return mux
}

func buildMetricsServer(kubeClient clientset.Interface, storeBuilder *store.Builder, m *metricshandler.MetricsHandler, opts *options.Options) *http.ServeMux {
	mux := http.NewServeMux()

	// TODO: This doesn't belong into serveMetrics
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))

	mux.Handle(metricsPath, m)

	// Add healthzPath
	mux.HandleFunc(healthzPath, func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(http.StatusText(http.StatusOK)))
	})
	// Add index
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Kube Metrics Server</title>

Kube Metrics

<a href='` + metricsPath + `'>metrics

<a href='` + healthzPath + `'>healthz

             `))
	})
	return mux
}
<commit_msg>main: error handling in run group<commit_after>/*
Copyright 2015 The Kubernetes Authors All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package main

import (
	"context"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"strconv"

	"github.com/oklog/run"
	"github.com/pkg/errors"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	vpaclientset "k8s.io/autoscaler/vertical-pod-autoscaler/pkg/client/clientset/versioned"
	clientset "k8s.io/client-go/kubernetes"
	_ "k8s.io/client-go/plugin/pkg/client/auth"
	"k8s.io/client-go/tools/clientcmd"
	"k8s.io/klog"

	"k8s.io/kube-state-metrics/internal/store"
	"k8s.io/kube-state-metrics/pkg/allowdenylist"
	"k8s.io/kube-state-metrics/pkg/metricshandler"
	"k8s.io/kube-state-metrics/pkg/options"
	"k8s.io/kube-state-metrics/pkg/util/proc"
	"k8s.io/kube-state-metrics/pkg/version"
)

const (
	metricsPath = "/metrics"
	healthzPath = "/healthz"
)

// promLogger implements promhttp.Logger
type promLogger struct{}

func (pl promLogger) Println(v ...interface{}) {
	klog.Error(v...)
}

func main() {
	opts := options.NewOptions()
	opts.AddFlags()

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	err := opts.Parse()
	if err != nil {
		klog.Fatalf("Error: %s", err)
	}

	if opts.Version {
		fmt.Printf("%#v\n", version.GetVersion())
		os.Exit(0)
	}

	if opts.Help {
		opts.Usage()
		os.Exit(0)
	}
	storeBuilder := store.NewBuilder()

	ksmMetricsRegistry := prometheus.NewRegistry()
	storeBuilder.WithMetrics(ksmMetricsRegistry)

	var resources []string
	if len(opts.Resources) == 0 {
		klog.Info("Using default resources")
		resources = options.DefaultResources.AsSlice()
	} else {
		klog.Infof("Using resources %s", opts.Resources.String())
		resources = opts.Resources.AsSlice()
	}

	if err := storeBuilder.WithEnabledResources(resources); err != nil {
		klog.Fatalf("Failed to set up resources: %v", err)
	}

	if len(opts.Namespaces) == 0 {
		klog.Info("Using all namespace")
		storeBuilder.WithNamespaces(options.DefaultNamespaces)
	} else {
		if opts.Namespaces.IsAllNamespaces() {
			klog.Info("Using all namespace")
		} else {
			klog.Infof("Using %s namespaces", opts.Namespaces)
		}
		storeBuilder.WithNamespaces(opts.Namespaces)
	}

	allowDenyList, err := allowdenylist.New(opts.MetricAllowlist, opts.MetricDenylist)
	if err != nil {
		klog.Fatal(err)
	}

	err = allowDenyList.Parse()
	if err != nil {
		klog.Fatalf("error initializing the allowdeny list : %v", err)
	}

	klog.Infof("metric allow-denylisting: %v", allowDenyList.Status())

	storeBuilder.WithAllowDenyList(allowDenyList)

	storeBuilder.WithGenerateStoreFunc(storeBuilder.DefaultGenerateStoreFunc())

	proc.StartReaper()

	kubeClient, vpaClient, err := createKubeClient(opts.Apiserver, opts.Kubeconfig)
	if err != nil {
		klog.Fatalf("Failed to create client: %v", err)
	}
	storeBuilder.WithKubeClient(kubeClient)
	storeBuilder.WithVPAClient(vpaClient)
	storeBuilder.WithSharding(opts.Shard, opts.TotalShards)

	ksmMetricsRegistry.MustRegister(
		prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
		prometheus.NewGoCollector(),
	)

	var g run.Group
	m := metricshandler.New(
		opts,
		kubeClient,
		storeBuilder,
		opts.EnableGZIPEncoding,
	)

	// Run MetricsHandler
	{
		g.Add(func() error {
			return m.Run(ctx)
		}, func(error) {
			//cancel()
		})
	}

	telemetryMux := buildTelemetryServer(ksmMetricsRegistry)
	telemetryServer := http.Server{Handler: telemetryMux}
	telemetryListenAddress := net.JoinHostPort(opts.TelemetryHost, strconv.Itoa(opts.TelemetryPort))
	telemetryLn, err := net.Listen("tcp", telemetryListenAddress)
	if err != nil {
		klog.Fatalf("Failed to create Telemetry Listener: %v", err)
	}
	metricsMux := buildMetricsServer(kubeClient, storeBuilder, m, opts)
	metricsServer := http.Server{Handler: metricsMux}
	metricsServerListenAddress := net.JoinHostPort(opts.Host, strconv.Itoa(opts.Port))
	metricsServerLn, err := net.Listen("tcp", metricsServerListenAddress)
	if err != nil {
		klog.Fatalf("Failed to create MetricsServer Listener: %v", err)
	}

	// Run Telemetry server
	{
		g.Add(func() error {
			klog.Infof("Starting kube-state-metrics self metrics server: %s", telemetryListenAddress)
			return telemetryServer.Serve(telemetryLn)
		}, func(error) {
			telemetryServer.Shutdown(ctx)
		})
	}
	// Run Metrics server
	{
		g.Add(func() error {
			klog.Infof("Starting metrics server: %s", metricsServerListenAddress)
			return metricsServer.Serve(metricsServerLn)
		}, func(error) {
			metricsServer.Shutdown(ctx)
		})
	}

	if err := g.Run(); err != nil {
		klog.Fatalf("RunGroup Error: %v", err)
	}
	klog.Info("Exiting")
}

func createKubeClient(apiserver string, kubeconfig string) (clientset.Interface, vpaclientset.Interface, error) {
	config, err := clientcmd.BuildConfigFromFlags(apiserver, kubeconfig)
	if err != nil {
		return nil, nil, err
	}

	config.UserAgent = version.GetVersion().String()
	config.AcceptContentTypes = "application/vnd.kubernetes.protobuf,application/json"
	config.ContentType = "application/vnd.kubernetes.protobuf"

	kubeClient, err := clientset.NewForConfig(config)
	if err != nil {
		return nil, nil, err
	}

	vpaClient, err := vpaclientset.NewForConfig(config)
	if err != nil {
		return nil, nil, err
	}
	// Informers don't seem to do a good job logging error messages when it
	// can't reach the server, making debugging hard. This makes it easier to
	// figure out if apiserver is configured incorrectly.
	klog.Infof("Testing communication with server")
	v, err := kubeClient.Discovery().ServerVersion()
	if err != nil {
		return nil, nil, errors.Wrap(err, "error while trying to communicate with apiserver")
	}
	klog.Infof("Running with Kubernetes cluster version: v%s.%s. git version: %s. git tree state: %s. commit: %s. platform: %s",
		v.Major, v.Minor, v.GitVersion, v.GitTreeState, v.GitCommit, v.Platform)
	klog.Infof("Communication with server successful")

	return kubeClient, vpaClient, nil
}

func buildTelemetryServer(registry prometheus.Gatherer) *http.ServeMux {
	mux := http.NewServeMux()

	// Add metricsPath
	mux.Handle(metricsPath, promhttp.HandlerFor(registry, promhttp.HandlerOpts{ErrorLog: promLogger{}}))
	// Add index
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Kube-State-Metrics Metrics Server</title>

Kube-State-Metrics Metrics

<a href='` + metricsPath + `'>metrics

             `))
	})
	return mux
}

func buildMetricsServer(kubeClient clientset.Interface, storeBuilder *store.Builder, m *metricshandler.MetricsHandler, opts *options.Options) *http.ServeMux {
	mux := http.NewServeMux()

	// TODO: This doesn't belong into serveMetrics
	mux.Handle("/debug/pprof/", http.HandlerFunc(pprof.Index))
	mux.Handle("/debug/pprof/cmdline", http.HandlerFunc(pprof.Cmdline))
	mux.Handle("/debug/pprof/profile", http.HandlerFunc(pprof.Profile))
	mux.Handle("/debug/pprof/symbol", http.HandlerFunc(pprof.Symbol))
	mux.Handle("/debug/pprof/trace", http.HandlerFunc(pprof.Trace))

	mux.Handle(metricsPath, m)

	// Add healthzPath
	mux.HandleFunc(healthzPath, func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusOK)
		w.Write([]byte(http.StatusText(http.StatusOK)))
	})
	// Add index
	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte(`
             <title>Kube Metrics Server</title>

Kube Metrics

<a href='` + metricsPath + `'>metrics

<a href='` + healthzPath + `'>healthz

             `))
	})
	return mux
}
<|endoftext|>


<commit_before>// Copyright 2014-2015 Chadev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"

	"github.com/danryan/hal"
	_ "github.com/danryan/hal/adapter/irc"
	_ "github.com/danryan/hal/adapter/shell"
	_ "github.com/danryan/hal/store/memory"
	_ "github.com/danryan/hal/store/redis"
)

// VERSION contians the current verison number and revison if need be
const VERSION = "2015-05-03"

// handler is an interface for objects to implement in order to respond to messages.
type handler interface {
	Handle(res *hal.Response) error
}

var helpMessages = make(map[string]string)

var pingHandler = hear(`ping`, "ping", "Causes Ash to reply with PONG", func(res *hal.Response) error {
	return res.Send("PONG")
})

var fooHandler = hear(`foo`, "foo", "Causes Ash to reply with a BAR", func(res *hal.Response) error {
	return res.Send("BAR")
})

var synHandler = hear(`SYN`, "SYN", "Causes Ash to reply with ACK", func(res *hal.Response) error {
	return res.Send("ACK")
})

var selfHandler = hear(`who are you`, "self", "", func(res *hal.Response) error {
	return res.Send("I'm Ash, the friendly #chadev bot.  I can preform a variety of tasks, and I am learning new tricks all the time.  I am open source, and pull requests are welcome!")
})

var quitHandler = hear(`(.*)+/quit(.*)+`, "quit", "", func(res *hal.Response) error {
	name := res.UserName()
	return res.Send(fmt.Sprintf("No!  Bad %s!", name))
})

var helpHandler = hear(`help`, "help", "Displays this message", func(res *hal.Response) error {
	helpMsg := []string{
		"HAL Chadev IRC Edition build: " + VERSION + "\n",
		"Supported commands:\n",
	}

	for command, message := range helpMessages {
		if command != "" && message != "" {
			helpMsg = append(helpMsg, command+" - "+message+"\n")
		}
	}

	var text string
	for _, msg := range helpMsg {
		text = text + msg
	}

	text = uploadHelpMsg(text)
	res.Send(fmt.Sprintf("My usage information can be found at %s", text))

	return nil
})

func hear(pattern string, command string, message string, fn func(res *hal.Response) error) handler {
	addHelpMessage(command, message)
	return hal.Hear("^(?i)Ash "+pattern, fn)
}

func addHelpMessage(command string, message string) {
	helpMessages[command] = message
}

func main() {
	os.Exit(run())
}

func run() int {
	robot, err := hal.NewRobot()
	if err != nil {
		hal.Logger.Error(err)
		return 1
	}

	robot.Handle(
		fooHandler,
		tableFlipHandler,
		eventHandler,
		synHandler,
		helpHandler,
		pingHandler,
		sourceHandler,
		issueHandler,
		cageMeHandler,
		whoisHandler,
		whoamHandler,
		isHandler,
		selfHandler,
		quitHandler,
		fizzBuzzHandler,
		noteStoreHandler,
		noteGetHandler,
		noteRemoveHandler,
		chadevCountHandler,
		chadevListAllHandler,
		chadevInfoHandler,
		fatherHandler,
		partyHandler,
		whoBackHandler,
		whatAreHandler,
		musicHandler,
		lunchHandler,
		talkHandler,
		addTalkHandler,
		devTalkLinkHandler,
		isAliveHandler,
		groupListHandler,
		groupAddHandler,
		groupDetailsHandler,
		groupRemoveHandler,
		groupRSVPHandler,
		devlunchRSVPHandler,
	)

	if err := robot.Run(); err != nil {
		hal.Logger.Error(err)
		return 1
	}
	return 0
}
<commit_msg>Version bump<commit_after>// Copyright 2014-2015 Chadev. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package main

import (
	"fmt"
	"os"

	"github.com/danryan/hal"
	_ "github.com/danryan/hal/adapter/irc"
	_ "github.com/danryan/hal/adapter/shell"
	_ "github.com/danryan/hal/store/memory"
	_ "github.com/danryan/hal/store/redis"
)

// VERSION contians the current verison number and revison if need be
const VERSION = "2015-05-13"

// handler is an interface for objects to implement in order to respond to messages.
type handler interface {
	Handle(res *hal.Response) error
}

var helpMessages = make(map[string]string)

var pingHandler = hear(`ping`, "ping", "Causes Ash to reply with PONG", func(res *hal.Response) error {
	return res.Send("PONG")
})

var fooHandler = hear(`foo`, "foo", "Causes Ash to reply with a BAR", func(res *hal.Response) error {
	return res.Send("BAR")
})

var synHandler = hear(`SYN`, "SYN", "Causes Ash to reply with ACK", func(res *hal.Response) error {
	return res.Send("ACK")
})

var selfHandler = hear(`who are you`, "self", "", func(res *hal.Response) error {
	return res.Send("I'm Ash, the friendly #chadev bot.  I can preform a variety of tasks, and I am learning new tricks all the time.  I am open source, and pull requests are welcome!")
})

var quitHandler = hear(`(.*)+/quit(.*)+`, "quit", "", func(res *hal.Response) error {
	name := res.UserName()
	return res.Send(fmt.Sprintf("No!  Bad %s!", name))
})

var helpHandler = hear(`help`, "help", "Displays this message", func(res *hal.Response) error {
	helpMsg := []string{
		"HAL Chadev IRC Edition build: " + VERSION + "\n",
		"Supported commands:\n",
	}

	for command, message := range helpMessages {
		if command != "" && message != "" {
			helpMsg = append(helpMsg, command+" - "+message+"\n")
		}
	}

	var text string
	for _, msg := range helpMsg {
		text = text + msg
	}

	text = uploadHelpMsg(text)
	res.Send(fmt.Sprintf("My usage information can be found at %s", text))

	return nil
})

func hear(pattern string, command string, message string, fn func(res *hal.Response) error) handler {
	addHelpMessage(command, message)
	return hal.Hear("^(?i)Ash "+pattern, fn)
}

func addHelpMessage(command string, message string) {
	helpMessages[command] = message
}

func main() {
	os.Exit(run())
}

func run() int {
	robot, err := hal.NewRobot()
	if err != nil {
		hal.Logger.Error(err)
		return 1
	}

	robot.Handle(
		fooHandler,
		tableFlipHandler,
		eventHandler,
		synHandler,
		helpHandler,
		pingHandler,
		sourceHandler,
		issueHandler,
		cageMeHandler,
		whoisHandler,
		whoamHandler,
		isHandler,
		selfHandler,
		quitHandler,
		fizzBuzzHandler,
		noteStoreHandler,
		noteGetHandler,
		noteRemoveHandler,
		chadevCountHandler,
		chadevListAllHandler,
		chadevInfoHandler,
		fatherHandler,
		partyHandler,
		whoBackHandler,
		whatAreHandler,
		musicHandler,
		lunchHandler,
		talkHandler,
		addTalkHandler,
		devTalkLinkHandler,
		isAliveHandler,
		groupListHandler,
		groupAddHandler,
		groupDetailsHandler,
		groupRemoveHandler,
		groupRSVPHandler,
		devlunchRSVPHandler,
	)

	if err := robot.Run(); err != nil {
		hal.Logger.Error(err)
		return 1
	}
	return 0
}
<|endoftext|>


<commit_before>package main

import (
	"compress/gzip"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

var (
	config       *Configuration
	index_html   string
	proxy_target *url.URL
)

func main() {
	config = loadConfig("config.json")

	index_html = parseIndexFile("public/index.html")
	proxy_target, _ = url.Parse(fmt.Sprintf("%s:%d%s",
		config.HTTP_Bind.Host, config.HTTP_Bind.Port, config.HTTP_Bind.Path))

	http.HandleFunc("/", serveIndexFunc)
	http.HandleFunc("/http-bind/", serveProxyFunc)
	http.Handle("/candy/", http.StripPrefix("/candy/", http.FileServer(http.Dir("./public/candy/"))))

	http.ListenAndServe(fmt.Sprintf("%s:%d", config.App.Host, config.App.Port), nil)
}

func serveIndexFunc(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintf(w, index_html)
}

func serveProxyFunc(w http.ResponseWriter, r *http.Request) {
	r.RequestURI = ""
	r.URL = proxy_target
	r.Host = proxy_target.Host

	client := &http.Client{}
	response, _ := client.Do(r)

	defer response.Body.Close()

	var reader io.ReadCloser
	switch response.Header.Get("Content-Encoding") {
	case "gzip":
		reader, _ = gzip.NewReader(response.Body)
		defer reader.Close()
	default:
		reader = response.Body
	}
	w.Header().Set("Content-Type", response.Header.Get("Content-Type"))
	io.Copy(w, reader)
}

func parseIndexFile(path string) string {
	buf, _ := ioutil.ReadFile(path)
	str := string(buf)

	// TODO: make this more pretty
	core_config, _ := json.Marshal(config.Candy.Core)
	view_config, _ := json.Marshal(config.Candy.View)
	connect_config, _ := json.Marshal(config.Candy.Connect)
	str = strings.Replace(str, "OPTIONS", "{core:"+string(core_config)+",view:"+string(view_config)+"}", 1)
	str = strings.Replace(str, "CONNECT", string(connect_config[1:len(connect_config)-1]), 1)
	return str
}
<commit_msg>retain proxy response content-encoding<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"strings"
)

var (
	config       *Configuration
	index_html   string
	proxy_target *url.URL
)

func main() {
	config = loadConfig("config.json")

	index_html = parseIndexFile("public/index.html")
	proxy_target, _ = url.Parse(fmt.Sprintf("%s:%d%s",
		config.HTTP_Bind.Host, config.HTTP_Bind.Port, config.HTTP_Bind.Path))

	http.HandleFunc("/", serveIndexFunc)
	http.HandleFunc("/http-bind/", serveProxyFunc)
	http.Handle("/candy/", http.StripPrefix("/candy/", http.FileServer(http.Dir("./public/candy/"))))

	http.ListenAndServe(fmt.Sprintf("%s:%d", config.App.Host, config.App.Port), nil)
}

func serveIndexFunc(w http.ResponseWriter, r *http.Request) {
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintf(w, index_html)
}

func serveProxyFunc(w http.ResponseWriter, r *http.Request) {
	r.RequestURI = ""
	r.URL = proxy_target
	r.Host = proxy_target.Host

	client := &http.Client{}
	response, _ := client.Do(r)

	w.Header().Set("Content-Type", response.Header.Get("Content-Type"))
	w.Header().Set("Content-Encoding", response.Header.Get("Content-Encoding"))
	io.Copy(w, response.Body)
}

func parseIndexFile(path string) string {
	buf, _ := ioutil.ReadFile(path)
	str := string(buf)

	core_config, _ := json.Marshal(config.Candy.Core)
	view_config, _ := json.Marshal(config.Candy.View)
	connect_config, _ := json.Marshal(config.Candy.Connect)
	str = strings.Replace(str, "OPTIONS", "{core:"+string(core_config)+",view:"+string(view_config)+"}", 1)
	str = strings.Replace(str, "CONNECT", string(connect_config[1:len(connect_config)-1]), 1)
	return str
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

var (
	GithubToken       string
	GithubUser        string
	GithubRepo        string
	GithubAPIEndpoint string
	Version           string
	Debug             bool
)

type Release struct {
	UploadURL  string `json:"upload_url,omitempty"`
	TagName    string `json:"tag_name"`
	Branch     string `json:"target_commitish"`
	Name       string `json:"name"`
	Body       string `json:"body"`
	Draft      bool   `json:"draft"`
	Prerelease bool   `json:"prerelease"`
}

var verFlag bool

func init() {
	log.SetFlags(0)

	Debug, _ = strconv.ParseBool(os.Getenv("DEBUG"))

	GithubToken = os.Getenv("GITHUB_TOKEN")
	GithubUser = os.Getenv("GITHUB_USER")
	GithubRepo = os.Getenv("GITHUB_REPO")
	GithubAPIEndpoint = os.Getenv("GITHUB_API")

	flag.BoolVar(&verFlag, "version", false, "-version")
	flag.Parse()
}

var usage string = `Github command line release tool.
Usage:
	gh-release <user/repo> <tag> <branch> <description> <files>

	<files> can be specified using glob patterns.

Options:
	-version: Displays version
`

func main() {
	if verFlag {
		log.Println(Version)
		return
	}

	if len(os.Args) < 6 {
		log.Fatal(usage)
	}

	userRepo := strings.Split(os.Args[1], "/")
	if len(userRepo) != 2 {
		log.Printf("Error: Invalid format used for username and repository: %s\n\n", os.Args[1])
		log.Fatal(usage)
	}

	if GithubToken == "" {
		log.Fatal(`Error: GITHUB_TOKEN environment variable is not set.
Please refer to https://help.github.com/articles/creating-an-access-token-for-command-line-use/ for more help\n`)
	}

	GithubUser = userRepo[0]
	GithubRepo = userRepo[1]
	GithubAPIEndpoint = fmt.Sprintf("https://api.github.com/repos/%s/%s", GithubUser, GithubRepo)

	if Debug {
		log.Println("Glob pattern received: ")
		log.Println(os.Args[5])
	}

	filepaths, err := filepath.Glob(os.Args[5])
	if err != nil {
		log.Fatalf("Error: Invalid glob pattern: %s\n", os.Args[5])
	}

	if Debug {
		log.Println("Expanded glob pattern: ")
		log.Printf("%v\n", filepaths)
	}

	tag := os.Args[2]
	branch := os.Args[3]
	desc := os.Args[4]

	CreateRelease(tag, branch, desc, filepaths)
	log.Println("Done")
}

func uploadFile(uploadURL, path string) {
	file, err := os.Open(path)
	if err != nil {
		log.Printf("Error: %s\n", err.Error())
		return
	}
	defer file.Close()

	size, err := fileSize(file)
	if err != nil {
		log.Printf("Error: %s\n", err.Error())
		return
	}

	log.Printf("Uploading %s...\n", file.Name())
	body, err := doRequest("POST", uploadURL+"?name="+file.Name(), "application/octet-stream", file, size)
	if err != nil {
		log.Printf("Error: %s\n", err.Error())
	}

	if Debug {
		log.Println("========= UPLOAD RESPONSE ===========")
		log.Println(string(body[:]))
	}
}

// Creates a Github Release, attaching the given files as release assets
// If a release already exist, up in Github, this function will attempt to attach the given files to it
func CreateRelease(tag, branch, desc string, filepaths []string) {
	endpoint := fmt.Sprintf("%s/releases", GithubAPIEndpoint)

	release := Release{
		TagName:    tag,
		Name:       tag,
		Prerelease: false,
		Draft:      false,
		Branch:     branch,
		Body:       desc,
	}

	releaseData, err := json.Marshal(release)
	if err != nil {
		log.Fatalln(err)
	}

	releaseBuffer := bytes.NewBuffer(releaseData)

	data, err := doRequest("POST", endpoint, "application/json", releaseBuffer, int64(releaseBuffer.Len()))
	if err != nil {
		log.Fatalln(err)
	}

	// Gets the release Upload URL from the returned JSON data
	err = json.Unmarshal(data, &release)
	if err != nil {
		log.Fatalln(err)
	}

	// Upload URL comes like this https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name}
	// So we need to remove the {?name} part
	uploadURL := strings.Split(release.UploadURL, "{")[0]

	var wg sync.WaitGroup
	for i := range filepaths {
		wg.Add(1)
		func(index int) {
			uploadFile(uploadURL, filepaths[index])
			wg.Done()
		}(i)
	}
	wg.Wait()
}

func fileSize(file *os.File) (int64, error) {
	stat, err := file.Stat()
	if err != nil {
		return 0, err
	}
	return stat.Size(), nil
}

// Sends HTTP request to Github API
func doRequest(method, url, contentType string, reqBody io.Reader, bodySize int64) ([]byte, error) {
	req, err := http.NewRequest(method, url, reqBody)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", fmt.Sprintf("token %s", GithubToken))
	req.Header.Set("Content-type", contentType)
	req.Header.Set("Accept", "application/vnd.github.v3+json")
	req.ContentLength = bodySize

	if Debug {
		log.Println("================ REQUEST DUMP ==================")
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			log.Println(err.Error())
		}
		log.Println(string(dump[:]))
	}

	resp, err := http.DefaultClient.Do(req)

	if Debug {
		log.Println("================ RESPONSE DUMP ==================")
		dump, err := httputil.DumpResponse(resp, true)
		if err != nil {
			log.Println(err.Error())
		}
		log.Println(string(dump[:]))
	}

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		return nil, fmt.Errorf("Github returned an error:\n Code: %s. \n Body: %s", resp.Status, respBody)
	}

	return respBody, nil
}
<commit_msg>Fixes file name when uploading asset to Github<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httputil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
)

var (
	GithubToken       string
	GithubUser        string
	GithubRepo        string
	GithubAPIEndpoint string
	Version           string
	Debug             bool
)

type Release struct {
	UploadURL  string `json:"upload_url,omitempty"`
	TagName    string `json:"tag_name"`
	Branch     string `json:"target_commitish"`
	Name       string `json:"name"`
	Body       string `json:"body"`
	Draft      bool   `json:"draft"`
	Prerelease bool   `json:"prerelease"`
}

var verFlag bool

func init() {
	log.SetFlags(0)

	Debug, _ = strconv.ParseBool(os.Getenv("DEBUG"))

	GithubToken = os.Getenv("GITHUB_TOKEN")
	GithubUser = os.Getenv("GITHUB_USER")
	GithubRepo = os.Getenv("GITHUB_REPO")
	GithubAPIEndpoint = os.Getenv("GITHUB_API")

	flag.BoolVar(&verFlag, "version", false, "-version")
	flag.Parse()
}

var usage string = `Github command line release tool.
Usage:
	gh-release <user/repo> <tag> <branch> <description> <files>

	<files> can be specified using glob patterns.

Options:
	-version: Displays version
`

func main() {
	if verFlag {
		log.Println(Version)
		return
	}

	if len(os.Args) < 6 {
		log.Fatal(usage)
	}

	userRepo := strings.Split(os.Args[1], "/")
	if len(userRepo) != 2 {
		log.Printf("Error: Invalid format used for username and repository: %s\n\n", os.Args[1])
		log.Fatal(usage)
	}

	if GithubToken == "" {
		log.Fatal(`Error: GITHUB_TOKEN environment variable is not set.
Please refer to https://help.github.com/articles/creating-an-access-token-for-command-line-use/ for more help\n`)
	}

	GithubUser = userRepo[0]
	GithubRepo = userRepo[1]
	GithubAPIEndpoint = fmt.Sprintf("https://api.github.com/repos/%s/%s", GithubUser, GithubRepo)

	if Debug {
		log.Println("Glob pattern received: ")
		log.Println(os.Args[5])
	}

	filepaths, err := filepath.Glob(os.Args[5])
	if err != nil {
		log.Fatalf("Error: Invalid glob pattern: %s\n", os.Args[5])
	}

	if Debug {
		log.Println("Expanded glob pattern: ")
		log.Printf("%v\n", filepaths)
	}

	tag := os.Args[2]
	branch := os.Args[3]
	desc := os.Args[4]

	CreateRelease(tag, branch, desc, filepaths)
	log.Println("Done")
}

func uploadFile(uploadURL, path string) {
	file, err := os.Open(path)
	if err != nil {
		log.Printf("Error: %s\n", err.Error())
		return
	}
	defer file.Close()

	size, err := fileSize(file)
	if err != nil {
		log.Printf("Error: %s\n", err.Error())
		return
	}

	filename := filepath.Base(file.Name())
	log.Printf("Uploading %s...\n", filename)
	body, err := doRequest("POST", uploadURL+"?name="+filename, "application/octet-stream", file, size)
	if err != nil {
		log.Printf("Error: %s\n", err.Error())
	}

	if Debug {
		log.Println("========= UPLOAD RESPONSE ===========")
		log.Println(string(body[:]))
	}
}

// Creates a Github Release, attaching the given files as release assets
// If a release already exist, up in Github, this function will attempt to attach the given files to it
func CreateRelease(tag, branch, desc string, filepaths []string) {
	endpoint := fmt.Sprintf("%s/releases", GithubAPIEndpoint)

	release := Release{
		TagName:    tag,
		Name:       tag,
		Prerelease: false,
		Draft:      false,
		Branch:     branch,
		Body:       desc,
	}

	releaseData, err := json.Marshal(release)
	if err != nil {
		log.Fatalln(err)
	}

	releaseBuffer := bytes.NewBuffer(releaseData)

	data, err := doRequest("POST", endpoint, "application/json", releaseBuffer, int64(releaseBuffer.Len()))
	if err != nil {
		log.Fatalln(err)
	}

	// Gets the release Upload URL from the returned JSON data
	err = json.Unmarshal(data, &release)
	if err != nil {
		log.Fatalln(err)
	}

	// Upload URL comes like this https://uploads.github.com/repos/octocat/Hello-World/releases/1/assets{?name}
	// So we need to remove the {?name} part
	uploadURL := strings.Split(release.UploadURL, "{")[0]

	var wg sync.WaitGroup
	for i := range filepaths {
		wg.Add(1)
		func(index int) {
			uploadFile(uploadURL, filepaths[index])
			wg.Done()
		}(i)
	}
	wg.Wait()
}

func fileSize(file *os.File) (int64, error) {
	stat, err := file.Stat()
	if err != nil {
		return 0, err
	}
	return stat.Size(), nil
}

// Sends HTTP request to Github API
func doRequest(method, url, contentType string, reqBody io.Reader, bodySize int64) ([]byte, error) {
	req, err := http.NewRequest(method, url, reqBody)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Authorization", fmt.Sprintf("token %s", GithubToken))
	req.Header.Set("Content-type", contentType)
	req.Header.Set("Accept", "application/vnd.github.v3+json")
	req.ContentLength = bodySize

	if Debug {
		log.Println("================ REQUEST DUMP ==================")
		dump, err := httputil.DumpRequestOut(req, true)
		if err != nil {
			log.Println(err.Error())
		}
		log.Println(string(dump[:]))
	}

	resp, err := http.DefaultClient.Do(req)

	if Debug {
		log.Println("================ RESPONSE DUMP ==================")
		dump, err := httputil.DumpResponse(resp, true)
		if err != nil {
			log.Println(err.Error())
		}
		log.Println(string(dump[:]))
	}

	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	if resp.StatusCode != http.StatusOK && resp.StatusCode != http.StatusCreated {
		return nil, fmt.Errorf("Github returned an error:\n Code: %s. \n Body: %s", resp.Status, respBody)
	}

	return respBody, nil
}
<|endoftext|>


<commit_before>package main

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

var (
	name    = "circlecrossgame"
	version = "0.2.1"
)

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s [OPTION]...
Play circle cross game on terminal.

Options:
	--help       show this help message
	--version    print the version
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func _main() error {
	f := flag.NewFlagSet(name, flag.ContinueOnError)
	f.SetOutput(ioutil.Discard)

	isHelp := f.Bool("help", false, "")
	isVersion := f.Bool("version", false, "")
	if err := f.Parse(os.Args[1:]); err != nil {
		return err
	}
	switch {
	case *isHelp:
		printUsage()
		return nil
	case *isVersion:
		printVersion()
		return nil
	}

	g := NewGame()
	if err := g.Init(); err != nil {
		return err
	}
	return g.Main()
}

func main() {
	if err := _main(); err != nil {
		fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
		os.Exit(1)
	}
}
<commit_msg>Extract flagset<commit_after>package main

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

var (
	name    = "circlecrossgame"
	version = "0.2.1"

	flagset   = flag.NewFlagSet(name, flag.ContinueOnError)
	isHelp    = flagset.Bool("help", false, "")
	isVersion = flagset.Bool("version", false, "")
)

func printUsage() {
	fmt.Fprintf(os.Stderr, `
Usage: %s [OPTION]...
Play circle cross game on terminal.

Options:
	--help       show this help message
	--version    print the version
`[1:], name)
}

func printVersion() {
	fmt.Fprintln(os.Stderr, version)
}

func _main() error {
	flagset.SetOutput(ioutil.Discard)
	if err := flagset.Parse(os.Args[1:]); err != nil {
		return err
	}
	switch {
	case *isHelp:
		printUsage()
		return nil
	case *isVersion:
		printVersion()
		return nil
	}

	g := NewGame()
	if err := g.Init(); err != nil {
		return err
	}
	return g.Main()
}

func main() {
	if err := _main(); err != nil {
		fmt.Fprintf(os.Stderr, "%s: %s\n", name, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	"github.com/Bullpeen/infobot"
	"github.com/Bullpeen/stox"
	log "github.com/Sirupsen/logrus"
	"github.com/jirwin/gifs-quadlek/src"
	"github.com/jirwin/quadlek/plugins/comics"
	"github.com/jirwin/quadlek/plugins/echo"
	"github.com/jirwin/quadlek/plugins/eslogs"
	"github.com/jirwin/quadlek/plugins/karma"
	"github.com/jirwin/quadlek/plugins/nextep"
	"github.com/jirwin/quadlek/plugins/random"
	"github.com/jirwin/quadlek/plugins/spotify"
	"github.com/jirwin/quadlek/plugins/twitter"
	"github.com/jirwin/quadlek/quadlek"
	"github.com/morgabra/cointip/quadlek"
	twitch "github.com/morgabra/libtwitch/quadlek"
	"github.com/urfave/cli"
)

const Version = "0.0.1"

func run(c *cli.Context) error {
	var apiToken string
	if c.IsSet("api-key") {
		apiToken = c.String("api-key")
	} else {
		cli.ShowAppHelp(c)
		return cli.NewExitError("Missing --api-key arg.", 1)
	}

	var verificationToken string
	if c.IsSet("verification-token") {
		verificationToken = c.String("verification-token")
	} else {
		cli.ShowAppHelp(c)
		return cli.NewExitError("Missing --verification-token arg.", 1)
	}

	dbPath := c.String("db-path")

	bot, err := quadlek.NewBot(context.Background(), apiToken, verificationToken, dbPath)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Error("error creating bot")
		return nil
	}

	err = bot.RegisterPlugin(echo.Register())
	if err != nil {
		fmt.Printf("error registering echo plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(karma.Register())
	if err != nil {
		fmt.Printf("error registering karma plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(random.Register())
	if err != nil {
		fmt.Printf("error registering random plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(spotify.Register())
	if err != nil {
		fmt.Printf("error registering spotify plugin: %s\n", err.Error())
		return nil
	}

	if c.IsSet("tvdb-key") {
		tvdbKey := c.String("tvdb-key")

		err = bot.RegisterPlugin(nextep.Register(tvdbKey))
		if err != nil {
			fmt.Printf("error registering nextep plugin: %s\n", err.Error())
			return nil
		}
	}

	err = bot.RegisterPlugin(infobot.Register())
	if err != nil {
		fmt.Printf("error registering infobot plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(twitter.Register(
		c.String("twitter-consumer-key"),
		c.String("twitter-consumer-secret"),
		c.String("twitter-access-token"),
		c.String("twitter-access-secret"),
		// These must be twitter user ids, not names. https://tweeterid.com/ for easy conversion between the two.
		map[string]string{
			"25073877":           "politics", // @realDonaldTrump
			"830896623689547776": "politics", // @PresVillain
			"934474767645724672": "politics", // @realtrumpweathr
			"976366106561490944": "artfolio", // @DrawnDavidsOff
			"921111554371682304": "artfolio", // @DrawnDavidson
			"1581511":            "wwdc",     //@macrumorslive
			//added for retweet testing
			"778682": "quadlek-chat", // @jirwin

		},
	))

	coinbasePlugin := cointip.Register(
		c.String("coinbase-key"),
		c.String("coinbase-secret"),
		c.String("coinbase-account"),
	)
	if coinbasePlugin != nil {
		err = bot.RegisterPlugin(coinbasePlugin)
		if err != nil {
			panic(err)
		}
	}

	err = bot.RegisterPlugin(comics.Register(
		c.String("imgur-client-id"),
		"/opt/quad-assets/Arial.ttf",
	))
	if err != nil {
		fmt.Printf("error registering comic plugin: %s\n", err.Error())
		return err
	}
	esPlugin, err := eslogs.Register(c.String("es-endpoint"), c.String("es-index"))
	if err != nil {
		fmt.Printf("Error creating eslogs plugin: %s\n", err.Error())
		return err
	}
	err = bot.RegisterPlugin(esPlugin)
	if err != nil {
		fmt.Printf("Error registering eslogs plugin: %s\n", err.Error())
		return err
	}

	stoxPlugin := stox.Register(c.String("av-key"))
	err = bot.RegisterPlugin(stoxPlugin)
	if err != nil {
		fmt.Printf("Error registering stox plugin: %s\n", err.Error())
		return err
	}

	gifPlugin := gifs.Register(c.String("giphy-api-key"))
	err = bot.RegisterPlugin(gifPlugin)
	if err != nil {
		fmt.Printf("Error registering gifs plugin: %s\n", err.Error())
		return err
	}

	twitchFollows := []*twitch.TwitchFollow{
		{
			SlackChannels: []string{"vidyagames"},
			TwitchUser:    "kitboga",
			WatchStream:   true,
		},
		{
			SlackChannels: []string{"vidyagames"},
			TwitchUser:    "khryo72",
			SlackUser:     "morgabra",
			WatchStream:   true,
			WatchFollows:  true,
		},
		{
                        SlackChannels: []string{"vidyagames"},
                        TwitchUser:    "mekilek",
                        SlackUser:     "jirwin",
                        WatchStream:   true,
                        WatchFollows:  true,
                },
	}

	twitchPlugin := twitch.Register(c.String("twitch-oauth-client-id"), "", "https://quadlek.jirw.in/slack/plugin/twitch", false, twitchFollows)
	if twitchPlugin != nil {
		err = bot.RegisterPlugin(twitchPlugin)
		if err != nil {
			fmt.Printf("error registering twitch plugin: %s\n", err.Error())
			return err
		}
	}

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	bot.Start()
	<-signals
	bot.Stop()

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "quadlek"
	app.Version = Version
	app.Usage = "a slack bot"
	app.Action = run
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "api-key",
			Usage:  "The slack api token for the bot",
			EnvVar: "QUADLEK_API_TOKEN",
		},
		cli.StringFlag{
			Name:   "verification-token",
			Usage:  "The slack webhook verification token.",
			EnvVar: "QUADLEK_VERIFICATION_TOKEN",
		},
		cli.StringFlag{
			Name:   "db-path",
			Usage:  "The path where the database is stored.",
			Value:  "quadlek.db",
			EnvVar: "QUADLEK_DB_PATH",
		},
		cli.StringFlag{
			Name:   "tvdb-key",
			Usage:  "The TVDB api key for the bot, used by the nextep command",
			EnvVar: "QUADLEK_TVDB_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-key",
			Usage:  "The consumer key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_KEY",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-secret",
			Usage:  "The consumer secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_SECRET",
		},
		cli.StringFlag{
			Name:   "twitter-access-token",
			Usage:  "The access key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-access-secret",
			Usage:  "The access secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-key",
			Usage:  "The access key for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_KEY",
		},
		cli.StringFlag{
			Name:   "coinbase-secret",
			Usage:  "The access secret for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-account",
			Usage:  "The bank account for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_BANK_ACCOUNT",
		},
		cli.StringFlag{
			Name:   "imgur-client-id",
			Usage:  "The imgur client id",
			EnvVar: "QUADLEK_IMGUR_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "es-endpoint",
			Usage:  "The elastic search endpoint",
			EnvVar: "QUADLEK_ES_ENDPOINT",
		},
		cli.StringFlag{
			Name:   "es-index",
			Usage:  "The elastic search index",
			EnvVar: "QUADLEK_ES_INDEX",
		},
		cli.StringFlag{
			Name:   "av-key",
			Usage:  "Alpha Vantage Api Key",
			EnvVar: "QUADLEK_AV_KEY",
		},
		cli.StringFlag{
			Name:   "giphy-api-key",
			Usage:  "Giphy API Key",
			EnvVar: "QUADLEK_GIPHY_KEY",
		},
		cli.StringFlag{
			Name:   "twitch-oauth-client-id",
			Usage:  "OAuth application client id for twitch API",
			EnvVar: "QUADLEK_TWITCH_OAUTH_CLIENT_ID",
		},
	}

	app.Run(os.Args)
}
<commit_msg>Temp disable kitboga<commit_after>package main

import (
	"context"
	"fmt"
	"os"
	"os/signal"

	"github.com/Bullpeen/infobot"
	"github.com/Bullpeen/stox"
	log "github.com/Sirupsen/logrus"
	"github.com/jirwin/gifs-quadlek/src"
	"github.com/jirwin/quadlek/plugins/comics"
	"github.com/jirwin/quadlek/plugins/echo"
	"github.com/jirwin/quadlek/plugins/eslogs"
	"github.com/jirwin/quadlek/plugins/karma"
	"github.com/jirwin/quadlek/plugins/nextep"
	"github.com/jirwin/quadlek/plugins/random"
	"github.com/jirwin/quadlek/plugins/spotify"
	"github.com/jirwin/quadlek/plugins/twitter"
	"github.com/jirwin/quadlek/quadlek"
	"github.com/morgabra/cointip/quadlek"
	twitch "github.com/morgabra/libtwitch/quadlek"
	"github.com/urfave/cli"
)

const Version = "0.0.1"

func run(c *cli.Context) error {
	var apiToken string
	if c.IsSet("api-key") {
		apiToken = c.String("api-key")
	} else {
		cli.ShowAppHelp(c)
		return cli.NewExitError("Missing --api-key arg.", 1)
	}

	var verificationToken string
	if c.IsSet("verification-token") {
		verificationToken = c.String("verification-token")
	} else {
		cli.ShowAppHelp(c)
		return cli.NewExitError("Missing --verification-token arg.", 1)
	}

	dbPath := c.String("db-path")

	bot, err := quadlek.NewBot(context.Background(), apiToken, verificationToken, dbPath)
	if err != nil {
		log.WithFields(log.Fields{
			"err": err,
		}).Error("error creating bot")
		return nil
	}

	err = bot.RegisterPlugin(echo.Register())
	if err != nil {
		fmt.Printf("error registering echo plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(karma.Register())
	if err != nil {
		fmt.Printf("error registering karma plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(random.Register())
	if err != nil {
		fmt.Printf("error registering random plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(spotify.Register())
	if err != nil {
		fmt.Printf("error registering spotify plugin: %s\n", err.Error())
		return nil
	}

	if c.IsSet("tvdb-key") {
		tvdbKey := c.String("tvdb-key")

		err = bot.RegisterPlugin(nextep.Register(tvdbKey))
		if err != nil {
			fmt.Printf("error registering nextep plugin: %s\n", err.Error())
			return nil
		}
	}

	err = bot.RegisterPlugin(infobot.Register())
	if err != nil {
		fmt.Printf("error registering infobot plugin: %s\n", err.Error())
		return nil
	}

	err = bot.RegisterPlugin(twitter.Register(
		c.String("twitter-consumer-key"),
		c.String("twitter-consumer-secret"),
		c.String("twitter-access-token"),
		c.String("twitter-access-secret"),
		// These must be twitter user ids, not names. https://tweeterid.com/ for easy conversion between the two.
		map[string]string{
			"25073877":           "politics", // @realDonaldTrump
			"830896623689547776": "politics", // @PresVillain
			"934474767645724672": "politics", // @realtrumpweathr
			"976366106561490944": "artfolio", // @DrawnDavidsOff
			"921111554371682304": "artfolio", // @DrawnDavidson
			"1581511":            "wwdc",     //@macrumorslive
			//added for retweet testing
			"778682": "quadlek-chat", // @jirwin

		},
	))

	coinbasePlugin := cointip.Register(
		c.String("coinbase-key"),
		c.String("coinbase-secret"),
		c.String("coinbase-account"),
	)
	if coinbasePlugin != nil {
		err = bot.RegisterPlugin(coinbasePlugin)
		if err != nil {
			panic(err)
		}
	}

	err = bot.RegisterPlugin(comics.Register(
		c.String("imgur-client-id"),
		"/opt/quad-assets/Arial.ttf",
	))
	if err != nil {
		fmt.Printf("error registering comic plugin: %s\n", err.Error())
		return err
	}
	esPlugin, err := eslogs.Register(c.String("es-endpoint"), c.String("es-index"))
	if err != nil {
		fmt.Printf("Error creating eslogs plugin: %s\n", err.Error())
		return err
	}
	err = bot.RegisterPlugin(esPlugin)
	if err != nil {
		fmt.Printf("Error registering eslogs plugin: %s\n", err.Error())
		return err
	}

	stoxPlugin := stox.Register(c.String("av-key"))
	err = bot.RegisterPlugin(stoxPlugin)
	if err != nil {
		fmt.Printf("Error registering stox plugin: %s\n", err.Error())
		return err
	}

	gifPlugin := gifs.Register(c.String("giphy-api-key"))
	err = bot.RegisterPlugin(gifPlugin)
	if err != nil {
		fmt.Printf("Error registering gifs plugin: %s\n", err.Error())
		return err
	}

	twitchFollows := []*twitch.TwitchFollow{
	//	{
	//		SlackChannels: []string{"vidyagames"},
	//		TwitchUser:    "kitboga",
	//		WatchStream:   true,
	//	},
		{
			SlackChannels: []string{"vidyagames"},
			TwitchUser:    "khryo72",
			SlackUser:     "morgabra",
			WatchStream:   true,
			WatchFollows:  true,
		},
		{
                        SlackChannels: []string{"vidyagames"},
                        TwitchUser:    "mekilek",
                        SlackUser:     "jirwin",
                        WatchStream:   true,
                        WatchFollows:  true,
                },
	}

	twitchPlugin := twitch.Register(c.String("twitch-oauth-client-id"), "", "https://quadlek.jirw.in/slack/plugin/twitch", false, twitchFollows)
	if twitchPlugin != nil {
		err = bot.RegisterPlugin(twitchPlugin)
		if err != nil {
			fmt.Printf("error registering twitch plugin: %s\n", err.Error())
			return err
		}
	}

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, os.Interrupt)

	bot.Start()
	<-signals
	bot.Stop()

	return nil
}

func main() {
	app := cli.NewApp()
	app.Name = "quadlek"
	app.Version = Version
	app.Usage = "a slack bot"
	app.Action = run
	app.Flags = []cli.Flag{
		cli.StringFlag{
			Name:   "api-key",
			Usage:  "The slack api token for the bot",
			EnvVar: "QUADLEK_API_TOKEN",
		},
		cli.StringFlag{
			Name:   "verification-token",
			Usage:  "The slack webhook verification token.",
			EnvVar: "QUADLEK_VERIFICATION_TOKEN",
		},
		cli.StringFlag{
			Name:   "db-path",
			Usage:  "The path where the database is stored.",
			Value:  "quadlek.db",
			EnvVar: "QUADLEK_DB_PATH",
		},
		cli.StringFlag{
			Name:   "tvdb-key",
			Usage:  "The TVDB api key for the bot, used by the nextep command",
			EnvVar: "QUADLEK_TVDB_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-key",
			Usage:  "The consumer key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_KEY",
		},
		cli.StringFlag{
			Name:   "twitter-consumer-secret",
			Usage:  "The consumer secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_CONSUMER_SECRET",
		},
		cli.StringFlag{
			Name:   "twitter-access-token",
			Usage:  "The access key for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_TOKEN",
		},
		cli.StringFlag{
			Name:   "twitter-access-secret",
			Usage:  "The access secret for the twitter api",
			EnvVar: "QUADLEK_TWITTER_ACCESS_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-key",
			Usage:  "The access key for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_KEY",
		},
		cli.StringFlag{
			Name:   "coinbase-secret",
			Usage:  "The access secret for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_SECRET",
		},
		cli.StringFlag{
			Name:   "coinbase-account",
			Usage:  "The bank account for the coinbase api",
			EnvVar: "QUADLEK_COINBASE_BANK_ACCOUNT",
		},
		cli.StringFlag{
			Name:   "imgur-client-id",
			Usage:  "The imgur client id",
			EnvVar: "QUADLEK_IMGUR_CLIENT_ID",
		},
		cli.StringFlag{
			Name:   "es-endpoint",
			Usage:  "The elastic search endpoint",
			EnvVar: "QUADLEK_ES_ENDPOINT",
		},
		cli.StringFlag{
			Name:   "es-index",
			Usage:  "The elastic search index",
			EnvVar: "QUADLEK_ES_INDEX",
		},
		cli.StringFlag{
			Name:   "av-key",
			Usage:  "Alpha Vantage Api Key",
			EnvVar: "QUADLEK_AV_KEY",
		},
		cli.StringFlag{
			Name:   "giphy-api-key",
			Usage:  "Giphy API Key",
			EnvVar: "QUADLEK_GIPHY_KEY",
		},
		cli.StringFlag{
			Name:   "twitch-oauth-client-id",
			Usage:  "OAuth application client id for twitch API",
			EnvVar: "QUADLEK_TWITCH_OAUTH_CLIENT_ID",
		},
	}

	app.Run(os.Args)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	pb "github.com/dgryski/carbonzipper/carbonzipperpb"
)

type zipper string

var Zipper zipper

var timeFormats = []string{"15:04 20060102", "20060102", "01/02/06"}

// dateParamToEpoch turns a passed string parameter into an epoch we can send to the Zipper
func dateParamToEpoch(s string, d int64) string {

	if s == "" {
		// return the default if nothing was passed
		return strconv.Itoa(int(d))
	}

	// relative timestamp
	if s[0] == '-' {

		j := 1
		for j < len(s) && s[j] >= '0' && s[j] <= '9' {
			j++
		}
		offsetStr, unitStr := s[:j], s[j:]

		var units time.Duration
		switch unitStr {
		case "s", "sec", "secs", "second", "seconds":
			units = time.Second
		case "min", "minute", "minutes":
			units = time.Minute
		case "h", "hour", "hours":
			units = time.Hour
		case "d", "day", "days":
			units = 24 * time.Hour
		case "mon", "month", "months":
			units = 30 * 24 * time.Hour
		case "y", "year", "years":
			units = 365 * 24 * time.Hour
		}

		offset, err := strconv.Atoi(offsetStr)
		if err != nil {
			return strconv.Itoa(int(d))
		}

		return strconv.Itoa(int(timeNow().Add(time.Duration(offset) * units).Unix()))

	}

	_, err := strconv.Atoi(s)
	if err == nil && len(s) > 8 {
		return s // We got a timestamp so returning it
	}

	if strings.Contains(s, "_") {
		s = strings.Replace(s, "_", " ", 1) // Go can't parse _ in date strings
	}

	for _, format := range timeFormats {
		t, err := time.Parse(format, s)
		if err == nil {
			return strconv.Itoa(int(t.Unix()))
		}
	}
	return strconv.Itoa(int(d))
}

// FIXME(dgryski): extract the http.Get + unproto code into its own function

func (z zipper) Find(metric string) (pb.GlobResponse, error) {

	u, _ := url.Parse(string(z) + "/metrics/find/")

	u.RawQuery = url.Values{
		"query":  []string{metric},
		"format": []string{"protobuf"},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Find: http.Get: %+v\n", err)
		return pb.GlobResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Find: ioutil.ReadAll: %+v\n", err)
		return pb.GlobResponse{}, err
	}

	var pbresp pb.GlobResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Find: proto.Unmarshal: %+v\n", err)
		return pb.GlobResponse{}, err
	}

	return pbresp, nil
}

func (z zipper) Render(metric, from, until string) (pb.FetchResponse, error) {

	u, _ := url.Parse(string(z) + "/render/")

	u.RawQuery = url.Values{
		"target": []string{metric},
		"format": []string{"protobuf"},
		"from":   []string{from},
		"until":  []string{until},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Render: http.Get: %s: %+v\n", metric, err)
		return pb.FetchResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Render: ioutil.ReadAll: %s: %+v\n", metric, err)
		return pb.FetchResponse{}, err
	}

	var pbresp pb.FetchResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Render: proto.Unmarshal: %s: %+v\n", metric, err)
		return pb.FetchResponse{}, err
	}

	return pbresp, nil
}

type limiter chan struct{}

func (l limiter) enter() { l <- struct{}{} }
func (l limiter) leave() { <-l }

var Limiter limiter

// for testing
var timeNow = time.Now

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

	r.ParseForm()
	targets := r.Form["target"]
	from := r.FormValue("from")
	until := r.FormValue("until")

	// normalize from and until values
	from = dateParamToEpoch(from, timeNow().Add(-24*time.Hour).Unix())
	until = dateParamToEpoch(until, timeNow().Unix())

	var results []*pb.FetchResponse
	// query zipper for find

	for _, target := range targets {

		exp, e, err := parseExpr(target)
		if err != nil || e != "" {
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			return
		}

		metricMap := make(map[string][]*pb.FetchResponse)

		for _, metric := range exp.metrics() {

			// query zipper for find
			glob, err := Zipper.Find(metric)
			if err != nil {
				continue
			}

			// for each server in find response query render
			rch := make(chan *pb.FetchResponse, len(glob.GetMatches()))
			for _, m := range glob.GetMatches() {
				go func(m *pb.GlobMatch) {
					Limiter.enter()
					var rptr *pb.FetchResponse
					if m.GetIsLeaf() {
						r, err := Zipper.Render(m.GetPath(), from, until)
						if err == nil {
							rptr = &r
						}
					}
					rch <- rptr
					Limiter.leave()
				}(m)
			}

			for i := 0; i < len(glob.GetMatches()); i++ {
				r := <-rch
				if r != nil {
					metricMap[metric] = append(metricMap[metric], r)
				}
			}
		}

		exprs := evalExpr(exp, metricMap)
		results = append(results, exprs...)
	}

	w.Header().Set("Content-Type", "application/json")
	jEnc := json.NewEncoder(w)
	jEnc.Encode(results)
}

func main() {

	z := flag.String("z", "", "zipper")
	port := flag.Int("p", 8080, "port")
	l := flag.Int("l", 20, "concurrency limit")

	flag.Parse()

	if *z == "" {
		log.Fatal("no zipper (-z) provided")
	}

	if p := os.Getenv("PORT"); p != "" {
		*port, _ = strconv.Atoi(p)
	}

	Limiter = make(chan struct{}, *l)

	if *z == "" {
		log.Fatal("no zipper provided")
	}

	if _, err := url.Parse(*z); err != nil {
		log.Fatal("unable to parze zipper:", err)
	}

	log.Println("using zipper", *z)

	Zipper = zipper(*z)

	http.HandleFunc("/render/", renderHandler)

	log.Println("listening on port", *port)
	log.Fatalln(http.ListenAndServe(":"+strconv.Itoa(*port), nil))

}
<commit_msg>Match graphite's JSON result format<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"math"
	"net/http"
	"net/url"
	"os"
	"strconv"
	"strings"
	"time"

	"code.google.com/p/gogoprotobuf/proto"

	pb "github.com/dgryski/carbonzipper/carbonzipperpb"
)

type zipper string

var Zipper zipper

var timeFormats = []string{"15:04 20060102", "20060102", "01/02/06"}

// dateParamToEpoch turns a passed string parameter into an epoch we can send to the Zipper
func dateParamToEpoch(s string, d int64) string {

	if s == "" {
		// return the default if nothing was passed
		return strconv.Itoa(int(d))
	}

	// relative timestamp
	if s[0] == '-' {

		j := 1
		for j < len(s) && s[j] >= '0' && s[j] <= '9' {
			j++
		}
		offsetStr, unitStr := s[:j], s[j:]

		var units time.Duration
		switch unitStr {
		case "s", "sec", "secs", "second", "seconds":
			units = time.Second
		case "min", "minute", "minutes":
			units = time.Minute
		case "h", "hour", "hours":
			units = time.Hour
		case "d", "day", "days":
			units = 24 * time.Hour
		case "mon", "month", "months":
			units = 30 * 24 * time.Hour
		case "y", "year", "years":
			units = 365 * 24 * time.Hour
		}

		offset, err := strconv.Atoi(offsetStr)
		if err != nil {
			return strconv.Itoa(int(d))
		}

		return strconv.Itoa(int(timeNow().Add(time.Duration(offset) * units).Unix()))

	}

	_, err := strconv.Atoi(s)
	if err == nil && len(s) > 8 {
		return s // We got a timestamp so returning it
	}

	if strings.Contains(s, "_") {
		s = strings.Replace(s, "_", " ", 1) // Go can't parse _ in date strings
	}

	for _, format := range timeFormats {
		t, err := time.Parse(format, s)
		if err == nil {
			return strconv.Itoa(int(t.Unix()))
		}
	}
	return strconv.Itoa(int(d))
}

// FIXME(dgryski): extract the http.Get + unproto code into its own function

func (z zipper) Find(metric string) (pb.GlobResponse, error) {

	u, _ := url.Parse(string(z) + "/metrics/find/")

	u.RawQuery = url.Values{
		"query":  []string{metric},
		"format": []string{"protobuf"},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Find: http.Get: %+v\n", err)
		return pb.GlobResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Find: ioutil.ReadAll: %+v\n", err)
		return pb.GlobResponse{}, err
	}

	var pbresp pb.GlobResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Find: proto.Unmarshal: %+v\n", err)
		return pb.GlobResponse{}, err
	}

	return pbresp, nil
}

func (z zipper) Render(metric, from, until string) (pb.FetchResponse, error) {

	u, _ := url.Parse(string(z) + "/render/")

	u.RawQuery = url.Values{
		"target": []string{metric},
		"format": []string{"protobuf"},
		"from":   []string{from},
		"until":  []string{until},
	}.Encode()

	resp, err := http.Get(u.String())
	if err != nil {
		log.Printf("Render: http.Get: %s: %+v\n", metric, err)
		return pb.FetchResponse{}, err
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Printf("Render: ioutil.ReadAll: %s: %+v\n", metric, err)
		return pb.FetchResponse{}, err
	}

	var pbresp pb.FetchResponse

	err = proto.Unmarshal(body, &pbresp)
	if err != nil {
		log.Printf("Render: proto.Unmarshal: %s: %+v\n", metric, err)
		return pb.FetchResponse{}, err
	}

	return pbresp, nil
}

type limiter chan struct{}

func (l limiter) enter() { l <- struct{}{} }
func (l limiter) leave() { <-l }

var Limiter limiter

// for testing
var timeNow = time.Now

type graphitePoint struct {
	value float64
	t     int32
}

func (g graphitePoint) MarshalJSON() ([]byte, error) {
	if math.IsNaN(g.value) {
		return []byte(fmt.Sprintf("[null,%d]", g.t)), nil
	}
	return []byte(fmt.Sprintf("[%g,%d]", g.value, g.t)), nil
}

type jsonResponse struct {
	Target     string          `json:"target"`
	Datapoints []graphitePoint `json:"datapoints"`
}

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

	r.ParseForm()
	targets := r.Form["target"]
	from := r.FormValue("from")
	until := r.FormValue("until")

	// normalize from and until values
	from = dateParamToEpoch(from, timeNow().Add(-24*time.Hour).Unix())
	until = dateParamToEpoch(until, timeNow().Unix())

	var results []*pb.FetchResponse
	// query zipper for find

	for _, target := range targets {

		exp, e, err := parseExpr(target)
		if err != nil || e != "" {
			http.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
			return
		}

		metricMap := make(map[string][]*pb.FetchResponse)

		for _, metric := range exp.metrics() {

			// query zipper for find
			glob, err := Zipper.Find(metric)
			if err != nil {
				continue
			}

			// for each server in find response query render
			rch := make(chan *pb.FetchResponse, len(glob.GetMatches()))
			for _, m := range glob.GetMatches() {
				go func(m *pb.GlobMatch) {
					Limiter.enter()
					var rptr *pb.FetchResponse
					if m.GetIsLeaf() {
						r, err := Zipper.Render(m.GetPath(), from, until)
						if err == nil {
							rptr = &r
						}
					}
					rch <- rptr
					Limiter.leave()
				}(m)
			}

			for i := 0; i < len(glob.GetMatches()); i++ {
				r := <-rch
				if r != nil {
					metricMap[metric] = append(metricMap[metric], r)
				}
			}
		}

		exprs := evalExpr(exp, metricMap)
		results = append(results, exprs...)
	}

	w.Header().Set("Content-Type", "application/json")

	var jresults []jsonResponse

	for _, r := range results {
		if r == nil {
			log.Println("skipping nil result")
		}
		datapoints := make([]graphitePoint, 0, len(r.Values))
		t := *r.StartTime
		for i, v := range r.Values {
			if r.IsAbsent[i] {
				v = math.NaN()
			}
			datapoints = append(datapoints, graphitePoint{value: v, t: t})
			t += *r.StepTime
		}
		jresults = append(jresults, jsonResponse{Target: r.GetName(), Datapoints: datapoints})
	}

	jEnc := json.NewEncoder(w)
	jEnc.Encode(jresults)
}

func main() {

	z := flag.String("z", "", "zipper")
	port := flag.Int("p", 8080, "port")
	l := flag.Int("l", 20, "concurrency limit")

	flag.Parse()

	if *z == "" {
		log.Fatal("no zipper (-z) provided")
	}

	if p := os.Getenv("PORT"); p != "" {
		*port, _ = strconv.Atoi(p)
	}

	Limiter = make(chan struct{}, *l)

	if *z == "" {
		log.Fatal("no zipper provided")
	}

	if _, err := url.Parse(*z); err != nil {
		log.Fatal("unable to parze zipper:", err)
	}

	log.Println("using zipper", *z)

	Zipper = zipper(*z)

	http.HandleFunc("/render/", renderHandler)

	log.Println("listening on port", *port)
	log.Fatalln(http.ListenAndServe(":"+strconv.Itoa(*port), nil))

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/getdiskette/diskette/admin"
	"github.com/getdiskette/diskette/collections"
	"github.com/getdiskette/diskette/middleware"
	"github.com/getdiskette/diskette/rest"
	"github.com/getdiskette/diskette/session"
	"github.com/getdiskette/diskette/user"

	"github.com/labstack/echo"
	"labix.org/v2/mgo"
)

type config struct {
	Database string `json:"database"`
	JwtKey   string `json:"jwtKey"`
}

func main() {
	cfg := readConfig()
	jwtKey := []byte(cfg.JwtKey)

	mongoSession := createMongoSession()
	defer mongoSession.Close()

	db := mongoSession.DB(cfg.Database)
	userCollection := db.C(collections.UserCollectionName)

	e := echo.New()

	e.Get("/ping", func(c *echo.Context) error {
		return c.JSON(http.StatusOK, "pong")
	})

	restService := rest.NewService(db)
	restGroup := e.Group("/collection")
	restGroup.Get("/:collection", restService.Get)
	restGroup.Post("/:collection", restService.Post)
	restGroup.Put("/:collection", restService.Put)
	restGroup.Delete("/:collection", restService.Delete)

	userService := user.NewService(userCollection, jwtKey)
	userGroup := e.Group("/user")
	userGroup.Post("/signup", userService.Signup)
	userGroup.Post("/confirm", userService.ConfirmSignup)
	userGroup.Post("/signin", userService.Signin)
	userGroup.Post("/forgot-password", userService.ForgotPassword)
	userGroup.Post("/reset-password", userService.ResetPassword)

	sessionService := session.NewService(userCollection, jwtKey)
	sessionMiddleware := middleware.CreateSessionMiddleware(userCollection, jwtKey)
	sessionGroup := e.Group("/session", sessionMiddleware)
	sessionGroup.Post("/signout", sessionService.Signout)
	sessionGroup.Post("/change-password", sessionService.ChangePassword)
	sessionGroup.Post("/change-email", sessionService.ChangeEmail)
	sessionGroup.Post("/set-profile", sessionService.SetProfile)

	adminService := admin.NewService(userCollection, jwtKey)
	adminSessionMiddleware := middleware.CreateAdminSessionMiddleware(userCollection, jwtKey)
	adminGroup := e.Group("/admin", adminSessionMiddleware)
	adminGroup.Get("/get-users", adminService.GetUsers)
	adminGroup.Post("/create-user", adminService.CreateUser)
	adminGroup.Post("/change-user-password", adminService.ChangeUserPassword)
	adminGroup.Post("/change-user-email", adminService.ChangeUserEmail)
	adminGroup.Post("/set-user-roles", adminService.SetUserRoles)
	adminGroup.Post("/set-user-profile", adminService.SetUserProfile)
	adminGroup.Delete("/remove-users", adminService.RemoveUsers)
	adminGroup.Post("/signout-users", adminService.SignoutUsers)
	adminGroup.Post("/suspend-users", adminService.SuspendUsers)
	adminGroup.Post("/unsuspend-users", adminService.UnsuspendUsers)
	adminGroup.Delete("/remove-unconfirmed-users", adminService.RemoveUnconfirmedUsers)
	adminGroup.Post("/remove-expired-reset-keys", adminService.RemoveExpiredResetKeys)

	fmt.Println("Listening at http://localhost:5025")
	e.Run(":5025")
}

func readConfig() config {
	var cfg config
	cfgData, err := ioutil.ReadFile("config.json")
	if err != nil {
		log.Fatal(err)
	}
	if err := json.Unmarshal(cfgData, &cfg); err != nil {
		log.Fatal(err)
	}
	return cfg
}

func createMongoSession() *mgo.Session {
	session, err := mgo.Dial("127.0.0.1")
	if err != nil {
		log.Fatal(err)
	}
	return session
}
<commit_msg>update<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"

	"github.com/getdiskette/diskette/admin"
	"github.com/getdiskette/diskette/collections"
	"github.com/getdiskette/diskette/middleware"
	"github.com/getdiskette/diskette/rest"
	"github.com/getdiskette/diskette/session"
	"github.com/getdiskette/diskette/user"

	"github.com/labstack/echo"
	"labix.org/v2/mgo"
)

type config struct {
	Database string `json:"database"`
	JwtKey   string `json:"jwtKey"`
}

func main() {
	cfg := readConfig()
	jwtKey := []byte(cfg.JwtKey)

	mongoSession := createMongoSession()
	defer mongoSession.Close()

	db := mongoSession.DB(cfg.Database)
	userCollection := db.C(collections.UserCollectionName)

	e := echo.New()

	e.Get("/ping", func(c *echo.Context) error {
		return c.String(http.StatusOK, "pong")
	})

	restService := rest.NewService(db)
	restGroup := e.Group("/collection")
	restGroup.Get("/:collection", restService.Get)
	restGroup.Post("/:collection", restService.Post)
	restGroup.Put("/:collection", restService.Put)
	restGroup.Delete("/:collection", restService.Delete)

	userService := user.NewService(userCollection, jwtKey)
	userGroup := e.Group("/user")
	userGroup.Post("/signup", userService.Signup)
	userGroup.Post("/confirm", userService.ConfirmSignup)
	userGroup.Post("/signin", userService.Signin)
	userGroup.Post("/forgot-password", userService.ForgotPassword)
	userGroup.Post("/reset-password", userService.ResetPassword)

	sessionService := session.NewService(userCollection, jwtKey)
	sessionMiddleware := middleware.CreateSessionMiddleware(userCollection, jwtKey)
	sessionGroup := e.Group("/session", sessionMiddleware)
	sessionGroup.Post("/signout", sessionService.Signout)
	sessionGroup.Post("/change-password", sessionService.ChangePassword)
	sessionGroup.Post("/change-email", sessionService.ChangeEmail)
	sessionGroup.Post("/set-profile", sessionService.SetProfile)

	adminService := admin.NewService(userCollection, jwtKey)
	adminSessionMiddleware := middleware.CreateAdminSessionMiddleware(userCollection, jwtKey)
	adminGroup := e.Group("/admin", adminSessionMiddleware)
	adminGroup.Get("/get-users", adminService.GetUsers)
	adminGroup.Post("/create-user", adminService.CreateUser)
	adminGroup.Post("/change-user-password", adminService.ChangeUserPassword)
	adminGroup.Post("/change-user-email", adminService.ChangeUserEmail)
	adminGroup.Post("/set-user-roles", adminService.SetUserRoles)
	adminGroup.Post("/set-user-profile", adminService.SetUserProfile)
	adminGroup.Delete("/remove-users", adminService.RemoveUsers)
	adminGroup.Post("/signout-users", adminService.SignoutUsers)
	adminGroup.Post("/suspend-users", adminService.SuspendUsers)
	adminGroup.Post("/unsuspend-users", adminService.UnsuspendUsers)
	adminGroup.Delete("/remove-unconfirmed-users", adminService.RemoveUnconfirmedUsers)
	adminGroup.Post("/remove-expired-reset-keys", adminService.RemoveExpiredResetKeys)

	fmt.Println("Listening at http://localhost:5025")
	e.Run(":5025")
}

func readConfig() config {
	var cfg config
	cfgData, err := ioutil.ReadFile("config.json")
	if err != nil {
		log.Fatal(err)
	}
	if err := json.Unmarshal(cfgData, &cfg); err != nil {
		log.Fatal(err)
	}
	return cfg
}

func createMongoSession() *mgo.Session {
	session, err := mgo.Dial("127.0.0.1")
	if err != nil {
		log.Fatal(err)
	}
	return session
}
<|endoftext|>


<commit_before>// 2015 - Mathieu Lonjaret

// The montorrent program serves the status of rtorrent. It uses
// github.com/mpl/rtorrentrpc to query rtorrent.
package main

import (
	"bufio"
	"bytes"
	"crypto/rand"
	"crypto/tls"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mpl/basicauth"
	"golang.org/x/crypto/acme/autocert"
)

var (
	cache   = flag.Int("cache", 0, "cache the status for that many seconds. 0 means no caching")
	help    = flag.Bool("h", false, "show this help")
	host    = flag.String("host", "localhost:8080", "listening host:port")
	scgi    = flag.String("scgi", "localhost:5000", "host:port for rtorrent's scgi.")
	flagTLS = flag.Bool("tls", false, `For https. If "key.pem" or "cert.pem" are not found in $HOME/keys/, in-memory self-signed are generated and used instead.`)

	flagAutocert = flag.Bool("autocert", false, `Get https certificate from Let's Encrypt. Implies -tls=true. Obviously -host must contain a full qualified domain name. The cached certificate(s) will be in $HOME/keys/letsencrypt.cache.`)
	userpass     = flag.String("userpass", "", "optional username:password protection")
	verbose      = flag.Bool("v", false, "verbose")
)

const (
	numRetry   = 20
	retryDelay = 2 * time.Second
	idstring   = "http://golang.org/pkg/http/#ListenAndServe"
)

func usage() {
	flag.PrintDefaults()
	os.Exit(2)
}

func rpc(args ...string) ([]byte, error) {
	var answer []byte
	for i := 0; i < numRetry; i++ {
		cmd := exec.Command("rtorrentrpc", args...)
		output, err := cmd.CombinedOutput()
		if err != nil {
			// TODO(mpl): diagnose better the error and return early if it's not the expected EOF one.
			if *verbose {
				log.Printf("ignoring error: %v", err)
			}
			continue
		}
		if len(output) > 0 {
			answer = output
			break
		}
		time.Sleep(retryDelay)
	}
	if len(answer) == 0 {
		return nil, fmt.Errorf("empty answer for %v", args)
	}
	return answer, nil
}

func scanAnswer(answer []byte, prefix, suffix string) ([]string, error) {
	var list []string
	scanner := bufio.NewScanner(bytes.NewReader(answer))
	for scanner.Scan() {
		line := scanner.Text()
		if !strings.HasPrefix(line, prefix) || !strings.HasSuffix(line, suffix) {
			continue
		}
		list = append(list, strings.TrimSuffix(strings.TrimPrefix(line, prefix), suffix))
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("could not scan answer: %v", err)
	}
	return list, nil
}

func downloadList() ([]string, error) {
	answer, err := rpc(*scgi, "download_list", "")
	if err != nil {
		return nil, err
	}
	return scanAnswer(answer, "<value><string>", "</string></value>")
}

func torrentName(torrentHash string) (string, error) {
	answer, err := rpc(*scgi, "d.name", torrentHash)
	if err != nil {
		return "", err
	}
	list, err := scanAnswer(answer, "<param><value><string>", "</string></value></param>")
	if err != nil {
		return "", err
	}
	if len(list) == 0 {
		return "", fmt.Errorf("%v: name not found", torrentHash)
	}
	return list[0], nil
}

func bytesDone(torrentHash string) (int, error) {
	var n int
	answer, err := rpc(*scgi, "d.get_bytes_done", torrentHash)
	if err != nil {
		return n, err
	}
	list, err := scanAnswer(answer, "<param><value><i8>", "</i8></value></param>")
	if err != nil {
		return n, err
	}
	if len(list) == 0 {
		return n, fmt.Errorf("%v: bytes_done not found", torrentHash)
	}
	n, err = strconv.Atoi(list[0])
	if err != nil {
		return n, fmt.Errorf("could not convert bytes_done to int: %v", err)
	}
	return n, nil
}

func bytesLeft(torrentHash string) (int, error) {
	var n int
	answer, err := rpc(*scgi, "d.get_left_bytes", torrentHash)
	if err != nil {
		return n, err
	}
	list, err := scanAnswer(answer, "<param><value><i8>", "</i8></value></param>")
	if err != nil {
		return n, err
	}
	if len(list) == 0 {
		return n, fmt.Errorf("%v: bytes_left not found", torrentHash)
	}
	n, err = strconv.Atoi(list[0])
	if err != nil {
		return n, fmt.Errorf("could not convert bytes_left to int: %v", err)
	}
	return n, nil
}

type torrentStatus struct {
	Name        string
	BytesDone   int
	BytesLeft   int
	BytesTotal  int
	PercentDone int
}

func getStatus(torrentHash string) (*torrentStatus, error) {
	name, err := torrentName(torrentHash)
	if err != nil {
		return nil, err
	}
	nDone, err := bytesDone(torrentHash)
	if err != nil {
		return nil, err
	}
	nLeft, err := bytesLeft(torrentHash)
	if err != nil {
		return nil, err
	}
	total := nDone + nLeft // yay, super precision!!
	percent := nDone * 100 / total
	return &torrentStatus{
		Name:        name,
		BytesDone:   nDone,
		BytesLeft:   nLeft,
		BytesTotal:  total,
		PercentDone: percent,
	}, nil
}

func makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if e, ok := recover().(error); ok {
				http.Error(w, e.Error(), http.StatusInternalServerError)
				return
			}
		}()
		w.Header().Set("Server", idstring)
		if up.IsAllowed(r) {
			fn(w, r)
		} else {
			basicauth.SendUnauthorized(w, r, "montorrent")
		}
	}
}

func serveJSON(w http.ResponseWriter, r *http.Request, data []byte) {
	w.Header().Set("Content-Type", "text/javascript")
	w.Header().Set("Content-Length", strconv.Itoa(len(data)+1))
	w.WriteHeader(200)
	w.Write(data)
	w.Write([]byte("\n"))
}

func serveStatus(w http.ResponseWriter, r *http.Request) {
	if *cache > 0 {
		lastUpdateMu.RLock()
		// TODO(mpl): maybe use a checkLastModified. pull it out of simpleHttpd as a lib?
		if time.Now().Before(lastUpdate.Add(time.Duration(*cache) * time.Second)) {
			defer lastUpdateMu.RUnlock()
			statusMu.RLock()
			defer statusMu.RUnlock()
			data, err := json.MarshalIndent(status, "", "	")
			if err != nil {
				if *verbose {
					log.Printf("could not get json encode: %v", err)
				}
				http.Error(w, "could not json encode", http.StatusInternalServerError)
				return
			}
			serveJSON(w, r, data)
			return
		}
		lastUpdateMu.RUnlock()
	}
	list, err := downloadList()
	if err != nil {
		if *verbose {
			log.Printf("could not get torrents list: %v", err)
		}
		http.Error(w, "could not get torrents list", http.StatusInternalServerError)
		return
	}
	allStatus := make(map[string]*torrentStatus)
	for _, v := range list {
		tStatus, err := getStatus(v)
		if err != nil {
			if *verbose {
				log.Printf("could not get torrent status: %v", err)
			}
			http.Error(w, "could not get torrent status", http.StatusInternalServerError)
			return
		}
		allStatus[v] = tStatus
	}
	lastUpdateMu.Lock()
	defer lastUpdateMu.Unlock()
	statusMu.Lock()
	defer statusMu.Unlock()
	data, err := json.MarshalIndent(allStatus, "", "	")
	if err != nil {
		if *verbose {
			log.Printf("could not get json encode: %v", err)
		}
		http.Error(w, "could not json encode", http.StatusInternalServerError)
		return
	}
	status = allStatus
	lastUpdate = time.Now()
	serveJSON(w, r, data)
}

func setupTLS() (*tls.Config, error) {
	hostname := *host
	if strings.Contains(hostname, ":") {
		h, _, err := net.SplitHostPort(hostname)
		if err != nil {
			return nil, err
		}
		hostname = h
	}
	if *flagAutocert {
		m := autocert.Manager{
			Prompt:     autocert.AcceptTOS,
			HostPolicy: autocert.HostWhitelist(hostname),
			Cache:      autocert.DirCache(certCache),
		}
		return &tls.Config{
			GetCertificate: m.GetCertificate,
		}, nil
	}
	cert, err := tls.LoadX509KeyPair(tlsCert, tlsKey)
	if err != nil {
		return nil, fmt.Errorf("Failed to load TLS cert: %v", err)
	}
	return &tls.Config{
		Rand:         rand.Reader,
		Time:         time.Now,
		NextProtos:   []string{"http/1.1"},
		Certificates: []tls.Certificate{cert},
	}, nil
}

var (
	up        *basicauth.UserPass
	tlsKey    = filepath.Join(os.Getenv("HOME"), "keys", "key.pem")
	tlsCert   = filepath.Join(os.Getenv("HOME"), "keys", "cert.pem")
	certCache = filepath.Join(os.Getenv("HOME"), "keys", "letsencrypt.cache")

	statusMu sync.RWMutex
	status   map[string]*torrentStatus

	lastUpdateMu sync.RWMutex
	lastUpdate   time.Time
)

func main() {
	flag.Usage = usage
	flag.Parse()

	var err error
	up, err = basicauth.New(*userpass)
	if err != nil {
		log.Fatal(err)
	}
	if *verbose {
		basicauth.Verbose = true
	}

	listener, err := net.Listen("tcp", *host)
	if err != nil {
		log.Fatalf("Failed to listen on %s: %v", *host, err)
	}

	if !*flagTLS && *flagAutocert {
		*flagTLS = true
	}

	if *flagTLS {
		config, err := setupTLS()
		if err != nil {
			log.Fatalf("could not configure TLS connection: %v", err)
		}
		listener = tls.NewListener(listener, config)
	}

	http.Handle("/", makeHandler(serveStatus))
	log.Fatal(http.Serve(listener, nil))
}
<commit_msg>use simpletls<commit_after>// 2015 - Mathieu Lonjaret

// The montorrent program serves the status of rtorrent. It uses
// github.com/mpl/rtorrentrpc to query rtorrent.
package main

import (
	"bufio"
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net"
	"net/http"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/mpl/basicauth"
	"github.com/mpl/simpletls"
)

var (
	cache   = flag.Int("cache", 0, "cache the status for that many seconds. 0 means no caching")
	help    = flag.Bool("h", false, "show this help")
	host    = flag.String("host", "localhost:8080", "listening host:port")
	scgi    = flag.String("scgi", "localhost:5000", "host:port for rtorrent's scgi.")
	flagTLS = flag.Bool("tls", false, `For https.`)

	userpass = flag.String("userpass", "", "optional username:password protection")
	verbose  = flag.Bool("v", false, "verbose")
)

const (
	numRetry   = 20
	retryDelay = 2 * time.Second
	idstring   = "http://golang.org/pkg/http/#ListenAndServe"
)

func usage() {
	flag.PrintDefaults()
	os.Exit(2)
}

func rpc(args ...string) ([]byte, error) {
	var answer []byte
	for i := 0; i < numRetry; i++ {
		cmd := exec.Command("rtorrentrpc", args...)
		output, err := cmd.CombinedOutput()
		if err != nil {
			// TODO(mpl): diagnose better the error and return early if it's not the expected EOF one.
			if *verbose {
				log.Printf("ignoring error: %v", err)
			}
			continue
		}
		if len(output) > 0 {
			answer = output
			break
		}
		time.Sleep(retryDelay)
	}
	if len(answer) == 0 {
		return nil, fmt.Errorf("empty answer for %v", args)
	}
	return answer, nil
}

func scanAnswer(answer []byte, prefix, suffix string) ([]string, error) {
	var list []string
	scanner := bufio.NewScanner(bytes.NewReader(answer))
	for scanner.Scan() {
		line := scanner.Text()
		if !strings.HasPrefix(line, prefix) || !strings.HasSuffix(line, suffix) {
			continue
		}
		list = append(list, strings.TrimSuffix(strings.TrimPrefix(line, prefix), suffix))
	}
	if err := scanner.Err(); err != nil {
		return nil, fmt.Errorf("could not scan answer: %v", err)
	}
	return list, nil
}

func downloadList() ([]string, error) {
	answer, err := rpc(*scgi, "download_list", "")
	if err != nil {
		return nil, err
	}
	return scanAnswer(answer, "<value><string>", "</string></value>")
}

func torrentName(torrentHash string) (string, error) {
	answer, err := rpc(*scgi, "d.name", torrentHash)
	if err != nil {
		return "", err
	}
	list, err := scanAnswer(answer, "<param><value><string>", "</string></value></param>")
	if err != nil {
		return "", err
	}
	if len(list) == 0 {
		return "", fmt.Errorf("%v: name not found", torrentHash)
	}
	return list[0], nil
}

func bytesDone(torrentHash string) (int, error) {
	var n int
	answer, err := rpc(*scgi, "d.get_bytes_done", torrentHash)
	if err != nil {
		return n, err
	}
	list, err := scanAnswer(answer, "<param><value><i8>", "</i8></value></param>")
	if err != nil {
		return n, err
	}
	if len(list) == 0 {
		return n, fmt.Errorf("%v: bytes_done not found", torrentHash)
	}
	n, err = strconv.Atoi(list[0])
	if err != nil {
		return n, fmt.Errorf("could not convert bytes_done to int: %v", err)
	}
	return n, nil
}

func bytesLeft(torrentHash string) (int, error) {
	var n int
	answer, err := rpc(*scgi, "d.get_left_bytes", torrentHash)
	if err != nil {
		return n, err
	}
	list, err := scanAnswer(answer, "<param><value><i8>", "</i8></value></param>")
	if err != nil {
		return n, err
	}
	if len(list) == 0 {
		return n, fmt.Errorf("%v: bytes_left not found", torrentHash)
	}
	n, err = strconv.Atoi(list[0])
	if err != nil {
		return n, fmt.Errorf("could not convert bytes_left to int: %v", err)
	}
	return n, nil
}

type torrentStatus struct {
	Name        string
	BytesDone   int
	BytesLeft   int
	BytesTotal  int
	PercentDone int
}

func getStatus(torrentHash string) (*torrentStatus, error) {
	name, err := torrentName(torrentHash)
	if err != nil {
		return nil, err
	}
	nDone, err := bytesDone(torrentHash)
	if err != nil {
		return nil, err
	}
	nLeft, err := bytesLeft(torrentHash)
	if err != nil {
		return nil, err
	}
	total := nDone + nLeft // yay, super precision!!
	percent := nDone * 100 / total
	return &torrentStatus{
		Name:        name,
		BytesDone:   nDone,
		BytesLeft:   nLeft,
		BytesTotal:  total,
		PercentDone: percent,
	}, nil
}

func makeHandler(fn func(http.ResponseWriter, *http.Request)) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if e, ok := recover().(error); ok {
				http.Error(w, e.Error(), http.StatusInternalServerError)
				return
			}
		}()
		w.Header().Set("Server", idstring)
		if up.IsAllowed(r) {
			fn(w, r)
		} else {
			basicauth.SendUnauthorized(w, r, "montorrent")
		}
	}
}

func serveJSON(w http.ResponseWriter, r *http.Request, data []byte) {
	w.Header().Set("Content-Type", "text/javascript")
	w.Header().Set("Content-Length", strconv.Itoa(len(data)+1))
	w.WriteHeader(200)
	w.Write(data)
	w.Write([]byte("\n"))
}

func serveStatus(w http.ResponseWriter, r *http.Request) {
	if *cache > 0 {
		lastUpdateMu.RLock()
		// TODO(mpl): maybe use a checkLastModified. pull it out of simpleHttpd as a lib?
		if time.Now().Before(lastUpdate.Add(time.Duration(*cache) * time.Second)) {
			defer lastUpdateMu.RUnlock()
			statusMu.RLock()
			defer statusMu.RUnlock()
			data, err := json.MarshalIndent(status, "", "	")
			if err != nil {
				if *verbose {
					log.Printf("could not get json encode: %v", err)
				}
				http.Error(w, "could not json encode", http.StatusInternalServerError)
				return
			}
			serveJSON(w, r, data)
			return
		}
		lastUpdateMu.RUnlock()
	}
	list, err := downloadList()
	if err != nil {
		if *verbose {
			log.Printf("could not get torrents list: %v", err)
		}
		http.Error(w, "could not get torrents list", http.StatusInternalServerError)
		return
	}
	allStatus := make(map[string]*torrentStatus)
	for _, v := range list {
		tStatus, err := getStatus(v)
		if err != nil {
			if *verbose {
				log.Printf("could not get torrent status: %v", err)
			}
			http.Error(w, "could not get torrent status", http.StatusInternalServerError)
			return
		}
		allStatus[v] = tStatus
	}
	lastUpdateMu.Lock()
	defer lastUpdateMu.Unlock()
	statusMu.Lock()
	defer statusMu.Unlock()
	data, err := json.MarshalIndent(allStatus, "", "	")
	if err != nil {
		if *verbose {
			log.Printf("could not get json encode: %v", err)
		}
		http.Error(w, "could not json encode", http.StatusInternalServerError)
		return
	}
	status = allStatus
	lastUpdate = time.Now()
	serveJSON(w, r, data)
}

var (
	up *basicauth.UserPass

	statusMu sync.RWMutex
	status   map[string]*torrentStatus

	lastUpdateMu sync.RWMutex
	lastUpdate   time.Time
)

func main() {
	flag.Usage = usage
	flag.Parse()

	var err error
	up, err = basicauth.New(*userpass)
	if err != nil {
		log.Fatal(err)
	}
	if *verbose {
		basicauth.Verbose = true
	}

	http.Handle("/", makeHandler(serveStatus))

	if !*flagTLS && *simpletls.FlagAutocert {
		*flagTLS = true
	}

	var listener net.Listener
	if *flagTLS {
		listener, err = simpletls.Listen(*host)
	} else {
		listener, err = net.Listen("tcp", *host)
	}
	if err != nil {
		log.Fatalf("Failed to listen on %s: %v", *host, err)
	}

	log.Fatal(http.Serve(listener, nil))
}
<|endoftext|>