<commit_before>package reactive

import (
	"context"
	"errors"
	"sync"
	"time"
)

var (
	// Sentrinel error to tell the rerunner to not dump the current
	// computation cache and let the error'd function retry.
	RetrySentinelError = errors.New("retry")
)

// locker is a collection of mutexes indexed by arbitrary keys
type locker struct {
	mu sync.Mutex
	m  map[interface{}]*lock
}

// newLocker creates a new locker instance.
func newLocker() *locker {
	return &locker{
		m: make(map[interface{}]*lock),
	}
}

// lock is a single mutex in a locker
type lock struct {
	ref int
	mu  sync.Mutex
}

// Lock locks a locker by (optionally) allocating, increasing the ref count,
// and locking
func (l *locker) Lock(k interface{}) {
	l.mu.Lock()
	m, ok := l.m[k]
	if !ok {
		m = new(lock)
		l.m[k] = m
	}
	m.ref++
	l.mu.Unlock()
	m.mu.Lock()
}

// Unlock unlocks a locker by unlocking, decreasing the ref count, and
// (optionally) deleting
func (l *locker) Unlock(k interface{}) {
	l.mu.Lock()
	m := l.m[k]
	m.mu.Unlock()
	m.ref--
	if m.ref == 0 {
		delete(l.m, k)
	}
	l.mu.Unlock()
}

type computation struct {
	node  node
	value interface{}
}

// cache caches computations
type cache struct {
	mu           sync.Mutex
	locker       *locker
	computations map[interface{}]*computation
}

func (c *cache) get(key interface{}) *computation {
	c.mu.Lock()
	defer c.mu.Unlock()

	return c.computations[key]
}

// set adds a computation to the cache for the given key
func (c *cache) set(key interface{}, computation *computation) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.computations[key] == nil {
		c.computations[key] = computation
	}
}

func (c *cache) cleanInvalidated() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for key, computation := range c.computations {
		if computation.node.Invalidated() {
			delete(c.computations, key)
		}
	}
}

// Resource represents a leaf-level dependency in a computation
type Resource struct {
	node
}

// NewResource creates a new Resource
func NewResource() *Resource {
	return &Resource{
		node: node{},
	}
}

// Invalidate permanently invalidates r
func (r *Resource) Invalidate() {
	go r.invalidate()
}

// Store invalidates all computations currently depending on r
func (r *Resource) Strobe() {
	go r.strobe()
}

// Cleanup registers a handler to be called when all computations using r stop
//
// NOTE: For f to be called, at least one computation must AddDependency r!
func (r *Resource) Cleanup(f func()) {
	r.node.handleRelease(f)
}

type computationKey struct{}
type cacheKey struct{}

func AddDependency(ctx context.Context, r *Resource) {
	if !HasRerunner(ctx) {
		r.node.addOut(&node{released: true})
		return
	}

	computation := ctx.Value(computationKey{}).(*computation)
	r.node.addOut(&computation.node)
}

type ComputeFunc func(context.Context) (interface{}, error)

func run(ctx context.Context, f ComputeFunc) (*computation, error) {
	// build result computation and local computation Ctx
	c := &computation{
		// this node will be freed either when the computation fails, or by our
		// caller
		node: node{},
	}

	childCtx := context.WithValue(ctx, computationKey{}, c)

	// Compute f and write the results to the c
	value, err := f(childCtx)
	if err != nil {
		go c.node.release()
		return nil, err
	}

	c.value = value

	return c, nil
}

func Cache(ctx context.Context, key interface{}, f ComputeFunc) (interface{}, error) {
	if !HasRerunner(ctx) {
		val, err := f(ctx)
		return val, err
	}

	cache := ctx.Value(cacheKey{}).(*cache)
	computation := ctx.Value(computationKey{}).(*computation)

	cache.locker.Lock(key)
	defer cache.locker.Unlock(key)

	if child := cache.get(key); child != nil {
		child.node.addOut(&computation.node)
		return child.value, nil
	}

	child, err := run(ctx, f)
	if err != nil {
		return nil, err
	}
	cache.set(key, child)

	child.node.addOut(&computation.node)
	return child.value, nil
}

// Rerunner automatically reruns a computation whenever its dependencies
// change.
//
// The computation stops when it returns an error or after calling Stop.  There
// is no way to get the output value from a computation. Instead, the
// computation should communicate its result before returning.
type Rerunner struct {
	ctx       context.Context
	cancelCtx context.CancelFunc

	f                ComputeFunc
	cache            *cache
	minRerunInterval time.Duration

	mu          sync.Mutex
	computation *computation
	stop        bool

	lastRun time.Time
}

// NewRerunner runs f continuously
func NewRerunner(ctx context.Context, f ComputeFunc, minRerunInterval time.Duration) *Rerunner {
	ctx, cancelCtx := context.WithCancel(ctx)

	r := &Rerunner{
		ctx:       ctx,
		cancelCtx: cancelCtx,

		f: f,
		cache: &cache{
			computations: make(map[interface{}]*computation),
			locker:       newLocker(),
		},
		minRerunInterval: minRerunInterval,
	}
	go r.run()
	return r
}

// run performs an actual computation
func (r *Rerunner) run() {
	// Wait for the minimum rerun interval. Exit early if the computation is stopped.
	delta := r.minRerunInterval - time.Now().Sub(r.lastRun)
	t := time.NewTimer(delta)
	select {
	case <-r.ctx.Done():
		t.Stop()
		return
	case <-t.C:
	}

	r.mu.Lock()
	defer r.mu.Unlock()

	// Bail out if the computation has been stopped.
	if r.stop {
		return
	}

	r.cache.cleanInvalidated()
	ctx := context.WithValue(r.ctx, cacheKey{}, r.cache)

	// Run f, and release the old computation right after.
	computation, err := run(ctx, r.f)
	if err != nil {
		if err == RetrySentinelError {
			go r.run()
		} else {
			return
		}
	} else {
		if r.computation != nil {
			go r.computation.node.release()
			r.computation = nil
		}

		r.computation = computation

		// schedule a rerun whenever our node becomes invalidated (which might already
		// have happened!)
		computation.node.handleInvalidate(r.run)
	}

	r.lastRun = time.Now()
}

func (r *Rerunner) Stop() {
	// Call cancelCtx before acquiring the lock as the lock might be held for a long time during a running computation.
	r.cancelCtx()

	r.mu.Lock()
	r.stop = true
	if r.computation != nil {
		go r.computation.node.release()
		r.computation = nil
	}
	r.mu.Unlock()
}

func HasRerunner(ctx context.Context) bool {
	return ctx.Value(computationKey{}) != nil
}
<commit_msg>reactive: add exponential backoff on retries for rerunner<commit_after>package reactive

import (
	"context"
	"errors"
	"sync"
	"time"
)

var (
	// Sentrinel error to tell the rerunner to not dump the current
	// computation cache and let the error'd function retry.
	RetrySentinelError = errors.New("retry")
)

// locker is a collection of mutexes indexed by arbitrary keys
type locker struct {
	mu sync.Mutex
	m  map[interface{}]*lock
}

// newLocker creates a new locker instance.
func newLocker() *locker {
	return &locker{
		m: make(map[interface{}]*lock),
	}
}

// lock is a single mutex in a locker
type lock struct {
	ref int
	mu  sync.Mutex
}

// Lock locks a locker by (optionally) allocating, increasing the ref count,
// and locking
func (l *locker) Lock(k interface{}) {
	l.mu.Lock()
	m, ok := l.m[k]
	if !ok {
		m = new(lock)
		l.m[k] = m
	}
	m.ref++
	l.mu.Unlock()
	m.mu.Lock()
}

// Unlock unlocks a locker by unlocking, decreasing the ref count, and
// (optionally) deleting
func (l *locker) Unlock(k interface{}) {
	l.mu.Lock()
	m := l.m[k]
	m.mu.Unlock()
	m.ref--
	if m.ref == 0 {
		delete(l.m, k)
	}
	l.mu.Unlock()
}

type computation struct {
	node  node
	value interface{}
}

// cache caches computations
type cache struct {
	mu           sync.Mutex
	locker       *locker
	computations map[interface{}]*computation
}

func (c *cache) get(key interface{}) *computation {
	c.mu.Lock()
	defer c.mu.Unlock()

	return c.computations[key]
}

// set adds a computation to the cache for the given key
func (c *cache) set(key interface{}, computation *computation) {
	c.mu.Lock()
	defer c.mu.Unlock()

	if c.computations[key] == nil {
		c.computations[key] = computation
	}
}

func (c *cache) cleanInvalidated() {
	c.mu.Lock()
	defer c.mu.Unlock()

	for key, computation := range c.computations {
		if computation.node.Invalidated() {
			delete(c.computations, key)
		}
	}
}

// Resource represents a leaf-level dependency in a computation
type Resource struct {
	node
}

// NewResource creates a new Resource
func NewResource() *Resource {
	return &Resource{
		node: node{},
	}
}

// Invalidate permanently invalidates r
func (r *Resource) Invalidate() {
	go r.invalidate()
}

// Store invalidates all computations currently depending on r
func (r *Resource) Strobe() {
	go r.strobe()
}

// Cleanup registers a handler to be called when all computations using r stop
//
// NOTE: For f to be called, at least one computation must AddDependency r!
func (r *Resource) Cleanup(f func()) {
	r.node.handleRelease(f)
}

type computationKey struct{}
type cacheKey struct{}

func AddDependency(ctx context.Context, r *Resource) {
	if !HasRerunner(ctx) {
		r.node.addOut(&node{released: true})
		return
	}

	computation := ctx.Value(computationKey{}).(*computation)
	r.node.addOut(&computation.node)
}

type ComputeFunc func(context.Context) (interface{}, error)

func run(ctx context.Context, f ComputeFunc) (*computation, error) {
	// build result computation and local computation Ctx
	c := &computation{
		// this node will be freed either when the computation fails, or by our
		// caller
		node: node{},
	}

	childCtx := context.WithValue(ctx, computationKey{}, c)

	// Compute f and write the results to the c
	value, err := f(childCtx)
	if err != nil {
		go c.node.release()
		return nil, err
	}

	c.value = value

	return c, nil
}

func Cache(ctx context.Context, key interface{}, f ComputeFunc) (interface{}, error) {
	if !HasRerunner(ctx) {
		val, err := f(ctx)
		return val, err
	}

	cache := ctx.Value(cacheKey{}).(*cache)
	computation := ctx.Value(computationKey{}).(*computation)

	cache.locker.Lock(key)
	defer cache.locker.Unlock(key)

	if child := cache.get(key); child != nil {
		child.node.addOut(&computation.node)
		return child.value, nil
	}

	child, err := run(ctx, f)
	if err != nil {
		return nil, err
	}
	cache.set(key, child)

	child.node.addOut(&computation.node)
	return child.value, nil
}

// Rerunner automatically reruns a computation whenever its dependencies
// change.
//
// The computation stops when it returns an error or after calling Stop.  There
// is no way to get the output value from a computation. Instead, the
// computation should communicate its result before returning.
type Rerunner struct {
	ctx       context.Context
	cancelCtx context.CancelFunc

	f                ComputeFunc
	cache            *cache
	minRerunInterval time.Duration
	retryDelay       time.Duration

	mu          sync.Mutex
	computation *computation
	stop        bool

	lastRun time.Time
}

