<commit_before>// Copyright 2016 Mehrdad Arshad Rad <arshad.rad@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package icmp

import (
	"net"
	"os"
	"syscall"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

const (
	// DefaultTXTimeout is socket send timeout
	DefaultTXTimeout int64 = 2000
	// ProtocolIPv4ICMP is IANA ICMP IPv4
	ProtocolIPv4ICMP = 1
	// ProtocolIPv6ICMP is IANA ICMP IPv6
	ProtocolIPv6ICMP = 58

	// IPv4ICMPTypeEchoReply is ICMPv4 Echo Reply
	IPv4ICMPTypeEchoReply = 0
	// IPv4ICMPTypeDestinationUnreachable is ICMPv4 Destination Unreachable
	IPv4ICMPTypeDestinationUnreachable = 3
	// IPv4ICMPTypeTimeExceeded is ICMPv4 Time Exceeded
	IPv4ICMPTypeTimeExceeded = 11

	// IPv6ICMPTypeEchoReply is ICMPv6 Echo Reply
	IPv6ICMPTypeEchoReply = 129
	// IPv6ICMPTypeDestinationUnreachable is ICMPv6 Destination Unreachable
	IPv6ICMPTypeDestinationUnreachable = 1
	//IPv6ICMPTypeTimeExceeded is ICMPv6 Time Exceeded
	IPv6ICMPTypeTimeExceeded = 3
)

// Trace represents trace properties
type Trace struct {
	src      string
	host     string
	ip       net.IP
	ips      []net.IP
	ttl      int
	fd       int
	family   int
	proto    int
	wait     string
	icmp     bool
	resolve  bool
	ripe     bool
	realTime bool
	pSize    int
	maxTTL   int
}

// Ping represents ping request
type Ping struct {
	m         icmp.Message
	id        int
	seq       int
	pSize     int
	count     int
	addr      *net.IPAddr
	addrs     []net.IP
	target    string
	isV4Avail bool
	isV6Avail bool
	isCIDR    bool
	forceV4   bool
	forceV6   bool
	network   string
	source    string
	timeout   time.Duration
	interval  time.Duration
	MaxRTT    time.Duration
}

// HopResp represents hop's response
type HopResp struct {
	num     int
	hop     string
	ip      string
	elapsed float64
	last    bool
	err     error
	whois   Whois
}

// ICMPResp represents ICMP response msg
type ICMPResp struct {
	typ  int
	code int
	id   int
	seq  int
	src  net.IP
	udp  struct{ dstPort int }
	ip   struct{ dst net.IP }
}

// Whois represents prefix info from RIPE
type Whois struct {
	holder string
	asn    float64
}

// Stats represents statistic's fields
type Stats struct {
	count int64   // sent packet
	min   float64 // minimum/best rtt
	avg   float64 // average rtt
	max   float64 // maximum/worst rtt
	pkl   int64   // packet loss
}

func bytesToIPv6(b []byte) net.IP {
	ip := make(net.IP, net.IPv6len)
	copy(ip, b)
	return ip
}

func icmpV4RespParser(b []byte) ICMPResp {
	var resp ICMPResp

	resp.typ = int(b[20])
	resp.code = int(b[21])
	resp.src = net.IPv4(b[12], b[13], b[14], b[15])

	switch resp.typ {
	case IPv4ICMPTypeEchoReply:
		resp.id = int(b[24])<<8 | int(b[25])
		resp.seq = int(b[26])<<8 | int(b[27])
	case IPv4ICMPTypeDestinationUnreachable:
		resp.ip.dst = net.IPv4(b[44], b[45], b[46], b[47])
	case IPv4ICMPTypeTimeExceeded:
		resp.id = int(b[52])<<8 | int(b[53])
		resp.seq = int(b[54])<<8 | int(b[55])
		resp.ip.dst = net.IPv4(b[44], b[45], b[46], b[47])
	}

	return resp
}

func icmpV6RespParser(b []byte) ICMPResp {
	var resp ICMPResp

	resp.typ = int(b[0])
	resp.code = int(b[1])

	//getting time exceeded w/ less than 32 bytes
	if len(b) < 44 {
		return resp
	}

	switch resp.typ {
	case IPv6ICMPTypeEchoReply:
		resp.id = int(b[44])<<8 | int(b[25])
		resp.seq = int(b[46])<<8 | int(b[27])
	case IPv6ICMPTypeDestinationUnreachable:
		resp.ip.dst = bytesToIPv6(b[32:48])
	case IPv6ICMPTypeTimeExceeded:
		resp.id = int(b[32])<<8 | int(b[33])
		resp.seq = int(b[34])<<8 | int(b[35])
		resp.ip.dst = bytesToIPv6(b[32:48])
	}

	return resp
}

func icmpV6Message(id, seq int) ([]byte, error) {
	m, err := (&icmp.Message{
		Type: ipv6.ICMPTypeEchoRequest, Code: 0,
		Body: &icmp.Echo{
			ID: id, Seq: seq,
			Data: []byte("myLG - [mylg.io]"),
		},
	}).Marshal(nil)

	if err != nil {
		return m, os.NewSyscallError("icmpmsg", err)
	}
	return m, nil
}

func icmpV4Message(id, seq int) ([]byte, error) {
	m, err := (&icmp.Message{
		Type: ipv4.ICMPTypeEcho, Code: 0,
		Body: &icmp.Echo{
			ID: id, Seq: seq,
			Data: []byte("myLG - [mylg.io]"),
		},
	}).Marshal(nil)

	if err != nil {
		return m, os.NewSyscallError("icmpmsg", err)
	}
	return m, nil
}

func setIPv4TOS(fd int, v int) error {
	err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TOS, v)
	if err != nil {
		return os.NewSyscallError("setsockopt", err)
	}
	return nil
}

func setIPv4TTL(fd int, v int) error {
	err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TTL, v)
	if err != nil {
		return os.NewSyscallError("setsockopt", err)
	}
	return nil
}

func setIPv6HopLimit(fd int, v int) error {
	err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v)
	if err != nil {
		return os.NewSyscallError("setsockopt", err)
	}
	return nil
}
<commit_msg>add uitheme to trace struct<commit_after>// Copyright 2016 Mehrdad Arshad Rad <arshad.rad@gmail.com>. All rights reserved.
// Use of this source code is governed by a MIT license that can
// be found in the LICENSE file.

package icmp

import (
	"net"
	"os"
	"syscall"
	"time"

	"golang.org/x/net/icmp"
	"golang.org/x/net/ipv4"
	"golang.org/x/net/ipv6"
)

const (
	// DefaultTXTimeout is socket send timeout
	DefaultTXTimeout int64 = 2000
	// ProtocolIPv4ICMP is IANA ICMP IPv4
	ProtocolIPv4ICMP = 1
	// ProtocolIPv6ICMP is IANA ICMP IPv6
	ProtocolIPv6ICMP = 58

	// IPv4ICMPTypeEchoReply is ICMPv4 Echo Reply
	IPv4ICMPTypeEchoReply = 0
	// IPv4ICMPTypeDestinationUnreachable is ICMPv4 Destination Unreachable
	IPv4ICMPTypeDestinationUnreachable = 3
	// IPv4ICMPTypeTimeExceeded is ICMPv4 Time Exceeded
	IPv4ICMPTypeTimeExceeded = 11

	// IPv6ICMPTypeEchoReply is ICMPv6 Echo Reply
	IPv6ICMPTypeEchoReply = 129
	// IPv6ICMPTypeDestinationUnreachable is ICMPv6 Destination Unreachable
	IPv6ICMPTypeDestinationUnreachable = 1
	//IPv6ICMPTypeTimeExceeded is ICMPv6 Time Exceeded
	IPv6ICMPTypeTimeExceeded = 3
)

// Trace represents trace properties
type Trace struct {
	src      string
	host     string
	ip       net.IP
	ips      []net.IP
	ttl      int
	fd       int
	family   int
	proto    int
	wait     string
	icmp     bool
	resolve  bool
	ripe     bool
	realTime bool
	pSize    int
	maxTTL   int

	uiTheme string
}

// Ping represents ping request
type Ping struct {
	m         icmp.Message
	id        int
	seq       int
	pSize     int
	count     int
	addr      *net.IPAddr
	addrs     []net.IP
	target    string
	isV4Avail bool
	isV6Avail bool
	isCIDR    bool
	forceV4   bool
	forceV6   bool
	network   string
	source    string
	timeout   time.Duration
	interval  time.Duration
	MaxRTT    time.Duration
}

// HopResp represents hop's response
type HopResp struct {
	num     int
	hop     string
	ip      string
	elapsed float64
	last    bool
	err     error
	whois   Whois
}

// ICMPResp represents ICMP response msg
type ICMPResp struct {
	typ  int
	code int
	id   int
	seq  int
	src  net.IP
	udp  struct{ dstPort int }
	ip   struct{ dst net.IP }
}

// Whois represents prefix info from RIPE
type Whois struct {
	holder string
	asn    float64
}

// Stats represents statistic's fields
type Stats struct {
	count int64   // sent packet
	min   float64 // minimum/best rtt
	avg   float64 // average rtt
	max   float64 // maximum/worst rtt
	pkl   int64   // packet loss
}

func bytesToIPv6(b []byte) net.IP {
	ip := make(net.IP, net.IPv6len)
	copy(ip, b)
	return ip
}

func icmpV4RespParser(b []byte) ICMPResp {
	var resp ICMPResp

	resp.typ = int(b[20])
	resp.code = int(b[21])
	resp.src = net.IPv4(b[12], b[13], b[14], b[15])

	switch resp.typ {
	case IPv4ICMPTypeEchoReply:
		resp.id = int(b[24])<<8 | int(b[25])
		resp.seq = int(b[26])<<8 | int(b[27])
	case IPv4ICMPTypeDestinationUnreachable:
		resp.ip.dst = net.IPv4(b[44], b[45], b[46], b[47])
	case IPv4ICMPTypeTimeExceeded:
		resp.id = int(b[52])<<8 | int(b[53])
		resp.seq = int(b[54])<<8 | int(b[55])
		resp.ip.dst = net.IPv4(b[44], b[45], b[46], b[47])
	}

	return resp
}

func icmpV6RespParser(b []byte) ICMPResp {
	var resp ICMPResp

	resp.typ = int(b[0])
	resp.code = int(b[1])

	//getting time exceeded w/ less than 32 bytes
	if len(b) < 44 {
		return resp
	}

	switch resp.typ {
	case IPv6ICMPTypeEchoReply:
		resp.id = int(b[44])<<8 | int(b[25])
		resp.seq = int(b[46])<<8 | int(b[27])
	case IPv6ICMPTypeDestinationUnreachable:
		resp.ip.dst = bytesToIPv6(b[32:48])
	case IPv6ICMPTypeTimeExceeded:
		resp.id = int(b[32])<<8 | int(b[33])
		resp.seq = int(b[34])<<8 | int(b[35])
		resp.ip.dst = bytesToIPv6(b[32:48])
	}

	return resp
}

func icmpV6Message(id, seq int) ([]byte, error) {
	m, err := (&icmp.Message{
		Type: ipv6.ICMPTypeEchoRequest, Code: 0,
		Body: &icmp.Echo{
			ID: id, Seq: seq,
			Data: []byte("myLG - [mylg.io]"),
		},
	}).Marshal(nil)

	if err != nil {
		return m, os.NewSyscallError("icmpmsg", err)
	}
	return m, nil
}

func icmpV4Message(id, seq int) ([]byte, error) {
	m, err := (&icmp.Message{
		Type: ipv4.ICMPTypeEcho, Code: 0,
		Body: &icmp.Echo{
			ID: id, Seq: seq,
			Data: []byte("myLG - [mylg.io]"),
		},
	}).Marshal(nil)

	if err != nil {
		return m, os.NewSyscallError("icmpmsg", err)
	}
	return m, nil
}

func setIPv4TOS(fd int, v int) error {
	err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TOS, v)
	if err != nil {
		return os.NewSyscallError("setsockopt", err)
	}
	return nil
}

func setIPv4TTL(fd int, v int) error {
	err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IP, syscall.IP_TTL, v)
	if err != nil {
		return os.NewSyscallError("setsockopt", err)
	}
	return nil
}

