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

package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"strings"
	"syscall"

	alertmanagercontroller "github.com/coreos/prometheus-operator/pkg/alertmanager"
	"github.com/coreos/prometheus-operator/pkg/api"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	prometheuscontroller "github.com/coreos/prometheus-operator/pkg/prometheus"
	"github.com/coreos/prometheus-operator/pkg/version"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"
	"k8s.io/api/core/v1"
)

const (
	logLevelAll   = "all"
	logLevelDebug = "debug"
	logLevelInfo  = "info"
	logLevelWarn  = "warn"
	logLevelError = "error"
	logLevelNone  = "none"
)

const (
	logFormatLogfmt = "logfmt"
	logFormatJson   = "json"
)

var (
	ns = namespaces{}
)

type namespaces map[string]struct{}

// Set implements the flagset.Value interface.
func (n namespaces) Set(value string) error {
	if n == nil {
		return errors.New("expected n of type namespaces to be initialized")
	}
	ns := strings.Split(value, ",")
	for i := range ns {
		n[ns[i]] = struct{}{}
	}
	return nil
}

// String implements the flagset.Value interface.
func (n namespaces) String() string {
	return strings.Join(n.asSlice(), ",")
}

func (n namespaces) asSlice() []string {
	var ns []string
	for k := range n {
		ns = append(ns, k)
	}
	if len(ns) == 0 {
		ns = append(ns, v1.NamespaceAll)
	}
	return ns
}

var (
	cfg                prometheuscontroller.Config
	availableLogLevels = []string{
		logLevelAll,
		logLevelDebug,
		logLevelInfo,
		logLevelWarn,
		logLevelError,
		logLevelNone,
	}
	availableLogFormats = []string{
		logFormatLogfmt,
		logFormatJson,
	}
)

func init() {
	cfg.CrdKinds = monitoringv1.DefaultCrdKinds
	flagset := flag.CommandLine
	flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
	flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	// The Prometheus config reloader image is released along with the
	// Prometheus Operator image, tagged with the same semver version. Default to
	// the Prometheus Operator version if no Prometheus config reloader image is
	// specified.
	flagset.StringVar(&cfg.PrometheusConfigReloader, "prometheus-config-reloader", fmt.Sprintf("quay.io/coreos/prometheus-config-reloader:v%v", version.Version), "Prometheus config reloader image")
	flagset.StringVar(&cfg.ConfigReloaderImage, "config-reloader-image", "quay.io/coreos/configmap-reload:v0.0.1", "Reload Image")
	flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", "quay.io/prometheus/alertmanager", "Alertmanager default base image")
	flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", "quay.io/prometheus/prometheus", "Prometheus default base image")
	flagset.StringVar(&cfg.ThanosDefaultBaseImage, "thanos-default-base-image", "improbable/thanos", "Thanos default base image")
	flagset.Var(ns, "namespaces", "Namespaces to scope the interaction of the Prometheus Operator and the apiserver.")
	flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
	flagset.StringVar(&cfg.CrdGroup, "crd-apigroup", monitoringv1.Group, "prometheus CRD  API group name")
	flagset.Var(&cfg.CrdKinds, "crd-kinds", " - EXPERIMENTAL (could be removed in future releases) - customize CRD kind names")
	flagset.BoolVar(&cfg.EnableValidation, "with-validation", true, "Include the validation spec in the CRD")
	flagset.BoolVar(&cfg.DisableAutoUserGroup, "disable-auto-user-group", false, "Disables the Prometheus Operator setting the `runAsUser` and `fsGroup` fields in Pods.")
	flagset.StringVar(&cfg.LocalHost, "localhost", "localhost", "EXPERIMENTAL (could be removed in future releases) - Host used to communicate between local services on a pod. Fixes issues where localhost resolves incorrectly.")
	flagset.StringVar(&cfg.LogLevel, "log-level", logLevelInfo, fmt.Sprintf("Log level to use. Possible values: %s", strings.Join(availableLogLevels, ", ")))
	flagset.StringVar(&cfg.LogFormat, "log-format", logFormatLogfmt, fmt.Sprintf("Log format to use. Possible values: %s", strings.Join(availableLogFormats, ", ")))
	flagset.BoolVar(&cfg.ManageCRDs, "manage-crds", true, "Manage all CRDs with the Prometheus Operator.")
	flagset.Parse(os.Args[1:])
	cfg.Namespaces = ns.asSlice()
}

func Main() int {
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	if cfg.LogFormat == logFormatJson {
		logger = log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
	}
	switch cfg.LogLevel {
	case logLevelAll:
		logger = level.NewFilter(logger, level.AllowAll())
	case logLevelDebug:
		logger = level.NewFilter(logger, level.AllowDebug())
	case logLevelInfo:
		logger = level.NewFilter(logger, level.AllowInfo())
	case logLevelWarn:
		logger = level.NewFilter(logger, level.AllowWarn())
	case logLevelError:
		logger = level.NewFilter(logger, level.AllowError())
	case logLevelNone:
		logger = level.NewFilter(logger, level.AllowNone())
	default:
		fmt.Fprintf(os.Stderr, "log level %v unknown, %v are possible values", cfg.LogLevel, availableLogLevels)
		return 1
	}
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)

	logger.Log("msg", fmt.Sprintf("Starting Prometheus Operator version '%v'.", version.Version))

	po, err := prometheuscontroller.New(cfg, log.With(logger, "component", "prometheusoperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
		return 1
	}

	ao, err := alertmanagercontroller.New(cfg, log.With(logger, "component", "alertmanageroperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
		return 1
	}

	mux := http.NewServeMux()
	web, err := api.New(cfg, log.With(logger, "component", "api"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating api failed: ", err)
		return 1
	}

	web.Register(mux)
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Fprint(os.Stderr, "listening port 8080 failed", err)
		return 1
	}

	reconcileErrorsCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "prometheus_operator_reconcile_errors_total",
		Help: "Number of errors that occurred while reconciling the alertmanager statefulset",
	}, []string{"controller"})

	triggerByCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "prometheus_operator_triggered_total",
		Help: "Number of times a Kubernetes object add, delete or update event" +
			" triggered the Prometheus Operator to reconcile an object",
	}, []string{"controller", "triggered_by", "action"})

	r := prometheus.NewRegistry()
	r.MustRegister(
		prometheus.NewGoCollector(),
		prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
		reconcileErrorsCounter,
		triggerByCounter,
	)

	prometheusLabels := prometheus.Labels{"controller": "prometheus"}
	po.RegisterMetrics(
		prometheus.WrapRegistererWith(prometheusLabels, r),
		reconcileErrorsCounter.MustCurryWith(prometheusLabels),
		triggerByCounter.MustCurryWith(prometheusLabels),
	)

	alertmanagerLabels := prometheus.Labels{"controller": "alertmanager"}
	ao.RegisterMetrics(
		prometheus.WrapRegistererWith(alertmanagerLabels, r),
		reconcileErrorsCounter.MustCurryWith(alertmanagerLabels),
		triggerByCounter.MustCurryWith(prometheusLabels),
	)

	mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
	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))

	ctx, cancel := context.WithCancel(context.Background())
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error { return po.Run(ctx.Done()) })
	wg.Go(func() error { return ao.Run(ctx.Done()) })

	srv := &http.Server{Handler: mux}
	go srv.Serve(l)

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

	select {
	case <-term:
		logger.Log("msg", "Received SIGTERM, exiting gracefully...")
	case <-ctx.Done():
	}

	cancel()
	if err := wg.Wait(); err != nil {
		logger.Log("msg", "Unhandled error received. Exiting...", "err", err)
		return 1
	}

	return 0
}

func main() {
	os.Exit(Main())
}
<commit_msg>cmd/operator: Fix labels for alertmanager controller<commit_after>// Copyright 2016 The prometheus-operator Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"context"
	"errors"
	"flag"
	"fmt"
	"net"
	"net/http"
	"net/http/pprof"
	"os"
	"os/signal"
	"strings"
	"syscall"

	alertmanagercontroller "github.com/coreos/prometheus-operator/pkg/alertmanager"
	"github.com/coreos/prometheus-operator/pkg/api"
	monitoringv1 "github.com/coreos/prometheus-operator/pkg/client/monitoring/v1"
	prometheuscontroller "github.com/coreos/prometheus-operator/pkg/prometheus"
	"github.com/coreos/prometheus-operator/pkg/version"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/log/level"
	"github.com/prometheus/client_golang/prometheus"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"golang.org/x/sync/errgroup"
	"k8s.io/api/core/v1"
)

const (
	logLevelAll   = "all"
	logLevelDebug = "debug"
	logLevelInfo  = "info"
	logLevelWarn  = "warn"
	logLevelError = "error"
	logLevelNone  = "none"
)

const (
	logFormatLogfmt = "logfmt"
	logFormatJson   = "json"
)

var (
	ns = namespaces{}
)

type namespaces map[string]struct{}

// Set implements the flagset.Value interface.
func (n namespaces) Set(value string) error {
	if n == nil {
		return errors.New("expected n of type namespaces to be initialized")
	}
	ns := strings.Split(value, ",")
	for i := range ns {
		n[ns[i]] = struct{}{}
	}
	return nil
}

// String implements the flagset.Value interface.
func (n namespaces) String() string {
	return strings.Join(n.asSlice(), ",")
}

func (n namespaces) asSlice() []string {
	var ns []string
	for k := range n {
		ns = append(ns, k)
	}
	if len(ns) == 0 {
		ns = append(ns, v1.NamespaceAll)
	}
	return ns
}

var (
	cfg                prometheuscontroller.Config
	availableLogLevels = []string{
		logLevelAll,
		logLevelDebug,
		logLevelInfo,
		logLevelWarn,
		logLevelError,
		logLevelNone,
	}
	availableLogFormats = []string{
		logFormatLogfmt,
		logFormatJson,
	}
)

