<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.

// Command memlat is a web-based interactive browser for memory
// latency profiles.
//
// Intel CPUs starting with Nehalem support detailed hardware
// profiling of memory load operations and, in particular, load
// latency. Sandy Bridge introduced further support for profiling
// memory store operations. Memory stalls and conflicts are
// increasingly impacting software performance and these profiles can
// give incredible insight into these problems. However, the richness
// of these profiles make them difficult to interpret using
// traditional profiling tools and techniques.
//
// memlat is a profile browser built for understanding and
// interpreting memory latency profiles. The central concept is a
// latency distribution, which indicates how many cycles are spent in
// memory operations according to the latency of those operations. For
// example, if there are 10 loads that take 10 cycles and 2 loads that
// takes 100 cycles, the latency distribution consists of a 100 cycle
// spike at 10 cycles and a 200 cycle spike at 100 cycles. The total
// weight of this distribution accounts for the total cycles spent
// waiting on memory loads.
//
// To download and install memlat, run
//
//    go get github.com/aclements/go-perf/cmd/memlat
//
// memlat works with the standard memory latency profiles recorded by
// the Linux perf tool. To record a memory latency profile, use perf's
// "mem" subcommand. For example,
//
//    perf mem record <command>  # Record a memory profile for command
//    perf mem record -a         # Record a system-wide memory profile
//
// This will write the profile to a file called perf.data. Then,
// simply start memlat with
//
//    memlat
//
// memlat will parse and symbolize the profile and start a web server
// listening by default on localhost;8001.
package main

// TODO: Document the user interface once it's settled.

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strconv"

	"github.com/aclements/go-moremath/scale"
	"github.com/aclements/go-moremath/vec"
)

//go:generate go run makestatic.go

// staticFiles is the static file tree baked in to the binary. This is
// assigned by an init function if the static files are available.
var staticFiles mapFS

// TODO: Open a browser automatically. Maybe bind to any address in
// this mode.

func main() {
	var (
		flagInput   = flag.String("i", "perf.data", "read memory latency profile from `file`")
		flagHttp    = flag.String("http", "localhost:8001", "serve HTTP on `address`")
		flagDocRoot = flag.String("docroot", "", "alternate `path` to static web resources")
	)
	flag.Parse()
	if flag.NArg() > 0 {
		flag.Usage()
		os.Exit(1)
	}

	fmt.Fprintln(os.Stderr, "loading profile...")
	db := parsePerf(*flagInput)
	fmt.Fprintln(os.Stderr, "profile loaded")

	mux := http.NewServeMux()
	if *flagDocRoot == "" && staticFiles == nil {
		// No baked-in static file system.
		*flagDocRoot = "static"
	}
	if *flagDocRoot == "" {
		mux.Handle("/", http.FileServer(staticFiles))
	} else {
		mux.Handle("/", http.FileServer(http.Dir(*flagDocRoot)))
	}
	mux.Handle("/h", &heatMapHandler{db})
	mux.Handle("/metadata", &metadataHandler{*flagInput, db.metadata})

	fmt.Fprintf(os.Stderr, "serving on %s\n", *flagHttp)
	if err := http.ListenAndServe(*flagHttp, mux); err != nil {
		log.Fatal(err)
	}
}

type heatMapHandler struct {
	db *database
}