func setIPv6HopLimit(fd int, v int) error {
	err := syscall.SetsockoptInt(fd, syscall.IPPROTO_IPV6, syscall.IPV6_UNICAST_HOPS, v)
	if err != nil {
		return os.NewSyscallError("setsockopt", err)
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 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.

// The coordinator runs on GCE and coordinates builds in Docker containers.
package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"sort"
	"strings"
	"sync"
	"time"
)

var (
	masterKeyFile = flag.String("masterkey", "", "Path to builder master key. Else fetched using GCE project attribute 'builder-master-key'.")
	maxBuilds     = flag.Int("maxbuilds", 6, "Max concurrent builds")

	// Debug flags:
	addTemp = flag.Bool("temp", false, "Append -temp to all builders.")
	just    = flag.String("just", "", "If non-empty, run single build in the foreground. Requires rev.")
	rev     = flag.String("rev", "", "Revision to build.")
)

var (
	startTime = time.Now()
	builders  = map[string]buildConfig{} // populated once at startup
	donec     = make(chan builderRev)    // reports of finished builders

	statusMu sync.Mutex
	status   = map[builderRev]*buildStatus{}
)

type imageInfo struct {
	url string // of tar file

	mu      sync.Mutex
	lastMod string
}

var images = map[string]*imageInfo{
	"gobuilders/linux-x86-base": {url: "https://storage.googleapis.com/go-builder-data/docker-linux.base.tar.gz"},
	"gobuilders/linux-x86-nacl": {url: "https://storage.googleapis.com/go-builder-data/docker-linux.nacl.tar.gz"},
}

type buildConfig struct {
	name  string   // "linux-amd64-race"
	image string   // Docker image to use to build
	cmd   string   // optional -cmd flag (relative to go/src/)
	env   []string // extra environment ("key=value") pairs
}

func main() {
	flag.Parse()
	addBuilder(buildConfig{name: "linux-386"})
	addBuilder(buildConfig{name: "linux-386-387", env: []string{"GO386=387"}})
	addBuilder(buildConfig{name: "linux-amd64"})
	addBuilder(buildConfig{name: "linux-amd64-nocgo", env: []string{"CGO_ENABLED=0"}})
	addBuilder(buildConfig{name: "linux-amd64-race"})
	addBuilder(buildConfig{name: "nacl-386"})
	addBuilder(buildConfig{name: "nacl-amd64p32"})

	if (*just != "") != (*rev != "") {
		log.Fatalf("--just and --rev must be used together")
	}
	if *just != "" {
		conf, ok := builders[*just]
		if !ok {
			log.Fatalf("unknown builder %q", *just)
		}
		cmd := exec.Command("docker", append([]string{"run"}, conf.dockerRunArgs(*rev)...)...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			log.Fatalf("Build failed: %v", err)
		}
		return
	}

	http.HandleFunc("/", handleStatus)
	http.HandleFunc("/logs", handleLogs)
	go http.ListenAndServe(":80", nil)

	workc := make(chan builderRev)
	for name := range builders {
		go findWorkLoop(name, workc)
	}

	ticker := time.NewTicker(1 * time.Minute)
	for {
		select {
		case work := <-workc:
			log.Printf("workc received %+v; len(status) = %v, maxBuilds = %v; cur = %p", work, len(status), *maxBuilds, status[work])
			mayBuild := mayBuildRev(work)
			if mayBuild {
				out, _ := exec.Command("docker", "ps").Output()
				numBuilds := bytes.Count(out, []byte("\n")) - 1
				log.Printf("num current docker builds: %d", numBuilds)
				if numBuilds > *maxBuilds {
					mayBuild = false
				}
			}
			if mayBuild {
				if st, err := startBuilding(builders[work.name], work.rev); err == nil {
					setStatus(work, st)
					log.Printf("%v now building in %v", work, st.container)
				} else {
					log.Printf("Error starting to build %v: %v", work, err)
				}
			}
		case done := <-donec:
			log.Printf("%v done", done)
			setStatus(done, nil)
		case <-ticker.C:
			if numCurrentBuilds() == 0 && time.Now().After(startTime.Add(10*time.Minute)) {
				// TODO: halt the whole machine to kill the VM or something
			}
		}
	}
}

func numCurrentBuilds() int {
	statusMu.Lock()
	defer statusMu.Unlock()
	return len(status)
}

func mayBuildRev(work builderRev) bool {
	statusMu.Lock()
	defer statusMu.Unlock()
	return len(status) < *maxBuilds && status[work] == nil
}

func setStatus(work builderRev, st *buildStatus) {
	statusMu.Lock()
	defer statusMu.Unlock()
	if st == nil {
		delete(status, work)
	} else {
		status[work] = st
	}
}

func getStatus(work builderRev) *buildStatus {
	statusMu.Lock()
	defer statusMu.Unlock()
	return status[work]
}

type byAge []*buildStatus

func (s byAge) Len() int           { return len(s) }
func (s byAge) Less(i, j int) bool { return s[i].start.Before(s[j].start) }
func (s byAge) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func handleStatus(w http.ResponseWriter, r *http.Request) {
	var active []*buildStatus
	statusMu.Lock()
	for _, st := range status {
		active = append(active, st)
	}
	statusMu.Unlock()

	fmt.Fprintf(w, "
Go build coordinator
%d of max %d builds running:

", len(status), *maxBuilds)
	sort.Sort(byAge(active))
	for _, st := range active {
		fmt.Fprintf(w, "%-22s hg %s in container %s, %v ago\n", st.name, st.rev, st.name, st.rev,
			st.container, time.Now().Sub(st.start))
	}
	fmt.Fprintf(w, "
")
}

func handleLogs(w http.ResponseWriter, r *http.Request) {
	st := getStatus(builderRev{r.FormValue("name"), r.FormValue("rev")})
	if st == nil {
		fmt.Fprintf(w, "
not building
")
		return
	}
	out, err := exec.Command("docker", "logs", st.container).CombinedOutput()
	if err != nil {
		log.Print(err)
		http.Error(w, "Error fetching logs. Already finished?", 500)
		return
	}
	key := builderKey(st.name)
	logs := strings.Replace(string(out), key, "BUILDERKEY", -1)
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	io.WriteString(w, logs)
}

func findWorkLoop(builderName string, work chan<- builderRev) {
	// TODO: make this better
	for {
		rev, err := findWork(builderName)
		if err != nil {
			log.Printf("Finding work for %s: %v", builderName, err)
		} else if rev != "" {
			work <- builderRev{builderName, rev}
		}
		time.Sleep(60 * time.Second)
	}
}

func findWork(builderName string) (rev string, err error) {
	var jres struct {
		Response struct {
			Kind string
			Data struct {
				Hash        string
				PerfResults []string
			}
		}
	}
	res, err := http.Get("https://build.golang.org/todo?builder=" + builderName + "&kind=build-go-commit")
	if err != nil {
		return
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return "", fmt.Errorf("unexpected http status %d", res.StatusCode)
	}
	err = json.NewDecoder(res.Body).Decode(&jres)
	if jres.Response.Kind == "build-go-commit" {
		rev = jres.Response.Data.Hash
	}
	return rev, err
}

type builderRev struct {
	name, rev string
}

// returns the part after "docker run"
func (conf buildConfig) dockerRunArgs(rev string) (args []string) {
	if key := builderKey(conf.name); key != "" {
		tmpKey := "/tmp/" + conf.name + ".buildkey"
		if _, err := os.Stat(tmpKey); err != nil {
			if err := ioutil.WriteFile(tmpKey, []byte(key), 0600); err != nil {
				log.Fatal(err)
			}
		}
		args = append(args, "-v", tmpKey+":/.gobuildkey")
	}
	for _, pair := range conf.env {
		args = append(args, "-e", pair)
	}
	args = append(args,
		conf.image,
		"/usr/local/bin/builder",
		"-rev="+rev,
		"-buildroot=/",
		"-v",
	)
	if conf.cmd != "" {
		args = append(args, "-cmd", conf.cmd)
	}
	args = append(args, conf.name)
	return
}

func addBuilder(c buildConfig) {
	if c.name == "" {
		panic("empty name")
	}
	if *addTemp {
		c.name += "-temp"
	}
	if _, dup := builders[c.name]; dup {
		panic("dup name")
	}
	if strings.HasPrefix(c.name, "nacl-") {
		if c.image == "" {
			c.image = "gobuilders/linux-x86-nacl"
		}
		if c.cmd == "" {
			c.cmd = "/usr/local/bin/build-command.pl"
		}
	}
	if strings.HasPrefix(c.name, "linux-") && c.image == "" {
		c.image = "gobuilders/linux-x86-base"
	}
	if c.image == "" {
		panic("empty image")
	}
	builders[c.name] = c
}

func condUpdateImage(img string) error {
	ii := images[img]
	if ii == nil {
		log.Fatalf("Image %q not described.", img)
	}
	ii.mu.Lock()
	defer ii.mu.Unlock()
	res, err := http.Head(ii.url)
	if err != nil {
		return fmt.Errorf("Error checking %s: %v", ii.url, err)
	}
	if res.StatusCode != 200 {
		return fmt.Errorf("Error checking %s: %v", ii.url, res.Status)
	}
	if res.Header.Get("Last-Modified") == ii.lastMod {
		return nil
	}

	res, err = http.Get(ii.url)
	if err != nil || res.StatusCode != 200 {
		return fmt.Errorf("Get after Head failed for %s: %v, %v", ii.url, err, res)
	}
	defer res.Body.Close()

	log.Printf("Running: docker load of %s\n", ii.url)
	cmd := exec.Command("docker", "load")
	cmd.Stdin = res.Body

	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out

	if cmd.Run(); err != nil {
		log.Printf("Failed to pull latest %s from %s and pipe into docker load: %v, %s", img, ii.url, err, out.Bytes())
		return err
	}
	ii.lastMod = res.Header.Get("Last-Modified")
	return nil
}

func startBuilding(conf buildConfig, rev string) (*buildStatus, error) {
	if err := condUpdateImage(conf.image); err != nil {
		log.Printf("Failed to setup container for %v %v: %v", conf.name, rev, err)
		return nil, err
	}

	cmd := exec.Command("docker", append([]string{"run", "-d"}, conf.dockerRunArgs(rev)...)...)
	all, err := cmd.CombinedOutput()
	log.Printf("Docker run for %v %v = err:%v, output:%s", conf.name, rev, err, all)
	if err != nil {
		return nil, err
	}
	container := strings.TrimSpace(string(all))
	go func() {
		all, err := exec.Command("docker", "wait", container).CombinedOutput()
		log.Printf("docker wait %s: %v, %s", container, err, strings.TrimSpace(string(all)))
		donec <- builderRev{conf.name, rev}
		exec.Command("docker", "rm", container).Run()
	}()
	return &buildStatus{
		builderRev: builderRev{
			name: conf.name,
			rev:  rev,
		},
		container: container,
		start:     time.Now(),
	}, nil
}

type buildStatus struct {
	builderRev
	container string
	start     time.Time

	mu sync.Mutex
	// ...
}

func builderKey(builder string) string {
	master := masterKey()
	if len(master) == 0 {
		return ""
	}
	h := hmac.New(md5.New, master)
	io.WriteString(h, builder)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func masterKey() []byte {
	keyOnce.Do(loadKey)
	return masterKeyCache
}

var (
	keyOnce        sync.Once
	masterKeyCache []byte
)

func loadKey() {
	if *masterKeyFile != "" {
		b, err := ioutil.ReadFile(*masterKeyFile)
		if err != nil {
			log.Fatal(err)
		}
		masterKeyCache = bytes.TrimSpace(b)
		return
	}
	req, _ := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/project/attributes/builder-master-key", nil)
	req.Header.Set("Metadata-Flavor", "Google")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal("No builder master key available")
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("No builder-master-key project attribute available.")
	}
	slurp, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	masterKeyCache = bytes.TrimSpace(slurp)
}
<commit_msg>dashboard/coordinator: set USER for the -nocgo builder<commit_after>// Copyright 2014 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.

// The coordinator runs on GCE and coordinates builds in Docker containers.
package main

import (
	"bytes"
	"crypto/hmac"
	"crypto/md5"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
	"os"
	"os/exec"
	"sort"
	"strings"
	"sync"
	"time"
)

var (
	masterKeyFile = flag.String("masterkey", "", "Path to builder master key. Else fetched using GCE project attribute 'builder-master-key'.")
	maxBuilds     = flag.Int("maxbuilds", 6, "Max concurrent builds")

	// Debug flags:
	addTemp = flag.Bool("temp", false, "Append -temp to all builders.")
	just    = flag.String("just", "", "If non-empty, run single build in the foreground. Requires rev.")
	rev     = flag.String("rev", "", "Revision to build.")
)

var (
	startTime = time.Now()
	builders  = map[string]buildConfig{} // populated once at startup
	donec     = make(chan builderRev)    // reports of finished builders

	statusMu sync.Mutex
	status   = map[builderRev]*buildStatus{}
)

type imageInfo struct {
	url string // of tar file

	mu      sync.Mutex
	lastMod string
}

var images = map[string]*imageInfo{
	"gobuilders/linux-x86-base": {url: "https://storage.googleapis.com/go-builder-data/docker-linux.base.tar.gz"},
	"gobuilders/linux-x86-nacl": {url: "https://storage.googleapis.com/go-builder-data/docker-linux.nacl.tar.gz"},
}

type buildConfig struct {
	name  string   // "linux-amd64-race"
	image string   // Docker image to use to build
	cmd   string   // optional -cmd flag (relative to go/src/)
	env   []string // extra environment ("key=value") pairs
}

func main() {
	flag.Parse()
	addBuilder(buildConfig{name: "linux-386"})
	addBuilder(buildConfig{name: "linux-386-387", env: []string{"GO386=387"}})
	addBuilder(buildConfig{name: "linux-amd64"})
	addBuilder(buildConfig{name: "linux-amd64-nocgo", env: []string{"CGO_ENABLED=0", "USER=root"}})
	addBuilder(buildConfig{name: "linux-amd64-race"})
	addBuilder(buildConfig{name: "nacl-386"})
	addBuilder(buildConfig{name: "nacl-amd64p32"})

	if (*just != "") != (*rev != "") {
		log.Fatalf("--just and --rev must be used together")
	}
	if *just != "" {
		conf, ok := builders[*just]
		if !ok {
			log.Fatalf("unknown builder %q", *just)
		}
		cmd := exec.Command("docker", append([]string{"run"}, conf.dockerRunArgs(*rev)...)...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		if err := cmd.Run(); err != nil {
			log.Fatalf("Build failed: %v", err)
		}
		return
	}

	http.HandleFunc("/", handleStatus)
	http.HandleFunc("/logs", handleLogs)
	go http.ListenAndServe(":80", nil)

	workc := make(chan builderRev)
	for name := range builders {
		go findWorkLoop(name, workc)
	}

	ticker := time.NewTicker(1 * time.Minute)
	for {
		select {
		case work := <-workc:
			log.Printf("workc received %+v; len(status) = %v, maxBuilds = %v; cur = %p", work, len(status), *maxBuilds, status[work])
			mayBuild := mayBuildRev(work)
			if mayBuild {
				out, _ := exec.Command("docker", "ps").Output()
				numBuilds := bytes.Count(out, []byte("\n")) - 1
				log.Printf("num current docker builds: %d", numBuilds)
				if numBuilds > *maxBuilds {
					mayBuild = false
				}
			}
			if mayBuild {
				if st, err := startBuilding(builders[work.name], work.rev); err == nil {
					setStatus(work, st)
					log.Printf("%v now building in %v", work, st.container)
				} else {
					log.Printf("Error starting to build %v: %v", work, err)
				}
			}
		case done := <-donec:
			log.Printf("%v done", done)
			setStatus(done, nil)
		case <-ticker.C:
			if numCurrentBuilds() == 0 && time.Now().After(startTime.Add(10*time.Minute)) {
				// TODO: halt the whole machine to kill the VM or something
			}
		}
	}
}

func numCurrentBuilds() int {
	statusMu.Lock()
	defer statusMu.Unlock()
	return len(status)
}

func mayBuildRev(work builderRev) bool {
	statusMu.Lock()
	defer statusMu.Unlock()
	return len(status) < *maxBuilds && status[work] == nil
}

func setStatus(work builderRev, st *buildStatus) {
	statusMu.Lock()
	defer statusMu.Unlock()
	if st == nil {
		delete(status, work)
	} else {
		status[work] = st
	}
}

func getStatus(work builderRev) *buildStatus {
	statusMu.Lock()
	defer statusMu.Unlock()
	return status[work]
}

type byAge []*buildStatus

func (s byAge) Len() int           { return len(s) }
func (s byAge) Less(i, j int) bool { return s[i].start.Before(s[j].start) }
func (s byAge) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }

func handleStatus(w http.ResponseWriter, r *http.Request) {
	var active []*buildStatus
	statusMu.Lock()
	for _, st := range status {
		active = append(active, st)
	}
	statusMu.Unlock()

	fmt.Fprintf(w, "
Go build coordinator
%d of max %d builds running:

", len(status), *maxBuilds)
	sort.Sort(byAge(active))
	for _, st := range active {
		fmt.Fprintf(w, "%-22s hg %s in container %s, %v ago\n", st.name, st.rev, st.name, st.rev,
			st.container, time.Now().Sub(st.start))
	}
	fmt.Fprintf(w, "
")
}

func handleLogs(w http.ResponseWriter, r *http.Request) {
	st := getStatus(builderRev{r.FormValue("name"), r.FormValue("rev")})
	if st == nil {
		fmt.Fprintf(w, "
not building
")
		return
	}
	out, err := exec.Command("docker", "logs", st.container).CombinedOutput()
	if err != nil {
		log.Print(err)
		http.Error(w, "Error fetching logs. Already finished?", 500)
		return
	}
	key := builderKey(st.name)
	logs := strings.Replace(string(out), key, "BUILDERKEY", -1)
	w.Header().Set("Content-Type", "text/plain; charset=utf-8")
	io.WriteString(w, logs)
}

func findWorkLoop(builderName string, work chan<- builderRev) {
	// TODO: make this better
	for {
		rev, err := findWork(builderName)
		if err != nil {
			log.Printf("Finding work for %s: %v", builderName, err)
		} else if rev != "" {
			work <- builderRev{builderName, rev}
		}
		time.Sleep(60 * time.Second)
	}
}

func findWork(builderName string) (rev string, err error) {
	var jres struct {
		Response struct {
			Kind string
			Data struct {
				Hash        string
				PerfResults []string
			}
		}
	}
	res, err := http.Get("https://build.golang.org/todo?builder=" + builderName + "&kind=build-go-commit")
	if err != nil {
		return
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		return "", fmt.Errorf("unexpected http status %d", res.StatusCode)
	}
	err = json.NewDecoder(res.Body).Decode(&jres)
	if jres.Response.Kind == "build-go-commit" {
		rev = jres.Response.Data.Hash
	}
	return rev, err
}

type builderRev struct {
	name, rev string
}

// returns the part after "docker run"
func (conf buildConfig) dockerRunArgs(rev string) (args []string) {
	if key := builderKey(conf.name); key != "" {
		tmpKey := "/tmp/" + conf.name + ".buildkey"
		if _, err := os.Stat(tmpKey); err != nil {
			if err := ioutil.WriteFile(tmpKey, []byte(key), 0600); err != nil {
				log.Fatal(err)
			}
		}
		args = append(args, "-v", tmpKey+":/.gobuildkey")
	}
	for _, pair := range conf.env {
		args = append(args, "-e", pair)
	}
	args = append(args,
		conf.image,
		"/usr/local/bin/builder",
		"-rev="+rev,
		"-buildroot=/",
		"-v",
	)
	if conf.cmd != "" {
		args = append(args, "-cmd", conf.cmd)
	}
	args = append(args, conf.name)
	return
}

func addBuilder(c buildConfig) {
	if c.name == "" {
		panic("empty name")
	}
	if *addTemp {
		c.name += "-temp"
	}
	if _, dup := builders[c.name]; dup {
		panic("dup name")
	}
	if strings.HasPrefix(c.name, "nacl-") {
		if c.image == "" {
			c.image = "gobuilders/linux-x86-nacl"
		}
		if c.cmd == "" {
			c.cmd = "/usr/local/bin/build-command.pl"
		}
	}
	if strings.HasPrefix(c.name, "linux-") && c.image == "" {
		c.image = "gobuilders/linux-x86-base"
	}
	if c.image == "" {
		panic("empty image")
	}
	builders[c.name] = c
}

func condUpdateImage(img string) error {
	ii := images[img]
	if ii == nil {
		log.Fatalf("Image %q not described.", img)
	}
	ii.mu.Lock()
	defer ii.mu.Unlock()
	res, err := http.Head(ii.url)
	if err != nil {
		return fmt.Errorf("Error checking %s: %v", ii.url, err)
	}
	if res.StatusCode != 200 {
		return fmt.Errorf("Error checking %s: %v", ii.url, res.Status)
	}
	if res.Header.Get("Last-Modified") == ii.lastMod {
		return nil
	}

	res, err = http.Get(ii.url)
	if err != nil || res.StatusCode != 200 {
		return fmt.Errorf("Get after Head failed for %s: %v, %v", ii.url, err, res)
	}
	defer res.Body.Close()

	log.Printf("Running: docker load of %s\n", ii.url)
	cmd := exec.Command("docker", "load")
	cmd.Stdin = res.Body

	var out bytes.Buffer
	cmd.Stdout = &out
	cmd.Stderr = &out

	if cmd.Run(); err != nil {
		log.Printf("Failed to pull latest %s from %s and pipe into docker load: %v, %s", img, ii.url, err, out.Bytes())
		return err
	}
	ii.lastMod = res.Header.Get("Last-Modified")
	return nil
}

func startBuilding(conf buildConfig, rev string) (*buildStatus, error) {
	if err := condUpdateImage(conf.image); err != nil {
		log.Printf("Failed to setup container for %v %v: %v", conf.name, rev, err)
		return nil, err
	}

	cmd := exec.Command("docker", append([]string{"run", "-d"}, conf.dockerRunArgs(rev)...)...)
	all, err := cmd.CombinedOutput()
	log.Printf("Docker run for %v %v = err:%v, output:%s", conf.name, rev, err, all)
	if err != nil {
		return nil, err
	}
	container := strings.TrimSpace(string(all))
	go func() {
		all, err := exec.Command("docker", "wait", container).CombinedOutput()
		log.Printf("docker wait %s: %v, %s", container, err, strings.TrimSpace(string(all)))
		donec <- builderRev{conf.name, rev}
		exec.Command("docker", "rm", container).Run()
	}()
	return &buildStatus{
		builderRev: builderRev{
			name: conf.name,
			rev:  rev,
		},
		container: container,
		start:     time.Now(),
	}, nil
}

type buildStatus struct {
	builderRev
	container string
	start     time.Time

	mu sync.Mutex
	// ...
}

func builderKey(builder string) string {
	master := masterKey()
	if len(master) == 0 {
		return ""
	}
	h := hmac.New(md5.New, master)
	io.WriteString(h, builder)
	return fmt.Sprintf("%x", h.Sum(nil))
}

func masterKey() []byte {
	keyOnce.Do(loadKey)
	return masterKeyCache
}

var (
	keyOnce        sync.Once
	masterKeyCache []byte
)

func loadKey() {
	if *masterKeyFile != "" {
		b, err := ioutil.ReadFile(*masterKeyFile)
		if err != nil {
			log.Fatal(err)
		}
		masterKeyCache = bytes.TrimSpace(b)
		return
	}
	req, _ := http.NewRequest("GET", "http://metadata.google.internal/computeMetadata/v1/project/attributes/builder-master-key", nil)
	req.Header.Set("Metadata-Flavor", "Google")
	res, err := http.DefaultClient.Do(req)
	if err != nil {
		log.Fatal("No builder master key available")
	}
	defer res.Body.Close()
	if res.StatusCode != 200 {
		log.Fatalf("No builder-master-key project attribute available.")
	}
	slurp, err := ioutil.ReadAll(res.Body)
	if err != nil {
		log.Fatal(err)
	}
	masterKeyCache = bytes.TrimSpace(slurp)
}
<|endoftext|>


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

// OutlierReport API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/report

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// OutlierReport defines a outlier report
type OutlierReport struct {
	CID              string   `json:"_cid,omitempty"`
	Created          uint     `json:"_created,omitempty"`
	CreatedBy        string   `json:"_created_by,omitempty"`
	LastModified     uint     `json:"_last_modified,omitempty"`
	LastModifiedBy   string   `json:"_last_modified_by,omitempty"`
	Config           string   `json:"config,omitempty"`
	MetricClusterCID string   `json:"metric_cluster,omitempty"`
	Tags             []string `json:"tags,omitempty"`
	Title            string   `json:"title,omitempty"`
}

// NewOutlierReport returns a new OutlierReport (with defaults, if applicable)
func NewOutlierReport() *OutlierReport {
	return &OutlierReport{}
}

// FetchOutlierReport retrieves outlier report with passed cid.
func (a *API) FetchOutlierReport(cid CIDType) (*OutlierReport, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid outlier report CID [none]")
	}

	reportCID := string(*cid)

	matched, err := regexp.MatchString(config.OutlierReportCIDRegex, reportCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid outlier report CID [%s]", reportCID)
	}

	result, err := a.Get(reportCID)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch outlier report, received JSON: %s", string(result))
	}

	report := &OutlierReport{}
	if err := json.Unmarshal(result, report); err != nil {
		return nil, err
	}

	return report, nil
}

// FetchOutlierReports retrieves all outlier reports
func (a *API) FetchOutlierReports() (*[]OutlierReport, error) {
	result, err := a.Get(config.OutlierReportPrefix)
	if err != nil {
		return nil, err
	}

	var reports []OutlierReport
	if err := json.Unmarshal(result, &reports); err != nil {
		return nil, err
	}

	return &reports, nil
}

// UpdateOutlierReport update outlier report definition
func (a *API) UpdateOutlierReport(cfg *OutlierReport) (*OutlierReport, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid outlier report config [nil]")
	}

	reportCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.OutlierReportCIDRegex, reportCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid outlier report CID [%s]", reportCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update outlier report, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(reportCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	report := &OutlierReport{}
	if err := json.Unmarshal(result, report); err != nil {
		return nil, err
	}

	return report, nil
}

// CreateOutlierReport create a new outlier report
func (a *API) CreateOutlierReport(cfg *OutlierReport) (*OutlierReport, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid outlier report config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create outlier report, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.OutlierReportPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	report := &OutlierReport{}
	if err := json.Unmarshal(result, report); err != nil {
		return nil, err
	}

	return report, nil
}

// DeleteOutlierReport delete a report
func (a *API) DeleteOutlierReport(cfg *OutlierReport) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid report config [none]")
	}
	return a.DeleteOutlierReportByCID(CIDType(&cfg.CID))
}

// DeleteOutlierReportByCID delete a outlier report by cid
func (a *API) DeleteOutlierReportByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid outlier report CID [none]")
	}

	reportCID := string(*cid)

	matched, err := regexp.MatchString(config.OutlierReportCIDRegex, reportCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid outlier report CID [%s]", reportCID)
	}

	_, err = a.Delete(reportCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchOutlierReports returns list of outlier reports matching a search query and/or filter
//    - a search query (see: https://login.circonus.com/resources/api#searching)
//    - a filter (see: https://login.circonus.com/resources/api#filtering)
func (a *API) SearchOutlierReports(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]OutlierReport, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchOutlierReports()
	}

	reqURL := url.URL{
		Path:     config.OutlierReportPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var reports []OutlierReport
	if err := json.Unmarshal(result, &reports); err != nil {
		return nil, err
	}

	return &reports, nil
}
<commit_msg>upd: documentation<commit_after>// Copyright 2016 Circonus, Inc. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// OutlierReport API support - Fetch, Create, Update, Delete, and Search
// See: https://login.circonus.com/resources/api/calls/report

package api

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"

	"github.com/circonus-labs/circonus-gometrics/api/config"
)