// NewRerunner runs f continuously
func NewRerunner(ctx context.Context, f ComputeFunc, minRerunInterval time.Duration) *Rerunner {
	ctx, cancelCtx := context.WithCancel(ctx)

	r := &Rerunner{
		ctx:       ctx,
		cancelCtx: cancelCtx,

		f: f,
		cache: &cache{
			computations: make(map[interface{}]*computation),
			locker:       newLocker(),
		},
		minRerunInterval: minRerunInterval,
		retryDelay:       minRerunInterval,
	}
	go r.run()
	return r
}

// run performs an actual computation
func (r *Rerunner) run() {
	// Wait for the minimum rerun interval. Exit early if the computation is stopped.
	delta := r.retryDelay - time.Now().Sub(r.lastRun)
	t := time.NewTimer(delta)
	select {
	case <-r.ctx.Done():
		t.Stop()
		return
	case <-t.C:
	}

	r.mu.Lock()
	defer r.mu.Unlock()

	// Bail out if the computation has been stopped.
	if r.stop {
		return
	}

	r.cache.cleanInvalidated()
	ctx := context.WithValue(r.ctx, cacheKey{}, r.cache)

	// Run f, and release the old computation right after.
	computation, err := run(ctx, r.f)
	if err != nil {
		if err == RetrySentinelError {
			r.retryDelay = r.retryDelay * 2

			// Max out the retry delay to at 1 minute
			if r.retryDelay > time.Minute {
				r.retryDelay = time.Minute
			}
			go r.run()
		} else {
			// If we encountered an error that is not the retry sentinel,
			// we should stop the rerunner.
			return
		}
	} else {
		// If we succeeded in the computation, we can replace the old computation
		// and reset the retry delay.
		if r.computation != nil {
			go r.computation.node.release()
			r.computation = nil
		}

		r.computation = computation
		r.retryDelay = r.minRerunInterval

		// schedule a rerun whenever our node becomes invalidated (which might already
		// have happened!)
		computation.node.handleInvalidate(r.run)
	}

	r.lastRun = time.Now()
}

func (r *Rerunner) Stop() {
	// Call cancelCtx before acquiring the lock as the lock might be held for a long time during a running computation.
	r.cancelCtx()

	r.mu.Lock()
	r.stop = true
	if r.computation != nil {
		go r.computation.node.release()
		r.computation = nil
	}
	r.mu.Unlock()
}

func HasRerunner(ctx context.Context) bool {
	return ctx.Value(computationKey{}) != nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013-2014 The go-meeko-webhook-receiver AUTHORS
//
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package receiver

import (
	"net"
	"net/http"
	"os"
	"os/signal"

	"github.com/meeko-contrib/go-meeko-webhook-receiver/receiver/server"

	"github.com/meeko/go-meeko/agent"
)

// API functions ---------------------------------------------------------------

// Serve POST requests using the handler passed into ListenAndServe.
// This function blocks until a signal is received. So signals are being
// handled by this function, no need to do it manually.
func ListenAndServe(handler http.Handler) {
	if err := runListenAndServe(handler); err != nil {
		os.Exit(1)
	}
}

func runListenAndServe(handler http.Handler) error {
	// Make sure agent is terminated properly.
	defer agent.Terminate()

	// Load all the required environment variables, panic if any is not set.
	// This is placed here and not outside to make testing easier (possible).
	// The applications do not have to really connect to Cider to run tests.
	var (
		addr  = os.Getenv("LISTEN_ADDRESS")
		token = os.Getenv("ACCESS_TOKEN")
	)
	switch {
	case addr == "":
		return agent.Logging.Critical("LISTEN_ADDRESS variable is not set")
	case token == "":
		return agent.Logging.Critical("ACCESS_TOKEN variable is not set")
	}

	// Start catching interrupts.
	signalCh := make(chan os.Signal, 1)
	signal.Notify(signalCh, os.Interrupt)

	// Listen.
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return agent.Logging.Critical(err)
	}

	// Start processing interrupts.
	interruptedCh := make(chan bool, 1)
	go func() {
		<-signalCh
		interruptedCh <- true
		listener.Close()
	}()

	// Keep serving until interrupted.
	err = http.Serve(listener, server.AuthenticatedServer(token, handler))
	if err != nil {
		select {
		case <-interruptedCh:
		default:
			return agent.Logging.Critical(err)
		}
	}
	return nil
}
<commit_msg>Update to the new agent API<commit_after>// Copyright (c) 2013-2014 The go-meeko-webhook-receiver AUTHORS
//
// Use of this source code is governed by The MIT License
// that can be found in the LICENSE file.

package receiver

import (
	"net"
	"net/http"
	"os"

	"github.com/meeko-contrib/go-meeko-webhook-receiver/receiver/server"

	"github.com/meeko/go-meeko/agent"
)

// API functions ---------------------------------------------------------------

// Serve POST requests using the handler passed into ListenAndServe.
// This function blocks until a signal is received. So signals are being
// handled by this function, no need to do it manually.
func ListenAndServe(handler http.Handler) {
	if err := runListenAndServe(handler); err != nil {
		os.Exit(1)
	}
}

func runListenAndServe(handler http.Handler) error {
	log := agent.Logging()

	// Load all the required environment variables, panic if any is not set.
	// This is placed here and not outside to make testing easier (possible).
	// The applications do not have to really connect to Cider to run tests.
	var (
		addr  = os.Getenv("LISTEN_ADDRESS")
		token = os.Getenv("ACCESS_TOKEN")
	)
	switch {
	case addr == "":
		return log.Critical("LISTEN_ADDRESS variable is not set")
	case token == "":
		return log.Critical("ACCESS_TOKEN variable is not set")
	}

	// Listen.
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		return log.Critical(err)
	}

	// Start processing interrupts.
	interruptedCh := make(chan struct{})
	go func() {
		<-agent.Stopped()
		close(interruptedCh)
		listener.Close()
	}()

	// Keep serving until interrupted.
	err = http.Serve(listener, server.AuthenticatedServer(token, handler))
	if err != nil {
		select {
		case <-interruptedCh:
		default:
			return log.Critical(err)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

// merges multiple frequency count files into one
// this is could use a merge sort and be smart

import (
	"bufio"
	"compress/gzip"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
)

// freqCount is mapping of string->count
type freqCount map[string]int

// make a new counter with some minor preallocation
//  each month has about 2.2M uniques
func newFreqCount() freqCount {
	return make(freqCount, 3000000)
}

// LoadCSV loads in a CSV in form of WORD,COUNT
func loadCSV(counts freqCount, fname string) error {
	fi, err := os.Open(fname)
	if err != nil {
		return err
	}
	defer fi.Close()

	fizip, err := gzip.NewReader(fi)
	if err != nil {
		return err
	}
	defer fizip.Close()

	scanner := bufio.NewScanner(fizip)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.SplitN(line, ",", 2)
		if len(parts) != 2 {
			return fmt.Errorf("Got extra junk in line: %q", line)
		}
		c, err := strconv.Atoi(parts[1])
		if err != nil {
			return fmt.Errorf("Number conversion failed: %q", line)
		}

		counts[parts[0]] += c
	}
	return scanner.Err()
}

func main() {
	outfile := flag.String("o", "", "output file name")
	mincount := flag.Int("mincount", 0, "only output if freqcount greater than this, 0 = all")
	flag.Parse()
	if *outfile == "" {
		log.Fatalf("Must specificy outfile")
	}
	args := flag.Args()
	counts := newFreqCount()
	for _, arg := range args {
		log.Printf("Loading %s", arg)
		err := loadCSV(counts, arg)
		if err != nil {
			log.Fatalf("%s: %s", arg, err)
		}
	}
	fo, err := os.Create(*outfile)
	if err != nil {
		log.Fatalf("OH NO, unable to write: %s", err)
	}
	fout := gzip.NewWriter(fo)

	keys := make([]string, 0, len(counts))
	total := 0
	for k, v := range counts {
		keys = append(keys, k)
		total += v
	}
	sort.Strings(keys)
	uniques := 0
	for _, k := range keys {
		if counts[k] > *mincount {
			fout.Write([]byte(fmt.Sprintf("%s,%d\n", k, counts[k])))
			uniques++
		}
	}

	fout.Close()
	fo.Close()
	log.Printf("DONE: wrote %s got %d unique words from %d", *outfile, uniques, total)
}
<commit_msg>improve uniqueness count<commit_after>package main

// merges multiple frequency count files into one
// this is could use a merge sort and be smart

import (
	"bufio"
	"compress/gzip"
	"flag"
	"fmt"
	"log"
	"os"
	"sort"
	"strconv"
	"strings"
)

// freqCount is mapping of string->count
type freqCount map[string]int

// make a new counter with some minor preallocation
//  each month has about 2.2M uniques
func newFreqCount() freqCount {
	return make(freqCount, 3000000)
}

// LoadCSV loads in a CSV in form of WORD,COUNT
func loadCSV(counts freqCount, fname string) error {
	fi, err := os.Open(fname)
	if err != nil {
		return err
	}
	defer fi.Close()

	fizip, err := gzip.NewReader(fi)
	if err != nil {
		return err
	}
	defer fizip.Close()

	scanner := bufio.NewScanner(fizip)
	for scanner.Scan() {
		line := scanner.Text()
		parts := strings.SplitN(line, ",", 2)
		if len(parts) != 2 {
			return fmt.Errorf("Got extra junk in line: %q", line)
		}
		c, err := strconv.Atoi(parts[1])
		if err != nil {
			return fmt.Errorf("Number conversion failed: %q", line)
		}

		counts[parts[0]] += c
	}
	return scanner.Err()
}

func main() {
	outfile := flag.String("o", "", "output file name")
	mincount := flag.Int("mincount", 0, "only output if freqcount greater than this, 0 = all")
	flag.Parse()
	if *outfile == "" {
		log.Fatalf("Must specificy outfile")
	}
	args := flag.Args()
	counts := newFreqCount()
	for _, arg := range args {
		log.Printf("Loading %s", arg)
		err := loadCSV(counts, arg)
		if err != nil {
			log.Fatalf("%s: %s", arg, err)
		}
	}
	fo, err := os.Create(*outfile)
	if err != nil {
		log.Fatalf("OH NO, unable to write: %s", err)
	}
	fout := gzip.NewWriter(fo)

	keys := make([]string, 0, len(counts))
	total := 0
	for k, v := range counts {
		total += v
		if v > *mincount {
			keys = append(keys, k)
		}
	}
	sort.Strings(keys)
	for _, k := range keys {
		fout.Write([]byte(fmt.Sprintf("%s,%d\n", k, counts[k])))
	}

	fout.Close()
	fo.Close()
	log.Printf("DONE: wrote %s got %d unique words from %d", *outfile, len(keys), total)
}
<|endoftext|>


<commit_before>// Package pinata is a utility to beat data out of interface{}, []interface{}
// and map[string]interface{}.
package pinata

import (
	"bytes"
	"fmt"
	"strings"
)

// Pinata holds a value and offers methods for extracting data from it.
type Pinata interface {
	Contents() interface{}
	Error() error
	ClearError()
	StringAtPath(...string) string
	String() string
	StringAtIndex(int) string
	PinataAtPath(...string) Pinata
	PinataAtIndex(int) Pinata
}

// New creates a new Pinata. Instances returned are not thread safe.
func New(contents interface{}) Pinata {
	switch t := contents.(type) {
	default:
		return &otherPinata{contents: t}
	case map[string]interface{}:
		return &mapPinata{contents: t}
	case []interface{}:
		return &slicePinata{}
	}
}

var _ = error(PinataError{})

// ErrorReason describes the reason for returning a PinataError.
type ErrorReason string

const (
	// ErrorReasonIncompatibleType indicates the contents of the Pinata is not compatible with the invoked method.
	ErrorReasonIncompatibleType ErrorReason = "incompatible type"
	// ErrorReasonNotFound indicates the input has not been found in the Pinata.
	ErrorReasonNotFound = "not found"
	// ErrorReasonInvalidInput indicates the input is not in the expected range or format.
	ErrorReasonInvalidInput = "invalid input"
)

// PinataError is set on the Pinata if something goes wrong.
type PinataError struct {
	Reason ErrorReason
	Method string
	Input  []interface{}
	Advice string
}

func (p PinataError) Error() string {
	var input string
	if len(p.Input) > 0 {
		var buf bytes.Buffer
		for i := range p.Input {
			_, _ = buf.WriteString("%#v")
			if i < len(p.Input)-1 {
				_, _ = buf.WriteString(", ")
			}
		}
		input = fmt.Sprintf(buf.String(), p.Input...)
	}
	return fmt.Sprintf("pinata: %s(%s) - %s (%s)", p.Method, input, p.Reason, p.Advice)
}

type basePinata struct {
	err error
}

func (p *basePinata) Error() error {
	return p.err
}

func (p *basePinata) ClearError() {
	p.err = nil
}

func (p *basePinata) String() string {
	if p.err != nil {
		return ""
	}
	p.err = &PinataError{
		Method: "String",
		Reason: ErrorReasonIncompatibleType,
		Input:  nil,
		Advice: "call this method on a string pinata",
	}
	return ""
}

func (p *basePinata) PinataAtIndex(index int) Pinata {
	if p.err != nil {
		return nil
	}
	p.indexUnsupported("PinataAtIndex", index)
	return nil
}

func (p *basePinata) PinataAtPath(path ...string) Pinata {
	if p.err != nil {
		return nil
	}
	p.pathUnsupported("PinataAtPath", path)
	return nil
}

func (p *basePinata) StringAtPath(path ...string) string {
	if p.err != nil {
		return ""
	}
	p.pathUnsupported("StringAtPath", path)
	return ""
}

func (p *basePinata) StringAtIndex(index int) string {
	if p.err != nil {
		return ""
	}
	p.indexUnsupported("StringAtIndex", index)
	return ""
}

func (p *basePinata) Contents() interface{} {
	return nil // should always override this method
}

// this method assumes p.err != nil
func (p *basePinata) indexUnsupported(method string, index int) {
	p.err = &PinataError{
		Method: method,
		Reason: ErrorReasonIncompatibleType,
		Input:  []interface{}{index},
		Advice: "call this method on a slice pinata",
	}
}

// this method assumes p.err != nil
func (p *basePinata) setIndexOutOfRange(method string, index int, contents []interface{}) bool {
	if index < 0 || index >= len(contents) {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonInvalidInput,
			Input:  []interface{}{index},
			Advice: fmt.Sprintf("specify an index from 0 to %d", len(contents)-1),
		}
		return true
	}
	return false
}