func (h *heatMapHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// TOOD: Include a signature for this profile in the request
	// and mark the response as cacheable.

	// TODO: Compress the output.

	// Request includes filter, group by. Response: map from group
	// by to histograms.
	qs, err := url.ParseQuery(req.URL.RawQuery)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	atoi := func(s string) int {
		x, _ := strconv.Atoi(s)
		return x
	}
	f := filter{
		pid:      atoi(qs.Get("pid")),
		funcName: qs.Get("funcName"),
		fileName: qs.Get("fileName"),
		line:     atoi(qs.Get("line")),
		address:  uint64(atoi(qs.Get("address"))),
	}
	groupBy := qs.Get("groupBy")
	limit := atoi(qs.Get("limit"))

	// Compute the scale for this histogram set.
	const useLocalScale = false
	var maxLatency uint32 = 1
	if useLocalScale {
		h.db.filter(&f, func(p *proc, rec *record) {
			if rec.latency > maxLatency {
				maxLatency = rec.latency
			}
		})
	} else {
		maxLatency = h.db.maxLatency
	}
	scale, err := scale.NewLog(1, float64(maxLatency), 10)
	if err != nil {
		log.Fatal(err)
	}
	scale.Nice(6)

	var histograms []*latencyHistogram
	newHist := func() *latencyHistogram {
		hist := newLatencyHistogram(&scale)
		histograms = append(histograms, hist)
		return hist
	}

	// Create aggregation function.
	var agg func(*proc, *record)
	switch groupBy {
	default:
		http.Error(w, fmt.Sprintf("unknown groupby %q", groupBy), http.StatusBadRequest)
		return

	case "all":
		hist := newHist()
		agg = func(p *proc, r *record) {
			hist.update(r)

		}

	case "pid":
		groups := make(map[*proc]*latencyHistogram)
		agg = func(p *proc, r *record) {
			hist, ok := groups[p]
			if !ok {
				hist = newHist()
				hist.PID = p.pid
				hist.Comm = p.comm
				groups[p] = hist
			}
			hist.update(r)
		}

	case "funcName":
		groups := make(map[string]*latencyHistogram)
		agg = func(p *proc, r *record) {
			funcName := p.ipInfo[r.ip].funcName
			hist, ok := groups[funcName]
			if !ok {
				hist = newHist()
				hist.FuncName = funcName
				groups[funcName] = hist
			}
			hist.update(r)
		}

	case "annotation", "line":
		groups := make(map[ipInfo]*latencyHistogram)
		agg = func(p *proc, r *record) {
			ipInfo := p.ipInfo[r.ip]
			hist, ok := groups[ipInfo]
			if !ok {
				hist = newHist()
				hist.FileName = ipInfo.fileName
				hist.Line = ipInfo.line
				groups[ipInfo] = hist
			}
			hist.update(r)
		}

	case "address":
		groups := make(map[uint64]*latencyHistogram)
		agg = func(p *proc, r *record) {
			hist, ok := groups[r.address]
			if !ok {
				hist = newHist()
				hist.Address = r.address
				groups[r.address] = hist
			}
			hist.update(r)
		}
	}

	h.db.filter(&f, agg)

	if groupBy != "annotation" {
		// Sort histograms by weight.
		sort.Sort(sort.Reverse(weightSorter(histograms)))

		if limit != 0 && limit < len(histograms) {
			histograms = histograms[:limit]
		}
	} else if len(histograms) != 0 {
		// Find the min and max covered line.
		fileName := histograms[0].FileName
		minLine := histograms[0].Line
		maxLine := minLine
		haveLines := map[int]*latencyHistogram{}
		for _, h := range histograms {
			if h.FileName != fileName {
				continue
			}
			if h.Line < minLine {
				minLine = h.Line
			}
			if h.Line > maxLine {
				maxLine = h.Line
			}
			haveLines[h.Line] = h
		}

		// Read source lines and create empty histograms for
		// lines we missed.
		lines, err := getLines(fileName, minLine, maxLine)
		if err != nil {
			log.Println(err)
			// Keep going
		}
		for line := minLine; line <= maxLine; line++ {
			hist := haveLines[line]
			if hist == nil {
				hist = newHist()
				hist.FileName = fileName
				hist.Line = line
				hist.Bins = nil
			}
			hist.Text = lines[line-minLine]
		}

		// Sort histograms by line number.
		sort.Sort(lineSorter(histograms))
	}

	// Compute maximum bin size for bin scaling.
	maxBin := 0
	for _, hist := range histograms {
		max := hist.max()
		if max > maxBin {
			maxBin = max
		}
	}

	// Construct JSON reply.
	major, minor := scale.Ticks(6)
	majorX, minorX := vec.Map(scale.Map, major), vec.Map(scale.Map, minor)
	err = json.NewEncoder(w).Encode(struct {
		Histograms []*latencyHistogram
		MaxBin     int

		MajorTicks, MajorTicksX []float64
		MinorTicksX             []float64
	}{histograms, maxBin, major, majorX, minorX})
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

const latencyHistogramBins = 60

type latencyHistogram struct {
	scale  scale.Quantitative
	Bins   []int `json:",omitempty"`
	weight int

	PID      int    `json:"pid,omitempty"`
	Comm     string `json:"comm,omitempty"`
	FuncName string `json:"funcName,omitempty"`
	FileName string `json:"fileName,omitempty"`
	Line     int    `json:"line,omitempty"`
	Address  uint64 `json:"address,omitempty"`
	Text     string `json:"text,omitempty"`
}

func newLatencyHistogram(scale scale.Quantitative) *latencyHistogram {
	return &latencyHistogram{
		scale:  scale,
		Bins:   make([]int, latencyHistogramBins),
		weight: 0,
	}
}

func (h *latencyHistogram) update(r *record) {
	bin := int(h.scale.Map(float64(r.latency)) * latencyHistogramBins)
	if bin < 0 {
		bin = 0
	}
	if bin >= latencyHistogramBins {
		bin = latencyHistogramBins
	}
	h.Bins[bin] += int(r.latency)
	h.weight += int(r.latency)
}

func (h *latencyHistogram) max() int {
	out := 0
	for _, count := range h.Bins {
		if count > out {
			out = count
		}
	}
	return out
}

type weightSorter []*latencyHistogram

func (w weightSorter) Len() int {
	return len(w)
}

func (w weightSorter) Less(i, j int) bool {
	return w[i].weight < w[j].weight
}

func (w weightSorter) Swap(i, j int) {
	w[i], w[j] = w[j], w[i]
}

type lineSorter []*latencyHistogram

func (s lineSorter) Len() int {
	return len(s)
}

func (s lineSorter) Less(i, j int) bool {
	return s[i].Line < s[j].Line
}

func (s lineSorter) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func getLines(path string, minLine, maxLine int) ([]string, error) {
	lines := make([]string, maxLine-minLine+1)

	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// Skip to minLine.
	scanner := bufio.NewScanner(file)
	for i := 0; i < minLine && scanner.Scan(); i++ {
		// Do nothing
	}

	for line := minLine; line <= maxLine && scanner.Err() == nil; line++ {
		lines[line-minLine] = scanner.Text()
		scanner.Scan()
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return lines, nil

}

type metadataHandler struct {
	Filename string
	Metadata
}

func (h *metadataHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	err := json.NewEncoder(w).Encode(h)
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<commit_msg>cmd/memlat: TODO to document the UI<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.

// Command memlat is a web-based interactive browser for memory
// latency profiles.
//
// Intel CPUs starting with Nehalem support detailed hardware
// profiling of memory load operations and, in particular, load
// latency. Sandy Bridge introduced further support for profiling
// memory store operations. Memory stalls and conflicts are
// increasingly impacting software performance and these profiles can
// give incredible insight into these problems. However, the richness
// of these profiles make them difficult to interpret using
// traditional profiling tools and techniques.
//
// memlat is a profile browser built for understanding and
// interpreting memory latency profiles. The central concept is a
// latency distribution, which indicates how many cycles are spent in
// memory operations according to the latency of those operations. For
// example, if there are 10 loads that take 10 cycles and 2 loads that
// takes 100 cycles, the latency distribution consists of a 100 cycle
// spike at 10 cycles and a 200 cycle spike at 100 cycles. The total
// weight of this distribution accounts for the total cycles spent
// waiting on memory loads.
//
// To download and install memlat, run
//
//    go get github.com/aclements/go-perf/cmd/memlat
//
// memlat works with the standard memory latency profiles recorded by
// the Linux perf tool. To record a memory latency profile, use perf's
// "mem" subcommand. For example,
//
//    perf mem record <command>  # Record a memory profile for command
//    perf mem record -a         # Record a system-wide memory profile
//
// This will write the profile to a file called perf.data. Then,
// simply start memlat with
//
//    memlat
//
// memlat will parse and symbolize the profile and start a web server
// listening by default on localhost;8001.
//
// TODO: Document the user interface once it's settled.
package main

import (
	"bufio"
	"encoding/json"
	"flag"
	"fmt"
	"log"
	"net/http"
	"net/url"
	"os"
	"sort"
	"strconv"

	"github.com/aclements/go-moremath/scale"
	"github.com/aclements/go-moremath/vec"
)

//go:generate go run makestatic.go

// staticFiles is the static file tree baked in to the binary. This is
// assigned by an init function if the static files are available.
var staticFiles mapFS

// TODO: Open a browser automatically. Maybe bind to any address in
// this mode.

func main() {
	var (
		flagInput   = flag.String("i", "perf.data", "read memory latency profile from `file`")
		flagHttp    = flag.String("http", "localhost:8001", "serve HTTP on `address`")
		flagDocRoot = flag.String("docroot", "", "alternate `path` to static web resources")
	)
	flag.Parse()
	if flag.NArg() > 0 {
		flag.Usage()
		os.Exit(1)
	}

	fmt.Fprintln(os.Stderr, "loading profile...")
	db := parsePerf(*flagInput)
	fmt.Fprintln(os.Stderr, "profile loaded")

	mux := http.NewServeMux()
	if *flagDocRoot == "" && staticFiles == nil {
		// No baked-in static file system.
		*flagDocRoot = "static"
	}
	if *flagDocRoot == "" {
		mux.Handle("/", http.FileServer(staticFiles))
	} else {
		mux.Handle("/", http.FileServer(http.Dir(*flagDocRoot)))
	}
	mux.Handle("/h", &heatMapHandler{db})
	mux.Handle("/metadata", &metadataHandler{*flagInput, db.metadata})

	fmt.Fprintf(os.Stderr, "serving on %s\n", *flagHttp)
	if err := http.ListenAndServe(*flagHttp, mux); err != nil {
		log.Fatal(err)
	}
}

type heatMapHandler struct {
	db *database
}

func (h *heatMapHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	// TOOD: Include a signature for this profile in the request
	// and mark the response as cacheable.

	// TODO: Compress the output.

	// Request includes filter, group by. Response: map from group
	// by to histograms.
	qs, err := url.ParseQuery(req.URL.RawQuery)
	if err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}
	atoi := func(s string) int {
		x, _ := strconv.Atoi(s)
		return x
	}
	f := filter{
		pid:      atoi(qs.Get("pid")),
		funcName: qs.Get("funcName"),
		fileName: qs.Get("fileName"),
		line:     atoi(qs.Get("line")),
		address:  uint64(atoi(qs.Get("address"))),
	}
	groupBy := qs.Get("groupBy")
	limit := atoi(qs.Get("limit"))

	// Compute the scale for this histogram set.
	const useLocalScale = false
	var maxLatency uint32 = 1
	if useLocalScale {
		h.db.filter(&f, func(p *proc, rec *record) {
			if rec.latency > maxLatency {
				maxLatency = rec.latency
			}
		})
	} else {
		maxLatency = h.db.maxLatency
	}
	scale, err := scale.NewLog(1, float64(maxLatency), 10)
	if err != nil {
		log.Fatal(err)
	}
	scale.Nice(6)

	var histograms []*latencyHistogram
	newHist := func() *latencyHistogram {
		hist := newLatencyHistogram(&scale)
		histograms = append(histograms, hist)
		return hist
	}

	// Create aggregation function.
	var agg func(*proc, *record)
	switch groupBy {
	default:
		http.Error(w, fmt.Sprintf("unknown groupby %q", groupBy), http.StatusBadRequest)
		return

	case "all":
		hist := newHist()
		agg = func(p *proc, r *record) {
			hist.update(r)

		}

	case "pid":
		groups := make(map[*proc]*latencyHistogram)
		agg = func(p *proc, r *record) {
			hist, ok := groups[p]
			if !ok {
				hist = newHist()
				hist.PID = p.pid
				hist.Comm = p.comm
				groups[p] = hist
			}
			hist.update(r)
		}

	case "funcName":
		groups := make(map[string]*latencyHistogram)
		agg = func(p *proc, r *record) {
			funcName := p.ipInfo[r.ip].funcName
			hist, ok := groups[funcName]
			if !ok {
				hist = newHist()
				hist.FuncName = funcName
				groups[funcName] = hist
			}
			hist.update(r)
		}

	case "annotation", "line":
		groups := make(map[ipInfo]*latencyHistogram)
		agg = func(p *proc, r *record) {
			ipInfo := p.ipInfo[r.ip]
			hist, ok := groups[ipInfo]
			if !ok {
				hist = newHist()
				hist.FileName = ipInfo.fileName
				hist.Line = ipInfo.line
				groups[ipInfo] = hist
			}
			hist.update(r)
		}

	case "address":
		groups := make(map[uint64]*latencyHistogram)
		agg = func(p *proc, r *record) {
			hist, ok := groups[r.address]
			if !ok {
				hist = newHist()
				hist.Address = r.address
				groups[r.address] = hist
			}
			hist.update(r)
		}
	}

	h.db.filter(&f, agg)

	if groupBy != "annotation" {
		// Sort histograms by weight.
		sort.Sort(sort.Reverse(weightSorter(histograms)))

		if limit != 0 && limit < len(histograms) {
			histograms = histograms[:limit]
		}
	} else if len(histograms) != 0 {
		// Find the min and max covered line.
		fileName := histograms[0].FileName
		minLine := histograms[0].Line
		maxLine := minLine
		haveLines := map[int]*latencyHistogram{}
		for _, h := range histograms {
			if h.FileName != fileName {
				continue
			}
			if h.Line < minLine {
				minLine = h.Line
			}
			if h.Line > maxLine {
				maxLine = h.Line
			}
			haveLines[h.Line] = h
		}

		// Read source lines and create empty histograms for
		// lines we missed.
		lines, err := getLines(fileName, minLine, maxLine)
		if err != nil {
			log.Println(err)
			// Keep going
		}
		for line := minLine; line <= maxLine; line++ {
			hist := haveLines[line]
			if hist == nil {
				hist = newHist()
				hist.FileName = fileName
				hist.Line = line
				hist.Bins = nil
			}
			hist.Text = lines[line-minLine]
		}

		// Sort histograms by line number.
		sort.Sort(lineSorter(histograms))
	}

	// Compute maximum bin size for bin scaling.
	maxBin := 0
	for _, hist := range histograms {
		max := hist.max()
		if max > maxBin {
			maxBin = max
		}
	}

	// Construct JSON reply.
	major, minor := scale.Ticks(6)
	majorX, minorX := vec.Map(scale.Map, major), vec.Map(scale.Map, minor)
	err = json.NewEncoder(w).Encode(struct {
		Histograms []*latencyHistogram
		MaxBin     int

		MajorTicks, MajorTicksX []float64
		MinorTicksX             []float64
	}{histograms, maxBin, major, majorX, minorX})
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}

const latencyHistogramBins = 60

type latencyHistogram struct {
	scale  scale.Quantitative
	Bins   []int `json:",omitempty"`
	weight int

	PID      int    `json:"pid,omitempty"`
	Comm     string `json:"comm,omitempty"`
	FuncName string `json:"funcName,omitempty"`
	FileName string `json:"fileName,omitempty"`
	Line     int    `json:"line,omitempty"`
	Address  uint64 `json:"address,omitempty"`
	Text     string `json:"text,omitempty"`
}

func newLatencyHistogram(scale scale.Quantitative) *latencyHistogram {
	return &latencyHistogram{
		scale:  scale,
		Bins:   make([]int, latencyHistogramBins),
		weight: 0,
	}
}

func (h *latencyHistogram) update(r *record) {
	bin := int(h.scale.Map(float64(r.latency)) * latencyHistogramBins)
	if bin < 0 {
		bin = 0
	}
	if bin >= latencyHistogramBins {
		bin = latencyHistogramBins
	}
	h.Bins[bin] += int(r.latency)
	h.weight += int(r.latency)
}

func (h *latencyHistogram) max() int {
	out := 0
	for _, count := range h.Bins {
		if count > out {
			out = count
		}
	}
	return out
}

type weightSorter []*latencyHistogram

func (w weightSorter) Len() int {
	return len(w)
}

func (w weightSorter) Less(i, j int) bool {
	return w[i].weight < w[j].weight
}

func (w weightSorter) Swap(i, j int) {
	w[i], w[j] = w[j], w[i]
}

type lineSorter []*latencyHistogram

func (s lineSorter) Len() int {
	return len(s)
}

func (s lineSorter) Less(i, j int) bool {
	return s[i].Line < s[j].Line
}

func (s lineSorter) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func getLines(path string, minLine, maxLine int) ([]string, error) {
	lines := make([]string, maxLine-minLine+1)

	file, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer file.Close()

	// Skip to minLine.
	scanner := bufio.NewScanner(file)
	for i := 0; i < minLine && scanner.Scan(); i++ {
		// Do nothing
	}

	for line := minLine; line <= maxLine && scanner.Err() == nil; line++ {
		lines[line-minLine] = scanner.Text()
		scanner.Scan()
	}
	if err := scanner.Err(); err != nil {
		return nil, err
	}

	return lines, nil

}

type metadataHandler struct {
	Filename string
	Metadata
}

func (h *metadataHandler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
	err := json.NewEncoder(w).Encode(h)
	if err != nil {
		log.Print(err)
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<|endoftext|>


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

package main

import (
	"flag"
	"log"
	"net"

	"github.com/googleinterns/cloud-operations-api-mock/server/metric"
	"github.com/googleinterns/cloud-operations-api-mock/server/trace"

	"google.golang.org/genproto/googleapis/devtools/cloudtrace/v2"
	"google.golang.org/genproto/googleapis/monitoring/v3"
	"google.golang.org/grpc"
)

const (
	defaultAddress = "localhost:8080"
)

var (
	address = flag.String("address", defaultAddress,
		"The address to run the server on, of the form <host>:<port>.")
)

func main() {
	flag.Parse()
	startStandaloneServer()
}

func startStandaloneServer() {
	lis, err := net.Listen("tcp", *address)
	if err != nil {
		log.Fatalf("mock server failed to listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	cloudtrace.RegisterTraceServiceServer(grpcServer, &trace.MockTraceServer{})
	monitoring.RegisterMetricServiceServer(grpcServer, &metric.MockMetricServer{})

	log.Printf("Listening on %s\n", lis.Addr().String())

	if err := grpcServer.Serve(lis); err != nil {
		log.Fatalf("mock server failed to serve: %v", err)
	}
}
<commit_msg>Add interrupt handler for standalone server<commit_after>// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"flag"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"

	"github.com/googleinterns/cloud-operations-api-mock/server/metric"
	"github.com/googleinterns/cloud-operations-api-mock/server/trace"

	"google.golang.org/genproto/googleapis/devtools/cloudtrace/v2"
	"google.golang.org/genproto/googleapis/monitoring/v3"
	"google.golang.org/grpc"
)

const (
	defaultAddress = "localhost:8080"
)

var (
	address = flag.String("address", defaultAddress,
		"The address to run the server on, of the form <host>:<port>.")
)

func main() {
	flag.Parse()
	startStandaloneServer()
}

func startStandaloneServer() {
	lis, err := net.Listen("tcp", *address)
	if err != nil {
		log.Fatalf("mock server failed to listen: %v", err)
	}

	grpcServer := grpc.NewServer()
	cloudtrace.RegisterTraceServiceServer(grpcServer, &trace.MockTraceServer{})
	monitoring.RegisterMetricServiceServer(grpcServer, &metric.MockMetricServer{})

	log.Printf("Listening on %s\n", lis.Addr().String())

	sig := make(chan os.Signal)
	signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
	go func() {
		<-sig
		grpcServer.GracefulStop()
	}()

	if err := grpcServer.Serve(lis); err != nil {
		log.Fatalf("mock server failed to serve: %v", err)
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"

	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitconfig "github.com/tcnksm/go-gitconfig"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/git"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

// mrCheckoutConfig holds configuration values for calls to lab mr checkout
type mrCheckoutConfig struct {
	branch string
	track  bool
}

var (
	mrCheckoutCfg mrCheckoutConfig
)

// listCmd represents the list command
var checkoutCmd = &cobra.Command{
	Use:              "checkout <id>",
	Short:            "Checkout an open merge request",
	Long:             ``,
	Args:             cobra.MinimumNArgs(1),
	PersistentPreRun: LabPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		rn, mrID, err := parseArgs(args)
		if err != nil {
			log.Fatal(err)
		}

		mrs, err := lab.MRList(rn, gitlab.ListProjectMergeRequestsOptions{
			IIDs: []int{int(mrID)},
		}, 1)
		if err != nil {
			log.Fatal(err)
		}
		if len(mrs) < 1 {
			fmt.Printf("MR #%d not found\n", mrID)
			return
		}

		mr := mrs[0]
		// If the config does not specify a branch, use the mr source branch name
		if mrCheckoutCfg.branch == "" {
			mrCheckoutCfg.branch = mr.SourceBranch
		}
		// By default, fetch to configured branch
		fetchToRef := mrCheckoutCfg.branch

		// If track, make sure we have a remote for the mr author and then set
		// the fetchToRef to the mr author/sourceBranch
		if mrCheckoutCfg.track {
			// Check if remote already exists
			if _, err := gitconfig.Local("remote." + mr.Author.Username + ".url"); err != nil {
				// Find and create remote
				mrProject, err := lab.GetProject(mr.SourceProjectID)
				if err != nil {
					log.Fatal(err)
				}
				if err := git.RemoteAdd(mr.Author.Username, mrProject.SSHURLToRepo, "."); err != nil {
					log.Fatal(err)
				}
			}
			fetchToRef = fmt.Sprintf("refs/remotes/%s/%s", mr.Author.Username, mr.SourceBranch)
		}

		// https://docs.gitlab.com/ce/user/project/merge_requests/#checkout-merge-requests-locally
		mrRef := fmt.Sprintf("refs/merge-requests/%d/head", mrID)
		fetchRefSpec := fmt.Sprintf("%s:%s", mrRef, fetchToRef)
		if err := git.New("fetch", forkedFromRemote, fetchRefSpec).Run(); err != nil {
			log.Fatal(err)
		}

		if mrCheckoutCfg.track {
			// Create configured branch with tracking from fetchToRef
			// git branch --flags <branchname> [<start-point>]
			if err := git.New("branch", "--track", mrCheckoutCfg.branch, fetchToRef).Run(); err != nil {
				log.Fatal(err)
			}
		}

		// Check out branch
		if err := git.New("checkout", mrCheckoutCfg.branch).Run(); err != nil {
			log.Fatal(err)
		}
	},
}

func init() {
	checkoutCmd.Flags().StringVarP(&mrCheckoutCfg.branch, "branch", "b", "", "checkout merge request with <branch> name")
	checkoutCmd.Flags().BoolVarP(&mrCheckoutCfg.track, "track", "t", false, "set checked out branch to track mr author remote branch, adds remote if needed")
	mrCmd.AddCommand(checkoutCmd)
	carapace.Gen(checkoutCmd).PositionalCompletion(
		carapace.ActionCallback(func(args []string) carapace.Action {
			return action.MergeRequests(mrList).Callback([]string{"origin"})
		}),
	)
}
<commit_msg>fix: (mr checkout) override target remote when passed as arg<commit_after>package cmd

import (
	"fmt"
	"log"

	"github.com/rsteube/carapace"
	"github.com/spf13/cobra"
	gitconfig "github.com/tcnksm/go-gitconfig"
	gitlab "github.com/xanzy/go-gitlab"
	"github.com/zaquestion/lab/internal/action"
	"github.com/zaquestion/lab/internal/git"
	lab "github.com/zaquestion/lab/internal/gitlab"
)

// mrCheckoutConfig holds configuration values for calls to lab mr checkout
type mrCheckoutConfig struct {
	branch string
	track  bool
}

var (
	mrCheckoutCfg mrCheckoutConfig
)

// listCmd represents the list command
var checkoutCmd = &cobra.Command{
	Use:              "checkout [remote] <id>",
	Short:            "Checkout an open merge request",
	Long:             ``,
	Args:             cobra.RangeArgs(1, 2),
	PersistentPreRun: LabPersistentPreRun,
	Run: func(cmd *cobra.Command, args []string) {
		rn, mrID, err := parseArgs(args)
		if err != nil {
			log.Fatal(err)
		}
		var targetRemote = forkedFromRemote
		if len(args) == 2 {
			// parseArgs above already validated this is a remote
			targetRemote = args[0]
		}

		mrs, err := lab.MRList(rn, gitlab.ListProjectMergeRequestsOptions{
			IIDs: []int{int(mrID)},
		}, 1)
		if err != nil {
			log.Fatal(err)
		}
		if len(mrs) < 1 {
			fmt.Printf("MR #%d not found\n", mrID)
			return
		}

		mr := mrs[0]
		// If the config does not specify a branch, use the mr source branch name
		if mrCheckoutCfg.branch == "" {
			mrCheckoutCfg.branch = mr.SourceBranch
		}
		// By default, fetch to configured branch
		fetchToRef := mrCheckoutCfg.branch

		// If track, make sure we have a remote for the mr author and then set
		// the fetchToRef to the mr author/sourceBranch
		if mrCheckoutCfg.track {
			// Check if remote already exists
			if _, err := gitconfig.Local("remote." + mr.Author.Username + ".url"); err != nil {
				// Find and create remote
				mrProject, err := lab.GetProject(mr.SourceProjectID)
				if err != nil {
					log.Fatal(err)
				}
				if err := git.RemoteAdd(mr.Author.Username, mrProject.SSHURLToRepo, "."); err != nil {
					log.Fatal(err)
				}
			}
			fetchToRef = fmt.Sprintf("refs/remotes/%s/%s", mr.Author.Username, mr.SourceBranch)
		}

		// https://docs.gitlab.com/ce/user/project/merge_requests/#checkout-merge-requests-locally
		mrRef := fmt.Sprintf("refs/merge-requests/%d/head", mrID)
		fetchRefSpec := fmt.Sprintf("%s:%s", mrRef, fetchToRef)
		if err := git.New("fetch", targetRemote, fetchRefSpec).Run(); err != nil {
			log.Fatal(err)
		}

		if mrCheckoutCfg.track {
			// Create configured branch with tracking from fetchToRef
			// git branch --flags <branchname> [<start-point>]
			if err := git.New("branch", "--track", mrCheckoutCfg.branch, fetchToRef).Run(); err != nil {
				log.Fatal(err)
			}
		}

		// Check out branch
		if err := git.New("checkout", mrCheckoutCfg.branch).Run(); err != nil {
			log.Fatal(err)
		}
	},
}

func init() {
	checkoutCmd.Flags().StringVarP(&mrCheckoutCfg.branch, "branch", "b", "", "checkout merge request with <branch> name")
	checkoutCmd.Flags().BoolVarP(&mrCheckoutCfg.track, "track", "t", false, "set checked out branch to track mr author remote branch, adds remote if needed")
	mrCmd.AddCommand(checkoutCmd)
	carapace.Gen(checkoutCmd).PositionalCompletion(
		carapace.ActionCallback(func(args []string) carapace.Action {
			return action.MergeRequests(mrList).Callback([]string{"origin"})
		}),
	)
}
<|endoftext|>


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

// Regress is a tool to display build and runtime statistics over a range of
// changelists.
package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"text/tabwriter"
	"time"

	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/git"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/shell"
)

var (
	root      = flag.String("root", "", "Path to the root GAPID source directory")
	verbose   = flag.Bool("verbose", false, "Verbose logging")
	incBuild  = flag.Bool("inc", true, "Time incremental builds")
	optimize  = flag.Bool("optimize", false, "Build using '-c opt'")
	pkg       = flag.String("pkg", "", "Partial name of a package name to capture")
	atSHA     = flag.String("at", "", "The SHA or branch of the first changelist to profile")
	count     = flag.Int("count", 2, "The number of changelists to profile since HEAD")
	tracePath = flag.String("trace", "", "Path to a .gfxtrace used for report timing")
)

func main() {
	app.ShortHelp = "Regress is a tool to perform performance measurments over a range of CLs."
	app.Run(run)
}

type stats struct {
	SHA                  string   `name:"sha"`
	IncrementalBuildTime float64  `name:"incremental-build"` // in seconds
	FileSizes            struct { // in bytes
		LibGAPII                   int `name:"libgapii"`
		LibVkLayerVirtualSwapchain int `name:"libVkLayer_VirtualSwapchain"`
		GAPIDAarch64APK            int `name:"gapid-aarch64"`
		GAPIDArmeabi64APK          int `name:"gapid-armeabi"`
		GAPIDX86APK                int `name:"gapid-x86"`
		GAPID                      int `name:"gapid"`
		GAPIR                      int `name:"gapir"`
		GAPIS                      int `name:"gapis"`
		GAPIT                      int `name:"gapit"`
	}
	CaptureStats struct {
		Frames   int `name:"frames"`
		Draws    int `name:"draws"`
		Commands int `name:"commands"`
	}
	ReportStats struct {
		Time float64 `name:"report-time"` // in seconds
	}
}

func run(ctx context.Context) error {
	if *root == "" {
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		*root = wd
	}

	g, err := git.New(*root)
	if err != nil {
		return err
	}
	s, err := g.Status(ctx)
	if err != nil {
		return err
	}
	if !s.Clean() {
		return fmt.Errorf("Local changes found. Please submit any changes and run again")
	}

	branch, err := g.CurrentBranch(ctx)
	if err != nil {
		return err
	}

	defer g.CheckoutBranch(ctx, branch)

	cls, err := g.LogFrom(ctx, *atSHA, *count)
	if err != nil {
		return err
	}

	rnd := rand.New(rand.NewSource(time.Now().Unix()))

	res := []stats{}
	for i := range cls {
		i := len(cls) - 1 - i
		cl := cls[i]
		sha := cl.SHA.String()[:6]

		r := stats{SHA: sha}

		log.I(ctx, "HEAD~%.2d: Building at %v: %v", i, sha, cl.Subject)
		if err := g.Checkout(ctx, cl.SHA); err != nil {
			return err
		}

		_, err := build(ctx)
		if err != nil {
			continue
		}

		// Gather file size build stats
		pkgDir := filepath.Join(*root, "bazel-bin", "pkg")
		for _, f := range []struct {
			path string
			size *int
		}{
			{filepath.Join(pkgDir, "lib", dllExt("libgapii")), &r.FileSizes.LibGAPII},
			{filepath.Join(pkgDir, "lib", dllExt("libVkLayer_VirtualSwapchain")), &r.FileSizes.LibVkLayerVirtualSwapchain},
			{filepath.Join(pkgDir, "gapid-aarch64.apk"), &r.FileSizes.GAPIDAarch64APK},
			{filepath.Join(pkgDir, "gapid-armeabi.apk"), &r.FileSizes.GAPIDArmeabi64APK},
			{filepath.Join(pkgDir, "gapid-x86.apk"), &r.FileSizes.GAPIDX86APK},
			{filepath.Join(pkgDir, exeExt("gapid")), &r.FileSizes.GAPID},
			{filepath.Join(pkgDir, exeExt("gapir")), &r.FileSizes.GAPIR},
			{filepath.Join(pkgDir, exeExt("gapis")), &r.FileSizes.GAPIS},
			{filepath.Join(pkgDir, exeExt("gapit")), &r.FileSizes.GAPIT},
		} {
			fi, err := os.Stat(f.path)
			if err != nil {
				log.W(ctx, "Couldn't stat file '%v': %v", f.path, err)
				continue
			}
			*f.size = int(fi.Size())
		}

		// Gather capture stats
		if *pkg != "" {
			file, err := trace(ctx)
			if err != nil {
				log.W(ctx, "Couldn't capture trace: %v", err)
				continue
			}
			defer os.Remove(file)
			frames, draws, cmds, err := captureStats(ctx, file)
			if err != nil {
				continue
			}
			r.CaptureStats.Frames = frames
			r.CaptureStats.Draws = draws
			r.CaptureStats.Commands = cmds
			*tracePath = file
		}

		if *tracePath != "" {
			start := time.Now()
			if err := report(ctx, *tracePath); err != nil {
				return err
			}
			r.ReportStats.Time = time.Since(start).Seconds()
		}

		// Gather incremental build stats
		if *incBuild {
			if err := withTouchedGLES(ctx, rnd, func() error {
				log.I(ctx, "HEAD~%.2d: Building incremental change at %v: %v", i, sha, cl.Subject)
				if duration, err := build(ctx); err == nil {
					r.IncrementalBuildTime = duration.Seconds()
				}
				return nil
			}); err != nil {
				continue
			}
		}

		res = append(res, r)
	}

	w := tabwriter.NewWriter(os.Stdout, 1, 4, 0, ' ', 0)
	defer w.Flush()

	var display func(get func(stats) reflect.Value, ty reflect.Type, name string)
	display = func(get func(stats) reflect.Value, ty reflect.Type, name string) {
		switch ty.Kind() {
		case reflect.Struct:
			for i, c := 0, ty.NumField(); i < c; i++ {
				f := ty.Field(i)
				get := func(s stats) reflect.Value { return get(s).Field(i) }
				name := f.Name
				if n := f.Tag.Get("name"); n != "" {
					name = n
				}
				display(get, f.Type, name)
			}
		default:
			fmt.Fprint(w, name)
			var prev reflect.Value
			for i, s := range res {
				v := get(s)
				var old, new float64
				if i > 0 {
					switch v.Kind() {
					case reflect.Int:
						old, new = float64(prev.Int()), float64(v.Int())
					case reflect.Float64:
						old, new = prev.Float(), v.Float()
					}
				}
				if old != new {
					percent := 100 * (new - old) / old
					fmt.Fprintf(w, "\t | %v \t(%+4.1f%%)", v.Interface(), percent)
				} else {
					fmt.Fprintf(w, "\t | %v \t", v.Interface())
				}
				prev = v
			}
			fmt.Fprintln(w)
		}
	}

	display(
		func(s stats) reflect.Value { return reflect.ValueOf(s) },
		reflect.TypeOf(stats{}),
		"")

	return nil
}

func withTouchedGLES(ctx context.Context, r *rand.Rand, f func() error) error {
	glesAPIPath := filepath.Join(*root, "gapis", "api", "gles", "gles.api")
	fi, err := os.Stat(glesAPIPath)
	if err != nil {
		return err
	}
	glesAPI, err := ioutil.ReadFile(glesAPIPath)
	if err != nil {
		return err
	}
	modGlesAPI := []byte(fmt.Sprintf("%v\ncmd void fake_cmd_%d() {}\n", string(glesAPI), r.Int()))
	ioutil.WriteFile(glesAPIPath, modGlesAPI, fi.Mode().Perm())
	defer ioutil.WriteFile(glesAPIPath, glesAPI, fi.Mode().Perm())
	return f()
}

func build(ctx context.Context) (time.Duration, error) {
	args := []string{"build"}
	if *optimize {
		args = append(args, "-c", "opt")
	}
	args = append(args, "pkg")
	cmd := shell.Cmd{
		Name:      "bazel",
		Args:      args,
		Verbosity: *verbose,
		Dir:       *root,
	}
	start := time.Now()
	_, err := cmd.Call(ctx)
	return time.Since(start), err
}

func dllExt(n string) string {
	switch runtime.GOOS {
	case "windows":
		return n + ".dll"
	case "darwin":
		return n + ".dylib"
	default:
		return n + ".so"
	}
}

func exeExt(n string) string {
	switch runtime.GOOS {
	case "windows":
		return n + ".exe"
	default:
		return n
	}
}

func gapitPath() string { return filepath.Join(*root, "bazel-bin", "pkg", exeExt("gapit")) }

func trace(ctx context.Context) (string, error) {
	file := filepath.Join(os.TempDir(), "gapid-regres.gfxtrace")
	cmd := shell.Cmd{
		Name:      gapitPath(),
		Args:      []string{"--log-style", "raw", "trace", "--for", "60s", "--out", file, *pkg},
		Verbosity: *verbose,
	}
	_, err := cmd.Call(ctx)
	if err != nil {
		os.Remove(file)
		return "", err
	}
	return file, err
}

func report(ctx context.Context, trace string) error {
	cmd := shell.Cmd{
		Name:      gapitPath(),
		Args:      []string{"--log-style", "raw", "report", trace},
		Verbosity: *verbose,
	}
	_, err := cmd.Call(ctx)
	return err
}

func captureStats(ctx context.Context, file string) (numFrames, numDraws, numCmds int, err error) {
	cmd := shell.Cmd{
		Name:      gapitPath(),
		Args:      []string{"--log-style", "raw", "--log-level", "error", "stats", file},
		Verbosity: *verbose,
	}
	stdout, err := cmd.Call(ctx)
	if err != nil {
		return 0, 0, 0, nil
	}
	re := regexp.MustCompile(`([a-zA-Z]+):\s+([0-9]+)`)
	for _, matches := range re.FindAllStringSubmatch(stdout, -1) {
		if len(matches) != 3 {
			continue
		}
		n, err := strconv.Atoi(matches[2])
		if err != nil {
			continue
		}
		switch matches[1] {
		case "Frames":
			numFrames = n
		case "Draws":
			numDraws = n
		case "Commands":
			numCmds = n
		}
	}
	return
}
<commit_msg>cmd/regres: Time linearize_trace.<commit_after>// Copyright (C) 2018 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Regress is a tool to display build and runtime statistics over a range of
// changelists.
package main

import (
	"context"
	"flag"
	"fmt"
	"io/ioutil"
	"math/rand"
	"os"
	"path/filepath"
	"reflect"
	"regexp"
	"runtime"
	"strconv"
	"text/tabwriter"
	"time"

	"github.com/google/gapid/core/app"
	"github.com/google/gapid/core/git"
	"github.com/google/gapid/core/log"
	"github.com/google/gapid/core/os/shell"
)

var (
	root      = flag.String("root", "", "Path to the root GAPID source directory")
	verbose   = flag.Bool("verbose", false, "Verbose logging")
	incBuild  = flag.Bool("inc", true, "Time incremental builds")
	optimize  = flag.Bool("optimize", false, "Build using '-c opt'")
	pkg       = flag.String("pkg", "", "Partial name of a package name to capture")
	atSHA     = flag.String("at", "", "The SHA or branch of the first changelist to profile")
	count     = flag.Int("count", 2, "The number of changelists to profile since HEAD")
	tracePath = flag.String("trace", "", "Path to a .gfxtrace used for report timing")
)

func main() {
	app.ShortHelp = "Regress is a tool to perform performance measurments over a range of CLs."
	app.Run(run)
}

type stats struct {
	SHA                  string   `name:"sha"`
	IncrementalBuildTime float64  `name:"incremental-build"` // in seconds
	FileSizes            struct { // in bytes
		LibGAPII                   int `name:"libgapii"`
		LibVkLayerVirtualSwapchain int `name:"libVkLayer_VirtualSwapchain"`
		GAPIDAarch64APK            int `name:"gapid-aarch64"`
		GAPIDArmeabi64APK          int `name:"gapid-armeabi"`
		GAPIDX86APK                int `name:"gapid-x86"`
		GAPID                      int `name:"gapid"`
		GAPIR                      int `name:"gapir"`
		GAPIS                      int `name:"gapis"`
		GAPIT                      int `name:"gapit"`
	}
	CaptureStats struct {
		Frames   int `name:"frames"`
		Draws    int `name:"draws"`
		Commands int `name:"commands"`
	}
	ReplayStats struct {
		ReportTime    float64 `name:"report-time"`    // in seconds
		LinearizeTime float64 `name:"linearize-time"` // in seconds
	}
}

func run(ctx context.Context) error {
	if *root == "" {
		wd, err := os.Getwd()
		if err != nil {
			return err
		}
		*root = wd
	}

	g, err := git.New(*root)
	if err != nil {
		return err
	}
	s, err := g.Status(ctx)
	if err != nil {
		return err
	}
	if !s.Clean() {
		return fmt.Errorf("Local changes found. Please submit any changes and run again")
	}

	branch, err := g.CurrentBranch(ctx)
	if err != nil {
		return err
	}

	defer g.CheckoutBranch(ctx, branch)

	cls, err := g.LogFrom(ctx, *atSHA, *count)
	if err != nil {
		return err
	}

	rnd := rand.New(rand.NewSource(time.Now().Unix()))

	res := []stats{}
	for i := range cls {
		i := len(cls) - 1 - i
		cl := cls[i]
		sha := cl.SHA.String()[:6]

		r := stats{SHA: sha}

		log.I(ctx, "HEAD~%.2d: Building at %v: %v", i, sha, cl.Subject)
		if err := g.Checkout(ctx, cl.SHA); err != nil {
			return err
		}

		_, err := build(ctx)
		if err != nil {
			continue
		}

		// Gather file size build stats
		pkgDir := filepath.Join(*root, "bazel-bin", "pkg")
		for _, f := range []struct {
			path string
			size *int
		}{
			{filepath.Join(pkgDir, "lib", dllExt("libgapii")), &r.FileSizes.LibGAPII},
			{filepath.Join(pkgDir, "lib", dllExt("libVkLayer_VirtualSwapchain")), &r.FileSizes.LibVkLayerVirtualSwapchain},
			{filepath.Join(pkgDir, "gapid-aarch64.apk"), &r.FileSizes.GAPIDAarch64APK},
			{filepath.Join(pkgDir, "gapid-armeabi.apk"), &r.FileSizes.GAPIDArmeabi64APK},
			{filepath.Join(pkgDir, "gapid-x86.apk"), &r.FileSizes.GAPIDX86APK},
			{filepath.Join(pkgDir, exeExt("gapid")), &r.FileSizes.GAPID},
			{filepath.Join(pkgDir, exeExt("gapir")), &r.FileSizes.GAPIR},
			{filepath.Join(pkgDir, exeExt("gapis")), &r.FileSizes.GAPIS},
			{filepath.Join(pkgDir, exeExt("gapit")), &r.FileSizes.GAPIT},
		} {
			fi, err := os.Stat(f.path)
			if err != nil {
				log.W(ctx, "Couldn't stat file '%v': %v", f.path, err)
				continue
			}
			*f.size = int(fi.Size())
		}

		// Gather capture stats
		if *pkg != "" {
			file, err := trace(ctx)
			if err != nil {
				log.W(ctx, "Couldn't capture trace: %v", err)
				continue
			}
			defer os.Remove(file)
			frames, draws, cmds, err := captureStats(ctx, file)
			if err != nil {
				continue
			}
			r.CaptureStats.Frames = frames
			r.CaptureStats.Draws = draws
			r.CaptureStats.Commands = cmds
			*tracePath = file
		}

		if *tracePath != "" {
			start := time.Now()
			if err := report(ctx, *tracePath); err != nil {
				return err
			}
			r.ReplayStats.ReportTime = time.Since(start).Seconds()

			start = time.Now()
			if err := linearize(ctx, *tracePath); err != nil {
				return err
			}
			r.ReplayStats.LinearizeTime = time.Since(start).Seconds()
		}

		// Gather incremental build stats
		if *incBuild {
			if err := withTouchedGLES(ctx, rnd, func() error {
				log.I(ctx, "HEAD~%.2d: Building incremental change at %v: %v", i, sha, cl.Subject)
				if duration, err := build(ctx); err == nil {
					r.IncrementalBuildTime = duration.Seconds()
				}
				return nil
			}); err != nil {
				continue
			}
		}

		res = append(res, r)
	}

	w := tabwriter.NewWriter(os.Stdout, 1, 4, 0, ' ', 0)
	defer w.Flush()

	var display func(get func(stats) reflect.Value, ty reflect.Type, name string)
	display = func(get func(stats) reflect.Value, ty reflect.Type, name string) {
		switch ty.Kind() {
		case reflect.Struct:
			for i, c := 0, ty.NumField(); i < c; i++ {
				f := ty.Field(i)
				get := func(s stats) reflect.Value { return get(s).Field(i) }
				name := f.Name
				if n := f.Tag.Get("name"); n != "" {
					name = n
				}
				display(get, f.Type, name)
			}
		default:
			fmt.Fprint(w, name)
			var prev reflect.Value
			for i, s := range res {
				v := get(s)
				var old, new float64
				if i > 0 {
					switch v.Kind() {
					case reflect.Int:
						old, new = float64(prev.Int()), float64(v.Int())
					case reflect.Float64:
						old, new = prev.Float(), v.Float()
					}
				}
				if old != new {
					percent := 100 * (new - old) / old
					fmt.Fprintf(w, "\t | %v \t(%+4.1f%%)", v.Interface(), percent)
				} else {
					fmt.Fprintf(w, "\t | %v \t", v.Interface())
				}
				prev = v
			}
			fmt.Fprintln(w)
		}
	}

	display(
		func(s stats) reflect.Value { return reflect.ValueOf(s) },
		reflect.TypeOf(stats{}),
		"")

	return nil
}

func withTouchedGLES(ctx context.Context, r *rand.Rand, f func() error) error {
	glesAPIPath := filepath.Join(*root, "gapis", "api", "gles", "gles.api")
	fi, err := os.Stat(glesAPIPath)
	if err != nil {
		return err
	}
	glesAPI, err := ioutil.ReadFile(glesAPIPath)
	if err != nil {
		return err
	}
	modGlesAPI := []byte(fmt.Sprintf("%v\ncmd void fake_cmd_%d() {}\n", string(glesAPI), r.Int()))
	ioutil.WriteFile(glesAPIPath, modGlesAPI, fi.Mode().Perm())
	defer ioutil.WriteFile(glesAPIPath, glesAPI, fi.Mode().Perm())
	return f()
}

func build(ctx context.Context) (time.Duration, error) {
	start := time.Now()
	args := []string{"build"}
	if *optimize {
		args = append(args, "-c", "opt")
	}
	args = append(args, "pkg")
	cmd := shell.Cmd{
		Name:      "bazel",
		Args:      args,
		Verbosity: *verbose,
		Dir:       *root,
	}
	if _, err := cmd.Call(ctx); err != nil {
		return 0, err
	}
	return time.Since(start), nil
}

func dllExt(n string) string {
	switch runtime.GOOS {
	case "windows":
		return n + ".dll"
	case "darwin":
		return n + ".dylib"
	default:
		return n + ".so"
	}
}

func exeExt(n string) string {
	switch runtime.GOOS {
	case "windows":
		return n + ".exe"
	default:
		return n
	}
}

func gapitPath() string { return filepath.Join(*root, "bazel-bin", "pkg", exeExt("gapit")) }

func trace(ctx context.Context) (string, error) {
	file := filepath.Join(os.TempDir(), "gapid-regres.gfxtrace")
	cmd := shell.Cmd{
		Name:      gapitPath(),
		Args:      []string{"--log-style", "raw", "trace", "--for", "60s", "--out", file, *pkg},
		Verbosity: *verbose,
	}
	_, err := cmd.Call(ctx)
	if err != nil {
		os.Remove(file)
		return "", err
	}
	return file, err
}

func report(ctx context.Context, trace string) error {
	cmd := shell.Cmd{
		Name:      gapitPath(),
		Args:      []string{"--log-style", "raw", "report", trace},
		Verbosity: *verbose,
	}
	_, err := cmd.Call(ctx)
	return err
}

func linearize(ctx context.Context, trace string) error {
	args := []string{
		"run", "cmd/linearize_trace",
		"-c", "opt",
		"--",
		"--file", trace,
	}
	args = append(args, "pkg")
	cmd := shell.Cmd{
		Name:      "bazel",
		Args:      args,
		Verbosity: *verbose,
		Dir:       *root,
	}
	_, err := cmd.Call(ctx)
	return err
}

func captureStats(ctx context.Context, file string) (numFrames, numDraws, numCmds int, err error) {
	cmd := shell.Cmd{
		Name:      gapitPath(),
		Args:      []string{"--log-style", "raw", "--log-level", "error", "stats", file},
		Verbosity: *verbose,
	}
	stdout, err := cmd.Call(ctx)
	if err != nil {
		return 0, 0, 0, nil
	}
	re := regexp.MustCompile(`([a-zA-Z]+):\s+([0-9]+)`)
	for _, matches := range re.FindAllStringSubmatch(stdout, -1) {
		if len(matches) != 3 {
			continue
		}
		n, err := strconv.Atoi(matches[2])
		if err != nil {
			continue
		}
		switch matches[1] {
		case "Frames":
			numFrames = n
		case "Draws":
			numDraws = n
		case "Commands":
			numCmds = n
		}
	}
	return
}
<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/Bowery/prompt"
	"github.com/mkideal/cli"
)

const maxRedirect = 21

type argT struct {
	cli.Helper
	Protocol string `cli:"s,scheme" usage:"protocol scheme (http or https)" dft:"http"`
	Host     string `cli:"H,host" usage:"rqlited host address" dft:"127.0.0.1"`
	Port     uint16 `cli:"p,port" usage:"rqlited host port" dft:"4001"`
	Prefix   string `cli:"P,prefix" usage:"rqlited HTTP URL prefix" dft:"/"`
	Insecure bool   `cli:"i,insecure" usage:"do not verify rqlited HTTPS certificate" dft:"false"`
}

func main() {
	cli.SetUsageStyle(cli.ManualStyle)
	cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		if argv.Help {
			ctx.WriteUsage()
			return nil
		}

		prefix := fmt.Sprintf("%s:%d> ", argv.Host, argv.Port)
	FOR_READ:
		for {
			line, err := prompt.Basic(prefix, false)
			if err != nil {
				return err
			}
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}
			var (
				index = strings.Index(line, " ")
				cmd   = line
			)
			if index >= 0 {
				cmd = line[:index]
			}
			cmd = strings.ToUpper(cmd)
			switch cmd {
			case ".TABLES":
				err = query(ctx, cmd, `SELECT name FROM sqlite_master WHERE type="table"`, argv)
			case ".INDEXES":
				err = query(ctx, cmd, `SELECT sql FROM sqlite_master WHERE type="index"`, argv)
			case ".SCHEMA":
				err = query(ctx, cmd, "SELECT sql FROM sqlite_master", argv)
			case ".QUIT", "QUIT", "EXIT":
				break FOR_READ
			case "SELECT":
				err = query(ctx, cmd, line, argv)
			default:
				err = execute(ctx, cmd, line, argv)
			}
			if err != nil {
				ctx.String("%s %v\n", ctx.Color().Red("ERR!"), err)
			}
		}
		ctx.String("bye~\n")
		return nil
	})
}

func makeJSONBody(line string) string {
	data, err := json.MarshalIndent([]string{line}, "", "    ")
	if err != nil {
		return ""
	}
	return string(data)
}

func sendRequest(ctx *cli.Context, urlStr string, line string, argv *argT, ret interface{}) error {
	data := makeJSONBody(line)
	url := urlStr

	client := http.Client{Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: argv.Insecure},
	}}

	// Explicitly handle redirects.
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
	}

	nRedirect := 0
	for {
		resp, err := client.Post(url, "application/json", strings.NewReader(data))
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		// Check for redirect.
		if resp.StatusCode == http.StatusMovedPermanently {
			nRedirect++
			if nRedirect > maxRedirect {
				return fmt.Errorf("maximum leader redirect limit exceeded")
			}
			url = resp.Header["Location"][0]
			continue
		}

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

		if err := json.Unmarshal(body, ret); err != nil {
			return err
		}
		return nil
	}
}
<commit_msg>Add .STATUS command to CLI<commit_after>package main