// OutlierReport defines a outlier report. See https://login.circonus.com/resources/api/calls/report for more information.
type OutlierReport struct {
	CID              string   `json:"_cid,omitempty"`
	Created          uint     `json:"_created,omitempty"`
	CreatedBy        string   `json:"_created_by,omitempty"`
	LastModified     uint     `json:"_last_modified,omitempty"`
	LastModifiedBy   string   `json:"_last_modified_by,omitempty"`
	Config           string   `json:"config,omitempty"`
	MetricClusterCID string   `json:"metric_cluster,omitempty"`
	Tags             []string `json:"tags,omitempty"`
	Title            string   `json:"title,omitempty"`
}

// NewOutlierReport returns a new OutlierReport (with defaults, if applicable)
func NewOutlierReport() *OutlierReport {
	return &OutlierReport{}
}

// FetchOutlierReport retrieves outlier report with passed cid.
func (a *API) FetchOutlierReport(cid CIDType) (*OutlierReport, error) {
	if cid == nil || *cid == "" {
		return nil, fmt.Errorf("Invalid outlier report CID [none]")
	}

	reportCID := string(*cid)

	matched, err := regexp.MatchString(config.OutlierReportCIDRegex, reportCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid outlier report CID [%s]", reportCID)
	}

	result, err := a.Get(reportCID)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] fetch outlier report, received JSON: %s", string(result))
	}

	report := &OutlierReport{}
	if err := json.Unmarshal(result, report); err != nil {
		return nil, err
	}

	return report, nil
}