// this method assumes p.err != nil
func (p *basePinata) pathUnsupported(method string, path []string) {
	p.err = &PinataError{
		Method: method,
		Reason: ErrorReasonIncompatibleType,
		Input:  toInterfaceSlice(path),
		Advice: "call this method on a map pinata",
	}
}

type otherPinata struct {
	basePinata
	contents interface{}
}

func (p *otherPinata) String() string {
	if p.err != nil {
		return ""
	}
	if v, ok := p.contents.(string); ok {
		return v
	}
	return p.basePinata.String()
}

func (p *otherPinata) Contents() interface{} {
	return p.contents
}

type slicePinata struct {
	basePinata
	contents []interface{}
}

// this method assumes p.err != nil
func (p *slicePinata) pinataAtIndex(method string, index int) Pinata {
	if p.setIndexOutOfRange(method, index, p.contents) {
		return nil
	}
	return New(p.contents[index])
}

func (p *slicePinata) PinataAtIndex(index int) Pinata {
	if p.err != nil {
		return nil
	}
	return p.pinataAtIndex("PinataAtIndex", index)
}

func (p *slicePinata) StringAtIndex(index int) string {
	if p.err != nil {
		return ""
	}
	const method = "StringAtIndex"
	pinata := p.pinataAtIndex(method, index)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonIncompatibleType,
			Input:  []interface{}{index},
			Advice: "not a string, try another type",
		}
	}
	return s
}

func (p *slicePinata) Contents() interface{} {
	return p.contents
}

type mapPinata struct {
	basePinata
	contents map[string]interface{}
}

// this method assumes p.err != nil
func (p *mapPinata) pinataAtPath(method string, path ...string) Pinata {
	if len(path) == 0 {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonInvalidInput,
			Input:  toInterfaceSlice(path),
			Advice: "specify a path",
		}
		return nil
	}

	contents := p.contents
	for i := 0; i < len(path)-1; i++ {
		current := path[i]
		if v, ok := contents[current]; ok {
			if v, ok := v.(map[string]interface{}); ok {
				contents = v
			} else {
				p.err = &PinataError{
					Method: method,
					Reason: ErrorReasonIncompatibleType,
					Input:  toInterfaceSlice(path),
					Advice: fmt.Sprintf(`"%s" does not hold a pinata`, strings.Join(path[:i+1], `", "`)),
				}
				return nil
			}
		} else {
			p.err = &PinataError{
				Method: method,
				Reason: ErrorReasonNotFound,
				Input:  toInterfaceSlice(path),
				Advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path[:i+1], `", "`)),
			}
			return nil
		}
	}

	if v, ok := contents[path[len(path)-1]]; ok {
		return New(v)
	}

	p.err = &PinataError{
		Method: method,
		Reason: ErrorReasonNotFound,
		Input:  toInterfaceSlice(path),
		Advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path, `", "`)),
	}
	return nil
}

func (p *mapPinata) PinataAtPath(path ...string) Pinata {
	if p.err != nil {
		return nil
	}
	return p.pinataAtPath("PinataAtPath", path...)
}

func (p *mapPinata) StringAtPath(path ...string) string {
	if p.err != nil {
		return ""
	}
	const method = "StringAtPath"
	pinata := p.pinataAtPath(method, path...)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonIncompatibleType,
			Input:  toInterfaceSlice(path),
			Advice: "not a string, try another type",
		}
	}
	return s
}

func (p *mapPinata) Contents() interface{} {
	return p.contents
}

func toInterfaceSlice(c []string) []interface{} {
	ifaces := make([]interface{}, len(c))
	for i := range c {
		ifaces[i] = c[i]
	}
	return ifaces
}
<commit_msg>use methods for future optimisation<commit_after>// Package pinata is a utility to beat data out of interface{}, []interface{}
// and map[string]interface{}.
package pinata

import (
	"bytes"
	"fmt"
	"strings"
)

// Pinata holds a value and offers methods for extracting data from it.
type Pinata interface {
	Contents() interface{}
	Error() error
	ClearError()
	StringAtPath(...string) string
	String() string
	StringAtIndex(int) string
	PinataAtPath(...string) Pinata
	PinataAtIndex(int) Pinata
}

// New creates a new Pinata. Instances returned are not thread safe.
func New(contents interface{}) Pinata {
	switch t := contents.(type) {
	default:
		return &otherPinata{contents: t}
	case map[string]interface{}:
		return &mapPinata{contents: t}
	case []interface{}:
		return &slicePinata{}
	}
}

var _ = error(PinataError{})

// ErrorReason describes the reason for returning a PinataError.
type ErrorReason string

const (
	// ErrorReasonIncompatibleType indicates the contents of the Pinata is not compatible with the invoked method.
	ErrorReasonIncompatibleType ErrorReason = "incompatible type"
	// ErrorReasonNotFound indicates the input has not been found in the Pinata.
	ErrorReasonNotFound = "not found"
	// ErrorReasonInvalidInput indicates the input is not in the expected range or format.
	ErrorReasonInvalidInput = "invalid input"
)

// PinataError is set on the Pinata if something goes wrong.
type PinataError struct {
	Reason ErrorReason
	Method string
	input  []interface{}
	advice string
}

func (p PinataError) MethodInput() []interface{} {
	return p.input
}

func (p PinataError) Advice() string {
	return p.advice
}

func (p PinataError) Error() string {
	var methodInput = p.MethodInput()
	var input string
	if len(methodInput) > 0 {
		var buf bytes.Buffer
		for i := range methodInput {
			_, _ = buf.WriteString("%#v")
			if i < len(methodInput)-1 {
				_, _ = buf.WriteString(", ")
			}
		}
		input = fmt.Sprintf(buf.String(), methodInput...)
	}
	return fmt.Sprintf("pinata: %s(%s) - %s (%s)", p.Method, input, p.Reason, p.Advice())
}

type basePinata struct {
	err error
}

func (p *basePinata) Error() error {
	return p.err
}

func (p *basePinata) ClearError() {
	p.err = nil
}

func (p *basePinata) String() string {
	if p.err != nil {
		return ""
	}
	p.err = &PinataError{
		Method: "String",
		Reason: ErrorReasonIncompatibleType,
		input:  nil,
		advice: "call this method on a string pinata",
	}
	return ""
}

func (p *basePinata) PinataAtIndex(index int) Pinata {
	if p.err != nil {
		return nil
	}
	p.indexUnsupported("PinataAtIndex", index)
	return nil
}

func (p *basePinata) PinataAtPath(path ...string) Pinata {
	if p.err != nil {
		return nil
	}
	p.pathUnsupported("PinataAtPath", path)
	return nil
}

func (p *basePinata) StringAtPath(path ...string) string {
	if p.err != nil {
		return ""
	}
	p.pathUnsupported("StringAtPath", path)
	return ""
}

func (p *basePinata) StringAtIndex(index int) string {
	if p.err != nil {
		return ""
	}
	p.indexUnsupported("StringAtIndex", index)
	return ""
}

func (p *basePinata) Contents() interface{} {
	return nil // should always override this method
}

// this method assumes p.err != nil
func (p *basePinata) indexUnsupported(method string, index int) {
	p.err = &PinataError{
		Method: method,
		Reason: ErrorReasonIncompatibleType,
		input:  []interface{}{index},
		advice: "call this method on a slice pinata",
	}
}

// this method assumes p.err != nil
func (p *basePinata) setIndexOutOfRange(method string, index int, contents []interface{}) bool {
	if index < 0 || index >= len(contents) {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonInvalidInput,
			input:  []interface{}{index},
			advice: fmt.Sprintf("specify an index from 0 to %d", len(contents)-1),
		}
		return true
	}
	return false
}