func init() {
	cfg.CrdKinds = monitoringv1.DefaultCrdKinds
	flagset := flag.CommandLine
	flagset.StringVar(&cfg.Host, "apiserver", "", "API Server addr, e.g. ' - NOT RECOMMENDED FOR PRODUCTION - http://127.0.0.1:8080'. Omit parameter to run in on-cluster mode and utilize the service account token.")
	flagset.StringVar(&cfg.TLSConfig.CertFile, "cert-file", "", " - NOT RECOMMENDED FOR PRODUCTION - Path to public TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.KeyFile, "key-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to private TLS certificate file.")
	flagset.StringVar(&cfg.TLSConfig.CAFile, "ca-file", "", "- NOT RECOMMENDED FOR PRODUCTION - Path to TLS CA file.")
	flagset.StringVar(&cfg.KubeletObject, "kubelet-service", "", "Service/Endpoints object to write kubelets into in format \"namespace/name\"")
	flagset.BoolVar(&cfg.TLSInsecure, "tls-insecure", false, "- NOT RECOMMENDED FOR PRODUCTION - Don't verify API server's CA certificate.")
	// The Prometheus config reloader image is released along with the
	// Prometheus Operator image, tagged with the same semver version. Default to
	// the Prometheus Operator version if no Prometheus config reloader image is
	// specified.
	flagset.StringVar(&cfg.PrometheusConfigReloader, "prometheus-config-reloader", fmt.Sprintf("quay.io/coreos/prometheus-config-reloader:v%v", version.Version), "Prometheus config reloader image")
	flagset.StringVar(&cfg.ConfigReloaderImage, "config-reloader-image", "quay.io/coreos/configmap-reload:v0.0.1", "Reload Image")
	flagset.StringVar(&cfg.AlertmanagerDefaultBaseImage, "alertmanager-default-base-image", "quay.io/prometheus/alertmanager", "Alertmanager default base image")
	flagset.StringVar(&cfg.PrometheusDefaultBaseImage, "prometheus-default-base-image", "quay.io/prometheus/prometheus", "Prometheus default base image")
	flagset.StringVar(&cfg.ThanosDefaultBaseImage, "thanos-default-base-image", "improbable/thanos", "Thanos default base image")
	flagset.Var(ns, "namespaces", "Namespaces to scope the interaction of the Prometheus Operator and the apiserver.")
	flagset.Var(&cfg.Labels, "labels", "Labels to be add to all resources created by the operator")
	flagset.StringVar(&cfg.CrdGroup, "crd-apigroup", monitoringv1.Group, "prometheus CRD  API group name")
	flagset.Var(&cfg.CrdKinds, "crd-kinds", " - EXPERIMENTAL (could be removed in future releases) - customize CRD kind names")
	flagset.BoolVar(&cfg.EnableValidation, "with-validation", true, "Include the validation spec in the CRD")
	flagset.BoolVar(&cfg.DisableAutoUserGroup, "disable-auto-user-group", false, "Disables the Prometheus Operator setting the `runAsUser` and `fsGroup` fields in Pods.")
	flagset.StringVar(&cfg.LocalHost, "localhost", "localhost", "EXPERIMENTAL (could be removed in future releases) - Host used to communicate between local services on a pod. Fixes issues where localhost resolves incorrectly.")
	flagset.StringVar(&cfg.LogLevel, "log-level", logLevelInfo, fmt.Sprintf("Log level to use. Possible values: %s", strings.Join(availableLogLevels, ", ")))
	flagset.StringVar(&cfg.LogFormat, "log-format", logFormatLogfmt, fmt.Sprintf("Log format to use. Possible values: %s", strings.Join(availableLogFormats, ", ")))
	flagset.BoolVar(&cfg.ManageCRDs, "manage-crds", true, "Manage all CRDs with the Prometheus Operator.")
	flagset.Parse(os.Args[1:])
	cfg.Namespaces = ns.asSlice()
}

func Main() int {
	logger := log.NewLogfmtLogger(log.NewSyncWriter(os.Stdout))
	if cfg.LogFormat == logFormatJson {
		logger = log.NewJSONLogger(log.NewSyncWriter(os.Stdout))
	}
	switch cfg.LogLevel {
	case logLevelAll:
		logger = level.NewFilter(logger, level.AllowAll())
	case logLevelDebug:
		logger = level.NewFilter(logger, level.AllowDebug())
	case logLevelInfo:
		logger = level.NewFilter(logger, level.AllowInfo())
	case logLevelWarn:
		logger = level.NewFilter(logger, level.AllowWarn())
	case logLevelError:
		logger = level.NewFilter(logger, level.AllowError())
	case logLevelNone:
		logger = level.NewFilter(logger, level.AllowNone())
	default:
		fmt.Fprintf(os.Stderr, "log level %v unknown, %v are possible values", cfg.LogLevel, availableLogLevels)
		return 1
	}
	logger = log.With(logger, "ts", log.DefaultTimestampUTC)
	logger = log.With(logger, "caller", log.DefaultCaller)

	logger.Log("msg", fmt.Sprintf("Starting Prometheus Operator version '%v'.", version.Version))

	po, err := prometheuscontroller.New(cfg, log.With(logger, "component", "prometheusoperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating prometheus controller failed: ", err)
		return 1
	}

	ao, err := alertmanagercontroller.New(cfg, log.With(logger, "component", "alertmanageroperator"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating alertmanager controller failed: ", err)
		return 1
	}

	mux := http.NewServeMux()
	web, err := api.New(cfg, log.With(logger, "component", "api"))
	if err != nil {
		fmt.Fprint(os.Stderr, "instantiating api failed: ", err)
		return 1
	}

	web.Register(mux)
	l, err := net.Listen("tcp", ":8080")
	if err != nil {
		fmt.Fprint(os.Stderr, "listening port 8080 failed", err)
		return 1
	}

	reconcileErrorsCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "prometheus_operator_reconcile_errors_total",
		Help: "Number of errors that occurred while reconciling the alertmanager statefulset",
	}, []string{"controller"})

	triggerByCounter := prometheus.NewCounterVec(prometheus.CounterOpts{
		Name: "prometheus_operator_triggered_total",
		Help: "Number of times a Kubernetes object add, delete or update event" +
			" triggered the Prometheus Operator to reconcile an object",
	}, []string{"controller", "triggered_by", "action"})

	r := prometheus.NewRegistry()
	r.MustRegister(
		prometheus.NewGoCollector(),
		prometheus.NewProcessCollector(prometheus.ProcessCollectorOpts{}),
		reconcileErrorsCounter,
		triggerByCounter,
	)

	prometheusLabels := prometheus.Labels{"controller": "prometheus"}
	po.RegisterMetrics(
		prometheus.WrapRegistererWith(prometheusLabels, r),
		reconcileErrorsCounter.MustCurryWith(prometheusLabels),
		triggerByCounter.MustCurryWith(prometheusLabels),
	)

	alertmanagerLabels := prometheus.Labels{"controller": "alertmanager"}
	ao.RegisterMetrics(
		prometheus.WrapRegistererWith(alertmanagerLabels, r),
		reconcileErrorsCounter.MustCurryWith(alertmanagerLabels),
		triggerByCounter.MustCurryWith(alertmanagerLabels),
	)

	mux.Handle("/metrics", promhttp.HandlerFor(r, promhttp.HandlerOpts{}))
	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))

	ctx, cancel := context.WithCancel(context.Background())
	wg, ctx := errgroup.WithContext(ctx)

	wg.Go(func() error { return po.Run(ctx.Done()) })
	wg.Go(func() error { return ao.Run(ctx.Done()) })

	srv := &http.Server{Handler: mux}
	go srv.Serve(l)

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

	select {
	case <-term:
		logger.Log("msg", "Received SIGTERM, exiting gracefully...")
	case <-ctx.Done():
	}

	cancel()
	if err := wg.Wait(); err != nil {
		logger.Log("msg", "Unhandled error received. Exiting...", "err", err)
		return 1
	}

	return 0
}

func main() {
	os.Exit(Main())
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/mackerelio/go-osstat/uptime"
)

type uptimeGenerator struct {
	uptime time.Duration
	err    error
}

func (gen *uptimeGenerator) Get() {
	gen.uptime, gen.err = uptime.Get()
}

func (gen *uptimeGenerator) Error() error {
	return gen.err
}

func (gen *uptimeGenerator) Print(out chan<- value) {
	uptime := gen.uptime
	out <- value{"uptime", fmt.Sprintf("%f", float64(uptime.Nanoseconds())/1e9), "s"}
}
<commit_msg>improve unit for uptime in osstat command<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/mackerelio/go-osstat/uptime"
)

type uptimeGenerator struct {
	uptime time.Duration
	err    error
}

func (gen *uptimeGenerator) Get() {
	gen.uptime, gen.err = uptime.Get()
}

func (gen *uptimeGenerator) Error() error {
	return gen.err
}

func (gen *uptimeGenerator) Print(out chan<- value) {
	uptime := gen.uptime
	out <- value{"uptime", fmt.Sprintf("%f", float64(uptime.Nanoseconds())/1e9), "seconds"}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/percol"
	"github.com/nsf/termbox-go"
)

func showHelp() {
	const v = `
Usage: percol [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --tty=TTY             path to the TTY (usually, the value of $TTY)
  --rcfile=RCFILE       path to the settings file
  --output-encoding=OUTPUT_ENCODING
                        encoding for output
  --input-encoding=INPUT_ENCODING
                        encoding for input and output (default 'utf8')
  --query=QUERY         pre-input query
  --eager               suppress lazy matching (slower, but display correct
                        candidates count)
  --eval=STRING_TO_EVAL
                        eval given string after loading the rc file
  --prompt=PROMPT       specify prompt (percol.view.PROMPT)
  --right-prompt=RIGHT_PROMPT
                        specify right prompt (percol.view.RPROMPT)
  --match-method=MATCH_METHOD
                        specify matching method for query. ` + "`string`" + ` (default)
                        and ` + "`regex`" + ` are currently supported
  --caret-position=CARET
                        position of the caret (default length of the ` + "`query`" + `)
  --initial-index=INDEX
                        position of the initial index of the selection
                        (numeric, "first" or "last")
  --case-sensitive      whether distinguish the case of query or not
  --reverse             whether reverse the order of candidates or not
  --auto-fail           auto fail if no candidates
  --auto-match          auto matching if only one candidate
  --prompt-top          display prompt top of the screen (default)
  --prompt-bottom       display prompt bottom of the screen
  --result-top-down     display results top down (default)
  --result-bottom-up    display results bottom up instead of top down
  --quote               whether quote the output line
  --peep                exit immediately with doing nothing to cache module
                        files and speed up start-up time
`
	os.Stderr.Write([]byte(v))
}

type CmdOptions struct {
	Help  bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY   string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query string `long:"query"`
}

func main() {
	var err error

	opts := &CmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse() // &opts, os.Args)
	if err != nil {
		panic(err)
		os.Exit(1)
	}

	if opts.Help {
		showHelp()
		os.Exit(1)
	}

	var input *os.File

	// receive input from either a file or Stdin
	if len(args) > 0 {
		input, err = os.Open(args[0])
		if err != nil {
			os.Exit(1)
		}
	} else if !percol.IsTty() {
		input = os.Stdin
	}

	ctx := percol.NewCtx()
	defer func() {
		if result := ctx.Result(); result != "" {
			os.Stdout.WriteString(result)
		}
	}()

	ctx.ReadBuffer(input)

	err = percol.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	ui := ctx.NewUI()
	filter := ctx.NewFilter()

	go ui.Loop()
	go filter.Loop()
	go ctx.Loop()

	if len(opts.Query) > 0 {
		ctx.ExecQuery(string(string(opts.Query)))
	} else {
		ui.Refresh()
	}

	ctx.WaitDone()
	ctx.PrintResult()
}
<commit_msg>Fix package name<commit_after>package main

import (
	"fmt"
	"os"

	"github.com/jessevdk/go-flags"
	"github.com/lestrrat/go-percol"
	"github.com/nsf/termbox-go"
)