// FetchOutlierReports retrieves all outlier reports available to API Token.
func (a *API) FetchOutlierReports() (*[]OutlierReport, error) {
	result, err := a.Get(config.OutlierReportPrefix)
	if err != nil {
		return nil, err
	}

	var reports []OutlierReport
	if err := json.Unmarshal(result, &reports); err != nil {
		return nil, err
	}

	return &reports, nil
}

// UpdateOutlierReport updates passed outlier report.
func (a *API) UpdateOutlierReport(cfg *OutlierReport) (*OutlierReport, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid outlier report config [nil]")
	}

	reportCID := string(cfg.CID)

	matched, err := regexp.MatchString(config.OutlierReportCIDRegex, reportCID)
	if err != nil {
		return nil, err
	}
	if !matched {
		return nil, fmt.Errorf("Invalid outlier report CID [%s]", reportCID)
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] update outlier report, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Put(reportCID, jsonCfg)
	if err != nil {
		return nil, err
	}

	report := &OutlierReport{}
	if err := json.Unmarshal(result, report); err != nil {
		return nil, err
	}

	return report, nil
}

// CreateOutlierReport creates a new outlier report.
func (a *API) CreateOutlierReport(cfg *OutlierReport) (*OutlierReport, error) {
	if cfg == nil {
		return nil, fmt.Errorf("Invalid outlier report config [nil]")
	}

	jsonCfg, err := json.Marshal(cfg)
	if err != nil {
		return nil, err
	}

	if a.Debug {
		a.Log.Printf("[DEBUG] create outlier report, sending JSON: %s", string(jsonCfg))
	}

	result, err := a.Post(config.OutlierReportPrefix, jsonCfg)
	if err != nil {
		return nil, err
	}

	report := &OutlierReport{}
	if err := json.Unmarshal(result, report); err != nil {
		return nil, err
	}

	return report, nil
}

// DeleteOutlierReport deletes passed outlier report.
func (a *API) DeleteOutlierReport(cfg *OutlierReport) (bool, error) {
	if cfg == nil {
		return false, fmt.Errorf("Invalid outlier report config [nil]")
	}
	return a.DeleteOutlierReportByCID(CIDType(&cfg.CID))
}

// DeleteOutlierReportByCID deletes outlier report with passed cid.
func (a *API) DeleteOutlierReportByCID(cid CIDType) (bool, error) {
	if cid == nil || *cid == "" {
		return false, fmt.Errorf("Invalid outlier report CID [none]")
	}

	reportCID := string(*cid)

	matched, err := regexp.MatchString(config.OutlierReportCIDRegex, reportCID)
	if err != nil {
		return false, err
	}
	if !matched {
		return false, fmt.Errorf("Invalid outlier report CID [%s]", reportCID)
	}

	_, err = a.Delete(reportCID)
	if err != nil {
		return false, err
	}

	return true, nil
}

// SearchOutlierReports returns outlier report matching the
// specified search query and/or filter. If nil is passed for
// both parameters all outlier report will be returned.
func (a *API) SearchOutlierReports(searchCriteria *SearchQueryType, filterCriteria *SearchFilterType) (*[]OutlierReport, error) {
	q := url.Values{}

	if searchCriteria != nil && *searchCriteria != "" {
		q.Set("search", string(*searchCriteria))
	}

	if filterCriteria != nil && len(*filterCriteria) > 0 {
		for filter, criteria := range *filterCriteria {
			for _, val := range criteria {
				q.Add(filter, val)
			}
		}
	}

	if q.Encode() == "" {
		return a.FetchOutlierReports()
	}

	reqURL := url.URL{
		Path:     config.OutlierReportPrefix,
		RawQuery: q.Encode(),
	}

	result, err := a.Get(reqURL.String())
	if err != nil {
		return nil, fmt.Errorf("[ERROR] API call error %+v", err)
	}

	var reports []OutlierReport
	if err := json.Unmarshal(result, &reports); err != nil {
		return nil, err
	}

	return &reports, nil
}
<|endoftext|>


<commit_before>package pairwise

import (
	"math"

	"gonum.org/v1/gonum/mat"
)

type Euclidean struct{}

func NewEuclidean() *Euclidean {
	return &Euclidean{}
}

// InnerProduct computes a Eucledian inner product.
func (e *Euclidean) InnerProduct(vectorX *mat.Dense, vectorY *mat.Dense) float64 {
	subVector := mat.NewDense(0, 0, nil)
	subVector.MulElem(vectorX, vectorY)
	result := mat.Sum(subVector)

	return result
}

// Distance computes Euclidean distance (also known as L2 distance).
func (e *Euclidean) Distance(vectorX *mat.Dense, vectorY *mat.Dense) float64 {
	subVector := mat.NewDense(0, 0, nil)
	subVector.Sub(vectorX, vectorY)

	result := e.InnerProduct(subVector, subVector)

	return math.Sqrt(result)
}
<commit_msg>resolves #222<commit_after>package pairwise

import (
	"math"

	"gonum.org/v1/gonum/mat"
)

type Euclidean struct{}

func NewEuclidean() *Euclidean {
	return &Euclidean{}
}

// InnerProduct computes a Eucledian inner product.
func (e *Euclidean) InnerProduct(vectorX *mat.Dense, vectorY *mat.Dense) float64 {
	subVector := mat.NewDense(1, 1, nil)
	subVector.Reset()
	subVector.MulElem(vectorX, vectorY)
	result := mat.Sum(subVector)

	return result
}

// Distance computes Euclidean distance (also known as L2 distance).
func (e *Euclidean) Distance(vectorX *mat.Dense, vectorY *mat.Dense) float64 {
	subVector := mat.NewDense(1, 1, nil)
	subVector.Reset()
	subVector.Sub(vectorX, vectorY)

	result := e.InnerProduct(subVector, subVector)

	return math.Sqrt(result)
}
<|endoftext|>


<commit_before>// Package eval handles evaluation of nodes and consists the runtime of the
// shell.
package eval

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
	"unicode/utf8"

	"github.com/elves/elvish/errutil"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
)

const FnPrefix = "fn-"

// ns is a namespace.
type ns map[string]Variable

// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
	global      ns
	mod         map[string]ns
	searchPaths []string
	store       *store.Store
}

// evalCtx maintains an Evaler along with its runtime context. After creation
// an evalCtx is not modified, and new instances are created when needed.
type evalCtx struct {
	*Evaler
	name, text, context string

	local, up ns
	ports     []*port
}

func HasFailure(vs []Value) bool {
	for _, v := range vs {
		v, ok := v.(exitus)
		if !ok {
			// Silently ignore non-exitus values
			continue
		}
		if v.Sort != Ok {
			return true
		}
	}
	return false
}

// NewEvaler creates a new Evaler.
func NewEvaler(st *store.Store, dataDir string) *Evaler {
	// Construct searchPaths
	var searchPaths []string
	if path := os.Getenv("PATH"); path != "" {
		searchPaths = strings.Split(path, ":")
	} else {
		searchPaths = []string{"/bin"}
	}

	// Construct initial global namespace
	pid := str(strconv.Itoa(syscall.Getpid()))
	paths := newTable()
	paths.appendStrings(searchPaths)
	global := ns{
		"pid":   newInternalVariable(pid),
		"ok":    newInternalVariable(ok),
		"true":  newInternalVariable(boolean(true)),
		"false": newInternalVariable(boolean(false)),
		"paths": newInternalVariable(paths),
	}
	for _, b := range builtinFns {
		global[FnPrefix+b.Name] = newInternalVariable(b)
	}

	return &Evaler{global, map[string]ns{}, searchPaths, st}
}