// this method assumes p.err != nil
func (p *basePinata) pathUnsupported(method string, path []string) {
	p.err = &PinataError{
		Method: method,
		Reason: ErrorReasonIncompatibleType,
		input:  toInterfaceSlice(path),
		advice: "call this method on a map pinata",
	}
}

type otherPinata struct {
	basePinata
	contents interface{}
}

func (p *otherPinata) String() string {
	if p.err != nil {
		return ""
	}
	if v, ok := p.contents.(string); ok {
		return v
	}
	return p.basePinata.String()
}

func (p *otherPinata) Contents() interface{} {
	return p.contents
}

type slicePinata struct {
	basePinata
	contents []interface{}
}

// this method assumes p.err != nil
func (p *slicePinata) pinataAtIndex(method string, index int) Pinata {
	if p.setIndexOutOfRange(method, index, p.contents) {
		return nil
	}
	return New(p.contents[index])
}

func (p *slicePinata) PinataAtIndex(index int) Pinata {
	if p.err != nil {
		return nil
	}
	return p.pinataAtIndex("PinataAtIndex", index)
}

func (p *slicePinata) StringAtIndex(index int) string {
	if p.err != nil {
		return ""
	}
	const method = "StringAtIndex"
	pinata := p.pinataAtIndex(method, index)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonIncompatibleType,
			input:  []interface{}{index},
			advice: "not a string, try another type",
		}
	}
	return s
}

func (p *slicePinata) Contents() interface{} {
	return p.contents
}

type mapPinata struct {
	basePinata
	contents map[string]interface{}
}

// this method assumes p.err != nil
func (p *mapPinata) pinataAtPath(method string, path ...string) Pinata {
	if len(path) == 0 {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonInvalidInput,
			input:  toInterfaceSlice(path),
			advice: "specify a path",
		}
		return nil
	}

	contents := p.contents
	for i := 0; i < len(path)-1; i++ {
		current := path[i]
		if v, ok := contents[current]; ok {
			if v, ok := v.(map[string]interface{}); ok {
				contents = v
			} else {
				p.err = &PinataError{
					Method: method,
					Reason: ErrorReasonIncompatibleType,
					input:  toInterfaceSlice(path),
					advice: fmt.Sprintf(`"%s" does not hold a pinata`, strings.Join(path[:i+1], `", "`)),
				}
				return nil
			}
		} else {
			p.err = &PinataError{
				Method: method,
				Reason: ErrorReasonNotFound,
				input:  toInterfaceSlice(path),
				advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path[:i+1], `", "`)),
			}
			return nil
		}
	}

	if v, ok := contents[path[len(path)-1]]; ok {
		return New(v)
	}

	p.err = &PinataError{
		Method: method,
		Reason: ErrorReasonNotFound,
		input:  toInterfaceSlice(path),
		advice: fmt.Sprintf(`"%s" does not exist`, strings.Join(path, `", "`)),
	}
	return nil
}

func (p *mapPinata) PinataAtPath(path ...string) Pinata {
	if p.err != nil {
		return nil
	}
	return p.pinataAtPath("PinataAtPath", path...)
}

func (p *mapPinata) StringAtPath(path ...string) string {
	if p.err != nil {
		return ""
	}
	const method = "StringAtPath"
	pinata := p.pinataAtPath(method, path...)
	if p.err != nil {
		return ""
	}
	s := pinata.String()
	if pinata.Error() != nil {
		p.err = &PinataError{
			Method: method,
			Reason: ErrorReasonIncompatibleType,
			input:  toInterfaceSlice(path),
			advice: "not a string, try another type",
		}
	}
	return s
}

func (p *mapPinata) Contents() interface{} {
	return p.contents
}

func toInterfaceSlice(c []string) []interface{} {
	ifaces := make([]interface{}, len(c))
	for i := range c {
		ifaces[i] = c[i]
	}
	return ifaces
}
<|endoftext|>


<commit_before>// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txsort_test

import (
	"bytes"
	"encoding/hex"
	"io/ioutil"
	"path/filepath"
	"testing"

	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil/txsort"
)

// TestSort ensures the transaction sorting works according to the BIP.
func TestSort(t *testing.T) {
	tests := []struct {
		name         string
		hexFile      string
		isSorted     bool
		unsortedHash string
		sortedHash   string
	}{
		{
			name:         "first test case from BIPLI01 - sorts inputs only",
			hexFile:      "li01-1.hex",
			isSorted:     false,
			unsortedHash: "0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3",
			sortedHash:   "839503cb611a3e3734bd521c608f881be2293ff77b7384057ab994c794fce623",
		},
		{
			name:         "second test case from BIPLI01 - already sorted",
			hexFile:      "li01-2.hex",
			isSorted:     true,
			unsortedHash: "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f",
			sortedHash:   "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f",
		},
		{
			name:         "block 10001 tx[1] - sorts both inputs and outputs",
			hexFile:      "li01-3.hex",
			isSorted:     false,
			unsortedHash: "fbde5d03b027d2b9ba4cf5d4fecab9a99864df2637b25ea4cbcb1796ff6550ca",
			sortedHash:   "0a8c246c55f6b82f094d211f4f57167bf2ea4898741d218b09bdb2536fd8d13f",
		},
		{
			name:         "block 10001 tx[2] - sorts outputs only",
			hexFile:      "li01-4.hex",
			isSorted:     false,
			unsortedHash: "8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb",
			sortedHash:   "a3196553b928b0b6154b002fa9a1ce875adabc486fedaaaf4c17430fd4486329",
		},
		{
			name:         "block 100998 tx[6] - sorts outputs only, based on output script",
			hexFile:      "li01-5.hex",
			isSorted:     false,
			unsortedHash: "ff85e8fc92e71bbc217e3ea9a3bacb86b435e52b6df0b089d67302c293a2b81d",
			sortedHash:   "9a6c24746de024f77cac9b2138694f11101d1c66289261224ca52a25155a7c94",
		},
	}

	for _, test := range tests {
		// Load and deserialize the test transaction.
		filePath := filepath.Join("testdata", test.hexFile)
		txHexBytes, err := ioutil.ReadFile(filePath)
		if err != nil {
			t.Errorf("ReadFile (%s): failed to read test file: %v",
				test.name, err)
			continue
		}
		txBytes, err := hex.DecodeString(string(txHexBytes))
		if err != nil {
			t.Errorf("DecodeString (%s): failed to decode tx: %v",
				test.name, err)
			continue
		}
		var tx wire.MsgTx
		err = tx.Deserialize(bytes.NewReader(txBytes))
		if err != nil {
			t.Errorf("Deserialize (%s): unexpected error %v",
				test.name, err)
			continue
		}

		// Ensure the sort order of the original transaction matches the
		// expected value.
		if got := txsort.IsSorted(&tx); got != test.isSorted {
			t.Errorf("IsSorted (%s): sort does not match "+
				"expected - got %v, want %v", test.name, got,
				test.isSorted)
			continue
		}

		// Sort the transaction and ensure the resulting hash is the
		// expected value.
		sortedTx := txsort.Sort(&tx)
		if got := sortedTx.TxSha().String(); got != test.sortedHash {
			t.Errorf("Sort (%s): sorted hash does not match "+
				"expected - got %v, want %v", test.name, got,
				test.sortedHash)
			continue
		}

		// Ensure the original transaction is not modified.
		if got := tx.TxSha().String(); got != test.unsortedHash {
			t.Errorf("Sort (%s): unsorted hash does not match "+
				"expected - got %v, want %v", test.name, got,
				test.unsortedHash)
			continue
		}

		// Now sort the transaction using the mutable version and ensure
		// the resulting hash is the expected value.
		txsort.InPlaceSort(&tx)
		if got := tx.TxSha().String(); got != test.sortedHash {
			t.Errorf("SortMutate (%s): sorted hash does not match "+
				"expected - got %v, want %v", test.name, got,
				test.sortedHash)
			continue
		}
	}
}
<commit_msg>txsort: Correct the names of the tests.<commit_after>// Copyright (c) 2015 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package txsort_test

import (
	"bytes"
	"encoding/hex"
	"io/ioutil"
	"path/filepath"
	"testing"

	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil/txsort"
)

// TestSort ensures the transaction sorting works according to the BIP.
func TestSort(t *testing.T) {
	tests := []struct {
		name         string
		hexFile      string
		isSorted     bool
		unsortedHash string
		sortedHash   string
	}{
		{
			name:         "first test case from BIPLI01 - sorts inputs only, based on hash",
			hexFile:      "li01-1.hex",
			isSorted:     false,
			unsortedHash: "0a6a357e2f7796444e02638749d9611c008b253fb55f5dc88b739b230ed0c4c3",
			sortedHash:   "839503cb611a3e3734bd521c608f881be2293ff77b7384057ab994c794fce623",
		},
		{
			name:         "second test case from BIPLI01 - already sorted",
			hexFile:      "li01-2.hex",
			isSorted:     true,
			unsortedHash: "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f",
			sortedHash:   "28204cad1d7fc1d199e8ef4fa22f182de6258a3eaafe1bbe56ebdcacd3069a5f",
		},
		{
			name:         "block 100001 tx[1] - sorts outputs only, based on amount",
			hexFile:      "li01-3.hex",
			isSorted:     false,
			unsortedHash: "fbde5d03b027d2b9ba4cf5d4fecab9a99864df2637b25ea4cbcb1796ff6550ca",
			sortedHash:   "0a8c246c55f6b82f094d211f4f57167bf2ea4898741d218b09bdb2536fd8d13f",
		},
		{
			name:         "block 100001 tx[2] - sorts both inputs and outputs",
			hexFile:      "li01-4.hex",
			isSorted:     false,
			unsortedHash: "8131ffb0a2c945ecaf9b9063e59558784f9c3a74741ce6ae2a18d0571dac15bb",
			sortedHash:   "a3196553b928b0b6154b002fa9a1ce875adabc486fedaaaf4c17430fd4486329",
		},
		{
			name:         "block 100998 tx[6] - sorts outputs only, based on output script",
			hexFile:      "li01-5.hex",
			isSorted:     false,
			unsortedHash: "ff85e8fc92e71bbc217e3ea9a3bacb86b435e52b6df0b089d67302c293a2b81d",
			sortedHash:   "9a6c24746de024f77cac9b2138694f11101d1c66289261224ca52a25155a7c94",
		},
	}

	for _, test := range tests {
		// Load and deserialize the test transaction.
		filePath := filepath.Join("testdata", test.hexFile)
		txHexBytes, err := ioutil.ReadFile(filePath)
		if err != nil {
			t.Errorf("ReadFile (%s): failed to read test file: %v",
				test.name, err)
			continue
		}
		txBytes, err := hex.DecodeString(string(txHexBytes))
		if err != nil {
			t.Errorf("DecodeString (%s): failed to decode tx: %v",
				test.name, err)
			continue
		}
		var tx wire.MsgTx
		err = tx.Deserialize(bytes.NewReader(txBytes))
		if err != nil {
			t.Errorf("Deserialize (%s): unexpected error %v",
				test.name, err)
			continue
		}

		// Ensure the sort order of the original transaction matches the
		// expected value.
		if got := txsort.IsSorted(&tx); got != test.isSorted {
			t.Errorf("IsSorted (%s): sort does not match "+
				"expected - got %v, want %v", test.name, got,
				test.isSorted)
			continue
		}

		// Sort the transaction and ensure the resulting hash is the
		// expected value.
		sortedTx := txsort.Sort(&tx)
		if got := sortedTx.TxSha().String(); got != test.sortedHash {
			t.Errorf("Sort (%s): sorted hash does not match "+
				"expected - got %v, want %v", test.name, got,
				test.sortedHash)
			continue
		}

		// Ensure the original transaction is not modified.
		if got := tx.TxSha().String(); got != test.unsortedHash {
			t.Errorf("Sort (%s): unsorted hash does not match "+
				"expected - got %v, want %v", test.name, got,
				test.unsortedHash)
			continue
		}

		// Now sort the transaction using the mutable version and ensure
		// the resulting hash is the expected value.
		txsort.InPlaceSort(&tx)
		if got := tx.TxSha().String(); got != test.sortedHash {
			t.Errorf("SortMutate (%s): sorted hash does not match "+
				"expected - got %v, want %v", test.name, got,
				test.sortedHash)
			continue
		}
	}
}
<|endoftext|>