func showHelp() {
	const v = `
Usage: percol [options] [FILE]

Options:
  -h, --help            show this help message and exit
  --tty=TTY             path to the TTY (usually, the value of $TTY)
  --rcfile=RCFILE       path to the settings file
  --output-encoding=OUTPUT_ENCODING
                        encoding for output
  --input-encoding=INPUT_ENCODING
                        encoding for input and output (default 'utf8')
  --query=QUERY         pre-input query
  --eager               suppress lazy matching (slower, but display correct
                        candidates count)
  --eval=STRING_TO_EVAL
                        eval given string after loading the rc file
  --prompt=PROMPT       specify prompt (percol.view.PROMPT)
  --right-prompt=RIGHT_PROMPT
                        specify right prompt (percol.view.RPROMPT)
  --match-method=MATCH_METHOD
                        specify matching method for query. ` + "`string`" + ` (default)
                        and ` + "`regex`" + ` are currently supported
  --caret-position=CARET
                        position of the caret (default length of the ` + "`query`" + `)
  --initial-index=INDEX
                        position of the initial index of the selection
                        (numeric, "first" or "last")
  --case-sensitive      whether distinguish the case of query or not
  --reverse             whether reverse the order of candidates or not
  --auto-fail           auto fail if no candidates
  --auto-match          auto matching if only one candidate
  --prompt-top          display prompt top of the screen (default)
  --prompt-bottom       display prompt bottom of the screen
  --result-top-down     display results top down (default)
  --result-bottom-up    display results bottom up instead of top down
  --quote               whether quote the output line
  --peep                exit immediately with doing nothing to cache module
                        files and speed up start-up time
`
	os.Stderr.Write([]byte(v))
}

type CmdOptions struct {
	Help  bool   `short:"h" long:"help" description:"show this help message and exit"`
	TTY   string `long:"tty" description:"path to the TTY (usually, the value of $TTY)"`
	Query string `long:"query"`
}

func main() {
	var err error

	opts := &CmdOptions{}
	p := flags.NewParser(opts, flags.PrintErrors)
	args, err := p.Parse() // &opts, os.Args)
	if err != nil {
		panic(err)
		os.Exit(1)
	}

	if opts.Help {
		showHelp()
		os.Exit(1)
	}

	var input *os.File

	// receive input from either a file or Stdin
	if len(args) > 0 {
		input, err = os.Open(args[0])
		if err != nil {
			os.Exit(1)
		}
	} else if !percol.IsTty() {
		input = os.Stdin
	}

	ctx := percol.NewCtx()
	defer func() {
		if result := ctx.Result(); result != "" {
			os.Stdout.WriteString(result)
		}
	}()

	ctx.ReadBuffer(input)

	err = percol.TtyReady()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}

	err = termbox.Init()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
	defer termbox.Close()
	termbox.SetInputMode(termbox.InputEsc)

	ui := ctx.NewUI()
	filter := ctx.NewFilter()

	go ui.Loop()
	go filter.Loop()
	go ctx.Loop()

	if len(opts.Query) > 0 {
		ctx.ExecQuery(string(string(opts.Query)))
	} else {
		ui.Refresh()
	}

	ctx.WaitDone()
	ctx.PrintResult()
}
<|endoftext|>


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

package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"gopkg.in/alecthomas/kingpin.v2"
	"gopkg.in/yaml.v2"

	config_util "github.com/prometheus/common/config"
	"github.com/prometheus/common/model"
	"github.com/prometheus/common/version"
	"github.com/prometheus/prometheus/config"
	"github.com/prometheus/prometheus/pkg/rulefmt"
	"github.com/prometheus/prometheus/promql"
	"github.com/prometheus/prometheus/util/promlint"
)

func main() {
	app := kingpin.New(filepath.Base(os.Args[0]), "Tooling for the Prometheus monitoring system.")
	app.Version(version.Print("promtool"))
	app.HelpFlag.Short('h')

	checkCmd := app.Command("check", "Check the resources for validity.")

	checkConfigCmd := checkCmd.Command("config", "Check if the config files are valid or not.")
	configFiles := checkConfigCmd.Arg(
		"config-files",
		"The config files to check.",
	).Required().ExistingFiles()

	checkRulesCmd := checkCmd.Command("rules", "Check if the rule files are valid or not.")
	ruleFiles := checkRulesCmd.Arg(
		"rule-files",
		"The rule files to check.",
	).Required().ExistingFiles()

	checkMetricsCmd := checkCmd.Command("metrics", checkMetricsUsage)

	updateCmd := app.Command("update", "Update the resources to newer formats.")
	updateRulesCmd := updateCmd.Command("rules", "Update rules from the 1.x to 2.x format.")
	ruleFilesUp := updateRulesCmd.Arg("rule-files", "The rule files to update.").Required().ExistingFiles()

	switch kingpin.MustParse(app.Parse(os.Args[1:])) {
	case checkConfigCmd.FullCommand():
		os.Exit(CheckConfig(*configFiles...))

	case checkRulesCmd.FullCommand():
		os.Exit(CheckRules(*ruleFiles...))

	case checkMetricsCmd.FullCommand():
		os.Exit(CheckMetrics())

	case updateRulesCmd.FullCommand():
		os.Exit(UpdateRules(*ruleFilesUp...))

	}

}

// CheckConfig validates configuration files.
func CheckConfig(files ...string) int {
	failed := false

	for _, f := range files {
		ruleFiles, err := checkConfig(f)
		if err != nil {
			fmt.Fprintln(os.Stderr, "  FAILED:", err)
			failed = true
		} else {
			fmt.Printf("  SUCCESS: %d rule files found\n", len(ruleFiles))
		}
		fmt.Println()

		for _, rf := range ruleFiles {
			if n, err := checkRules(rf); err != nil {
				fmt.Fprintln(os.Stderr, "  FAILED:", err)
				failed = true
			} else {
				fmt.Printf("  SUCCESS: %d rules found\n", n)
			}
			fmt.Println()
		}
	}
	if failed {
		return 1
	}
	return 0
}

func checkFileExists(fn string) error {
	// Nothing set, nothing to error on.
	if fn == "" {
		return nil
	}
	_, err := os.Stat(fn)
	return err
}

func checkConfig(filename string) ([]string, error) {
	fmt.Println("Checking", filename)

	cfg, err := config.LoadFile(filename)
	if err != nil {
		return nil, err
	}

	var ruleFiles []string
	for _, rf := range cfg.RuleFiles {
		rfs, err := filepath.Glob(rf)
		if err != nil {
			return nil, err
		}
		// If an explicit file was given, error if it is not accessible.
		if !strings.Contains(rf, "*") {
			if len(rfs) == 0 {
				return nil, fmt.Errorf("%q does not point to an existing file", rf)
			}
			if err := checkFileExists(rfs[0]); err != nil {
				return nil, fmt.Errorf("error checking rule file %q: %s", rfs[0], err)
			}
		}
		ruleFiles = append(ruleFiles, rfs...)
	}

	for _, scfg := range cfg.ScrapeConfigs {
		if err := checkFileExists(scfg.HTTPClientConfig.BearerTokenFile); err != nil {
			return nil, fmt.Errorf("error checking bearer token file %q: %s", scfg.HTTPClientConfig.BearerTokenFile, err)
		}

		if err := checkTLSConfig(scfg.HTTPClientConfig.TLSConfig); err != nil {
			return nil, err
		}

		for _, kd := range scfg.ServiceDiscoveryConfig.KubernetesSDConfigs {
			if err := checkTLSConfig(kd.TLSConfig); err != nil {
				return nil, err
			}
		}

		for _, filesd := range scfg.ServiceDiscoveryConfig.FileSDConfigs {
			for _, file := range filesd.Files {
				files, err := filepath.Glob(file)
				if err != nil {
					return nil, err
				}
				if len(files) != 0 {
					// There was at least one match for the glob and we can assume checkFileExists
					// for all matches would pass, we can continue the loop.
					continue
				}
				fmt.Printf("  WARNING: file %q for file_sd in scrape job %q does not exist\n", file, scfg.JobName)
			}
		}
	}

	return ruleFiles, nil
}

func checkTLSConfig(tlsConfig config_util.TLSConfig) error {
	if err := checkFileExists(tlsConfig.CertFile); err != nil {
		return fmt.Errorf("error checking client cert file %q: %s", tlsConfig.CertFile, err)
	}
	if err := checkFileExists(tlsConfig.KeyFile); err != nil {
		return fmt.Errorf("error checking client key file %q: %s", tlsConfig.KeyFile, err)
	}

	if len(tlsConfig.CertFile) > 0 && len(tlsConfig.KeyFile) == 0 {
		return fmt.Errorf("client cert file %q specified without client key file", tlsConfig.CertFile)
	}
	if len(tlsConfig.KeyFile) > 0 && len(tlsConfig.CertFile) == 0 {
		return fmt.Errorf("client key file %q specified without client cert file", tlsConfig.KeyFile)
	}

	return nil
}

// CheckRules validates rule files.
func CheckRules(files ...string) int {
	failed := false

	for _, f := range files {
		if n, errs := checkRules(f); errs != nil {
			fmt.Fprintln(os.Stderr, "  FAILED:")
			for _, e := range errs {
				fmt.Fprintln(os.Stderr, e.Error())
			}
			failed = true
		} else {
			fmt.Printf("  SUCCESS: %d rules found\n", n)
		}
		fmt.Println()
	}
	if failed {
		return 1
	}
	return 0
}

func checkRules(filename string) (int, []error) {
	fmt.Println("Checking", filename)

	rgs, errs := rulefmt.ParseFile(filename)
	if errs != nil {
		return 0, errs
	}

	numRules := 0
	for _, rg := range rgs.Groups {
		numRules += len(rg.Rules)
	}

	return numRules, nil
}

// UpdateRules updates the rule files.
func UpdateRules(files ...string) int {
	failed := false

	for _, f := range files {
		if err := updateRules(f); err != nil {
			fmt.Fprintln(os.Stderr, "  FAILED:", err)
			failed = true
		}
	}

	if failed {
		return 1
	}
	return 0
}

func updateRules(filename string) error {
	fmt.Println("Updating", filename)

	content, err := ioutil.ReadFile(filename)
	if err != nil {
		return err
	}

	rules, err := promql.ParseStmts(string(content))
	if err != nil {
		return err
	}

	yamlRG := &rulefmt.RuleGroups{
		Groups: []rulefmt.RuleGroup{{
			Name: filename,
		}},
	}

	yamlRules := make([]rulefmt.Rule, 0, len(rules))

	for _, rule := range rules {
		switch r := rule.(type) {
		case *promql.AlertStmt:
			yamlRules = append(yamlRules, rulefmt.Rule{
				Alert:       r.Name,
				Expr:        r.Expr.String(),
				For:         model.Duration(r.Duration),
				Labels:      r.Labels.Map(),
				Annotations: r.Annotations.Map(),
			})
		case *promql.RecordStmt:
			yamlRules = append(yamlRules, rulefmt.Rule{
				Record: r.Name,
				Expr:   r.Expr.String(),
				Labels: r.Labels.Map(),
			})
		default:
			panic("unknown statement type")
		}
	}

	yamlRG.Groups[0].Rules = yamlRules
	y, err := yaml.Marshal(yamlRG)
	if err != nil {
		return err
	}

	return ioutil.WriteFile(filename+".yml", y, 0666)
}