func printExitus(e exitus) {
	switch e.Sort {
	case Ok:
		fmt.Print("\033[32mok\033[m")
	case Failure:
		fmt.Print("\033[31;1m" + e.Failure + "\033[m")
	case Traceback:
		fmt.Print("(")
		for i, c := range e.Traceback.causes {
			if i > 0 {
				fmt.Print(" | ")
			}
			printExitus(c)
		}
		fmt.Print(")")
	default:
		// Control flow sorts
		fmt.Print("\033[33m" + flowExitusNames[e.Sort] + "\033[m")
	}
}

func PrintExituses(vs []Value) {
	if !HasFailure(vs) {
		return
	}
	fmt.Print("Status: ")
	for i, v := range vs {
		if i > 0 {
			fmt.Print(" | ")
		}
		printExitus(v.(exitus))
	}
	fmt.Println()
}

const (
	outChanSize   = 32
	outChanLeader = "▶ "
)

// newTopEvalCtx creates a top-level evalCtx.
func newTopEvalCtx(ev *Evaler, name, text string) *evalCtx {
	ch := make(chan Value, outChanSize)
	go func() {
		for v := range ch {
			fmt.Printf("%s%s\n", outChanLeader, v.Repr())
		}
	}()

	return &evalCtx{
		ev,
		name, text, "top",
		ev.global, ns{},
		[]*port{{f: os.Stdin},
			{f: os.Stdout, ch: ch, closeCh: true}, {f: os.Stderr}},
	}
}

// copy returns a copy of ec. The ports are copied deeply, with shouldClose
// flags reset, and the context is changed to the given value. Other fields are
// copied shallowly.
func (ec *evalCtx) copy(newContext string) *evalCtx {
	newPorts := make([]*port, len(ec.ports))
	for i, p := range ec.ports {
		newPorts[i] = &port{p.f, p.ch, false, false}
	}
	return &evalCtx{
		ec.Evaler,
		ec.name, ec.text, newContext,
		ec.local, ec.up,
		newPorts,
	}
}

// port returns ec.ports[i] or nil if i is out of range. This makes it possible
// to treat ec.ports as if it has an infinite tail of nil's.
func (ec *evalCtx) port(i int) *port {
	if i >= len(ec.ports) {
		return nil
	}
	return ec.ports[i]
}

// growPorts makes the size of ec.ports at least n, adding nil's if necessary.
func (ec *evalCtx) growPorts(n int) {
	if len(ec.ports) >= n {
		return
	}
	ports := ec.ports
	ec.ports = make([]*port, n)
	copy(ec.ports, ports)
}

func makeScope(s ns) scope {
	sc := scope{}
	for name, _ := range s {
		sc[name] = true
	}
	return sc
}

// Eval evaluates a chunk node n. The supplied name and text are used in
// diagnostic messages.
func (ev *Evaler) Eval(name, text string, n *parse.Chunk) ([]Value, error) {
	return ev.evalWithOut(name, text, n, nil)
}

func (ev *Evaler) evalWithOut(name, text string, n *parse.Chunk, out *port) ([]Value, error) {
	op, err := compile(name, text, makeScope(ev.global), n)
	if err != nil {
		return nil, err
	}
	ec := newTopEvalCtx(ev, name, text)
	if out != nil {
		ec.ports[1] = out
	}
	return ec.eval(op)
}

// eval evaluates an Op.
func (ec *evalCtx) eval(op valuesOp) (vs []Value, err error) {
	if op == nil {
		return nil, nil
	}
	defer ec.closePorts()
	defer errutil.Catch(&err)
	vs = op(ec)
	return vs, nil
}

// errorf stops the ec.eval immediately by panicking with a diagnostic message.
// The panic is supposed to be caught by ec.eval.
func (ec *evalCtx) errorf(p int, format string, args ...interface{}) {
	errutil.Throw(errutil.NewContextualError(
		fmt.Sprintf("%s (%s)", ec.name, ec.context), "error",
		ec.text, p, format, args...))
}

// mustSingleString returns a String if that is the only element of vs.
// Otherwise it errors.
func (ec *evalCtx) mustSingleString(vs []Value, what string, p int) str {
	if len(vs) != 1 {
		ec.errorf(p, "Expect exactly one word for %s, got %d", what, len(vs))
	}
	v, ok := vs[0].(str)
	if !ok {
		ec.errorf(p, "Expect string for %s, got %s", what, vs[0])
	}
	return v
}

// SourceText evaluates a chunk of elvish source.
func (ev *Evaler) SourceText(name, src, dir string) ([]Value, error) {
	n, err := parse.Parse(name, src)
	if err != nil {
		return nil, err
	}
	return ev.Eval(name, src, n)
}

func readFileUTF8(fname string) (string, error) {
	bytes, err := ioutil.ReadFile(fname)
	if err != nil {
		return "", err
	}
	if !utf8.Valid(bytes) {
		return "", fmt.Errorf("%s: source is not valid UTF-8", fname)
	}
	return string(bytes), nil
}

// Source evaluates the content of a file.
func (ev *Evaler) Source(fname string) ([]Value, error) {
	src, err := readFileUTF8(fname)
	if err != nil {
		return nil, err
	}
	return ev.SourceText(fname, src, path.Dir(fname))
}

// Global returns the global namespace.
func (ev *Evaler) Global() ns {
	return ev.global
}

// ResolveVar resolves a variable. When the variable cannot be found, nil is
// returned.
func (ec *evalCtx) ResolveVar(ns, name string) Variable {
	if ns == "env" {
		return newEnvVariable(name)
	}
	if mod, ok := ec.mod[ns]; ok {
		return mod[name]
	}

	may := func(n string) bool {
		return ns == "" || ns == n
	}
	if may("local") {
		if v, ok := ec.local[name]; ok {
			return v
		}
	}
	if may("up") {
		if v, ok := ec.up[name]; ok {
			return v
		}
	}
	return nil
}
<commit_msg>eval: synchronize conversion of channel to stdout correctly<commit_after>// Package eval handles evaluation of nodes and consists the runtime of the
// shell.
package eval

import (
	"fmt"
	"io/ioutil"
	"os"
	"path"
	"strconv"
	"strings"
	"syscall"
	"unicode/utf8"

	"github.com/elves/elvish/errutil"
	"github.com/elves/elvish/parse"
	"github.com/elves/elvish/store"
)

const FnPrefix = "fn-"

// ns is a namespace.
type ns map[string]Variable

// Evaler is used to evaluate elvish sources. It maintains runtime context
// shared among all evalCtx instances.
type Evaler struct {
	global      ns
	mod         map[string]ns
	searchPaths []string
	store       *store.Store
}

// evalCtx maintains an Evaler along with its runtime context. After creation
// an evalCtx is not modified, and new instances are created when needed.
type evalCtx struct {
	*Evaler
	name, text, context string

	local, up ns
	ports     []*port
}

func HasFailure(vs []Value) bool {
	for _, v := range vs {
		v, ok := v.(exitus)
		if !ok {
			// Silently ignore non-exitus values
			continue
		}
		if v.Sort != Ok {
			return true
		}
	}
	return false
}

// NewEvaler creates a new Evaler.
func NewEvaler(st *store.Store, dataDir string) *Evaler {
	// Construct searchPaths
	var searchPaths []string
	if path := os.Getenv("PATH"); path != "" {
		searchPaths = strings.Split(path, ":")
	} else {
		searchPaths = []string{"/bin"}
	}

	// Construct initial global namespace
	pid := str(strconv.Itoa(syscall.Getpid()))
	paths := newTable()
	paths.appendStrings(searchPaths)
	global := ns{
		"pid":   newInternalVariable(pid),
		"ok":    newInternalVariable(ok),
		"true":  newInternalVariable(boolean(true)),
		"false": newInternalVariable(boolean(false)),
		"paths": newInternalVariable(paths),
	}
	for _, b := range builtinFns {
		global[FnPrefix+b.Name] = newInternalVariable(b)
	}

	return &Evaler{global, map[string]ns{}, searchPaths, st}
}

func printExitus(e exitus) {
	switch e.Sort {
	case Ok:
		fmt.Print("\033[32mok\033[m")
	case Failure:
		fmt.Print("\033[31;1m" + e.Failure + "\033[m")
	case Traceback:
		fmt.Print("(")
		for i, c := range e.Traceback.causes {
			if i > 0 {
				fmt.Print(" | ")
			}
			printExitus(c)
		}
		fmt.Print(")")
	default:
		// Control flow sorts
		fmt.Print("\033[33m" + flowExitusNames[e.Sort] + "\033[m")
	}
}

func PrintExituses(vs []Value) {
	if !HasFailure(vs) {
		return
	}
	fmt.Print("Status: ")
	for i, v := range vs {
		if i > 0 {
			fmt.Print(" | ")
		}
		printExitus(v.(exitus))
	}
	fmt.Println()
}

const (
	outChanSize   = 32
	outChanLeader = "▶ "
)

// newTopEvalCtx creates a top-level evalCtx.
func newTopEvalCtx(ev *Evaler, name, text string) (*evalCtx, chan bool) {
	ch := make(chan Value, outChanSize)
	done := make(chan bool)
	go func() {
		for v := range ch {
			fmt.Printf("%s%s\n", outChanLeader, v.Repr())
		}
		done <- true
	}()

	return &evalCtx{
		ev,
		name, text, "top",
		ev.global, ns{},
		[]*port{{f: os.Stdin},
			{f: os.Stdout, ch: ch, closeCh: true}, {f: os.Stderr}},
	}, done
}

// copy returns a copy of ec. The ports are copied deeply, with shouldClose
// flags reset, and the context is changed to the given value. Other fields are
// copied shallowly.
func (ec *evalCtx) copy(newContext string) *evalCtx {
	newPorts := make([]*port, len(ec.ports))
	for i, p := range ec.ports {
		newPorts[i] = &port{p.f, p.ch, false, false}
	}
	return &evalCtx{
		ec.Evaler,
		ec.name, ec.text, newContext,
		ec.local, ec.up,
		newPorts,
	}
}

// port returns ec.ports[i] or nil if i is out of range. This makes it possible
// to treat ec.ports as if it has an infinite tail of nil's.
func (ec *evalCtx) port(i int) *port {
	if i >= len(ec.ports) {
		return nil
	}
	return ec.ports[i]
}

// growPorts makes the size of ec.ports at least n, adding nil's if necessary.
func (ec *evalCtx) growPorts(n int) {
	if len(ec.ports) >= n {
		return
	}
	ports := ec.ports
	ec.ports = make([]*port, n)
	copy(ec.ports, ports)
}

func makeScope(s ns) scope {
	sc := scope{}
	for name, _ := range s {
		sc[name] = true
	}
	return sc
}

// Eval evaluates a chunk node n. The supplied name and text are used in
// diagnostic messages.
func (ev *Evaler) Eval(name, text string, n *parse.Chunk) ([]Value, error) {
	return ev.evalWithOut(name, text, n, nil)
}

func (ev *Evaler) evalWithOut(name, text string, n *parse.Chunk, out *port) ([]Value, error) {
	op, err := compile(name, text, makeScope(ev.global), n)
	if err != nil {
		return nil, err
	}
	ec, outdone := newTopEvalCtx(ev, name, text)
	if out != nil {
		ec.ports[1] = out
	}
	vs, err := ec.eval(op)
	if err == nil {
		// XXX maybe the out channel is always closed regardless of the error? need some checking
		<-outdone
	}
	return vs, err
}

// eval evaluates an Op.
func (ec *evalCtx) eval(op valuesOp) (vs []Value, err error) {
	if op == nil {
		return nil, nil
	}
	defer ec.closePorts()
	defer errutil.Catch(&err)
	vs = op(ec)
	return vs, nil
}

// errorf stops the ec.eval immediately by panicking with a diagnostic message.
// The panic is supposed to be caught by ec.eval.
func (ec *evalCtx) errorf(p int, format string, args ...interface{}) {
	errutil.Throw(errutil.NewContextualError(
		fmt.Sprintf("%s (%s)", ec.name, ec.context), "error",
		ec.text, p, format, args...))
}