<commit_before>package registry

type Registry interface {
	Register(*Service, ...RegisterOption) error
	Deregister(*Service) error
	GetService(string) ([]*Service, error)
	ListServices() ([]*Service, error)
	Watch() (Watcher, error)
	String() string
}

type Option func(*Options)

type RegisterOption func(*RegisterOptions)

var (
	DefaultRegistry = newConsulRegistry([]string{})
)

func NewRegistry(addrs []string, opt ...Option) Registry {
	return newConsulRegistry(addrs, opt...)
}

func Register(s *Service, opts ...RegisterOption) error {
	return DefaultRegistry.Register(s, opts...)
}

func Deregister(s *Service) error {
	return DefaultRegistry.Deregister(s)
}

func GetService(name string) ([]*Service, error) {
	return DefaultRegistry.GetService(name)
}

func ListServices() ([]*Service, error) {
	return DefaultRegistry.ListServices()
}

func Watch() (Watcher, error) {
	return DefaultRegistry.Watch()
}

func String() string {
	return DefaultRegistry.String()
}
<commit_msg>Add some comments<commit_after>package registry

// The registry provides an interface for service discovery
// and an abstraction over varying implementations
// {consul, etcd, zookeeper, ...}
type Registry interface {
	Register(*Service, ...RegisterOption) error
	Deregister(*Service) error
	GetService(string) ([]*Service, error)
	ListServices() ([]*Service, error)
	Watch() (Watcher, error)
	String() string
}

type Option func(*Options)

type RegisterOption func(*RegisterOptions)

var (
	DefaultRegistry = newConsulRegistry([]string{})
)

func NewRegistry(addrs []string, opt ...Option) Registry {
	return newConsulRegistry(addrs, opt...)
}

// Register a service node. Additionally supply options such as TTL.
func Register(s *Service, opts ...RegisterOption) error {
	return DefaultRegistry.Register(s, opts...)
}

// Deregister a service node
func Deregister(s *Service) error {
	return DefaultRegistry.Deregister(s)
}

// Retrieve a service. A slice is returned since we separate Name/Version.
func GetService(name string) ([]*Service, error) {
	return DefaultRegistry.GetService(name)
}

// List the services. Only returns service names
func ListServices() ([]*Service, error) {
	return DefaultRegistry.ListServices()
}

// Watch returns a watcher which allows you to track updates to the registry.
func Watch() (Watcher, error) {
	return DefaultRegistry.Watch()
}

func String() string {
	return DefaultRegistry.String()
}
<|endoftext|>


<commit_before>// Package registry contains client primitives to interact with a remote Docker registry.
package registry // import "github.com/docker/docker/registry"

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/docker/distribution/registry/client/transport"
	"github.com/docker/go-connections/tlsconfig"
	"github.com/sirupsen/logrus"
)

var (
	// ErrAlreadyExists is an error returned if an image being pushed
	// already exists on the remote side
	ErrAlreadyExists = errors.New("Image already exists")
)

// HostCertsDir returns the config directory for a specific host
func HostCertsDir(hostname string) (string, error) {
	certsDir := CertsDir()

	hostDir := filepath.Join(certsDir, cleanPath(hostname))

	return hostDir, nil
}

func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
	// PreferredServerCipherSuites should have no effect
	tlsConfig := tlsconfig.ServerDefault()

	tlsConfig.InsecureSkipVerify = !isSecure

	if isSecure && CertsDir() != "" {
		hostDir, err := HostCertsDir(hostname)
		if err != nil {
			return nil, err
		}

		logrus.Debugf("hostDir: %s", hostDir)
		if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
			return nil, err
		}
	}

	return tlsConfig, nil
}

func hasFile(files []os.DirEntry, name string) bool {
	for _, f := range files {
		if f.Name() == name {
			return true
		}
	}
	return false
}

// ReadCertsDirectory reads the directory for TLS certificates
// including roots and certificate pairs and updates the
// provided TLS configuration.
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
	fs, err := os.ReadDir(directory)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	for _, f := range fs {
		if strings.HasSuffix(f.Name(), ".crt") {
			if tlsConfig.RootCAs == nil {
				systemPool, err := tlsconfig.SystemCertPool()
				if err != nil {
					return fmt.Errorf("unable to get system cert pool: %v", err)
				}
				tlsConfig.RootCAs = systemPool
			}
			logrus.Debugf("crt: %s", filepath.Join(directory, f.Name()))
			data, err := os.ReadFile(filepath.Join(directory, f.Name()))
			if err != nil {
				return err
			}
			tlsConfig.RootCAs.AppendCertsFromPEM(data)
		}
		if strings.HasSuffix(f.Name(), ".cert") {
			certName := f.Name()
			keyName := certName[:len(certName)-5] + ".key"
			logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, keyName) {
				return fmt.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
			}
			cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
			if err != nil {
				return err
			}
			tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		}
		if strings.HasSuffix(f.Name(), ".key") {
			keyName := f.Name()
			certName := keyName[:len(keyName)-4] + ".cert"
			logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, certName) {
				return fmt.Errorf("Missing client certificate %s for key %s", certName, keyName)
			}
		}
	}

	return nil
}

// Headers returns request modifiers with a User-Agent and metaHeaders
func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModifier {
	modifiers := []transport.RequestModifier{}
	if userAgent != "" {
		modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{
			"User-Agent": []string{userAgent},
		}))
	}
	if metaHeaders != nil {
		modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
	}
	return modifiers
}

// HTTPClient returns an HTTP client structure which uses the given transport
// and contains the necessary headers for redirected requests
func HTTPClient(transport http.RoundTripper) *http.Client {
	return &http.Client{
		Transport:     transport,
		CheckRedirect: addRequiredHeadersToRedirectedRequests,
	}
}

func trustedLocation(req *http.Request) bool {
	var (
		trusteds = []string{"docker.com", "docker.io"}
		hostname = strings.SplitN(req.Host, ":", 2)[0]
	)
	if req.URL.Scheme != "https" {
		return false
	}

	for _, trusted := range trusteds {
		if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) {
			return true
		}
	}
	return false
}

// addRequiredHeadersToRedirectedRequests adds the necessary redirection headers
// for redirected requests
func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error {
	if len(via) != 0 && via[0] != nil {
		if trustedLocation(req) && trustedLocation(via[0]) {
			req.Header = via[0].Header
			return nil
		}
		for k, v := range via[0].Header {
			if k != "Authorization" {
				for _, vv := range v {
					req.Header.Add(k, vv)
				}
			}
		}
	}
	return nil
}

// NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
// default TLS configuration.
func NewTransport(tlsConfig *tls.Config) *http.Transport {
	if tlsConfig == nil {
		tlsConfig = tlsconfig.ServerDefault()
	}

	direct := &net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		DualStack: true,
	}

	base := &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		DialContext:         direct.DialContext,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     tlsConfig,
		// TODO(dmcgowan): Call close idle connections when complete and use keep alive
		DisableKeepAlives: true,
	}

	return base
}
<commit_msg>registry: remove unused registry.ErrAlreadyExists<commit_after>// Package registry contains client primitives to interact with a remote Docker registry.
package registry // import "github.com/docker/docker/registry"

import (
	"crypto/tls"
	"fmt"
	"net"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/docker/distribution/registry/client/transport"
	"github.com/docker/go-connections/tlsconfig"
	"github.com/sirupsen/logrus"
)

// HostCertsDir returns the config directory for a specific host
func HostCertsDir(hostname string) (string, error) {
	certsDir := CertsDir()

	hostDir := filepath.Join(certsDir, cleanPath(hostname))

	return hostDir, nil
}

func newTLSConfig(hostname string, isSecure bool) (*tls.Config, error) {
	// PreferredServerCipherSuites should have no effect
	tlsConfig := tlsconfig.ServerDefault()

	tlsConfig.InsecureSkipVerify = !isSecure

	if isSecure && CertsDir() != "" {
		hostDir, err := HostCertsDir(hostname)
		if err != nil {
			return nil, err
		}

		logrus.Debugf("hostDir: %s", hostDir)
		if err := ReadCertsDirectory(tlsConfig, hostDir); err != nil {
			return nil, err
		}
	}

	return tlsConfig, nil
}

func hasFile(files []os.DirEntry, name string) bool {
	for _, f := range files {
		if f.Name() == name {
			return true
		}
	}
	return false
}

// ReadCertsDirectory reads the directory for TLS certificates
// including roots and certificate pairs and updates the
// provided TLS configuration.
func ReadCertsDirectory(tlsConfig *tls.Config, directory string) error {
	fs, err := os.ReadDir(directory)
	if err != nil && !os.IsNotExist(err) {
		return err
	}

	for _, f := range fs {
		if strings.HasSuffix(f.Name(), ".crt") {
			if tlsConfig.RootCAs == nil {
				systemPool, err := tlsconfig.SystemCertPool()
				if err != nil {
					return fmt.Errorf("unable to get system cert pool: %v", err)
				}
				tlsConfig.RootCAs = systemPool
			}
			logrus.Debugf("crt: %s", filepath.Join(directory, f.Name()))
			data, err := os.ReadFile(filepath.Join(directory, f.Name()))
			if err != nil {
				return err
			}
			tlsConfig.RootCAs.AppendCertsFromPEM(data)
		}
		if strings.HasSuffix(f.Name(), ".cert") {
			certName := f.Name()
			keyName := certName[:len(certName)-5] + ".key"
			logrus.Debugf("cert: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, keyName) {
				return fmt.Errorf("missing key %s for client certificate %s. Note that CA certificates should use the extension .crt", keyName, certName)
			}
			cert, err := tls.LoadX509KeyPair(filepath.Join(directory, certName), filepath.Join(directory, keyName))
			if err != nil {
				return err
			}
			tlsConfig.Certificates = append(tlsConfig.Certificates, cert)
		}
		if strings.HasSuffix(f.Name(), ".key") {
			keyName := f.Name()
			certName := keyName[:len(keyName)-4] + ".cert"
			logrus.Debugf("key: %s", filepath.Join(directory, f.Name()))
			if !hasFile(fs, certName) {
				return fmt.Errorf("Missing client certificate %s for key %s", certName, keyName)
			}
		}
	}

	return nil
}

