<commit_before>package server

import (
	"bufio"
	"bytes"
	"io"
	"net/http"

	"github.com/influxdata/chronograf"
)

// URLPrefixer is a wrapper for an http.Handler that will prefix all occurrences of a relative URL with the configured Prefix
type URLPrefixer struct {
	Prefix string            // the prefix to be appended after any detected Attrs
	Next   http.Handler      // the http.Handler which will generate the content to be modified by this handler
	Attrs  [][]byte          // a list of attrs that should have their URLs prefixed. For example `src="` or `href="` would be valid
	Logger chronograf.Logger // The logger where prefixing errors will be dispatched to
}

type wrapResponseWriter struct {
	http.ResponseWriter
	Substitute *io.PipeWriter

	headerWritten bool
	dupHeader     http.Header
}

func (wrw wrapResponseWriter) Write(p []byte) (int, error) {
	return wrw.Substitute.Write(p)
}

func (wrw wrapResponseWriter) WriteHeader(code int) {
	if !wrw.headerWritten {
		wrw.ResponseWriter.Header().Set("Content-Type", wrw.Header().Get("Content-Type"))
		wrw.headerWritten = true
	}
	wrw.ResponseWriter.WriteHeader(code)
}

// Header() copies the Header map from the underlying ResponseWriter to prevent
// modifications to it by callers
func (wrw wrapResponseWriter) Header() http.Header {
	wrw.dupHeader = http.Header{}
	origHeader := wrw.ResponseWriter.Header()
	for k, v := range origHeader {
		wrw.dupHeader[k] = v
	}
	return wrw.dupHeader
}

const CHUNK_SIZE int = 512

// ServeHTTP implements an http.Handler that prefixes relative URLs from the
// Next handler with the configured prefix. It does this by examining the
// stream through the ResponseWriter, and appending the Prefix after any of the
// Attrs detected in the stream.
func (up *URLPrefixer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {

	// chunked transfer because we're modifying the response on the fly, so we
	// won't know the final content-length
	rw.Header().Set("Connection", "Keep-Alive")
	rw.Header().Set("Transfer-Encoding", "chunked")
	//rw.Header().Set("X-Content-Type-Options", "nosniff")

	writtenCount := 0 // number of bytes written to rw

	// extract the flusher for flushing chunks
	flusher, ok := rw.(http.Flusher)
	if !ok {
		up.Logger.
			WithField("component", "prefixer").
			Fatal("Exected http.ResponseWriter to be an http.Flusher, but wasn't")
	}

	nextRead, nextWrite := io.Pipe()
	go func() {
		defer nextWrite.Close()
		up.Next.ServeHTTP(wrapResponseWriter{ResponseWriter: rw, Substitute: nextWrite}, r)
	}()

	// setup a buffer which is the max length of our target attrs
	b := make([]byte, up.maxlen(up.Attrs...))
	io.ReadFull(nextRead, b) // prime the buffer with the start of the input
	buf := bytes.NewBuffer(b)

	// Read next handler's response byte by byte
	src := bufio.NewScanner(nextRead)
	src.Split(bufio.ScanBytes)
	for {
		window := buf.Bytes()

		// advance a byte if window is not a src attr
		if matchlen, match := up.match(window, up.Attrs...); matchlen == 0 {
			if src.Scan() {
				// shift the next byte into buf
				rw.Write(buf.Next(1))
				writtenCount++
				buf.Write(src.Bytes())

				if writtenCount >= CHUNK_SIZE {
					flusher.Flush()
					writtenCount = 0
				}
			} else {
				if err := src.Err(); err != nil {
					up.Logger.
						WithField("component", "prefixer").
						Error("Error encountered while scanning: err:", err)
				}
				rw.Write(window)
				flusher.Flush()
				break
			}
			continue
		} else {
			buf.Next(matchlen) // advance to the relative URL
			for i := 0; i < matchlen; i++ {
				src.Scan()
				buf.Write(src.Bytes())
			}
			rw.Write(match)               // add the src attr to the output
			io.WriteString(rw, up.Prefix) // write the prefix
		}
	}
}

// match compares the subject against a list of targets. If there is a match
// between any of them a non-zero value is returned. The returned value is the
// length of the match. It is assumed that subject's length > length of all
// targets. The matching []byte is also returned as the second return parameter
func (up *URLPrefixer) match(subject []byte, targets ...[]byte) (int, []byte) {
	for _, target := range targets {
		if bytes.Equal(subject[:len(target)], target) {
			return len(target), target
		}
	}
	return 0, []byte{}
}

// maxlen returns the length of the largest []byte provided to it as an argument
func (up *URLPrefixer) maxlen(targets ...[]byte) int {
	max := 0
	for _, tgt := range targets {
		if tlen := len(tgt); tlen > max {
			max = tlen
		}
	}
	return max
}

// NewDefaultURLPrefixer returns a URLPrefixer that will prefix any src and
// href attributes found in HTML as well as any url() directives found in CSS
// with the provided prefix. Additionally, it will prefix any `data-basepath`
// attributes as well for informing front end logic about any prefixes. `next`
// is the next http.Handler that will have its output prefixed
func NewDefaultURLPrefixer(prefix string, next http.Handler, lg chronograf.Logger) *URLPrefixer {
	return &URLPrefixer{
		Prefix: prefix,
		Next:   next,
		Logger: lg,
		Attrs: [][]byte{
			[]byte(`src="`),
			[]byte(`href="`),
			[]byte(`url(`),
			[]byte(`data-basepath="`), // for forwarding basepath to frontend
		},
	}
}
<commit_msg>Remove extraneous lines<commit_after>package server

import (
	"bufio"
	"bytes"
	"io"
	"net/http"

	"github.com/influxdata/chronograf"
)

// URLPrefixer is a wrapper for an http.Handler that will prefix all occurrences of a relative URL with the configured Prefix
type URLPrefixer struct {
	Prefix string            // the prefix to be appended after any detected Attrs
	Next   http.Handler      // the http.Handler which will generate the content to be modified by this handler
	Attrs  [][]byte          // a list of attrs that should have their URLs prefixed. For example `src="` or `href="` would be valid
	Logger chronograf.Logger // The logger where prefixing errors will be dispatched to
}

type wrapResponseWriter struct {
	http.ResponseWriter
	Substitute *io.PipeWriter

	headerWritten bool
	dupHeader     http.Header
}

func (wrw wrapResponseWriter) Write(p []byte) (int, error) {
	return wrw.Substitute.Write(p)
}

func (wrw wrapResponseWriter) WriteHeader(code int) {
	if !wrw.headerWritten {
		wrw.ResponseWriter.Header().Set("Content-Type", wrw.Header().Get("Content-Type"))
		wrw.headerWritten = true
	}
	wrw.ResponseWriter.WriteHeader(code)
}

// Header() copies the Header map from the underlying ResponseWriter to prevent
// modifications to it by callers
func (wrw wrapResponseWriter) Header() http.Header {
	wrw.dupHeader = http.Header{}
	origHeader := wrw.ResponseWriter.Header()
	for k, v := range origHeader {
		wrw.dupHeader[k] = v
	}
	return wrw.dupHeader
}

const CHUNK_SIZE int = 512

// ServeHTTP implements an http.Handler that prefixes relative URLs from the
// Next handler with the configured prefix. It does this by examining the
// stream through the ResponseWriter, and appending the Prefix after any of the
// Attrs detected in the stream.
func (up *URLPrefixer) ServeHTTP(rw http.ResponseWriter, r *http.Request) {
	// chunked transfer because we're modifying the response on the fly, so we
	// won't know the final content-length
	rw.Header().Set("Connection", "Keep-Alive")
	rw.Header().Set("Transfer-Encoding", "chunked")

	writtenCount := 0 // number of bytes written to rw

	// extract the flusher for flushing chunks
	flusher, ok := rw.(http.Flusher)
	if !ok {
		up.Logger.
			WithField("component", "prefixer").
			Fatal("Exected http.ResponseWriter to be an http.Flusher, but wasn't")
	}

	nextRead, nextWrite := io.Pipe()
	go func() {
		defer nextWrite.Close()
		up.Next.ServeHTTP(wrapResponseWriter{ResponseWriter: rw, Substitute: nextWrite}, r)
	}()

	// setup a buffer which is the max length of our target attrs
	b := make([]byte, up.maxlen(up.Attrs...))
	io.ReadFull(nextRead, b) // prime the buffer with the start of the input
	buf := bytes.NewBuffer(b)

	// Read next handler's response byte by byte
	src := bufio.NewScanner(nextRead)
	src.Split(bufio.ScanBytes)
	for {
		window := buf.Bytes()

		// advance a byte if window is not a src attr
		if matchlen, match := up.match(window, up.Attrs...); matchlen == 0 {
			if src.Scan() {
				// shift the next byte into buf
				rw.Write(buf.Next(1))
				writtenCount++
				buf.Write(src.Bytes())

				if writtenCount >= CHUNK_SIZE {
					flusher.Flush()
					writtenCount = 0
				}
			} else {
				if err := src.Err(); err != nil {
					up.Logger.
						WithField("component", "prefixer").
						Error("Error encountered while scanning: err:", err)
				}
				rw.Write(window)
				flusher.Flush()
				break
			}
			continue
		} else {
			buf.Next(matchlen) // advance to the relative URL
			for i := 0; i < matchlen; i++ {
				src.Scan()
				buf.Write(src.Bytes())
			}
			rw.Write(match)               // add the src attr to the output
			io.WriteString(rw, up.Prefix) // write the prefix
		}
	}
}

// match compares the subject against a list of targets. If there is a match
// between any of them a non-zero value is returned. The returned value is the
// length of the match. It is assumed that subject's length > length of all
// targets. The matching []byte is also returned as the second return parameter
func (up *URLPrefixer) match(subject []byte, targets ...[]byte) (int, []byte) {
	for _, target := range targets {
		if bytes.Equal(subject[:len(target)], target) {
			return len(target), target
		}
	}
	return 0, []byte{}
}

// maxlen returns the length of the largest []byte provided to it as an argument
func (up *URLPrefixer) maxlen(targets ...[]byte) int {
	max := 0
	for _, tgt := range targets {
		if tlen := len(tgt); tlen > max {
			max = tlen
		}
	}
	return max
}

// NewDefaultURLPrefixer returns a URLPrefixer that will prefix any src and
// href attributes found in HTML as well as any url() directives found in CSS
// with the provided prefix. Additionally, it will prefix any `data-basepath`
// attributes as well for informing front end logic about any prefixes. `next`
// is the next http.Handler that will have its output prefixed
func NewDefaultURLPrefixer(prefix string, next http.Handler, lg chronograf.Logger) *URLPrefixer {
	return &URLPrefixer{
		Prefix: prefix,
		Next:   next,
		Logger: lg,
		Attrs: [][]byte{
			[]byte(`src="`),
			[]byte(`href="`),
			[]byte(`url(`),
			[]byte(`data-basepath="`), // for forwarding basepath to frontend
		},
	}
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"log"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"
	"os"
	"sync"
)

func asKey(p string) string {
	basename := filepath.Base(p)
	return strings.TrimSuffix(basename, filepath.Ext(basename))
}

// Info is the main storage for information. All yaml files map to this.
type Info struct {
	ID   string
	Type string `yaml:"type"`
	Body string `yaml:"body"`
}

// LoadInfo loads an Info object from a file path
func LoadInfo(p string) (i Info, err error) {
	data, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal("Reading file failed: ", p)
	}

	i = Info{ID: asKey(p)}
	yaml.Unmarshal(data, &i)
	return
}

// Repo represents a repository of information yaml files.
type Repo struct {
	Key     string
	Info    map[string]Info
	Control map[string]Info
	root    string
	wg      sync.WaitGroup
}

// NewRepo loads a repository on a path
func NewRepo(p string) (r Repo) {
	path, _ := filepath.Abs(p)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatal("no such file or directory: %s", path)
	}

	r = Repo{Key: asKey(p), root: path}
	r.Info = make(map[string]Info)
	r.Control = make(map[string]Info)

	filepath.Walk(r.root, r.walk)
	r.wg.Wait()

	return
}