// mustSingleString returns a String if that is the only element of vs.
// Otherwise it errors.
func (ec *evalCtx) mustSingleString(vs []Value, what string, p int) str {
	if len(vs) != 1 {
		ec.errorf(p, "Expect exactly one word for %s, got %d", what, len(vs))
	}
	v, ok := vs[0].(str)
	if !ok {
		ec.errorf(p, "Expect string for %s, got %s", what, vs[0])
	}
	return v
}

// SourceText evaluates a chunk of elvish source.
func (ev *Evaler) SourceText(name, src, dir string) ([]Value, error) {
	n, err := parse.Parse(name, src)
	if err != nil {
		return nil, err
	}
	return ev.Eval(name, src, n)
}

func readFileUTF8(fname string) (string, error) {
	bytes, err := ioutil.ReadFile(fname)
	if err != nil {
		return "", err
	}
	if !utf8.Valid(bytes) {
		return "", fmt.Errorf("%s: source is not valid UTF-8", fname)
	}
	return string(bytes), nil
}

// Source evaluates the content of a file.
func (ev *Evaler) Source(fname string) ([]Value, error) {
	src, err := readFileUTF8(fname)
	if err != nil {
		return nil, err
	}
	return ev.SourceText(fname, src, path.Dir(fname))
}

// Global returns the global namespace.
func (ev *Evaler) Global() ns {
	return ev.global
}