// Headers returns request modifiers with a User-Agent and metaHeaders
func Headers(userAgent string, metaHeaders http.Header) []transport.RequestModifier {
	modifiers := []transport.RequestModifier{}
	if userAgent != "" {
		modifiers = append(modifiers, transport.NewHeaderRequestModifier(http.Header{
			"User-Agent": []string{userAgent},
		}))
	}
	if metaHeaders != nil {
		modifiers = append(modifiers, transport.NewHeaderRequestModifier(metaHeaders))
	}
	return modifiers
}

// HTTPClient returns an HTTP client structure which uses the given transport
// and contains the necessary headers for redirected requests
func HTTPClient(transport http.RoundTripper) *http.Client {
	return &http.Client{
		Transport:     transport,
		CheckRedirect: addRequiredHeadersToRedirectedRequests,
	}
}

func trustedLocation(req *http.Request) bool {
	var (
		trusteds = []string{"docker.com", "docker.io"}
		hostname = strings.SplitN(req.Host, ":", 2)[0]
	)
	if req.URL.Scheme != "https" {
		return false
	}

	for _, trusted := range trusteds {
		if hostname == trusted || strings.HasSuffix(hostname, "."+trusted) {
			return true
		}
	}
	return false
}

// addRequiredHeadersToRedirectedRequests adds the necessary redirection headers
// for redirected requests
func addRequiredHeadersToRedirectedRequests(req *http.Request, via []*http.Request) error {
	if len(via) != 0 && via[0] != nil {
		if trustedLocation(req) && trustedLocation(via[0]) {
			req.Header = via[0].Header
			return nil
		}
		for k, v := range via[0].Header {
			if k != "Authorization" {
				for _, vv := range v {
					req.Header.Add(k, vv)
				}
			}
		}
	}
	return nil
}

// NewTransport returns a new HTTP transport. If tlsConfig is nil, it uses the
// default TLS configuration.
func NewTransport(tlsConfig *tls.Config) *http.Transport {
	if tlsConfig == nil {
		tlsConfig = tlsconfig.ServerDefault()
	}

	direct := &net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
		DualStack: true,
	}

	base := &http.Transport{
		Proxy:               http.ProxyFromEnvironment,
		DialContext:         direct.DialContext,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     tlsConfig,
		// TODO(dmcgowan): Call close idle connections when complete and use keep alive
		DisableKeepAlives: true,
	}

	return base
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package metrics

import (
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
)

const (
	// SchedulerSubsystem - subsystem name used by scheduler
	SchedulerSubsystem = "scheduler"
	// SchedulingLatencyName - scheduler latency metric name
	SchedulingLatencyName           = "scheduling_duration_seconds"
	DeprecatedSchedulingLatencyName = "scheduling_latency_seconds"

	// OperationLabel - operation label name
	OperationLabel = "operation"
	// Below are possible values for the operation label. Each represents a substep of e2e scheduling:

	// PredicateEvaluation - predicate evaluation operation label value
	PredicateEvaluation = "predicate_evaluation"
	// PriorityEvaluation - priority evaluation operation label value
	PriorityEvaluation = "priority_evaluation"
	// PreemptionEvaluation - preemption evaluation operation label value (occurs in case of scheduling fitError).
	PreemptionEvaluation = "preemption_evaluation"
	// Binding - binding operation label value
	Binding = "binding"
	// E2eScheduling - e2e scheduling operation label value
)

// All the histogram based metrics have 1ms as size for the smallest bucket.
var (
	scheduleAttempts = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "schedule_attempts_total",
			Help:      "Number of attempts to schedule pods, by the result. 'unschedulable' means a pod could not be scheduled, while 'error' means an internal scheduler problem.",
		}, []string{"result"})
	// PodScheduleSuccesses counts how many pods were scheduled.
	PodScheduleSuccesses = scheduleAttempts.With(prometheus.Labels{"result": "scheduled"})
	// PodScheduleFailures counts how many pods could not be scheduled.
	PodScheduleFailures = scheduleAttempts.With(prometheus.Labels{"result": "unschedulable"})
	// PodScheduleErrors counts how many pods could not be scheduled due to a scheduler error.
	PodScheduleErrors = scheduleAttempts.With(prometheus.Labels{"result": "error"})
	SchedulingLatency = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Subsystem: SchedulerSubsystem,
			Name:      SchedulingLatencyName,
			Help:      "Scheduling latency in seconds split by sub-parts of the scheduling operation",
			// Make the sliding window of 5h.
			// TODO: The value for this should be based on some SLI definition (long term).
			MaxAge: 5 * time.Hour,
		},
		[]string{OperationLabel},
	)
	DeprecatedSchedulingLatency = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Subsystem: SchedulerSubsystem,
			Name:      DeprecatedSchedulingLatencyName,
			Help:      "(Deprecated) Scheduling latency in seconds split by sub-parts of the scheduling operation",
			// Make the sliding window of 5h.
			// TODO: The value for this should be based on some SLI definition (long term).
			MaxAge: 5 * time.Hour,
		},
		[]string{OperationLabel},
	)
	E2eSchedulingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "e2e_scheduling_duration_seconds",
			Help:      "E2e scheduling latency in seconds (scheduling algorithm + binding)",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedE2eSchedulingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "e2e_scheduling_latency_microseconds",
			Help:      "(Deprecated) E2e scheduling latency in microseconds (scheduling algorithm + binding)",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_duration_seconds",
			Help:      "Scheduling algorithm latency in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_latency_microseconds",
			Help:      "(Deprecated) Scheduling algorithm latency in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmPredicateEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_predicate_evaluation_seconds",
			Help:      "Scheduling algorithm predicate evaluation duration in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmPredicateEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_predicate_evaluation",
			Help:      "(Deprecated) Scheduling algorithm predicate evaluation duration in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmPriorityEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_priority_evaluation_seconds",
			Help:      "Scheduling algorithm priority evaluation duration in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmPriorityEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_priority_evaluation",
			Help:      "(Deprecated) Scheduling algorithm priority evaluation duration in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmPremptionEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_preemption_evaluation_seconds",
			Help:      "Scheduling algorithm preemption evaluation duration in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmPremptionEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_preemption_evaluation",
			Help:      "(Deprecated) Scheduling algorithm preemption evaluation duration in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	BindingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "binding_duration_seconds",
			Help:      "Binding latency in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedBindingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "binding_latency_microseconds",
			Help:      "(Deprecated) Binding latency in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	PreemptionVictims = prometheus.NewGauge(
		prometheus.GaugeOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "pod_preemption_victims",
			Help:      "Number of selected preemption victims",
		})
	PreemptionAttempts = prometheus.NewCounter(
		prometheus.CounterOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "total_preemption_attempts",
			Help:      "Total preemption attempts in the cluster till now",
		})

	metricsList = []prometheus.Collector{
		scheduleAttempts,
		SchedulingLatency,
		DeprecatedSchedulingLatency,
		E2eSchedulingLatency,
		DeprecatedE2eSchedulingLatency,
		SchedulingAlgorithmLatency,
		DeprecatedSchedulingAlgorithmLatency,
		BindingLatency,
		DeprecatedBindingLatency,
		SchedulingAlgorithmPredicateEvaluationDuration,
		DeprecatedSchedulingAlgorithmPredicateEvaluationDuration,
		SchedulingAlgorithmPriorityEvaluationDuration,
		DeprecatedSchedulingAlgorithmPriorityEvaluationDuration,
		SchedulingAlgorithmPremptionEvaluationDuration,
		DeprecatedSchedulingAlgorithmPremptionEvaluationDuration,
		PreemptionVictims,
		PreemptionAttempts,
	}
)

var registerMetrics sync.Once

// Register all metrics.
func Register() {
	// Register the metrics.
	registerMetrics.Do(func() {
		for _, metric := range metricsList {
			prometheus.MustRegister(metric)
		}

		persistentvolume.RegisterVolumeSchedulingMetrics()
	})
}

// Reset resets metrics
func Reset() {
	SchedulingLatency.Reset()
	DeprecatedSchedulingLatency.Reset()
}

// SinceInMicroseconds gets the time since the specified start in microseconds.
func SinceInMicroseconds(start time.Time) float64 {
	return float64(time.Since(start).Nanoseconds() / time.Microsecond.Nanoseconds())
}

// SinceInSeconds gets the time since the specified start in seconds.
func SinceInSeconds(start time.Time) float64 {
	return time.Since(start).Seconds()
}
<commit_msg>add comments for new const<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package metrics

import (
	"sync"
	"time"

	"github.com/prometheus/client_golang/prometheus"
	"k8s.io/kubernetes/pkg/controller/volume/persistentvolume"
)

const (
	// SchedulerSubsystem - subsystem name used by scheduler
	SchedulerSubsystem = "scheduler"
	// SchedulingLatencyName - scheduler latency metric name
	SchedulingLatencyName = "scheduling_duration_seconds"
	// DeprecatedSchedulingLatencyName - scheduler latency metric name which is deprecated
	DeprecatedSchedulingLatencyName = "scheduling_latency_seconds"

	// OperationLabel - operation label name
	OperationLabel = "operation"
	// Below are possible values for the operation label. Each represents a substep of e2e scheduling:

	// PredicateEvaluation - predicate evaluation operation label value
	PredicateEvaluation = "predicate_evaluation"
	// PriorityEvaluation - priority evaluation operation label value
	PriorityEvaluation = "priority_evaluation"
	// PreemptionEvaluation - preemption evaluation operation label value (occurs in case of scheduling fitError).
	PreemptionEvaluation = "preemption_evaluation"
	// Binding - binding operation label value
	Binding = "binding"
	// E2eScheduling - e2e scheduling operation label value
)