func (r *Repo) walk(path string, info os.FileInfo, err error) error {
	if err != nil {
		log.Println("walk error: ", err)
		return err
	}

	if strings.HasSuffix(path, ".yml") {
		r.wg.Add(1)
		go r.loadInfo(path)
	}

	return nil
}

func (r *Repo) loadInfo(path string) {
	defer r.wg.Done()

	info, err := LoadInfo(path)
	if err != nil {
		log.Println("Failed to load info: ", err)
	}

	// Control files start with an underscore and should not be stored as
	// normal Info documents.
	if strings.HasPrefix(asKey(path), "_") {
		r.Control[info.ID] = info
	} else {
		r.Info[info.ID] = info
	}
}
<commit_msg>Refactor repeated path checking code into getPath()<commit_after>package main

import (
	"io/ioutil"
	"log"
	"path/filepath"
	"strings"

	"gopkg.in/yaml.v2"
	"os"
	"sync"
)

func getPath(p string) string {
	path, _ := filepath.Abs(p)
	if _, err := os.Stat(path); os.IsNotExist(err) {
		log.Fatal(err)
	}
	return path
}

func asKey(p string) string {
	basename := filepath.Base(p)
	return strings.TrimSuffix(basename, filepath.Ext(basename))
}

// Info is the main storage for information. All yaml files map to this.
type Info struct {
	ID   string
	Type string `yaml:"type"`
	Body string `yaml:"body"`
}

// LoadInfo loads an Info object from a file path
func LoadInfo(p string) (i Info, err error) {
	data, err := ioutil.ReadFile(p)
	if err != nil {
		log.Fatal("Reading file failed: ", p)
	}

	i = Info{ID: asKey(p)}
	yaml.Unmarshal(data, &i)
	return
}

// Repo represents a repository of information yaml files.
type Repo struct {
	Key     string
	Info    map[string]Info
	Control map[string]Info
	root    string
	wg      sync.WaitGroup
}

// NewRepo loads a repository on a path
func NewRepo(p string) (r Repo) {
	r = Repo{Key: asKey(p), root: getPath(p)}
	r.Info = make(map[string]Info)
	r.Control = make(map[string]Info)

	filepath.Walk(r.root, r.walk)
	r.wg.Wait()

	return
}

func (r *Repo) walk(path string, info os.FileInfo, err error) error {
	if err != nil {
		log.Println("walk error: ", err)
		return err
	}

	if strings.HasSuffix(path, ".yml") {
		r.wg.Add(1)
		go r.loadInfo(path)
	}

	return nil
}

func (r *Repo) loadInfo(path string) {
	defer r.wg.Done()

	info, err := LoadInfo(path)
	if err != nil {
		log.Println("Failed to load info: ", err)
	}

	// Control files start with an underscore and should not be stored as
	// normal Info documents.
	if strings.HasPrefix(asKey(path), "_") {
		r.Control[info.ID] = info
	} else {
		r.Info[info.ID] = info
	}
}
<|endoftext|>


<commit_before>package commands

import (
	"os"

	"github.com/spf13/cobra"

	cfg "github.com/tendermint/go-config"
	"github.com/tendermint/log15"
	"github.com/tendermint/tendermint/types"
)

var resetAllCmd = &cobra.Command{
	Use:   "unsafe_reset_all",
	Short: "(unsafe) Remove all the data and WAL, reset this node's validator",
	Run:   resetAll,
}

var resetPrivValidatorCmd = &cobra.Command{
	Use:   "unsafe_reset_priv_validator",
	Short: "(unsafe) Reset this node's validator",
	Run:   resetPrivValidator,
}

func init() {
	RootCmd.AddCommand(resetAllCmd)
	RootCmd.AddCommand(resetPrivValidatorCmd)
}

// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetAll(cmd *cobra.Command, args []string) {
	ResetAll(config, log)
}

// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetPrivValidator(cmd *cobra.Command, args []string) {
	ResetPrivValidator(config, log)
}

// Exported so other CLI tools can use  it
func ResetAll(c cfg.Config, l log15.Logger) {
	ResetPrivValidator(c, l)
	os.RemoveAll(c.GetString("db_dir"))
}

func ResetPrivValidator(c cfg.Config, l log15.Logger) {
	// Get PrivValidator
	var privValidator *types.PrivValidator
	privValidatorFile := config.GetString("priv_validator_file")
	if _, err := os.Stat(privValidatorFile); err == nil {
		privValidator = types.LoadPrivValidator(privValidatorFile)
		privValidator.Reset()
		log.Notice("Reset PrivValidator", "file", privValidatorFile)
	} else {
		privValidator = types.GenPrivValidator()
		privValidator.SetFile(privValidatorFile)
		privValidator.Save()
		log.Notice("Generated PrivValidator", "file", privValidatorFile)
	}
}
<commit_msg>reset fix<commit_after>package commands

import (
	"os"

	"github.com/spf13/cobra"

	cfg "github.com/tendermint/go-config"
	"github.com/tendermint/log15"
	"github.com/tendermint/tendermint/types"
)

var resetAllCmd = &cobra.Command{
	Use:   "unsafe_reset_all",
	Short: "(unsafe) Remove all the data and WAL, reset this node's validator",
	Run:   resetAll,
}

var resetPrivValidatorCmd = &cobra.Command{
	Use:   "unsafe_reset_priv_validator",
	Short: "(unsafe) Reset this node's validator",
	Run:   resetPrivValidator,
}

func init() {
	RootCmd.AddCommand(resetAllCmd)
	RootCmd.AddCommand(resetPrivValidatorCmd)
}

// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetAll(cmd *cobra.Command, args []string) {
	ResetAll(config, log)
}

// XXX: this is totally unsafe.
// it's only suitable for testnets.
func resetPrivValidator(cmd *cobra.Command, args []string) {
	ResetPrivValidator(config, log)
}

// Exported so other CLI tools can use  it
func ResetAll(c cfg.Config, l log15.Logger) {
	ResetPrivValidator(c, l)
	os.RemoveAll(c.GetString("db_dir"))
}

func ResetPrivValidator(c cfg.Config, l log15.Logger) {
	// Get PrivValidator
	var privValidator *types.PrivValidator
	privValidatorFile := c.GetString("priv_validator_file")
	if _, err := os.Stat(privValidatorFile); err == nil {
		privValidator = types.LoadPrivValidator(privValidatorFile)
		privValidator.Reset()
		l.Notice("Reset PrivValidator", "file", privValidatorFile)
	} else {
		privValidator = types.GenPrivValidator()
		privValidator.SetFile(privValidatorFile)
		privValidator.Save()
		l.Notice("Generated PrivValidator", "file", privValidatorFile)
	}
}
<|endoftext|>


<commit_before>/* line line intersection given two lines as follows

consider w,s,and u as points, all with given coordinates
w and s lie on "l1", a line
consider the line l2 to be between the origin 0,0 and u

consider q as the intersection of lines one and two
find it's value!

                  l1
   |             /
   |            /s
   |           /
   |          /
   |         /w      ___----l2
   |        /  __---u
   |       /---
   |   __-/q
   |---  /
   ------------------------

How?

  First, consider V, a vector between w and s, or s minus w.
  vx is the x-distance between points w and s. vy is the y-distance.
  Consider W a vector from 0,0 to point w
  Consider U a vector from 0,0 to point u

  Now consider the wedge operator, '^', as defined by Geometric Algebra.
  It operates on two vectors with tails at origin, the result describing the
  "signed area" of a paralellogram formed by them if added.

  wedge A,B = A ^ B = ax*by - bx*ay

  Now, through various algebraic manipulations, it can be shown that the point
  q of intersection an be described as follows:

  qx = ux * ( V ^ W ) / ( V ^ U )
  qy = uy * ( V ^ W ) / ( V ^ U )

  Output is svg graphics file (text based graphics format)

Fun Fact
 If you only need to 'scale' the vector u so that it's head rests on the
 intersection point, then you can just do this

  scale factor = V^W / V^U

 Why? because Intersection vector = scale factor * u vector
 This is interesting to me... the simplicity of the scale factor could be
 useful in some situations.

 In this method, if modified for the situation where one is given 4 general
 points, two lying on line1 and two on line2... in such case as one needs
 to subtract the highest from lowest points and find wx,wy,ux,uy relative
 to the 'lowest' point which will become the origin, translated as such,
 it is interesting that you don't need to do this translation for the 'higher'
 point on line1, the "head" of v. You only need v's head as it relates
 to it's tail, not as it relates to origin.

 Computation

 wedge between two vectors described by rational-number coordinates are
 intesting.

 V ^ W

 becomes

 Vx Wy - Wx Vy

 which becomes, splitting numerator and denominator,

 Vxa   Wyc     Wxa   Vyc
 --- * ---  -  --- * ---
 Vxb   Wyd     Wxb   Vyd

 This in turn becomes

 Wxb * Vyd * Vxa * Wyc  -  Vxb * Wyd * Wxa * Vyc
 -----------------------------------------------
             Vxb * Wyd * Wxb * Vyd

 And the other wedge, V^U is similar

 Vx Uy - Ux Vy

 Uxb * Vyd * Vxa * Uyc - Vxb * Uyd * Uxa * Vyc
 ---------------------------------------------
             Vxb * Uyd * Uxb * Vyd

 But this second is divded from the first, which is the same thing
 as to multiply it by it's inverse. So.

 (Wxb*Vyd*Vxa*Wyc - Vxb*Wyd*Wxa*Vyc)              Vxb*Uyd*Uxb*Vyd
 ----------------------------------- * -------------------------------------
             Vxb*Wyd*Wxb*Vyd           ( Uxb*Vyd*Vxa*Uyc - Vxb*Uyd*Uxa*Vyc )

 Already there is something to cancel

 (Wxb*Vyd*Vxa*Wyc - Vxb*Wyd*Wxa*Vyc)                  Uyd*Uxb
 ----------------------------------- * -------------------------------------
                 Wyd*Wxb               ( Uxb*Vyd*Vxa*Uyc - Vxb*Uyd*Uxa*Vyc )

 And to regroup

 [ (Vyd*Vxa)(Wxb*Vyd) - (Vxb*Vyc)(Wyd*Wxa) ]   Uyd*Uxb
 -----------------------------------------------------
 [ (Vyd*Vxa)(Uxb*Uyc) - (Vxb*Vyc)(Uyd*Uxa) ]   Wyd*Wxb

 Notice anything strange? Yes!

 Vyd*Vxa - appears twice
 Vxb*Vyc - appears twice

 This means we have boiled the transactio down to the following operations
 integer multiplication: 8 initial, all paralellizable
 integer subtraction: 2
 integer multiplication: 2 final

 Now, I know in Traditional Rational Number implementations, like GNU
 gmp, they run a greatest common divisor algorithm to reduce the fraction
 after every calculation. But we don't need to do that.

 We don't necessarily need to do anything. We could, if we wanted to draw
 a point, do a division... then again... didn't Bresenham prove that
 division is overrated when drawing stuff on a pixel screen? Not saying
 I have an algorithm here... I'm just saying. What if?

 What this program actually does is just do a conversion to Floating Point
 and then does a division, without any of these above optimizations.

Number Type

 This uses 'fake rationals'. The rational is a ratio of to ints, but
 they do not expand if either one, numerator or denominator, overflows 32 bits,
 64 bits, or whatever bit width is being used.

 */

package main

import "fmt"

type Rational struct {
	num, denom int
}

type Vector struct {
	x, y Rational
}

type Point Vector

func tofloat( r Rational ) ( f float32 ) {
	if r.denom==0 {
		f = 3.40282346638528859811704183484516925440e+38
	} else {
		f = float32(r.num)/float32(r.denom)
	}
	return
}

func neg( a Rational ) ( r Rational ) {
	r.num = -a.num
	r.denom = a.denom
	return
}

func div( a,b Rational ) ( r Rational ) {
	r.num = a.num * b.denom
	r.denom = a.denom * b.num
	return
}

func add( a,b Rational ) ( r Rational ) {
	r.denom = a.denom * b.denom
	r.num = a.denom * b.num + b.denom * a.num
	return
}

func sub(a, b Rational ) ( r Rational ) {
	fmt.Println("sub",a,b,neg(b),add(a,neg(b)))
	r = add( a, neg(b) )
	return
}