// ResolveVar resolves a variable. When the variable cannot be found, nil is
// returned.
func (ec *evalCtx) ResolveVar(ns, name string) Variable {
	if ns == "env" {
		return newEnvVariable(name)
	}
	if mod, ok := ec.mod[ns]; ok {
		return mod[name]
	}

	may := func(n string) bool {
		return ns == "" || ns == n
	}
	if may("local") {
		if v, ok := ec.local[name]; ok {
			return v
		}
	}
	if may("up") {
		if v, ok := ec.up[name]; ok {
			return v
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad/structs"
)

func main() {
	client, err := api.NewClient(api.DefaultConfig())
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	total := 4000
	running := 0
	start := time.Now()
	allocClient := client.Allocations()

	fmt.Printf("benchmarking %d allocations\n", total)
	for i := 0; ; i++ {
		time.Sleep(100 * time.Millisecond)

		allocs, _, err := allocClient.List(nil)
		if err != nil {
			fmt.Println(err.Error())

			// keep going to paper over minor errors
			continue
		}
		now := time.Now()

		for _, alloc := range allocs {
			if alloc.ClientStatus == structs.AllocClientStatusRunning {
				if running == 0 {
					fmt.Println("time to first running: %s", now.Sub(start))
				}
				running++
			}
		}

		if i%10 == 0 || running == total {
			fmt.Printf("%d running after %s\n", running, now.Sub(start))
		}

		if running == total {
			return
		}
	}
}
<commit_msg>demo/digitalocean: better benchmark<commit_after>package main

import (
	"fmt"
	"os/exec"
	"time"

	"github.com/hashicorp/nomad/api"
	"github.com/hashicorp/nomad/nomad/structs"
)

func main() {
	client, err := api.NewClient(api.DefaultConfig())
	if err != nil {
		fmt.Println(err.Error())
		return
	}

	total := 4000
	isRunning := false
	start := time.Now()
	allocClient := client.Allocations()

	cmd := exec.Command("nomad", "run", "bench.nomad")
	if err := cmd.Run(); err != nil {
		fmt.Println("nomad run failed: " + err.Error())
		return
	}

	fmt.Printf("benchmarking %d allocations\n", total)
	for i := 0; ; i++ {
		time.Sleep(100 * time.Millisecond)

		allocs, _, err := allocClient.List(nil)
		if err != nil {
			fmt.Println(err.Error())

			// keep going to paper over minor errors
			continue
		}
		now := time.Now()

		running := 0
		for _, alloc := range allocs {
			if alloc.ClientStatus == structs.AllocClientStatusRunning {
				if !isRunning {
					fmt.Printf("time to first running: %s\n", now.Sub(start))
					isRunning = true
				}
				running++
			}
		}

		if i%10 == 0 || running == total {
			fmt.Printf("%d running after %s\n", running, now.Sub(start))
		}

		if running == total {
			return
		}
	}
}
<|endoftext|>


<commit_before>package aci

import (
	"bytes"
	"fmt"
)

func rnBridgeDomain(bd string) string {
	return "BD-" + bd
}

func dnBridgeDomain(tenant, bd string) string {
	return rnTenant(tenant) + "/" + rnBridgeDomain(bd)
}

func rnSubnet(subnet string) string {
	return "subnet-[" + subnet + "]"
}

func dnSubnet(tenant, bd, subnet string) string {
	return dnBridgeDomain(tenant, bd) + "/" + rnSubnet(subnet)
}

// BridgeDomainAdd creates a new bridge domain in a tenant.
func (c *Client) BridgeDomainAdd(tenant, bd, descr string) error {

	me := "BridgeDomainAdd"

	rn := rnBridgeDomain(bd)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","name":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dn, bd, descr, rn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainDel deletes an existing bridge domain from a tenant.
func (c *Client) BridgeDomainDel(tenant, bd string) error {

	me := "BridgeDomainDel"

	rnT := rnTenant(tenant)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + rnT + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvTenant":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvBD":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		rnT, dn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainList retrieves the list of bridge domains from a tenant.
func (c *Client) BridgeDomainList(tenant string) ([]map[string]interface{}, error) {

	me := "BridgeDomainList"

	key := "fvBD"

	t := rnTenant(tenant)

	api := "/api/node/mo/uni/" + t + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainVrfSet defines the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfSet(tenant, bd, vrf string) error {

	me := "BridgeDomainVrfSet"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + "/rsctx.json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvRsCtx":{"attributes":{"tnFvCtxName":"%s"}}}`,
		vrf)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainVrfGet retrieves the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfGet(tenant, bd string) (string, error) {

	me := "BridgeDomainVrfGet"

	key := "fvRsCtx"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return "", fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	attrs, errAttr := jsonImdataAttributes(c, body, key, me)
	if errAttr != nil {
		return "", fmt.Errorf("%s: %v", me, errAttr)
	}

	if len(attrs) < 1 {
		return "", fmt.Errorf("%s: empty list of VRFs", me)
	}

	attr := attrs[0]
	v := attr["tnFvCtxName"]

	vrf, isStr := v.(string)
	if !isStr {
		return "", fmt.Errorf("%s: VRF is not a string", me)
	}

	if vrf == "" {
		return "", fmt.Errorf("%s: empty VRF name")
	}

	return vrf, nil
}

// BridgeDomainSubnetAdd creates a new subnet a bridge domain.
func (c *Client) BridgeDomainSubnetAdd(tenant, bd, subnet, descr string) error {

	me := "BridgeDomainSubnetAdd"

	rnSN := rnSubnet(subnet)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","ip":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dnSN, subnet, descr, rnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetDel deletes an existing subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetDel(tenant, bd, subnet string) error {

	me := "BridgeDomainSubnetDel"

	dnBD := dnBridgeDomain(tenant, bd)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnBD + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvSubnet":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		dnBD, dnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetList retrieves the list of subnets from a bridge domain.
func (c *Client) BridgeDomainSubnetList(tenant, bd string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetList"

	key := "fvSubnet"

	dnBD := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dnBD + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetGet retrieves specific subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetGet(tenant, bd, subnet string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetGet"

	key := "fvSubnet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetScopeSet defines the scope for a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeSet(tenant, bd, subnet, scope string) error {

	me := "BridgeDomainSubnetScopeSet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","scope":"%s"}}}`,
		dnSN, scope)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetGet retrieves the scope from a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeGet(tenant, bd, subnet string) (string, error) {

	me := "BridgeDomainSubnetScopeGet"

	list, errSubnet := c.BridgeDomainSubnetGet(tenant, bd, subnet)
	if errSubnet != nil {
		return "", fmt.Errorf("%s: %v", me, errSubnet)
	}

	if len(list) < 1 {
		return "", fmt.Errorf("%s: empty list of subnets", me)
	}

	attrs := list[0]
	s := attrs["scope"]

	scope, isStr := s.(string)
	if !isStr {
		return "", fmt.Errorf("%s: scope is not a string", me)
	}

	return scope, nil
}
<commit_msg>Fix error message.<commit_after>package aci

import (
	"bytes"
	"fmt"
)

func rnBridgeDomain(bd string) string {
	return "BD-" + bd
}

func dnBridgeDomain(tenant, bd string) string {
	return rnTenant(tenant) + "/" + rnBridgeDomain(bd)
}

func rnSubnet(subnet string) string {
	return "subnet-[" + subnet + "]"
}

func dnSubnet(tenant, bd, subnet string) string {
	return dnBridgeDomain(tenant, bd) + "/" + rnSubnet(subnet)
}

// BridgeDomainAdd creates a new bridge domain in a tenant.
func (c *Client) BridgeDomainAdd(tenant, bd, descr string) error {

	me := "BridgeDomainAdd"

	rn := rnBridgeDomain(bd)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","name":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dn, bd, descr, rn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainDel deletes an existing bridge domain from a tenant.
func (c *Client) BridgeDomainDel(tenant, bd string) error {

	me := "BridgeDomainDel"

	rnT := rnTenant(tenant)

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + rnT + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvTenant":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvBD":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		rnT, dn)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainList retrieves the list of bridge domains from a tenant.
func (c *Client) BridgeDomainList(tenant string) ([]map[string]interface{}, error) {

	me := "BridgeDomainList"

	key := "fvBD"

	t := rnTenant(tenant)

	api := "/api/node/mo/uni/" + t + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainVrfSet defines the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfSet(tenant, bd, vrf string) error {

	me := "BridgeDomainVrfSet"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + "/rsctx.json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvRsCtx":{"attributes":{"tnFvCtxName":"%s"}}}`,
		vrf)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainVrfGet retrieves the VRF for a bridge domain.
func (c *Client) BridgeDomainVrfGet(tenant, bd string) (string, error) {

	me := "BridgeDomainVrfGet"

	key := "fvRsCtx"

	dn := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dn + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return "", fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	attrs, errAttr := jsonImdataAttributes(c, body, key, me)
	if errAttr != nil {
		return "", fmt.Errorf("%s: %v", me, errAttr)
	}

	if len(attrs) < 1 {
		return "", fmt.Errorf("%s: empty list of VRFs", me)
	}

	attr := attrs[0]
	v := attr["tnFvCtxName"]

	vrf, isStr := v.(string)
	if !isStr {
		return "", fmt.Errorf("%s: VRF is not a string", me)
	}

	if vrf == "" {
		return "", fmt.Errorf("%s: empty VRF name", me)
	}

	return vrf, nil
}

// BridgeDomainSubnetAdd creates a new subnet a bridge domain.
func (c *Client) BridgeDomainSubnetAdd(tenant, bd, subnet, descr string) error {

	me := "BridgeDomainSubnetAdd"

	rnSN := rnSubnet(subnet)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","ip":"%s","descr":"%s","rn":"%s","status":"created"}}}`,
		dnSN, subnet, descr, rnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetDel deletes an existing subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetDel(tenant, bd, subnet string) error {

	me := "BridgeDomainSubnetDel"

	dnBD := dnBridgeDomain(tenant, bd)

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnBD + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvBD":{"attributes":{"dn":"uni/%s","status":"modified"},"children":[{"fvSubnet":{"attributes":{"dn":"uni/%s","status":"deleted"}}}]}}`,
		dnBD, dnSN)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetList retrieves the list of subnets from a bridge domain.
func (c *Client) BridgeDomainSubnetList(tenant, bd string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetList"

	key := "fvSubnet"

	dnBD := dnBridgeDomain(tenant, bd)

	api := "/api/node/mo/uni/" + dnBD + ".json?query-target=children&target-subtree-class=" + key

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetGet retrieves specific subnet from a bridge domain.
func (c *Client) BridgeDomainSubnetGet(tenant, bd, subnet string) ([]map[string]interface{}, error) {

	me := "BridgeDomainSubnetGet"

	key := "fvSubnet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	c.debugf("%s: url=%s", me, url)

	body, errGet := c.get(url)
	if errGet != nil {
		return nil, fmt.Errorf("%s: %v", me, errGet)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return jsonImdataAttributes(c, body, key, me)
}

// BridgeDomainSubnetScopeSet defines the scope for a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeSet(tenant, bd, subnet, scope string) error {

	me := "BridgeDomainSubnetScopeSet"

	dnSN := dnSubnet(tenant, bd, subnet)

	api := "/api/node/mo/uni/" + dnSN + ".json"

	url := c.getURL(api)

	j := fmt.Sprintf(`{"fvSubnet":{"attributes":{"dn":"uni/%s","scope":"%s"}}}`,
		dnSN, scope)

	c.debugf("%s: url=%s json=%s", me, url, j)

	body, errPost := c.post(url, contentTypeJSON, bytes.NewBufferString(j))
	if errPost != nil {
		return fmt.Errorf("%s: %v", me, errPost)
	}

	c.debugf("%s: reply: %s", me, string(body))

	return parseJSONError(body)
}

// BridgeDomainSubnetGet retrieves the scope from a bridge domain subnet.
func (c *Client) BridgeDomainSubnetScopeGet(tenant, bd, subnet string) (string, error) {

	me := "BridgeDomainSubnetScopeGet"

	list, errSubnet := c.BridgeDomainSubnetGet(tenant, bd, subnet)
	if errSubnet != nil {
		return "", fmt.Errorf("%s: %v", me, errSubnet)
	}

	if len(list) < 1 {
		return "", fmt.Errorf("%s: empty list of subnets", me)
	}

	attrs := list[0]
	s := attrs["scope"]

	scope, isStr := s.(string)
	if !isStr {
		return "", fmt.Errorf("%s: scope is not a string", me)
	}

	return scope, nil
}
<|endoftext|>


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

package main

import (
	"database/sql"
	"errors"
	"log"
	"math/rand"
	"time"

	"github.com/cockroachdb/cockroach/util"
	// Import postgres driver.
	_ "github.com/cockroachdb/pq"
)

const (
	// TODO(spencer): update the CREATE DATABASE statement in the schema
	//   to pull out the database specified in the DB URL and use it instead
	//   of "photos" below.
	photosSchema = `
CREATE DATABASE IF NOT EXISTS photos;

CREATE TABLE IF NOT EXISTS users (
  id           INT,
  photoCount   INT,
  commentCount INT,
  name         STRING,
  address      STRING,

  PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS photos (
  id           BYTES DEFAULT EXPERIMENTAL_UUID_V4(),
  userID       INT,
  commentCount INT,
  caption      STRING,
  latitude     FLOAT,
  longitude    FLOAT,
  timestamp    TIMESTAMP,

  PRIMARY KEY (id),
  UNIQUE INDEX byUserID (userID, timestamp)
);

CREATE TABLE IF NOT EXISTS comments (
  photoID   BYTES,
  commentID BYTES DEFAULT EXPERIMENTAL_UUID_V4(),
  userID    INT,
  message   STRING,
  timestamp TIMESTAMP,

  PRIMARY KEY (photoID, timestamp, commentID)
);`
)

// openDB opens the database connection according to the context.
func openDB(ctx Context) (*sql.DB, error) {
	return sql.Open("postgres", ctx.DBUrl)
}

// initSchema creates the database schema if it doesn't exist.
func initSchema(db *sql.DB) error {
	_, err := db.Exec(photosSchema)
	return err
}

// dropDatabase drops the database.
func dropDatabase(db *sql.DB) error {
	_, err := db.Exec("DROP DATABASE IF EXISTS photos;")
	return err
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

var errNoUser = errors.New("no user found")

// userExists looks up a user by ID.
func userExists(tx *sql.Tx, userID int) (bool, error) {
	var id int
	const selectSQL = `
SELECT id FROM users WHERE id = $1;
`
	err := tx.QueryRow(selectSQL, userID).Scan(&id)
	switch err {
	case sql.ErrNoRows:
		return false, nil
	case nil:
		return true, nil
	default:
		return false, err
	}
}

// findClosestUserByID selects the first user which exists with
// id >= userID. Returns the found user ID or an error.
func findClosestUserByID(tx *sql.Tx, userID int) (int, error) {
	var id int
	const selectSQL = `
SELECT id FROM users WHERE id >= $1 ORDER BY id LIMIT 1;
`
	err := tx.QueryRow(selectSQL, userID).Scan(&id)
	if err == sql.ErrNoRows {
		return 0, errNoUser
	}
	return id, err
}

// createUser creates a new user with random name and address strings.
func createUser(tx *sql.Tx, userID int) error {
	exists, err := userExists(tx, userID)
	if err != nil || exists {
		return err
	}
	const insertSQL = `
INSERT INTO users VALUES ($1, 0, 0, $2, $3);
`
	const minNameLen = 1
	const maxNameLen = 30
	const minAddrLen = 20
	const maxAddrLen = 100
	name := randString(minNameLen + rand.Intn(maxNameLen-minNameLen))
	addr := randString(minAddrLen + rand.Intn(maxAddrLen-minAddrLen))
	_, err = tx.Exec(insertSQL, userID, name, addr)
	return err
}

// createPhoto looks up or creates a new user to match userID (it's
// the only method in this interface which doesn't match an existing
// user except for createUser). It then creates a new photo for the
// new or pre-existing user.
func createPhoto(tx *sql.Tx, userID int) error {
	if err := createUser(tx, userID); err != nil {
		return err
	}

	const insertSQL = `
INSERT INTO photos VALUES (DEFAULT, $1, 0, $2, $3, $4, NOW());
`
	const minCaptionLen = 10
	const maxCaptionLen = 200
	caption := randString(minCaptionLen + rand.Intn(maxCaptionLen-minCaptionLen))
	latitude := rand.Float32() * 90
	longitude := rand.Float32() * 180
	if _, err := tx.Exec(insertSQL, userID, caption, latitude, longitude); err != nil {
		return err
	}

	const updateSQL = `
UPDATE users SET photoCount = photoCount + 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateSQL, userID); err != nil {
		return err
	}
	return nil
}

// createComment chooses a random photo from a user with the closest
// matching user ID and generates a random author ID to author the
// comment. Counts are updated on the photo and author user.
func createComment(tx *sql.Tx, userID int) error {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return err
	}
	authorID := rand.Intn(userID) + 1

	const insertSQL = `
INSERT INTO comments VALUES ($1, DEFAULT, $2, $3, NOW());
`
	const minMessageLen = 32
	const maxMessageLen = 1024
	message := randString(minMessageLen + rand.Intn(maxMessageLen-minMessageLen))
	if _, err := tx.Exec(insertSQL, photoID, authorID, message); err != nil {
		log.Printf("insert into photos failed: %s", err)
		return err
	}

	const updatePhotoSQL = `
UPDATE photos SET commentCount = commentCount + 1 WHERE id = $1;
`
	if _, err := tx.Exec(updatePhotoSQL, photoID); err != nil {
		return err
	}

	const updateUserSQL = `
UPDATE users SET commentCount = commentCount + 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateUserSQL, authorID); err != nil {
		return err
	}
	return nil
}

// listPhotos queries up to 100 photos, sorted by timestamp in
// descending order, for the first user with ID >= userID. If photoIDs
// is not nil, stores the queried photo IDs in photoIDs.
func listPhotos(tx *sql.Tx, userID int, photoIDs *[][]byte) error {
	var err error
	userID, err = findClosestUserByID(tx, userID)
	if err != nil {
		return err
	}
	const selectSQL = `
SELECT id, caption, commentCount, latitude, longitude, timestamp FROM photos WHERE userID = $1 ORDER BY timestamp DESC LIMIT 100`
	rows, err := tx.Query(selectSQL, userID)
	switch {
	case err == sql.ErrNoRows:
		return nil
	case err != nil:
		return err
	}
	defer func() { _ = rows.Close() }()
	// Count and process the result set so we make sure work is done to
	// stream the results.
	var count int
	for rows.Next() {
		if err := rows.Err(); err != nil {
			return err
		}
		var id []byte
		var caption string
		var cCount int
		var lat, lon float64
		var ts time.Time
		if err := rows.Scan(&id, &caption, &cCount, &lat, &lon, &ts); err != nil {
			return util.Errorf("failed to scan result set for user %d: %s", userID, err)
		}
		count++
		if photoIDs != nil {
			*photoIDs = append(*photoIDs, id)
		}
	}
	//log.Printf("selected %d photos for user %d", count, userID)
	return nil
}

// chooseRandomPhoto selects a random photo for the specified
// user or an existing user with the closest user ID. Returns
// the photo ID or an error.
func chooseRandomPhoto(tx *sql.Tx, userID int) ([]byte, error) {
	photoIDs := [][]byte{}
	if err := listPhotos(tx, userID, &photoIDs); err != nil {
		return nil, err
	}
	if len(photoIDs) == 0 {
		return nil, nil
	}
	photoID := photoIDs[rand.Intn(len(photoIDs))]
	return photoID, nil
}

// listComments chooses a random photo and lists up to 100 of its
// comments. Returns the photoID or an error. If the commentIDs slice
// is not nil, it's set to the queried comments' IDs.
func listComments(tx *sql.Tx, userID int, commentIDs *[][]byte) ([]byte, error) {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return nil, err
	}
	const selectSQL = `SELECT commentID, userID, message, timestamp FROM comments ` +
		`WHERE photoID = $1 ORDER BY timestamp DESC LIMIT 100`
	rows, err := tx.Query(selectSQL, photoID)
	switch {
	case err == sql.ErrNoRows:
		return photoID, nil
	case err != nil:
		return nil, err
	}
	defer func() { _ = rows.Close() }()
	// Count and process the result set so we make sure work is done to
	// stream the results.
	var count int
	for rows.Next() {
		if err := rows.Err(); err != nil {
			return nil, err
		}
		var commentID []byte
		var message string
		var userID int
		var ts time.Time
		if err := rows.Scan(&commentID, &userID, &message, &ts); err != nil {
			return nil, util.Errorf("failed to scan result set for photo %q: %s", photoID, err)
		}
		count++
		if commentIDs != nil {
			*commentIDs = append(*commentIDs, commentID)
		}
	}
	//log.Printf("selected %d comments for photo %q", count, photoID)
	return photoID, nil
}

// chooseRandomComment selects a random comment for the specified
// user or an existing user with the closest user ID. Returns
// the photo and comment IDs or an error.
func chooseRandomComment(tx *sql.Tx, userID int) ([]byte, []byte, error) {
	commentIDs := [][]byte{}
	photoID, err := listComments(tx, userID, &commentIDs)
	if err != nil {
		return nil, nil, err
	}
	if len(commentIDs) == 0 {
		return photoID, nil, nil
	}
	commentID := commentIDs[rand.Intn(len(commentIDs))]
	return photoID, commentID, nil
}

func updatePhoto(tx *sql.Tx, userID int) error {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return err
	}

	const updatePhotoSQL = `
UPDATE photos SET caption = $1 WHERE id = $2;
`
	const minCaptionLen = 10
	const maxCaptionLen = 200
	caption := randString(minCaptionLen + rand.Intn(maxCaptionLen-minCaptionLen))
	if _, err := tx.Exec(updatePhotoSQL, caption, photoID); err != nil {
		return err
	}
	return nil
}

func updateComment(tx *sql.Tx, userID int) error {
	photoID, commentID, err := chooseRandomComment(tx, userID)
	if err != nil {
		return err
	}

	const updateCommentSQL = `
UPDATE comments SET message = $1 WHERE photoID = $2 AND commentID = $3;
`
	const minMessageLen = 10
	const maxMessageLen = 200
	message := randString(minMessageLen + rand.Intn(maxMessageLen-minMessageLen))
	if _, err := tx.Exec(updateCommentSQL, message, photoID, commentID); err != nil {
		return err
	}
	return nil
}

func deletePhoto(tx *sql.Tx, userID int) error {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return err
	}
	const deletePhotoSQL = `
DELETE FROM photos WHERE id = $1;
`
	if _, err := tx.Exec(deletePhotoSQL, photoID); err != nil {
		return err
	}

	const updateSQL = `
UPDATE users SET photoCount = photoCount - 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateSQL, userID); err != nil {
		return err
	}
	return nil
}

func deleteComment(tx *sql.Tx, userID int) error {
	photoID, commentID, err := chooseRandomComment(tx, userID)
	if err != nil {
		return err
	}
	const deleteCommentSQL = `
DELETE FROM comments WHERE photoID = $1 AND commentID = $2;
`
	if _, err := tx.Exec(deleteCommentSQL, photoID, commentID); err != nil {
		return err
	}

	const updatePhotoSQL = `
UPDATE photos SET commentCount = commentCount - 1 WHERE id = $1;
`
	if _, err := tx.Exec(updatePhotoSQL, photoID); err != nil {
		return err
	}

	const updateUserSQL = `
UPDATE users SET commentCount = commentCount - 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateUserSQL, userID); err != nil {
		return err
	}
	return nil
}
<commit_msg>Use uuid_v4() instead of experimental_uuid_v4().<commit_after>// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.
//
// Author: Spencer Kimball (spencer.kimball@gmail.com)

package main

import (
	"database/sql"
	"errors"
	"log"
	"math/rand"
	"time"

	"github.com/cockroachdb/cockroach/util"
	// Import postgres driver.
	_ "github.com/cockroachdb/pq"
)

const (
	// TODO(spencer): update the CREATE DATABASE statement in the schema
	//   to pull out the database specified in the DB URL and use it instead
	//   of "photos" below.
	photosSchema = `
CREATE DATABASE IF NOT EXISTS photos;

CREATE TABLE IF NOT EXISTS users (
  id           INT,
  photoCount   INT,
  commentCount INT,
  name         STRING,
  address      STRING,

  PRIMARY KEY (id)
);

CREATE TABLE IF NOT EXISTS photos (
  id           BYTES DEFAULT uuid_v4(),
  userID       INT,
  commentCount INT,
  caption      STRING,
  latitude     FLOAT,
  longitude    FLOAT,
  timestamp    TIMESTAMP,

  PRIMARY KEY (id),
  UNIQUE INDEX byUserID (userID, timestamp)
);

CREATE TABLE IF NOT EXISTS comments (
  photoID   BYTES,
  commentID BYTES DEFAULT uuid_v4(),
  userID    INT,
  message   STRING,
  timestamp TIMESTAMP,

  PRIMARY KEY (photoID, timestamp, commentID)
);`
)

// openDB opens the database connection according to the context.
func openDB(ctx Context) (*sql.DB, error) {
	return sql.Open("postgres", ctx.DBUrl)
}

// initSchema creates the database schema if it doesn't exist.
func initSchema(db *sql.DB) error {
	_, err := db.Exec(photosSchema)
	return err
}

// dropDatabase drops the database.
func dropDatabase(db *sql.DB) error {
	_, err := db.Exec("DROP DATABASE IF EXISTS photos;")
	return err
}

const letterBytes = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"

func randString(n int) string {
	b := make([]byte, n)
	for i := range b {
		b[i] = letterBytes[rand.Intn(len(letterBytes))]
	}
	return string(b)
}

var errNoUser = errors.New("no user found")

// userExists looks up a user by ID.
func userExists(tx *sql.Tx, userID int) (bool, error) {
	var id int
	const selectSQL = `
SELECT id FROM users WHERE id = $1;
`
	err := tx.QueryRow(selectSQL, userID).Scan(&id)
	switch err {
	case sql.ErrNoRows:
		return false, nil
	case nil:
		return true, nil
	default:
		return false, err
	}
}

// findClosestUserByID selects the first user which exists with
// id >= userID. Returns the found user ID or an error.
func findClosestUserByID(tx *sql.Tx, userID int) (int, error) {
	var id int
	const selectSQL = `
SELECT id FROM users WHERE id >= $1 ORDER BY id LIMIT 1;
`
	err := tx.QueryRow(selectSQL, userID).Scan(&id)
	if err == sql.ErrNoRows {
		return 0, errNoUser
	}
	return id, err
}

// createUser creates a new user with random name and address strings.
func createUser(tx *sql.Tx, userID int) error {
	exists, err := userExists(tx, userID)
	if err != nil || exists {
		return err
	}
	const insertSQL = `
INSERT INTO users VALUES ($1, 0, 0, $2, $3);
`
	const minNameLen = 1
	const maxNameLen = 30
	const minAddrLen = 20
	const maxAddrLen = 100
	name := randString(minNameLen + rand.Intn(maxNameLen-minNameLen))
	addr := randString(minAddrLen + rand.Intn(maxAddrLen-minAddrLen))
	_, err = tx.Exec(insertSQL, userID, name, addr)
	return err
}

// createPhoto looks up or creates a new user to match userID (it's
// the only method in this interface which doesn't match an existing
// user except for createUser). It then creates a new photo for the
// new or pre-existing user.
func createPhoto(tx *sql.Tx, userID int) error {
	if err := createUser(tx, userID); err != nil {
		return err
	}

	const insertSQL = `
INSERT INTO photos VALUES (DEFAULT, $1, 0, $2, $3, $4, NOW());
`
	const minCaptionLen = 10
	const maxCaptionLen = 200
	caption := randString(minCaptionLen + rand.Intn(maxCaptionLen-minCaptionLen))
	latitude := rand.Float32() * 90
	longitude := rand.Float32() * 180
	if _, err := tx.Exec(insertSQL, userID, caption, latitude, longitude); err != nil {
		return err
	}

	const updateSQL = `
UPDATE users SET photoCount = photoCount + 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateSQL, userID); err != nil {
		return err
	}
	return nil
}

// createComment chooses a random photo from a user with the closest
// matching user ID and generates a random author ID to author the
// comment. Counts are updated on the photo and author user.
func createComment(tx *sql.Tx, userID int) error {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return err
	}
	authorID := rand.Intn(userID) + 1

	const insertSQL = `
INSERT INTO comments VALUES ($1, DEFAULT, $2, $3, NOW());
`
	const minMessageLen = 32
	const maxMessageLen = 1024
	message := randString(minMessageLen + rand.Intn(maxMessageLen-minMessageLen))
	if _, err := tx.Exec(insertSQL, photoID, authorID, message); err != nil {
		log.Printf("insert into photos failed: %s", err)
		return err
	}

	const updatePhotoSQL = `
UPDATE photos SET commentCount = commentCount + 1 WHERE id = $1;
`
	if _, err := tx.Exec(updatePhotoSQL, photoID); err != nil {
		return err
	}

	const updateUserSQL = `
UPDATE users SET commentCount = commentCount + 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateUserSQL, authorID); err != nil {
		return err
	}
	return nil
}

// listPhotos queries up to 100 photos, sorted by timestamp in
// descending order, for the first user with ID >= userID. If photoIDs
// is not nil, stores the queried photo IDs in photoIDs.
func listPhotos(tx *sql.Tx, userID int, photoIDs *[][]byte) error {
	var err error
	userID, err = findClosestUserByID(tx, userID)
	if err != nil {
		return err
	}
	const selectSQL = `
SELECT id, caption, commentCount, latitude, longitude, timestamp FROM photos WHERE userID = $1 ORDER BY timestamp DESC LIMIT 100`
	rows, err := tx.Query(selectSQL, userID)
	switch {
	case err == sql.ErrNoRows:
		return nil
	case err != nil:
		return err
	}
	defer func() { _ = rows.Close() }()
	// Count and process the result set so we make sure work is done to
	// stream the results.
	var count int
	for rows.Next() {
		if err := rows.Err(); err != nil {
			return err
		}
		var id []byte
		var caption string
		var cCount int
		var lat, lon float64
		var ts time.Time
		if err := rows.Scan(&id, &caption, &cCount, &lat, &lon, &ts); err != nil {
			return util.Errorf("failed to scan result set for user %d: %s", userID, err)
		}
		count++
		if photoIDs != nil {
			*photoIDs = append(*photoIDs, id)
		}
	}
	//log.Printf("selected %d photos for user %d", count, userID)
	return nil
}

// chooseRandomPhoto selects a random photo for the specified
// user or an existing user with the closest user ID. Returns
// the photo ID or an error.
func chooseRandomPhoto(tx *sql.Tx, userID int) ([]byte, error) {
	photoIDs := [][]byte{}
	if err := listPhotos(tx, userID, &photoIDs); err != nil {
		return nil, err
	}
	if len(photoIDs) == 0 {
		return nil, nil
	}
	photoID := photoIDs[rand.Intn(len(photoIDs))]
	return photoID, nil
}

// listComments chooses a random photo and lists up to 100 of its
// comments. Returns the photoID or an error. If the commentIDs slice
// is not nil, it's set to the queried comments' IDs.
func listComments(tx *sql.Tx, userID int, commentIDs *[][]byte) ([]byte, error) {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return nil, err
	}
	const selectSQL = `SELECT commentID, userID, message, timestamp FROM comments ` +
		`WHERE photoID = $1 ORDER BY timestamp DESC LIMIT 100`
	rows, err := tx.Query(selectSQL, photoID)
	switch {
	case err == sql.ErrNoRows:
		return photoID, nil
	case err != nil:
		return nil, err
	}
	defer func() { _ = rows.Close() }()
	// Count and process the result set so we make sure work is done to
	// stream the results.
	var count int
	for rows.Next() {
		if err := rows.Err(); err != nil {
			return nil, err
		}
		var commentID []byte
		var message string
		var userID int
		var ts time.Time
		if err := rows.Scan(&commentID, &userID, &message, &ts); err != nil {
			return nil, util.Errorf("failed to scan result set for photo %q: %s", photoID, err)
		}
		count++
		if commentIDs != nil {
			*commentIDs = append(*commentIDs, commentID)
		}
	}
	//log.Printf("selected %d comments for photo %q", count, photoID)
	return photoID, nil
}

// chooseRandomComment selects a random comment for the specified
// user or an existing user with the closest user ID. Returns
// the photo and comment IDs or an error.
func chooseRandomComment(tx *sql.Tx, userID int) ([]byte, []byte, error) {
	commentIDs := [][]byte{}
	photoID, err := listComments(tx, userID, &commentIDs)
	if err != nil {
		return nil, nil, err
	}
	if len(commentIDs) == 0 {
		return photoID, nil, nil
	}
	commentID := commentIDs[rand.Intn(len(commentIDs))]
	return photoID, commentID, nil
}

func updatePhoto(tx *sql.Tx, userID int) error {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return err
	}

	const updatePhotoSQL = `
UPDATE photos SET caption = $1 WHERE id = $2;
`
	const minCaptionLen = 10
	const maxCaptionLen = 200
	caption := randString(minCaptionLen + rand.Intn(maxCaptionLen-minCaptionLen))
	if _, err := tx.Exec(updatePhotoSQL, caption, photoID); err != nil {
		return err
	}
	return nil
}