import (
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"

	"github.com/Bowery/prompt"
	"github.com/mkideal/cli"
)

const maxRedirect = 21

type argT struct {
	cli.Helper
	Protocol string `cli:"s,scheme" usage:"protocol scheme (http or https)" dft:"http"`
	Host     string `cli:"H,host" usage:"rqlited host address" dft:"127.0.0.1"`
	Port     uint16 `cli:"p,port" usage:"rqlited host port" dft:"4001"`
	Prefix   string `cli:"P,prefix" usage:"rqlited HTTP URL prefix" dft:"/"`
	Insecure bool   `cli:"i,insecure" usage:"do not verify rqlited HTTPS certificate" dft:"false"`
}

func main() {
	cli.SetUsageStyle(cli.ManualStyle)
	cli.Run(new(argT), func(ctx *cli.Context) error {
		argv := ctx.Argv().(*argT)
		if argv.Help {
			ctx.WriteUsage()
			return nil
		}

		prefix := fmt.Sprintf("%s:%d> ", argv.Host, argv.Port)
	FOR_READ:
		for {
			line, err := prompt.Basic(prefix, false)
			if err != nil {
				return err
			}
			line = strings.TrimSpace(line)
			if line == "" {
				continue
			}
			var (
				index = strings.Index(line, " ")
				cmd   = line
			)
			if index >= 0 {
				cmd = line[:index]
			}
			cmd = strings.ToUpper(cmd)
			switch cmd {
			case ".TABLES":
				err = query(ctx, cmd, `SELECT name FROM sqlite_master WHERE type="table"`, argv)
			case ".INDEXES":
				err = query(ctx, cmd, `SELECT sql FROM sqlite_master WHERE type="index"`, argv)
			case ".SCHEMA":
				err = query(ctx, cmd, "SELECT sql FROM sqlite_master", argv)
			case ".STATUS":
				err = status(ctx, cmd, line, argv)
			case ".QUIT", "QUIT", "EXIT":
				break FOR_READ
			case "SELECT":
				err = query(ctx, cmd, line, argv)
			default:
				err = execute(ctx, cmd, line, argv)
			}
			if err != nil {
				ctx.String("%s %v\n", ctx.Color().Red("ERR!"), err)
			}
		}
		ctx.String("bye~\n")
		return nil
	})
}