func mul(a, b Rational) (c Rational) {
	c.num = a.num * b.num
	c.denom = a.denom * b.denom
	return
}

func wedge(a,b Vector) (r Rational) {
	r = sub(mul(a.x,b.y),mul(b.x,a.y))
	return
}

func svgbegin( width, height int ) (s string) {
	ns := "http://www.w3.org/2000/svg"
	tmp := `
<svg width='%d' height='%d' xmlns='%s' version='%s'>
 <!-- border -->
 <path fill='none' stroke='black' d='M 0 0 L %d %d L %d %d L %d %d L %d %d'/>
`
	s = fmt.Sprintf(tmp,width,height,ns,"1.1",0,0,height,0,width,height,width,0)
	return
}

func svgpoint(p Point) (s string) {
	f:=`
  <circle cx='%f' cy='%f' r='2' stroke='black' fill='none' />
`
	return fmt.Sprintf(f,tofloat(p.x),tofloat(p.y))
}

func svgend() (s string) {
	return "</svg>"
}

func svglines() (s string) {
	s = "  <path fill='none' stroke='black' fill-opacity='0.45' d='"
	return
}

func main() {
	fmt.Println(svgbegin(400,400))

	wx := Rational{2,1}
	wy := Rational{1,1}
	vx := Rational{3,1}
	vy := Rational{4,1}
	ux := Rational{6,1}
	uy := Rational{2,1}
	w := Vector{wx,wy}
	v := Vector{vx,vy}
	u := Vector{ux,uy}
	wedger := div(wedge(v,w),wedge(v,u))
	fmt.Println("wedge vw", wedge(v,w), "wedge vu", wedge(v,u))
	qx := mul(ux, wedger)
	qy := mul(uy, wedger)
	q := Point{qx,qy}

	fmt.Println("<!--",w,v,u,q,"-->")
	fmt.Println(svgpoint(q))
	fmt.Println(svgend())

}
<commit_msg>refactor to enable intersection given four points<commit_after>/* line line intersection given two lines as follows

consider w,s,and u as points, all with given coordinates
w and s lie on "l1", a line
consider the line l2 to be between the origin 0,0 and u

consider q as the intersection of lines one and two
find it's value!

                  l1
   |             /
   |            /s
   |           /
   |          /
   |         /w      ___----l2
   |        /  __---u
   |       /---
   |   __-/q
   |---  /
   ------------------------

How?

  First, consider V, a vector between w and s, or s minus w.
  vx is the x-distance between points w and s. vy is the y-distance.
  Consider W a vector from 0,0 to point w
  Consider U a vector from 0,0 to point u

  Now consider the wedge operator, '^', as defined by Geometric Algebra.
  It operates on two vectors with tails at origin, the result describing the
  "signed area" of a paralellogram formed by them if added.

  wedge A,B = A ^ B = ax*by - bx*ay

  Now, through various algebraic manipulations, it can be shown that the point
  q of intersection an be described as follows:

  qx = ux * ( V ^ W ) / ( V ^ U )
  qy = uy * ( V ^ W ) / ( V ^ U )

  Output is svg graphics file (text based graphics format)

Fun Fact
 If you only need to 'scale' the vector u so that it's head rests on the
 intersection point, then you can just do this

  scale factor = V^W / V^U

 Why? because Intersection vector = scale factor * u vector
 This is interesting to me... the simplicity of the scale factor could be
 useful in some situations.

 In this method, if modified for the situation where one is given 4 general
 points, two lying on line1 and two on line2... in such case as one needs
 to subtract the highest from lowest points and find wx,wy,ux,uy relative
 to the 'lowest' point which will become the origin, translated as such,
 it is interesting that you don't need to do this translation for the 'higher'
 point on line1, the "head" of v. You only need v's head as it relates
 to it's tail, not as it relates to origin.

 Computation

 wedge between two vectors described by rational-number coordinates are
 intesting.

 V ^ W

 becomes

 Vx Wy - Wx Vy

 which becomes, splitting numerator and denominator,

 Vxa   Wyc     Wxa   Vyc
 --- * ---  -  --- * ---
 Vxb   Wyd     Wxb   Vyd

 This in turn becomes

 Wxb * Vyd * Vxa * Wyc  -  Vxb * Wyd * Wxa * Vyc
 -----------------------------------------------
             Vxb * Wyd * Wxb * Vyd

 And the other wedge, V^U is similar

 Vx Uy - Ux Vy

 Uxb * Vyd * Vxa * Uyc - Vxb * Uyd * Uxa * Vyc
 ---------------------------------------------
             Vxb * Uyd * Uxb * Vyd

 But this second is divded from the first, which is the same thing
 as to multiply it by it's inverse. So.

 (Wxb*Vyd*Vxa*Wyc - Vxb*Wyd*Wxa*Vyc)              Vxb*Uyd*Uxb*Vyd
 ----------------------------------- * -------------------------------------
             Vxb*Wyd*Wxb*Vyd           ( Uxb*Vyd*Vxa*Uyc - Vxb*Uyd*Uxa*Vyc )

 Already there is something to cancel

 (Wxb*Vyd*Vxa*Wyc - Vxb*Wyd*Wxa*Vyc)                  Uyd*Uxb
 ----------------------------------- * -------------------------------------
                 Wyd*Wxb               ( Uxb*Vyd*Vxa*Uyc - Vxb*Uyd*Uxa*Vyc )

 And to regroup

 [ (Vyd*Vxa)(Wxb*Vyd) - (Vxb*Vyc)(Wyd*Wxa) ]   Uyd*Uxb
 -----------------------------------------------------
 [ (Vyd*Vxa)(Uxb*Uyc) - (Vxb*Vyc)(Uyd*Uxa) ]   Wyd*Wxb

 Notice anything strange? Yes!

 Vyd*Vxa - appears twice
 Vxb*Vyc - appears twice

 This means we have boiled the transactio down to the following operations
 integer multiplication: 8 initial, all paralellizable
 integer subtraction: 2
 integer multiplication: 2 final

 Now, I know in Traditional Rational Number implementations, like GNU
 gmp, they run a greatest common divisor algorithm to reduce the fraction
 after every calculation. But we don't need to do that.

 We don't necessarily need to do anything. We could, if we wanted to draw
 a point, do a division... then again... didn't Bresenham prove that
 division is overrated when drawing stuff on a pixel screen? Not saying
 I have an algorithm here... I'm just saying. What if?

 What this program actually does is just do a conversion to Floating Point
 and then does a division, without any of these above optimizations.

Number Type

 This uses 'fake rationals'. The rational is a ratio of to ints, but
 they do not expand if either one, numerator or denominator, overflows 32 bits,
 64 bits, or whatever bit width is being used.

 */

package main

import "fmt"

type Rational struct {
	num, denom int
}

type Vector struct {
	x, y Rational
}

type Point Vector

func tofloat( r Rational ) ( f float32 ) {
	if r.denom==0 {
		f = 3.40282346638528859811704183484516925440e+38
	} else {
		f = float32(r.num)/float32(r.denom)
	}
	return
}

func neg( a Rational ) ( r Rational ) {
	r.num = -a.num
	r.denom = a.denom
	return
}

func div( a,b Rational ) ( r Rational ) {
	r.num = a.num * b.denom
	r.denom = a.denom * b.num
	return
}

func add( a,b Rational ) ( r Rational ) {
	r.denom = a.denom * b.denom
	r.num = a.denom * b.num + b.denom * a.num
	return
}

func sub(a, b Rational ) ( r Rational ) {
	r = add( a, neg(b) )
	return
}

func mul(a, b Rational) (c Rational) {
	c.num = a.num * b.num
	c.denom = a.denom * b.denom
	return
}

func subpts( p2, p1 Point ) ( v Vector ) {
	v.x = sub( p2.x, p1.x )
	v.y = sub( p2.y, p1.y )
	return
}

func wedge(a,b Vector) (r Rational) {
	r = sub(mul(a.x,b.y),mul(b.x,a.y))
	return
}

func svgbegin( width, height int ) (s string) {
	ns := "http://www.w3.org/2000/svg"
	tmp := `
<svg width='%d' height='%d' xmlns='%s' version='%s'>
 <!-- border -->
 <path fill='none' stroke='black' d='M 0 0 L %d %d L %d %d L %d %d L %d %d'/>
`
	s = fmt.Sprintf(tmp,width,height,ns,"1.1",0,0,height,0,width,height,width,0)
	return
}

func svgpoint(p Point) (s string) {
	f:=`
  <circle cx='%f' cy='%f' r='2' stroke='black' fill='none' />
`
	return fmt.Sprintf(f,tofloat(p.x),tofloat(p.y))
}

func svgend() (s string) {
	return "</svg>"
}

func svglines() (s string) {
	s = "  <path fill='none' stroke='black' fill-opacity='0.45' d='"
	return
}

func find_intersect(w,v,u Vector) (q Point) {
	wedger := div(wedge(v,w),wedge(v,u))
	//fmt.Println("wedge vw", wedge(v,w), "wedge vu", wedge(v,u))
	qx := mul(u.x, wedger)
	qy := mul(u.y, wedger)
	return Point{qx,qy}
}

func raw_intersect( p1, p2, p3, p4 Point ) (q Point) {
	w := subpts(p1,p3)
	u := subpts(p4,p3)
	v := subpts(p2,p1)
	return find_intersect( w, u, v )
}

func main() {
	fmt.Println(svgbegin(400,400))

	wx := Rational{2,1}
	wy := Rational{1,1}
	vx := Rational{3,1}
	vy := Rational{4,1}
	ux := Rational{6,1}
	uy := Rational{2,1}
	w := Vector{wx,wy}
	v := Vector{vx,vy}
	u := Vector{ux,uy}

	q := find_intersect( w,v,u )

	fmt.Println("<!--",w,v,u,q,"-->")
	fmt.Println(svgpoint(q))
	fmt.Println(svgend())

}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/b1101/systemgo/system"
)

// TODO: introduce proper configuration
type config struct {
	// Default target
	Target string

	// Paths to search for unit files
	Paths []string

	// Method of communication with systemctl
	Method string

	// Http connection configuration
	*httpConfig
}

type httpConfig struct {
	// Port for system daemon to listen on
	Port
}

type Port int

func (p Port) String() string {
	return fmt.Sprintf(":%s", int(p))
}

var (
	// Instance of a system
	sys *system.System

	// Default configuration
	conf *config = &config{
		Target: "default.target",
		Method: "http",
		httpConfig: &httpConfig{
			Port: 28537,
		},
		Paths: []string{
			// Gentoo-specific
			//"/usr/lib/systemd/system",
			// User overrides
			//"/etc/systemd/system",

			"test",
		},
	}
)

func main() {
	// Initialize system
	sys = system.New()
	sys.SetPaths(conf.Paths...)

	// Start the default target
	if err := sys.Start(conf.Target); err != nil {
		sys.Log.Printf("Error starting default target %s: %s", conf.Target, err)
		log.Printf("Error starting %s: %s", conf.Target, err)
	}

	// Listen for systemctl requests
	switch conf.Method {
	case "http":
		if err := listenHTTP(conf.Port.String()); err != nil {
			log.Fatalf("Error starting server on %s: %s", conf.Port, err)
		}
	}
}

// Handle systemctl requests using HTTP
func listenHTTP(addr string) (err error) {
	server := http.NewServeMux()

	for name, handler := range sys.Handlers() {
		func(handler system.Handler) {
			server.HandleFunc("/"+name, func(w http.ResponseWriter, req *http.Request) {
				//msg := []systemctl.Response{}
				v := req.URL.Query()

				names, ok := v["unit"]
				if !ok {
					names = []string{}
				}

				var resp []byte
				if resp, err = json.Marshal(handler(names...)); err != nil {
					log.Printf("json.Marshal(result): %s", err)
					return
				}

				if _, err = w.Write(resp); err != nil {
					log.Printf("Write(resp): %s", err)
				}
			})
		}(handler)
	}

	return http.ListenAndServe(addr, server)
}
<commit_msg>init: start the rescue target, if the default fails<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"log"
	"net/http"

	"github.com/b1101/systemgo/system"
)