// All the histogram based metrics have 1ms as size for the smallest bucket.
var (
	scheduleAttempts = prometheus.NewCounterVec(
		prometheus.CounterOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "schedule_attempts_total",
			Help:      "Number of attempts to schedule pods, by the result. 'unschedulable' means a pod could not be scheduled, while 'error' means an internal scheduler problem.",
		}, []string{"result"})
	// PodScheduleSuccesses counts how many pods were scheduled.
	PodScheduleSuccesses = scheduleAttempts.With(prometheus.Labels{"result": "scheduled"})
	// PodScheduleFailures counts how many pods could not be scheduled.
	PodScheduleFailures = scheduleAttempts.With(prometheus.Labels{"result": "unschedulable"})
	// PodScheduleErrors counts how many pods could not be scheduled due to a scheduler error.
	PodScheduleErrors = scheduleAttempts.With(prometheus.Labels{"result": "error"})
	SchedulingLatency = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Subsystem: SchedulerSubsystem,
			Name:      SchedulingLatencyName,
			Help:      "Scheduling latency in seconds split by sub-parts of the scheduling operation",
			// Make the sliding window of 5h.
			// TODO: The value for this should be based on some SLI definition (long term).
			MaxAge: 5 * time.Hour,
		},
		[]string{OperationLabel},
	)
	DeprecatedSchedulingLatency = prometheus.NewSummaryVec(
		prometheus.SummaryOpts{
			Subsystem: SchedulerSubsystem,
			Name:      DeprecatedSchedulingLatencyName,
			Help:      "(Deprecated) Scheduling latency in seconds split by sub-parts of the scheduling operation",
			// Make the sliding window of 5h.
			// TODO: The value for this should be based on some SLI definition (long term).
			MaxAge: 5 * time.Hour,
		},
		[]string{OperationLabel},
	)
	E2eSchedulingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "e2e_scheduling_duration_seconds",
			Help:      "E2e scheduling latency in seconds (scheduling algorithm + binding)",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedE2eSchedulingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "e2e_scheduling_latency_microseconds",
			Help:      "(Deprecated) E2e scheduling latency in microseconds (scheduling algorithm + binding)",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_duration_seconds",
			Help:      "Scheduling algorithm latency in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_latency_microseconds",
			Help:      "(Deprecated) Scheduling algorithm latency in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmPredicateEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_predicate_evaluation_seconds",
			Help:      "Scheduling algorithm predicate evaluation duration in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmPredicateEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_predicate_evaluation",
			Help:      "(Deprecated) Scheduling algorithm predicate evaluation duration in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmPriorityEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_priority_evaluation_seconds",
			Help:      "Scheduling algorithm priority evaluation duration in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmPriorityEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_priority_evaluation",
			Help:      "(Deprecated) Scheduling algorithm priority evaluation duration in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	SchedulingAlgorithmPremptionEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_preemption_evaluation_seconds",
			Help:      "Scheduling algorithm preemption evaluation duration in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedSchedulingAlgorithmPremptionEvaluationDuration = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "scheduling_algorithm_preemption_evaluation",
			Help:      "(Deprecated) Scheduling algorithm preemption evaluation duration in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	BindingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "binding_duration_seconds",
			Help:      "Binding latency in seconds",
			Buckets:   prometheus.ExponentialBuckets(0.001, 2, 15),
		},
	)
	DeprecatedBindingLatency = prometheus.NewHistogram(
		prometheus.HistogramOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "binding_latency_microseconds",
			Help:      "(Deprecated) Binding latency in microseconds",
			Buckets:   prometheus.ExponentialBuckets(1000, 2, 15),
		},
	)
	PreemptionVictims = prometheus.NewGauge(
		prometheus.GaugeOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "pod_preemption_victims",
			Help:      "Number of selected preemption victims",
		})
	PreemptionAttempts = prometheus.NewCounter(
		prometheus.CounterOpts{
			Subsystem: SchedulerSubsystem,
			Name:      "total_preemption_attempts",
			Help:      "Total preemption attempts in the cluster till now",
		})

	metricsList = []prometheus.Collector{
		scheduleAttempts,
		SchedulingLatency,
		DeprecatedSchedulingLatency,
		E2eSchedulingLatency,
		DeprecatedE2eSchedulingLatency,
		SchedulingAlgorithmLatency,
		DeprecatedSchedulingAlgorithmLatency,
		BindingLatency,
		DeprecatedBindingLatency,
		SchedulingAlgorithmPredicateEvaluationDuration,
		DeprecatedSchedulingAlgorithmPredicateEvaluationDuration,
		SchedulingAlgorithmPriorityEvaluationDuration,
		DeprecatedSchedulingAlgorithmPriorityEvaluationDuration,
		SchedulingAlgorithmPremptionEvaluationDuration,
		DeprecatedSchedulingAlgorithmPremptionEvaluationDuration,
		PreemptionVictims,
		PreemptionAttempts,
	}
)

var registerMetrics sync.Once

// Register all metrics.
func Register() {
	// Register the metrics.
	registerMetrics.Do(func() {
		for _, metric := range metricsList {
			prometheus.MustRegister(metric)
		}

		persistentvolume.RegisterVolumeSchedulingMetrics()
	})
}

// Reset resets metrics
func Reset() {
	SchedulingLatency.Reset()
	DeprecatedSchedulingLatency.Reset()
}

// SinceInMicroseconds gets the time since the specified start in microseconds.
func SinceInMicroseconds(start time.Time) float64 {
	return float64(time.Since(start).Nanoseconds() / time.Microsecond.Nanoseconds())
}

// SinceInSeconds gets the time since the specified start in seconds.
func SinceInSeconds(start time.Time) float64 {
	return time.Since(start).Seconds()
}
<|endoftext|>


<commit_before>package png

import (
	"fmt"
	"io"

	"github.com/fogleman/gg"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gomono"
	"golang.org/x/image/font/gofont/gosmallcapsitalic"

	"github.com/tomarus/chart/data"
	"github.com/tomarus/chart/format"
	myimg "github.com/tomarus/chart/image"
	"github.com/tomarus/chart/palette"
)

// PNG implements the chart interface to write PNG images.
type PNG struct {
	w                io.Writer
	gg               *gg.Context
	data             data.Collection
	width, height    int
	marginx, marginy int
	start, end       int64
	pal              *palette.Palette
}

// New initializes a new png chart image writer.
func New() *PNG {
	return &PNG{}
}

// Start initializes a new image and sets the defaults.
func (png *PNG) Start(wr io.Writer, w, h, mx, my int, start, end int64, p *palette.Palette, d data.Collection) {
	png.w = wr
	png.data = d
	png.width = w
	png.height = h
	png.marginx = mx
	png.marginy = my
	png.start = start
	png.end = end
	png.pal = p
}

// End finishes and writes the image to the output writer.
func (png *PNG) End() error {
	return png.gg.EncodePNG(png.w)
}

// Graph renders all chart dataset values to the visible chart area.
func (png *PNG) Graph() error {
	png.gg = gg.NewContext(png.width+png.marginx+4, png.height+(2*png.marginy)+((png.data.Len()+1)*16))
	png.gg.SetColor(png.pal.GetColor("background"))
	png.gg.Clear()

	for pt, data := range png.data {
		col := png.pal.GetAxisColorName(pt)
		a := float64(data.NMax) / float64(png.height)
		b := float64(data.NMax) - a*float64(png.height)
		for i := range data.Values {
			if data.Values[i] < 0 {
				return fmt.Errorf("Negative values not supported")
			}
			v := int(float64(data.Values[i])*a + b)
			png.Line(col, i+png.marginx, png.height+png.marginy, i+png.marginx, png.height-v+png.marginy)
		}
	}
	return nil
}

// face returns the font face to use. If the role is set to "title" a larger font is used.
func (png *PNG) face(role myimg.TextRole) {
	var ttfont *truetype.Font
	size := 13.
	dpi := 72.
	h := font.HintingNone

	if role == myimg.GridRole {
		f, err := truetype.Parse(gomono.TTF)
		if err != nil {
			panic(err)
		}
		ttfont = f
	} else {
		f, err := truetype.Parse(gosmallcapsitalic.TTF)
		if err != nil {
			panic(err)
		}
		ttfont = f
		size = 16.
	}

	face := truetype.NewFace(ttfont, &truetype.Options{
		Size:    size,
		DPI:     dpi,
		Hinting: h,
	})
	png.gg.SetFontFace(face)
}

// Text writes a string to the image.
func (png *PNG) Text(col, align string, role myimg.TextRole, x, y int, txt string) {
	ax := 0.
	switch align {
	case "middle", "center":
		ax = .5
	case "end", "right":
		ax = 1
	}
	png.gg.SetColor(png.pal.GetColor(col))
	png.face(role)
	png.gg.DrawStringAnchored(txt, float64(x), float64(y), ax, 0)
}

// TextID writes a string to the image.
func (png *PNG) TextID(id, col, align string, role myimg.TextRole, x, y int, txt string) {
	png.Text(col, align, role, x, y, txt)
}

// Line draws a line between the points using the color name from the palette.
func (png *PNG) Line(color string, x1, y1, x2, y2 int) {
	ruler := png.pal.GetColor(color)
	if color == "grid" || color == "grid2" {
		png.gg.SetDash(1)
		png.gg.SetLineWidth(.5)
		png.gg.DrawLine(float64(x1), float64(y1), float64(x2), float64(y2))
		png.gg.SetColor(png.pal.GetColor(color))
		png.gg.Stroke()
	} else {
		png.gg.SetLineWidth(1)
		png.gg.DrawLine(float64(x1), float64(y1), float64(x2), float64(y2))
		png.gg.SetColor(ruler)
		png.gg.Stroke()
	}
}

func (png *PNG) rectFill(color string, x1, y1, w, h int) {
	for i := 0; i < h; i++ {
		png.Line(color, x1, y1+i, x1+w, y1+i)
	}
}

// Legend draws the image specific legend.
func (png *PNG) Legend(base float64) {
	x := png.marginx
	y := png.height + png.marginy + 4

	q := "Min     Max     Avg"
	png.Text("title2", "right", myimg.GridRole, x+png.width, y+26, q)
	y += 16

	for i, d := range png.data {
		png.rectFill(png.pal.GetAxisColorName(i), x, y+16, 12, 12)

		min, max, avg := d.MinMaxAvg()
		// FIXME use axis formatters for this.
		mmax := format.SI(max, 1, base, "", "", "")
		mmin := format.SI(min, 1, base, "", "", "")
		mavg := format.SI(avg, 1, base, "", "", "")
		q := fmt.Sprintf("%6s  %6s  %6s", mmin, mmax, mavg)
		png.Text("title", "left", myimg.GridRole, x+20, y+26, d.Title)
		png.Text("title", "right", myimg.GridRole, x+png.width, y+26, q)
		png.Line("grid2", x, y+26+3, x+png.width, y+26+3)
		y += 16
	}
}

// Border draws a border around the chart area.
func (png *PNG) Border(x, y, w, h int) {
	c := "border"
	png.Line(c, x, y, x+w, y)
	png.Line(c, x+w, y, x+w, y+h)
	png.Line(c, x+w, y+h, x, y+h)
	png.Line(c, x, y+h, x, y)
}
<commit_msg>Use smaller font size for png output.<commit_after>package png

import (
	"fmt"
	"io"

	"github.com/fogleman/gg"
	"github.com/golang/freetype/truetype"
	"golang.org/x/image/font"
	"golang.org/x/image/font/gofont/gomono"
	"golang.org/x/image/font/gofont/gosmallcapsitalic"

	"github.com/tomarus/chart/data"
	"github.com/tomarus/chart/format"
	myimg "github.com/tomarus/chart/image"
	"github.com/tomarus/chart/palette"
)

// PNG implements the chart interface to write PNG images.
type PNG struct {
	w                io.Writer
	gg               *gg.Context
	data             data.Collection
	width, height    int
	marginx, marginy int
	start, end       int64
	pal              *palette.Palette
}

// New initializes a new png chart image writer.
func New() *PNG {
	return &PNG{}
}

// Start initializes a new image and sets the defaults.
func (png *PNG) Start(wr io.Writer, w, h, mx, my int, start, end int64, p *palette.Palette, d data.Collection) {
	png.w = wr
	png.data = d
	png.width = w
	png.height = h
	png.marginx = mx
	png.marginy = my
	png.start = start
	png.end = end
	png.pal = p
}

// End finishes and writes the image to the output writer.
func (png *PNG) End() error {
	return png.gg.EncodePNG(png.w)
}