func makeJSONBody(line string) string {
	data, err := json.MarshalIndent([]string{line}, "", "    ")
	if err != nil {
		return ""
	}
	return string(data)
}

func status(ctx *cli.Context, cmd, line string, argv *argT) error {
	client := http.Client{Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: argv.Insecure},
	}}

	// Explicitly handle redirects.
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
	}

	// Recursive JSON printer.
	var pprint func(indent int, m map[string]interface{})
	pprint = func(indent int, m map[string]interface{}) {
		indentation := "  "
		for k, v := range m {
			switch v.(type) {
			case map[string]interface{}:
				for i := 0; i < indent; i++ {
					fmt.Print(indentation)
				}
				fmt.Printf("%s:\n", k)
				pprint(indent+1, v.(map[string]interface{}))
			default:
				for i := 0; i < indent; i++ {
					fmt.Print(indentation)
				}
				fmt.Printf("%s: %v\n", k, v)
			}
		}
	}

	nRedirect := 0
	url := `http://localhost:4001/status`
	for {
		resp, err := client.Get(url)
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		// Check for redirect.
		if resp.StatusCode == http.StatusMovedPermanently {
			nRedirect++
			if nRedirect > maxRedirect {
				return fmt.Errorf("maximum leader redirect limit exceeded")
			}
			url = resp.Header["Location"][0]
			continue
		}

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

		ret := make(map[string]interface{})
		if err := json.Unmarshal(body, &ret); err != nil {
			return err
		}
		fmt.Println()
		pprint(0, ret)
		fmt.Println()

		return nil
	}
}