// TODO: introduce proper configuration
type config struct {
	// Default target
	Target string

	// Paths to search for unit files
	Paths []string

	// Method of communication with systemctl
	Method string

	// Http connection configuration
	*httpConfig
}

type httpConfig struct {
	// Port for system daemon to listen on
	Port
}

type Port int

func (p Port) String() string {
	return fmt.Sprintf(":%v", int(p))
}

var (
	// Instance of a system
	sys *system.System

	// Default configuration
	conf *config = &config{
		Target: "default.target",
		Method: "http",
		httpConfig: &httpConfig{
			Port: 28537,
		},
		Paths: []string{
			// Gentoo-specific
			//"/usr/lib/systemd/system",
			// User overrides
			//"/etc/systemd/system",

			"test",
		},
	}
)

func main() {
	// Initialize system
	sys = system.New()
	sys.SetPaths(conf.Paths...)

	// Start the default target
	if err := sys.Start(conf.Target); err != nil {
		sys.Log.Printf("Error starting default target %s: %s", conf.Target, err)
		log.Printf("Error starting %s: %s", conf.Target, err)
		if err = sys.Start("rescue.target"); err != nil {
			sys.Log.Printf("Error starting rescue target %s: %s", "rescue.target", err)
			log.Printf("Error starting %s: %s", "rescue.target", err)
		}
	}

	// Listen for systemctl requests
	switch conf.Method {
	case "http":
		if err := listenHTTP(conf.Port.String()); err != nil {
			log.Fatalf("Error starting server on %s: %s", conf.Port, err)
		}
	}
}

// Handle systemctl requests using HTTP
func listenHTTP(addr string) (err error) {
	server := http.NewServeMux()

	for name, handler := range sys.Handlers() {
		func(handler system.Handler) {
			server.HandleFunc("/"+name, func(w http.ResponseWriter, req *http.Request) {
				//msg := []systemctl.Response{}
				v := req.URL.Query()

				names, ok := v["unit"]
				if !ok {
					names = []string{}
				}

				var resp []byte
				if resp, err = json.Marshal(handler(names...)); err != nil {
					log.Printf("json.Marshal(result): %s", err)
					return
				}

				if _, err = w.Write(resp); err != nil {
					log.Printf("Write(resp): %s", err)
				}
			})
		}(handler)
	}

	return http.ListenAndServe(addr, server)
}
<|endoftext|>


<commit_before>package game

import (
	"github.com/MichaelDiBernardo/srl/lib/math"
	"testing"
)

type fakefighter struct {
	Trait
	Called bool
}

func (f *fakefighter) Hit(other Fighter) {
	f.Called = true
}

func TestPlayerMonsterCollisionsHit(t *testing.T) {
	g := newTestGame()
	pf := &fakefighter{Trait: Trait{obj: g.Player}}
	g.Player.Fighter = pf

	monster := g.NewObj(atActorSpec)
	mf := &fakefighter{Trait: Trait{obj: monster}}
	monster.Fighter = mf

	g.Level.Place(g.Player, math.Pt(1, 1))
	g.Level.Place(monster, math.Pt(1, 2))

	g.Player.Mover.Move(math.Pt(0, 1))

	if !pf.Called {
		t.Error("Moving player into other did not try to hit.")
	}

	monster.Mover.Move(math.Pt(0, -1))

	if !mf.Called {
		t.Error("Moving other into player did not try to hit.")
	}
}

func TestMonsterMonsterCollisionsHit(t *testing.T) {
	g := newTestGame()
	mon1 := g.NewObj(atActorSpec)
	mf1 := &fakefighter{Trait: Trait{obj: mon1}}
	mon1.Fighter = mf1

	mon2 := g.NewObj(atActorSpec)
	mf2 := &fakefighter{Trait: Trait{obj: mon2}}
	mon2.Fighter = mf2

	g.Level.Place(mon1, math.Pt(1, 1))
	g.Level.Place(mon2, math.Pt(1, 2))

	mon1.Mover.Move(math.Pt(0, 1))

	if mf1.Called {
		t.Error("Moving monster into monster tried to hit.")
	}
}

type hitTest struct {
	rolls    []int
	wanthp   int
	protdice Dice
}

func TestHit(t *testing.T) {
	tests := []hitTest{
		// Meleeroll = 1, evaderoll = 1, miss
		{[]int{1, 1}, 20, ZeroDice},
		// Meleeroll = 2, evaderoll = 1, roll 5 damage
		{[]int{2, 1, 5}, 15, ZeroDice},
		// Meleeroll = 2, evaderoll = 1, roll 5 damage, roll 2 prot
		{[]int{2, 1, 5, 2}, 17, NewDice(1, 4)},
		// Meleeroll = 8, evaderoll = 1, crit = 1, roll 5 + 3 damage
		{[]int{12, 1, 5, 3}, 12, ZeroDice},
		// Meleeroll = 15, evaderoll = 1, crit = 2, roll 3 + 2 + 1 damage
		{[]int{15, 1, 3, 2, 1}, 14, ZeroDice},
	}

	for i, test := range tests {
		testMonSpec := &Spec{
			Family:  FamActor,
			Genus:   GenMonster,
			Species: SpecOrc,
			Name:    "ORC",
			Traits: &Traits{
				Fighter: NewActorFighter,
				Sheet: NewMonsterSheet(&MonsterSheet{
					critdivmod: 0,
					maxhp:      20,
					damroll:    NewDice(1, 5),
					protroll:   test.protdice,
				}),
			},
		}

		g := newTestGame()
		attacker, defender := g.NewObj(testMonSpec), g.NewObj(testMonSpec)
		FixRandomDie(test.rolls)
		defer RestoreRandom()

		attacker.Fighter.Hit(defender.Fighter)
		if hp := defender.Sheet.HP(); hp != test.wanthp {
			t.Errorf(`Test %d: Defender has %d hp; want %d.`, i, hp, test.wanthp)
		}
	}
}

type applyBrandTest struct {
	atk  Effects
	def  Effects
	verb string
	dice int
}

func TestApplyBrand(t *testing.T) {
	const (
		fakeBrand1 Effect = NumEffects + iota
		fakeBrand2
		fakeResist1
	)

	oldEffectsSpecs := EffectsSpecs

	EffectsSpecs = EffectsSpec{
		fakeBrand1:  {Type: EffectTypeBrand, ResistedBy: fakeResist1, Verb: "frobs"},
		fakeBrand2:  {Type: EffectTypeBrand, Verb: "norfs"},
		fakeResist1: {Type: EffectTypeResist},
	}

	restoreEffectsDeps := func() {
		EffectsSpecs = oldEffectsSpecs
	}
	defer restoreEffectsDeps()

	tests := []applyBrandTest{
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  Effects{},
			verb: "frobs",
			dice: 1,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 2}),
			def:  Effects{},
			verb: "frobs",
			dice: 1,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: 1}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: 2}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: -1}),
			verb: "*frobs*",
			dice: 2,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: -2}),
			verb: "*frobs*",
			dice: 2,
		},
		{
			atk:  Effects{},
			def:  NewEffects(map[Effect]int{fakeResist1: -1}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  Effects{},
			def:  NewEffects(map[Effect]int{fakeResist1: 1}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1, fakeBrand2: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: -1}),
			verb: "*frobs*",
			dice: 3,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1, fakeBrand2: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: 1}),
			verb: "norfs",
			dice: 1,
		},
	}

	// No resist.
	for i, test := range tests {
		if dice, verb := applybrands(test.atk, test.def); dice != test.dice || verb != test.verb {
			t.Errorf(`Test %d: got (%d,"%s") want (%d,"%s")`, i, dice, verb, test.dice, test.verb)
		}
	}
}
<commit_msg>Testing melee effect infliction.<commit_after>package game

import (
	"github.com/MichaelDiBernardo/srl/lib/math"
	"testing"
)

type fakefighter struct {
	Trait
	Called bool
}

func (f *fakefighter) Hit(other Fighter) {
	f.Called = true
}

func TestPlayerMonsterCollisionsHit(t *testing.T) {
	g := newTestGame()
	pf := &fakefighter{Trait: Trait{obj: g.Player}}
	g.Player.Fighter = pf

	monster := g.NewObj(atActorSpec)
	mf := &fakefighter{Trait: Trait{obj: monster}}
	monster.Fighter = mf

	g.Level.Place(g.Player, math.Pt(1, 1))
	g.Level.Place(monster, math.Pt(1, 2))

	g.Player.Mover.Move(math.Pt(0, 1))

	if !pf.Called {
		t.Error("Moving player into other did not try to hit.")
	}

	monster.Mover.Move(math.Pt(0, -1))

	if !mf.Called {
		t.Error("Moving other into player did not try to hit.")
	}
}

func TestMonsterMonsterCollisionsHit(t *testing.T) {
	g := newTestGame()
	mon1 := g.NewObj(atActorSpec)
	mf1 := &fakefighter{Trait: Trait{obj: mon1}}
	mon1.Fighter = mf1

	mon2 := g.NewObj(atActorSpec)
	mf2 := &fakefighter{Trait: Trait{obj: mon2}}
	mon2.Fighter = mf2

	g.Level.Place(mon1, math.Pt(1, 1))
	g.Level.Place(mon2, math.Pt(1, 2))

	mon1.Mover.Move(math.Pt(0, 1))

	if mf1.Called {
		t.Error("Moving monster into monster tried to hit.")
	}
}

type hitTest struct {
	rolls    []int
	wanthp   int
	protdice Dice
}

func TestHit(t *testing.T) {
	tests := []hitTest{
		// Meleeroll = 1, evaderoll = 1, miss
		{[]int{1, 1}, 20, ZeroDice},
		// Meleeroll = 2, evaderoll = 1, roll 5 damage
		{[]int{2, 1, 5}, 15, ZeroDice},
		// Meleeroll = 2, evaderoll = 1, roll 5 damage, roll 2 prot
		{[]int{2, 1, 5, 2}, 17, NewDice(1, 4)},
		// Meleeroll = 8, evaderoll = 1, crit = 1, roll 5 + 3 damage
		{[]int{12, 1, 5, 3}, 12, ZeroDice},
		// Meleeroll = 15, evaderoll = 1, crit = 2, roll 3 + 2 + 1 damage
		{[]int{15, 1, 3, 2, 1}, 14, ZeroDice},
	}

	for i, test := range tests {
		testMonSpec := &Spec{
			Family:  FamActor,
			Genus:   GenMonster,
			Species: SpecOrc,
			Name:    "ORC",
			Traits: &Traits{
				Fighter: NewActorFighter,
				Sheet: NewMonsterSheet(&MonsterSheet{
					critdivmod: 0,
					maxhp:      20,
					damroll:    NewDice(1, 5),
					protroll:   test.protdice,
				}),
			},
		}

		g := newTestGame()
		attacker, defender := g.NewObj(testMonSpec), g.NewObj(testMonSpec)
		FixRandomDie(test.rolls)
		defer RestoreRandom()

		attacker.Fighter.Hit(defender.Fighter)
		if hp := defender.Sheet.HP(); hp != test.wanthp {
			t.Errorf(`Test %d: Defender has %d hp; want %d.`, i, hp, test.wanthp)
		}
	}
}

func TestHitPoison(t *testing.T) {
	testMonSpec := &Spec{
		Family:  FamActor,
		Genus:   GenMonster,
		Species: SpecOrc,
		Name:    "ORC",
		Traits: &Traits{
			Fighter: NewActorFighter,
			Sheet: NewMonsterSheet(&MonsterSheet{
				critdivmod: 0,
				maxhp:      20,
				damroll:    NewDice(1, 5),
				atkeffects: NewEffects(map[Effect]int{BrandPoison: 1}),
			}),
			Ticker: NewActorTicker,
		},
	}

	g := newTestGame()
	attacker, defender := g.NewObj(testMonSpec), g.NewObj(testMonSpec)
	// Roll 5 damage.
	FixRandomDie([]int{7, 1, 5, 0})
	defer RestoreRandom()

	attacker.Fighter.Hit(defender.Fighter)

	if poison := defender.Ticker.Counter(EffectPoison); poison != 5 {
		t.Errorf(`Poison counter %d; want 5`, poison)
	}
}