func updateComment(tx *sql.Tx, userID int) error {
	photoID, commentID, err := chooseRandomComment(tx, userID)
	if err != nil {
		return err
	}

	const updateCommentSQL = `
UPDATE comments SET message = $1 WHERE photoID = $2 AND commentID = $3;
`
	const minMessageLen = 10
	const maxMessageLen = 200
	message := randString(minMessageLen + rand.Intn(maxMessageLen-minMessageLen))
	if _, err := tx.Exec(updateCommentSQL, message, photoID, commentID); err != nil {
		return err
	}
	return nil
}

func deletePhoto(tx *sql.Tx, userID int) error {
	photoID, err := chooseRandomPhoto(tx, userID)
	if err != nil {
		return err
	}
	const deletePhotoSQL = `
DELETE FROM photos WHERE id = $1;
`
	if _, err := tx.Exec(deletePhotoSQL, photoID); err != nil {
		return err
	}

	const updateSQL = `
UPDATE users SET photoCount = photoCount - 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateSQL, userID); err != nil {
		return err
	}
	return nil
}

func deleteComment(tx *sql.Tx, userID int) error {
	photoID, commentID, err := chooseRandomComment(tx, userID)
	if err != nil {
		return err
	}
	const deleteCommentSQL = `
DELETE FROM comments WHERE photoID = $1 AND commentID = $2;
`
	if _, err := tx.Exec(deleteCommentSQL, photoID, commentID); err != nil {
		return err
	}

	const updatePhotoSQL = `
UPDATE photos SET commentCount = commentCount - 1 WHERE id = $1;
`
	if _, err := tx.Exec(updatePhotoSQL, photoID); err != nil {
		return err
	}

	const updateUserSQL = `
UPDATE users SET commentCount = commentCount - 1 WHERE id = $1;
`
	if _, err := tx.Exec(updateUserSQL, userID); err != nil {
		return err
	}
	return nil
}
<|endoftext|>