func sendRequest(ctx *cli.Context, urlStr string, line string, argv *argT, ret interface{}) error {
	data := makeJSONBody(line)
	url := urlStr

	client := http.Client{Transport: &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: argv.Insecure},
	}}

	// Explicitly handle redirects.
	client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
		return http.ErrUseLastResponse
	}

	nRedirect := 0
	for {
		resp, err := client.Post(url, "application/json", strings.NewReader(data))
		if err != nil {
			return err
		}
		defer resp.Body.Close()

		// Check for redirect.
		if resp.StatusCode == http.StatusMovedPermanently {
			nRedirect++
			if nRedirect > maxRedirect {
				return fmt.Errorf("maximum leader redirect limit exceeded")
			}
			url = resp.Header["Location"][0]
			continue
		}

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

		if err := json.Unmarshal(body, ret); err != nil {
			return err
		}
		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// A simple server that can host different stark services.
//
// The module loading hides a few implementation details, so for a better
// introduction, look at cmd/starkping (it works serverless)
package main

import (
	"github.com/xconstruct/stark/core/server"
	"github.com/xconstruct/stark/services/commands"
	"github.com/xconstruct/stark/services/events"
	"github.com/xconstruct/stark/services/hostscan"
	"github.com/xconstruct/stark/services/know"
	"github.com/xconstruct/stark/services/lastfm"
	"github.com/xconstruct/stark/services/location"
	"github.com/xconstruct/stark/services/logger"
	"github.com/xconstruct/stark/services/luascripts"
	"github.com/xconstruct/stark/services/meals"
	"github.com/xconstruct/stark/services/natural"
	"github.com/xconstruct/stark/services/nlparser"
	"github.com/xconstruct/stark/services/reasoner"
	"github.com/xconstruct/stark/services/scheduler"
	"github.com/xconstruct/stark/services/store"
	"github.com/xconstruct/stark/services/vdir"
	"github.com/xconstruct/stark/services/web"
	"github.com/xconstruct/stark/services/xmpp"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
)

func main() {
	srv := server.New("stark", "server")

	srv.RegisterModule(commands.Module)
	srv.RegisterModule(events.Module)
	srv.RegisterModule(hostscan.Module)
	srv.RegisterModule(know.Module)
	srv.RegisterModule(lastfm.Module)
	srv.RegisterModule(logger.Module)
	srv.RegisterModule(location.Module)
	srv.RegisterModule(luascripts.Module)
	srv.RegisterModule(meals.Module)
	srv.RegisterModule(natural.Module)
	srv.RegisterModule(nlparser.Module)
	srv.RegisterModule(reasoner.Module)
	srv.RegisterModule(scheduler.Module)
	srv.RegisterModule(store.Module)
	srv.RegisterModule(vdir.Module)
	srv.RegisterModule(web.Module)
	srv.RegisterModule(xmpp.Module)

	// Default configuration
	srv.ServerConfig.EnabledModules = []string{
		"commands",
		"events",
		"know",
		"location",
		"logger",
		"meals",
		"natural",
		"nlparser",
		"reasoner",
		"scheduler",
		"states",
		"store",
		"trigger",
		"vdir",
		"web",
	}

	srv.Run()
}
<commit_msg>starkd: Remove missing modules in default configuration. Fixes #1<commit_after>// Copyright (C) 2014 Constantin Schomburg <me@cschomburg.com>
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

// A simple server that can host different stark services.
//
// The module loading hides a few implementation details, so for a better
// introduction, look at cmd/starkping (it works serverless)
package main

import (
	"github.com/xconstruct/stark/core/server"
	"github.com/xconstruct/stark/services/commands"
	"github.com/xconstruct/stark/services/events"
	"github.com/xconstruct/stark/services/hostscan"
	"github.com/xconstruct/stark/services/know"
	"github.com/xconstruct/stark/services/lastfm"
	"github.com/xconstruct/stark/services/location"
	"github.com/xconstruct/stark/services/logger"
	"github.com/xconstruct/stark/services/luascripts"
	"github.com/xconstruct/stark/services/meals"
	"github.com/xconstruct/stark/services/natural"
	"github.com/xconstruct/stark/services/nlparser"
	"github.com/xconstruct/stark/services/reasoner"
	"github.com/xconstruct/stark/services/scheduler"
	"github.com/xconstruct/stark/services/store"
	"github.com/xconstruct/stark/services/vdir"
	"github.com/xconstruct/stark/services/web"
	"github.com/xconstruct/stark/services/xmpp"

	_ "github.com/go-sql-driver/mysql"
	_ "github.com/lib/pq"
)

func main() {
	srv := server.New("stark", "server")

	srv.RegisterModule(commands.Module)
	srv.RegisterModule(events.Module)
	srv.RegisterModule(hostscan.Module)
	srv.RegisterModule(know.Module)
	srv.RegisterModule(lastfm.Module)
	srv.RegisterModule(logger.Module)
	srv.RegisterModule(location.Module)
	srv.RegisterModule(luascripts.Module)
	srv.RegisterModule(meals.Module)
	srv.RegisterModule(natural.Module)
	srv.RegisterModule(nlparser.Module)
	srv.RegisterModule(reasoner.Module)
	srv.RegisterModule(scheduler.Module)
	srv.RegisterModule(store.Module)
	srv.RegisterModule(vdir.Module)
	srv.RegisterModule(web.Module)
	srv.RegisterModule(xmpp.Module)

	// Default configuration
	srv.ServerConfig.EnabledModules = []string{
		"commands",
		"events",
		"know",
		"location",
		"logger",
		"meals",
		"natural",
		"nlparser",
		"reasoner",
		"scheduler",
		"store",
		"vdir",
		"web",
	}

	srv.Run()
}
<|endoftext|>


<commit_before>// Copyright 2012 tsuru 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 main

import (
	"errors"
	"fmt"
	"github.com/fsouza/gogit/git"
	"os"
	"regexp"
)

// AppGuesser is used to guess the name of an app based in a file path.
type AppGuesser interface {
	GuessName(path string) (string, error)
}

// GitGuesser uses git to guess the name of the app.
//
// It reads the "tsuru" remote from git config file. If the remote does not
// exist, or does not match the tsuru pattern (git@<something>:<app-name>.git),
// GuessName will return an error.
type GitGuesser struct{}

func (g GitGuesser) GuessName(path string) (string, error) {
	repoPath, err := git.DiscoverRepositoryPath(path)
	if err != nil {
		return "", fmt.Errorf("Git repository not found: %s.", err)
	}
	repo, err := git.OpenRepository(repoPath)
	if err != nil {
		return "", err
	}
	defer repo.Free()
	config, err := repo.Config()
	if err != nil {
		return "", err
	}
	defer config.Free()
	remoteUrl, err := config.GetString("remote.tsuru.url")
	if err != nil {
		return "", errors.New("tsuru remote not declared.")
	}
	re := regexp.MustCompile(`^git@.*:(.*)\.git$`)
	matches := re.FindStringSubmatch(remoteUrl)
	if len(matches) < 2 {
		return "", fmt.Errorf(`"tsuru" remote did not match the pattern. Want something like git@<host>:<app-name>.git, got %s`, remoteUrl)
	}
	return matches[1], nil
}

// Embed this struct if you want your command to guess the name of the app.
type GuessingCommand struct {
	g AppGuesser
}

func (cmd *GuessingCommand) guesser() AppGuesser {
	if cmd.g == nil {
		cmd.g = GitGuesser{}
	}
	return cmd.g
}

func (cmd *GuessingCommand) Guess() (string, error) {
	if appName != nil && *appName != "" {
		return *appName, nil
	}
	path, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("Unable to guess app name: %s.", err)
	}
	name, err := cmd.guesser().GuessName(path)
	if err != nil {
		return "", errors.New(`tsuru wasn't able to guess the name of the app.

Use the --app flag to specify the name of the app.`)
	}
	return name, nil
}
<commit_msg>cmd/tsuru: using tsuru's git package instead of libgit2 dependency<commit_after>// Copyright 2012 tsuru 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 main