func TestHitStun(t *testing.T) {
	testMonSpec := &Spec{
		Family:  FamActor,
		Genus:   GenMonster,
		Species: SpecOrc,
		Name:    "ORC",
		Traits: &Traits{
			Fighter: NewActorFighter,
			Sheet: NewMonsterSheet(&MonsterSheet{
				critdivmod: 0,
				maxhp:      20,
				damroll:    NewDice(1, 5),
				atkeffects: NewEffects(map[Effect]int{EffectStun: 1}),
			}),
			Ticker: NewActorTicker,
		},
	}

	g := newTestGame()
	attacker, defender := g.NewObj(testMonSpec), g.NewObj(testMonSpec)
	// Roll 5 damage, and make sure to win stun skillroll (10 vs 0 on d10s.)
	FixRandomDie([]int{7, 1, 5, 10, 0})
	defer RestoreRandom()

	attacker.Fighter.Hit(defender.Fighter)

	if stun := defender.Ticker.Counter(EffectStun); stun != 5 {
		t.Errorf(`Stun counter %d; want 5`, stun)
	}
}

type applyBrandTest struct {
	atk  Effects
	def  Effects
	verb string
	dice int
}

func TestApplyBrand(t *testing.T) {
	const (
		fakeBrand1 Effect = NumEffects + iota
		fakeBrand2
		fakeResist1
	)

	oldEffectsSpecs := EffectsSpecs

	EffectsSpecs = EffectsSpec{
		fakeBrand1:  {Type: EffectTypeBrand, ResistedBy: fakeResist1, Verb: "frobs"},
		fakeBrand2:  {Type: EffectTypeBrand, Verb: "norfs"},
		fakeResist1: {Type: EffectTypeResist},
	}

	restoreEffectsDeps := func() {
		EffectsSpecs = oldEffectsSpecs
	}
	defer restoreEffectsDeps()

	tests := []applyBrandTest{
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  Effects{},
			verb: "frobs",
			dice: 1,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 2}),
			def:  Effects{},
			verb: "frobs",
			dice: 1,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: 1}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: 2}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: -1}),
			verb: "*frobs*",
			dice: 2,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: -2}),
			verb: "*frobs*",
			dice: 2,
		},
		{
			atk:  Effects{},
			def:  NewEffects(map[Effect]int{fakeResist1: -1}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  Effects{},
			def:  NewEffects(map[Effect]int{fakeResist1: 1}),
			verb: "hits",
			dice: 0,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1, fakeBrand2: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: -1}),
			verb: "*frobs*",
			dice: 3,
		},
		{
			atk:  NewEffects(map[Effect]int{fakeBrand1: 1, fakeBrand2: 1}),
			def:  NewEffects(map[Effect]int{fakeResist1: 1}),
			verb: "norfs",
			dice: 1,
		},
	}

	// No resist.
	for i, test := range tests {
		if dice, verb := applybrands(test.atk, test.def); dice != test.dice || verb != test.verb {
			t.Errorf(`Test %d: got (%d,"%s") want (%d,"%s")`, i, dice, verb, test.dice, test.verb)
		}
	}
}
<|endoftext|>


<commit_before>package deployment

import (
	"fmt"
	"sort"

	ct "github.com/flynn/flynn/controller/types"
	"gopkg.in/inconshreveable/log15.v2"
)

type WaitJobsFn func(releaseID string, expected ct.JobEvents, log log15.Logger) error

func (d *DeployJob) deployOneByOne() error {
	return d.deployOneByOneWithWaitFn(d.waitForJobEvents)
}

func (d *DeployJob) deployOneByOneWithWaitFn(waitJobs WaitJobsFn) error {
	log := d.logger.New("fn", "deployOneByOne")
	log.Info("starting one-by-one deployment")

	oldScale := make(map[string]int, len(d.oldReleaseState))
	for typ, count := range d.oldReleaseState {
		oldScale[typ] = count
		if d.isOmni(typ) {
			oldScale[typ] /= d.hostCount
		}
	}

	newScale := make(map[string]int, len(d.newReleaseState))
	for typ, count := range d.newReleaseState {
		newScale[typ] = count
		if d.isOmni(typ) {
			newScale[typ] /= d.hostCount
		}
	}

	processTypes := make([]string, 0, len(d.Processes))
	for typ := range d.Processes {
		processTypes = append(processTypes, typ)
	}
	sort.Sort(sort.StringSlice(processTypes))

	olog := log.New("release_id", d.OldReleaseID)
	nlog := log.New("release_id", d.NewReleaseID)
	for _, typ := range processTypes {
		num := d.Processes[typ]
		// don't scale processes which no longer exist in the new release
		if _, ok := d.newRelease.Processes[typ]; !ok {
			num = 0
		}
		diff := 1
		if d.isOmni(typ) {
			diff = d.hostCount
		}

		for i := newScale[typ]; i < num; i++ {
			nlog.Info("scaling new formation up by one", "type", typ)
			newScale[typ]++
			if err := d.client.PutFormation(&ct.Formation{
				AppID:     d.AppID,
				ReleaseID: d.NewReleaseID,
				Processes: newScale,
			}); err != nil {
				nlog.Error("error scaling new formation up by one", "type", typ, "err", err)
				return err
			}
			for i := 0; i < diff; i++ {
				d.deployEvents <- ct.DeploymentEvent{
					ReleaseID: d.NewReleaseID,
					JobState:  ct.JobStateStarting,
					JobType:   typ,
				}
			}
			nlog.Info(fmt.Sprintf("waiting for %d job up event(s)", diff), "type", typ)
			if err := waitJobs(d.NewReleaseID, ct.JobEvents{typ: ct.JobUpEvents(diff)}, nlog); err != nil {
				nlog.Error("error waiting for job up events", "err", err)
				return err
			}

			olog.Info("scaling old formation down by one", "type", typ)
			oldScale[typ]--
			if err := d.client.PutFormation(&ct.Formation{
				AppID:     d.AppID,
				ReleaseID: d.OldReleaseID,
				Processes: oldScale,
			}); err != nil {
				olog.Error("error scaling old formation down by one", "type", typ, "err", err)
				return err
			}
			for i := 0; i < diff; i++ {
				d.deployEvents <- ct.DeploymentEvent{
					ReleaseID: d.OldReleaseID,
					JobState:  ct.JobStateStopping,
					JobType:   typ,
				}
			}

			olog.Info(fmt.Sprintf("waiting for %d job down event(s)", diff), "type", typ)
			if err := waitJobs(d.OldReleaseID, ct.JobEvents{typ: ct.JobDownEvents(diff)}, olog); err != nil {
				olog.Error("error waiting for job down events", "err", err)
				return err
			}
		}
	}

	// ensure any old leftover jobs are stopped (this can happen when new
	// workers continue deployments from old workers and still see the
	// old worker running even though it has been scaled down), returning
	// ErrSkipRollback if an error occurs (rolling back doesn't make a ton
	// of sense because it involves stopping the new working jobs).
	log.Info("ensuring old formation is scaled down to zero")
	diff := make(ct.JobEvents, len(oldScale))
	for typ, count := range oldScale {
		diff[typ] = ct.JobDownEvents(count)
	}
	if err := d.client.PutFormation(&ct.Formation{
		AppID:     d.AppID,
		ReleaseID: d.OldReleaseID,
	}); err != nil {
		log.Error("error scaling old formation down to zero", "err", err)
		return ErrSkipRollback{err.Error()}
	}
	if diff.Count() > 0 {
		log.Info(fmt.Sprintf("waiting for %d job down event(s)", diff.Count()))
		if err := d.waitForJobEvents(d.OldReleaseID, diff, log); err != nil {
			log.Error("error waiting for job down events", "err", err)
			return ErrSkipRollback{err.Error()}
		}
	}

	log.Info("finished one-by-one deployment")
	return nil
}
<commit_msg>worker: Fix final scale down in one-by-one deploy<commit_after>package deployment

import (
	"fmt"
	"sort"

	ct "github.com/flynn/flynn/controller/types"
	"gopkg.in/inconshreveable/log15.v2"
)

type WaitJobsFn func(releaseID string, expected ct.JobEvents, log log15.Logger) error

func (d *DeployJob) deployOneByOne() error {
	return d.deployOneByOneWithWaitFn(d.waitForJobEvents)
}

func (d *DeployJob) deployOneByOneWithWaitFn(waitJobs WaitJobsFn) error {
	log := d.logger.New("fn", "deployOneByOne")
	log.Info("starting one-by-one deployment")

	oldScale := make(map[string]int, len(d.oldReleaseState))
	for typ, count := range d.oldReleaseState {
		oldScale[typ] = count
		if d.isOmni(typ) {
			oldScale[typ] /= d.hostCount
		}
	}

	newScale := make(map[string]int, len(d.newReleaseState))
	for typ, count := range d.newReleaseState {
		newScale[typ] = count
		if d.isOmni(typ) {
			newScale[typ] /= d.hostCount
		}
	}

	processTypes := make([]string, 0, len(d.Processes))
	for typ := range d.Processes {
		processTypes = append(processTypes, typ)
	}
	sort.Sort(sort.StringSlice(processTypes))

	olog := log.New("release_id", d.OldReleaseID)
	nlog := log.New("release_id", d.NewReleaseID)
	for _, typ := range processTypes {
		num := d.Processes[typ]
		// don't scale processes which no longer exist in the new release
		if _, ok := d.newRelease.Processes[typ]; !ok {
			num = 0
		}
		diff := 1
		if d.isOmni(typ) {
			diff = d.hostCount
		}

		for i := newScale[typ]; i < num; i++ {
			nlog.Info("scaling new formation up by one", "type", typ)
			newScale[typ]++
			if err := d.client.PutFormation(&ct.Formation{
				AppID:     d.AppID,
				ReleaseID: d.NewReleaseID,
				Processes: newScale,
			}); err != nil {
				nlog.Error("error scaling new formation up by one", "type", typ, "err", err)
				return err
			}
			for i := 0; i < diff; i++ {
				d.deployEvents <- ct.DeploymentEvent{
					ReleaseID: d.NewReleaseID,
					JobState:  ct.JobStateStarting,
					JobType:   typ,
				}
			}
			nlog.Info(fmt.Sprintf("waiting for %d job up event(s)", diff), "type", typ)
			if err := waitJobs(d.NewReleaseID, ct.JobEvents{typ: ct.JobUpEvents(diff)}, nlog); err != nil {
				nlog.Error("error waiting for job up events", "err", err)
				return err
			}

			olog.Info("scaling old formation down by one", "type", typ)
			oldScale[typ]--
			if err := d.client.PutFormation(&ct.Formation{
				AppID:     d.AppID,
				ReleaseID: d.OldReleaseID,
				Processes: oldScale,
			}); err != nil {
				olog.Error("error scaling old formation down by one", "type", typ, "err", err)
				return err
			}
			for i := 0; i < diff; i++ {
				d.deployEvents <- ct.DeploymentEvent{
					ReleaseID: d.OldReleaseID,
					JobState:  ct.JobStateStopping,
					JobType:   typ,
				}
			}

			olog.Info(fmt.Sprintf("waiting for %d job down event(s)", diff), "type", typ)
			if err := waitJobs(d.OldReleaseID, ct.JobEvents{typ: ct.JobDownEvents(diff)}, olog); err != nil {
				olog.Error("error waiting for job down events", "err", err)
				return err
			}
		}
	}

	// ensure any old leftover jobs are stopped (this can happen when new
	// workers continue deployments from old workers and still see the
	// old worker running even though it has been scaled down), returning
	// ErrSkipRollback if an error occurs (rolling back doesn't make a ton
	// of sense because it involves stopping the new working jobs).
	log.Info("ensuring old formation is scaled down to zero")
	diff := make(ct.JobEvents, len(oldScale))
	for typ, count := range oldScale {
		if count > 0 {
			diff[typ] = ct.JobDownEvents(count)
		}
	}
	if err := d.client.PutFormation(&ct.Formation{
		AppID:     d.AppID,
		ReleaseID: d.OldReleaseID,
	}); err != nil {
		log.Error("error scaling old formation down to zero", "err", err)
		return ErrSkipRollback{err.Error()}
	}
	if diff.Count() > 0 {
		log.Info(fmt.Sprintf("waiting for %d job down event(s)", diff.Count()), "diff", diff)
		if err := d.waitForJobEvents(d.OldReleaseID, diff, log); err != nil {
			log.Error("error waiting for job down events", "diff", diff, "err", err)
			return ErrSkipRollback{err.Error()}
		}
	}

	log.Info("finished one-by-one deployment")
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2014 The Serviced 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 zookeeper

import (
	"encoding/json"
	"path"
	"sync"

	zklib "github.com/control-center/go-zookeeper/zk"
	"github.com/control-center/serviced/coordinator/client"
)

type dir struct {
	version interface{}
}

func (n *dir) SetVersion(version interface{}) { n.version = version }
func (n *dir) Version() interface{}           { return n.version }

// Connection is a Zookeeper based implementation of client.Connection.
type Connection struct {
	sync.RWMutex
	conn     *zklib.Conn
	basePath string
	onClose  func(int)
	id       int
}

// Assert that Connection implements client.Connection.
var _ client.Connection = &Connection{}

// IsClosed returns connection closed error if true, otherwise returns nil.
func (c *Connection) isClosed() error {
	if c.conn == nil {
		return client.ErrConnectionClosed
	}
	return nil
}

// Close closes the client connection to zookeeper. Calling close twice will
// result in a no-op.
func (c *Connection) Close() {
	c.Lock()
	defer c.Unlock()
	if c.conn != nil {
		c.conn.Close()
		c.conn = nil
		if c.onClose != nil {
			c.onClose(c.id)
			c.onClose = nil
		}
	}
}

// SetID sets the connection ID
func (c *Connection) SetID(i int) {
	c.Lock()
	defer c.Unlock()
	c.id = i
}

// ID gets the connection ID
func (c *Connection) ID() int {
	c.RLock()
	defer c.RUnlock()
	return c.id
}

// SetOnClose performs cleanup when a connection is closed
func (c *Connection) SetOnClose(onClose func(int)) {
	c.Lock()
	defer c.Unlock()
	if err := c.isClosed(); err == nil {
		c.onClose = onClose
	}
}

// NewTransaction creates a new transaction object
func (c *Connection) NewTransaction() client.Transaction {
	return &Transaction{
		conn: c,
		ops:  []multiReq{},
	}
}

// NewLock creates a new lock object
func (c *Connection) NewLock(p string) (client.Lock, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return nil, err
	}
	lock := &Lock{
		lock: zklib.NewLock(c.conn, path.Join(c.basePath, p), zklib.WorldACL(zklib.PermAll)),
	}
	return lock, nil
}