var checkMetricsUsage = strings.TrimSpace(`
Pass Prometheus metrics over stdin to lint them for consistency and correctness.

examples:

$ cat metrics.prom | promtool check metrics

$ curl -s http://localhost:9090/metrics | promtool check metrics
`)

// CheckMetrics performs a linting pass on input metrics.
func CheckMetrics() int {
	l := promlint.New(os.Stdin)
	problems, err := l.Lint()
	if err != nil {
		fmt.Fprintln(os.Stderr, "error while linting:", err)
		return 1
	}

	for _, p := range problems {
		fmt.Fprintln(os.Stderr, p.Metric, p.Text)
	}

	if len(problems) > 0 {
		return 3
	}

	return 0
}
<commit_msg>Removed promtool<commit_after><|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/jhh/puka/model"
	"github.com/jhh/puka/resolver"
	"github.com/jhh/puka/resource"
	"github.com/jhh/puka/storage"
	"github.com/manyminds/api2go"
)

func main() {
	port, err := strconv.Atoi(os.Getenv("PORT"))
	if err != nil {
		log.Fatal("$PORT must be set")
	}
	api := api2go.NewAPIWithResolver("v0", &resolver.RequestURL{Port: port})
	bookmarkStorage := storage.NewBookmarkMemoryStorage()
	api.AddResource(model.Bookmark{}, resource.BookmarkResource{BookmarkStorage: bookmarkStorage})

	fmt.Printf("Listening on :%d", port)
	http.ListenAndServe(fmt.Sprintf(":%d", port), api.Handler())
}
<commit_msg>Don't use resolver.<commit_after>package main

import (
	"fmt"
	"log"
	"net/http"
	"os"
	"strconv"

	"github.com/jhh/puka/model"
	"github.com/jhh/puka/resource"
	"github.com/jhh/puka/storage"
	"github.com/manyminds/api2go"
)

func main() {
	port, err := strconv.Atoi(os.Getenv("PORT"))
	if err != nil {
		log.Fatal("$PORT must be set")
	}
	api := api2go.NewAPI("v0")
	bookmarkStorage := storage.NewBookmarkMemoryStorage()
	api.AddResource(model.Bookmark{}, resource.BookmarkResource{BookmarkStorage: bookmarkStorage})

	fmt.Printf("Listening on :%d", port)
	http.ListenAndServe(fmt.Sprintf(":%d", port), api.Handler())
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"os"
	"strings"
	"time"

	"github.com/spf13/cobra"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/fs"
	"github.com/restic/restic/internal/restic"
	"github.com/restic/restic/internal/walker"
)