import (
	"errors"
	"fmt"
	"github.com/globocom/tsuru/git"
	"os"
	"regexp"
)

// AppGuesser is used to guess the name of an app based in a file path.
type AppGuesser interface {
	GuessName(path string) (string, error)
}

// GitGuesser uses git to guess the name of the app.
//
// It reads the "tsuru" remote from git config file. If the remote does not
// exist, or does not match the tsuru pattern (git@<something>:<app-name>.git),
// GuessName will return an error.
type GitGuesser struct{}

func (g GitGuesser) GuessName(path string) (string, error) {
	repoPath, err := git.DiscoverRepositoryPath(path)
	if err != nil {
		return "", fmt.Errorf("Git repository not found: %s.", err)
	}
	repo, err := git.OpenRepository(repoPath)
	if err != nil {
		return "", err
	}
	remoteUrl, err := repo.GetRemoteUrl("tsuru")
	if err != nil {
		return "", errors.New("tsuru remote not declared.")
	}
	re := regexp.MustCompile(`^git@.*:(.*)\.git$`)
	matches := re.FindStringSubmatch(remoteUrl)
	if len(matches) < 2 {
		return "", fmt.Errorf(`"tsuru" remote did not match the pattern. Want something like git@<host>:<app-name>.git, got %s`, remoteUrl)
	}
	return matches[1], nil
}

// Embed this struct if you want your command to guess the name of the app.
type GuessingCommand struct {
	g AppGuesser
}

func (cmd *GuessingCommand) guesser() AppGuesser {
	if cmd.g == nil {
		cmd.g = GitGuesser{}
	}
	return cmd.g
}

func (cmd *GuessingCommand) Guess() (string, error) {
	if appName != nil && *appName != "" {
		return *appName, nil
	}
	path, err := os.Getwd()
	if err != nil {
		return "", fmt.Errorf("Unable to guess app name: %s.", err)
	}
	name, err := cmd.guesser().GuessName(path)
	if err != nil {
		return "", errors.New(`tsuru wasn't able to guess the name of the app.

Use the --app flag to specify the name of the app.`)
	}
	return name, nil
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"

	"github.com/appc/cni/pkg/skel"
	"github.com/appc/cni/pkg/types"
	"github.com/cloudfoundry-incubator/ducati-daemon/client"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/executor"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/ip"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/links"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/namespace"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/nl"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/ns"
	"github.com/cloudfoundry-incubator/ducati-daemon/models"
	"github.com/vishvananda/netlink"
)

const vni = 1

type NetConf struct {
	types.NetConf
	NetworkID string `json:"network_id"`
}

func init() {
	runtime.LockOSThread()
}

func loadConf(bytes []byte) (*NetConf, error) {
	n := &NetConf{}

	if err := json.Unmarshal(bytes, n); err != nil {
		return nil, fmt.Errorf("failed to load netconf: %v", err)
	}

	if n.NetworkID == "" {
		return nil, fmt.Errorf(`"network_id" field is required. It identifies the network.`)
	}

	return n, nil
}

func getSandboxRepo() (namespace.Repository, error) {
	sandboxRepoDir := os.Getenv("DUCATI_OS_SANDBOX_REPO")
	if sandboxRepoDir == "" {
		return nil, errors.New("DUCATI_OS_SANDBOX_REPO is required")
	}

	sandboxRepo, err := namespace.NewRepository(sandboxRepoDir)
	if err != nil {
		return nil, fmt.Errorf("failed to create sandbox repository: %s", err)
	}

	return sandboxRepo, nil
}

func getSandboxNS(name string) (namespace.Namespace, error) {
	sandboxRepo, err := getSandboxRepo()
	if err != nil {
		return nil, err
	}

	sandboxNS, err := sandboxRepo.Get(name)
	if err != nil {
		sandboxNS, err = sandboxRepo.Create(name)
		if err != nil {
			panic(err)
		}
	}

	return sandboxNS, nil
}

func getHostIP() (string, error) {
	routes, err := nl.Netlink.RouteList(nil, nl.FAMILY_V4)
	if err != nil {
		return "", fmt.Errorf("route list failed: %s", err)
	}

	var ifaceName string
	for _, r := range routes {
		link, err := nl.Netlink.LinkByIndex(r.LinkIndex)
		if err != nil {
			return "", fmt.Errorf("link by index failed: %s", err)
		}
		if r.Dst == nil && r.Src == nil {
			ifaceName = link.Attrs().Name
		}
	}

	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		return "", fmt.Errorf("error getting interface: %s", err)
	}

	addrs, err := iface.Addrs()
	if err != nil {
		return "", fmt.Errorf("error getting addrs: %s", err)
	}

	if len(addrs) == 0 {
		return "", fmt.Errorf("no addrs found for interface: %s", ifaceName)
	}

	return addrs[0].String(), nil
}

func cmdAdd(args *skel.CmdArgs) error {
	daemonBaseURL := os.Getenv("DAEMON_BASE_URL")
	if daemonBaseURL == "" {
		return fmt.Errorf("missing required env var 'DAEMON_BASE_URL'")
	}

	if args.ContainerID == "" {
		return errors.New("CNI_CONTAINERID is required")
	}

	netConf, err := loadConf(args.StdinData)
	if err != nil {
		return fmt.Errorf("loading config: %s", err)
	}

	sandboxNS, err := getSandboxNS(fmt.Sprintf("vni-%d", vni))
	if err != nil {
		return fmt.Errorf("getting vxlan sandbox: %s", err)
	}

	linkFactory := &links.Factory{Netlinker: nl.Netlink}
	addressManager := &ip.AddressManager{Netlinker: nl.Netlink}
	daemonClient := client.New(daemonBaseURL, http.DefaultClient)

	containerNS := namespace.NewNamespace(args.Netns)

	executor := executor.Executor{
		NetworkNamespacer: ns.LinuxNamespacer,
		LinkFactory:       linkFactory,
		Netlinker:         nl.Netlink,
		AddressManager:    addressManager,
	}

	ipamResult, err := daemonClient.AllocateIP(netConf.NetworkID, args.ContainerID)
	if err != nil {
		return err
	}

	sandboxLink, containerMAC, err := executor.SetupContainerNS(sandboxNS.Path(), containerNS.Path(), args.ContainerID, args.IfName, ipamResult)
	if err != nil {
		return err
	}

	bridgeName := fmt.Sprintf("vxlanbr%d", vni)
	vxlanName, err := executor.EnsureVxlanDeviceExists(vni, sandboxNS)
	if err != nil {
		return err
	}

	err = executor.SetupSandboxNS(vxlanName, bridgeName, sandboxNS, sandboxLink, ipamResult)
	if err != nil {
		return fmt.Errorf("configuring sandbox namespace: %s", err)
	}

	container := models.Container{
		ID:  args.ContainerID,
		MAC: containerMAC,
		IP:  ipamResult.IP4.IP.IP.String(),
	}

	container.HostIP, err = getHostIP()
	if err != nil {
		return fmt.Errorf("getting host IP:", err)
	}

	err = daemonClient.SaveContainer(container)
	if err != nil {
		return fmt.Errorf("saving container data to store: %s", err)
	}

	return ipamResult.Print()
}

func cmdDel(args *skel.CmdArgs) error {
	daemonBaseURL := os.Getenv("DAEMON_BASE_URL")
	if daemonBaseURL == "" {
		return fmt.Errorf("missing required env var 'DAEMON_BASE_URL'")
	}

	// n, err := loadConf(args.StdinData)
	// if err != nil {
	// 	return err
	// }

	daemonClient := client.New(daemonBaseURL, http.DefaultClient)

	err := daemonClient.RemoveContainer(args.ContainerID)
	if err != nil {
		return fmt.Errorf("removing container data to store: %s", err)
	}

	// err = ipam.ExecDel(n.IPAM.Type, args.StdinData)
	// if err != nil {
	// 	return err
	// }

	linkFactory := &links.Factory{Netlinker: nl.Netlink}
	containerNS := namespace.NewNamespace(args.Netns)

	err = containerNS.Execute(func(ns *os.File) error {
		linkFactory.DeleteLinkByName(args.IfName)
		return nil
	})
	if err != nil {
		return fmt.Errorf("failed to delete link in container namespace: %s", err)
	}

	sandboxRepo, err := getSandboxRepo()
	if err != nil {
		return fmt.Errorf("failed to open sandbox repository: %s", err)
	}

	sandboxNS, err := sandboxRepo.Get(fmt.Sprintf("vni-%d", vni))
	if err != nil {
		return fmt.Errorf("failed to get sandbox namespace: %s", err)
	}

	var sandboxLinks []netlink.Link
	err = sandboxNS.Execute(func(ns *os.File) error {
		var err error
		sandboxLinks, err = linkFactory.ListLinks()
		return err
	})
	if err != nil {
		return fmt.Errorf("failed to get sandbox links: %s", err)
	}

	for _, link := range sandboxLinks {
		if _, ok := link.(*netlink.Veth); ok {
			return nil // we still have a container attached
		}
	}

	err = sandboxNS.Destroy()
	if err != nil {
		return fmt.Errorf("failed to destroy sandbox namespace: %s", err)
	}

	return nil
}

func main() {
	skel.PluginMain(cmdAdd, cmdDel)
}
<commit_msg>Remove commented out code<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"runtime"

	"github.com/appc/cni/pkg/skel"
	"github.com/appc/cni/pkg/types"
	"github.com/cloudfoundry-incubator/ducati-daemon/client"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/executor"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/ip"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/links"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/namespace"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/nl"
	"github.com/cloudfoundry-incubator/ducati-daemon/lib/ns"
	"github.com/cloudfoundry-incubator/ducati-daemon/models"
	"github.com/vishvananda/netlink"
)

const vni = 1

