<commit_before>package mudlib

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"runtime"
	"runtime/debug"
)

var httpPort = flag.Int("httpPort", 8080, "Port for HTTP interface")

func init() {
	http.HandleFunc("/gc", gcHandler)
	http.HandleFunc("/mem", memHandler)
	http.HandleFunc("/errors", errorHandler)
	go startServing()
}

func startServing() {
	log.Printf("HTTP listening on port %d", *httpPort)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", *httpPort), nil); err != nil {
		errorLog.Printf("Failed to start HTTP server on port %d", *httpPort)
	}
}

// TODO: Templates
func gcHandler(w http.ResponseWriter, r *http.Request) {
	gcStats := new(debug.GCStats)
	debug.ReadGCStats(gcStats)
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintln(w, "<title>GC</title>")
	fmt.Fprintln(w, "")
	fmt.Fprintln(w, "
GC
")
	fmt.Fprintln(w, "
")
	fmt.Fprintf(w, "
Last GC	%v
\n", gcStats.LastGC)
	fmt.Fprintf(w, "
Num GC	%v
\n", gcStats.NumGC)
	fmt.Fprintf(w, "
Pause Total	%v
\n", gcStats.PauseTotal)
	fmt.Fprintf(w, "
Pause	%v
\n", gcStats.Pause)
	fmt.Fprintf(w, "
Pause Quantiles	%v
\n", gcStats.PauseQuantiles)
	fmt.Fprintln(w, "
")
	fmt.Fprintln(w, "")
	fmt.Fprintln(w, "")
}

func memHandler(w http.ResponseWriter, r *http.Request) {
	memStats := new(runtime.MemStats)
	runtime.ReadMemStats(memStats)
	w.Header().Set("Content-Type", "text/html")
	fmt.Fprintln(w, "<title>Mem</title>")
	fmt.Fprintln(w, "")
	fmt.Fprintln(w, "
Mem
")
	fmt.Fprintln(w, "
General
")
	fmt.Fprintln(w, "
")
	fmt.Fprintf(w, "
Alloc	%v
\n", memStats.Alloc)
	fmt.Fprintf(w, "
Total alloc	%v
\n", memStats.TotalAlloc)
	fmt.Fprintf(w, "
Sys	%v
\n", memStats.Sys)
	fmt.Fprintf(w, "
Lookups	%v
\n", memStats.Lookups)
	fmt.Fprintf(w, "
Mallocs	%v
\n", memStats.Mallocs)
	fmt.Fprintf(w, "
Frees	%v
\n", memStats.Frees)
	fmt.Fprintln(w, "
")
	fmt.Fprintln(w, "
Heap
")
	fmt.Fprintln(w, "
")
	fmt.Fprintf(w, "
Alloc	%v
\n", memStats.HeapAlloc)
	fmt.Fprintf(w, "
Sys	%v
\n", memStats.HeapSys)
	fmt.Fprintf(w, "
Idle	%v
\n", memStats.HeapIdle)
	fmt.Fprintf(w, "
Inuse	%v
\n", memStats.HeapInuse)
	fmt.Fprintf(w, "
Released	%v
\n", memStats.HeapReleased)
	fmt.Fprintf(w, "
Objects	%v
\n", memStats.HeapObjects)
	fmt.Fprintln(w, "
")
	fmt.Fprintln(w, "
Low-level
")
	fmt.Fprintln(w, "
")
	fmt.Fprintf(w, "
Stack Inuse	%v
\n", memStats.StackInuse)
	fmt.Fprintf(w, "
Stack Sys	%v
\n", memStats.StackSys)
	fmt.Fprintf(w, "
MSpan Inuse	%v
\n", memStats.MSpanInuse)
	fmt.Fprintf(w, "
MSpan Sys	%v
\n", memStats.MSpanSys)
	fmt.Fprintf(w, "
MCache Inuse	%v
\n", memStats.MCacheInuse)
	fmt.Fprintf(w, "
MCache Sys	%v
\n", memStats.MCacheSys)
	fmt.Fprintf(w, "
Bucket Hash Sys	%v
\n", memStats.BuckHashSys)
	fmt.Fprintln(w, "
")
	fmt.Fprintln(w, "
Per-size
")
	fmt.Fprintln(w, "
")
	fmt.Fprintln(w, "
Size	Mallocs	Frees
")
	// TODO: histogram
	for _, bs := range memStats.BySize {
		fmt.Fprintf(w, "
%v	%v	%v
\n", bs.Size, bs.Mallocs, bs.Frees)
	}
	fmt.Fprintln(w, "
")
	fmt.Fprintln(w, "")
	fmt.Fprintln(w, "")
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	// TODO: either read the last few lines of error log, or keep them in memory and write the
	// error log buffered.
}
<commit_msg>Use templates instead<commit_after>package mudlib

import (
	"flag"
	"fmt"
	"html/template"
	"log"
	"net/http"
	"runtime"
	"runtime/debug"
)

const (
	gcTemplateContent =`

	<title>GC</title>

GC

Last GC	{{.LastGC}}

Num GC	{{.NumGC}}

Pause Total	{{.PauseTotal}}

Pause	{{.Pause}}

Pause Quantiles	{{.PauseQuantiles}}

`
	memTemplateContent =`

	<title>Mem</title>

Mem

General

Alloc	{{.Alloc}}

Total alloc	{{.TotalAlloc}}

Sys	{{.Sys}}

Lookups	{{.Lookups}}

Mallocs	{{.Mallocs}}

Frees	{{.Frees}}

Heap

Alloc	{{.HeapAlloc}}

Sys	{{.HeapSys}}

Idle	{{.HeapIdle}}

Inuse	{{.HeapInuse}}

Released	{{.HeapReleased}}

Objects	{{.HeapObjects}}

Low-level

Stack Inuse	{{.StackInuse}}

Stack Sys	{{.StackSys}}

MSpan Inuse	{{.MSpanInuse}}

MSpan Sys	{{.MSpanSys}}

MCache Inuse	{{.MCacheInuse}}

MCache Sys	{{.MCacheSys}}

Bucket Hash Sys	{{.BuckHashSys}}

Per-size

Size	Mallocs	Frees

			{{range .BySize}}

{{.Size}}	{{.Mallocs}}	{{.Frees}}

			{{end}}

`
)

var (
	httpPort = flag.Int("httpPort", 8080, "Port for HTTP interface")
	gcTemplate = template.New("GC")
	memTemplate = template.New("Mem")
)

func init() {
	gcTemplate = template.Must(gcTemplate.Parse(gcTemplateContent))
	memTemplate = template.Must(memTemplate.Parse(memTemplateContent))

	http.HandleFunc("/gc", gcHandler)
	http.HandleFunc("/mem", memHandler)
	http.HandleFunc("/errors", errorHandler)
	go startServing()
}

func startServing() {
	log.Printf("HTTP listening on port %d", *httpPort)
	if err := http.ListenAndServe(fmt.Sprintf(":%d", *httpPort), nil); err != nil {
		errorLog.Printf("Failed to start HTTP server on port %d", *httpPort)
	}
}

// TODO: Templates
func gcHandler(w http.ResponseWriter, r *http.Request) {
	gcStats := new(debug.GCStats)
	debug.ReadGCStats(gcStats)
	if err := gcTemplate.Execute(w, *gcStats); err != nil {
		errorLog.Printf("Failed to execute GC template: %+v", err)
		w.WriteHeader(500)
	}
}

func memHandler(w http.ResponseWriter, r *http.Request) {
	memStats := new(runtime.MemStats)
	runtime.ReadMemStats(memStats)
	if err := memTemplate.Execute(w, *memStats); err != nil {
		errorLog.Printf("Failed to execute Mem template: %+v", err)
		w.WriteHeader(500)
	}
}

func errorHandler(w http.ResponseWriter, r *http.Request) {
	// TODO: either read the last few lines of error log, or keep them in memory and write the
	// error log buffered.
}
<|endoftext|>


<commit_before>package brain

import (
	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
	"io"
)

func definitionDefaultFields(f output.Format) string {
	switch f {
	case output.List:
		return "Name, Description"
	default: // also output.Table
		return "Name, Description"
	}
}

func definitionPrettyPrint(definition interface{}, wr io.Writer, detail prettyprint.DetailLevel) error {
	defTpl := `
	{{ define "definition_sgl" }}{{ .Name }}: {{ .Description }}{{ end }}
	{{ define "definition_medium" }}{{ template "definition_sgl" . }}{{ end }}
	{{ define "definition_full" }}{{ template "definition_medium" . }}{{ end }}
	`
	return prettyprint.Run(wr, defTpl, "definition"+string(detail), definition)
}

// DistributionDefinition is an object we assemble from distributions and distribution_descriptions from the /definitions API call
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.Distributions slice and Definitions.DistributionDescriptions map.
type DistributionDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (d DistributionDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (d DistributionDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(d, wr, detail)
}

// HardwareProfileDefinition is an object we assemble from hardwareprofiles in the /*Definitions API call and some static data in lib/definitions.go
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.HardwareProfiles slice.
type HardwareProfileDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (hp HardwareProfileDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (hp HardwareProfileDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(hp, wr, detail)
}

// StorageGradeDefinition is an object we assemble from storage_grades and storage_grade_descriptions in the /*Definitions API call
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.StorageGrades slice and Definitions.StorageGradeDescriptions map.
type StorageGradeDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (sg StorageGradeDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (sg StorageGradeDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(sg, wr, detail)
}

// ZoneDefinition is an object we assemble from zone_names in the /*Definitions API call and some static data in lib/definitions.go
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.ZoneNames slice.
type ZoneDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (z ZoneDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (z ZoneDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(z, wr, detail)
}
<commit_msg>Fix issues with definition documentation commits brought up during MR<commit_after>package brain

import (
	"github.com/BytemarkHosting/bytemark-client/lib/output"
	"github.com/BytemarkHosting/bytemark-client/lib/output/prettyprint"
	"io"
)

func definitionDefaultFields(f output.Format) string {
	switch f {
	case output.List:
		return "Name, Description"
	default: // also output.Table
		return "Name, Description"
	}
}

func definitionPrettyPrint(definition interface{}, wr io.Writer, detail prettyprint.DetailLevel) error {
	defTpl := `
	{{ define "definition_sgl" }}{{ .Name }}: {{ .Description }}{{ end }}
	{{ define "definition_medium" }}{{ template "definition_sgl" . }}{{ end }}
	{{ define "definition_full" }}{{ template "definition_medium" . }}{{ end }}
	`
	return prettyprint.Run(wr, defTpl, "definition"+string(detail), definition)
}

// DistributionDefinition is an object we assemble from distributions and distribution_descriptions from the /definitions API call
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.Distributions slice and Definitions.DistributionDescriptions map.
type DistributionDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (d DistributionDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (d DistributionDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(d, wr, detail)
}

// HardwareProfileDefinition is an object we assemble from hardwareprofiles from the /definitions API call
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.HardwareProfiles slice.
type HardwareProfileDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (hp HardwareProfileDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (hp HardwareProfileDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(hp, wr, detail)
}

// StorageGradeDefinition is an object we assemble from storage_grades and storage_grade_descriptions from the /definitions API call
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.StorageGrades slice and Definitions.StorageGradeDescriptions map.
type StorageGradeDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (sg StorageGradeDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (sg StorageGradeDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(sg, wr, detail)
}

// ZoneDefinition is an object we assemble from zone_names from the /definitions API call and some static data in lib/definitions.go
// in the future (bytemark-client 3.0?) a slice of these this will replace the Definitions.ZoneNames slice.
type ZoneDefinition struct {
	Name        string
	Description string
}

// DefaultFields returns the list of default fields to feed to github.com/BytemarkHosting/bytemark-client for this type.
func (z ZoneDefinition) DefaultFields(f output.Format) string {
	return definitionDefaultFields(f)
}

// PrettyPrint outputs a vaguely human-readable version of the definition to wr. Detail is ignored.
func (z ZoneDefinition) PrettyPrint(wr io.Writer, detail prettyprint.DetailLevel) error {
	return definitionPrettyPrint(z, wr, detail)
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"github.com/ArthurHlt/travis-resource/common"
	"github.com/ArthurHlt/travis-resource/model"
	"encoding/json"
	"errors"
	"github.com/ArthurHlt/travis-resource/travis"
	"strconv"
	"fmt"
	"reflect"
)

func main() {

	var request model.OutRequest
	err := json.NewDecoder(os.Stdin).Decode(&request)
	common.FatalIf("failed to read request", err)
	if request.Source.Repository == "" {
		common.FatalIf("can't get build", errors.New("there is no repository set"))
	}
	travisClient, err := common.MakeTravisClient(request.Source)
	common.FatalIf("failed to create travis client", err)

	var build travis.Build
	repository := request.Source.Repository
	if request.OutParams.Repository != "" {
		repository = request.OutParams.Repository
	}
	buildParam := ""
	if buildParamInt, ok := request.OutParams.Build.(int); ok {
		buildParam = strconv.Itoa(buildParamInt)
	}
	if buildParamString, ok := request.OutParams.Build.(string); ok {
		buildParam = buildParamString
	}
	info := fmt.Sprintf("%s %s %v", buildParam, reflect.TypeOf(request.OutParams.Build), request.OutParams.Build)
	common.FatalIf("err", err);
	common.FatalIf("build number", errors.New(info));
	if buildParam == "latest" || (request.OutParams.Repository != "" && request.OutParams.Build == "" && request.OutParams.Branch == "") {
		build, err = travisClient.Builds.GetFirstFinishedBuild(repository)
		common.FatalIf("can't get build", err)
	} else if buildParam != "" {
		build, err = travisClient.Builds.GetFirstBuildFromBuildNumber(repository, buildParam)
		common.FatalIf("can't get build", err)
	} else if request.OutParams.Branch != "" {
		build, err = travisClient.Builds.GetFirstFinishedBuildWithBranch(repository, request.OutParams.Branch)
		common.FatalIf("can't get build", err)
	} else {
		builds, _, _, _, err := travisClient.Builds.ListFromRepository(request.Source.Repository, &travis.BuildListOptions{
			Number: request.Version.BuildNumber,
		})
		common.FatalIf("can't get build", err)
		if len(builds) == 0 {
			common.FatalIf("can't get build", errors.New("there is no builds in travis"))
		}
		build = builds[0]
	}

	travisClient.Builds.Restart(build.Id)
	response := model.InResponse{common.GetMetadatasFromBuild(build), model.Version{build.Number}}
	json.NewEncoder(os.Stdout).Encode(response)
}
<commit_msg>fix out resource<commit_after>package main

import (
	"os"
	"github.com/ArthurHlt/travis-resource/common"
	"github.com/ArthurHlt/travis-resource/model"
	"encoding/json"
	"errors"
	"github.com/ArthurHlt/travis-resource/travis"
	"strconv"
)

func main() {

	var request model.OutRequest
	err := json.NewDecoder(os.Stdin).Decode(&request)
	common.FatalIf("failed to read request", err)
	if request.Source.Repository == "" {
		common.FatalIf("can't get build", errors.New("there is no repository set"))
	}
	travisClient, err := common.MakeTravisClient(request.Source)
	common.FatalIf("failed to create travis client", err)

	var build travis.Build
	repository := request.Source.Repository
	if request.OutParams.Repository != "" {
		repository = request.OutParams.Repository
	}
	buildParam := ""
	if buildParamNumber, ok := request.OutParams.Build.(float64); ok {
		buildParam = strconv.FormatFloat(buildParamNumber, 'f', 6, 64)
	}
	if buildParamString, ok := request.OutParams.Build.(string); ok {
		buildParam = buildParamString
	}

	if buildParam == "latest" || (request.OutParams.Repository != "" && request.OutParams.Build == "" && request.OutParams.Branch == "") {
		build, err = travisClient.Builds.GetFirstFinishedBuild(repository)
		common.FatalIf("can't get build", err)
	} else if buildParam != "" {
		build, err = travisClient.Builds.GetFirstBuildFromBuildNumber(repository, buildParam)
		common.FatalIf("can't get build", err)
	} else if request.OutParams.Branch != "" {
		build, err = travisClient.Builds.GetFirstFinishedBuildWithBranch(repository, request.OutParams.Branch)
		common.FatalIf("can't get build", err)
	} else {
		builds, _, _, _, err := travisClient.Builds.ListFromRepository(request.Source.Repository, &travis.BuildListOptions{
			Number: request.Version.BuildNumber,
		})
		common.FatalIf("can't get build", err)
		if len(builds) == 0 {
			common.FatalIf("can't get build", errors.New("there is no builds in travis"))
		}
		build = builds[0]
	}

	travisClient.Builds.Restart(build.Id)
	response := model.InResponse{common.GetMetadatasFromBuild(build), model.Version{build.Number}}
	json.NewEncoder(os.Stdout).Encode(response)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"time"
)

type Sparkle struct {
	Sparkler string    `json:"sparkler"`
	Sparklee string    `json:"sparklee"`
	Reason   string    `json:"reason,omitempty"`
	Time     time.Time `json:"time,omitempty"`
}

type Leader struct {
	Name  string `json:"name"`
	Score int    `json:"score"`
}

// Return the data in JSON format. This is the default return method.
func returnJson(obj interface{}, w http.ResponseWriter, h *http.Request) {
	// Don't cache json returns. This is to work around ie's weird caching behavior
	w.Header().Set("Cache-Control", "no-cache")
	// Set the content type to json
	w.Header().Set("Content-Type", "application/json")

	j, err := json.Marshal(obj)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	fmt.Fprint(w, string(j))
}

// Placeholder in case anyone calls the root of the service.
// Perhaps change this to 404.
func defaultHandler(w http.ResponseWriter, h *http.Request) {
	fmt.Fprint(w, "Default sparkles")
}

// Add a sparkle!
func addSparkle(w http.ResponseWriter, h *http.Request) {
	fmt.Fprint(w, "Add a sparkle")
	var s Sparkle
	b := json.NewDecoder(h.Body)
	b.Decode(&s)

	result := db.AddSparkle(s)
	returnJson(result, w, h)
}

// Get the entire data set
func getSparkles(w http.ResponseWriter, h *http.Request) {
	returnJson(db.Sparkles, w, h)
}

// Get the top 5 givers
func topGivers(w http.ResponseWriter, h *http.Request) {
	result := db.TopGivers(5)
	returnJson(result, w, h)
}

// Get the top 5 receivers
func topReceivers(w http.ResponseWriter, h *http.Request) {
	result := db.TopReceivers(5)
	returnJson(result, w, h)
}

// Get all the sparkles for someone in particular
func getSparklesForRecipient(w http.ResponseWriter, h *http.Request) {
	vars := mux.Vars(h)
	rcpt := vars["recipient"]
	sparkles := db.SparklesForUser(rcpt)
	returnJson(sparkles, w, h)
}
<commit_msg>removed a line in the return that shouldn't have been there<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"github.com/gorilla/mux"
	"net/http"
	"time"
)

type Sparkle struct {
	Sparkler string    `json:"sparkler"`
	Sparklee string    `json:"sparklee"`
	Reason   string    `json:"reason,omitempty"`
	Time     time.Time `json:"time,omitempty"`
}

type Leader struct {
	Name  string `json:"name"`
	Score int    `json:"score"`
}

// Return the data in JSON format. This is the default return method.
func returnJson(obj interface{}, w http.ResponseWriter, h *http.Request) {
	// Don't cache json returns. This is to work around ie's weird caching behavior
	w.Header().Set("Cache-Control", "no-cache")
	// Set the content type to json
	w.Header().Set("Content-Type", "application/json")

	j, err := json.Marshal(obj)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}

	fmt.Fprint(w, string(j))
}

// Placeholder in case anyone calls the root of the service.
// Perhaps change this to 404.
func defaultHandler(w http.ResponseWriter, h *http.Request) {
	fmt.Fprint(w, "Default sparkles")
}

// Add a sparkle!
func addSparkle(w http.ResponseWriter, h *http.Request) {
	var s Sparkle
	b := json.NewDecoder(h.Body)
	b.Decode(&s)

	result := db.AddSparkle(s)
	returnJson(result, w, h)
}

// Get the entire data set
func getSparkles(w http.ResponseWriter, h *http.Request) {
	returnJson(db.Sparkles, w, h)
}

// Get the top 5 givers
func topGivers(w http.ResponseWriter, h *http.Request) {
	result := db.TopGivers(5)
	returnJson(result, w, h)
}

// Get the top 5 receivers
func topReceivers(w http.ResponseWriter, h *http.Request) {
	result := db.TopReceivers(5)
	returnJson(result, w, h)
}

// Get all the sparkles for someone in particular
func getSparklesForRecipient(w http.ResponseWriter, h *http.Request) {
	vars := mux.Vars(h)
	rcpt := vars["recipient"]
	sparkles := db.SparklesForUser(rcpt)
	returnJson(sparkles, w, h)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"

	as "github.com/aerospike/aerospike-client-go"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	latencyMetrics = []string{"query", "query-rec-count", "read", "udf", "write"}
	nsHeader       = regexp.MustCompile("^{(?P<namespace>.+)}-(?P<operation>.+)$")
)

type latencyCollector struct {
	latency cmetrics
	ops     cmetrics
}

func newLatencyCollector() latencyCollector {
	lc := latencyCollector{
		latency: map[string]cmetric{},
		ops:     map[string]cmetric{},
	}
	for _, m := range latencyMetrics {
		lc.latency[m] = cmetric{
			typ: prometheus.GaugeValue,
			desc: prometheus.NewDesc(
				promkey(systemLatency, m),
				m+" latency histogram",
				[]string{"namespace", "threshold"},
				nil,
			),
		}
		lc.ops[m] = cmetric{
			typ: prometheus.GaugeValue,
			desc: prometheus.NewDesc(
				promkey(systemOps, m),
				m+" ops per second",
				[]string{"namespace"},
				nil,
			),
		}
	}
	return lc
}

func (lc latencyCollector) describe(ch chan<- *prometheus.Desc) {
	for _, s := range lc.latency {
		ch <- s.desc
	}
	for _, s := range lc.ops {
		ch <- s.desc
	}
}

func (lc latencyCollector) collect(conn *as.Connection, ch chan<- prometheus.Metric) {
	stats, err := as.RequestInfo(conn, "latency:")
	if err != nil {
		log.Print(err)
		return
	}
	lat, err := parseLatency(stats["latency:"])
	if err != nil {
		log.Print(err)
		return
	}
	for key, metrics := range lat {
		if key == "batch-index" {
			continue // TODO: would be nice to do something with this key
		}
		ns, op, err := readNS(key)
		if err != nil {
			log.Print("weird latency key %q: %s", key, err)
			continue
		}
		for threshold, data := range metrics {
			if threshold == "ops/sec" {
				m := lc.ops[op]
				ch <- prometheus.MustNewConstMetric(m.desc, m.typ, data, ns)
				continue
			}
			m := lc.latency[op]
			ch <- prometheus.MustNewConstMetric(m.desc, m.typ, data, ns, threshold)
		}
	}
}

// parseLatency returns map with: "[{namespace}]-[op]" -> map[threshold]measurement
// It doesn't interprets the keys.
func parseLatency(lat string) (map[string]map[string]float64, error) {
	results := map[string]map[string]float64{}
	// Lines come in pairs, and look like this:
	// reads:{namespace}-read:14:08:38-GMT,ops/sec,>1ms,>8ms,>64ms;14:08:48,2586.8,1.58,0.77,0.00;
	lines := strings.Split(lat, ";")
	for i := 0; i < len(lines); i++ {
		line := lines[i]
		if strings.HasPrefix(line, "error") {
			continue
		}
		vs := strings.Split(line, ",")
		key := strings.SplitN(vs[0], ":", 2)[0] // strips timestamp
		cols := vs[1:]

		if i+1 >= len(lines) {
			return nil, fmt.Errorf("latency: missing measurements line")
		}
		nextLine := lines[i+1]
		i++
		measurements := strings.Split(nextLine, ",")
		if len(measurements) != len(cols)+1 {
			return nil, fmt.Errorf("invalid latency format")
		}

		ms := map[string]float64{}
		for i, v := range measurements[1:] {
			f, err := strconv.ParseFloat(v, 64)
			if err != nil {
				return nil, fmt.Errorf("%q invalid latency value %q: %s", namespace, v, err)
			}
			ms[cols[i]] = f
		}
		results[key] = ms
	}
	return results, nil
}

// readNS converts a key like "{foo}-bar" to "foo", "bar"
func readNS(s string) (string, string, error) {
	m := nsHeader.FindStringSubmatch(s)
	if len(m) != 3 {
		return "", "", fmt.Errorf("invalid namespace key")
	}
	return m[1], m[2], nil
}
<commit_msg>fix printf<commit_after>package main

import (
	"fmt"
	"log"
	"regexp"
	"strconv"
	"strings"

	as "github.com/aerospike/aerospike-client-go"
	"github.com/prometheus/client_golang/prometheus"
)

var (
	latencyMetrics = []string{"query", "query-rec-count", "read", "udf", "write"}
	nsHeader       = regexp.MustCompile("^{(?P<namespace>.+)}-(?P<operation>.+)$")
)

type latencyCollector struct {
	latency cmetrics
	ops     cmetrics
}

func newLatencyCollector() latencyCollector {
	lc := latencyCollector{
		latency: map[string]cmetric{},
		ops:     map[string]cmetric{},
	}
	for _, m := range latencyMetrics {
		lc.latency[m] = cmetric{
			typ: prometheus.GaugeValue,
			desc: prometheus.NewDesc(
				promkey(systemLatency, m),
				m+" latency histogram",
				[]string{"namespace", "threshold"},
				nil,
			),
		}
		lc.ops[m] = cmetric{
			typ: prometheus.GaugeValue,
			desc: prometheus.NewDesc(
				promkey(systemOps, m),
				m+" ops per second",
				[]string{"namespace"},
				nil,
			),
		}
	}
	return lc
}

func (lc latencyCollector) describe(ch chan<- *prometheus.Desc) {
	for _, s := range lc.latency {
		ch <- s.desc
	}
	for _, s := range lc.ops {
		ch <- s.desc
	}
}

func (lc latencyCollector) collect(conn *as.Connection, ch chan<- prometheus.Metric) {
	stats, err := as.RequestInfo(conn, "latency:")
	if err != nil {
		log.Print(err)
		return
	}
	lat, err := parseLatency(stats["latency:"])
	if err != nil {
		log.Print(err)
		return
	}
	for key, metrics := range lat {
		if key == "batch-index" {
			continue // TODO: would be nice to do something with this key
		}
		ns, op, err := readNS(key)
		if err != nil {
			log.Printf("weird latency key %q: %s", key, err)
			continue
		}
		for threshold, data := range metrics {
			if threshold == "ops/sec" {
				m := lc.ops[op]
				ch <- prometheus.MustNewConstMetric(m.desc, m.typ, data, ns)
				continue
			}
			m := lc.latency[op]
			ch <- prometheus.MustNewConstMetric(m.desc, m.typ, data, ns, threshold)
		}
	}
}

// parseLatency returns map with: "[{namespace}]-[op]" -> map[threshold]measurement
// It doesn't interprets the keys.
func parseLatency(lat string) (map[string]map[string]float64, error) {
	results := map[string]map[string]float64{}
	// Lines come in pairs, and look like this:
	// reads:{namespace}-read:14:08:38-GMT,ops/sec,>1ms,>8ms,>64ms;14:08:48,2586.8,1.58,0.77,0.00;
	lines := strings.Split(lat, ";")
	for i := 0; i < len(lines); i++ {
		line := lines[i]
		if strings.HasPrefix(line, "error") {
			continue
		}
		vs := strings.Split(line, ",")
		key := strings.SplitN(vs[0], ":", 2)[0] // strips timestamp
		cols := vs[1:]

		if i+1 >= len(lines) {
			return nil, fmt.Errorf("latency: missing measurements line")
		}
		nextLine := lines[i+1]
		i++
		measurements := strings.Split(nextLine, ",")
		if len(measurements) != len(cols)+1 {
			return nil, fmt.Errorf("invalid latency format")
		}

		ms := map[string]float64{}
		for i, v := range measurements[1:] {
			f, err := strconv.ParseFloat(v, 64)
			if err != nil {
				return nil, fmt.Errorf("%q invalid latency value %q: %s", namespace, v, err)
			}
			ms[cols[i]] = f
		}
		results[key] = ms
	}
	return results, nil
}

// readNS converts a key like "{foo}-bar" to "foo", "bar"
func readNS(s string) (string, string, error) {
	m := nsHeader.FindStringSubmatch(s)
	if len(m) != 3 {
		return "", "", fmt.Errorf("invalid namespace key")
	}
	return m[1], m[2], nil
}
<|endoftext|>


<commit_before>// Package storage provides types and functionality for abstracting storage systems (Local, in memory, S3, Google Cloud
// storage) into a common interface.
package storage

import (
	"fmt"
	"io"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// File contains the metadata required to define a file (for reading).
type File struct {
	io.ReadCloser           // Underlying data.
	Name          string    // Name of the file (likely basename).
	ModTime       time.Time // Modified time of the file.
	Size          int64     // Size of the file.
}

// isNotExister is an interface used to define the behaviour of errors resulting
// from operations which report missing files/paths.
type isNotExister interface {
	isNotExist() bool
}

// IsNotExist returns a boolean indicating whether the error is known to report that
// a path.
func IsNotExist(err error) bool {
	e, ok := err.(isNotExister)
	return ok && e.isNotExist()
}

// notExistError is returned from FS.Open implementations when a requested
// path does not exist.
type notExistError struct {
	Path string
}

func (e *notExistError) isNotExist() bool { return true }

// Error implements error
func (e *notExistError) Error() string {
	return fmt.Sprintf("storage %v: file does not exist", e.Path)
}

// FS is an interface which defines a virtual filesystem.
type FS interface {
	Walker

	// Open opens an existing file at path in the filesystem.
	Open(ctx context.Context, path string) (*File, error)

	// Create makes a new file at path in the filesystem.  Callers must close the
	// returned WriteCloser and check the error to be sure that the file
	// was successfully written.
	Create(ctx context.Context, path string) (io.WriteCloser, error)

	// Delete removes a path from the filesystem.
	Delete(ctx context.Context, path string) error
}

// FSFromURL takes a file system path and returns a FSWalker
// corresponding to a supported storage system (CloudStorage,
// S3, or Local if no platform-specific prefix is used).
func FSFromURL(path string) FS {
	if strings.HasPrefix(path, "gs://") {
		return &CloudStorage{Bucket: strings.TrimPrefix(path, "gs://")}
	}
	if strings.HasPrefix(path, "s3://") {
		return &S3{Bucket: strings.TrimPrefix(path, "s3://")}
	}
	return Local(path)
}

// Prefix creates a FS which wraps fs and prefixes all paths with prefix.
func Prefix(fs FS, prefix string) FS {
	return pfx{
		fs:     fs,
		prefix: prefix,
	}
}

type pfx struct {
	fs     FS
	prefix string
}

func (p pfx) addPrefix(path string) string {
	return fmt.Sprintf("%v%v", p.prefix, path)
}

// Open implements FS.
func (p pfx) Open(ctx context.Context, path string) (*File, error) {
	return p.fs.Open(ctx, p.addPrefix(path))
}

// Create implements FS.
func (p pfx) Create(ctx context.Context, path string) (io.WriteCloser, error) {
	return p.fs.Create(ctx, p.addPrefix(path))
}

// Delete implements FS.
func (p pfx) Delete(ctx context.Context, path string) error {
	return p.fs.Delete(ctx, p.addPrefix(path))
}

// Walk transverses all paths underneath path, calling fn on each visited path.
func (p pfx) Walk(ctx context.Context, path string, fn WalkFn) error {
	return p.fs.Walk(ctx, p.addPrefix(path), func(path string) error {
		path = strings.TrimPrefix(path, p.prefix)
		return fn(path)
	})
}
<commit_msg>docs: Small improvements.<commit_after>// Package storage provides types and functionality for abstracting storage systems (Local, in memory, S3, Google Cloud
// storage) into a common interface.
package storage

import (
	"fmt"
	"io"
	"strings"
	"time"

	"golang.org/x/net/context"
)

// File contains the metadata required to define a file (for reading).
type File struct {
	io.ReadCloser           // Underlying data.
	Name          string    // Name of the file (likely basename).
	ModTime       time.Time // Modified time of the file.
	Size          int64     // Size of the file.
}

// isNotExister is an interface used to define the behaviour of errors resulting
// from operations which report missing files/paths.
type isNotExister interface {
	isNotExist() bool
}

// IsNotExist returns a boolean indicating whether the error is known to report that
// a path does not exist.
func IsNotExist(err error) bool {
	e, ok := err.(isNotExister)
	return ok && e.isNotExist()
}

// notExistError is returned from FS.Open implementations when a requested
// path does not exist.
type notExistError struct {
	Path string
}

func (e *notExistError) isNotExist() bool { return true }

// Error implements error
func (e *notExistError) Error() string {
	return fmt.Sprintf("storage %v: path does not exist", e.Path)
}

// FS is an interface which defines a virtual filesystem.
type FS interface {
	Walker

	// Open opens an existing file at path in the filesystem.  Callers must close the
	// File when done to release all underlying resources.
	Open(ctx context.Context, path string) (*File, error)

	// Create makes a new file at path in the filesystem.  Callers must close the
	// returned WriteCloser and check the error to be sure that the file
	// was successfully written.
	Create(ctx context.Context, path string) (io.WriteCloser, error)

	// Delete removes a path from the filesystem.
	Delete(ctx context.Context, path string) error
}

// FSFromURL takes a file system path and returns a FSWalker
// corresponding to a supported storage system (CloudStorage,
// S3, or Local if no platform-specific prefix is used).
func FSFromURL(path string) FS {
	if strings.HasPrefix(path, "gs://") {
		return &CloudStorage{Bucket: strings.TrimPrefix(path, "gs://")}
	}
	if strings.HasPrefix(path, "s3://") {
		return &S3{Bucket: strings.TrimPrefix(path, "s3://")}
	}
	return Local(path)
}

// Prefix creates a FS which wraps fs and prefixes all paths with prefix.
func Prefix(fs FS, prefix string) FS {
	return pfx{
		fs:     fs,
		prefix: prefix,
	}
}

type pfx struct {
	fs     FS
	prefix string
}

func (p pfx) addPrefix(path string) string {
	return fmt.Sprintf("%v%v", p.prefix, path)
}

// Open implements FS.
func (p pfx) Open(ctx context.Context, path string) (*File, error) {
	return p.fs.Open(ctx, p.addPrefix(path))
}

// Create implements FS.
func (p pfx) Create(ctx context.Context, path string) (io.WriteCloser, error) {
	return p.fs.Create(ctx, p.addPrefix(path))
}

// Delete implements FS.
func (p pfx) Delete(ctx context.Context, path string) error {
	return p.fs.Delete(ctx, p.addPrefix(path))
}

// Walk transverses all paths underneath path, calling fn on each visited path.
func (p pfx) Walk(ctx context.Context, path string, fn WalkFn) error {
	return p.fs.Walk(ctx, p.addPrefix(path), func(path string) error {
		path = strings.TrimPrefix(path, p.prefix)
		return fn(path)
	})
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/sheenobu/go-gamekit"
	"github.com/sheenobu/rxgen/rx"
	"github.com/veandco/go-sdl2/sdl"
	"golang.org/x/net/context"
)

func newButton(r *sdl.Rect, t *sdl.Texture) *button {
	return &button{
		X:            r.X,
		Y:            r.Y,
		W:            r.W,
		H:            r.H,
		T:            t,
		Clicked:      rx.NewBool(false),
		clickedState: false,
	}
}

type button struct {
	X int32
	Y int32
	W int32
	H int32

	T *sdl.Texture

	Clicked      *rx.Bool
	clickedState bool
}

func (b *button) Run(ctx context.Context, m *gamekit.Mouse) {

	posS := m.Position.Subscribe()
	clickS := m.LeftButtonState.Subscribe()
	defer posS.Close()
	defer clickS.Close()

	hovering := false

	for {
		select {
		case <-ctx.Done():
			return
		case pos := <-posS.C:
			x := pos.L
			y := pos.R
			hovering = x > b.X && y > b.Y && x < b.X+b.W && y < b.Y+b.H
		case leftClick := <-clickS.C:
			if hovering && leftClick {
				b.clickedState = true
				b.Clicked.Set(true)
			} else {
				b.clickedState = false
				b.Clicked.Set(false)
			}
		}
	}
}

func (b *button) Render(r *sdl.Renderer) {
	if b.T == nil {
		return
	}

	r.Copy(b.T, nil, &sdl.Rect{X: b.X, Y: b.Y, W: b.W, H: b.H})
}
<commit_msg>launcher2/button - make hovering state a struct member<commit_after>package main

import (
	"github.com/sheenobu/go-gamekit"
	"github.com/sheenobu/rxgen/rx"
	"github.com/veandco/go-sdl2/sdl"
	"golang.org/x/net/context"
)

func newButton(r *sdl.Rect, t *sdl.Texture) *button {
	return &button{
		X:             r.X,
		Y:             r.Y,
		W:             r.W,
		H:             r.H,
		T:             t,
		Clicked:       rx.NewBool(false),
		clickedState:  false,
		hoveringState: false,
	}
}

type button struct {
	X int32
	Y int32
	W int32
	H int32

	T *sdl.Texture

	Clicked *rx.Bool

	clickedState  bool
	hoveringState bool
}

func (b *button) Run(ctx context.Context, m *gamekit.Mouse) {

	posS := m.Position.Subscribe()
	clickS := m.LeftButtonState.Subscribe()
	defer posS.Close()
	defer clickS.Close()

	for {
		select {
		case <-ctx.Done():
			return
		case pos := <-posS.C:
			x := pos.L
			y := pos.R
			b.hoveringState = x > b.X && y > b.Y && x < b.X+b.W && y < b.Y+b.H
		case leftClick := <-clickS.C:
			if b.hoveringState && leftClick {
				b.clickedState = true
				b.Clicked.Set(true)
			} else {
				b.clickedState = false
				b.Clicked.Set(false)
			}
		}
	}
}

func (b *button) Render(r *sdl.Renderer) {
	if b.T == nil {
		return
	}

	r.Copy(b.T, nil, &sdl.Rect{X: b.X, Y: b.Y, W: b.W, H: b.H})
}
<|endoftext|>


<commit_before>//helpful link for match: https://golang.org/src/path/filepath/match_test.go
//GetApps()'s apps-summary is WAY too limited!!! They REALLY need to improve it >:(
//

package main

import (
	"errors"
	"fmt" //standard
	"os"
	//"reflect" //used to see type of object
	"strconv"
	"github.com/guidowb/cf-go-client/panic" //panics
	"strings"
	"github.com/cloudfoundry/cli/plugin/models"
	//"github.com/cloudfoundry/cli/cf/api"
	//"github.com/cloudfoundry/cli/cf/formatters"
	"github.com/cloudfoundry/cli/plugin" //standard//https://github.com/cloudfoundry/cli/blob/8c310da376377c53f001d916708c056ce1558959/plugin/plugin.go

	"path/filepath" //for matches//https://golang.org/pkg/path/filepath/
	"github.com/cloudfoundry/cli/cf/terminal" //for table || https://github.com/cloudfoundry/cli/blob/4a108fd21d6633b250f6d9f46e870967cae96ac0/cf/terminal/table.go
	//. "github.com/cloudfoundry/cli/cf/i18n"
)

//Wildcard is this plugin
type Wildcard struct {
	ui 				terminal.UI
	matchedApps 	[]plugin_models.ApplicationSummary
}

//GetMetadata returns metatada
func (cmd *Wildcard) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "wildcard",
		Version: plugin.VersionType{ //leavealone
			Major: 0,
			Minor: 1,
			Build: 0,
		},
		Commands: []plugin.Command{  //****** array of command structures
			{
				Name:     "wildcard-apps",
				Alias:	  "wc-a",
				HelpText: "List all apps in the target space matching the wildcard",
				UsageDetails: plugin.Usage{
					Usage: "cf wildcard-apps APP_NAME_WITH_WILDCARD",
				},
			},
			{
				Name:     "wildcard-delete-a",
				Alias:	  "wc-da",
				HelpText: "Delete all apps in the target space matching the wildcard",
				UsageDetails: plugin.Usage{
					Usage: "cf wildcard-delete-a APP_NAME_WITH_WILDCARD",
				},
			},
			{
				Name:     "wildcard-delete-i",
				Alias:	  "wc-di",
				HelpText: "Interactively delete apps in the target space matching the wildcard",
				UsageDetails: plugin.Usage{
					Usage: "cf wildcard-delete-i APP_NAME_WITH_WILDCARD",
				},
			},
		},
	}
}

func main() {
	plugin.Start(new(Wildcard))
	//plugin.Start(newWildcard())
}

func (cmd *Wildcard) usage(args []string) error {
	badArgs := 3 != len(args)
	if badArgs {
		return errors.New("Usage: cf wildcard-apps\n\tcf wildcard-apps APP_NAME_WITH_WILDCARD")
	}
	return nil
}

//Run runs the plugin
//called everytime user executes the command
func (cmd *Wildcard) Run(cliConnection plugin.CliConnection, args []string) {
	//fmt.Println(formatters.ToMegabytes("d"))
	if args[0] == "wildcard-apps" { //checking is very imp.
		cmd.WildcardCommandApps(cliConnection, args)
	} else if args[0] == "wildcard-delete-a" {
		cmd.WildcardCommandDeleteAll(cliConnection, args)
	} //else if args[0] == "wildcard-delete-i" {
	// 	cmd.WildcardCommandDeleteInteractive(cliConnection, args)
	// }
}

//WildcardCommand creates a new instance of this plugin
//this is the actual implementation
//one method per command

func (cmd *Wildcard) WildcardCommandApps(cliConnection plugin.CliConnection, args []string) {
	defer panic.HandlePanics()
	pattern := args[1]
	output, _ := cliConnection.GetApps()
	for i := 0; i < (len(output)); i++ {
		ok, _ := filepath.Match(pattern, output[i].Name)
		if ok {
			cmd.matchedApps = append(cmd.matchedApps, output[i])
		}
	}
	cmd.ui = terminal.NewUI(os.Stdin, terminal.NewTeePrinter())
	table := terminal.NewTable(cmd.ui, []string{("name"), ("requested state"), ("instances"), ("memory"), ("disk"), ("urls")})
	for _, app := range cmd.matchedApps {
		 var urls []string
		for _, route := range app.Routes {
			if route.Host == "" {
				urls = append(urls, route.Domain.Name)
			}
			urls = append(urls, fmt.Sprintf("%s.%s", route.Host, route.Domain.Name))
		}
		table.Add(
			app.Name,
			app.State,
			strconv.Itoa(app.RunningInstances),
			strconv.FormatInt(app.Memory, 10),
			strconv.FormatInt(app.DiskQuota, 10),
			strings.Join(urls, ", "),
		)
	}
	table.Print()
}

func (cmd *Wildcard) WildcardCommandDeleteAll(cliConnection plugin.CliConnection, args []string) {
	cmd.WildcardCommandApps(cliConnection, args)
	for _, app := range cmd.matchedApps {
		cliConnection.CliCommandWithoutTerminalOutput("delete", app.Name, "-f")
	}
}

<commit_msg>added T() formatting. Getting undefined T error<commit_after>//helpful link for match: https://golang.org/src/path/filepath/match_test.go
//GetApps()'s apps-summary is WAY too limited!!! They REALLY need to improve it >:(
//

package main

import (
	"errors"
	"fmt" //standard
	"os"
	//"reflect" //used to see type of object
	"strconv"
	"github.com/guidowb/cf-go-client/panic" //panics
	"strings"
	"github.com/cloudfoundry/cli/plugin/models"
	//"github.com/cloudfoundry/cli/cf/api"
	//"github.com/cloudfoundry/cli/cf/formatters"
	"github.com/cloudfoundry/cli/plugin" //standard//https://github.com/cloudfoundry/cli/blob/8c310da376377c53f001d916708c056ce1558959/plugin/plugin.go

	"path/filepath" //for matches//https://golang.org/pkg/path/filepath/
	"github.com/cloudfoundry/cli/cf/terminal" //for table || https://github.com/cloudfoundry/cli/blob/4a108fd21d6633b250f6d9f46e870967cae96ac0/cf/terminal/table.go

	//for implementing T
	"github.com/cloudfoundry/cli/cf/trace"
	"github.com/cloudfoundry/cli/cf/i18n"
	"github.com/cloudfoundry/cli/cf/i18n/detection"
	"github.com/cloudfoundry/cli/cf/configuration/core_config"
	"github.com/cloudfoundry/cli/cf/configuration/config_helpers"

)

//Wildcard is this plugin
type Wildcard struct {
	ui 				terminal.UI
	matchedApps 	[]plugin_models.ApplicationSummary
}

//GetMetadata returns metatada
func (cmd *Wildcard) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name: "wildcard",
		Version: plugin.VersionType{ //leavealone
			Major: 0,
			Minor: 1,
			Build: 0,
		},
		Commands: []plugin.Command{  //****** array of command structures
			{
				Name:     "wildcard-apps",
				Alias:	  "wc-a",
				HelpText: "List all apps in the target space matching the wildcard",
				UsageDetails: plugin.Usage{
					Usage: "cf wildcard-apps APP_NAME_WITH_WILDCARD",
				},
			},
			{
				Name:     "wildcard-delete",
				Alias:	  "wc-d",
				HelpText: "Delete apps in the target space matching the wildcard",
				UsageDetails: plugin.Usage{
					Usage: "cf wildcard-delete-a APP_NAME_WITH_WILDCARD",
				},
			},
		},
	}
}

func main() {
	plugin.Start(new(Wildcard))
	//plugin.Start(newWildcard())
}

func (cmd *Wildcard) usage(args []string) error {
	badArgs := 3 != len(args)
	if badArgs {
		return errors.New("Usage: cf wildcard-apps\n\tcf wildcard-apps APP_NAME_WITH_WILDCARD")
	}
	return nil
}

//Run runs the plugin
//called everytime user executes the command
func (cmd *Wildcard) Run(cliConnection plugin.CliConnection, args []string) {
	//fmt.Println(formatters.ToMegabytes("d"))
	if args[0] == "wildcard-apps" { //checking is very imp.
		cmd.WildcardCommandApps(cliConnection, args)
	} else if args[0] == "wildcard-delete-a" {
		cmd.WildcardCommandDelete(cliConnection, args)
	}
}

//WildcardCommand creates a new instance of this plugin
//this is the actual implementation
//one method per command
func CloudControllerCreator() {
	errorHandler := func(err error) {
		if err != nil {
			fmt.Sprintf("Config error: %s", err)
		}
	}
	cc_config := core_config.NewRepositoryFromFilepath(config_helpers.DefaultFilePath(), errorHandler)
	i18n.T = i18n.Init(cc_config, &detection.JibberJabberDetector{})
	if os.Getenv("CF_TRACE") != "" {
		trace.Logger = trace.NewLogger(os.Getenv("CF_TRACE"))
	} else {
		trace.Logger = trace.NewLogger(cc_config.Trace())
	}

}

func (cmd *Wildcard) WildcardCommandApps(cliConnection plugin.CliConnection, args []string) {
	CloudControllerCreator()
	defer panic.HandlePanics()
	pattern := args[1]
	output, _ := cliConnection.GetApps()
	for i := 0; i < (len(output)); i++ {
		ok, _ := filepath.Match(pattern, output[i].Name)
		if ok {
			cmd.matchedApps = append(cmd.matchedApps, output[i])
		}
	}
	cmd.ui = terminal.NewUI(os.Stdin, terminal.NewTeePrinter())
	//table := terminal.NewTable(cmd.ui, []string{("name"), ("requested state"), ("instances"), ("memory"), ("disk"), ("urls")})
	table := terminal.NewTable(cmd.ui, []string{T("name"), T("requested state"), T("instances"), T("memory"), T("disk"), T("urls")})
	for _, app := range cmd.matchedApps {
		 var urls []string
		for _, route := range app.Routes {
			if route.Host == "" {
				urls = append(urls, route.Domain.Name)
			}
			urls = append(urls, fmt.Sprintf("%s.%s", route.Host, route.Domain.Name))
		}
		table.Add(
			app.Name,
			app.State,
			strconv.Itoa(app.RunningInstances),
			strconv.FormatInt(app.Memory, 10),
			strconv.FormatInt(app.DiskQuota, 10),
			strings.Join(urls, ", "),
		)
	}
	table.Print()
}

func (cmd *Wildcard) WildcardCommandDelete(cliConnection plugin.CliConnection, args []string) {
	cmd.WildcardCommandApps(cliConnection, args)
	for _, app := range cmd.matchedApps {
		cliConnection.CliCommandWithoutTerminalOutput("delete", app.Name, "-f")
	}
}

<|endoftext|>


<commit_before>package cas

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"
	"path/filepath"

	"github.com/coreos/rocket/app-container/aci"
	"github.com/coreos/rocket/Godeps/_workspace/src/github.com/peterbourgon/diskv"
)

// TODO(philips): use a database for the secondary indexes like remoteType and
// appType. This is OK for now though.
const (
	blobType int64 = iota
	remoteType
	tmpType
)

var otmap = [...]string{
	"blob",
	"remote",
	"tmp",
}

type Store struct {
	stores []*diskv.Diskv
}

func NewStore(base string) *Store {
	ds := &Store{}
	ds.stores = make([]*diskv.Diskv, len(otmap))

	for i, p := range otmap {
		ds.stores[i] = diskv.New(diskv.Options{
			BasePath:     filepath.Join(base, "cas", p),
			Transform:    blockTransform,
			CacheSizeMax: 1024 * 1024, // 1MB
		})
	}

	return ds
}

func (ds Store) ReadStream(key string) (io.ReadCloser, error) {
	return ds.stores[blobType].ReadStream(key, false)
}

func (ds Store) WriteStream(key string, r io.Reader) error {
	return ds.stores[blobType].WriteStream(key, r, true)
}

func (ds Store) WriteACI(tmpKey string, orig io.Reader) (string, error) {
	var b bytes.Buffer

	// TODO(philips): use go routines to parallelize this pipeline and make
	// the file type detection happen without a second stream
	_, err := io.Copy(&b, orig)
	if err != nil {
		return "", err
	}
	err = ds.stores[tmpType].WriteStream(tmpKey, &b, true)
	if err != nil {
		return "", err
	}

	// Detect the filetype
	rs, err := ds.stores[tmpType].ReadStream(tmpKey, false)
	if err != nil {
		return "", err
	}
	defer rs.Close()
	typ, err := aci.DetectFileType(rs)
	if err != nil {
		return "", err
	}
	rs, err = ds.stores[tmpType].ReadStream(tmpKey, false)
	if err != nil {
		return "", err
	}
	defer rs.Close()

	// Generate the hash of the decompressed tar
	dr, err := decompress(rs, typ)
	if err != nil {
		return "", err
	}
	hash := sha256.New()
	_, err = io.Copy(hash, dr)
	if err != nil {
		return "", err
	}

	// Store the decompressed tar
	rs, err = ds.stores[tmpType].ReadStream(tmpKey, false)
	if err != nil {
		return "", err
	}
	defer rs.Close()
	dr, err = decompress(rs, typ)
	if err != nil {
		return "", err
	}

	key := fmt.Sprintf("sha256-%x", hash.Sum(nil))
	err = ds.stores[blobType].WriteStream(key, dr, true)
	if err != nil {
		return "", err
	}

	ds.stores[tmpType].Erase(tmpKey)

	return key, nil
}

type Index interface {
	Hash() string
	Marshal() []byte
	Unmarshal([]byte)
	Type() int64
}

func (ds Store) WriteIndex(i Index) {
	ds.stores[i.Type()].Write(i.Hash(), i.Marshal())
}

func (ds Store) ReadIndex(i Index) error {
	buf, err := ds.stores[i.Type()].Read(i.Hash())
	if err != nil {
		return err
	}

	i.Unmarshal(buf)

	return nil
}

func (ds Store) Dump(hex bool) {
	for _, s := range ds.stores {
		var keyCount int
		for key := range s.Keys() {
			val, err := s.Read(key)
			if err != nil {
				panic(fmt.Sprintf("key %s had no value", key))
			}
			if len(val) > 128 {
				val = val[:128]
			}
			out := string(val)
			if hex {
				out = fmt.Sprintf("%x", val)
			}
			fmt.Printf("%s/%s: %s\n", s.BasePath, key, out)
			keyCount++
		}
		fmt.Printf("%d total keys\n", keyCount)
	}
}
<commit_msg>cas: eliminate a read/write from WriteACI path<commit_after>package cas

import (
	"bytes"
	"crypto/sha256"
	"fmt"
	"io"
	"path/filepath"

	"github.com/coreos/rocket/Godeps/_workspace/src/github.com/peterbourgon/diskv"
	"github.com/coreos/rocket/app-container/aci"
)

// TODO(philips): use a database for the secondary indexes like remoteType and
// appType. This is OK for now though.
const (
	blobType int64 = iota
	remoteType
	tmpType
)

var otmap = [...]string{
	"blob",
	"remote",
	"tmp",
}

type Store struct {
	stores []*diskv.Diskv
}

func NewStore(base string) *Store {
	ds := &Store{}
	ds.stores = make([]*diskv.Diskv, len(otmap))

	for i, p := range otmap {
		ds.stores[i] = diskv.New(diskv.Options{
			BasePath:     filepath.Join(base, "cas", p),
			Transform:    blockTransform,
			CacheSizeMax: 1024 * 1024, // 1MB
		})
	}

	return ds
}

func (ds Store) ReadStream(key string) (io.ReadCloser, error) {
	return ds.stores[blobType].ReadStream(key, false)
}

func (ds Store) WriteStream(key string, r io.Reader) error {
	return ds.stores[blobType].WriteStream(key, r, true)
}

// limitedWriter is similar to io.LimitedReader; it writes to W but limits the
// amount of data written to just N bytes. Each subsequent call to Write()
// will return a nil error and a count of 0.
type limitedWriter struct {
	W io.ReadWriter
	N int64
}

func (h *limitedWriter) Write(data []byte) (n int, err error) {
	if h.N <= 0 {
		return 0, nil
	}
	if int64(len(data)) > h.N {
		data = data[0:h.N]
	}
	n, err = h.W.Write(data)
	h.N -= int64(n)
	return
}

func (h *limitedWriter) Read(p []byte) (n int, err error) {
	return h.W.Read(p)
}

func (ds Store) WriteACI(tmpKey string, orig io.Reader) (string, error) {
	// We initially write the ACI into the store using a temporary key,
	// teeing a header so we can detect the filetype for decompression
	hdr := &limitedWriter{
		W: &bytes.Buffer{},
		N: 512,
	}
	tr := io.TeeReader(orig, hdr)

	err := ds.stores[tmpType].WriteStream(tmpKey, tr, true)
	if err != nil {
		return "", err
	}

	// Now detect the filetype so we can choose the appropriate decompressor
	typ, err := aci.DetectFileType(hdr.W)
	if err != nil {
		return "", err
	}
	// Read the image back out of the store to generate the hash of the decompressed tar
	rs, err := ds.stores[tmpType].ReadStream(tmpKey, false)
	if err != nil {
		return "", err
	}
	defer rs.Close()

	dr, err := decompress(rs, typ)
	if err != nil {
		return "", err
	}
	hash := sha256.New()
	_, err = io.Copy(hash, dr)
	if err != nil {
		return "", err
	}

	// Store the decompressed tar using the hash as the real key
	rs, err = ds.stores[tmpType].ReadStream(tmpKey, false)
	if err != nil {
		return "", err
	}
	defer rs.Close()
	dr, err = decompress(rs, typ)
	if err != nil {
		return "", err
	}

	key := fmt.Sprintf("sha256-%x", hash.Sum(nil))
	err = ds.stores[blobType].WriteStream(key, dr, true)
	if err != nil {
		return "", err
	}

	ds.stores[tmpType].Erase(tmpKey)

	return key, nil
}

type Index interface {
	Hash() string
	Marshal() []byte
	Unmarshal([]byte)
	Type() int64
}

func (ds Store) WriteIndex(i Index) {
	ds.stores[i.Type()].Write(i.Hash(), i.Marshal())
}

func (ds Store) ReadIndex(i Index) error {
	buf, err := ds.stores[i.Type()].Read(i.Hash())
	if err != nil {
		return err
	}

	i.Unmarshal(buf)

	return nil
}

func (ds Store) Dump(hex bool) {
	for _, s := range ds.stores {
		var keyCount int
		for key := range s.Keys() {
			val, err := s.Read(key)
			if err != nil {
				panic(fmt.Sprintf("key %s had no value", key))
			}
			if len(val) > 128 {
				val = val[:128]
			}
			out := string(val)
			if hex {
				out = fmt.Sprintf("%x", val)
			}
			fmt.Printf("%s/%s: %s\n", s.BasePath, key, out)
			keyCount++
		}
		fmt.Printf("%d total keys\n", keyCount)
	}
}
<|endoftext|>


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

package x509

import (
	"crypto/dsa"
	"crypto/ecdsa"
	"crypto/rsa"
	"encoding/asn1"
	"encoding/json"

	"time"

	"github.com/asaskevich/govalidator"
	"github.com/zmap/zgrab/ztools/keys"
	"github.com/zmap/zgrab/ztools/x509/pkix"
)

type auxKeyUsage struct {
	DigitalSignature  bool   `json:"digital_signature,omitempty"`
	ContentCommitment bool   `json:"content_commitment,omitempty"`
	KeyEncipherment   bool   `json:"key_encipherment,omitempty"`
	DataEncipherment  bool   `json:"data_encipherment,omitempty"`
	KeyAgreement      bool   `json:"key_agreement,omitempty"`
	CertificateSign   bool   `json:"certificate_sign,omitempty"`
	CRLSign           bool   `json:"crl_sign,omitempty"`
	EncipherOnly      bool   `json:"encipher_only,omitempty"`
	DecipherOnly      bool   `json:"decipher_only,omitempty"`
	Value             uint32 `json:"value"`
}

// MarshalJSON implements the json.Marshaler interface
func (k KeyUsage) MarshalJSON() ([]byte, error) {
	var enc auxKeyUsage
	enc.Value = uint32(k)
	if k&KeyUsageDigitalSignature > 0 {
		enc.DigitalSignature = true
	}
	if k&KeyUsageContentCommitment > 0 {
		enc.ContentCommitment = true
	}
	if k&KeyUsageKeyEncipherment > 0 {
		enc.KeyEncipherment = true
	}
	if k&KeyUsageDataEncipherment > 0 {
		enc.DataEncipherment = true
	}
	if k&KeyUsageKeyAgreement > 0 {
		enc.KeyAgreement = true
	}
	if k&KeyUsageCertSign > 0 {
		enc.CertificateSign = true
	}
	if k&KeyUsageCRLSign > 0 {
		enc.CRLSign = true
	}
	if k&KeyUsageEncipherOnly > 0 {
		enc.EncipherOnly = true
	}
	if k&KeyUsageDecipherOnly > 0 {
		enc.DecipherOnly = true
	}
	return json.Marshal(&enc)
}

// UnmarshalJSON implements the json.Unmarshler interface
func (k *KeyUsage) UnmarshalJSON(b []byte) error {
	var aux auxKeyUsage
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	// TODO: validate the flags match
	v := int(aux.Value)
	*k = KeyUsage(v)
	return nil
}

type auxSignatureAlgorithm struct {
	Name string      `json:"name,omitempty"`
	OID  pkix.AuxOID `json:"oid"`
}

// MarshalJSON implements the json.Marshaler interface
func (s *SignatureAlgorithm) MarshalJSON() ([]byte, error) {
	aux := auxSignatureAlgorithm{
		Name: s.String(),
	}
	for _, val := range signatureAlgorithmDetails {
		if val.algo == *s {
			aux.OID = make([]int, len(val.oid))
			for idx := range val.oid {
				aux.OID[idx] = val.oid[idx]
			}
		}
	}
	return json.Marshal(&aux)
}

// UnmarshalJSON implements the json.Unmarshler interface
func (s *SignatureAlgorithm) UnmarshalJSON(b []byte) error {
	var aux auxSignatureAlgorithm
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	*s = UnknownSignatureAlgorithm
	oid := asn1.ObjectIdentifier(aux.OID.AsSlice())
	for _, val := range signatureAlgorithmDetails {
		if val.oid.Equal(oid) {
			*s = val.algo
			break
		}
	}
	return nil
}

type auxPublicKeyAlgorithm struct {
	Name string      `json:"name,omitempty"`
	OID  pkix.AuxOID `json:"oid"`
}

// MarshalJSON implements the json.Marshaler interface
func (p *PublicKeyAlgorithm) MarshalJSON() ([]byte, error) {
	aux := auxPublicKeyAlgorithm{
		Name: p.String(),
	}
	return json.Marshal(&aux)
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (p *PublicKeyAlgorithm) UnmarshalJSON(b []byte) error {
	var aux auxPublicKeyAlgorithm
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	panic("unimplemented")
}

type auxValidity struct {
	Start          string `json:"start"`
	End            string `json:"end"`
	ValidityPeriod int    `json:"length"`
}

func (v *validity) MarshalJSON() ([]byte, error) {
	aux := auxValidity{
		Start:          v.NotBefore.UTC().Format(time.RFC3339),
		End:            v.NotAfter.UTC().Format(time.RFC3339),
		ValidityPeriod: int(v.NotAfter.Sub(v.NotBefore).Seconds()),
	}
	return json.Marshal(&aux)
}

func (v *validity) UnmarshalJSON(b []byte) error {
	var aux auxValidity
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	var err error
	if v.NotBefore, err = time.Parse(time.RFC3339, aux.Start); err != nil {
		return err
	}
	if v.NotAfter, err = time.Parse(time.RFC3339, aux.End); err != nil {
		return err
	}

	return nil
}

type jsonSubjectKeyInfo struct {
	KeyAlgorithm    PublicKeyAlgorithm     `json:"key_algorithm"`
	RSAPublicKey    *keys.RSAPublicKey     `json:"rsa_public_key,omitempty"`
	DSAPublicKey    interface{}            `json:"dsa_public_key,omitempty"`
	ECDSAPublicKey  interface{}            `json:"ecdsa_public_key,omitempty"`
	SPKIFingerprint CertificateFingerprint `json:"fingerprint_sha256"`
}

type jsonSignature struct {
	SignatureAlgorithm SignatureAlgorithm `json:"signature_algorithm"`
	Value              []byte             `json:"value"`
	Valid              bool               `json:"valid"`
	SelfSigned         bool               `json:"self_signed"`
}

type fullValidity struct {
	validity
	ValidityPeriod int
}

type jsonCertificate struct {
	Version                   int                          `json:"version"`
	SerialNumber              string                       `json:"serial_number"`
	SignatureAlgorithm        SignatureAlgorithm           `json:"signature_algorithm"`
	Issuer                    pkix.Name                    `json:"issuer"`
	IssuerDN                  string                       `json:"issuer_dn,omitempty"`
	Validity                  fullValidity                 `json:"validity"`
	Subject                   pkix.Name                    `json:"subject"`
	SubjectDN                 string                       `json:"subject_dn,omitempty"`
	SubjectKeyInfo            jsonSubjectKeyInfo           `json:"subject_key_info"`
	Extensions                *CertificateExtensions       `json:"extensions,omitempty"`
	UnknownExtensions         UnknownCertificateExtensions `json:"unknown_extensions,omitempty"`
	Signature                 jsonSignature                `json:"signature"`
	FingerprintMD5            CertificateFingerprint       `json:"fingerprint_md5"`
	FingerprintSHA1           CertificateFingerprint       `json:"fingerprint_sha1"`
	FingerprintSHA256         CertificateFingerprint       `json:"fingerprint_sha256"`
	SPKISubjectFingerprint    CertificateFingerprint       `json:"spki_subject_fingerprint"`
	TBSCertificateFingerprint CertificateFingerprint       `json:"tbs_fingerprint"`
	ValidationLevel           CertValidationLevel          `json:"validation_level"`
	Names                     []string                     `json:"names"`
}

func (c *Certificate) MarshalJSON() ([]byte, error) {
	// Fill out the certificate
	jc := new(jsonCertificate)
	jc.Version = c.Version
	jc.SerialNumber = c.SerialNumber.String()
	jc.SignatureAlgorithm = c.SignatureAlgorithm
	jc.Issuer = c.Issuer
	jc.IssuerDN = c.Issuer.String()
	jc.Validity.NotBefore = c.NotBefore
	jc.Validity.NotAfter = c.NotAfter
	jc.Validity.ValidityPeriod = c.ValidityPeriod
	jc.Subject = c.Subject
	jc.SubjectDN = c.Subject.String()
	jc.SubjectKeyInfo.KeyAlgorithm = c.PublicKeyAlgorithm

	// Include all subject names, DNS names there are
	for _, obj := range c.Subject.Names {

		switch name := obj.Value.(type) {
		case string:

			flag := false

			if len(name) > 2 && name[0] == '*' {
				flag = govalidator.IsURL(name[2:])
			} else {
				flag = govalidator.IsURL(name)
			}

			// Check that this is actually a url and not something else
			if flag {
				jc.Names = append(jc.Names, name)
			}
		}
	}

	for _, name := range c.DNSNames {

		flag := false

		if len(name) > 2 && name[0] == '*' {
			flag = govalidator.IsURL(name[2:])
		} else {
			flag = govalidator.IsURL(name)
		}

		if flag {
			jc.Names = append(jc.Names, name)
		}
	}

	// Pull out the key
	keyMap := make(map[string]interface{})

	switch key := c.PublicKey.(type) {
	case *rsa.PublicKey:
		rsaKey := new(keys.RSAPublicKey)
		rsaKey.PublicKey = key
		jc.SubjectKeyInfo.RSAPublicKey = rsaKey
	case *dsa.PublicKey:
		keyMap["p"] = key.P.Bytes()
		keyMap["q"] = key.Q.Bytes()
		keyMap["g"] = key.G.Bytes()
		keyMap["y"] = key.Y.Bytes()
		jc.SubjectKeyInfo.DSAPublicKey = keyMap
	case *ecdsa.PublicKey:
		params := key.Params()
		keyMap["p"] = params.P.Bytes()
		keyMap["n"] = params.N.Bytes()
		keyMap["b"] = params.B.Bytes()
		keyMap["gx"] = params.Gx.Bytes()
		keyMap["gy"] = params.Gy.Bytes()
		keyMap["x"] = key.X.Bytes()
		keyMap["y"] = key.Y.Bytes()
		jc.SubjectKeyInfo.ECDSAPublicKey = keyMap
	case *AugmentedECDSA:
		pub := key.Pub

		keyMap["pub"] = key.Raw.Bytes

		params := pub.Params()
		keyMap["p"] = params.P.Bytes()
		keyMap["n"] = params.N.Bytes()
		keyMap["b"] = params.B.Bytes()
		keyMap["gx"] = params.Gx.Bytes()
		keyMap["gy"] = params.Gy.Bytes()
		keyMap["x"] = pub.X.Bytes()
		keyMap["y"] = pub.Y.Bytes()

		keyMap["asn1_oid"] = c.SignatureAlgorithmOID

		jc.SubjectKeyInfo.ECDSAPublicKey = keyMap
	}

	jc.Extensions, jc.UnknownExtensions = c.jsonifyExtensions()

	// TODO: Handle the fact this might not match
	jc.Signature.SignatureAlgorithm = jc.SignatureAlgorithm
	jc.Signature.Value = c.Signature
	jc.Signature.Valid = c.validSignature
	if c.Subject.CommonName == c.Issuer.CommonName {
		jc.Signature.SelfSigned = true
	}
	jc.FingerprintMD5 = c.FingerprintMD5
	jc.FingerprintSHA1 = c.FingerprintSHA1
	jc.FingerprintSHA256 = c.FingerprintSHA256
	jc.SPKISubjectFingerprint = c.SPKISubjectFingerprint
	jc.TBSCertificateFingerprint = c.TBSCertificateFingerprint
	jc.ValidationLevel = c.ValidationLevel

	return json.Marshal(jc)
}
<commit_msg>omitempty for names array so that we can use bigquery.<commit_after>// Copyright 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package x509

import (
	"crypto/dsa"
	"crypto/ecdsa"
	"crypto/rsa"
	"encoding/asn1"
	"encoding/json"

	"time"

	"github.com/asaskevich/govalidator"
	"github.com/zmap/zgrab/ztools/keys"
	"github.com/zmap/zgrab/ztools/x509/pkix"
)

type auxKeyUsage struct {
	DigitalSignature  bool   `json:"digital_signature,omitempty"`
	ContentCommitment bool   `json:"content_commitment,omitempty"`
	KeyEncipherment   bool   `json:"key_encipherment,omitempty"`
	DataEncipherment  bool   `json:"data_encipherment,omitempty"`
	KeyAgreement      bool   `json:"key_agreement,omitempty"`
	CertificateSign   bool   `json:"certificate_sign,omitempty"`
	CRLSign           bool   `json:"crl_sign,omitempty"`
	EncipherOnly      bool   `json:"encipher_only,omitempty"`
	DecipherOnly      bool   `json:"decipher_only,omitempty"`
	Value             uint32 `json:"value"`
}

// MarshalJSON implements the json.Marshaler interface
func (k KeyUsage) MarshalJSON() ([]byte, error) {
	var enc auxKeyUsage
	enc.Value = uint32(k)
	if k&KeyUsageDigitalSignature > 0 {
		enc.DigitalSignature = true
	}
	if k&KeyUsageContentCommitment > 0 {
		enc.ContentCommitment = true
	}
	if k&KeyUsageKeyEncipherment > 0 {
		enc.KeyEncipherment = true
	}
	if k&KeyUsageDataEncipherment > 0 {
		enc.DataEncipherment = true
	}
	if k&KeyUsageKeyAgreement > 0 {
		enc.KeyAgreement = true
	}
	if k&KeyUsageCertSign > 0 {
		enc.CertificateSign = true
	}
	if k&KeyUsageCRLSign > 0 {
		enc.CRLSign = true
	}
	if k&KeyUsageEncipherOnly > 0 {
		enc.EncipherOnly = true
	}
	if k&KeyUsageDecipherOnly > 0 {
		enc.DecipherOnly = true
	}
	return json.Marshal(&enc)
}

// UnmarshalJSON implements the json.Unmarshler interface
func (k *KeyUsage) UnmarshalJSON(b []byte) error {
	var aux auxKeyUsage
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	// TODO: validate the flags match
	v := int(aux.Value)
	*k = KeyUsage(v)
	return nil
}

type auxSignatureAlgorithm struct {
	Name string      `json:"name,omitempty"`
	OID  pkix.AuxOID `json:"oid"`
}

// MarshalJSON implements the json.Marshaler interface
func (s *SignatureAlgorithm) MarshalJSON() ([]byte, error) {
	aux := auxSignatureAlgorithm{
		Name: s.String(),
	}
	for _, val := range signatureAlgorithmDetails {
		if val.algo == *s {
			aux.OID = make([]int, len(val.oid))
			for idx := range val.oid {
				aux.OID[idx] = val.oid[idx]
			}
		}
	}
	return json.Marshal(&aux)
}

// UnmarshalJSON implements the json.Unmarshler interface
func (s *SignatureAlgorithm) UnmarshalJSON(b []byte) error {
	var aux auxSignatureAlgorithm
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	*s = UnknownSignatureAlgorithm
	oid := asn1.ObjectIdentifier(aux.OID.AsSlice())
	for _, val := range signatureAlgorithmDetails {
		if val.oid.Equal(oid) {
			*s = val.algo
			break
		}
	}
	return nil
}

type auxPublicKeyAlgorithm struct {
	Name string      `json:"name,omitempty"`
	OID  pkix.AuxOID `json:"oid"`
}

// MarshalJSON implements the json.Marshaler interface
func (p *PublicKeyAlgorithm) MarshalJSON() ([]byte, error) {
	aux := auxPublicKeyAlgorithm{
		Name: p.String(),
	}
	return json.Marshal(&aux)
}

// UnmarshalJSON implements the json.Unmarshaler interface
func (p *PublicKeyAlgorithm) UnmarshalJSON(b []byte) error {
	var aux auxPublicKeyAlgorithm
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	panic("unimplemented")
}

type auxValidity struct {
	Start          string `json:"start"`
	End            string `json:"end"`
	ValidityPeriod int    `json:"length"`
}

func (v *validity) MarshalJSON() ([]byte, error) {
	aux := auxValidity{
		Start:          v.NotBefore.UTC().Format(time.RFC3339),
		End:            v.NotAfter.UTC().Format(time.RFC3339),
		ValidityPeriod: int(v.NotAfter.Sub(v.NotBefore).Seconds()),
	}
	return json.Marshal(&aux)
}

func (v *validity) UnmarshalJSON(b []byte) error {
	var aux auxValidity
	if err := json.Unmarshal(b, &aux); err != nil {
		return err
	}
	var err error
	if v.NotBefore, err = time.Parse(time.RFC3339, aux.Start); err != nil {
		return err
	}
	if v.NotAfter, err = time.Parse(time.RFC3339, aux.End); err != nil {
		return err
	}

	return nil
}

type jsonSubjectKeyInfo struct {
	KeyAlgorithm    PublicKeyAlgorithm     `json:"key_algorithm"`
	RSAPublicKey    *keys.RSAPublicKey     `json:"rsa_public_key,omitempty"`
	DSAPublicKey    interface{}            `json:"dsa_public_key,omitempty"`
	ECDSAPublicKey  interface{}            `json:"ecdsa_public_key,omitempty"`
	SPKIFingerprint CertificateFingerprint `json:"fingerprint_sha256"`
}

type jsonSignature struct {
	SignatureAlgorithm SignatureAlgorithm `json:"signature_algorithm"`
	Value              []byte             `json:"value"`
	Valid              bool               `json:"valid"`
	SelfSigned         bool               `json:"self_signed"`
}

type fullValidity struct {
	validity
	ValidityPeriod int
}

type jsonCertificate struct {
	Version                   int                          `json:"version"`
	SerialNumber              string                       `json:"serial_number"`
	SignatureAlgorithm        SignatureAlgorithm           `json:"signature_algorithm"`
	Issuer                    pkix.Name                    `json:"issuer"`
	IssuerDN                  string                       `json:"issuer_dn,omitempty"`
	Validity                  fullValidity                 `json:"validity"`
	Subject                   pkix.Name                    `json:"subject"`
	SubjectDN                 string                       `json:"subject_dn,omitempty"`
	SubjectKeyInfo            jsonSubjectKeyInfo           `json:"subject_key_info"`
	Extensions                *CertificateExtensions       `json:"extensions,omitempty"`
	UnknownExtensions         UnknownCertificateExtensions `json:"unknown_extensions,omitempty"`
	Signature                 jsonSignature                `json:"signature"`
	FingerprintMD5            CertificateFingerprint       `json:"fingerprint_md5"`
	FingerprintSHA1           CertificateFingerprint       `json:"fingerprint_sha1"`
	FingerprintSHA256         CertificateFingerprint       `json:"fingerprint_sha256"`
	SPKISubjectFingerprint    CertificateFingerprint       `json:"spki_subject_fingerprint"`
	TBSCertificateFingerprint CertificateFingerprint       `json:"tbs_fingerprint"`
	ValidationLevel           CertValidationLevel          `json:"validation_level"`
	Names                     []string                     `json:"names,omitempty"`
}

func (c *Certificate) MarshalJSON() ([]byte, error) {
	// Fill out the certificate
	jc := new(jsonCertificate)
	jc.Version = c.Version
	jc.SerialNumber = c.SerialNumber.String()
	jc.SignatureAlgorithm = c.SignatureAlgorithm
	jc.Issuer = c.Issuer
	jc.IssuerDN = c.Issuer.String()
	jc.Validity.NotBefore = c.NotBefore
	jc.Validity.NotAfter = c.NotAfter
	jc.Validity.ValidityPeriod = c.ValidityPeriod
	jc.Subject = c.Subject
	jc.SubjectDN = c.Subject.String()
	jc.SubjectKeyInfo.KeyAlgorithm = c.PublicKeyAlgorithm

	// Include all subject names, DNS names there are
	for _, obj := range c.Subject.Names {

		switch name := obj.Value.(type) {
		case string:

			flag := false

			if len(name) > 2 && name[0] == '*' {
				flag = govalidator.IsURL(name[2:])
			} else {
				flag = govalidator.IsURL(name)
			}

			// Check that this is actually a url and not something else
			if flag {
				jc.Names = append(jc.Names, name)
			}
		}
	}

	for _, name := range c.DNSNames {

		flag := false

		if len(name) > 2 && name[0] == '*' {
			flag = govalidator.IsURL(name[2:])
		} else {
			flag = govalidator.IsURL(name)
		}

		if flag {
			jc.Names = append(jc.Names, name)
		}
	}

	// Pull out the key
	keyMap := make(map[string]interface{})

	switch key := c.PublicKey.(type) {
	case *rsa.PublicKey:
		rsaKey := new(keys.RSAPublicKey)
		rsaKey.PublicKey = key
		jc.SubjectKeyInfo.RSAPublicKey = rsaKey
	case *dsa.PublicKey:
		keyMap["p"] = key.P.Bytes()
		keyMap["q"] = key.Q.Bytes()
		keyMap["g"] = key.G.Bytes()
		keyMap["y"] = key.Y.Bytes()
		jc.SubjectKeyInfo.DSAPublicKey = keyMap
	case *ecdsa.PublicKey:
		params := key.Params()
		keyMap["p"] = params.P.Bytes()
		keyMap["n"] = params.N.Bytes()
		keyMap["b"] = params.B.Bytes()
		keyMap["gx"] = params.Gx.Bytes()
		keyMap["gy"] = params.Gy.Bytes()
		keyMap["x"] = key.X.Bytes()
		keyMap["y"] = key.Y.Bytes()
		jc.SubjectKeyInfo.ECDSAPublicKey = keyMap
	case *AugmentedECDSA:
		pub := key.Pub

		keyMap["pub"] = key.Raw.Bytes

		params := pub.Params()
		keyMap["p"] = params.P.Bytes()
		keyMap["n"] = params.N.Bytes()
		keyMap["b"] = params.B.Bytes()
		keyMap["gx"] = params.Gx.Bytes()
		keyMap["gy"] = params.Gy.Bytes()
		keyMap["x"] = pub.X.Bytes()
		keyMap["y"] = pub.Y.Bytes()

		keyMap["asn1_oid"] = c.SignatureAlgorithmOID

		jc.SubjectKeyInfo.ECDSAPublicKey = keyMap
	}

	jc.Extensions, jc.UnknownExtensions = c.jsonifyExtensions()

	// TODO: Handle the fact this might not match
	jc.Signature.SignatureAlgorithm = jc.SignatureAlgorithm
	jc.Signature.Value = c.Signature
	jc.Signature.Valid = c.validSignature
	if c.Subject.CommonName == c.Issuer.CommonName {
		jc.Signature.SelfSigned = true
	}
	jc.FingerprintMD5 = c.FingerprintMD5
	jc.FingerprintSHA1 = c.FingerprintSHA1
	jc.FingerprintSHA256 = c.FingerprintSHA256
	jc.SPKISubjectFingerprint = c.SPKISubjectFingerprint
	jc.TBSCertificateFingerprint = c.TBSCertificateFingerprint
	jc.ValidationLevel = c.ValidationLevel

	return json.Marshal(jc)
}
<|endoftext|>


<commit_before>package layouts

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"j4k.co/fmatter"
	"path/filepath"
	"sync"
)

type unexportedEntry struct {
	*template.Template
}

type Entry struct {
	unexportedEntry
	Name   string
	Path   string
	Parent string
}

// Group is a collection of template layouts.
type Group struct {
	dir   string
	tmpls *template.Template
	funcs template.FuncMap
	dict  map[string]Entry
	mu    sync.Mutex
}

func undefinedContent() interface{} {
	panic("content undefined")
}

// makeContentFunc makes a "content" func that is valid for exactly one call
func makeContentFunc(content template.HTML) interface{} {
	var fn func() interface{}
	fn = func() interface{} {
		fn = undefinedContent
		return content
	}
	return func() interface{} {
		return fn()
	}
}

// New returns a group with layouts relative to dir
func New(dir string) *Group {
	g := &Group{
		dir: dir,
	}
	g.Clear()
	return g
}

// SetPath sets a new path for layouts to be loaded from. This should be called
// before any layouts are loaded.
/*
func (g *Group) SetPath(dir string) {
	g.dir = dir
}
*/

func (g *Group) load(filename string) error {
	front := make(map[string]interface{}, 4)
	content, err := fmatter.ReadFile(filename, front)
	if err != nil {
		return err
	}

	path := filename
	filename = filepath.Base(filename)
	ext := filepath.Ext(filename)
	name := filename[:len(filename)-len(ext)]
	_, ok := g.dict[name]
	if ok {
		return nil
	}

	var t *template.Template
	if g.tmpls == nil {
		t = template.New(name)
		g.tmpls = t
		t.Funcs(template.FuncMap{
			"content": undefinedContent,
		})
		t.Funcs(g.funcs)
	} else {
		t = g.tmpls.New(name)
	}

	_, err = t.Parse(string(content))
	if err != nil {
		// hrm.. how do we remove a template..?
		return err
	}

	parent, _ := front["layout"].(string)
	g.dict[name] = Entry{unexportedEntry{t}, name, path, parent}
	return nil
}

// Files loads layouts by individual file names. Each layout's name
// comes from the file name without its extension.
func (g *Group) Files(files ...string) error {
	g.mu.Lock()
	defer g.mu.Unlock()
	for _, f := range files {
		f = filepath.Join(g.dir, f)
		err := g.load(f)
		if err != nil {
			return err
		}
	}
	return nil
}

// Glob loads layouts through pattern matching. Each layout's name
// comes from the file name without its extension.
func (g *Group) Glob(patterns ...string) error {
	files := make([]string, 0, 8)
	for _, pattern := range patterns {
		pattern = filepath.Join(g.dir, pattern)
		matches, err := filepath.Glob(pattern)
		if err != nil {
			return err
		}
		for _, m := range matches {
			m, err = filepath.Rel(g.dir, m)
			if err != nil {
				return err
			}
			if filepath.Base(m)[0] == '.' {
				continue
			}
			files = append(files, m)
		}
	}
	return g.Files(files...)
}

// Clear unloads all layouts.
func (g *Group) Clear() {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.dict = make(map[string]Entry)
	g.tmpls = nil
}

// Entries returns info about all loaded layouts.
func (g *Group) Entries() []Entry {
	list := make([]Entry, 0, len(g.dict))
	for _, e := range g.dict {
		list = append(list, e)
	}
	return list
}

func (g *Group) execute(w io.Writer, layout string, t *template.Template, data interface{}) error {
	l, ok := g.dict[layout]
	if !ok {
		return fmt.Errorf(`layouts: missing layout "%s"`, layout)
	}

	buf := bytes.NewBuffer(make([]byte, 0, 1024))
	err := t.Execute(buf, data)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(buf)
	if err != nil {
		return err
	}

	l.Funcs(template.FuncMap{
		"content": makeContentFunc(template.HTML(bytes.TrimSpace(b))),
	})

	if l.Parent != "" {
		return g.execute(w, l.Parent, l.Template, data)
	} else {
		err = l.Execute(w, data)
		if err != nil {
			return err
		}
	}
	return nil
}

func (g *Group) executeHTML(w io.Writer, layout string, content template.HTML, data interface{}) error {
	l, ok := g.dict[layout]
	if !ok {
		return fmt.Errorf(`layouts: missing layout "%s"`, layout)
	}

	l.Funcs(template.FuncMap{
		"content": makeContentFunc(content),
	})
	if l.Parent != "" {
		return g.execute(w, l.Parent, l.Template, data)
	} else {
		err := l.Execute(w, data)
		if err != nil {
			return err
		}
	}
	return nil
}

// Execute renders the specified template using the named layout,
// passing in data to the layout templates.
func (g *Group) Execute(w io.Writer, layout string, t *template.Template, data interface{}) error {
	g.mu.Lock()
	defer g.mu.Unlock()

	return g.execute(w, layout, t, data)
}

// ExecuteHTML renders the content string using the named layout,
// passing in data to the layout templates. Note that the content
// string is of type template.HTML; it is expected that the content
// string is safe, fully-escaped HTML.
func (g *Group) ExecuteHTML(w io.Writer, layout string, content template.HTML, data interface{}) error {
	g.mu.Lock()
	defer g.mu.Unlock()

	return g.executeHTML(w, layout, content, data)
}

// Funcs adds funcs to all layouts that are loaded. See template.Funcs in
// html/template. Note that any templates you pass in to Execute do not have
// these funcs applied.
func (g *Group) Funcs(f template.FuncMap) {
	if g.tmpls != nil {
		for _, t := range g.tmpls.Templates() {
			t.Funcs(f)
		}
	}
	if g.funcs == nil {
		g.funcs = template.FuncMap{}
	}
	for k, v := range f {
		g.funcs[k] = v
	}
}
<commit_msg>added locks to Entries and Funcs<commit_after>package layouts

import (
	"bytes"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"j4k.co/fmatter"
	"path/filepath"
	"sync"
)

type unexportedEntry struct {
	*template.Template
}

type Entry struct {
	unexportedEntry
	Name   string
	Path   string
	Parent string
}

// Group is a collection of template layouts.
type Group struct {
	dir   string
	tmpls *template.Template
	funcs template.FuncMap
	dict  map[string]Entry
	mu    sync.Mutex
}

func undefinedContent() interface{} {
	panic("content undefined")
}

// makeContentFunc makes a "content" func that is valid for exactly one call
func makeContentFunc(content template.HTML) interface{} {
	var fn func() interface{}
	fn = func() interface{} {
		fn = undefinedContent
		return content
	}
	return func() interface{} {
		return fn()
	}
}

// New returns a group with layouts relative to dir
func New(dir string) *Group {
	g := &Group{
		dir: dir,
	}
	g.Clear()
	return g
}

// SetPath sets a new path for layouts to be loaded from. This should be called
// before any layouts are loaded.
/*
func (g *Group) SetPath(dir string) {
	g.dir = dir
}
*/

func (g *Group) load(filename string) error {
	front := make(map[string]interface{}, 4)
	content, err := fmatter.ReadFile(filename, front)
	if err != nil {
		return err
	}

	path := filename
	filename = filepath.Base(filename)
	ext := filepath.Ext(filename)
	name := filename[:len(filename)-len(ext)]
	_, ok := g.dict[name]
	if ok {
		return nil
	}

	var t *template.Template
	if g.tmpls == nil {
		t = template.New(name)
		g.tmpls = t
		t.Funcs(template.FuncMap{
			"content": undefinedContent,
		})
		t.Funcs(g.funcs)
	} else {
		t = g.tmpls.New(name)
	}

	_, err = t.Parse(string(content))
	if err != nil {
		// hrm.. how do we remove a template..?
		return err
	}

	parent, _ := front["layout"].(string)
	g.dict[name] = Entry{unexportedEntry{t}, name, path, parent}
	return nil
}

// Files loads layouts by individual file names. Each layout's name
// comes from the file name without its extension.
func (g *Group) Files(files ...string) error {
	g.mu.Lock()
	defer g.mu.Unlock()
	for _, f := range files {
		f = filepath.Join(g.dir, f)
		err := g.load(f)
		if err != nil {
			return err
		}
	}
	return nil
}

// Glob loads layouts through pattern matching. Each layout's name
// comes from the file name without its extension.
func (g *Group) Glob(patterns ...string) error {
	files := make([]string, 0, 8)
	for _, pattern := range patterns {
		pattern = filepath.Join(g.dir, pattern)
		matches, err := filepath.Glob(pattern)
		if err != nil {
			return err
		}
		for _, m := range matches {
			m, err = filepath.Rel(g.dir, m)
			if err != nil {
				return err
			}
			if filepath.Base(m)[0] == '.' {
				continue
			}
			files = append(files, m)
		}
	}
	return g.Files(files...)
}

// Clear unloads all layouts.
func (g *Group) Clear() {
	g.mu.Lock()
	defer g.mu.Unlock()

	g.dict = make(map[string]Entry)
	g.tmpls = nil
}

// Entries returns info about all loaded layouts.
func (g *Group) Entries() []Entry {
	list := make([]Entry, 0, len(g.dict))
	g.mu.Lock()
	defer g.mu.Unlock()
	for _, e := range g.dict {
		list = append(list, e)
	}
	return list
}

func (g *Group) execute(w io.Writer, layout string, t *template.Template, data interface{}) error {
	l, ok := g.dict[layout]
	if !ok {
		return fmt.Errorf(`layouts: missing layout "%s"`, layout)
	}

	buf := bytes.NewBuffer(make([]byte, 0, 1024))
	err := t.Execute(buf, data)
	if err != nil {
		return err
	}

	b, err := ioutil.ReadAll(buf)
	if err != nil {
		return err
	}

	l.Funcs(template.FuncMap{
		"content": makeContentFunc(template.HTML(bytes.TrimSpace(b))),
	})

	if l.Parent != "" {
		return g.execute(w, l.Parent, l.Template, data)
	} else {
		err = l.Execute(w, data)
		if err != nil {
			return err
		}
	}
	return nil
}

func (g *Group) executeHTML(w io.Writer, layout string, content template.HTML, data interface{}) error {
	l, ok := g.dict[layout]
	if !ok {
		return fmt.Errorf(`layouts: missing layout "%s"`, layout)
	}

	l.Funcs(template.FuncMap{
		"content": makeContentFunc(content),
	})
	if l.Parent != "" {
		return g.execute(w, l.Parent, l.Template, data)
	} else {
		err := l.Execute(w, data)
		if err != nil {
			return err
		}
	}
	return nil
}

// Execute renders the specified template using the named layout,
// passing in data to the layout templates.
func (g *Group) Execute(w io.Writer, layout string, t *template.Template, data interface{}) error {
	g.mu.Lock()
	defer g.mu.Unlock()

	return g.execute(w, layout, t, data)
}

// ExecuteHTML renders the content string using the named layout,
// passing in data to the layout templates. Note that the content
// string is of type template.HTML; it is expected that the content
// string is safe, fully-escaped HTML.
func (g *Group) ExecuteHTML(w io.Writer, layout string, content template.HTML, data interface{}) error {
	g.mu.Lock()
	defer g.mu.Unlock()

	return g.executeHTML(w, layout, content, data)
}

// Funcs adds template funcs to all layouts that are loaded. See template.Funcs
// in html/template. Note that any templates you pass in to Execute do not have
// these funcs applied.
func (g *Group) Funcs(f template.FuncMap) {
	g.mu.Lock()
	defer g.mu.Unlock()
	if g.tmpls != nil {
		for _, t := range g.tmpls.Templates() {
			t.Funcs(f)
		}
	}
	if g.funcs == nil {
		g.funcs = template.FuncMap{}
	}
	for k, v := range f {
		g.funcs[k] = v
	}
}
<|endoftext|>


<commit_before>package storageconsul

import (
	"context"
	"net"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/caddyserver/certmagic"
	consul "github.com/hashicorp/consul/api"
	"github.com/pteich/errors"
	"go.uber.org/zap"
)

// ConsulStorage allows to store certificates and other TLS resources
// in a shared cluster environment using Consul's key/value-store.
// It uses distributed locks to ensure consistency.
type ConsulStorage struct {
	certmagic.Storage
	ConsulClient *consul.Client
	logger       *zap.SugaredLogger
	muLocks      sync.Mutex
	locks        map[string]*consul.Lock

	Address     string `json:"address"`
	Token       string `json:"token"`
	Timeout     int    `json:"timeout"`
	Prefix      string `json:"prefix"`
	ValuePrefix string `json:"value_prefix"`
	AESKey      []byte `json:"aes_key"`
	TlsEnabled  bool   `json:"tls_enabled"`
	TlsInsecure bool   `json:"tls_insecure"`
}

// New connects to Consul and returns a ConsulStorage
func New() *ConsulStorage {
	// create ConsulStorage and pre-set values
	s := ConsulStorage{
		locks:       make(map[string]*consul.Lock),
		AESKey:      []byte(DefaultAESKey),
		ValuePrefix: DefaultValuePrefix,
		Prefix:      DefaultPrefix,
		Timeout:     DefaultTimeout,
	}

	return &s
}

func (cs *ConsulStorage) prefixKey(key string) string {
	return path.Join(cs.Prefix, key)
}

// Lock acquires a distributed lock for the given key or blocks until it gets one
func (cs ConsulStorage) Lock(ctx context.Context, key string) error {
	cs.muLocks.Lock()
	defer cs.muLocks.Unlock()

	// if we already hold the lock, return early
	if _, exists := cs.locks[key]; exists {
		return nil
	}

	// prepare the lock
	lock, err := cs.ConsulClient.LockKey(cs.prefixKey(key))
	if err != nil {
		return errors.Wrapf(err, "could not create lock for %s", cs.prefixKey(key))
	}

	// acquire the lock and return a channel that is closed upon lost
	lockActive, err := lock.Lock(ctx.Done())
	if err != nil {
		return errors.Wrapf(err, "unable to lock %s", cs.prefixKey(key))
	}

	// auto-unlock and clean list of locks in case of lost
	go func() {
		<-lockActive
		cs.Unlock(key)
	}()

	// save the lock
	cs.muLocks.Lock()
	cs.locks[key] = lock
	cs.muLocks.Unlock()

	return nil
}

// Unlock releases a specific lock
func (cs ConsulStorage) Unlock(key string) error {
	cs.muLocks.Lock()
	defer cs.muLocks.Unlock()

	// check if we own it and unlock
	lock, exists := cs.locks[key]
	if !exists {
		return errors.Errorf("lock %s not found", cs.prefixKey(key))
	}

	err := lock.Unlock()
	if err != nil {
		return errors.Wrapf(err, "unable to unlock %s", cs.prefixKey(key))
	}

	delete(cs.locks, key)
	return nil
}

// Store saves encrypted data value for a key in Consul KV
func (cs ConsulStorage) Store(key string, value []byte) error {
	kv := &consul.KVPair{Key: cs.prefixKey(key)}

	// prepare the stored data
	consulData := &StorageData{
		Value:    value,
		Modified: time.Now(),
	}

	encryptedValue, err := cs.EncryptStorageData(consulData)
	if err != nil {
		return errors.Wrapf(err, "unable to encode data for %s", cs.prefixKey(key))
	}

	kv.Value = encryptedValue

	if _, err = cs.ConsulClient.KV().Put(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to store data for %s", cs.prefixKey(key))
	}

	return nil
}

// Load retrieves the value for a key from Consul KV
func (cs ConsulStorage) Load(key string) ([]byte, error) {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return nil, errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return nil, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to decrypt data for %s", cs.prefixKey(key))
	}

	return contents.Value, nil
}

// Delete a key from Consul KV
func (cs ConsulStorage) Delete(key string) error {
	// first obtain existing keypair
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.ErrNotExist(err)
	}

	// no do a Check-And-Set operation to verify we really deleted the key
	if success, _, err := cs.ConsulClient.KV().DeleteCAS(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to delete data for %s", cs.prefixKey(key))
	} else if !success {
		return errors.Errorf("failed to lock data delete for %s", cs.prefixKey(key))
	}

	return nil
}

// Exists checks if a key exists
func (cs ConsulStorage) Exists(key string) bool {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if kv != nil && err == nil {
		return true
	}
	return false
}

// List returns a list with all keys under a given prefix
func (cs ConsulStorage) List(prefix string, recursive bool) ([]string, error) {
	var keysFound []string

	// get a list of all keys at prefix
	keys, _, err := cs.ConsulClient.KV().Keys(cs.prefixKey(prefix), "", &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return keysFound, err
	}

	if len(keys) == 0 {
		return keysFound, certmagic.ErrNotExist(errors.Errorf("no keys at %s", prefix))
	}

	// remove default prefix from keys
	for _, key := range keys {
		if strings.HasPrefix(key, cs.prefixKey(prefix)) {
			key = strings.TrimPrefix(key, cs.Prefix+"/")
			keysFound = append(keysFound, key)
		}
	}

	// if recursive wanted, just return all keys
	if recursive {
		return keysFound, nil
	}

	// for non-recursive split path and look for unique keys just under given prefix
	keysMap := make(map[string]bool)
	for _, key := range keysFound {
		dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
		keysMap[dir[0]] = true
	}

	keysFound = make([]string, 0)
	for key := range keysMap {
		keysFound = append(keysFound, path.Join(prefix, key))
	}

	return keysFound, nil
}

// Stat returns statistic data of a key
func (cs ConsulStorage) Stat(key string) (certmagic.KeyInfo, error) {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.KeyInfo{}, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to decrypt data for %s", cs.prefixKey(key))
	}

	return certmagic.KeyInfo{
		Key:        key,
		Modified:   contents.Modified,
		Size:       int64(len(contents.Value)),
		IsTerminal: false,
	}, nil
}

func (cs *ConsulStorage) createConsulClient() error {
	// get the default config
	consulCfg := consul.DefaultConfig()
	if cs.Address != "" {
		consulCfg.Address = cs.Address
	}
	if cs.Token != "" {
		consulCfg.Token = cs.Token
	}
	if cs.TlsEnabled {
		consulCfg.Scheme = "https"
	}
	consulCfg.TLSConfig.InsecureSkipVerify = cs.TlsInsecure

	// set a dial context to prevent default keepalive
	consulCfg.Transport.DialContext = (&net.Dialer{
		Timeout:   time.Duration(cs.Timeout) * time.Second,
		KeepAlive: time.Duration(cs.Timeout) * time.Second,
	}).DialContext

	// create the Consul API client
	consulClient, err := consul.NewClient(consulCfg)
	if err != nil {
		return errors.Wrap(err, "unable to create Consul client")
	}
	if _, err := consulClient.Agent().NodeName(); err != nil {
		return errors.Wrap(err, "unable to ping Consul")
	}

	cs.ConsulClient = consulClient
	return nil
}
<commit_msg>Improve local locking code<commit_after>package storageconsul

import (
	"context"
	"net"
	"path"
	"strings"
	"sync"
	"time"

	"github.com/caddyserver/certmagic"
	consul "github.com/hashicorp/consul/api"
	"github.com/pteich/errors"
	"go.uber.org/zap"
)

// ConsulStorage allows to store certificates and other TLS resources
// in a shared cluster environment using Consul's key/value-store.
// It uses distributed locks to ensure consistency.
type ConsulStorage struct {
	certmagic.Storage
	ConsulClient *consul.Client
	logger       *zap.SugaredLogger
	muLocks      sync.RWMutex
	locks        map[string]*consul.Lock

	Address     string `json:"address"`
	Token       string `json:"token"`
	Timeout     int    `json:"timeout"`
	Prefix      string `json:"prefix"`
	ValuePrefix string `json:"value_prefix"`
	AESKey      []byte `json:"aes_key"`
	TlsEnabled  bool   `json:"tls_enabled"`
	TlsInsecure bool   `json:"tls_insecure"`
}

// New connects to Consul and returns a ConsulStorage
func New() *ConsulStorage {
	// create ConsulStorage and pre-set values
	s := ConsulStorage{
		locks:       make(map[string]*consul.Lock),
		AESKey:      []byte(DefaultAESKey),
		ValuePrefix: DefaultValuePrefix,
		Prefix:      DefaultPrefix,
		Timeout:     DefaultTimeout,
	}

	return &s
}

func (cs *ConsulStorage) prefixKey(key string) string {
	return path.Join(cs.Prefix, key)
}

// Lock acquires a distributed lock for the given key or blocks until it gets one
func (cs *ConsulStorage) Lock(ctx context.Context, key string) error {
	if cs.IsLocked(key) {
		return nil
	}

	// prepare the lock
	lock, err := cs.ConsulClient.LockKey(cs.prefixKey(key))
	if err != nil {
		return errors.Wrapf(err, "could not create lock for %s", cs.prefixKey(key))
	}

	// acquire the lock and return a channel that is closed upon lost
	lockActive, err := lock.Lock(ctx.Done())
	if err != nil {
		return errors.Wrapf(err, "unable to lock %s", cs.prefixKey(key))
	}

	// auto-unlock and clean list of locks in case of lost
	go func() {
		<-lockActive
		cs.Unlock(key)
	}()

	// save the lock
	cs.muLocks.Lock()
	cs.locks[key] = lock
	cs.muLocks.Unlock()

	return nil
}

func (cs *ConsulStorage) IsLocked(key string) bool {
	cs.muLocks.RLock()
	defer cs.muLocks.RUnlock()

	// if we already hold the lock, return early
	if _, exists := cs.locks[key]; exists {
		return true
	}

	return false
}

// Unlock releases a specific lock
func (cs *ConsulStorage) Unlock(key string) error {
	cs.muLocks.Lock()
	defer cs.muLocks.Unlock()

	// check if we own it and unlock
	lock, exists := cs.locks[key]
	if !exists {
		return errors.Errorf("lock %s not found", cs.prefixKey(key))
	}

	err := lock.Unlock()
	if err != nil {
		return errors.Wrapf(err, "unable to unlock %s", cs.prefixKey(key))
	}

	delete(cs.locks, key)
	return nil
}

// Store saves encrypted data value for a key in Consul KV
func (cs ConsulStorage) Store(key string, value []byte) error {
	kv := &consul.KVPair{Key: cs.prefixKey(key)}

	// prepare the stored data
	consulData := &StorageData{
		Value:    value,
		Modified: time.Now(),
	}

	encryptedValue, err := cs.EncryptStorageData(consulData)
	if err != nil {
		return errors.Wrapf(err, "unable to encode data for %s", cs.prefixKey(key))
	}

	kv.Value = encryptedValue

	if _, err = cs.ConsulClient.KV().Put(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to store data for %s", cs.prefixKey(key))
	}

	return nil
}

// Load retrieves the value for a key from Consul KV
func (cs ConsulStorage) Load(key string) ([]byte, error) {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return nil, errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return nil, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return nil, errors.Wrapf(err, "unable to decrypt data for %s", cs.prefixKey(key))
	}

	return contents.Value, nil
}

// Delete a key from Consul KV
func (cs ConsulStorage) Delete(key string) error {
	// first obtain existing keypair
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return errors.Wrapf(err, "unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.ErrNotExist(err)
	}

	// no do a Check-And-Set operation to verify we really deleted the key
	if success, _, err := cs.ConsulClient.KV().DeleteCAS(kv, nil); err != nil {
		return errors.Wrapf(err, "unable to delete data for %s", cs.prefixKey(key))
	} else if !success {
		return errors.Errorf("failed to lock data delete for %s", cs.prefixKey(key))
	}

	return nil
}

// Exists checks if a key exists
func (cs ConsulStorage) Exists(key string) bool {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if kv != nil && err == nil {
		return true
	}
	return false
}

// List returns a list with all keys under a given prefix
func (cs ConsulStorage) List(prefix string, recursive bool) ([]string, error) {
	var keysFound []string

	// get a list of all keys at prefix
	keys, _, err := cs.ConsulClient.KV().Keys(cs.prefixKey(prefix), "", &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return keysFound, err
	}

	if len(keys) == 0 {
		return keysFound, certmagic.ErrNotExist(errors.Errorf("no keys at %s", prefix))
	}

	// remove default prefix from keys
	for _, key := range keys {
		if strings.HasPrefix(key, cs.prefixKey(prefix)) {
			key = strings.TrimPrefix(key, cs.Prefix+"/")
			keysFound = append(keysFound, key)
		}
	}

	// if recursive wanted, just return all keys
	if recursive {
		return keysFound, nil
	}

	// for non-recursive split path and look for unique keys just under given prefix
	keysMap := make(map[string]bool)
	for _, key := range keysFound {
		dir := strings.Split(strings.TrimPrefix(key, prefix+"/"), "/")
		keysMap[dir[0]] = true
	}

	keysFound = make([]string, 0)
	for key := range keysMap {
		keysFound = append(keysFound, path.Join(prefix, key))
	}

	return keysFound, nil
}

// Stat returns statistic data of a key
func (cs ConsulStorage) Stat(key string) (certmagic.KeyInfo, error) {
	kv, _, err := cs.ConsulClient.KV().Get(cs.prefixKey(key), &consul.QueryOptions{RequireConsistent: true})
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to obtain data for %s", cs.prefixKey(key))
	} else if kv == nil {
		return certmagic.KeyInfo{}, certmagic.ErrNotExist(errors.Errorf("key %s does not exist", cs.prefixKey(key)))
	}

	contents, err := cs.DecryptStorageData(kv.Value)
	if err != nil {
		return certmagic.KeyInfo{}, errors.Errorf("unable to decrypt data for %s", cs.prefixKey(key))
	}

	return certmagic.KeyInfo{
		Key:        key,
		Modified:   contents.Modified,
		Size:       int64(len(contents.Value)),
		IsTerminal: false,
	}, nil
}

func (cs *ConsulStorage) createConsulClient() error {
	// get the default config
	consulCfg := consul.DefaultConfig()
	if cs.Address != "" {
		consulCfg.Address = cs.Address
	}
	if cs.Token != "" {
		consulCfg.Token = cs.Token
	}
	if cs.TlsEnabled {
		consulCfg.Scheme = "https"
	}
	consulCfg.TLSConfig.InsecureSkipVerify = cs.TlsInsecure

	// set a dial context to prevent default keepalive
	consulCfg.Transport.DialContext = (&net.Dialer{
		Timeout:   time.Duration(cs.Timeout) * time.Second,
		KeepAlive: time.Duration(cs.Timeout) * time.Second,
	}).DialContext

	// create the Consul API client
	consulClient, err := consul.NewClient(consulCfg)
	if err != nil {
		return errors.Wrap(err, "unable to create Consul client")
	}
	if _, err := consulClient.Agent().NodeName(); err != nil {
		return errors.Wrap(err, "unable to ping Consul")
	}

	cs.ConsulClient = consulClient
	return nil
}
<|endoftext|>