// Graph renders all chart dataset values to the visible chart area.
func (png *PNG) Graph() error {
	png.gg = gg.NewContext(png.width+png.marginx+4, png.height+(2*png.marginy)+((png.data.Len()+1)*16))
	png.gg.SetColor(png.pal.GetColor("background"))
	png.gg.Clear()

	for pt, data := range png.data {
		col := png.pal.GetAxisColorName(pt)
		a := float64(data.NMax) / float64(png.height)
		b := float64(data.NMax) - a*float64(png.height)
		for i := range data.Values {
			if data.Values[i] < 0 {
				return fmt.Errorf("Negative values not supported")
			}
			v := int(float64(data.Values[i])*a + b)
			png.Line(col, i+png.marginx, png.height+png.marginy, i+png.marginx, png.height-v+png.marginy)
		}
	}
	return nil
}

// face returns the font face to use. If the role is set to "title" a larger font is used.
func (png *PNG) face(role myimg.TextRole) {
	var ttfont *truetype.Font
	size := 12.
	dpi := 72.
	h := font.HintingNone

	if role == myimg.GridRole {
		f, err := truetype.Parse(gomono.TTF)
		if err != nil {
			panic(err)
		}
		ttfont = f
	} else {
		f, err := truetype.Parse(gosmallcapsitalic.TTF)
		if err != nil {
			panic(err)
		}
		ttfont = f
		size = 15.
	}

	face := truetype.NewFace(ttfont, &truetype.Options{
		Size:    size,
		DPI:     dpi,
		Hinting: h,
	})
	png.gg.SetFontFace(face)
}

// Text writes a string to the image.
func (png *PNG) Text(col, align string, role myimg.TextRole, x, y int, txt string) {
	ax := 0.
	switch align {
	case "middle", "center":
		ax = .5
	case "end", "right":
		ax = 1
	}
	png.gg.SetColor(png.pal.GetColor(col))
	png.face(role)
	png.gg.DrawStringAnchored(txt, float64(x), float64(y), ax, 0)
}

// TextID writes a string to the image.
func (png *PNG) TextID(id, col, align string, role myimg.TextRole, x, y int, txt string) {
	png.Text(col, align, role, x, y, txt)
}

// Line draws a line between the points using the color name from the palette.
func (png *PNG) Line(color string, x1, y1, x2, y2 int) {
	ruler := png.pal.GetColor(color)
	if color == "grid" || color == "grid2" {
		png.gg.SetDash(1)
		png.gg.SetLineWidth(.5)
		png.gg.DrawLine(float64(x1), float64(y1), float64(x2), float64(y2))
		png.gg.SetColor(png.pal.GetColor(color))
		png.gg.Stroke()
	} else {
		png.gg.SetLineWidth(1)
		png.gg.DrawLine(float64(x1), float64(y1), float64(x2), float64(y2))
		png.gg.SetColor(ruler)
		png.gg.Stroke()
	}
}

func (png *PNG) rectFill(color string, x1, y1, w, h int) {
	for i := 0; i < h; i++ {
		png.Line(color, x1, y1+i, x1+w, y1+i)
	}
}

// Legend draws the image specific legend.
func (png *PNG) Legend(base float64) {
	x := png.marginx
	y := png.height + png.marginy + 4

	q := "Min     Max     Avg"
	png.Text("title2", "right", myimg.GridRole, x+png.width, y+26, q)
	y += 16

	for i, d := range png.data {
		png.rectFill(png.pal.GetAxisColorName(i), x, y+16, 12, 12)

		min, max, avg := d.MinMaxAvg()
		// FIXME use axis formatters for this.
		mmax := format.SI(max, 1, base, "", "", "")
		mmin := format.SI(min, 1, base, "", "", "")
		mavg := format.SI(avg, 1, base, "", "", "")
		q := fmt.Sprintf("%6s  %6s  %6s", mmin, mmax, mavg)
		png.Text("title", "left", myimg.GridRole, x+20, y+26, d.Title)
		png.Text("title", "right", myimg.GridRole, x+png.width, y+26, q)
		png.Line("grid2", x, y+26+3, x+png.width, y+26+3)
		y += 16
	}
}

// Border draws a border around the chart area.
func (png *PNG) Border(x, y, w, h int) {
	c := "border"
	png.Line(c, x, y, x+w, y)
	png.Line(c, x+w, y, x+w, y+h)
	png.Line(c, x+w, y+h, x, y+h)
	png.Line(c, x, y+h, x, y)
}
<|endoftext|>


<commit_before>package frame

import (
	"image"
)

func (f *Frame) Grid(pt image.Point) image.Point {
	return f.grid(pt)
}

// PointOf returns the point on the frame's
// bitmap that intersects the index p.
func (f *Frame) PointOf(p int64) image.Point {
	return f.pointOf(p, f.r.Min, 0)
}

func (f *Frame) grid(pt image.Point) image.Point {
	pt.Y -= f.r.Min.Y
	pt.Y -= pt.Y % f.Font.Dy()
	pt.Y += f.r.Min.Y
	if pt.X > f.r.Max.X {
		pt.X = f.r.Max.X
	}
	return pt
}
func (f *Frame) pointOf(p int64, pt image.Point, bn int) (x image.Point) {
	for ; bn < f.Nbox; bn++ {
		b := &f.Box[bn]
		pt = f.wrapMax(pt, b)
		l := b.Len()
		if p < int64(l) {
			if b.Nrune > 0 {
				ptr := b.Ptr
				bsb := len(ptr)
				i := 0
				for p > 0 {
					if bsb == i {
						break
					}
					size := 1
					widthPx := f.Font.Dx(ptr[i : i+size])
					i += size
					p -= int64(size)
					pt.X += widthPx
					if pt.X > f.r.Max.X {
						panic("PtOfCharPtBox")
					}
				}
			}
			break
		}
		p -= int64(l)
		pt = f.advance(pt, b)
	}
	return pt
}

func (f *Frame) pointOfBox(p int64, nb int) (pt image.Point) {
	Nbox := f.Nbox
	f.Nbox = nb
	pt = f.pointOf(p, f.r.Min, 0)
	f.Nbox = Nbox
	return pt
}
<commit_msg>frame: simplify pointOf<commit_after>package frame

import (
	"image"
)

func (f *Frame) Grid(pt image.Point) image.Point {
	return f.grid(pt)
}

// PointOf returns the point on the frame's
// bitmap that intersects the index p.
func (f *Frame) PointOf(p int64) image.Point {
	return f.pointOf(p, f.r.Min, 0)
}

func (f *Frame) grid(pt image.Point) image.Point {
	pt.Y -= f.r.Min.Y
	pt.Y -= pt.Y % f.Font.Dy()
	pt.Y += f.r.Min.Y
	if pt.X > f.r.Max.X {
		pt.X = f.r.Max.X
	}
	return pt
}
func (f *Frame) pointOf(p int64, pt image.Point, bn int) (x image.Point) {
	for ; bn < f.Nbox; bn++ {
		b := &f.Box[bn]
		pt = f.wrapMax(pt, b)
		l := b.Len()
		if p < int64(l) {
			if b.Nrune > 0 {
				ptr := b.Ptr
				if p > 0{
					pt.X += f.Font.Dx(ptr[:p])
				}
//				bsb := len(ptr)
//				i := 0
//				for p > 0 {
//					if bsb == i {
//						break
//					}
//					size := 1
//					widthPx := f.Font.Dx(ptr[i : i+size])
//					i += size
//					p -= int64(size)
//					pt.X += widthPx
//					if pt.X > f.r.Max.X {
//						panic("PtOfCharPtBox")
//					}
//				}
			}
			break
		}
		p -= int64(l)
		pt = f.advance(pt, b)
	}
	return pt
}

func (f *Frame) pointOfBox(p int64, nb int) (pt image.Point) {
	Nbox := f.Nbox
	f.Nbox = nb
	pt = f.pointOf(p, f.r.Min, 0)
	f.Nbox = Nbox
	return pt
}
<|endoftext|>


<commit_before>package virthandler
<commit_msg>Remove empty go file<commit_after><|endoftext|>


<commit_before>package model

import (
	"errors"
	"time"
)

type Job struct {
	ID         uint64    `gorm:"column:id; primary_key:yes"`
	JoinDate   time.Time `sql:"DEFAULT:null"`
	EndDate    time.Time `sql:"DEFAULT:null"`
	LeaveYears []LeaveYear
	LeaveAlloc []LeaveAlloc
	CreatedAt  time.Time
	UpdatedAt  time.Time
	DeletedAt  time.Time
}

type LeaveAlloc struct {
	ID          uint64 `gorm:"column:id; primary_key:yes"`
	AddedBy     User
	Days        float64
	Description string
	StartDate   time.Time `sql:"DEFAULT:null"`
}

type LeaveYear struct {
	ID        uint64    `gorm:"column:id; primary_key:yes"`
	StartDate time.Time `sql:"DEFAULT:null"`
	Days      float64
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type User struct {
	ID        uint64 `gorm:"column:id; primary_key:yes"`
	Name      string `sql:"type:text;"`
	GitHubID  uint64
	Email     string `sql:"type:text;"`
	TimeZone  int
	Jobs      []Job
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

func (u *User) UpdateOrCreate() error {
	if u.GitHubID == 0 {
		return errors.New("GitHub user ID was set to zero; cannot match")
	}

	res := db.Where(User{GitHubID: u.GitHubID}).FirstOrInit(u)

	if res.Error != nil {
		return res.Error
	}

	res = db.Save(u)
	return res.Error
}

func FindUser(id uint64) (user User, err error) {
	res := db.First(&user, id)
	return user, res.Error
}
<commit_msg>Rename JoinDate, EndDate in User model<commit_after>package model

import (
	"errors"
	"time"
)

type Job struct {
	ID         uint64 `gorm:"column:id; primary_key:yes"`
	StartTime  time.Time `sql:"DEFAULT:null"`
	EndTime    time.Time `sql:"DEFAULT:null"`
	LeaveYears []LeaveYear
	LeaveAlloc []LeaveAlloc

	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type LeaveAlloc struct {
	ID          uint64 `gorm:"column:id; primary_key:yes"`
	AddedBy     User
	Days        float64
	Description string
	StartDate   time.Time `sql:"DEFAULT:null"`
}

type LeaveYear struct {
	ID        uint64    `gorm:"column:id; primary_key:yes"`
	StartDate time.Time `sql:"DEFAULT:null"`
	Days      float64
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type User struct {
	ID        uint64 `gorm:"column:id; primary_key:yes"`
	Name      string `sql:"type:text;"`
	GitHubID  uint64
	Email     string `sql:"type:text;"`
	TimeZone  int
	Jobs      []Job
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

func (u *User) UpdateOrCreate() error {
	if u.GitHubID == 0 {
		return errors.New("GitHub user ID was set to zero; cannot match")
	}

	res := db.Where(User{GitHubID: u.GitHubID}).FirstOrInit(u)

	if res.Error != nil {
		return res.Error
	}

	res = db.Save(u)
	return res.Error
}

func FindUser(id uint64) (user User, err error) {
	res := db.First(&user, id)
	return user, res.Error
}
<|endoftext|>