type NetConf struct {
	types.NetConf
	NetworkID string `json:"network_id"`
}

func init() {
	runtime.LockOSThread()
}

func loadConf(bytes []byte) (*NetConf, error) {
	n := &NetConf{}

	if err := json.Unmarshal(bytes, n); err != nil {
		return nil, fmt.Errorf("failed to load netconf: %v", err)
	}

	if n.NetworkID == "" {
		return nil, fmt.Errorf(`"network_id" field is required. It identifies the network.`)
	}

	return n, nil
}

func getSandboxRepo() (namespace.Repository, error) {
	sandboxRepoDir := os.Getenv("DUCATI_OS_SANDBOX_REPO")
	if sandboxRepoDir == "" {
		return nil, errors.New("DUCATI_OS_SANDBOX_REPO is required")
	}

	sandboxRepo, err := namespace.NewRepository(sandboxRepoDir)
	if err != nil {
		return nil, fmt.Errorf("failed to create sandbox repository: %s", err)
	}

	return sandboxRepo, nil
}

func getSandboxNS(name string) (namespace.Namespace, error) {
	sandboxRepo, err := getSandboxRepo()
	if err != nil {
		return nil, err
	}

	sandboxNS, err := sandboxRepo.Get(name)
	if err != nil {
		sandboxNS, err = sandboxRepo.Create(name)
		if err != nil {
			panic(err)
		}
	}

	return sandboxNS, nil
}

func getHostIP() (string, error) {
	routes, err := nl.Netlink.RouteList(nil, nl.FAMILY_V4)
	if err != nil {
		return "", fmt.Errorf("route list failed: %s", err)
	}

	var ifaceName string
	for _, r := range routes {
		link, err := nl.Netlink.LinkByIndex(r.LinkIndex)
		if err != nil {
			return "", fmt.Errorf("link by index failed: %s", err)
		}
		if r.Dst == nil && r.Src == nil {
			ifaceName = link.Attrs().Name
		}
	}

	iface, err := net.InterfaceByName(ifaceName)
	if err != nil {
		return "", fmt.Errorf("error getting interface: %s", err)
	}

	addrs, err := iface.Addrs()
	if err != nil {
		return "", fmt.Errorf("error getting addrs: %s", err)
	}

	if len(addrs) == 0 {
		return "", fmt.Errorf("no addrs found for interface: %s", ifaceName)
	}

	return addrs[0].String(), nil
}

func cmdAdd(args *skel.CmdArgs) error {
	daemonBaseURL := os.Getenv("DAEMON_BASE_URL")
	if daemonBaseURL == "" {
		return fmt.Errorf("missing required env var 'DAEMON_BASE_URL'")
	}

	if args.ContainerID == "" {
		return errors.New("CNI_CONTAINERID is required")
	}

	netConf, err := loadConf(args.StdinData)
	if err != nil {
		return fmt.Errorf("loading config: %s", err)
	}

	sandboxNS, err := getSandboxNS(fmt.Sprintf("vni-%d", vni))
	if err != nil {
		return fmt.Errorf("getting vxlan sandbox: %s", err)
	}

	linkFactory := &links.Factory{Netlinker: nl.Netlink}
	addressManager := &ip.AddressManager{Netlinker: nl.Netlink}
	daemonClient := client.New(daemonBaseURL, http.DefaultClient)

	containerNS := namespace.NewNamespace(args.Netns)

	executor := executor.Executor{
		NetworkNamespacer: ns.LinuxNamespacer,
		LinkFactory:       linkFactory,
		Netlinker:         nl.Netlink,
		AddressManager:    addressManager,
	}

	ipamResult, err := daemonClient.AllocateIP(netConf.NetworkID, args.ContainerID)
	if err != nil {
		return err
	}

	sandboxLink, containerMAC, err := executor.SetupContainerNS(sandboxNS.Path(), containerNS.Path(), args.ContainerID, args.IfName, ipamResult)
	if err != nil {
		return err
	}

	bridgeName := fmt.Sprintf("vxlanbr%d", vni)
	vxlanName, err := executor.EnsureVxlanDeviceExists(vni, sandboxNS)
	if err != nil {
		return err
	}

	err = executor.SetupSandboxNS(vxlanName, bridgeName, sandboxNS, sandboxLink, ipamResult)
	if err != nil {
		return fmt.Errorf("configuring sandbox namespace: %s", err)
	}

	container := models.Container{
		ID:  args.ContainerID,
		MAC: containerMAC,
		IP:  ipamResult.IP4.IP.IP.String(),
	}

	container.HostIP, err = getHostIP()
	if err != nil {
		return fmt.Errorf("getting host IP:", err)
	}

	err = daemonClient.SaveContainer(container)
	if err != nil {
		return fmt.Errorf("saving container data to store: %s", err)
	}

	return ipamResult.Print()
}

func cmdDel(args *skel.CmdArgs) error {
	daemonBaseURL := os.Getenv("DAEMON_BASE_URL")
	if daemonBaseURL == "" {
		return fmt.Errorf("missing required env var 'DAEMON_BASE_URL'")
	}

	daemonClient := client.New(daemonBaseURL, http.DefaultClient)

	err := daemonClient.RemoveContainer(args.ContainerID)
	if err != nil {
		return fmt.Errorf("removing container data to store: %s", err)
	}

	linkFactory := &links.Factory{Netlinker: nl.Netlink}
	containerNS := namespace.NewNamespace(args.Netns)

	err = containerNS.Execute(func(ns *os.File) error {
		linkFactory.DeleteLinkByName(args.IfName)
		return nil
	})
	if err != nil {
		return fmt.Errorf("failed to delete link in container namespace: %s", err)
	}

	sandboxRepo, err := getSandboxRepo()
	if err != nil {
		return fmt.Errorf("failed to open sandbox repository: %s", err)
	}

	sandboxNS, err := sandboxRepo.Get(fmt.Sprintf("vni-%d", vni))
	if err != nil {
		return fmt.Errorf("failed to get sandbox namespace: %s", err)
	}

	var sandboxLinks []netlink.Link
	err = sandboxNS.Execute(func(ns *os.File) error {
		var err error
		sandboxLinks, err = linkFactory.ListLinks()
		return err
	})
	if err != nil {
		return fmt.Errorf("failed to get sandbox links: %s", err)
	}

	for _, link := range sandboxLinks {
		if _, ok := link.(*netlink.Veth); ok {
			return nil // we still have a container attached
		}
	}

	err = sandboxNS.Destroy()
	if err != nil {
		return fmt.Errorf("failed to destroy sandbox namespace: %s", err)
	}

	return nil
}

func main() {
	skel.PluginMain(cmdAdd, cmdDel)
}
<|endoftext|>


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

package main

import (
	"fmt"
	"os"
	"path"
	"sort"
	"time"

	"github.com/samuel/go-zookeeper/zk"
	"github.com/spf13/cobra"
)

var (
	acl = zk.WorldACL(zk.PermAll)

	rootCmd = &cobra.Command{
		Use:   "zkctl",
		Short: "A simple command line client for Zookeeper.",
	}

	zkCtlEndpoints []string
	zkCtlEphemeral bool
	zkCtlSequence  bool
)

func init() {
	cobra.EnablePrefixMatching = true
	rootCmd.PersistentFlags().StringSliceVar(&zkCtlEndpoints, "endpoints", []string{"127.0.0.1:2181"}, "Zookeeper client endpoints")

	createCmd := &cobra.Command{
		Use:   "create <path> <value>",
		Short: "creates a key with a given value",
		Run:   createCommandFunc,
	}
	createCmd.Flags().BoolVar(&zkCtlSequence, "sequence", false, "name key using the sequence policy")
	createCmd.Flags().BoolVar(&zkCtlEphemeral, "ephemeral", false, "set key as ephemeral")

	rootCmd.AddCommand(
		createCmd,
		&cobra.Command{
			Use:   "delete <path>",
			Short: "deletes a znode",
			Run:   deleteCommandFunc,
		},
		&cobra.Command{
			Use:   "get <path>",
			Short: "gets the value for a given path",
			Run:   getCommandFunc,
		},
		&cobra.Command{
			Use:   "ls [path]",
			Short: "lists a directory (default /)",
			Run:   lsCommandFunc,
		},
		&cobra.Command{
			Use:   "set <path> <value>",
			Short: "sets the value for an existing znode",
			Run:   setCommandFunc,
		},
		&cobra.Command{
			Use:   "watch [path]",
			Short: "watches a znode (default /)",
			Run:   watchCommandFunc,
		},
	)
}

func main() {
	rootCmd.SetUsageFunc(usageFunc)
	rootCmd.SetHelpTemplate(`{{.UsageString}}`)
	if err := rootCmd.Execute(); err != nil {
		exitOn(err)
	}
}

func lsCommandFunc(cmd *cobra.Command, args []string) {
	dir := "/"
	if len(args) > 1 {
		dir = args[0]
	}
	fmt.Println("ls dir", dir)
	children, stat, err := mustZKClient().Children(dir)
	if err != nil {
		exitOn(err)
	}
	sort.Sort(sort.StringSlice(children))
	fmt.Println("Children:")
	for _, c := range children {
		fmt.Printf("%s (%s)\n", path.Clean(dir+"/"+c), c)
	}
	fmt.Printf("Stat: %+v\n", stat)
}

func watchCommandFunc(cmd *cobra.Command, args []string) {
	dir := "/"
	if len(args) > 1 {
		dir = args[0]
	}
	fmt.Println("watch dir", dir)
	children, stat, ch, err := mustZKClient().ChildrenW(dir)
	if err != nil {
		exitOn(err)
	}
	fmt.Printf("%+v %+v\n", children, stat)
	e := <-ch
	fmt.Printf("%+v\n", e)
}

func createCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		exitOn(fmt.Errorf("create expected path and data arguments, got %q", args))
	}
	path, data := args[0], args[1]
	var flags int32
	if zkCtlEphemeral {
		flags |= zk.FlagEphemeral
	}
	if zkCtlSequence {
		flags |= zk.FlagSequence
	}
	if _, err := mustZKClient().Create(path, []byte(data), flags, acl); err != nil {
		exitOn(err)
	}
}

func setCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		exitOn(fmt.Errorf("set expected path and data arguments, got %q", args))
	}
	path, data := args[0], args[1]
	if _, err := mustZKClient().Set(path, []byte(data), -1); err != nil {
		exitOn(err)
	}
}

func deleteCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		exitOn(fmt.Errorf("delete expected path argument, got %q", args))
	}
	if err := mustZKClient().Delete(args[0], -1); err != nil {
		exitOn(err)
	}
}

func getCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		exitOn(fmt.Errorf("get expected path argument, got %q", args))
	}
	dat, st, err := mustZKClient().Get(args[0])
	if err != nil {
		exitOn(err)
	}
	fmt.Println(dat)
	fmt.Printf("Stat:\n%+v\n", st)
}

func mustZKClient() *zk.Conn {
	c, _, err := zk.Connect(zkCtlEndpoints, time.Second)
	if err != nil {
		exitOn(err)
	}
	return c
}

func exitOn(err error) {
	fmt.Fprintf(os.Stderr, "%+v\n", err)
	os.Exit(1)
}
<commit_msg>zkctl: use arg[0] for ls and watch if one argument given<commit_after>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"fmt"
	"os"
	"path"
	"sort"
	"time"

	"github.com/samuel/go-zookeeper/zk"
	"github.com/spf13/cobra"
)

var (
	acl = zk.WorldACL(zk.PermAll)

	rootCmd = &cobra.Command{
		Use:   "zkctl",
		Short: "A simple command line client for Zookeeper.",
	}

	zkCtlEndpoints []string
	zkCtlEphemeral bool
	zkCtlSequence  bool
)

func init() {
	cobra.EnablePrefixMatching = true
	rootCmd.PersistentFlags().StringSliceVar(&zkCtlEndpoints, "endpoints", []string{"127.0.0.1:2181"}, "Zookeeper client endpoints")

	createCmd := &cobra.Command{
		Use:   "create <path> <value>",
		Short: "creates a key with a given value",
		Run:   createCommandFunc,
	}
	createCmd.Flags().BoolVar(&zkCtlSequence, "sequence", false, "name key using the sequence policy")
	createCmd.Flags().BoolVar(&zkCtlEphemeral, "ephemeral", false, "set key as ephemeral")

	rootCmd.AddCommand(
		createCmd,
		&cobra.Command{
			Use:   "delete <path>",
			Short: "deletes a znode",
			Run:   deleteCommandFunc,
		},
		&cobra.Command{
			Use:   "get <path>",
			Short: "gets the value for a given path",
			Run:   getCommandFunc,
		},
		&cobra.Command{
			Use:   "ls [path]",
			Short: "lists a directory (default /)",
			Run:   lsCommandFunc,
		},
		&cobra.Command{
			Use:   "set <path> <value>",
			Short: "sets the value for an existing znode",
			Run:   setCommandFunc,
		},
		&cobra.Command{
			Use:   "watch [path]",
			Short: "watches a znode (default /)",
			Run:   watchCommandFunc,
		},
	)
}