var cmdLs = &cobra.Command{
	Use:   "ls [flags] [snapshotID] [dir...]",
	Short: "List files in a snapshot",
	Long: `
The "ls" command lists files and directories in a snapshot.

The special snapshot ID "latest" can be used to list files and
directories of the latest snapshot in the repository. The
--host flag can be used in conjunction to select the latest
snapshot originating from a certain host only.

File listings can optionally be filtered by directories. Any
positional arguments after the snapshot ID are interpreted as
absolute directory paths, and only files inside those directories
will be listed. If the --recursive flag is used, then the filter
will allow traversing into matching directories' subfolders.
Any directory paths specified must be absolute (starting with
a path separator); paths use the forward slash '/' as separator.

EXIT STATUS
===========

Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runLs(lsOptions, globalOptions, args)
	},
}

// LsOptions collects all options for the ls command.
type LsOptions struct {
	ListLong  bool
	Hosts     []string
	Tags      restic.TagLists
	Paths     []string
	Recursive bool
}

var lsOptions LsOptions

func init() {
	cmdRoot.AddCommand(cmdLs)

	flags := cmdLs.Flags()
	flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
	flags.StringArrayVarP(&lsOptions.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when no snapshot ID is given (can be specified multiple times)")
	flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
	flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
	flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
}

type lsSnapshot struct {
	*restic.Snapshot
	ID         *restic.ID `json:"id"`
	ShortID    string     `json:"short_id"`
	StructType string     `json:"struct_type"` // "snapshot"
}

type lsNode struct {
	Name       string      `json:"name"`
	Type       string      `json:"type"`
	Path       string      `json:"path"`
	UID        uint32      `json:"uid"`
	GID        uint32      `json:"gid"`
	Size       uint64      `json:"size,omitempty"`
	Mode       os.FileMode `json:"mode,omitempty"`
	ModTime    time.Time   `json:"mtime,omitempty"`
	AccessTime time.Time   `json:"atime,omitempty"`
	ChangeTime time.Time   `json:"ctime,omitempty"`
	StructType string      `json:"struct_type"` // "node"
}

func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
	if len(args) == 0 && len(opts.Hosts) == 0 && len(opts.Tags) == 0 && len(opts.Paths) == 0 {
		return errors.Fatal("Invalid arguments, either give one or more snapshot IDs or set filters.")
	}

	// extract any specific directories to walk
	var dirs []string
	if len(args) > 1 {
		dirs = args[1:]
		for _, dir := range dirs {
			if !strings.HasPrefix(dir, "/") {
				return errors.Fatal("All path filters must be absolute, starting with a forward slash '/'")
			}
		}
	}

	withinDir := func(nodepath string) bool {
		if len(dirs) == 0 {
			return true
		}

		for _, dir := range dirs {
			// we're within one of the selected dirs, example:
			//   nodepath: "/test/foo"
			//   dir:      "/test"
			if fs.HasPathPrefix(dir, nodepath) {
				return true
			}
		}
		return false
	}

	approachingMatchingTree := func(nodepath string) bool {
		if len(dirs) == 0 {
			return true
		}

		for _, dir := range dirs {
			// the current node path is a prefix for one of the
			// directories, so we're interested in something deeper in the
			// tree. Example:
			//   nodepath: "/test"
			//   dir:      "/test/foo"
			if fs.HasPathPrefix(nodepath, dir) {
				return true
			}
		}
		return false
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if err = repo.LoadIndex(gopts.ctx); err != nil {
		return err
	}

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

	var (
		printSnapshot func(sn *restic.Snapshot)
		printNode     func(path string, node *restic.Node)
	)

	if gopts.JSON {
		enc := json.NewEncoder(gopts.stdout)

		printSnapshot = func(sn *restic.Snapshot) {
			enc.Encode(lsSnapshot{
				Snapshot:   sn,
				ID:         sn.ID(),
				ShortID:    sn.ID().Str(),
				StructType: "snapshot",
			})
		}

		printNode = func(path string, node *restic.Node) {
			enc.Encode(lsNode{
				Name:       node.Name,
				Type:       node.Type,
				Path:       path,
				UID:        node.UID,
				GID:        node.GID,
				Size:       node.Size,
				Mode:       node.Mode,
				ModTime:    node.ModTime,
				AccessTime: node.AccessTime,
				ChangeTime: node.ChangeTime,
				StructType: "node",
			})
		}
	} else {
		printSnapshot = func(sn *restic.Snapshot) {
			Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)
		}
		printNode = func(path string, node *restic.Node) {
			Printf("%s\n", formatNode(path, node, lsOptions.ListLong))
		}
	}

	for sn := range FindFilteredSnapshots(ctx, repo, opts.Hosts, opts.Tags, opts.Paths, args[:1]) {
		printSnapshot(sn)

		err := walker.Walk(ctx, repo, *sn.Tree, nil, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
			if err != nil {
				return false, err
			}
			if node == nil {
				return false, nil
			}

			if withinDir(nodepath) {
				// if we're within a dir, print the node
				printNode(nodepath, node)

				// if recursive listing is requested, signal the walker that it
				// should continue walking recursively
				if opts.Recursive {
					return false, nil
				}
			}

			// if there's an upcoming match deeper in the tree (but we're not
			// there yet), signal the walker to descend into any subdirs
			if approachingMatchingTree(nodepath) {
				return false, nil
			}

			// otherwise, signal the walker to not walk recursively into any
			// subdirs
			if node.Type == "dir" {
				return false, walker.SkipNode
			}
			return false, nil
		})

		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>ls: Explicitly enforce that the user specifies a snapshot ID<commit_after>package main

import (
	"context"
	"encoding/json"
	"os"
	"strings"
	"time"

	"github.com/spf13/cobra"

	"github.com/restic/restic/internal/errors"
	"github.com/restic/restic/internal/fs"
	"github.com/restic/restic/internal/restic"
	"github.com/restic/restic/internal/walker"
)

var cmdLs = &cobra.Command{
	Use:   "ls [flags] snapshotID [dir...]",
	Short: "List files in a snapshot",
	Long: `
The "ls" command lists files and directories in a snapshot.

The special snapshot ID "latest" can be used to list files and
directories of the latest snapshot in the repository. The
--host flag can be used in conjunction to select the latest
snapshot originating from a certain host only.

File listings can optionally be filtered by directories. Any
positional arguments after the snapshot ID are interpreted as
absolute directory paths, and only files inside those directories
will be listed. If the --recursive flag is used, then the filter
will allow traversing into matching directories' subfolders.
Any directory paths specified must be absolute (starting with
a path separator); paths use the forward slash '/' as separator.

EXIT STATUS
===========

Exit status is 0 if the command was successful, and non-zero if there was any error.
`,
	DisableAutoGenTag: true,
	RunE: func(cmd *cobra.Command, args []string) error {
		return runLs(lsOptions, globalOptions, args)
	},
}

// LsOptions collects all options for the ls command.
type LsOptions struct {
	ListLong  bool
	Hosts     []string
	Tags      restic.TagLists
	Paths     []string
	Recursive bool
}

var lsOptions LsOptions

func init() {
	cmdRoot.AddCommand(cmdLs)

	flags := cmdLs.Flags()
	flags.BoolVarP(&lsOptions.ListLong, "long", "l", false, "use a long listing format showing size and mode")
	flags.StringArrayVarP(&lsOptions.Hosts, "host", "H", nil, "only consider snapshots for this `host`, when no snapshot ID is given (can be specified multiple times)")
	flags.Var(&lsOptions.Tags, "tag", "only consider snapshots which include this `taglist`, when no snapshot ID is given")
	flags.StringArrayVar(&lsOptions.Paths, "path", nil, "only consider snapshots which include this (absolute) `path`, when no snapshot ID is given")
	flags.BoolVar(&lsOptions.Recursive, "recursive", false, "include files in subfolders of the listed directories")
}

type lsSnapshot struct {
	*restic.Snapshot
	ID         *restic.ID `json:"id"`
	ShortID    string     `json:"short_id"`
	StructType string     `json:"struct_type"` // "snapshot"
}

type lsNode struct {
	Name       string      `json:"name"`
	Type       string      `json:"type"`
	Path       string      `json:"path"`
	UID        uint32      `json:"uid"`
	GID        uint32      `json:"gid"`
	Size       uint64      `json:"size,omitempty"`
	Mode       os.FileMode `json:"mode,omitempty"`
	ModTime    time.Time   `json:"mtime,omitempty"`
	AccessTime time.Time   `json:"atime,omitempty"`
	ChangeTime time.Time   `json:"ctime,omitempty"`
	StructType string      `json:"struct_type"` // "node"
}

func runLs(opts LsOptions, gopts GlobalOptions, args []string) error {
	if len(args) == 0 {
		return errors.Fatal("no snapshot ID specified")
	}

	// extract any specific directories to walk
	var dirs []string
	if len(args) > 1 {
		dirs = args[1:]
		for _, dir := range dirs {
			if !strings.HasPrefix(dir, "/") {
				return errors.Fatal("All path filters must be absolute, starting with a forward slash '/'")
			}
		}
	}

	withinDir := func(nodepath string) bool {
		if len(dirs) == 0 {
			return true
		}

		for _, dir := range dirs {
			// we're within one of the selected dirs, example:
			//   nodepath: "/test/foo"
			//   dir:      "/test"
			if fs.HasPathPrefix(dir, nodepath) {
				return true
			}
		}
		return false
	}

	approachingMatchingTree := func(nodepath string) bool {
		if len(dirs) == 0 {
			return true
		}

		for _, dir := range dirs {
			// the current node path is a prefix for one of the
			// directories, so we're interested in something deeper in the
			// tree. Example:
			//   nodepath: "/test"
			//   dir:      "/test/foo"
			if fs.HasPathPrefix(nodepath, dir) {
				return true
			}
		}
		return false
	}

	repo, err := OpenRepository(gopts)
	if err != nil {
		return err
	}

	if err = repo.LoadIndex(gopts.ctx); err != nil {
		return err
	}

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

	var (
		printSnapshot func(sn *restic.Snapshot)
		printNode     func(path string, node *restic.Node)
	)

	if gopts.JSON {
		enc := json.NewEncoder(gopts.stdout)

		printSnapshot = func(sn *restic.Snapshot) {
			enc.Encode(lsSnapshot{
				Snapshot:   sn,
				ID:         sn.ID(),
				ShortID:    sn.ID().Str(),
				StructType: "snapshot",
			})
		}

		printNode = func(path string, node *restic.Node) {
			enc.Encode(lsNode{
				Name:       node.Name,
				Type:       node.Type,
				Path:       path,
				UID:        node.UID,
				GID:        node.GID,
				Size:       node.Size,
				Mode:       node.Mode,
				ModTime:    node.ModTime,
				AccessTime: node.AccessTime,
				ChangeTime: node.ChangeTime,
				StructType: "node",
			})
		}
	} else {
		printSnapshot = func(sn *restic.Snapshot) {
			Verbosef("snapshot %s of %v filtered by %v at %s):\n", sn.ID().Str(), sn.Paths, dirs, sn.Time)
		}
		printNode = func(path string, node *restic.Node) {
			Printf("%s\n", formatNode(path, node, lsOptions.ListLong))
		}
	}

	for sn := range FindFilteredSnapshots(ctx, repo, opts.Hosts, opts.Tags, opts.Paths, args[:1]) {
		printSnapshot(sn)

		err := walker.Walk(ctx, repo, *sn.Tree, nil, func(_ restic.ID, nodepath string, node *restic.Node, err error) (bool, error) {
			if err != nil {
				return false, err
			}
			if node == nil {
				return false, nil
			}

			if withinDir(nodepath) {
				// if we're within a dir, print the node
				printNode(nodepath, node)

				// if recursive listing is requested, signal the walker that it
				// should continue walking recursively
				if opts.Recursive {
					return false, nil
				}
			}

			// if there's an upcoming match deeper in the tree (but we're not
			// there yet), signal the walker to descend into any subdirs
			if approachingMatchingTree(nodepath) {
				return false, nil
			}

			// otherwise, signal the walker to not walk recursively into any
			// subdirs
			if node.Type == "dir" {
				return false, walker.SkipNode
			}
			return false, nil
		})

		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/gophergala/matching-snuggies/slicerjob"
)

func main() {
	server := flag.String("server", "localhost:8888", "snuggied server address")
	slicerBackend := flag.String("backend", "slic3r", "backend slicer")
	slicerPreset := flag.String("preset", "hiQ", "specify a configuration preset for the backend")
	gcodeDest := flag.String("o", "", "specify an output gcode filename")
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatalf("missing argument: mesh file")
	}
	meshpath := flag.Arg(0)

	client := &Client{
		ServerAddr: *server,
	}

	// start intercepting signals from the operating system
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	// send files to the slicer to be printed and poll the slicer until the job
	// has completed.
	log.Printf("sending file(s) to snuggied server at %v", *server)
	job, err := client.SliceFile(*slicerBackend, *slicerPreset, meshpath)
	if err != nil {
		log.Fatalf("sending files: %v", err)
	}
	tick := time.Tick(time.Second)
	for job.Status != slicerjob.Complete {
		// TODO: retry with exponential backoff on network failure
		select {
		case s := <-sig:
			// stop intercepting signals. if the job cancellation is taking too
			// long let the future signals terminate the process naturally.
			signal.Stop(sig)
			log.Printf("signal: %v", s)
			err := client.Cancel(job)
			if err != nil {
				log.Printf("failed to cancel job: %v", err)
			}
			log.Printf("slicing job canceled")
			return
		case <-tick:
			job, err = client.SlicerStatus(job)
			if err != nil {
				log.Fatalf("waiting: %v", err)
			}
		}
	}
	// stop intercepting signals because it because much more difficult to stop
	// gracefully while reading gcode from the server.
	signal.Stop(sig)

	// download gcode from the slicer and write to the specified file.
	log.Printf("retreiving gcode file")
	r, err := client.GCode(job)
	if err != nil {
		log.Fatalf("gcode: %v", err)
	}
	defer r.Close()
	var f *os.File
	if *gcodeDest == "" {
		f = os.Stdout
	} else {
		f, err = os.Create(*gcodeDest)
		if err != nil {
			log.Panic(err)
		}
	}
	defer func() {
		err := f.Close()
		if err != nil {
			log.Panic(err)
		}
	}()
	_, err = io.Copy(f, r)
	if err != nil {
		log.Panic(err)
	}
}

type Client struct {
	Client     *http.Client
	ServerAddr string
	HTTPS      bool
}

// SliceFiles tells the server to slice the specified paths.
func (c *Client) SliceFile(backend, preset string, path string) (*slicerjob.Job, error) {
	// check that a mesh file is given as the first argument and open it
	// so it may to encode in the form.
	if !IsMeshFile(path) {
		log.Fatalf("path is not a mesh file: %v", path)
	}
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	// write the multipart form out to a temporary file.  the temporary
	// file is closed and unlinked when the function terminates.
	tmp, err := ioutil.TempFile("", "matching-snuggies-post-")
	if err != nil {
		return nil, fmt.Errorf("tempfile: %v", err)
	}
	defer os.Remove(tmp.Name())
	defer tmp.Close()
	bodyw := multipart.NewWriter(tmp)
	err = c.writeJobForm(bodyw, backend, preset, path, f)
	if err != nil {
		return nil, fmt.Errorf("tempfile: %v", err)
	}

	// seek back to the beginning of the form and POST it to the slicer
	// server.  decode a slicerjob.Job from successful responses.
	var job *slicerjob.Job
	_, err = tmp.Seek(0, 0)
	if err != nil {
		return nil, fmt.Errorf("tempfile: %v", err)
	}
	url := c.url("/slicer/jobs")
	log.Printf("POST %v", url)
	resp, err := c.client().Post(url, bodyw.FormDataContentType(), tmp)
	if err != nil {
		return nil, fmt.Errorf("POST /slicer/jobs: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil, httpStatusError(resp)
	}
	err = json.NewDecoder(resp.Body).Decode(&job)
	if err != nil {
		return nil, fmt.Errorf("response: %v", err)
	}
	return job, nil
}

func (c *Client) writeJobForm(w *multipart.Writer, backend, preset, filename string, r io.Reader) error {
	err := w.WriteField("slicer", backend)
	if err != nil {
		return err
	}
	err = w.WriteField("preset", preset)
	if err != nil {
		return err
	}
	file, err := w.CreateFormFile("meshfile", filepath.Base(filename))
	if err != nil {
		return err
	}
	_, err = io.Copy(file, r)
	if err != nil {
		return err
	}
	return w.Close()
}

func (c *Client) Cancel(job *slicerjob.Job) error {
	if job.ID == "" {
		return fmt.Errorf("job missing id")
	}
	url := c.url("/slicer/jobs/" + job.ID)
	log.Printf("DELETE %v", url)
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return fmt.Errorf("request: %v", err)
	}
	resp, err := c.client().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return httpStatusError(resp)
	}
	return nil
}

// SlicerStatus returns a current copy of the provided job.
func (c *Client) SlicerStatus(job *slicerjob.Job) (*slicerjob.Job, error) {
	if job.ID == "" {
		return nil, fmt.Errorf("job missing id")
	}
	var jobcurr *slicerjob.Job
	url := c.url("/slicer/jobs/" + job.ID)
	log.Printf("GET %v", url)
	resp, err := c.client().Get(url)
	if err != nil {
		return nil, fmt.Errorf("POST /slicer/jobs/: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil, httpStatusError(resp)
	}
	err = json.NewDecoder(resp.Body).Decode(&jobcurr)
	if err != nil {
		return nil, fmt.Errorf("response: %v", err)
	}
	return jobcurr, nil
}

// GCode requests the gcode for job.
func (c *Client) GCode(job *slicerjob.Job) (io.ReadCloser, error) {
	url := c.url("/slicer/gcodes/" + job.ID)
	log.Printf("POST %v", url)
	resp, err := c.client().Get(url)
	if err != nil {
		return nil, fmt.Errorf("POST /slicer/jobs/: %v", err)
	}
	if resp.StatusCode != 200 {
		return nil, httpStatusError(resp)
	}
	return resp.Body, nil
}

func (c *Client) client() *http.Client {
	if c.Client == nil {
		return http.DefaultClient
	}
	return c.Client
}

func (c *Client) url(pathquery string) string {
	pathquery = strings.TrimPrefix(pathquery, "/")
	scheme := "http"
	if c.HTTPS {
		scheme = "https"
	}
	return scheme + "://" + c.ServerAddr + "/" + pathquery
}

var meshExts = map[string]bool{
	".stl": true,
	".amf": true,
}

func IsMeshFile(path string) bool {
	return meshExts[filepath.Ext(path)]
}

func httpStatusError(resp *http.Response) error {
	p, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 85))
	msg := trimMessage(string(p), 80)
	return fmt.Errorf("http %d %s: %q", resp.StatusCode, http.StatusText(resp.StatusCode), msg)
}

func trimMessage(s string, n int) string {
	s = strings.TrimSpace(s)
	if len(s) < n {
		return s
	}
	var rs []rune
	var m int
	for _, c := range s {
		if m >= n {
			break
		}
		rs = append(rs, c)
		m++
	}
	return string(rs) + "..."
}
<commit_msg>fix logging of http methods being requested by the client program<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/gophergala/matching-snuggies/slicerjob"
)

func main() {
	server := flag.String("server", "localhost:8888", "snuggied server address")
	slicerBackend := flag.String("backend", "slic3r", "backend slicer")
	slicerPreset := flag.String("preset", "hiQ", "specify a configuration preset for the backend")
	gcodeDest := flag.String("o", "", "specify an output gcode filename")
	flag.Parse()

	if flag.NArg() < 1 {
		log.Fatalf("missing argument: mesh file")
	}
	meshpath := flag.Arg(0)

	client := &Client{
		ServerAddr: *server,
	}

	// start intercepting signals from the operating system
	sig := make(chan os.Signal, 1)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)

	// send files to the slicer to be printed and poll the slicer until the job
	// has completed.
	log.Printf("sending file(s) to snuggied server at %v", *server)
	job, err := client.SliceFile(*slicerBackend, *slicerPreset, meshpath)
	if err != nil {
		log.Fatalf("sending files: %v", err)
	}
	tick := time.Tick(time.Second)
	for job.Status != slicerjob.Complete {
		// TODO: retry with exponential backoff on network failure
		select {
		case s := <-sig:
			// stop intercepting signals. if the job cancellation is taking too
			// long let the future signals terminate the process naturally.
			signal.Stop(sig)
			log.Printf("signal: %v", s)
			err := client.Cancel(job)
			if err != nil {
				log.Printf("failed to cancel job: %v", err)
			}
			log.Printf("slicing job canceled")
			return
		case <-tick:
			job, err = client.SlicerStatus(job)
			if err != nil {
				log.Fatalf("waiting: %v", err)
			}
		}
	}
	// stop intercepting signals because it because much more difficult to stop
	// gracefully while reading gcode from the server.
	signal.Stop(sig)

	// download gcode from the slicer and write to the specified file.
	log.Printf("retreiving gcode file")
	r, err := client.GCode(job)
	if err != nil {
		log.Fatalf("gcode: %v", err)
	}
	defer r.Close()
	var f *os.File
	if *gcodeDest == "" {
		f = os.Stdout
	} else {
		f, err = os.Create(*gcodeDest)
		if err != nil {
			log.Panic(err)
		}
	}
	defer func() {
		err := f.Close()
		if err != nil {
			log.Panic(err)
		}
	}()
	_, err = io.Copy(f, r)
	if err != nil {
		log.Panic(err)
	}
}

type Client struct {
	Client     *http.Client
	ServerAddr string
	HTTPS      bool
}

// SliceFiles tells the server to slice the specified paths.
func (c *Client) SliceFile(backend, preset string, path string) (*slicerjob.Job, error) {
	// check that a mesh file is given as the first argument and open it
	// so it may to encode in the form.
	if !IsMeshFile(path) {
		log.Fatalf("path is not a mesh file: %v", path)
	}
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()

	// write the multipart form out to a temporary file.  the temporary
	// file is closed and unlinked when the function terminates.
	tmp, err := ioutil.TempFile("", "matching-snuggies-post-")
	if err != nil {
		return nil, fmt.Errorf("tempfile: %v", err)
	}
	defer os.Remove(tmp.Name())
	defer tmp.Close()
	bodyw := multipart.NewWriter(tmp)
	err = c.writeJobForm(bodyw, backend, preset, path, f)
	if err != nil {
		return nil, fmt.Errorf("tempfile: %v", err)
	}

	// seek back to the beginning of the form and POST it to the slicer
	// server.  decode a slicerjob.Job from successful responses.
	var job *slicerjob.Job
	_, err = tmp.Seek(0, 0)
	if err != nil {
		return nil, fmt.Errorf("tempfile: %v", err)
	}
	url := c.url("/slicer/jobs")
	log.Printf("POST %v", url)
	resp, err := c.client().Post(url, bodyw.FormDataContentType(), tmp)
	if err != nil {
		return nil, fmt.Errorf("POST /slicer/jobs: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil, httpStatusError(resp)
	}
	err = json.NewDecoder(resp.Body).Decode(&job)
	if err != nil {
		return nil, fmt.Errorf("response: %v", err)
	}
	return job, nil
}

func (c *Client) writeJobForm(w *multipart.Writer, backend, preset, filename string, r io.Reader) error {
	err := w.WriteField("slicer", backend)
	if err != nil {
		return err
	}
	err = w.WriteField("preset", preset)
	if err != nil {
		return err
	}
	file, err := w.CreateFormFile("meshfile", filepath.Base(filename))
	if err != nil {
		return err
	}
	_, err = io.Copy(file, r)
	if err != nil {
		return err
	}
	return w.Close()
}

func (c *Client) Cancel(job *slicerjob.Job) error {
	if job.ID == "" {
		return fmt.Errorf("job missing id")
	}
	url := c.url("/slicer/jobs/" + job.ID)
	log.Printf("DELETE %v", url)
	req, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return fmt.Errorf("request: %v", err)
	}
	resp, err := c.client().Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return httpStatusError(resp)
	}
	return nil
}

// SlicerStatus returns a current copy of the provided job.
func (c *Client) SlicerStatus(job *slicerjob.Job) (*slicerjob.Job, error) {
	if job.ID == "" {
		return nil, fmt.Errorf("job missing id")
	}
	var jobcurr *slicerjob.Job
	url := c.url("/slicer/jobs/" + job.ID)
	log.Printf("GET %v", url)
	resp, err := c.client().Get(url)
	if err != nil {
		return nil, fmt.Errorf("GET /slicer/jobs/: %v", err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		return nil, httpStatusError(resp)
	}
	err = json.NewDecoder(resp.Body).Decode(&jobcurr)
	if err != nil {
		return nil, fmt.Errorf("response: %v", err)
	}
	return jobcurr, nil
}

// GCode requests the gcode for job.
func (c *Client) GCode(job *slicerjob.Job) (io.ReadCloser, error) {
	url := c.url("/slicer/gcodes/" + job.ID)
	log.Printf("GET %v", url)
	resp, err := c.client().Get(url)
	if err != nil {
		return nil, fmt.Errorf("GET /slicer/codes/: %v", err)
	}
	if resp.StatusCode != 200 {
		return nil, httpStatusError(resp)
	}
	return resp.Body, nil
}

func (c *Client) client() *http.Client {
	if c.Client == nil {
		return http.DefaultClient
	}
	return c.Client
}

func (c *Client) url(pathquery string) string {
	pathquery = strings.TrimPrefix(pathquery, "/")
	scheme := "http"
	if c.HTTPS {
		scheme = "https"
	}
	return scheme + "://" + c.ServerAddr + "/" + pathquery
}

var meshExts = map[string]bool{
	".stl": true,
	".amf": true,
}

func IsMeshFile(path string) bool {
	return meshExts[filepath.Ext(path)]
}

func httpStatusError(resp *http.Response) error {
	p, _ := ioutil.ReadAll(io.LimitReader(resp.Body, 85))
	msg := trimMessage(string(p), 80)
	return fmt.Errorf("http %d %s: %q", resp.StatusCode, http.StatusText(resp.StatusCode), msg)
}

func trimMessage(s string, n int) string {
	s = strings.TrimSpace(s)
	if len(s) < n {
		return s
	}
	var rs []rune
	var m int
	for _, c := range s {
		if m >= n {
			break
		}
		rs = append(rs, c)
		m++
	}
	return string(rs) + "..."
}
<|endoftext|>


<commit_before>package status

import (
	"fmt"
	"log"
	"os"
	"sort"

	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/mansion"
	itchio "github.com/itchio/go-itchio"
	"github.com/itchio/httpkit/progress"
	"github.com/itchio/wharf/state"
	"github.com/olekukonko/tablewriter"
	"github.com/pkg/errors"
)

var args = struct {
	target       *string
	showAllFiles *bool
}{}

func Register(ctx *mansion.Context) {
	cmd := ctx.App.Command("status", "Show a list of channels and the status of their latest and pending builds.")
	ctx.Register(cmd, do)

	args.target = cmd.Arg("target", "Which user/project to show the status of, for example 'leafo/x-moon'").Required().String()
	args.showAllFiles = cmd.Flag("show-all-files", "Show status of all files, not just archive").Bool()
}

func do(ctx *mansion.Context) {
	go ctx.DoVersionCheck()
	ctx.Must(Do(ctx, *args.target, *args.showAllFiles))
}

func Do(ctx *mansion.Context, specStr string, showAllFiles bool) error {
	spec, err := itchio.ParseSpec(specStr)
	if err != nil {
		return errors.Wrapf(err, "parsing spec %s", spec)
	}

	client, err := ctx.AuthenticateViaOauth()
	if err != nil {
		return errors.Wrap(err, "authenticating")
	}

	listChannelsResp, err := client.ListChannels(spec.Target)
	if err != nil {
		return errors.Wrap(err, "listing channels")
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Channel", "Upload", "Build", "Version", "State"})

	found := false

	sortedChannelNames := []string{}
	for name := range listChannelsResp.Channels {
		sortedChannelNames = append(sortedChannelNames, name)
	}
	sort.Strings(sortedChannelNames)

	for _, channelName := range sortedChannelNames {
		ch := listChannelsResp.Channels[channelName]
		if spec.Channel != "" && ch.Name != spec.Channel {
			continue
		}
		found = true

		if ch.Head != nil {
			files := ch.Head.Files
			line := []string{ch.Name, fmt.Sprintf("#%d", ch.Upload.ID), buildState(ch.Head), versionState(ch.Head), filesState(files, showAllFiles)}
			table.Append(line)
		} else {
			line := []string{ch.Name, fmt.Sprintf("#%d", ch.Upload.ID), "No builds yet"}
			table.Append(line)
		}

		if ch.Pending != nil {
			files := ch.Pending.Files
			line := []string{"", "", buildState(ch.Pending), versionState(ch.Pending), filesState(files, showAllFiles)}
			table.Append(line)
		}
	}

	if found {
		table.Render()
	} else {
		comm.Logf("No channel %s found for %s", spec.Channel, spec.Target)
	}

	return nil
}

func buildState(build *itchio.Build) string {
	theme := state.GetTheme()
	var s string

	switch build.State {
	case itchio.BuildStateCompleted:
		s = fmt.Sprintf("%s #%d", theme.StatSign, build.ID)
	case itchio.BuildStateProcessing:
		s = fmt.Sprintf("%s #%d", theme.OpSign, build.ID)
	default:
		s = fmt.Sprintf("  #%d (%s)", build.ID, build.State)
	}

	if build.ParentBuildID != -1 {
		s += fmt.Sprintf(" (from #%d)", build.ParentBuildID)
	}

	return s
}

func versionState(build *itchio.Build) string {
	log.Printf("versionState %#v", build)

	switch build.State {
	case itchio.BuildStateCompleted:
		if build.UserVersion != "" {
			return build.UserVersion
		}

		return fmt.Sprintf("%d", build.Version)
	default:
		return ""
	}
}

func filesState(files []*itchio.BuildFile, showAllFiles bool) string {
	if len(files) == 0 {
		return "(no files)"
	}

	s := ""
	for _, file := range files {
		if !(showAllFiles || file.Type == itchio.BuildFileTypeArchive) {
			continue
		}

		if s != "" {
			s += ", "
		}
		s += fileState(file)
	}

	return s
}

func fileState(file *itchio.BuildFile) string {
	theme := state.GetTheme()

	fType := string(file.Type)
	if file.SubType != itchio.BuildFileSubTypeDefault {
		fType += fmt.Sprintf(" (%s)", file.SubType)
	}

	sign := theme.StatSign
	if file.State != itchio.BuildFileStateUploaded {
		sign = theme.OpSign
	}

	fSize := progress.FormatBytes(file.Size)

	return fmt.Sprintf("%s %s %s", sign, fSize, fType)
}
<commit_msg>Remove debug output in status command<commit_after>package status

import (
	"fmt"
	"os"
	"sort"

	"github.com/itchio/butler/comm"
	"github.com/itchio/butler/mansion"
	itchio "github.com/itchio/go-itchio"
	"github.com/itchio/httpkit/progress"
	"github.com/itchio/wharf/state"
	"github.com/olekukonko/tablewriter"
	"github.com/pkg/errors"
)

var args = struct {
	target       *string
	showAllFiles *bool
}{}

func Register(ctx *mansion.Context) {
	cmd := ctx.App.Command("status", "Show a list of channels and the status of their latest and pending builds.")
	ctx.Register(cmd, do)

	args.target = cmd.Arg("target", "Which user/project to show the status of, for example 'leafo/x-moon'").Required().String()
	args.showAllFiles = cmd.Flag("show-all-files", "Show status of all files, not just archive").Bool()
}

func do(ctx *mansion.Context) {
	go ctx.DoVersionCheck()
	ctx.Must(Do(ctx, *args.target, *args.showAllFiles))
}

func Do(ctx *mansion.Context, specStr string, showAllFiles bool) error {
	spec, err := itchio.ParseSpec(specStr)
	if err != nil {
		return errors.Wrapf(err, "parsing spec %s", spec)
	}

	client, err := ctx.AuthenticateViaOauth()
	if err != nil {
		return errors.Wrap(err, "authenticating")
	}

	listChannelsResp, err := client.ListChannels(spec.Target)
	if err != nil {
		return errors.Wrap(err, "listing channels")
	}

	table := tablewriter.NewWriter(os.Stdout)
	table.SetHeader([]string{"Channel", "Upload", "Build", "Version", "State"})

	found := false

	sortedChannelNames := []string{}
	for name := range listChannelsResp.Channels {
		sortedChannelNames = append(sortedChannelNames, name)
	}
	sort.Strings(sortedChannelNames)

	for _, channelName := range sortedChannelNames {
		ch := listChannelsResp.Channels[channelName]
		if spec.Channel != "" && ch.Name != spec.Channel {
			continue
		}
		found = true

		if ch.Head != nil {
			files := ch.Head.Files
			line := []string{ch.Name, fmt.Sprintf("#%d", ch.Upload.ID), buildState(ch.Head), versionState(ch.Head), filesState(files, showAllFiles)}
			table.Append(line)
		} else {
			line := []string{ch.Name, fmt.Sprintf("#%d", ch.Upload.ID), "No builds yet"}
			table.Append(line)
		}

		if ch.Pending != nil {
			files := ch.Pending.Files
			line := []string{"", "", buildState(ch.Pending), versionState(ch.Pending), filesState(files, showAllFiles)}
			table.Append(line)
		}
	}

	if found {
		table.Render()
	} else {
		comm.Logf("No channel %s found for %s", spec.Channel, spec.Target)
	}

	return nil
}

func buildState(build *itchio.Build) string {
	theme := state.GetTheme()
	var s string

	switch build.State {
	case itchio.BuildStateCompleted:
		s = fmt.Sprintf("%s #%d", theme.StatSign, build.ID)
	case itchio.BuildStateProcessing:
		s = fmt.Sprintf("%s #%d", theme.OpSign, build.ID)
	default:
		s = fmt.Sprintf("  #%d (%s)", build.ID, build.State)
	}

	if build.ParentBuildID != -1 {
		s += fmt.Sprintf(" (from #%d)", build.ParentBuildID)
	}

	return s
}

func versionState(build *itchio.Build) string {
	switch build.State {
	case itchio.BuildStateCompleted:
		if build.UserVersion != "" {
			return build.UserVersion
		}

		return fmt.Sprintf("%d", build.Version)
	default:
		return ""
	}
}

func filesState(files []*itchio.BuildFile, showAllFiles bool) string {
	if len(files) == 0 {
		return "(no files)"
	}

	s := ""
	for _, file := range files {
		if !(showAllFiles || file.Type == itchio.BuildFileTypeArchive) {
			continue
		}

		if s != "" {
			s += ", "
		}
		s += fileState(file)
	}

	return s
}

func fileState(file *itchio.BuildFile) string {
	theme := state.GetTheme()

	fType := string(file.Type)
	if file.SubType != itchio.BuildFileSubTypeDefault {
		fType += fmt.Sprintf(" (%s)", file.SubType)
	}

	sign := theme.StatSign
	if file.State != itchio.BuildFileStateUploaded {
		sign = theme.OpSign
	}

	fSize := progress.FormatBytes(file.Size)

	return fmt.Sprintf("%s %s %s", sign, fSize, fType)
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"encoding/json"
	"errors"
	"flag"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/aws"
	aws_lambda "github.com/aws/aws-sdk-go/service/lambda"
	"github.com/whosonfirst/go-whosonfirst-aws/s3"
	"github.com/whosonfirst/go-whosonfirst-aws/session"
	"github.com/whosonfirst/go-whosonfirst-uri"
	"log"
	"os"
	"strconv"
)

type DeleteOptions struct {
	DSN    string `json:"dsn"`
	Dryrun bool   `json:"dryrun"`
	ID     int64  `json:"id"`
}

func delete(ctx context.Context, opts DeleteOptions) error {

	if opts.DSN == "" {

		dsn, ok := os.LookupEnv("DSN")

		if !ok {
			return errors.New("Missing DSN")
		}

		opts.DSN = dsn
	}

	if opts.ID == 0 {
		return errors.New("Invalid ID")
	}

	cfg, err := s3.NewS3ConfigFromString(opts.DSN)

	if err != nil {
		return err
	}

	conn, err := s3.NewS3Connection(cfg)

	if err != nil {
		return err
	}

	// add hooks for alternative paths (fullname, etc.)

	path, err := uri.Id2Path(opts.ID)

	if err != nil {
		return err
	}

	if opts.Dryrun {
		log.Println("DELETE", path)
		return nil
	}

	return conn.DeleteRecursive(path)
}

func main() {

	do_invoke := flag.Bool("invoke", false, "...")
	dryrun := flag.Bool("dryrun", false, "...")

	s3_dsn := flag.String("s3-dsn", "", "...")

	lambda_dsn := flag.String("lambda-dsn", "", "...")
	lambda_func := flag.String("lambda-func", "", "...")
	lambda_type := flag.String("lambda-type", "RequestResponse", "A valid go-aws-sdk lambda.InvocationType string")

	flag.Parse()

	opts := DeleteOptions{
		DSN:    *s3_dsn,
		Dryrun: *dryrun,
	}

	_, do_lambda := os.LookupEnv("LAMBDA")

	if do_lambda {
		lambda.Start(delete)
		os.Exit(0)
	}

	if *do_invoke {

		sess, err := session.NewSessionWithDSN(*lambda_dsn)

		if err != nil {
			log.Fatal(err)
		}

		svc := aws_lambda.New(sess)

		for _, str_id := range flag.Args() {

			id, err := strconv.ParseInt(str_id, 10, 64)

			if err != nil {
				log.Fatal(err)
			}

			opts.ID = id

			payload, err := json.Marshal(opts)

			if err != nil {
				log.Fatal(err)
			}

			input := &aws_lambda.InvokeInput{
				FunctionName:   aws.String(*lambda_func),
				InvocationType: aws.String(*lambda_type),
				Payload:        payload,
			}

			if *input.InvocationType == "RequestResponse" {
				input.LogType = aws.String("Tail")
			}

			rsp, err := svc.Invoke(input)

			if err != nil {
				log.Fatal(err)
			}

			log.Println(rsp)
		}

	} else {

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

		for _, str_id := range flag.Args() {

			id, err := strconv.ParseInt(str_id, 10, 64)

			if err != nil {
				log.Fatal(err)
			}

			opts.ID = id
			err = delete(ctx, opts)

			if err != nil {
				log.Fatal(err)
			}
		}

		os.Exit(0)
	}
}
<commit_msg>wrangle all the invoke (lambda) code<commit_after>package main

/*

Given an ID (1159324849) this will recursively delete everything
in PREFIX/115/932/484/9 - that is all (20181226/thisisaaronland)

*/

import (
	"bufio"
	"context"
	"encoding/base64"
	"encoding/json"
	"errors"
	"flag"
	"github.com/aws/aws-lambda-go/lambda"
	"github.com/aws/aws-sdk-go/aws"
	aws_lambda "github.com/aws/aws-sdk-go/service/lambda"
	"github.com/whosonfirst/go-whosonfirst-aws/s3"
	"github.com/whosonfirst/go-whosonfirst-aws/session"
	"github.com/whosonfirst/go-whosonfirst-uri"
	"log"
	"os"
	"strconv"
	"sync"
)

type DeleteOptions struct {
	DSN    string `json:"dsn"`
	Dryrun bool   `json:"dryrun"`
	ID     int64  `json:"id"`
}

func append_id(ids []int64, str_id string) ([]int64, error) {

	id, err := strconv.ParseInt(str_id, 10, 64)

	if err != nil {
		return ids, err
	}

	ids = append(ids, id)
	return ids, nil
}

// this (or something like it) should be put in go-whosonfirst-aws/lambda as a
// general purpose 80/20 wrapper function (20181226/thisisaaronland)

func invoke(svc *aws_lambda.Lambda, lambda_func string, lambda_type string, opts interface{}) error {

	payload, err := json.Marshal(opts)

	if err != nil {
		return err
	}

	input := &aws_lambda.InvokeInput{
		FunctionName:   aws.String(lambda_func),
		InvocationType: aws.String(lambda_type),
		Payload:        payload,
	}

	if *input.InvocationType == "RequestResponse" {
		input.LogType = aws.String("Tail")
	}

	rsp, err := svc.Invoke(input)

	if err != nil {
		return err
	}

	if *input.InvocationType == "RequestResponse" {

		enc_result := *rsp.LogResult

		result, err := base64.StdEncoding.DecodeString(enc_result)

		if err != nil {
			return err
		}

		if *rsp.StatusCode != 200 {
			return errors.New(string(result))
		}
	}

	log.Println("INVOKE", string(payload), *rsp.StatusCode)
	return nil
}

func delete(ctx context.Context, opts DeleteOptions) error {

	if opts.DSN == "" {

		dsn, ok := os.LookupEnv("DSN")

		if !ok {
			return errors.New("Missing DSN")
		}

		opts.DSN = dsn
	}

	if opts.ID == 0 {
		return errors.New("Invalid ID")
	}

	cfg, err := s3.NewS3ConfigFromString(opts.DSN)

	if err != nil {
		return err
	}

	conn, err := s3.NewS3Connection(cfg)

	if err != nil {
		return err
	}

	// add hooks for alternative paths (fullname, etc.)

	path, err := uri.Id2Path(opts.ID)

	if err != nil {
		return err
	}

	if opts.Dryrun {
		log.Println("[dryrun] DELETE", path)
		return nil
	}

	return conn.DeleteRecursive(path)
}

func main() {

	dryrun := flag.Bool("dryrun", false, "...")
	stdin := flag.Bool("stdin", false, "...")

	s3_dsn := flag.String("s3-dsn", "", "...")

	do_invoke := flag.Bool("lambda-invoke", false, "...")
	lambda_dsn := flag.String("lambda-dsn", "", "...")
	lambda_func := flag.String("lambda-func", "", "...")
	lambda_clients := flag.Int("lambda-clients", 10, "...")
	lambda_type := flag.String("lambda-type", "RequestResponse", "A valid go-aws-sdk lambda.InvocationType string")

	do_sqs := flag.Bool("sqs-invoke", false, "...")
	// sqs_dsn := flag.String("sqs-dsn", "", "...")

	flag.Parse()

	opts := DeleteOptions{
		DSN:    *s3_dsn,
		Dryrun: *dryrun,
	}

	_, do_lambda := os.LookupEnv("LAMBDA")

	if do_lambda {
		lambda.Start(delete)
		os.Exit(0)
	}

	ids := make([]int64, 0)
	var err error

	if *stdin {

		scanner := bufio.NewScanner(os.Stdin)

		for scanner.Scan() {

			ids, err = append_id(ids, scanner.Text())

			if err != nil {
				log.Fatal(err)
			}
		}

	} else {

		for _, str_id := range flag.Args() {

			ids, err = append_id(ids, str_id)

			if err != nil {
				log.Fatal(err)
			}
		}
	}

	if *do_sqs {
		log.Fatal("Please write me")
	}

	/*

		for example:

		$> cat /usr/local/data/to-delete.csv | ./bin/wof-s3-delete -lambda-invoke -lambda-dsn 'region=us-west-2 credentials=session' -lambda-func DeleteMedia -dryrun -stdin
		2018/12/26 12:15:23 INVOKE 1159337327 200
		... and so on

	*/

	if *do_invoke {

		sess, err := session.NewSessionWithDSN(*lambda_dsn)

		if err != nil {
			log.Fatal(err)
		}

		svc := aws_lambda.New(sess)
		wg := new(sync.WaitGroup)

		throttle := make(chan bool, *lambda_clients)

		for i := 0; i < *lambda_clients; i++ {
			throttle <- true
		}

		for _, id := range ids {

			wg.Add(1)

			go func(svc *aws_lambda.Lambda, wg *sync.WaitGroup, throttle chan bool, id int64) {

				<-throttle

				defer func() {
					throttle <- true
					wg.Done()
				}()

				opts.ID = id
				err := invoke(svc, *lambda_func, *lambda_type, opts)

				if err != nil {
					log.Println("ERROR", id, err)
				}

			}(svc, wg, throttle, id)
		}

		wg.Wait()
		os.Exit(0)
	}

	// nothing left but the command line

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

	for _, id := range ids {

		opts.ID = id
		err := delete(ctx, opts)

		if err != nil {
			log.Fatal(err)
		}
	}

	os.Exit(0)
}
<|endoftext|>


<commit_before>package jwt

import (
	"errors"

	jwt "github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
)

type contextKey string

const (
	// JWTTokenContextKey holds the key used to store a JWT Token in the
	// context.
	JWTTokenContextKey contextKey = "JWTToken"
	// JWTClaimsContxtKey holds the key used to store the JWT Claims in the
	// context.
	JWTClaimsContextKey contextKey = "JWTClaims"
)

var (
	// ErrTokenContextMissing denotes a token was not passed into the parsing
	// middleware's context.
	ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context")
	// ErrTokenInvalid denotes a token was not able to be validated.
	ErrTokenInvalid = errors.New("JWT Token was invalid")
	// ErrTokenExpired denotes a token's expire header (exp) has since passed.
	ErrTokenExpired = errors.New("JWT Token is expired")
	// ErrTokenMalformed denotes a token was not formatted as a JWT token.
	ErrTokenMalformed = errors.New("JWT Token is malformed")
	// ErrTokenNotActive denotes a token's not before header (nbf) is in the
	// future.
	ErrTokenNotActive = errors.New("token is not valid yet")
	// ErrUncesptedSigningMethod denotes a token was signed with an unexpected
	// signing method.
	ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
)

type Claims map[string]interface{}

// NewSigner creates a new JWT token generating middleware, specifying key ID,
// signing string, signing method and the claims you would like it to contain.
// Tokens are signed with a Key ID header (kid) which is useful for determining
// the key to use for parsing. Particularly useful for clients.
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			token := jwt.NewWithClaims(method, jwt.MapClaims(claims))
			token.Header["kid"] = kid

			// Sign and get the complete encoded token as a string using the secret
			tokenString, err := token.SignedString(key)
			if err != nil {
				return nil, err
			}
			ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString)

			return next(ctx, request)
		}
	}
}

// NewParser creates a new JWT token parsing middleware, specifying a
// jwt.Keyfunc interface and the signing method. NewParser adds the resulting
// claims to endpoint context or returns error on invalid token. Particularly
// useful for servers.
func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			// tokenString is stored in the context from the transport handlers.
			tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
			if !ok {
				return nil, ErrTokenContextMissing
			}

			// Parse takes the token string and a function for looking up the
			// key. The latter is especially useful if you use multiple keys
			// for your application.  The standard is to use 'kid' in the head
			// of the token to identify which key to use, but the parsed token
			// (head and claims) is provided to the callback, providing
			// flexibility.
			token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
				// Don't forget to validate the alg is what you expect:
				if token.Method != method {
					return nil, ErrUnexpectedSigningMethod
				}

				return keyFunc(token)
			})
			if err != nil {
				if e, ok := err.(*jwt.ValidationError); ok && e.Inner != nil {
					if e.Errors&jwt.ValidationErrorMalformed != 0 {
						// Token is malformed
						return nil, ErrTokenMalformed
					} else if e.Errors&jwt.ValidationErrorExpired != 0 {
						// Token is expired
						return nil, ErrTokenExpired
					} else if e.Errors&jwt.ValidationErrorNotValidYet != 0 {
						// Token is not active yet
						return nil, ErrTokenNotActive
					}

					return nil, e.Inner
				}

				return nil, err
			}

			if !token.Valid {
				return nil, ErrTokenInvalid
			}

			if claims, ok := token.Claims.(jwt.MapClaims); ok {
				ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(claims))
			}

			return next(ctx, request)
		}
	}
}
<commit_msg>auth/jwt: fix and add comments<commit_after>package jwt

import (
	"errors"

	jwt "github.com/dgrijalva/jwt-go"
	"golang.org/x/net/context"

	"github.com/go-kit/kit/endpoint"
)

type contextKey string

const (
	// JWTTokenContextKey holds the key used to store a JWT Token in the
	// context.
	JWTTokenContextKey contextKey = "JWTToken"

	// JWTClaimsContextKey holds the key used to store the JWT Claims in the
	// context.
	JWTClaimsContextKey contextKey = "JWTClaims"
)

var (
	// ErrTokenContextMissing denotes a token was not passed into the parsing
	// middleware's context.
	ErrTokenContextMissing = errors.New("token up for parsing was not passed through the context")

	// ErrTokenInvalid denotes a token was not able to be validated.
	ErrTokenInvalid = errors.New("JWT Token was invalid")

	// ErrTokenExpired denotes a token's expire header (exp) has since passed.
	ErrTokenExpired = errors.New("JWT Token is expired")

	// ErrTokenMalformed denotes a token was not formatted as a JWT token.
	ErrTokenMalformed = errors.New("JWT Token is malformed")

	// ErrTokenNotActive denotes a token's not before header (nbf) is in the
	// future.
	ErrTokenNotActive = errors.New("token is not valid yet")

	// ErrUnexpectedSigningMethod denotes a token was signed with an unexpected
	// signing method.
	ErrUnexpectedSigningMethod = errors.New("unexpected signing method")
)

// Claims is a map of arbitrary claim data.
type Claims map[string]interface{}

// NewSigner creates a new JWT token generating middleware, specifying key ID,
// signing string, signing method and the claims you would like it to contain.
// Tokens are signed with a Key ID header (kid) which is useful for determining
// the key to use for parsing. Particularly useful for clients.
func NewSigner(kid string, key []byte, method jwt.SigningMethod, claims Claims) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			token := jwt.NewWithClaims(method, jwt.MapClaims(claims))
			token.Header["kid"] = kid

			// Sign and get the complete encoded token as a string using the secret
			tokenString, err := token.SignedString(key)
			if err != nil {
				return nil, err
			}
			ctx = context.WithValue(ctx, JWTTokenContextKey, tokenString)

			return next(ctx, request)
		}
	}
}

// NewParser creates a new JWT token parsing middleware, specifying a
// jwt.Keyfunc interface and the signing method. NewParser adds the resulting
// claims to endpoint context or returns error on invalid token. Particularly
// useful for servers.
func NewParser(keyFunc jwt.Keyfunc, method jwt.SigningMethod) endpoint.Middleware {
	return func(next endpoint.Endpoint) endpoint.Endpoint {
		return func(ctx context.Context, request interface{}) (response interface{}, err error) {
			// tokenString is stored in the context from the transport handlers.
			tokenString, ok := ctx.Value(JWTTokenContextKey).(string)
			if !ok {
				return nil, ErrTokenContextMissing
			}

			// Parse takes the token string and a function for looking up the
			// key. The latter is especially useful if you use multiple keys
			// for your application.  The standard is to use 'kid' in the head
			// of the token to identify which key to use, but the parsed token
			// (head and claims) is provided to the callback, providing
			// flexibility.
			token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
				// Don't forget to validate the alg is what you expect:
				if token.Method != method {
					return nil, ErrUnexpectedSigningMethod
				}

				return keyFunc(token)
			})
			if err != nil {
				if e, ok := err.(*jwt.ValidationError); ok && e.Inner != nil {
					if e.Errors&jwt.ValidationErrorMalformed != 0 {
						// Token is malformed
						return nil, ErrTokenMalformed
					} else if e.Errors&jwt.ValidationErrorExpired != 0 {
						// Token is expired
						return nil, ErrTokenExpired
					} else if e.Errors&jwt.ValidationErrorNotValidYet != 0 {
						// Token is not active yet
						return nil, ErrTokenNotActive
					}

					return nil, e.Inner
				}

				return nil, err
			}

			if !token.Valid {
				return nil, ErrTokenInvalid
			}

			if claims, ok := token.Claims.(jwt.MapClaims); ok {
				ctx = context.WithValue(ctx, JWTClaimsContextKey, Claims(claims))
			}

			return next(ctx, request)
		}
	}
}
<|endoftext|>