// NewLeader returns a managed leader object at the given path bound to the
// current connection.
func (c *Connection) NewLeader(p string) (client.Leader, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return nil, err
	}
	return NewLeader(c.conn, path.Join(path.Join(c.basePath, p))), nil
}

// Create adds a node at the specified path
func (c *Connection) Create(path string, node client.Node) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	if err := c.ensurePath(path); err != nil {
		return err
	}
	return c.create(path, node)
}

func (c *Connection) create(p string, node client.Node) error {
	bytes, err := json.Marshal(node)
	if err != nil {
		return client.ErrSerialization
	}
	pth := path.Join(c.basePath, p)
	if _, err := c.conn.Create(pth, bytes, 0, zklib.WorldACL(zklib.PermAll)); err != nil {
		return xlateError(err)
	}
	node.SetVersion(&zklib.Stat{})
	return nil
}

// CreateDir adds a dir at the specified path
func (c *Connection) CreateDir(path string) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	if err := c.ensurePath(path); err != nil {
		return err
	}
	return c.createDir(path)
}

func (c *Connection) createDir(p string) error {
	return c.create(p, &dir{})
}

func (c *Connection) ensurePath(p string) error {
	dp := path.Dir(p)
	if ok, err := c.exists(dp); err != nil {
		return err
	} else if !ok {
		if p == "" || p == "/" {
			return nil
		} else if err := c.ensurePath(dp); err != nil {
			return err
		} else if err := c.createDir(dp); err != client.ErrNodeExists {
			return err
		}
	}
	return nil
}

// Exists returns true if the path exists
func (c *Connection) Exists(path string) (bool, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return false, err
	}
	return c.exists(path)
}

func (c *Connection) exists(p string) (bool, error) {
	exists, _, err := c.conn.Exists(path.Join(c.basePath, p))
	if err == zklib.ErrNoNode {
		return false, nil
	}
	return exists, xlateError(err)
}

// CreateEphemeral creates a node whose existance depends on the persistence of
// the connection.
func (c *Connection) CreateEphemeral(path string, node client.Node) (string, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return "", err
	}
	return c.createEphemeral(path, node)
}

func (c *Connection) createEphemeral(p string, node client.Node) (string, error) {
	bytes, err := json.Marshal(node)
	if err != nil {
		return "", client.ErrSerialization
	}
	if err := c.ensurePath(p); err != nil {
		return "", err
	}
	p = path.Join(c.basePath, p)
	pth, err := c.conn.CreateProtectedEphemeralSequential(p, bytes, zklib.WorldACL(zklib.PermAll))
	return pth, xlateError(err)
}

// Set assigns a value to an existing node at a given path
func (c *Connection) Set(path string, node client.Node) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	return c.set(path, node)
}

func (c *Connection) set(p string, node client.Node) error {
	bytes, err := json.Marshal(node)
	if err != nil {
		return client.ErrSerialization
	}
	stat := &zklib.Stat{}
	if version := node.Version(); version != nil {
		var ok bool
		if stat, ok = version.(*zklib.Stat); !ok {
			return client.ErrInvalidVersionObj
		}
	}
	pth := path.Join(c.basePath, p)
	if _, err := c.conn.Set(pth, bytes, stat.Version); err != nil {
		return xlateError(err)
	}
	return nil
}

// Delete recursively removes a path and its children
func (c *Connection) Delete(path string) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	return c.delete(path)
}

func (c *Connection) delete(p string) error {
	children, err := c.children(p)
	if err != nil {
		return err
	}
	for _, child := range children {
		if err := c.delete(path.Join(p, child)); err != nil {
			return err
		}
	}
	pth := path.Join(c.basePath, p)
	_, stat, err := c.conn.Get(pth)
	if err != nil {
		return xlateError(err)
	}
	return xlateError(c.conn.Delete(pth, stat.Version))
}

// Get returns the node at the given path.
func (c *Connection) Get(path string, node client.Node) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	return c.get(path, node)
}

func (c *Connection) get(p string, node client.Node) error {
	p = path.Join(c.basePath, p)
	bytes, stat, err := c.conn.Get(p)
	if err != nil {
		return xlateError(err)
	}
	if len(bytes) == 0 {
		return client.ErrEmptyNode
	}
	if err := json.Unmarshal(bytes, node); err != nil {
		return client.ErrSerialization
	}
	node.SetVersion(stat)
	return nil
}

// GetW returns the node at the given path as well as a channel to watch for
// events on that node.
func (c *Connection) GetW(path string, node client.Node, cancel <-chan struct{}) (<-chan client.Event, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return nil, err
	}
	return c.getW(path, node, cancel)
}

func (c *Connection) getW(p string, node client.Node, cancel <-chan struct{}) (<-chan client.Event, error) {
	p = path.Join(c.basePath, p)
	bytes, stat, ch, err := c.conn.GetW(p)
	if err != nil {
		return nil, xlateError(err)
	}
	if len(bytes) == 0 {
		return nil, client.ErrEmptyNode
	} else if err := json.Unmarshal(bytes, node); err != nil {
		return nil, client.ErrSerialization
	}
	node.SetVersion(stat)
	return c.toClientEvent(ch, cancel), nil
}

func (c *Connection) toClientEvent(ch <-chan zklib.Event, cancel <-chan struct{}) <-chan client.Event {
	evCh := make(chan client.Event, 1)
	go func() {
		select {
		case zkev := <-ch:
			ev := client.Event{Type: client.EventType(zkev.Type)}
			select {
			case evCh <- ev:
			case <-cancel:
			}
		case <-cancel:
			c.cancelEvent(ch)
		}
	}()
	return evCh
}

func (c *Connection) cancelEvent(ch <-chan zklib.Event) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return
	}
	c.conn.CancelEvent(ch)
}

// Children returns the children of the node at the given path.
func (c *Connection) Children(path string) ([]string, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return []string{}, err
	}
	return c.children(path)
}

func (c *Connection) children(p string) ([]string, error) {
	pth := path.Join(c.basePath, p)
	children, _, err := c.conn.Children(pth)
	if err != nil {
		return []string{}, xlateError(err)
	}
	return children, nil
}

// ChildrenW returns the children of the node at the given path as well as a
// channel to watch for events on that node.
func (c *Connection) ChildrenW(path string, cancel <-chan struct{}) ([]string, <-chan client.Event, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return []string{}, nil, err
	}
	return c.childrenW(path, cancel)
}

func (c *Connection) childrenW(p string, cancel <-chan struct{}) ([]string, <-chan client.Event, error) {
	p = path.Join(c.basePath, p)
	children, _, ch, err := c.conn.ChildrenW(p)
	if err != nil {
		return []string{}, nil, xlateError(err)
	}
	return children, c.toClientEvent(ch, cancel), nil
}
<commit_msg>make ephemeral node creation more consistent<commit_after>// Copyright 2014 The Serviced 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 zookeeper

import (
	"encoding/json"
	"path"
	"sync"

	zklib "github.com/control-center/go-zookeeper/zk"
	"github.com/control-center/serviced/coordinator/client"
)

type dir struct {
	version interface{}
}

func (n *dir) SetVersion(version interface{}) { n.version = version }
func (n *dir) Version() interface{}           { return n.version }

// Connection is a Zookeeper based implementation of client.Connection.
type Connection struct {
	sync.RWMutex
	conn     *zklib.Conn
	basePath string
	onClose  func(int)
	id       int
}

// Assert that Connection implements client.Connection.
var _ client.Connection = &Connection{}

// IsClosed returns connection closed error if true, otherwise returns nil.
func (c *Connection) isClosed() error {
	if c.conn == nil {
		return client.ErrConnectionClosed
	}
	return nil
}

// Close closes the client connection to zookeeper. Calling close twice will
// result in a no-op.
func (c *Connection) Close() {
	c.Lock()
	defer c.Unlock()
	if c.conn != nil {
		c.conn.Close()
		c.conn = nil
		if c.onClose != nil {
			c.onClose(c.id)
			c.onClose = nil
		}
	}
}

// SetID sets the connection ID
func (c *Connection) SetID(i int) {
	c.Lock()
	defer c.Unlock()
	c.id = i
}

// ID gets the connection ID
func (c *Connection) ID() int {
	c.RLock()
	defer c.RUnlock()
	return c.id
}

// SetOnClose performs cleanup when a connection is closed
func (c *Connection) SetOnClose(onClose func(int)) {
	c.Lock()
	defer c.Unlock()
	if err := c.isClosed(); err == nil {
		c.onClose = onClose
	}
}

// NewTransaction creates a new transaction object
func (c *Connection) NewTransaction() client.Transaction {
	return &Transaction{
		conn: c,
		ops:  []multiReq{},
	}
}

// NewLock creates a new lock object
func (c *Connection) NewLock(p string) (client.Lock, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return nil, err
	}
	lock := &Lock{
		lock: zklib.NewLock(c.conn, path.Join(c.basePath, p), zklib.WorldACL(zklib.PermAll)),
	}
	return lock, nil
}

// NewLeader returns a managed leader object at the given path bound to the
// current connection.
func (c *Connection) NewLeader(p string) (client.Leader, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return nil, err
	}
	return NewLeader(c.conn, path.Join(path.Join(c.basePath, p))), nil
}

// Create adds a node at the specified path
func (c *Connection) Create(path string, node client.Node) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	if err := c.ensurePath(path); err != nil {
		return err
	}
	return c.create(path, node)
}

func (c *Connection) create(p string, node client.Node) error {
	bytes, err := json.Marshal(node)
	if err != nil {
		return client.ErrSerialization
	}
	pth := path.Join(c.basePath, p)
	if _, err := c.conn.Create(pth, bytes, 0, zklib.WorldACL(zklib.PermAll)); err != nil {
		return xlateError(err)
	}
	node.SetVersion(&zklib.Stat{})
	return nil
}