func main() {
	rootCmd.SetUsageFunc(usageFunc)
	rootCmd.SetHelpTemplate(`{{.UsageString}}`)
	if err := rootCmd.Execute(); err != nil {
		exitOn(err)
	}
}

func lsCommandFunc(cmd *cobra.Command, args []string) {
	dir := "/"
	if len(args) == 1 {
		dir = args[0]
	} else if len(args) > 1 {
		exitOn(fmt.Errorf("ls expected one path argument, got more %q", args))
	}

	fmt.Println("ls dir", dir)
	children, stat, err := mustZKClient().Children(dir)
	if err != nil {
		exitOn(err)
	}
	sort.Sort(sort.StringSlice(children))
	fmt.Println("Children:")
	for _, c := range children {
		fmt.Printf("%s (%s)\n", path.Clean(dir+"/"+c), c)
	}
	fmt.Printf("Stat: %+v\n", stat)
}

func watchCommandFunc(cmd *cobra.Command, args []string) {
	dir := "/"
	if len(args) == 1 {
		dir = args[0]
	} else if len(args) > 1 {
		exitOn(fmt.Errorf("watch expected one path argument, got more %q", args))
	}

	fmt.Println("watch dir", dir)
	children, stat, ch, err := mustZKClient().ChildrenW(dir)
	if err != nil {
		exitOn(err)
	}
	fmt.Printf("%+v %+v\n", children, stat)
	e := <-ch
	fmt.Printf("%+v\n", e)
}

func createCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		exitOn(fmt.Errorf("create expected path and data arguments, got %q", args))
	}
	path, data := args[0], args[1]
	var flags int32
	if zkCtlEphemeral {
		flags |= zk.FlagEphemeral
	}
	if zkCtlSequence {
		flags |= zk.FlagSequence
	}
	if _, err := mustZKClient().Create(path, []byte(data), flags, acl); err != nil {
		exitOn(err)
	}
}

func setCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 2 {
		exitOn(fmt.Errorf("set expected path and data arguments, got %q", args))
	}
	path, data := args[0], args[1]
	if _, err := mustZKClient().Set(path, []byte(data), -1); err != nil {
		exitOn(err)
	}
}

func deleteCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		exitOn(fmt.Errorf("delete expected path argument, got %q", args))
	}
	if err := mustZKClient().Delete(args[0], -1); err != nil {
		exitOn(err)
	}
}

func getCommandFunc(cmd *cobra.Command, args []string) {
	if len(args) != 1 {
		exitOn(fmt.Errorf("get expected path argument, got %q", args))
	}
	dat, st, err := mustZKClient().Get(args[0])
	if err != nil {
		exitOn(err)
	}
	fmt.Println(dat)
	fmt.Printf("Stat:\n%+v\n", st)
}

func mustZKClient() *zk.Conn {
	c, _, err := zk.Connect(zkCtlEndpoints, time.Second)
	if err != nil {
		exitOn(err)
	}
	return c
}

func exitOn(err error) {
	fmt.Fprintf(os.Stderr, "%+v\n", err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>// Contains common structure handles errors in CausedBy way in command line
// utility applications.
//
// Service/daemon can use cmdline package to handle main goroutine's
// panic/errors, but can not handle panic/errors inside other goroutines, use
// redforks/life package to cover service goroutines.
package cmdline

import (
	"fmt"
	"os"
	"runtime"

	"github.com/redforks/life"

	"github.com/redforks/errors"
)

type exitError int

func (code exitError) Error() string {
	return fmt.Sprintf("Exit error %d", int(code))
}

// Create a new exit error. Panic with exit error or return it in MainFunc,
// Go() detect it and call os.Exit() with specific exit code.
// os.Exit() exit the application immediately without calling deferred code
// block, by using exit error we can *fix* this.
func NewExitError(code int) error {
	return exitError(code)
}

// Your application main function type.
type MainFunc func() error

// Call your application main function, handles any error returned or paniced,
// handle error by errors.CausedBy rule.
func Go(main MainFunc) {
	defer func() {
		handleError(recover())
	}()

	handleError(main())
}

func handleError(v interface{}) {
	if err, ok := v.(exitError); ok {
		life.Exit(int(err))
		return
	}

	cause := errors.GetPanicCausedBy(v)
	if cause == errors.NoError {
		return
	}

	errors.Handle(nil, v)

	switch cause {
	case errors.ByBug, errors.ByRuntime:
		fmt.Fprintln(os.Stderr, v)
		buf := make([]byte, 16*1024)
		buf = buf[0:runtime.Stack(buf, true)]
		fmt.Fprintln(os.Stderr, string(buf))
	case errors.ByInput, errors.ByExternal:
		fmt.Println(v)
	default:
		panic("Unknown CausedBy")
	}
	life.Exit(int(cause) + 1)
}
<commit_msg>cmdline process ByClientBug errors<commit_after>// Contains common structure handles errors in CausedBy way in command line
// utility applications.
//
// Service/daemon can use cmdline package to handle main goroutine's
// panic/errors, but can not handle panic/errors inside other goroutines, use
// redforks/life package to cover service goroutines.
package cmdline

import (
	"fmt"
	"os"
	"runtime"

	"github.com/redforks/life"

	"github.com/redforks/errors"
)

type exitError int

func (code exitError) Error() string {
	return fmt.Sprintf("Exit error %d", int(code))
}

// Create a new exit error. Panic with exit error or return it in MainFunc,
// Go() detect it and call os.Exit() with specific exit code.
// os.Exit() exit the application immediately without calling deferred code
// block, by using exit error we can *fix* this.
func NewExitError(code int) error {
	return exitError(code)
}

// Your application main function type.
type MainFunc func() error

// Call your application main function, handles any error returned or paniced,
// handle error by errors.CausedBy rule.
func Go(main MainFunc) {
	defer func() {
		handleError(recover())
	}()

	handleError(main())
}

func handleError(v interface{}) {
	if err, ok := v.(exitError); ok {
		life.Exit(int(err))
		return
	}

	cause := errors.GetPanicCausedBy(v)
	if cause == errors.NoError {
		return
	}

	errors.Handle(nil, v)

	switch cause {
	case errors.ByBug, errors.ByRuntime:
		fmt.Fprintln(os.Stderr, v)
		buf := make([]byte, 16*1024)
		buf = buf[0:runtime.Stack(buf, true)]
		fmt.Fprintln(os.Stderr, string(buf))
	case errors.ByInput, errors.ByExternal, errors.ByClientBug:
		fmt.Println(v)
	default:
		panic("Unknown CausedBy")
	}
	life.Exit(int(cause) + 1)
}
<|endoftext|>


<commit_before>package testserver

import (
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"time"
)

const (
	latestSuffix     = "LATEST"
	finishedFileMode = 0555
)

func downloadFile(response *http.Response, filePath string) error {
	output, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0200)
	if err != nil {
		return fmt.Errorf("error creating %s: %s", filePath, err)
	}
	defer output.Close()

	log.Printf("saving %s to %s, this may take some time", response.Request.URL, filePath)

	if _, err := io.Copy(output, response.Body); err != nil {
		return fmt.Errorf("problem saving %s to %s: %s", response.Request.URL, filePath, err)
	}

	// Download was successful, add the rw bits.
	return os.Chmod(filePath, finishedFileMode)
}

var muslRE = regexp.MustCompile(`(?i)\bmusl\b`)

func downloadLatestBinary() (string, error) {
	goos := runtime.GOOS
	if goos == "linux" {
		goos += func() string {
			// Detect which C library is present on the system. See
			// https://unix.stackexchange.com/a/120381.
			cmd := exec.Command("ldd", "--version")
			out, err := cmd.Output()
			if err != nil {
				log.Printf("%s: out=%q err=%s", cmd.Args, out, err)
			} else if muslRE.Match(out) {
				return "-musl"
			}
			return "-gnu"
		}()
	}
	binaryName := fmt.Sprintf("cockroach.%s-%s", goos, runtime.GOARCH)
	if runtime.GOOS == "windows" {
		binaryName += ".exe"
	}
	url := &url.URL{
		Scheme: "https",
		Host:   "edge-binaries.cockroachdb.com",
		Path:   path.Join("cockroach", fmt.Sprintf("%s.%s", binaryName, latestSuffix)),
	}
	log.Printf("GET %s", url)
	response, err := http.Get(url.String())
	if err != nil {
		return "", err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return "", fmt.Errorf("error downloading %s: %d (%s)", url, response.StatusCode, response.Status)
	}

	const contentDisposition = "Content-Disposition"
	_, disposition, err := mime.ParseMediaType(response.Header.Get(contentDisposition))
	if err != nil {
		return "", fmt.Errorf("error parsing %s headers %s: %s", contentDisposition, response.Header, err)
	}

	filename, ok := disposition["filename"]
	if !ok {
		return "", fmt.Errorf("content disposition header %s did not contain filename", disposition)
	}
	localFile := filepath.Join(os.TempDir(), filename)
	for {
		info, err := os.Stat(localFile)
		if os.IsNotExist(err) {
			// File does not exist: download it.
			break
		}
		if err != nil {
			return "", err
		}
		// File already present: check mode.
		if info.Mode().Perm() == finishedFileMode {
			return localFile, nil
		}
		time.Sleep(time.Millisecond * 10)
	}

	if err := downloadFile(response, localFile); err != nil {
		_ = os.Remove(localFile)
		return "", err
	}

	return localFile, nil
}
<commit_msg>testserver: improve error handling discipline<commit_after>package testserver

import (
	"fmt"
	"io"
	"log"
	"mime"
	"net/http"
	"net/url"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"regexp"
	"runtime"
	"time"
)

const (
	latestSuffix     = "LATEST"
	finishedFileMode = 0555
)

func downloadFile(response *http.Response, filePath string) error {
	output, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE|os.O_EXCL, 0200)
	if err != nil {
		return fmt.Errorf("error creating %s: %s", filePath, err)
	}
	defer output.Close()

	log.Printf("saving %s to %s, this may take some time", response.Request.URL, filePath)

	if _, err := io.Copy(output, response.Body); err != nil {
		return fmt.Errorf("problem saving %s to %s: %s", response.Request.URL, filePath, err)
	}

	// Download was successful, add the rw bits.
	if err := output.Chmod(finishedFileMode); err != nil {
		return err
	}

	// We explicitly close here to ensure the error is checked; the deferred
	// close above will likely error in this case, but that's harmless.
	return output.Close()
}

var muslRE = regexp.MustCompile(`(?i)\bmusl\b`)

func downloadLatestBinary() (string, error) {
	goos := runtime.GOOS
	if goos == "linux" {
		goos += func() string {
			// Detect which C library is present on the system. See
			// https://unix.stackexchange.com/a/120381.
			cmd := exec.Command("ldd", "--version")
			out, err := cmd.Output()
			if err != nil {
				log.Printf("%s: out=%q err=%s", cmd.Args, out, err)
			} else if muslRE.Match(out) {
				return "-musl"
			}
			return "-gnu"
		}()
	}
	binaryName := fmt.Sprintf("cockroach.%s-%s", goos, runtime.GOARCH)
	if runtime.GOOS == "windows" {
		binaryName += ".exe"
	}
	url := &url.URL{
		Scheme: "https",
		Host:   "edge-binaries.cockroachdb.com",
		Path:   path.Join("cockroach", fmt.Sprintf("%s.%s", binaryName, latestSuffix)),
	}
	log.Printf("GET %s", url)
	response, err := http.Get(url.String())
	if err != nil {
		return "", err
	}
	defer response.Body.Close()

	if response.StatusCode != 200 {
		return "", fmt.Errorf("error downloading %s: %d (%s)", url, response.StatusCode, response.Status)
	}

	const contentDisposition = "Content-Disposition"
	_, disposition, err := mime.ParseMediaType(response.Header.Get(contentDisposition))
	if err != nil {
		return "", fmt.Errorf("error parsing %s headers %s: %s", contentDisposition, response.Header, err)
	}

	filename, ok := disposition["filename"]
	if !ok {
		return "", fmt.Errorf("content disposition header %s did not contain filename", disposition)
	}
	localFile := filepath.Join(os.TempDir(), filename)
	for {
		info, err := os.Stat(localFile)
		if os.IsNotExist(err) {
			// File does not exist: download it.
			break
		}
		if err != nil {
			return "", err
		}
		// File already present: check mode.
		if info.Mode().Perm() == finishedFileMode {
			return localFile, nil
		}
		time.Sleep(time.Millisecond * 10)
	}

	if err := downloadFile(response, localFile); err != nil {
		if err := os.Remove(localFile); err != nil {
			log.Printf("failed to remove %s: %s", localFile, err)
		}
		return "", err
	}

	return localFile, nil
}
<|endoftext|>