// CreateDir adds a dir at the specified path
func (c *Connection) CreateDir(path string) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	if err := c.ensurePath(path); err != nil {
		return err
	}
	return c.createDir(path)
}

func (c *Connection) createDir(p string) error {
	return c.create(p, &dir{})
}

func (c *Connection) ensurePath(p string) error {
	dp := path.Dir(p)
	if ok, err := c.exists(dp); err != nil {
		return err
	} else if !ok {
		if p == "" || p == "/" {
			return nil
		} else if err := c.ensurePath(dp); err != nil {
			return err
		} else if err := c.createDir(dp); err != client.ErrNodeExists {
			return err
		}
	}
	return nil
}

// Exists returns true if the path exists
func (c *Connection) Exists(path string) (bool, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return false, err
	}
	return c.exists(path)
}

func (c *Connection) exists(p string) (bool, error) {
	exists, _, err := c.conn.Exists(path.Join(c.basePath, p))
	if err == zklib.ErrNoNode {
		return false, nil
	}
	return exists, xlateError(err)
}

// CreateEphemeral creates a node whose existance depends on the persistence of
// the connection.
func (c *Connection) CreateEphemeral(path string, node client.Node) (string, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return "", err
	}
	if err := c.ensurePath(path); err != nil {
		return "", err
	}
	return c.createEphemeral(path, node)
}

func (c *Connection) createEphemeral(p string, node client.Node) (string, error) {
	bytes, err := json.Marshal(node)
	if err != nil {
		return "", client.ErrSerialization
	}
	pth := path.Join(c.basePath, p)
	epth, err := c.conn.CreateProtectedEphemeralSequential(pth, bytes, zklib.WorldACL(zklib.PermAll))
	return epth, xlateError(err)
}

// Set assigns a value to an existing node at a given path
func (c *Connection) Set(path string, node client.Node) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	return c.set(path, node)
}

func (c *Connection) set(p string, node client.Node) error {
	bytes, err := json.Marshal(node)
	if err != nil {
		return client.ErrSerialization
	}
	stat := &zklib.Stat{}
	if version := node.Version(); version != nil {
		var ok bool
		if stat, ok = version.(*zklib.Stat); !ok {
			return client.ErrInvalidVersionObj
		}
	}
	pth := path.Join(c.basePath, p)
	if _, err := c.conn.Set(pth, bytes, stat.Version); err != nil {
		return xlateError(err)
	}
	return nil
}

// Delete recursively removes a path and its children
func (c *Connection) Delete(path string) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	return c.delete(path)
}

func (c *Connection) delete(p string) error {
	children, err := c.children(p)
	if err != nil {
		return err
	}
	for _, child := range children {
		if err := c.delete(path.Join(p, child)); err != nil {
			return err
		}
	}
	pth := path.Join(c.basePath, p)
	_, stat, err := c.conn.Get(pth)
	if err != nil {
		return xlateError(err)
	}
	return xlateError(c.conn.Delete(pth, stat.Version))
}

// Get returns the node at the given path.
func (c *Connection) Get(path string, node client.Node) error {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return err
	}
	return c.get(path, node)
}

func (c *Connection) get(p string, node client.Node) error {
	p = path.Join(c.basePath, p)
	bytes, stat, err := c.conn.Get(p)
	if err != nil {
		return xlateError(err)
	}
	if len(bytes) == 0 {
		return client.ErrEmptyNode
	}
	if err := json.Unmarshal(bytes, node); err != nil {
		return client.ErrSerialization
	}
	node.SetVersion(stat)
	return nil
}

// GetW returns the node at the given path as well as a channel to watch for
// events on that node.
func (c *Connection) GetW(path string, node client.Node, cancel <-chan struct{}) (<-chan client.Event, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return nil, err
	}
	return c.getW(path, node, cancel)
}

func (c *Connection) getW(p string, node client.Node, cancel <-chan struct{}) (<-chan client.Event, error) {
	p = path.Join(c.basePath, p)
	bytes, stat, ch, err := c.conn.GetW(p)
	if err != nil {
		return nil, xlateError(err)
	}
	if len(bytes) == 0 {
		return nil, client.ErrEmptyNode
	} else if err := json.Unmarshal(bytes, node); err != nil {
		return nil, client.ErrSerialization
	}
	node.SetVersion(stat)
	return c.toClientEvent(ch, cancel), nil
}

func (c *Connection) toClientEvent(ch <-chan zklib.Event, cancel <-chan struct{}) <-chan client.Event {
	evCh := make(chan client.Event, 1)
	go func() {
		select {
		case zkev := <-ch:
			ev := client.Event{Type: client.EventType(zkev.Type)}
			select {
			case evCh <- ev:
			case <-cancel:
			}
		case <-cancel:
			c.cancelEvent(ch)
		}
	}()
	return evCh
}

func (c *Connection) cancelEvent(ch <-chan zklib.Event) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return
	}
	c.conn.CancelEvent(ch)
}

// Children returns the children of the node at the given path.
func (c *Connection) Children(path string) ([]string, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return []string{}, err
	}
	return c.children(path)
}

func (c *Connection) children(p string) ([]string, error) {
	pth := path.Join(c.basePath, p)
	children, _, err := c.conn.Children(pth)
	if err != nil {
		return []string{}, xlateError(err)
	}
	return children, nil
}

// ChildrenW returns the children of the node at the given path as well as a
// channel to watch for events on that node.
func (c *Connection) ChildrenW(path string, cancel <-chan struct{}) ([]string, <-chan client.Event, error) {
	c.RLock()
	defer c.RUnlock()
	if err := c.isClosed(); err != nil {
		return []string{}, nil, err
	}
	return c.childrenW(path, cancel)
}

func (c *Connection) childrenW(p string, cancel <-chan struct{}) ([]string, <-chan client.Event, error) {
	p = path.Join(c.basePath, p)
	children, _, ch, err := c.conn.ChildrenW(p)
	if err != nil {
		return []string{}, nil, xlateError(err)
	}
	return children, c.toClientEvent(ch, cancel), nil
}
<|endoftext|>


<commit_before>package trimetrics

import (
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/Symantec/tricorder/go/tricorder/units"
	"time"
)

func newCounter(path, desc string) (*Counter, error) {
	result := &Counter{}

	if err := tricorder.RegisterMetric(
		path, result.get, units.None, desc); err != nil {
		return nil, err
	}
	return result, nil
}

func (c *Counter) inc(x uint64) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.value += x
}

func (c *Counter) get() uint64 {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.value
}

type writerSingleMetricsType struct {
	Total     uint64
	Success   uint64
	Failure   uint64
	LastError string
}

func createDistributions(
	parentDir *tricorder.DirectorySpec, result *WriterMetrics) error {
	result.timeToWriteDist = tricorder.NewGeometricBucketer(0.001, 1000000).NewCumulativeDistribution()
	result.batchSizeDist = tricorder.NewGeometricBucketer(1, 1000000).NewCumulativeDistribution()
	if err := parentDir.RegisterMetric(
		"timeToWrite",
		result.timeToWriteDist,
		units.Millisecond,
		"Time per write"); err != nil {
		return err
	}
	if err := parentDir.RegisterMetric(
		"batchSize",
		result.batchSizeDist,
		units.None,
		"Batch size"); err != nil {
		return err
	}
	return nil
}

func newWriterMetrics(parentPath string) (*WriterMetrics, error) {
	parentDir, err := tricorder.RegisterDirectory(parentPath)
	if err != nil {
		return nil, err
	}

	result := &WriterMetrics{}
	if err := createDistributions(parentDir, result); err != nil {
		return nil, err
	}

	var singles writerSingleMetricsType
	grp := tricorder.NewGroup()
	grp.RegisterUpdateFunc(func() time.Time {
		result.get(&singles)
		return time.Now()
	})

	if err := parentDir.RegisterMetricInGroup(
		"total", &singles.Total, grp, units.None, "Total write calls"); err != nil {
		return nil, err
	}
	if err := parentDir.RegisterMetricInGroup(
		"success", &singles.Success, grp, units.None, "Successful write calls"); err != nil {
		return nil, err
	}
	if err := parentDir.RegisterMetricInGroup(
		"failure", &singles.Failure, grp, units.None, "Failed write calls"); err != nil {
		return nil, err
	}
	if err := parentDir.RegisterMetricInGroup(
		"lastError", &singles.LastError, grp, units.None, "Last write error"); err != nil {
		return nil, err
	}
	return result, nil
}

func (w *WriterMetrics) get(dest *writerSingleMetricsType) {
	w.mu.Lock()
	defer w.mu.Unlock()
	*dest = w.singles
}

func (w *WriterMetrics) logError(
	elapsed time.Duration, numToWrite uint64, err error) {
	w.timeToWriteDist.Add(elapsed)
	w.batchSizeDist.Add(float64(numToWrite))
	w.mu.Lock()
	defer w.mu.Unlock()
	w.singles.Total++
	w.singles.Failure++
	w.singles.LastError = err.Error()
}

func (w *WriterMetrics) logSuccess(elapsed time.Duration, numWritten uint64) {
	w.timeToWriteDist.Add(elapsed)
	w.batchSizeDist.Add(float64(numWritten))
	w.mu.Lock()
	defer w.mu.Unlock()
	w.singles.Total++
	w.singles.Success++
}
<commit_msg>Use tricorder.DirectoryGroup within lib/trimetrics package.<commit_after>package trimetrics

import (
	"github.com/Symantec/tricorder/go/tricorder"
	"github.com/Symantec/tricorder/go/tricorder/units"
	"time"
)

func newCounter(path, desc string) (*Counter, error) {
	result := &Counter{}

	if err := tricorder.RegisterMetric(
		path, result.get, units.None, desc); err != nil {
		return nil, err
	}
	return result, nil
}

func (c *Counter) inc(x uint64) {
	c.mu.Lock()
	defer c.mu.Unlock()
	c.value += x
}

func (c *Counter) get() uint64 {
	c.mu.Lock()
	defer c.mu.Unlock()
	return c.value
}

type writerSingleMetricsType struct {
	Total     uint64
	Success   uint64
	Failure   uint64
	LastError string
}

func createDistributions(
	parentDir *tricorder.DirectorySpec, result *WriterMetrics) error {
	result.timeToWriteDist = tricorder.NewGeometricBucketer(0.001, 1000000).NewCumulativeDistribution()
	result.batchSizeDist = tricorder.NewGeometricBucketer(1, 1000000).NewCumulativeDistribution()
	if err := parentDir.RegisterMetric(
		"timeToWrite",
		result.timeToWriteDist,
		units.Millisecond,
		"Time per write"); err != nil {
		return err
	}
	if err := parentDir.RegisterMetric(
		"batchSize",
		result.batchSizeDist,
		units.None,
		"Batch size"); err != nil {
		return err
	}
	return nil
}

func newWriterMetrics(parentPath string) (*WriterMetrics, error) {
	parentDir, err := tricorder.RegisterDirectory(parentPath)
	if err != nil {
		return nil, err
	}

	result := &WriterMetrics{}
	if err := createDistributions(parentDir, result); err != nil {
		return nil, err
	}

	var singles writerSingleMetricsType
	grp := tricorder.NewGroup()
	grp.RegisterUpdateFunc(func() time.Time {
		result.get(&singles)
		return time.Now()
	})
	dg := tricorder.DirectoryGroup{Group: grp, Directory: parentDir}

	if err := dg.RegisterMetric(
		"total", &singles.Total, units.None, "Total write calls"); err != nil {
		return nil, err
	}
	if err := dg.RegisterMetric(
		"success", &singles.Success, units.None, "Successful write calls"); err != nil {
		return nil, err
	}
	if err := dg.RegisterMetric(
		"failure", &singles.Failure, units.None, "Failed write calls"); err != nil {
		return nil, err
	}
	if err := dg.RegisterMetric(
		"lastError", &singles.LastError, units.None, "Last write error"); err != nil {
		return nil, err
	}
	return result, nil
}

func (w *WriterMetrics) get(dest *writerSingleMetricsType) {
	w.mu.Lock()
	defer w.mu.Unlock()
	*dest = w.singles
}

func (w *WriterMetrics) logError(
	elapsed time.Duration, numToWrite uint64, err error) {
	w.timeToWriteDist.Add(elapsed)
	w.batchSizeDist.Add(float64(numToWrite))
	w.mu.Lock()
	defer w.mu.Unlock()
	w.singles.Total++
	w.singles.Failure++
	w.singles.LastError = err.Error()
}

func (w *WriterMetrics) logSuccess(elapsed time.Duration, numWritten uint64) {
	w.timeToWriteDist.Add(elapsed)
	w.batchSizeDist.Add(float64(numWritten))
	w.mu.Lock()
	defer w.mu.Unlock()
	w.singles.Total++
	w.singles.Success++
}
<|endoftext|>


<commit_before>package project

import (
	"bufio"
	"bytes"
	"fmt"
	"path"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/rancherio/rancher-compose/librcompose/util"
	"gopkg.in/yaml.v2"
)

var (
	ValidRemotes = []string{
		"git://",
		"git@github.com:",
		"github.com",
		"http:",
		"https:",
	}
	ignore = map[string]bool{
		"links":        true,
		"volumes_from": true,
	}
)

type rawService map[string]interface{}
type rawServiceMap map[string]rawService

func Merge(p *Project, bytes []byte) (map[string]*ServiceConfig, error) {
	configs := make(map[string]*ServiceConfig)

	datas := make(rawServiceMap)
	err := yaml.Unmarshal(bytes, &datas)
	if err != nil {
		logrus.Fatalf("Could not parse config for project %s : %v", p.Name, err)
	}

	for name, data := range datas {
		data, err := parse(p.ConfigLookup, p.File, data, datas)
		if err != nil {
			logrus.Errorf("Failed to parse service %s: %v", name, err)
			return nil, err
		}

		datas[name] = data
	}

	err = util.Convert(datas, &configs)
	return configs, err
}

func readEnvFile(configLookup ConfigLookup, inFile string, serviceData rawService) (rawService, error) {
	var config ServiceConfig

	if err := util.Convert(serviceData, &config); err != nil {
		return nil, err
	}

	if len(config.EnvFile.Slice()) == 0 {
		return serviceData, nil
	}

	if configLookup == nil {
		return nil, fmt.Errorf("Can not use env_file in file %s no mechanism provided to load files", inFile)
	}

	vars := config.Environment.Slice()

	for i := len(config.EnvFile.Slice()) - 1; i >= 0; i-- {
		envFile := config.EnvFile.Slice()[i]
		content, _, err := configLookup.Lookup(envFile, inFile)
		if err != nil {
			return nil, err
		}

		if err != nil {
			return nil, err
		}

		scanner := bufio.NewScanner(bytes.NewBuffer(content))
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			key := strings.SplitAfter(line, "=")[0]

			found := false
			for _, v := range vars {
				if strings.HasPrefix(v, key) {
					found = true
					break
				}
			}

			if !found {
				vars = append(vars, line)
			}
		}

		if scanner.Err() != nil {
			return nil, scanner.Err()
		}
	}

	serviceData["environment"] = vars

	delete(serviceData, "env_file")

	return serviceData, nil
}

func resolveBuild(inFile string, serviceData rawService) (rawService, error) {

	build := asString(serviceData["build"])
	if build == "" {
		return serviceData, nil
	}

	for _, remote := range ValidRemotes {
		if strings.HasPrefix(build, remote) {
			return serviceData, nil
		}
	}

	current := path.Dir(inFile)

	if build == "." {
		build = current
	} else {
		current = path.Join(current, build)
	}

	serviceData["build"] = current

	return serviceData, nil
}

func parse(configLookup ConfigLookup, inFile string, serviceData rawService, datas rawServiceMap) (rawService, error) {
	serviceData, err := readEnvFile(configLookup, inFile, serviceData)
	if err != nil {
		return nil, err
	}

	serviceData, err = resolveBuild(inFile, serviceData)
	if err != nil {
		return nil, err
	}

	value, ok := serviceData["extends"]
	if !ok {
		return serviceData, nil
	}

	mapValue, ok := value.(map[interface{}]interface{})
	if !ok {
		return serviceData, nil
	}

	if configLookup == nil {
		return nil, fmt.Errorf("Can not use extends in file %s no mechanism provided to files", inFile)
	}

	file := asString(mapValue["file"])
	service := asString(mapValue["service"])

	if service == "" {
		return serviceData, nil
	}

	var baseService rawService

	if file == "" {
		if serviceData, ok := datas[service]; ok {
			baseService, err = parse(configLookup, inFile, serviceData, datas)
		} else {
			return nil, fmt.Errorf("Failed to find service %s to extend", service)
		}
	} else {
		bytes, resolved, err := configLookup.Lookup(file, inFile)
		if err != nil {
			logrus.Errorf("Failed to lookup file %s: %v", file, err)
			return nil, err
		}

		var baseRawServices rawServiceMap
		if err := yaml.Unmarshal(bytes, &baseRawServices); err != nil {
			return nil, err
		}

		baseService, ok = baseRawServices[service]
		if !ok {
			return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)
		}

		baseService, err = parse(configLookup, resolved, baseService, baseRawServices)
	}

	if err != nil {
		return nil, err
	}

	baseService = clone(baseService)

	fmt.Printf("merging\n%#v\n %#v\n", baseService, serviceData)

	for k, v := range serviceData {
		if ignore[k] {
			continue
		}

		existing, ok := baseService[k]
		if ok {
			baseService[k] = merge(existing, v)
		} else {
			baseService[k] = v
		}
	}

	fmt.Printf("result\n%#v\n", baseService)

	return baseService, nil
}

func merge(existing, value interface{}) interface{} {
	// append strings
	if left, lok := existing.([]interface{}); lok {
		if right, rok := value.([]interface{}); rok {
			return append(left, right...)
		}
	}

	//merge maps
	if left, lok := existing.(map[interface{}]interface{}); lok {
		if right, rok := value.(map[interface{}]interface{}); rok {
			newLeft := make(map[interface{}]interface{})
			for k, v := range left {
				newLeft[k] = v
			}
			for k, v := range right {
				newLeft[k] = v
			}
			return newLeft
		}
	}

	return value
}

func clone(in rawService) rawService {
	result := rawService{}
	for k, v := range in {
		result[k] = v
	}

	return result
}

func asString(obj interface{}) string {
	if v, ok := obj.(string); ok {
		return v
	} else {
		return ""
	}
}
<commit_msg>Printf bad<commit_after>package project

import (
	"bufio"
	"bytes"
	"fmt"
	"path"
	"strings"

	"github.com/Sirupsen/logrus"
	"github.com/rancherio/rancher-compose/librcompose/util"
	"gopkg.in/yaml.v2"
)

var (
	ValidRemotes = []string{
		"git://",
		"git@github.com:",
		"github.com",
		"http:",
		"https:",
	}
	ignore = map[string]bool{
		"links":        true,
		"volumes_from": true,
	}
)

type rawService map[string]interface{}
type rawServiceMap map[string]rawService

func Merge(p *Project, bytes []byte) (map[string]*ServiceConfig, error) {
	configs := make(map[string]*ServiceConfig)

	datas := make(rawServiceMap)
	err := yaml.Unmarshal(bytes, &datas)
	if err != nil {
		logrus.Fatalf("Could not parse config for project %s : %v", p.Name, err)
	}

	for name, data := range datas {
		data, err := parse(p.ConfigLookup, p.File, data, datas)
		if err != nil {
			logrus.Errorf("Failed to parse service %s: %v", name, err)
			return nil, err
		}

		datas[name] = data
	}

	err = util.Convert(datas, &configs)
	return configs, err
}

func readEnvFile(configLookup ConfigLookup, inFile string, serviceData rawService) (rawService, error) {
	var config ServiceConfig

	if err := util.Convert(serviceData, &config); err != nil {
		return nil, err
	}

	if len(config.EnvFile.Slice()) == 0 {
		return serviceData, nil
	}

	if configLookup == nil {
		return nil, fmt.Errorf("Can not use env_file in file %s no mechanism provided to load files", inFile)
	}

	vars := config.Environment.Slice()

	for i := len(config.EnvFile.Slice()) - 1; i >= 0; i-- {
		envFile := config.EnvFile.Slice()[i]
		content, _, err := configLookup.Lookup(envFile, inFile)
		if err != nil {
			return nil, err
		}

		if err != nil {
			return nil, err
		}

		scanner := bufio.NewScanner(bytes.NewBuffer(content))
		for scanner.Scan() {
			line := strings.TrimSpace(scanner.Text())
			key := strings.SplitAfter(line, "=")[0]

			found := false
			for _, v := range vars {
				if strings.HasPrefix(v, key) {
					found = true
					break
				}
			}

			if !found {
				vars = append(vars, line)
			}
		}

		if scanner.Err() != nil {
			return nil, scanner.Err()
		}
	}

	serviceData["environment"] = vars

	delete(serviceData, "env_file")

	return serviceData, nil
}

func resolveBuild(inFile string, serviceData rawService) (rawService, error) {

	build := asString(serviceData["build"])
	if build == "" {
		return serviceData, nil
	}

	for _, remote := range ValidRemotes {
		if strings.HasPrefix(build, remote) {
			return serviceData, nil
		}
	}

	current := path.Dir(inFile)

	if build == "." {
		build = current
	} else {
		current = path.Join(current, build)
	}

	serviceData["build"] = current

	return serviceData, nil
}

func parse(configLookup ConfigLookup, inFile string, serviceData rawService, datas rawServiceMap) (rawService, error) {
	serviceData, err := readEnvFile(configLookup, inFile, serviceData)
	if err != nil {
		return nil, err
	}

	serviceData, err = resolveBuild(inFile, serviceData)
	if err != nil {
		return nil, err
	}

	value, ok := serviceData["extends"]
	if !ok {
		return serviceData, nil
	}

	mapValue, ok := value.(map[interface{}]interface{})
	if !ok {
		return serviceData, nil
	}

	if configLookup == nil {
		return nil, fmt.Errorf("Can not use extends in file %s no mechanism provided to files", inFile)
	}

	file := asString(mapValue["file"])
	service := asString(mapValue["service"])

	if service == "" {
		return serviceData, nil
	}

	var baseService rawService

	if file == "" {
		if serviceData, ok := datas[service]; ok {
			baseService, err = parse(configLookup, inFile, serviceData, datas)
		} else {
			return nil, fmt.Errorf("Failed to find service %s to extend", service)
		}
	} else {
		bytes, resolved, err := configLookup.Lookup(file, inFile)
		if err != nil {
			logrus.Errorf("Failed to lookup file %s: %v", file, err)
			return nil, err
		}

		var baseRawServices rawServiceMap
		if err := yaml.Unmarshal(bytes, &baseRawServices); err != nil {
			return nil, err
		}

		baseService, ok = baseRawServices[service]
		if !ok {
			return nil, fmt.Errorf("Failed to find service %s in file %s", service, file)
		}

		baseService, err = parse(configLookup, resolved, baseService, baseRawServices)
	}

	if err != nil {
		return nil, err
	}

	baseService = clone(baseService)

	logrus.Debugf("Merging %#v, %#v", baseService, serviceData)

	for k, v := range serviceData {
		if ignore[k] {
			continue
		}

		existing, ok := baseService[k]
		if ok {
			baseService[k] = merge(existing, v)
		} else {
			baseService[k] = v
		}
	}

	logrus.Debugf("Merged result %#v", baseService)

	return baseService, nil
}

func merge(existing, value interface{}) interface{} {
	// append strings
	if left, lok := existing.([]interface{}); lok {
		if right, rok := value.([]interface{}); rok {
			return append(left, right...)
		}
	}

	//merge maps
	if left, lok := existing.(map[interface{}]interface{}); lok {
		if right, rok := value.(map[interface{}]interface{}); rok {
			newLeft := make(map[interface{}]interface{})
			for k, v := range left {
				newLeft[k] = v
			}
			for k, v := range right {
				newLeft[k] = v
			}
			return newLeft
		}
	}

	return value
}

func clone(in rawService) rawService {
	result := rawService{}
	for k, v := range in {
		result[k] = v
	}

	return result
}

func asString(obj interface{}) string {
	if v, ok := obj.(string); ok {
		return v
	} else {
		return ""
	}
}
<|endoftext|>