<commit_before>package watcher

import (
	"os"
	"time"

	"github.com/cloudfoundry-incubator/route-emitter/nats_emitter"
	"github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry/gosteno"
)

type Watcher struct {
	bbs     bbs.LRPRouterBBS
	table   routing_table.RoutingTableInterface
	emitter nats_emitter.NATSEmitterInterface
	logger  *gosteno.Logger
}

func NewWatcher(bbs bbs.LRPRouterBBS, table routing_table.RoutingTableInterface, emitter nats_emitter.NATSEmitterInterface, logger *gosteno.Logger) *Watcher {
	return &Watcher{
		bbs:     bbs,
		table:   table,
		emitter: emitter,
		logger:  logger,
	}
}

func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	desiredLRPChanges, _, desiredErrors := watcher.bbs.WatchForDesiredLRPChanges()
	actualLRPChanges, _, actualErrors := watcher.bbs.WatchForActualLRPChanges()

	close(ready)

	var reWatchActual <-chan time.Time
	var reWatchDesired <-chan time.Time

	for {
	InnerLoop:
		for {
			select {
			case desiredChange, ok := <-desiredLRPChanges:
				if !ok {
					break InnerLoop
				}

				watcher.logger.Infod(map[string]interface{}{
					"desired-change": desiredChange,
				}, "route-emitter.watcher.detected-desired-change")

				var messagesToEmit routing_table.MessagesToEmit
				if desiredChange.After == nil {
					if desiredChange.Before != nil {
						messagesToEmit = watcher.table.RemoveRoutes(desiredChange.Before.ProcessGuid)
					}
				} else {
					messagesToEmit = watcher.table.SetRoutes(desiredChange.After.ProcessGuid, desiredChange.After.Routes...)
				}

				watcher.emitter.Emit(messagesToEmit)

			case actualChange, ok := <-actualLRPChanges:
				if !ok {
					break InnerLoop
				}

				watcher.logger.Infod(map[string]interface{}{
					"actual-change": actualChange,
				}, "route-emitter.watcher.detected-actual-change")

				var messagesToEmit routing_table.MessagesToEmit
				if actualChange.After == nil {
					if actualChange.Before != nil {
						container, err := routing_table.ContainerFromActual(*actualChange.Before)
						if err != nil {
							continue
						}
						messagesToEmit = watcher.table.RemoveContainer(actualChange.Before.ProcessGuid, container)
					}
				} else {
					container, err := routing_table.ContainerFromActual(*actualChange.After)
					if err != nil {
						continue
					}
					messagesToEmit = watcher.table.AddOrUpdateContainer(actualChange.After.ProcessGuid, container)
				}

				watcher.emitter.Emit(messagesToEmit)

			case err := <-desiredErrors:
				watcher.logger.Errord(map[string]interface{}{
					"error": err.Error(),
				}, "route-emitter.watcher.desired-watch-failed")

				reWatchDesired = time.After(3 * time.Second)
				desiredLRPChanges = nil
				desiredErrors = nil

			case err := <-actualErrors:
				watcher.logger.Errord(map[string]interface{}{
					"error": err.Error(),
				}, "route-emitter.watcher.actual-watch-failed")

				reWatchActual = time.After(3 * time.Second)
				actualLRPChanges = nil
				actualErrors = nil

			case <-reWatchActual:
				actualLRPChanges, _, actualErrors = watcher.bbs.WatchForActualLRPChanges()
				reWatchActual = nil

			case <-reWatchDesired:
				desiredLRPChanges, _, desiredErrors = watcher.bbs.WatchForDesiredLRPChanges()
				reWatchDesired = nil

			case <-signals:
				watcher.logger.Info("route-emitter.watcher.stopping")
				return nil
			}
		}
	}

	return nil
}
<commit_msg>refactor a bit<commit_after>package watcher

import (
	"os"
	"time"

	"github.com/cloudfoundry-incubator/route-emitter/nats_emitter"
	"github.com/cloudfoundry-incubator/route-emitter/routing_table"
	"github.com/cloudfoundry-incubator/runtime-schema/bbs"
	"github.com/cloudfoundry-incubator/runtime-schema/models"
	"github.com/cloudfoundry/gosteno"
)

type Watcher struct {
	bbs     bbs.LRPRouterBBS
	table   routing_table.RoutingTableInterface
	emitter nats_emitter.NATSEmitterInterface
	logger  *gosteno.Logger
}

func NewWatcher(bbs bbs.LRPRouterBBS, table routing_table.RoutingTableInterface, emitter nats_emitter.NATSEmitterInterface, logger *gosteno.Logger) *Watcher {
	return &Watcher{
		bbs:     bbs,
		table:   table,
		emitter: emitter,
		logger:  logger,
	}
}

func (watcher *Watcher) Run(signals <-chan os.Signal, ready chan<- struct{}) error {
	desiredLRPChanges, _, desiredErrors := watcher.bbs.WatchForDesiredLRPChanges()
	actualLRPChanges, _, actualErrors := watcher.bbs.WatchForActualLRPChanges()

	close(ready)

	var reWatchActual <-chan time.Time
	var reWatchDesired <-chan time.Time

	for {
		select {
		case desiredChange, ok := <-desiredLRPChanges:
			if !ok {
				desiredLRPChanges = nil
				break
			}

			watcher.handleDesiredChange(desiredChange)

		case actualChange, ok := <-actualLRPChanges:
			if !ok {
				actualLRPChanges = nil
				break
			}

			watcher.handleActualChange(actualChange)

		case err := <-desiredErrors:
			watcher.logger.Errord(map[string]interface{}{
				"error": err.Error(),
			}, "route-emitter.watcher.desired-watch-failed")

			reWatchDesired = time.After(3 * time.Second)
			desiredLRPChanges = nil
			desiredErrors = nil

		case err := <-actualErrors:
			watcher.logger.Errord(map[string]interface{}{
				"error": err.Error(),
			}, "route-emitter.watcher.actual-watch-failed")

			reWatchActual = time.After(3 * time.Second)
			actualLRPChanges = nil
			actualErrors = nil

		case <-reWatchActual:
			actualLRPChanges, _, actualErrors = watcher.bbs.WatchForActualLRPChanges()
			reWatchActual = nil

		case <-reWatchDesired:
			desiredLRPChanges, _, desiredErrors = watcher.bbs.WatchForDesiredLRPChanges()
			reWatchDesired = nil

		case <-signals:
			watcher.logger.Info("route-emitter.watcher.stopping")
			return nil
		}
	}

	return nil
}

func (watcher *Watcher) handleActualChange(change models.ActualLRPChange) {
	watcher.logger.Infod(map[string]interface{}{
		"actual-change": change,
	}, "route-emitter.watcher.detected-actual-change")

	var messagesToEmit routing_table.MessagesToEmit
	if change.After == nil {
		if change.Before != nil {
			container, err := routing_table.ContainerFromActual(*change.Before)
			if err != nil {
				return
			}

			messagesToEmit = watcher.table.RemoveContainer(change.Before.ProcessGuid, container)
		}
	} else {
		container, err := routing_table.ContainerFromActual(*change.After)
		if err != nil {
			return
		}

		messagesToEmit = watcher.table.AddOrUpdateContainer(change.After.ProcessGuid, container)
	}

	watcher.emitter.Emit(messagesToEmit)
}

func (watcher *Watcher) handleDesiredChange(change models.DesiredLRPChange) {
	watcher.logger.Infod(map[string]interface{}{
		"desired-change": change,
	}, "route-emitter.watcher.detected-desired-change")

	var messagesToEmit routing_table.MessagesToEmit
	if change.After == nil {
		if change.Before != nil {
			messagesToEmit = watcher.table.RemoveRoutes(change.Before.ProcessGuid)
		}
	} else {
		messagesToEmit = watcher.table.SetRoutes(change.After.ProcessGuid, change.After.Routes...)
	}

	watcher.emitter.Emit(messagesToEmit)
}
<|endoftext|>


<commit_before>package stats_test

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "bosh/platform/stats"
)

var _ = Describe("sigarStatsCollector", func() {
	var (
		collector StatsCollector
	)

	BeforeEach(func() {
		collector = NewSigarStatsCollector()
	})

	Describe("GetCPULoad", func() {
		It("returns cpu load", func() {
			load, err := collector.GetCPULoad()
			Expect(err).ToNot(HaveOccurred())
			Expect(load.One >= 0).To(BeTrue())
			Expect(load.Five >= 0).To(BeTrue())
			Expect(load.Fifteen >= 0).To(BeTrue())
		})
	})

	Describe("StartCollecting", func() {
		It("updates cpu stats", func() {
			collector.StartCollecting(100 * time.Millisecond)
			time.Sleep(200 * time.Millisecond)

			stats, err := collector.GetCPUStats()
			Expect(err).ToNot(HaveOccurred())

			Expect(stats.User).ToNot(BeZero())
			Expect(stats.Sys).ToNot(BeZero())
			Expect(stats.Total).ToNot(BeZero())
		})
	})

	Describe("GetCPUStats", func() {
		It("gets delta cpu stats if it is collecting", func() {
			collector.StartCollecting(10 * time.Millisecond)

			time.Sleep(5 * time.Millisecond)
			initialStats, err := collector.GetCPUStats()
			Expect(err).ToNot(HaveOccurred())

			// First iteration will return total cpu stats, so we wait > 2*duration
			time.Sleep(15 * time.Millisecond)
			currentStats, err := collector.GetCPUStats()
			Expect(err).ToNot(HaveOccurred())

			// The next iteration will return the deltas instead of cpu
			Expect(currentStats.User).To(BeNumerically("<", initialStats.User))
			Expect(currentStats.Sys).To(BeNumerically("<", initialStats.Sys))
			Expect(currentStats.Total).To(BeNumerically("<", initialStats.Total))
		})
	})

	Describe("GetMemStats", func() {
		It("returns mem stats", func() {
			stats, err := collector.GetMemStats()
			Expect(err).ToNot(HaveOccurred())
			Expect(stats.Total > 0).To(BeTrue())
			Expect(stats.Used > 0).To(BeTrue())
		})
	})

	Describe("GetSwapStats", func() {
		It("returns swap stats", func() {
			stats, err := collector.GetSwapStats()
			Expect(err).ToNot(HaveOccurred())
			Expect(stats.Total > 0).To(BeTrue())
		})
	})

	Describe("GetDiskStats", func() {
		It("returns disk stats", func() {
			stats, err := collector.GetDiskStats("/")
			Expect(err).ToNot(HaveOccurred())

			Expect(stats.DiskUsage.Total).ToNot(BeZero())
			Expect(stats.DiskUsage.Used).ToNot(BeZero())
			Expect(stats.InodeUsage.Total).ToNot(BeZero())
			Expect(stats.InodeUsage.Used).ToNot(BeZero())
		})
	})
})
<commit_msg>Increase CPU usage window to avoid flakiness<commit_after>package stats_test

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	. "bosh/platform/stats"
)

var _ = Describe("sigarStatsCollector", func() {
	var (
		collector StatsCollector
	)

	BeforeEach(func() {
		collector = NewSigarStatsCollector()
	})

	Describe("GetCPULoad", func() {
		It("returns cpu load", func() {
			load, err := collector.GetCPULoad()
			Expect(err).ToNot(HaveOccurred())
			Expect(load.One >= 0).To(BeTrue())
			Expect(load.Five >= 0).To(BeTrue())
			Expect(load.Fifteen >= 0).To(BeTrue())
		})
	})

	Describe("StartCollecting", func() {
		It("updates cpu stats", func() {
			collector.StartCollecting(100 * time.Millisecond)
			time.Sleep(1 * time.Second)

			stats, err := collector.GetCPUStats()
			Expect(err).ToNot(HaveOccurred())

			Expect(stats.User).ToNot(BeZero())
			Expect(stats.Sys).ToNot(BeZero())
			Expect(stats.Total).ToNot(BeZero())
		})
	})

	Describe("GetCPUStats", func() {
		It("gets delta cpu stats if it is collecting", func() {
			collector.StartCollecting(10 * time.Millisecond)

			time.Sleep(5 * time.Millisecond)
			initialStats, err := collector.GetCPUStats()
			Expect(err).ToNot(HaveOccurred())

			// First iteration will return total cpu stats, so we wait > 2*duration
			time.Sleep(15 * time.Millisecond)
			currentStats, err := collector.GetCPUStats()
			Expect(err).ToNot(HaveOccurred())

			// The next iteration will return the deltas instead of cpu
			Expect(currentStats.User).To(BeNumerically("<", initialStats.User))
			Expect(currentStats.Sys).To(BeNumerically("<", initialStats.Sys))
			Expect(currentStats.Total).To(BeNumerically("<", initialStats.Total))
		})
	})

	Describe("GetMemStats", func() {
		It("returns mem stats", func() {
			stats, err := collector.GetMemStats()
			Expect(err).ToNot(HaveOccurred())
			Expect(stats.Total > 0).To(BeTrue())
			Expect(stats.Used > 0).To(BeTrue())
		})
	})

	Describe("GetSwapStats", func() {
		It("returns swap stats", func() {
			stats, err := collector.GetSwapStats()
			Expect(err).ToNot(HaveOccurred())
			Expect(stats.Total > 0).To(BeTrue())
		})
	})

	Describe("GetDiskStats", func() {
		It("returns disk stats", func() {
			stats, err := collector.GetDiskStats("/")
			Expect(err).ToNot(HaveOccurred())

			Expect(stats.DiskUsage.Total).ToNot(BeZero())
			Expect(stats.DiskUsage.Used).ToNot(BeZero())
			Expect(stats.InodeUsage.Total).ToNot(BeZero())
			Expect(stats.InodeUsage.Used).ToNot(BeZero())
		})
	})
})
<|endoftext|>


<commit_before>package main

import (
	"log"
	"net"
	"strconv"
	"time"

	"github.com/StabbyCutyou/buffstreams"
	"github.com/StabbyCutyou/buffstreams/test/message"
	"github.com/golang/protobuf/proto"
)

// TestCallback tests the callbacks
func TestCallback(bts []byte) error {
	return nil
}

// This is not a proper test, but it lets me benchmark how it
// performs on a raw level. Run this with the time command
func main() {
	cfg := buffstreams.BuffManagerConfig{
		MaxMessageSize: 256,
		EnableLogging:  true,
	}
	// 100 byte message
	name := "Stabby"
	date := time.Now().UnixNano()
	data := "This is an intenntionally long and rambling sentence to pad out the size of the message."
	msg := &message.Note{Name: &name, Date: &date, Comment: &data}
	msgBytes, err := proto.Marshal(msg)
	if err != nil {
		log.Print(err)
	}
	startingPort := 5031
	bm := buffstreams.New(cfg)
	bm.StartListening(strconv.Itoa(startingPort), TestCallback)

	for i := 0; i < 10; i++ {
		go func(n int, port string, bm *buffstreams.BuffManager) {
			address := buffstreams.FormatAddress("127.0.0.1", port)
			count := 0
			for {
				_, err := bm.WriteTo(address, msgBytes, true)
				//log.Printf("Wrote %d Bytes of %d + 2, error was: %s", written, len(msgBytes), err)
				if err != nil {
					log.Printf("Error %s", err)
				}
				count = count + 1
				log.Printf("%d - %d", n, count)
			}
		}(i, strconv.Itoa(startingPort), bm)
		//startingPort = startingPort + 1
	}
	time.Sleep(time.Minute * 10)
}

func testTCPConns() {
	// Start Listening
	go func() {
		//readTicker := time.NewTicker(10 * time.Millisecond)
		tcpAddr, err := net.ResolveTCPAddr("tcp", ":5031")
		if err != nil {
			log.Print(err)
			return
		}
		receiveSocket, err := net.ListenTCP("tcp", tcpAddr)
		if err != nil {
			log.Print(err)
			return
		}

		conn, err := receiveSocket.AcceptTCP()
		if err != nil {
			log.Printf("Error attempting to accept connection: %s", err)
		} else {
			buffer := make([]byte, 10)
			for {
				//select {
				// Check to see if we have a tick
				//case <-readTicker.C:
				log.Print("Trying to read")
				bytesRead, err := conn.Read(buffer)
				log.Printf("Bytes Read: %d out of %d", bytesRead, 10)
				if err != nil {
					log.Printf("Error: %s", err)
				}
				//}
			}
		}
	}()

	// Start writing
	go func() {
		writeTicker := time.NewTicker(10 * time.Second)
		tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:5031")
		if err != nil {
			log.Print(err)
			return
		}
		bytes := []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}
		conn, err := net.DialTCP("tcp", nil, tcpAddr)
		for {
			select {
			// Check to see if we have a tick
			case <-writeTicker.C:
				log.Printf("%s", time.Now())
				bytesWritten, err := conn.Write(bytes)
				log.Printf("Bytes Written: %d out of %d", bytesWritten, 10)
				if err != nil {
					log.Printf("Error: %s", err)
				}
			}
		}
	}()

	time.Sleep(time.Minute * 10)
}
<commit_msg>Removing outdated and broken test main<commit_after><|endoftext|>


<commit_before>// Copyright 2013 Patrice FERLET
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file
package verbalexpressions

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

type Flag uint

const (
	MULTILINE   Flag = 1 << iota
	IGNORE_CASE Flag = 1 << iota
	DOTALL      Flag = 1 << iota
	UNGREEDY    Flag = 1 << iota
	GLOBAL      Flag = 1 << iota
)

// VerbalExpression structure to create expression
type VerbalExpression struct {
	expression string
	parts      []string
	suffixes   string
	prefixes   string
	flags      Flag
	compiled   bool
	regexp     *regexp.Regexp
}

// quote is an alias to regexp.QuoteMeta
func quote(s string) string {
	return regexp.QuoteMeta(s)
}

// utility function to return only strings
func tostring(i interface{}) string {
	var r string
	switch x := i.(type) {
	case string:
		r = x
	case uint64:
		r = strconv.FormatUint(x, 10)
	case int64:
		r = strconv.FormatInt(x, 10)
	case uint:
		r = strconv.FormatUint(uint64(x), 10)
	case int:
		r = strconv.FormatInt(int64(x), 10)
	default:
		log.Panicf("Could not convert %v %t", x, x)
	}
	return r
}

// Instanciate a new VerbalExpression. You should use this method to
// initalize some internal var.
//
// Example:
//		v := verbalexpression.New().Find("foo")
func New() *VerbalExpression {
	r := new(VerbalExpression)
	r.flags = MULTILINE | GLOBAL
	r.parts = make([]string, 0)
	return r
}

// append a modifier
func (v *VerbalExpression) addmodifier(f Flag) *VerbalExpression {
	v.compiled = false //reinit previous regexp compilation
	v.flags |= f
	return v
}

// remove a modifier
func (v *VerbalExpression) removemodifier(f Flag) *VerbalExpression {
	v.compiled = false //reinit previous regexp compilation
	v.flags &= ^f
	return v
}

// append modifiers that are activated
func (v *VerbalExpression) getFlags() string {
	flags := "misU" // warning, follow Flag const order
	result := []rune{}

	for i, flag := range flags {
		if v.flags&(1<<uint(i)) != 0 {
			result = append(result, flag)
		}
	}

	return string(result)
}

// add method, append expresions to the internal string that will be parsed
func (v *VerbalExpression) add(s string) *VerbalExpression {
	v.compiled = false //reinit previous regexp compilation
	v.expression += s
	return v
}

// Start to capture something, stop with EndCapture()
func (v *VerbalExpression) BeginCapture() *VerbalExpression {
	v.suffixes += ")"
	return v.add("(")
}

// Stop capturing expresions parts
func (v *VerbalExpression) EndCapture() *VerbalExpression {
	v.suffixes = strings.Replace(v.suffixes, ")", "", 1)
	return v.add(")")
}

// Anything will match any char
func (v *VerbalExpression) Anything() *VerbalExpression {
	return v.add(`(?:.*)`)
}

// AnythingBut will match anything excpeting the given string.
// Example:
//		s := "This is a simple test"
//		v := verbalexpressions.New().AnythingBut("ie").RegExp().FindAllString(s, -1)
//		[Th s  s a s mple t st]
func (v *VerbalExpression) AnythingBut(s string) *VerbalExpression {
	return v.add(`(?:[^` + quote(s) + `]*)`)
}

// Something matches at least one char
func (v *VerbalExpression) Something() *VerbalExpression {
	return v.add(`(?:.+)`)
}

// Same as Something but excepting chars given in string "s"
func (v *VerbalExpression) SomethingBut(s string) *VerbalExpression {
	return v.add(`(?:[^` + quote(s) + `]+)`)
}

// EndOfLine tells verbalexpressions to match a end of line.
// Warning, to check multiple line, you must use SearchOneLine(true)
func (v *VerbalExpression) EndOfLine() *VerbalExpression {
	if !strings.HasSuffix(v.suffixes, "$") {
		v.suffixes += "$"
	}
	return v
}

// Maybe will search string zero on more times
func (v *VerbalExpression) Maybe(s string) *VerbalExpression {
	return v.add(`(?:` + quote(s) + `)?`)
}

// StartOfLine seeks the begining of a line. As EndOfLine you should use
// SearchOneLine(true) to test multiple lines
func (v *VerbalExpression) StartOfLine() *VerbalExpression {
	if !strings.HasPrefix(v.prefixes, "^") {
		v.prefixes = `^` + v.prefixes
	}
	return v
}

// Find seeks string. The string MUST be there (unlike Maybe() method)
func (v *VerbalExpression) Find(s string) *VerbalExpression {
	return v.add(`(?:` + quote(s) + `)`)
}

// Not invert Find, meaning search something excepting "value". This
// is different than SomethingBut or AnythingBut that works with a list of
// caracters
// TODO: doesn't work at this time,
func (v *VerbalExpression) Not(value string) *VerbalExpression {
	//return v.add(`(?!(` + quote(value) + `))`)
	// because Golang doesn't implement ?!
	// we create a pseudo negative system...

	runes := []rune(quote(value))
	parts := make([]string, 0)
	prev := ""
	for _, r := range runes {
		parts = append(parts, prev+"[^"+string(r)+"]")
		prev += string(r)
	}

	exp := strings.Join(parts, "|")
	exp = "(?:" + exp + ")*?"
	return v.add(exp)
}

// Alias to Find()
func (v *VerbalExpression) Then(s string) *VerbalExpression {
	return v.Find(s)
}

// Any accepts caracters to be matched
//
// Example:
//		s := "foo1 foo5 foobar"
//		v := New().Find("foo").Any("1234567890").Regex().FindAllString(s, -1)
//		[foo1 foo5]
func (v *VerbalExpression) Any(s string) *VerbalExpression {
	return v.add(`(?:[` + quote(s) + `])`)
}

//AnyOf is an alias to Any
func (v *VerbalExpression) AnyOf(s string) *VerbalExpression {
	return v.Any(s)
}

// LineBreak to find "\n" or "\r\n"
func (v *VerbalExpression) LineBreak() *VerbalExpression {
	return v.add(`(?:(?:\n)|(?:\r\n))`)
}

// Alias to LineBreak
func (v *VerbalExpression) Br() *VerbalExpression {
	return v.LineBreak()
}

// Range accepts an even number of arguments. Each pair of values defines start and end of range.
// Think like this: Range(from, to [, from, to ...])
//
// Example:
//		s := "This 1 is 55 a TEST"
//		v := verbalexpressions.New().Range("a","z",0,9)
//		res := v.Regex().FindAllString()
//		[his 1 is 55 a]
func (v *VerbalExpression) Range(args ...interface{}) *VerbalExpression {
	if len(args)%2 != 0 {
		log.Panicf("Range: not even args number")
	}

	parts := make([]string, 3)
	app := ""
	for i := 0; i < len(args); i++ {
		app += tostring(args[i])
		if i%2 != 0 {
			parts = append(parts, quote(app))
			app = ""
		} else {
			app += "-"
		}
	}
	return v.add("[" + strings.Join(parts, "") + "]")
}

// Tab fetch tabulation char (\t)
func (v *VerbalExpression) Tab() *VerbalExpression {
	return v.add(`\t+`)
}

// Word matches any word (containing alpha char)
func (v *VerbalExpression) Word() *VerbalExpression {
	return v.add(`\w+`)
}

// Multiply string s expression
//
// Multiple(value string [, min int[, max int]])
//
// This method accepts 1 to 3 arguments, argument 2 is min, argument 3 is max:
//
//		// get "foo" at least one time
//		v.Multiple("foo")
//		v.Multiple("foo", 1)
//
//		// get "foo" 0 or more times
//		v.Multiple("foo", 0)
//
//		//get "foo" 0 or 1 times
//		v.Multiple("foo", 0, 1)
//
//		// get "foo" 0 to 10 times
//		v.Multiple("foo",0 ,10)
//
//		//get "foo" at least 10 times
//		v.Multiple("foo", 10)
//
//		//get "foo" exactly 10 times
//		v.Multiple("foo", 10, 10)
//
//		//get "foo" from 1 to 10 times
//		v.Multiple("foo", 1, 10)
func (v *VerbalExpression) Multiple(s string, mults ...int) *VerbalExpression {

	if len(mults) > 2 {
		panic("Multiple: you can only give 1 or to multipliers, min and max as int")
	}
	// fetch multiplier if any
	var min, max int = -1, -1
	mult := "+"

	if len(mults) > 0 {
		min = mults[0]
		if len(mults) == 2 {
			max = mults[1]
		}
	}

	if min == 0 && max == 1 {
		// 0 or 1 time
		mult = "?"
	}

	if min == 0 && max == -1 {
		// 0 or more
		mult = "*"
	}

	if min == 1 && max == -1 {
		// at least 1 time
		mult = "+"
	}

	if min > 1 && max == -1 {
		//at least min times
		mult = fmt.Sprintf("{%d,}", min)
	}

	if max > 1 {
		if min > 0 {
			// min to max times
			mult = fmt.Sprintf("{%d,%d}", min, max)
		} else {
			// max times
			mult = fmt.Sprintf("{,%d}", max)
		}
	}

	return v.add("(?:" + quote(s) + ")" + mult)
}

// Or, chains a alternate expression
//
// Example:
//		v := Verbalexpression.New().
//				Find("foobarbaz").
//				Or().
//				Find("footestbaz")
func (v *VerbalExpression) Or(ve *VerbalExpression) *VerbalExpression {
	v.parts = append(v.parts, ve.Regex().String()+"|")
	return v
}

func (v *VerbalExpression) And(ve *VerbalExpression) *VerbalExpression {
	return v.add("(?:" + ve.Regex().String() + ")")
}

// WithAnyCase asks verbalexpressions to match with or without case sensitivity
func (v *VerbalExpression) WithAnyCase(sensitive bool) *VerbalExpression {
	if sensitive {
		return v.addmodifier(IGNORE_CASE)
	}
	return v.removemodifier(IGNORE_CASE)
}

// SearchOneLine deactivates "multiline" mode if online argument is true
// Default is false
func (v *VerbalExpression) SearchOneLine(oneline bool) *VerbalExpression {
	if !oneline {
		return v.addmodifier(MULTILINE)
	}
	return v.removemodifier(MULTILINE)
}

// MatchAllWithDot lets VerbalExpression matching "." for everything including \n, \r, and so on
func (v *VerbalExpression) MatchAllWithDot(enable bool) *VerbalExpression {
	if enable {
		return v.addmodifier(DOTALL)
	}
	return v.removemodifier(DOTALL)
}

// Regex returns the regular expression to use to test on string.
func (v *VerbalExpression) Regex() *regexp.Regexp {

	if !v.compiled {
		v.regexp = regexp.MustCompile(
			strings.Join([]string{
				strings.Join(v.parts, ""),
				`(?` + v.getFlags() + `)`,
				v.prefixes,
				v.expression,
				v.suffixes}, ""))
		v.compiled = true
	}
	return v.regexp
}

func (v *VerbalExpression) StopAtFirst(enable bool) *VerbalExpression {
	if enable {
		return v.removemodifier(GLOBAL)
	}

	return v.addmodifier(GLOBAL)
}

/*
Already implemented => v

v	add
v	startOfLine
v	endOfLine
v	then
v	find
v	maybe
v	anything
v	anythingBut
v	something
v	somethingBut
v	replace
v	lineBreak
v	br (shorthand for lineBreak)
v	tab
v	word
v	anyOf
v	any (shorthand for anyOf)
v	range
v	withAnyCase
v	stopAtFirst
v	searchOneLine
v	multiple
v	or
v	begindCapture
v	endCapture
*/
<commit_msg>Append documentation<commit_after>// Copyright 2013 Patrice FERLET
// Use of this source code is governed by MIT-style
// license that can be found in the LICENSE file
package verbalexpressions

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

type Flag uint

const (
	MULTILINE   Flag = 1 << iota
	IGNORE_CASE Flag = 1 << iota
	DOTALL      Flag = 1 << iota
	UNGREEDY    Flag = 1 << iota
	GLOBAL      Flag = 1 << iota
)

// VerbalExpression structure to create expression
type VerbalExpression struct {
	expression string
	parts      []string
	suffixes   string
	prefixes   string
	flags      Flag
	compiled   bool
	regexp     *regexp.Regexp
}

// quote is an alias to regexp.QuoteMeta
func quote(s string) string {
	return regexp.QuoteMeta(s)
}

// utility function to return only strings
func tostring(i interface{}) string {
	var r string
	switch x := i.(type) {
	case string:
		r = x
	case uint64:
		r = strconv.FormatUint(x, 10)
	case int64:
		r = strconv.FormatInt(x, 10)
	case uint:
		r = strconv.FormatUint(uint64(x), 10)
	case int:
		r = strconv.FormatInt(int64(x), 10)
	default:
		log.Panicf("Could not convert %v %t", x, x)
	}
	return r
}

// Instanciate a new VerbalExpression. You should use this method to
// initalize some internal var.
//
// Example:
//		v := verbalexpression.New().Find("foo")
func New() *VerbalExpression {
	r := new(VerbalExpression)
	r.flags = MULTILINE | GLOBAL
	r.parts = make([]string, 0)
	return r
}

// append a modifier
func (v *VerbalExpression) addmodifier(f Flag) *VerbalExpression {
	v.compiled = false //reinit previous regexp compilation
	v.flags |= f
	return v
}

// remove a modifier
func (v *VerbalExpression) removemodifier(f Flag) *VerbalExpression {
	v.compiled = false //reinit previous regexp compilation
	v.flags &= ^f
	return v
}

// append modifiers that are activated
func (v *VerbalExpression) getFlags() string {
	flags := "misU" // warning, follow Flag const order
	result := []rune{}

	for i, flag := range flags {
		if v.flags&(1<<uint(i)) != 0 {
			result = append(result, flag)
		}
	}

	return string(result)
}

// add method, append expresions to the internal string that will be parsed
func (v *VerbalExpression) add(s string) *VerbalExpression {
	v.compiled = false //reinit previous regexp compilation
	v.expression += s
	return v
}

// Start to capture something, stop with EndCapture()
func (v *VerbalExpression) BeginCapture() *VerbalExpression {
	v.suffixes += ")"
	return v.add("(")
}

// Stop capturing expresions parts
func (v *VerbalExpression) EndCapture() *VerbalExpression {
	v.suffixes = strings.Replace(v.suffixes, ")", "", 1)
	return v.add(")")
}

// Anything will match any char
func (v *VerbalExpression) Anything() *VerbalExpression {
	return v.add(`(?:.*)`)
}

// AnythingBut will match anything excpeting the given string.
// Example:
//		s := "This is a simple test"
//		v := verbalexpressions.New().AnythingBut("ie").RegExp().FindAllString(s, -1)
//		[Th s  s a s mple t st]
func (v *VerbalExpression) AnythingBut(s string) *VerbalExpression {
	return v.add(`(?:[^` + quote(s) + `]*)`)
}

// Something matches at least one char
func (v *VerbalExpression) Something() *VerbalExpression {
	return v.add(`(?:.+)`)
}

// Same as Something but excepting chars given in string "s"
func (v *VerbalExpression) SomethingBut(s string) *VerbalExpression {
	return v.add(`(?:[^` + quote(s) + `]+)`)
}

// EndOfLine tells verbalexpressions to match a end of line.
// Warning, to check multiple line, you must use SearchOneLine(true)
func (v *VerbalExpression) EndOfLine() *VerbalExpression {
	if !strings.HasSuffix(v.suffixes, "$") {
		v.suffixes += "$"
	}
	return v
}

// Maybe will search string zero on more times
func (v *VerbalExpression) Maybe(s string) *VerbalExpression {
	return v.add(`(?:` + quote(s) + `)?`)
}

// StartOfLine seeks the begining of a line. As EndOfLine you should use
// SearchOneLine(true) to test multiple lines
func (v *VerbalExpression) StartOfLine() *VerbalExpression {
	if !strings.HasPrefix(v.prefixes, "^") {
		v.prefixes = `^` + v.prefixes
	}
	return v
}

// Find seeks string. The string MUST be there (unlike Maybe() method)
func (v *VerbalExpression) Find(s string) *VerbalExpression {
	return v.add(`(?:` + quote(s) + `)`)
}

// Not invert Find, meaning search something excepting "value". This
// is different than SomethingBut or AnythingBut that works with a list of
// caracters. Note that this method is not exactly the same as PCRE system.
//
// While PCRE allows: foo(?!bar)baz, "foobarrbaz" matches (note the double r).
// With our method, this doesn't match. But: "foobarbaz" doen't match (so it's ok).
// And "fooXXXbaz" matches also.
func (v *VerbalExpression) Not(value string) *VerbalExpression {
	//return v.add(`(?!(` + quote(value) + `))`)
	// because Golang doesn't implement ?!
	// we create a pseudo negative system...

	runes := []rune(quote(value))
	parts := make([]string, 0)
	prev := ""
	for _, r := range runes {
		parts = append(parts, prev+"[^"+string(r)+"]")
		prev += string(r)
	}

	exp := strings.Join(parts, "|")
	exp = "(?:" + exp + ")*?"
	return v.add(exp)
}

// Alias to Find()
func (v *VerbalExpression) Then(s string) *VerbalExpression {
	return v.Find(s)
}

// Any accepts caracters to be matched
//
// Example:
//		s := "foo1 foo5 foobar"
//		v := New().Find("foo").Any("1234567890").Regex().FindAllString(s, -1)
//		[foo1 foo5]
func (v *VerbalExpression) Any(s string) *VerbalExpression {
	return v.add(`(?:[` + quote(s) + `])`)
}

//AnyOf is an alias to Any
func (v *VerbalExpression) AnyOf(s string) *VerbalExpression {
	return v.Any(s)
}

// LineBreak to find "\n" or "\r\n"
func (v *VerbalExpression) LineBreak() *VerbalExpression {
	return v.add(`(?:(?:\n)|(?:\r\n))`)
}

// Alias to LineBreak
func (v *VerbalExpression) Br() *VerbalExpression {
	return v.LineBreak()
}

// Range accepts an even number of arguments. Each pair of values defines start and end of range.
// Think like this: Range(from, to [, from, to ...])
//
// Example:
//		s := "This 1 is 55 a TEST"
//		v := verbalexpressions.New().Range("a","z",0,9)
//		res := v.Regex().FindAllString()
//		[his 1 is 55 a]
func (v *VerbalExpression) Range(args ...interface{}) *VerbalExpression {
	if len(args)%2 != 0 {
		log.Panicf("Range: not even args number")
	}

	parts := make([]string, 3)
	app := ""
	for i := 0; i < len(args); i++ {
		app += tostring(args[i])
		if i%2 != 0 {
			parts = append(parts, quote(app))
			app = ""
		} else {
			app += "-"
		}
	}
	return v.add("[" + strings.Join(parts, "") + "]")
}

// Tab fetch tabulation char (\t)
func (v *VerbalExpression) Tab() *VerbalExpression {
	return v.add(`\t+`)
}

// Word matches any word (containing alpha char)
func (v *VerbalExpression) Word() *VerbalExpression {
	return v.add(`\w+`)
}

// Multiply string s expression
//
// Multiple(value string [, min int[, max int]])
//
// This method accepts 1 to 3 arguments, argument 2 is min, argument 3 is max:
//
//		// get "foo" at least one time
//		v.Multiple("foo")
//		v.Multiple("foo", 1)
//
//		// get "foo" 0 or more times
//		v.Multiple("foo", 0)
//
//		//get "foo" 0 or 1 times
//		v.Multiple("foo", 0, 1)
//
//		// get "foo" 0 to 10 times
//		v.Multiple("foo",0 ,10)
//
//		//get "foo" at least 10 times
//		v.Multiple("foo", 10)
//
//		//get "foo" exactly 10 times
//		v.Multiple("foo", 10, 10)
//
//		//get "foo" from 1 to 10 times
//		v.Multiple("foo", 1, 10)
func (v *VerbalExpression) Multiple(s string, mults ...int) *VerbalExpression {

	if len(mults) > 2 {
		panic("Multiple: you can only give 1 or to multipliers, min and max as int")
	}
	// fetch multiplier if any
	var min, max int = -1, -1
	mult := "+"

	if len(mults) > 0 {
		min = mults[0]
		if len(mults) == 2 {
			max = mults[1]
		}
	}

	if min == 0 && max == 1 {
		// 0 or 1 time
		mult = "?"
	}

	if min == 0 && max == -1 {
		// 0 or more
		mult = "*"
	}

	if min == 1 && max == -1 {
		// at least 1 time
		mult = "+"
	}

	if min > 1 && max == -1 {
		//at least min times
		mult = fmt.Sprintf("{%d,}", min)
	}

	if max > 1 {
		if min > 0 {
			// min to max times
			mult = fmt.Sprintf("{%d,%d}", min, max)
		} else {
			// max times
			mult = fmt.Sprintf("{,%d}", max)
		}
	}

	return v.add("(?:" + quote(s) + ")" + mult)
}

// Or, chains a alternate expression
//
// Example:
//		v := Verbalexpression.New().
//				Find("foobarbaz").
//				Or().
//				Find("footestbaz")
func (v *VerbalExpression) Or(ve *VerbalExpression) *VerbalExpression {
	v.parts = append(v.parts, ve.Regex().String()+"|")
	return v
}

func (v *VerbalExpression) And(ve *VerbalExpression) *VerbalExpression {
	return v.add("(?:" + ve.Regex().String() + ")")
}

// WithAnyCase asks verbalexpressions to match with or without case sensitivity
func (v *VerbalExpression) WithAnyCase(sensitive bool) *VerbalExpression {
	if sensitive {
		return v.addmodifier(IGNORE_CASE)
	}
	return v.removemodifier(IGNORE_CASE)
}

// SearchOneLine deactivates "multiline" mode if online argument is true
// Default is false
func (v *VerbalExpression) SearchOneLine(oneline bool) *VerbalExpression {
	if !oneline {
		return v.addmodifier(MULTILINE)
	}
	return v.removemodifier(MULTILINE)
}

// MatchAllWithDot lets VerbalExpression matching "." for everything including \n, \r, and so on
func (v *VerbalExpression) MatchAllWithDot(enable bool) *VerbalExpression {
	if enable {
		return v.addmodifier(DOTALL)
	}
	return v.removemodifier(DOTALL)
}

// Regex returns the regular expression to use to test on string.
func (v *VerbalExpression) Regex() *regexp.Regexp {

	if !v.compiled {
		v.regexp = regexp.MustCompile(
			strings.Join([]string{
				strings.Join(v.parts, ""),
				`(?` + v.getFlags() + `)`,
				v.prefixes,
				v.expression,
				v.suffixes}, ""))
		v.compiled = true
	}
	return v.regexp
}

func (v *VerbalExpression) StopAtFirst(enable bool) *VerbalExpression {
	if enable {
		return v.removemodifier(GLOBAL)
	}

	return v.addmodifier(GLOBAL)
}

/*
Already implemented => v

v	add
v	startOfLine
v	endOfLine
v	then
v	find
v	maybe
v	anything
v	anythingBut
v	something
v	somethingBut
v	replace
v	lineBreak
v	br (shorthand for lineBreak)
v	tab
v	word
v	anyOf
v	any (shorthand for anyOf)
v	range
v	withAnyCase
v	stopAtFirst
v	searchOneLine
v	multiple
v	or
v	begindCapture
v	endCapture
*/
<|endoftext|>


<commit_before>package release

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/cenk/backoff"
	"github.com/fatih/color"

	"github.com/servehub/serve/manifest"
	"github.com/servehub/utils"
)

var routeVarsExclude = []string{"host", "location", "cache", "extra", "ssl"}

func init() {
	manifest.PluginRegestry.Add("release.http", ReleaseHttp{})
}

type ReleaseHttp struct{}

func (p ReleaseHttp) Run(data manifest.Manifest) error {
	if !data.Has("routes") {
		log.Println("No routes configured for release.")
		return nil
	}

	consul, err := utils.ConsulClient(data.GetString("consul-address"))
	if err != nil {
		return err
	}

	fullName := data.GetString("full-name")

	// check current service is alive and healthy
	if err := backoff.Retry(func() error {
		services, _, err := consul.Health().Service(fullName, "", true, nil)
		if err != nil {
			log.Println(color.RedString("Error in check health in consul: %v", err))
			return err
		}

		if len(services) == 0 {
			log.Printf("Service `%s` not started yet! Retry...", fullName)
			return fmt.Errorf("Service `%s` not started!", fullName)
		} else {
			log.Printf("Service `%s` started with %v instances.", fullName, len(services))
			return nil
		}
	}, backoff.NewExponentialBackOff()); err != nil {
		return err
	}

	routeVars := make(map[string]string, 0)
	if data.Has("route-vars") {
		if err := json.Unmarshal([]byte(data.GetString("route-vars")), &routeVars); err != nil {
			log.Println(color.RedString("Error parse route json: %v, %s", err, data.GetString("route")))
			return err
		}
	}

	if data.Has("stage") {
		routeVars["stage"] = data.GetString("stage")
	}

	defaults := data.GetTree("defaults").Unwrap().(map[string]interface{})

	// collect routes
	routes := consulRoutes{}
	for _, route := range data.GetArray("routes") {

		// set default values
		for k, v := range defaults {
			if !route.Has(k) {
				route.Set(k, v)
			}
		}

		if !route.Has("host") {
			log.Printf("Not found 'host': %s, skip...", route.String())
			continue
		}

		fields := make(map[string]string)
		for k, v := range route.Unwrap().(map[string]interface{}) {
			if !utils.Contains(k, routeVarsExclude) {
				fields[k] = fmt.Sprintf("%v", v)
			}
		}

		var cache map[string]interface{} = nil
		if route.Has("cache") {
			cache = route.GetTree("cache").Unwrap().(map[string]interface{})
		}

		var ssl map[string]interface{} = nil
		if route.Has("ssl") {
			ssl = route.GetTree("ssl").Unwrap().(map[string]interface{})
		}

		extra := route.GetStringOr("extra", "")
		if data.GetBool("maintenance") {
			extra += "\n return 503; \n"
		}

		routes.Routes = append(routes.Routes, consulRoute{
			Host:     route.GetString("host"),
			Location: route.GetStringOr("location", ""),
			Vars:     utils.MergeMaps(fields, routeVars),
			Cache:    cache,
			Ssl:      ssl,
			Extra:    extra,
		})
	}

	if len(routes.Routes) == 0 {
		log.Println("No routes configured for release.")
		return nil
	}

	routesJson, err := json.MarshalIndent(routes, "", "  ")
	if err != nil {
		return err
	}

	// write routes to consul kv
	if err := utils.PutConsulKv(consul, "services/routes/"+fullName, string(routesJson)); err != nil {
		return err
	}

	utils.DelConsulKv(consul, "services/outdated/"+fullName) // force delete outdated key (if exists)

	log.Println(color.GreenString("Service `%s` released with routes: %s", fullName, string(routesJson)))

	// find old services with the same routes
	existsRoutes, err := utils.ListConsulKv(consul, "services/routes/"+data.GetString("name-prefix"), nil)
	if err != nil {
		return err
	}

	for _, existsRoute := range existsRoutes {
		if existsRoute.Key != fmt.Sprintf("services/routes/%s", fullName) { // skip current service
			oldRoutes := consulRoutes{}
			if err := json.Unmarshal(existsRoute.Value, &oldRoutes); err != nil {
				return err
			}

		OuterLoop:
			for _, route := range routes.Routes {
				for _, oldRoute := range oldRoutes.Routes {
					if route.Host == oldRoute.Host && route.Location == oldRoute.Location && utils.MapsEqual(route.Vars, oldRoute.Vars) {
						outdated := strings.TrimPrefix(existsRoute.Key, "services/routes/")
						log.Println(color.GreenString("Found %s with the same routes %v. Remove it!", outdated, string(existsRoute.Value)))

						if err := utils.DelConsulKv(consul, existsRoute.Key); err != nil {
							return err
						}

						if err := utils.MarkAsOutdated(consul, outdated, time.Duration(data.GetIntOr("outdated-timeout-sec", 600))*time.Second); err != nil {
							return err
						}

						break OuterLoop
					}
				}
			}
		}
	}

	return nil
}

type consulRoutes struct {
	Routes []consulRoute `json:"routes"`
}

type consulRoute struct {
	Host     string                 `json:"host"`
	Location string                 `json:"location,omitempty"`
	Vars     map[string]string      `json:"vars,omitempty"`
	Cache    map[string]interface{} `json:"cache,omitempty"`
	Ssl      map[string]interface{} `json:"ssl,omitempty"`
	Extra    string                 `json:"extra,omitempty"`
}
<commit_msg>= release.http: ignore "public" on finding old outdated routes<commit_after>package release

import (
	"encoding/json"
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/cenk/backoff"
	"github.com/fatih/color"

	"github.com/servehub/serve/manifest"
	"github.com/servehub/utils"
)

var routeVarsExclude = []string{"host", "location", "cache", "extra", "ssl"}

func init() {
	manifest.PluginRegestry.Add("release.http", ReleaseHttp{})
}

type ReleaseHttp struct{}

func (p ReleaseHttp) Run(data manifest.Manifest) error {
	if !data.Has("routes") {
		log.Println("No routes configured for release.")
		return nil
	}

	consul, err := utils.ConsulClient(data.GetString("consul-address"))
	if err != nil {
		return err
	}

	fullName := data.GetString("full-name")

	// check current service is alive and healthy
	if err := backoff.Retry(func() error {
		services, _, err := consul.Health().Service(fullName, "", true, nil)
		if err != nil {
			log.Println(color.RedString("Error in check health in consul: %v", err))
			return err
		}

		if len(services) == 0 {
			log.Printf("Service `%s` not started yet! Retry...", fullName)
			return fmt.Errorf("Service `%s` not started!", fullName)
		} else {
			log.Printf("Service `%s` started with %v instances.", fullName, len(services))
			return nil
		}
	}, backoff.NewExponentialBackOff()); err != nil {
		return err
	}

	routeVars := make(map[string]string, 0)
	if data.Has("route-vars") {
		if err := json.Unmarshal([]byte(data.GetString("route-vars")), &routeVars); err != nil {
			log.Println(color.RedString("Error parse route json: %v, %s", err, data.GetString("route")))
			return err
		}
	}

	if data.Has("stage") {
		routeVars["stage"] = data.GetString("stage")
	}

	defaults := data.GetTree("defaults").Unwrap().(map[string]interface{})

	// collect routes
	routes := consulRoutes{}
	for _, route := range data.GetArray("routes") {

		// set default values
		for k, v := range defaults {
			if !route.Has(k) {
				route.Set(k, v)
			}
		}

		if !route.Has("host") {
			log.Printf("Not found 'host': %s, skip...", route.String())
			continue
		}

		fields := make(map[string]string)
		for k, v := range route.Unwrap().(map[string]interface{}) {
			if !utils.Contains(k, routeVarsExclude) {
				fields[k] = fmt.Sprintf("%v", v)
			}
		}

		var cache map[string]interface{} = nil
		if route.Has("cache") {
			cache = route.GetTree("cache").Unwrap().(map[string]interface{})
		}

		var ssl map[string]interface{} = nil
		if route.Has("ssl") {
			ssl = route.GetTree("ssl").Unwrap().(map[string]interface{})
		}

		extra := route.GetStringOr("extra", "")
		if data.GetBool("maintenance") {
			extra += "\n return 503; \n"
		}

		routes.Routes = append(routes.Routes, consulRoute{
			Host:     route.GetString("host"),
			Location: route.GetStringOr("location", ""),
			Vars:     utils.MergeMaps(fields, routeVars),
			Cache:    cache,
			Ssl:      ssl,
			Extra:    extra,
		})
	}

	if len(routes.Routes) == 0 {
		log.Println("No routes configured for release.")
		return nil
	}

	routesJson, err := json.MarshalIndent(routes, "", "  ")
	if err != nil {
		return err
	}

	// write routes to consul kv
	if err := utils.PutConsulKv(consul, "services/routes/"+fullName, string(routesJson)); err != nil {
		return err
	}

	utils.DelConsulKv(consul, "services/outdated/"+fullName) // force delete outdated key (if exists)

	log.Println(color.GreenString("Service `%s` released with routes: %s", fullName, string(routesJson)))

	// find old services with the same routes
	existsRoutes, err := utils.ListConsulKv(consul, "services/routes/"+data.GetString("name-prefix"), nil)
	if err != nil {
		return err
	}

	for _, existsRoute := range existsRoutes {
		if existsRoute.Key != fmt.Sprintf("services/routes/%s", fullName) { // skip current service
			oldRoutes := consulRoutes{}
			if err := json.Unmarshal(existsRoute.Value, &oldRoutes); err != nil {
				return err
			}

		OuterLoop:
			for _, route := range routes.Routes {
				for _, oldRoute := range oldRoutes.Routes {
					delete(route.Vars, "public") // ignore public filter
					delete(oldRoute.Vars, "public")

					if route.Host == oldRoute.Host && route.Location == oldRoute.Location && utils.MapsEqual(route.Vars, oldRoute.Vars) {
						outdated := strings.TrimPrefix(existsRoute.Key, "services/routes/")
						log.Println(color.GreenString("Found %s with the same routes %v. Remove it!", outdated, string(existsRoute.Value)))

						if err := utils.DelConsulKv(consul, existsRoute.Key); err != nil {
							return err
						}

						if err := utils.MarkAsOutdated(consul, outdated, time.Duration(data.GetIntOr("outdated-timeout-sec", 600))*time.Second); err != nil {
							return err
						}

						break OuterLoop
					}
				}
			}
		}
	}

	return nil
}

type consulRoutes struct {
	Routes []consulRoute `json:"routes"`
}

type consulRoute struct {
	Host     string                 `json:"host"`
	Location string                 `json:"location,omitempty"`
	Vars     map[string]string      `json:"vars,omitempty"`
	Cache    map[string]interface{} `json:"cache,omitempty"`
	Ssl      map[string]interface{} `json:"ssl,omitempty"`
	Extra    string                 `json:"extra,omitempty"`
}
<|endoftext|>


<commit_before>package view

import (
	"image"
	"image/draw"
	"log"

	"github.com/nictuku/stardew-rocks/parser"
)

func drawHoeDirt(pm *parser.Map, season string, item *parser.TerrainItem, img draw.Image, items [][]*parser.TerrainItem) {
	// TODO: neighbor detection.
	if item.Value.TerrainFeature.Type != "HoeDirt" {
		return
	}

	m := pm.TMX

	// Fetch tile from tileset.
	p := "../TerrainFeatures/hoeDirt.png"
	if season == "winter" {
		p = "../TerrainFeatures/hoeDirtSnow.png"
	}
	src, err := pm.FetchSource(p)
	if err != nil {
		log.Printf("Error fetching image asset %v: %v", p, err)
		return
	}

	indexUsed := getFlooringIndex(item, items, func(otherItem *parser.TerrainItem) bool {
		return otherItem.Value.TerrainFeature.Type == "HoeDirt" && otherItem.Value.TerrainFeature.State == item.Value.TerrainFeature.State
	})
	x := indexUsed % 4 * 16 // TODO: +64 if watered.
	sr := image.Rect(x, indexUsed/4*16, indexUsed%4*16+16, indexUsed/4*16+16)
	r := sr.Sub(sr.Min).Add(image.Point{
		item.Key.Vector2.X * m.TileWidth,
		item.Key.Vector2.Y * m.TileHeight,
	})
	sb.Draw(img, r, src, sr.Min, flooringLayer)

	crop := item.Value.TerrainFeature.Crop

	if crop.IndexOfHarvest != 0 { // Not sure if this is the best way.

		// The sprite sheet is divided in two columns.
		// row=0 means first plant in the sheet, on the top left.
		// row=1 means the second plant, which starts at X=128 pixels
		// row=4 is in the second row *in the image*, or Y=32, but also starting at 128 pixels.

		p := "../TileSheets/crops.png"

		src, err := pm.FetchSource(p)
		if err != nil {
			log.Printf("Error fetching image asset %v: %v", p, err)
			return
		}
		x0 := 0
		if crop.FullyGrown {
			if crop.DaysOfCurrentPhase <= 0 {
				x0 = 6 * 16
			} else {
				x0 = 7 * 16
			}

		} else {
			x0 = (crop.CurrentPhase + 2) * 16
			if crop.RowInSpriteSheet%2 != 0 {
				x0 += 128
			}
		}

		sr := xnaRect(x0, (crop.RowInSpriteSheet/2)*32, 16, 32)
		r := sr.Sub(sr.Min).Add(image.Point{
			item.Key.Vector2.X * m.TileWidth,
			item.Key.Vector2.Y*m.TileHeight - 16, // because using tile height 32 above
		})
		sb.Draw(img, r, src, sr.Min, objectLayer)
	}
}
<commit_msg>Corn, not starfruit<commit_after>package view

import (
	"image"
	"image/draw"
	"log"

	"github.com/nictuku/stardew-rocks/parser"
)

func drawHoeDirt(pm *parser.Map, season string, item *parser.TerrainItem, img draw.Image, items [][]*parser.TerrainItem) {
	// TODO: neighbor detection.
	if item.Value.TerrainFeature.Type != "HoeDirt" {
		return
	}

	m := pm.TMX

	// Fetch tile from tileset.
	p := "../TerrainFeatures/hoeDirt.png"
	if season == "winter" {
		p = "../TerrainFeatures/hoeDirtSnow.png"
	}
	src, err := pm.FetchSource(p)
	if err != nil {
		log.Printf("Error fetching image asset %v: %v", p, err)
		return
	}

	indexUsed := getFlooringIndex(item, items, func(otherItem *parser.TerrainItem) bool {
		return otherItem.Value.TerrainFeature.Type == "HoeDirt" && otherItem.Value.TerrainFeature.State == item.Value.TerrainFeature.State
	})
	x := indexUsed % 4 * 16 // TODO: +64 if watered.
	sr := image.Rect(x, indexUsed/4*16, indexUsed%4*16+16, indexUsed/4*16+16)
	r := sr.Sub(sr.Min).Add(image.Point{
		item.Key.Vector2.X * m.TileWidth,
		item.Key.Vector2.Y * m.TileHeight,
	})
	sb.Draw(img, r, src, sr.Min, flooringLayer)

	crop := item.Value.TerrainFeature.Crop

	if crop.IndexOfHarvest != 0 { // Not sure if this is the best way.

		// The sprite sheet is divided in two columns.
		// row=0 means first plant in the sheet, on the top left.
		// row=1 means the second plant, which starts at X=128 pixels
		// row=4 is in the second row *in the image*, or Y=32, but also starting at 128 pixels.

		p := "../TileSheets/crops.png"

		src, err := pm.FetchSource(p)
		if err != nil {
			log.Printf("Error fetching image asset %v: %v", p, err)
			return
		}
		x0 := 0
		if crop.FullyGrown {
			if crop.DaysOfCurrentPhase <= 0 {
				x0 = 6 * 16
			} else {
				x0 = 7 * 16
			}

		} else {
			x0 = (crop.CurrentPhase + 2) * 16
		}
		if crop.RowInSpriteSheet%2 != 0 {
			x0 += 128
		}
		sr := xnaRect(x0, (crop.RowInSpriteSheet/2)*32, 16, 32)
		r := sr.Sub(sr.Min).Add(image.Point{
			item.Key.Vector2.X * m.TileWidth,
			item.Key.Vector2.Y*m.TileHeight - 16, // because using tile height 32 above
		})
		sb.Draw(img, r, src, sr.Min, objectLayer)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "encoding/json"
    "errors"
    "log"
    "net"
    "net/http"
    "os"
    "io/ioutil"

	"github.com/ClusterHQ/dvol/pkg/api"
)

const PLUGINS_DIR = "/run/docker/plugins"
const DVOL_SOCKET = PLUGINS_DIR + "/dvol.sock"
const VOL_DIR = "/var/lib/dvol/volumes"
const DVOL_BASE_DIR = "/var/run/dvol"

type ResponseImplements struct {
    // A response to the Plugin.Activate request
    Implements []string
}

type RequestCreate struct {
    // A request to create a volume for Docker
    Name string
    Opts map[string]string
}

type RequestMount struct {
    // A request to mount a volume for Docker
    Name string
}

type RequestRemove struct {
    // A request to remove a volume for Docker
    Name string
}

type ResponseSimple struct {
    // A response which only indicates if there was an error or not
    Err string
}

type ResponseMount struct {
    // A response to the VolumeDriver.Mount request
    Mountpoint string
    Err string
}

func main () {
    if _, err := os.Stat(PLUGINS_DIR); err != nil {
        if err := os.MkdirAll(PLUGINS_DIR, 0700); err != nil {
            log.Fatalf("Could not make plugin directory %s: %v", PLUGINS_DIR, err)
        }
    }
    if _, err := os.Stat(DVOL_SOCKET); err == nil {
        if err = os.Remove(DVOL_SOCKET); err != nil {
            log.Fatalf("Could not clean up existing socket at %s: %v", DVOL_SOCKET, err)
        }
    }
    if _, err := os.Stat(VOL_DIR); err != nil {
        // FIXME: We should not have to do this here. It should be done by
        // the `dvol` command: https://clusterhq.atlassian.net/browse/VOL-81
        if err := os.MkdirAll(VOL_DIR, 0700); err != nil {
            log.Fatalf("Could not make volumes directory %s: %v", VOL_DIR, err)
        }
    }

    listener, err := net.Listen("unix", DVOL_SOCKET)

    if err != nil {
        log.Fatalf("Could not listen on %s: %v", DVOL_SOCKET, err)
    }

    http.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
        log.Print("<= /Plugin.Activate")
        responseJSON, _ := json.Marshal(&ResponseImplements{
            Implements: []string{"VolumeDriver"},
        })
        w.Write(responseJSON)
    })

    http.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
        log.Print("<= /VolumeDriver.Create")
        requestJSON, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Fatalf("Unable to read response body %s", err)
        }
        request := new(RequestCreate)
        json.Unmarshal(requestJSON, request)
        name := request.Name
        dvol := api.NewDvolAPI(DVOL_BASE_DIR)
        if !dvol.VolumeExists(name) {
            // TODO error handling
            err := dvol.CreateVolume(name)
            if err != nil {
                WriteResponseErr(err, w)
                return
            }
        }
        WriteResponseOK(w)
    })

    http.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
        WriteResponseOK(w)
    })

    http.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
    })

    http.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
        log.Print("<= /VolumeDriver.Mount")
        requestJSON, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Fatalf("Unable to read response body %s", err)
        }
        request := new(RequestMount)
        json.Unmarshal(requestJSON, request)
        name := request.Name

        dvol := api.NewDvolAPI(DVOL_BASE_DIR)

        if dvol.VolumeExists(name) {
            err := dvol.SwitchVolume(name)
            if err != nil {
                WriteResponseErr(err, w)
            }
            _, err = dvol.ActiveVolume()
            if err != nil {
                WriteResponseErr(err, w)
            }
            // mountpoint should be:
            // /var/lib/docker/volumes/<volumename>/running_point
            responseJSON, _ := json.Marshal(&ResponseMount{
                Mountpoint: "/tmp", // TODO: Get the real path
                Err: "",
            })
            w.Write(responseJSON)
        } else {
            WriteResponseErr(errors.New("Requested to mount unknown volume " + name), w)
        }
    })

    http.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) {
        WriteResponseOK(w)
    })

    http.HandleFunc("/VolumeDriver.List", func(w http.ResponseWriter, r *http.Request) {
    })

    http.Serve(listener, nil)
}

func WriteResponseOK (w http.ResponseWriter) {
    // A shortcut to writing a ResponseOK to w
    responseJSON, _ := json.Marshal(&ResponseSimple{Err: ""})
    w.Write(responseJSON)
}

func WriteResponseErr (err error, w http.ResponseWriter) {
    // A shortcut to responding with an error, and then log the error
    errString := fmt.Sprintln(err)
    log.Printf("Error: %v", err)
    responseJSON, _ := json.Marshal(&ResponseSimple{Err: errString})
    w.Write(responseJSON)
}

<commit_msg>This is what you're meant to start with.<commit_after>package main

import (
    "fmt"
    "encoding/json"
    "errors"
    "log"
    "net"
    "net/http"
    "os"
    "io/ioutil"

	"github.com/ClusterHQ/dvol/pkg/api"
)

const PLUGINS_DIR = "/run/docker/plugins"
const DVOL_SOCKET = PLUGINS_DIR + "/dvol.sock"
const VOL_DIR = "/var/lib/dvol/volumes"

type ResponseImplements struct {
    // A response to the Plugin.Activate request
    Implements []string
}

type RequestCreate struct {
    // A request to create a volume for Docker
    Name string
    Opts map[string]string
}

type RequestMount struct {
    // A request to mount a volume for Docker
    Name string
}

type RequestRemove struct {
    // A request to remove a volume for Docker
    Name string
}

type ResponseSimple struct {
    // A response which only indicates if there was an error or not
    Err string
}

type ResponseMount struct {
    // A response to the VolumeDriver.Mount request
    Mountpoint string
    Err string
}

func main () {
    if _, err := os.Stat(PLUGINS_DIR); err != nil {
        if err := os.MkdirAll(PLUGINS_DIR, 0700); err != nil {
            log.Fatalf("Could not make plugin directory %s: %v", PLUGINS_DIR, err)
        }
    }
    if _, err := os.Stat(DVOL_SOCKET); err == nil {
        if err = os.Remove(DVOL_SOCKET); err != nil {
            log.Fatalf("Could not clean up existing socket at %s: %v", DVOL_SOCKET, err)
        }
    }
    if _, err := os.Stat(VOL_DIR); err != nil {
        if err := os.MkdirAll(VOL_DIR, 0700); err != nil {
            log.Fatalf("Could not make volumes directory %s: %v", VOL_DIR, err)
        }
    }

    listener, err := net.Listen("unix", DVOL_SOCKET)

    if err != nil {
        log.Fatalf("Could not listen on %s: %v", DVOL_SOCKET, err)
    }

    http.HandleFunc("/Plugin.Activate", func(w http.ResponseWriter, r *http.Request) {
        log.Print("<= /Plugin.Activate")
        responseJSON, _ := json.Marshal(&ResponseImplements{
            Implements: []string{"VolumeDriver"},
        })
        w.Write(responseJSON)
    })

    http.HandleFunc("/VolumeDriver.Create", func(w http.ResponseWriter, r *http.Request) {
        log.Print("<= /VolumeDriver.Create")
        requestJSON, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Fatalf("Unable to read response body %s", err)
        }
        request := new(RequestCreate)
        json.Unmarshal(requestJSON, request)
        name := request.Name
        dvol := api.NewDvolAPI(VOL_DIR)
        if !dvol.VolumeExists(name) {
            log.Print("Creating volume", name, " which doesn't exist")
            err := dvol.CreateVolume(name)
            if err != nil {
                WriteResponseErr(err, w)
                return
            }
        }
        WriteResponseOK(w)
    })

    http.HandleFunc("/VolumeDriver.Remove", func(w http.ResponseWriter, r *http.Request) {
        WriteResponseOK(w)
    })

    http.HandleFunc("/VolumeDriver.Path", func(w http.ResponseWriter, r *http.Request) {
    })

    http.HandleFunc("/VolumeDriver.Mount", func(w http.ResponseWriter, r *http.Request) {
        log.Print("<= /VolumeDriver.Mount")
        requestJSON, err := ioutil.ReadAll(r.Body)
        if err != nil {
            log.Fatalf("Unable to read response body %s", err)
        }
        request := new(RequestMount)
        json.Unmarshal(requestJSON, request)
        name := request.Name

        dvol := api.NewDvolAPI(VOL_DIR)

        if dvol.VolumeExists(name) {
            err := dvol.SwitchVolume(name)
            if err != nil {
                WriteResponseErr(err, w)
            }
            _, err = dvol.ActiveVolume()
            if err != nil {
                WriteResponseErr(err, w)
            }
            // mountpoint should be:
            // /var/lib/docker/volumes/<volumename>/running_point
            responseJSON, _ := json.Marshal(&ResponseMount{
                Mountpoint: "/tmp", // TODO: Get the real path
                Err: "",
            })
            w.Write(responseJSON)
        } else {
            WriteResponseErr(errors.New("Requested to mount unknown volume " + name), w)
        }
    })

    http.HandleFunc("/VolumeDriver.Unmount", func(w http.ResponseWriter, r *http.Request) {
        WriteResponseOK(w)
    })

    http.HandleFunc("/VolumeDriver.List", func(w http.ResponseWriter, r *http.Request) {
    })

    http.Serve(listener, nil)
}

func WriteResponseOK (w http.ResponseWriter) {
    // A shortcut to writing a ResponseOK to w
    responseJSON, _ := json.Marshal(&ResponseSimple{Err: ""})
    w.Write(responseJSON)
}

func WriteResponseErr (err error, w http.ResponseWriter) {
    // A shortcut to responding with an error, and then log the error
    errString := fmt.Sprintln(err)
    log.Printf("Error: %v", err)
    responseJSON, _ := json.Marshal(&ResponseSimple{Err: errString})
    w.Write(responseJSON)
}

<|endoftext|>


<commit_before>package serf_test

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/travisjeffery/jocko"
	"github.com/travisjeffery/jocko/serf"
	"github.com/travisjeffery/jocko/testutil"
	"github.com/travisjeffery/jocko/zap"
)

var (
	logger   jocko.Logger
	serfPort int
)

func init() {
	logger = zap.New()
	serfPort = 7946
}

func Test_Membership(t *testing.T) {
	s0, err := getSerf(0)
	require.NoError(t, err)
	s1, err := getSerf(1)
	require.NoError(t, err)

	t.Run("Join Peer", func(t *testing.T) {
		testJoin(t, s0, s1)

		testutil.WaitForResult(func() (bool, error) {
			if len(s1.Cluster()) != 2 {
				return false, nil
			}
			if len(s0.Cluster()) != 2 {
				return false, nil
			}
			return true, nil
		}, func(err error) {
			t.Fatalf("err: %v", err)
		})
	})

	t.Run("Remove Peer", func(t *testing.T) {
		require.NoError(t, s1.Shutdown())

		testutil.WaitForResult(func() (bool, error) {
			if len(s0.Cluster()) != 1 {
				return false, nil
			}
			return true, nil
		}, func(err error) {
			t.Fatalf("err: %v", err)
		})
	})

	require.NoError(t, s0.Shutdown())
}

func getSerf(id int32) (*serf.Serf, error) {
	s, err := serf.New(
		serf.Logger(logger),
		serf.Addr(getSerfAddr()),
	)
	if err != nil {
		return nil, err
	}
	member := &jocko.ClusterMember{
		ID: id,
	}
	if err := s.Bootstrap(member, make(chan *jocko.ClusterMember, 32)); err != nil {
		return nil, err
	}
	return s, nil
}

func getSerfAddr() string {
	serfPort++
	return fmt.Sprintf("0.0.0.0:%d", serfPort)
}

func testJoin(t *testing.T, s0 *serf.Serf, other ...*serf.Serf) {
	for ind, s1 := range other {
		num, err := s1.Join(s0.Addr())
		require.NoError(t, err)
		require.Equal(t, ind+1, num)
	}
}
<commit_msg>serf: fix style<commit_after>package serf_test

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/require"
	"github.com/travisjeffery/jocko"
	"github.com/travisjeffery/jocko/serf"
	"github.com/travisjeffery/jocko/testutil"
	"github.com/travisjeffery/jocko/zap"
)

var (
	logger   jocko.Logger
	serfPort int
)

func init() {
	logger = zap.New()
	serfPort = 7946
}

func Test_Membership(t *testing.T) {
	s0, err := testSerf(0)
	require.NoError(t, err)
	s1, err := testSerf(1)
	require.NoError(t, err)

	t.Run("Join Peer", func(t *testing.T) {
		testJoin(t, s0, s1)

		testutil.WaitForResult(func() (bool, error) {
			if len(s1.Cluster()) != 2 {
				return false, nil
			}
			if len(s0.Cluster()) != 2 {
				return false, nil
			}
			return true, nil
		}, func(err error) {
			t.Fatalf("err: %v", err)
		})
	})

	t.Run("Remove Peer", func(t *testing.T) {
		require.NoError(t, s1.Shutdown())

		testutil.WaitForResult(func() (bool, error) {
			if len(s0.Cluster()) != 1 {
				return false, nil
			}
			return true, nil
		}, func(err error) {
			t.Fatalf("err: %v", err)
		})
	})

	require.NoError(t, s0.Shutdown())
}

func testSerf(id int32) (*serf.Serf, error) {
	s, err := serf.New(
		serf.Logger(logger),
		serf.Addr(testSerfAddr()),
	)
	if err != nil {
		return nil, err
	}
	member := &jocko.ClusterMember{
		ID: id,
	}
	if err := s.Bootstrap(member, make(chan *jocko.ClusterMember, 32)); err != nil {
		return nil, err
	}
	return s, nil
}

func testSerfAddr() string {
	serfPort++
	return fmt.Sprintf("0.0.0.0:%d", serfPort)
}

func testJoin(t *testing.T, s0 *serf.Serf, other ...*serf.Serf) {
	for ind, s1 := range other {
		num, err := s1.Join(s0.Addr())
		require.NoError(t, err)
		require.Equal(t, ind+1, num)
	}
}
<|endoftext|>


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

// Package heap provides heap operations for any type that implements
// heap.Interface. A heap is a tree with the property that each node is the
// minimum-valued node in its subtree.
//
// The minimum element in the tree is the root, at index 0.
//
// A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the
// highest-priority item from the queue. The Examples include such an
// implementation; the file example_pq_test.go has the complete source.
//
package heap

import "sort"

// Any type that implements heap.Interface may be used as a
// min-heap with the following invariants (established after
// Init has been called or if the data is empty or sorted):
//
//	!h.Less(j, i) for 0 <= i < h.Len() and j = 2*i+1 or 2*i+2 and j < h.Len()
//
// Note that Push and Pop in this interface are for package heap's
// implementation to call.  To add and remove things from the heap,
// use heap.Push and heap.Pop.
type Interface interface {
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}

// A heap must be initialized before any of the heap operations
// can be used. Init is idempotent with respect to the heap invariants
// and may be called whenever the heap invariants may have been invalidated.
// Its complexity is O(n) where n = h.Len().
//
func Init(h Interface) {
	// heapify
	n := h.Len()
	for i := n/2 - 1; i >= 0; i-- {
		down(h, i, n)
	}
}

// Push pushes the element x onto the heap. The complexity is
// O(log(n)) where n = h.Len().
//
func Push(h Interface, x interface{}) {
	h.Push(x)
	up(h, h.Len()-1)
}

// Pop removes the minimum element (according to Less) from the heap
// and returns it. The complexity is O(log(n)) where n = h.Len().
// It is equivalent to Remove(h, 0).
//
func Pop(h Interface) interface{} {
	n := h.Len() - 1
	h.Swap(0, n)
	down(h, 0, n)
	return h.Pop()
}

// Remove removes the element at index i from the heap.
// The complexity is O(log(n)) where n = h.Len().
//
func Remove(h Interface, i int) interface{} {
	n := h.Len() - 1
	if n != i {
		h.Swap(i, n)
		down(h, i, n)
		up(h, i)
	}
	return h.Pop()
}

// Fix reestablishes the heap ordering after the element at index i has changed its value.
// Changing the value of the element at index i and then calling Fix is equivalent to,
// but less expensive than, calling Remove(h, i) followed by a Push of the new value.
// The complexity is O(log(n)) where n = h.Len().
func Fix(h Interface, i int) {
	down(h, i, h.Len())
	up(h, i)
}

func up(h Interface, j int) {
	for {
		i := (j - 1) / 2 // parent
		if i == j || !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		j = i
	}
}

func down(h Interface, i, n int) {
	for {
		j1 := 2*i + 1
		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
			break
		}
		j := j1 // left child
		if j2 := j1 + 1; j2 < n && !h.Less(j1, j2) {
			j = j2 // = 2*i + 2  // right child
		}
		if !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		i = j
	}
}
<commit_msg>container/heap: avoid and/or ambiguity in documentation<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package heap provides heap operations for any type that implements
// heap.Interface. A heap is a tree with the property that each node is the
// minimum-valued node in its subtree.
//
// The minimum element in the tree is the root, at index 0.
//
// A heap is a common way to implement a priority queue. To build a priority
// queue, implement the Heap interface with the (negative) priority as the
// ordering for the Less method, so Push adds items while Pop removes the
// highest-priority item from the queue. The Examples include such an
// implementation; the file example_pq_test.go has the complete source.
//
package heap

import "sort"

// Any type that implements heap.Interface may be used as a
// min-heap with the following invariants (established after
// Init has been called or if the data is empty or sorted):
//
//	!h.Less(j, i) for 0 <= i < h.Len() and 2*i+1 <= j <= 2*i+2 and j < h.Len()
//
// Note that Push and Pop in this interface are for package heap's
// implementation to call.  To add and remove things from the heap,
// use heap.Push and heap.Pop.
type Interface interface {
	sort.Interface
	Push(x interface{}) // add x as element Len()
	Pop() interface{}   // remove and return element Len() - 1.
}

// A heap must be initialized before any of the heap operations
// can be used. Init is idempotent with respect to the heap invariants
// and may be called whenever the heap invariants may have been invalidated.
// Its complexity is O(n) where n = h.Len().
//
func Init(h Interface) {
	// heapify
	n := h.Len()
	for i := n/2 - 1; i >= 0; i-- {
		down(h, i, n)
	}
}

// Push pushes the element x onto the heap. The complexity is
// O(log(n)) where n = h.Len().
//
func Push(h Interface, x interface{}) {
	h.Push(x)
	up(h, h.Len()-1)
}

// Pop removes the minimum element (according to Less) from the heap
// and returns it. The complexity is O(log(n)) where n = h.Len().
// It is equivalent to Remove(h, 0).
//
func Pop(h Interface) interface{} {
	n := h.Len() - 1
	h.Swap(0, n)
	down(h, 0, n)
	return h.Pop()
}

// Remove removes the element at index i from the heap.
// The complexity is O(log(n)) where n = h.Len().
//
func Remove(h Interface, i int) interface{} {
	n := h.Len() - 1
	if n != i {
		h.Swap(i, n)
		down(h, i, n)
		up(h, i)
	}
	return h.Pop()
}

// Fix reestablishes the heap ordering after the element at index i has changed its value.
// Changing the value of the element at index i and then calling Fix is equivalent to,
// but less expensive than, calling Remove(h, i) followed by a Push of the new value.
// The complexity is O(log(n)) where n = h.Len().
func Fix(h Interface, i int) {
	down(h, i, h.Len())
	up(h, i)
}

func up(h Interface, j int) {
	for {
		i := (j - 1) / 2 // parent
		if i == j || !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		j = i
	}
}

func down(h Interface, i, n int) {
	for {
		j1 := 2*i + 1
		if j1 >= n || j1 < 0 { // j1 < 0 after int overflow
			break
		}
		j := j1 // left child
		if j2 := j1 + 1; j2 < n && !h.Less(j1, j2) {
			j = j2 // = 2*i + 2  // right child
		}
		if !h.Less(j, i) {
			break
		}
		h.Swap(i, j)
		i = j
	}
}
<|endoftext|>


<commit_before>package test

import (
	"testing"

	"github.com/icrowley/fake"
)

func TestGeo(t *testing.T) {
	for _, lang := range fake.GetLangs() {
		fake.SetLang(lang)

		f := fake.Latitute()
		if f == 0 {
			t.Errorf("Latitude failed with lang %s", lang)
		}

		i := fake.LatitudeDegress()
		if i < -180 || i > 180 {
			t.Errorf("LatitudeDegress failed with lang %s", lang)
		}

		i = fake.LatitudeMinutes()
		if i < 0 || i >= 60 {
			t.Errorf("LatitudeMinutes failed with lang %s", lang)
		}

		i = fake.LatitudeSeconds()
		if i < 0 || i >= 60 {
			t.Errorf("LatitudeSeconds failed with lang %s", lang)
		}

		s := fake.LatitudeDirection()
		if s != "N" && s != "S" {
			t.Errorf("LatitudeDirection failed with lang %s", lang)
		}

		f = fake.Longitude()
		if f == 0 {
			t.Errorf("Longitude failed with lang %s", lang)
		}

		i = fake.LongitudeDegrees()
		if i < -180 || i > 180 {
			t.Errorf("LongitudeDegrees failed with lang %s", lang)
		}

		i = fake.LongitudeMinutes()
		if i < 0 || i >= 60 {
			t.Errorf("LongitudeMinutes failed with lang %s", lang)
		}

		i = fake.LongitudeSeconds()
		if i < 0 || i >= 60 {
			t.Errorf("LongitudeSeconds failed with lang %s", lang)
		}

		s = fake.LongitudeDirection()
		if s != "W" && s != "E" {
			t.Errorf("LongitudeDirection failed with lang %s", lang)
		}
	}
}
<commit_msg>Fixed regression added in b56e934<commit_after>package test

import (
	"testing"

	"github.com/icrowley/fake"
)

func TestGeo(t *testing.T) {
	for _, lang := range fake.GetLangs() {
		fake.SetLang(lang)

		f := fake.Latitute()
		if f == 0 {
			t.Errorf("Latitude failed with lang %s", lang)
		}

		i := fake.LatitudeDegreess()
		if i < -180 || i > 180 {
			t.Errorf("LatitudeDegress failed with lang %s", lang)
		}

		i = fake.LatitudeMinutes()
		if i < 0 || i >= 60 {
			t.Errorf("LatitudeMinutes failed with lang %s", lang)
		}

		i = fake.LatitudeSeconds()
		if i < 0 || i >= 60 {
			t.Errorf("LatitudeSeconds failed with lang %s", lang)
		}

		s := fake.LatitudeDirection()
		if s != "N" && s != "S" {
			t.Errorf("LatitudeDirection failed with lang %s", lang)
		}

		f = fake.Longitude()
		if f == 0 {
			t.Errorf("Longitude failed with lang %s", lang)
		}

		i = fake.LongitudeDegrees()
		if i < -180 || i > 180 {
			t.Errorf("LongitudeDegrees failed with lang %s", lang)
		}

		i = fake.LongitudeMinutes()
		if i < 0 || i >= 60 {
			t.Errorf("LongitudeMinutes failed with lang %s", lang)
		}

		i = fake.LongitudeSeconds()
		if i < 0 || i >= 60 {
			t.Errorf("LongitudeSeconds failed with lang %s", lang)
		}

		s = fake.LongitudeDirection()
		if s != "W" && s != "E" {
			t.Errorf("LongitudeDirection failed with lang %s", lang)
		}
	}
}
<|endoftext|>


<commit_before>package report

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"sync"
	"time"
)

// ErrorInfo represents the CI error
type ErrorInfo struct {
	TestName string
	ErrName  string
	ErrClass string
	Location string
}

// ErrorStat represents the aggregate error count and region
type ErrorStat struct {
	Count     int            `json:"count"`
	Locations map[string]int `json:"locations"`
}

// Manager represents the details about a build and errors in that build
type Manager struct {
	lock        sync.Mutex
	JobName     string    `json:"job"`
	BuildNum    int       `json:"build"`
	Deployments int       `json:"deployments"`
	Errors      int       `json:"errors"`
	StartTime   time.Time `json:"startTime"`
	Duration    string    `json:"duration"`
	// Failure map: key=error, value=locations
	Failures map[string]*ErrorStat `json:"failures"`
}

type logError struct {
	name  string
	class string
	regex string
}

const (
	// ErrClassDeployment represents an error during deployment
	ErrClassDeployment = "Deployment"
	// ErrClassValidation represents an error during validation (tests)
	ErrClassValidation = "Validation"
	// ErrClassAzcli represents an error with Azure CLI
	ErrClassAzcli = "AzCLI"
	// ErrClassNone represents absence of error
	ErrClassNone = "None"

	// ErrSuccess represents a success, for some reason
	ErrSuccess = "Success"
	// ErrUnknown represents an unknown error
	ErrUnknown = "UnspecifiedError"
)

var logErrors []logError

func init() {
	logErrors = []logError{
		{name: "AzCliRunError", class: ErrClassAzcli, regex: "_init__.py"},
		{name: "AzCliLoadError", class: ErrClassAzcli, regex: "Error loading command module"},

		{name: "VMStartTimedOut", class: ErrClassDeployment, regex: "VMStartTimedOut"},
		{name: "OSProvisioningTimedOut", class: ErrClassDeployment, regex: "OSProvisioningTimedOut"},
		{name: "VMExtensionProvisioningError", class: ErrClassDeployment, regex: "VMExtensionProvisioningError"},
		{name: "VMExtensionProvisioningTimeout", class: ErrClassDeployment, regex: "VMExtensionProvisioningTimeout"},
		{name: "InternalExecutionError", class: ErrClassDeployment, regex: "InternalExecutionError"},
		{name: "SkuNotAvailable", class: ErrClassDeployment, regex: "SkuNotAvailable"},
		{name: "MaxStorageAccountsCountPerSubscriptionExceeded", class: ErrClassDeployment, regex: "MaxStorageAccountsCountPerSubscriptionExceeded"},
		{name: "ImageManagementOperationError", class: ErrClassDeployment, regex: "ImageManagementOperationError"},
		{name: "DiskProcessingError", class: ErrClassDeployment, regex: "DiskProcessingError"},
		{name: "DiskServiceInternalError", class: ErrClassDeployment, regex: "DiskServiceInternalError"},
		{name: "AllocationFailed", class: ErrClassDeployment, regex: "AllocationFailed"},
		{name: "NetworkingInternalOperationError", class: ErrClassDeployment, regex: "NetworkingInternalOperationError"},
		{name: "PlatformFaultDomainCount", class: ErrClassDeployment, regex: "platformFaultDomainCount"},

		{name: "K8sNodeNotReady", class: ErrClassValidation, regex: "K8S: gave up waiting for apiserver"},
		{name: "K8sUnexpectedVersion", class: ErrClassValidation, regex: "K8S: unexpected kubernetes version"},
		{name: "K8sContainerNotCreated", class: ErrClassValidation, regex: "K8S: gave up waiting for containers"},
		{name: "K8sPodNotRunning", class: ErrClassValidation, regex: "K8S: gave up waiting for running pods"},
		{name: "K8sKubeDnsNotRunning", class: ErrClassValidation, regex: "K8S: gave up waiting for kube-dns"},
		{name: "K8sDashboardNotRunning", class: ErrClassValidation, regex: "K8S: gave up waiting for kubernetes-dashboard"},
		{name: "K8sKubeProxyNotRunning", class: ErrClassValidation, regex: "K8S: gave up waiting for kube-proxy"},
		{name: "K8sProxyNotWorking", class: ErrClassValidation, regex: "K8S: gave up verifying proxy"},
		{name: "K8sLinuxDeploymentNotReady", class: ErrClassValidation, regex: "K8S-Linux: gave up waiting for deployment"},
		{name: "K8sWindowsDeploymentNotReady", class: ErrClassValidation, regex: "K8S-Windows: gave up waiting for deployment"},
		{name: "K8sLinuxNoExternalIP", class: ErrClassValidation, regex: "K8S-Linux: gave up waiting for loadbalancer to get an ingress ip"},
		{name: "K8sWindowsNoExternalIP", class: ErrClassValidation, regex: "K8S-Windows: gave up waiting for loadbalancer to get an ingress ip"},
		{name: "K8sLinuxNginxUnreachable", class: ErrClassValidation, regex: "K8S-Linux: failed to get expected response from nginx through the loadbalancer"},
		{name: "K8sWindowsSimpleWebUnreachable", class: ErrClassValidation, regex: "K8S-Windows: failed to get expected response from simpleweb through the loadbalancer"},
		{name: "K8sWindowsNoSimpleWebPodName", class: ErrClassValidation, regex: "K8S-Windows: failed to get expected pod name for simpleweb"},
		{name: "K8sWindowsNoSimpleWebOutboundInternet", class: ErrClassValidation, regex: "K8S-Windows: failed to get outbound internet connection inside simpleweb container"},

		{name: "DcosNodeNotReady", class: ErrClassValidation, regex: "gave up waiting for DCOS nodes"},
		{name: "DcosMarathonValidationFailed", class: ErrClassValidation, regex: "dcos/test.sh] marathon validation failed"},
		{name: "DcosMarathonNotAdded", class: ErrClassValidation, regex: "dcos/test.sh] gave up waiting for marathon to be added"},
		{name: "DcosMarathonLbNotInstalled", class: ErrClassValidation, regex: "Failed to install marathon-lb"},

		{name: "DockerCeNetworkNotReady", class: ErrClassValidation, regex: "DockerCE: gave up waiting for network to be created"},
		{name: "DockerCeServiceNotReady", class: ErrClassValidation, regex: "DockerCE: gave up waiting for service to be created"},
		{name: "DockerCeServiceUnreachable", class: ErrClassValidation, regex: "DockerCE: gave up waiting for service to be externally reachable"},
	}
}

// New creates a new error report
func New(jobName string, buildNum int, nDeploys int) *Manager {
	h := &Manager{}
	h.JobName = jobName
	h.BuildNum = buildNum
	h.Deployments = nDeploys
	h.Errors = 0
	h.StartTime = time.Now().UTC()
	h.Failures = make(map[string]*ErrorStat)
	return h
}

// Copy TBD needs definition
func (h *Manager) Copy() *Manager {
	n := New(h.JobName, h.BuildNum, h.Deployments)
	n.Errors = h.Errors
	n.StartTime = h.StartTime
	for e, f := range h.Failures {
		locs := make(map[string]int)
		for l, c := range f.Locations {
			locs[l] = c
		}
		n.Failures[e] = &ErrorStat{Count: f.Count, Locations: locs}
	}
	return n
}

// Process TBD needs definition
func (h *Manager) Process(txt, testName, location string) *ErrorInfo {
	for _, logErr := range logErrors {
		if match, _ := regexp.MatchString(logErr.regex, txt); match {
			h.addFailure(logErr.name, map[string]int{location: 1})
			return NewErrorInfo(testName, logErr.name, logErr.class, location)
		}
	}
	h.addFailure(ErrUnknown, map[string]int{location: 1})
	return NewErrorInfo(testName, ErrUnknown, ErrClassNone, location)
}

func (h *Manager) addFailure(key string, locations map[string]int) {
	h.lock.Lock()
	defer h.lock.Unlock()

	cnt := 0
	if failure, ok := h.Failures[key]; !ok {
		locs := make(map[string]int)
		for l, c := range locations {
			locs[l] = c
			cnt += c
		}
		h.Failures[key] = &ErrorStat{Count: cnt, Locations: locs}
	} else {
		for l, c := range locations {
			cnt += c
			if _, ok := failure.Locations[l]; !ok {
				failure.Locations[l] = c
			} else {
				failure.Locations[l] += c
			}
		}
		failure.Count += cnt
	}
	h.Errors += cnt
}

// CreateTestReport TBD needs definition
func (h *Manager) CreateTestReport(filepath string) error {
	h.Duration = time.Now().UTC().Sub(h.StartTime).String()
	data, err := json.MarshalIndent(h, "", "  ")
	if err != nil {
		return err
	}
	file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, os.FileMode(0644))
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.Write(data)
	if err != nil {
		return err
	}
	return nil
}

// CreateCombinedReport TBD needs definition
func (h *Manager) CreateCombinedReport(filepath, testReportFname string) error {
	// "COMBINED_PAST_REPORTS" is the number of recent reports in the combined report
	reports, err := strconv.Atoi(os.Getenv("COMBINED_PAST_REPORTS"))
	if err != nil || reports <= 0 {
		return nil
	}
	combinedReport := h.Copy()
	for i := 1; i <= reports; i++ {
		data, err := ioutil.ReadFile(fmt.Sprintf("%s/%d/%s/%s",
			os.Getenv("JOB_BUILD_ROOTDIR"), h.BuildNum-i, os.Getenv("JOB_BUILD_SUBDIR"), testReportFname))
		if err != nil {
			break
		}
		testReport := &Manager{}
		if err := json.Unmarshal(data, &testReport); err != nil {
			break
		}
		combinedReport.StartTime = testReport.StartTime
		combinedReport.Deployments += testReport.Deployments

		for e, f := range testReport.Failures {
			combinedReport.addFailure(e, f.Locations)
		}
	}
	return combinedReport.CreateTestReport(filepath)
}

// NewErrorInfo TBD needs definition
func NewErrorInfo(testName, errName, errClass, location string) *ErrorInfo {
	return &ErrorInfo{TestName: testName, ErrName: errName, ErrClass: errClass, Location: location}
}
<commit_msg>Added LogErrors to Manager<commit_after>package report

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"os"
	"regexp"
	"strconv"
	"sync"
	"time"
)

// ErrorInfo represents the CI error
type ErrorInfo struct {
	TestName string
	ErrName  string
	ErrClass string
	Location string
}

// ErrorStat represents the aggregate error count and region
type ErrorStat struct {
	Count     int            `json:"count"`
	Locations map[string]int `json:"locations"`
}

// Manager represents the details about a build and errors in that build
type Manager struct {
	lock        sync.Mutex
	JobName     string    `json:"job"`
	BuildNum    int       `json:"build"`
	Deployments int       `json:"deployments"`
	Errors      int       `json:"errors"`
	StartTime   time.Time `json:"startTime"`
	Duration    string    `json:"duration"`
	// Failure map: key=error, value=locations
	Failures  map[string]*ErrorStat `json:"failures"`
	LogErrors []logError            `json:"Errors"`
}

type logError struct {
	Name  string `json:"name"`
	Class string `json:"class"`
	Regex string `json:"regex"`
}

const (
	// ErrClassDeployment represents an error during deployment
	ErrClassDeployment = "Deployment"
	// ErrClassValidation represents an error during validation (tests)
	ErrClassValidation = "Validation"
	// ErrClassAzcli represents an error with Azure CLI
	ErrClassAzcli = "AzCLI"
	// ErrClassNone represents absence of error
	ErrClassNone = "None"

	// ErrSuccess represents a success, for some reason
	ErrSuccess = "Success"
	// ErrUnknown represents an unknown error
	ErrUnknown = "UnspecifiedError"
)

// New creates a new error report
func New(jobName string, buildNum int, nDeploys int, fileName string) *Manager {
	h := &Manager{}
	h.JobName = jobName
	h.BuildNum = buildNum
	h.Deployments = nDeploys
	h.Errors = 0
	h.StartTime = time.Now().UTC()
	h.Failures = make(map[string]*ErrorStat)
	h.LogErrors = makeErrorList(fileName)
	return h
}

func makeErrorList(fileName string) []logError {
	if fileName != "" {
		file, e := ioutil.ReadFile(fileName)
		if e != nil {
			fmt.Printf("File error: %v\n", e)
			os.Exit(1)
		}

		var dummyMgr Manager
		json.Unmarshal(file, &dummyMgr)
		return dummyMgr.LogErrors
	}
	return nil
}

// Copy TBD needs definition [ToDo]
func (h *Manager) Copy() *Manager {
	n := New(h.JobName, h.BuildNum, h.Deployments, "")
	n.Errors = h.Errors
	n.StartTime = h.StartTime
	for e, f := range h.Failures {
		locs := make(map[string]int)
		for l, c := range f.Locations {
			locs[l] = c
		}
		n.Failures[e] = &ErrorStat{Count: f.Count, Locations: locs}
	}
	return n
}

// Process TBD needs definition
func (h *Manager) Process(txt, testName, location string) *ErrorInfo {
	for _, logErr := range h.LogErrors {
		if match, _ := regexp.MatchString(logErr.Regex, txt); match {
			h.addFailure(logErr.Name, map[string]int{location: 1})
			return NewErrorInfo(testName, logErr.Name, logErr.Class, location)
		}
	}
	h.addFailure(ErrUnknown, map[string]int{location: 1})
	return NewErrorInfo(testName, ErrUnknown, ErrClassNone, location)
}

func (h *Manager) addFailure(key string, locations map[string]int) {
	h.lock.Lock()
	defer h.lock.Unlock()

	cnt := 0
	if failure, ok := h.Failures[key]; !ok {
		locs := make(map[string]int)
		for l, c := range locations {
			locs[l] = c
			cnt += c
		}
		h.Failures[key] = &ErrorStat{Count: cnt, Locations: locs}
	} else {
		for l, c := range locations {
			cnt += c
			if _, ok := failure.Locations[l]; !ok {
				failure.Locations[l] = c
			} else {
				failure.Locations[l] += c
			}
		}
		failure.Count += cnt
	}
	h.Errors += cnt
}

//MarshalJSON gives back customized fields
func (h *Manager) MarshalJSON() ([]byte, error) {
	return json.MarshalIndent(struct {
		JobName     string                `json:"job"`
		BuildNum    int                   `json:"build"`
		Deployments int                   `json:"deployments"`
		Errors      int                   `json:"errors"`
		StartTime   time.Time             `json:"startTime"`
		Duration    string                `json:"duration"`
		Failures    map[string]*ErrorStat `json:"failures"`
	}{h.JobName, h.BuildNum, h.Deployments, h.Errors, h.StartTime, h.Duration, h.Failures}, "", "  ")
}

// CreateTestReport TBD needs definition
func (h *Manager) CreateTestReport(filepath string) error {
	h.Duration = time.Now().UTC().Sub(h.StartTime).String()
	data, err := h.MarshalJSON()
	if err != nil {
		return err
	}
	file, err := os.OpenFile(filepath, os.O_CREATE|os.O_WRONLY, os.FileMode(0644))
	if err != nil {
		return err
	}
	defer file.Close()
	_, err = file.Write(data)
	if err != nil {
		return err
	}
	return nil
}

// CreateCombinedReport TBD needs definition
func (h *Manager) CreateCombinedReport(filepath, testReportFname string) error {
	// "COMBINED_PAST_REPORTS" is the number of recent reports in the combined report
	reports, err := strconv.Atoi(os.Getenv("COMBINED_PAST_REPORTS"))
	if err != nil || reports <= 0 {
		return nil
	}
	combinedReport := h.Copy()
	for i := 1; i <= reports; i++ {
		data, err := ioutil.ReadFile(fmt.Sprintf("%s/%d/%s/%s",
			os.Getenv("JOB_BUILD_ROOTDIR"), h.BuildNum-i, os.Getenv("JOB_BUILD_SUBDIR"), testReportFname))
		if err != nil {
			break
		}
		testReport := &Manager{}
		if err := json.Unmarshal(data, &testReport); err != nil {
			break
		}
		combinedReport.StartTime = testReport.StartTime
		combinedReport.Deployments += testReport.Deployments

		for e, f := range testReport.Failures {
			combinedReport.addFailure(e, f.Locations)
		}
	}
	return combinedReport.CreateTestReport(filepath)
}

// NewErrorInfo TBD needs definition
func NewErrorInfo(testName, errName, errClass, location string) *ErrorInfo {
	return &ErrorInfo{TestName: testName, ErrName: errName, ErrClass: errClass, Location: location}
}
<|endoftext|>


<commit_before>package mesos

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/CiscoCloud/mesos-consul/registry"
	"github.com/CiscoCloud/mesos-consul/state"

	log "github.com/sirupsen/logrus"
)

// Query the consul agent on the Mesos Master
// to initialize the cache.
//
// All services created by mesos-consul are prefixed
// with `mesos-consul:`
//
func (m *Mesos) LoadCache() error {
	log.Debug("Populating cache from Consul")

	mh := m.getLeader()

	return m.Registry.CacheLoad(mh.Ip)
}

func (m *Mesos) RegisterHosts(s state.State) {
	log.Debug("Running RegisterHosts")

	m.Agents = make(map[string]string)

	// Register slaves
	for _, f := range s.Slaves {
		agent := toIP(f.PID.Host)
		port := toPort(f.PID.Port)

		m.Agents[f.ID] = agent

		m.registerHost(&registry.Service{
			ID:      fmt.Sprintf("mesos-consul:%s:%s:%s", m.ServiceName, f.ID, f.Hostname),
			Name:    m.ServiceName,
			Port:    port,
			Address: agent,
			Agent:   agent,
			Tags:    m.agentTags("agent", "follower"),
			Check: &registry.Check{
				HTTP:     fmt.Sprintf("http://%s:%d/slave(1)/health", agent, port),
				Interval: "10s",
			},
		})
	}

	// Register masters
	mas := m.getMasters()
	for _, ma := range mas {
		var tags []string

		if ma.IsLeader {
			tags = m.agentTags("leader", "master")
		} else {
			tags = m.agentTags("master")
		}
		s := &registry.Service{
			ID:      fmt.Sprintf("mesos-consul:%s:%s:%s", m.ServiceName, ma.Ip, ma.PortString),
			Name:    m.ServiceName,
			Port:    ma.Port,
			Address: ma.Ip,
			Agent:   ma.Ip,
			Tags:    tags,
			Check: &registry.Check{
				HTTP:     fmt.Sprintf("http://%s:%d/master/health", ma.Ip, ma.Port),
				Interval: "10s",
			},
		}

		m.registerHost(s)
	}
}

func (m *Mesos) registerHost(s *registry.Service) {
	h := m.Registry.CacheLookup(s.ID)
	if h != nil {
		log.Infof("Host found. Comparing tags: (%v, %v)", h.Tags, s.Tags)

		if sliceEq(s.Tags, h.Tags) {
			m.Registry.CacheMark(s.ID)

			// Tags are the same. Return
			return
		}

		log.Info("Tags changed. Re-registering")

		// Delete cache entry. It will be re-created below
		m.Registry.CacheDelete(s.ID)
	}

	m.Registry.Register(s)
}

func (m *Mesos) registerTask(t *state.Task, agent string) {
	var tags []string

	tname := cleanName(t.Name, m.Separator)
        log.Debugf("original TaskName : (%v)", tname)
	if t.Label("overrideTaskName") != "" {
		tname = cleanName(t.Label("overrideTaskName"), m.Separator)
		log.Debugf("overrideTaskName to : (%v)", tname)
	}
	if !m.TaskPrivilege.Allowed(tname) {
		// Task not allowed to be registered
		return
	}

	address := t.IP(m.IpOrder...)

	l := t.Label("tags")
	if l != "" {
		tags = strings.Split(t.Label("tags"), ",")
	} else {
		tags = []string{}
	}

	tags = buildRegisterTaskTags(tname, tags, m.taskTag)

	for key := range t.DiscoveryInfo.Ports.DiscoveryPorts {
		discoveryPort := state.DiscoveryPort(t.DiscoveryInfo.Ports.DiscoveryPorts[key])
		serviceName := discoveryPort.Name
		servicePort := strconv.Itoa(discoveryPort.Number)
		log.Debugf("%+v framework has %+v as a name for %+v port",
			t.Name,
			discoveryPort.Name,
			discoveryPort.Number)
		if discoveryPort.Name != "" {
			m.Registry.Register(&registry.Service{
				ID:      fmt.Sprintf("mesos-consul:%s:%s:%d", agent, tname, discoveryPort.Number),
				Name:    tname,
				Port:    toPort(servicePort),
				Address: address,
				Tags:    append(tags, serviceName),
				Check: GetCheck(t, &CheckVar{
					Host: toIP(address),
					Port: servicePort,
				}),
				Agent: toIP(agent),
			})
		}
	}

	if t.Resources.PortRanges != "" {
		for _, port := range t.Resources.Ports() {
			m.Registry.Register(&registry.Service{
				ID:      fmt.Sprintf("mesos-consul:%s:%s:%s", agent, tname, port),
				Name:    tname,
				Port:    toPort(port),
				Address: address,
				Tags:    tags,
				Check: GetCheck(t, &CheckVar{
					Host: toIP(address),
					Port: port,
				}),
				Agent: toIP(agent),
			})
		}
	} else {
		m.Registry.Register(&registry.Service{
			ID:      fmt.Sprintf("mesos-consul:%s-%s", agent, tname),
			Name:    tname,
			Address: address,
			Tags:    tags,
			Check: GetCheck(t, &CheckVar{
				Host: toIP(address),
			}),
			Agent: toIP(agent),
		})
	}
}

// buildRegisterTaskTags takes a cleaned task name, a slice of starting tags, and the processed
// taskTag map and returns a slice of tags that should be applied to this task.
func buildRegisterTaskTags(taskName string, startingTags []string, taskTag map[string][]string) []string {
	result := startingTags
	tnameLower := strings.ToLower(taskName)

	for pattern, taskTags := range taskTag {
		for _, tag := range taskTags {
			if strings.Contains(tnameLower, pattern) {
				if !sliceContainsString(result, tag) {
					log.WithField("task-tag", tnameLower).Debug("Task matches pattern for tag")
					result = append(result, tag)
				}
			}
		}
	}

	return result
}

func (m *Mesos) agentTags(ts ...string) []string {
	if len(m.ServiceTags) == 0 {
		return ts
	}

	rval := []string{}

	for _, tag := range m.ServiceTags {
		for _, t := range ts {
			rval = append(rval, fmt.Sprintf("%s.%s", t, tag))
		}
	}

	return rval
}
<commit_msg>Add service IP address to Consul unique ID<commit_after>package mesos

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/CiscoCloud/mesos-consul/registry"
	"github.com/CiscoCloud/mesos-consul/state"

	log "github.com/sirupsen/logrus"
)

// Query the consul agent on the Mesos Master
// to initialize the cache.
//
// All services created by mesos-consul are prefixed
// with `mesos-consul:`
//
func (m *Mesos) LoadCache() error {
	log.Debug("Populating cache from Consul")

	mh := m.getLeader()

	return m.Registry.CacheLoad(mh.Ip)
}

func (m *Mesos) RegisterHosts(s state.State) {
	log.Debug("Running RegisterHosts")

	m.Agents = make(map[string]string)

	// Register slaves
	for _, f := range s.Slaves {
		agent := toIP(f.PID.Host)
		port := toPort(f.PID.Port)

		m.Agents[f.ID] = agent

		m.registerHost(&registry.Service{
			ID:      fmt.Sprintf("mesos-consul:%s:%s:%s", m.ServiceName, f.ID, f.Hostname),
			Name:    m.ServiceName,
			Port:    port,
			Address: agent,
			Agent:   agent,
			Tags:    m.agentTags("agent", "follower"),
			Check: &registry.Check{
				HTTP:     fmt.Sprintf("http://%s:%d/slave(1)/health", agent, port),
				Interval: "10s",
			},
		})
	}

	// Register masters
	mas := m.getMasters()
	for _, ma := range mas {
		var tags []string

		if ma.IsLeader {
			tags = m.agentTags("leader", "master")
		} else {
			tags = m.agentTags("master")
		}
		s := &registry.Service{
			ID:      fmt.Sprintf("mesos-consul:%s:%s:%s", m.ServiceName, ma.Ip, ma.PortString),
			Name:    m.ServiceName,
			Port:    ma.Port,
			Address: ma.Ip,
			Agent:   ma.Ip,
			Tags:    tags,
			Check: &registry.Check{
				HTTP:     fmt.Sprintf("http://%s:%d/master/health", ma.Ip, ma.Port),
				Interval: "10s",
			},
		}

		m.registerHost(s)
	}
}

func (m *Mesos) registerHost(s *registry.Service) {
	h := m.Registry.CacheLookup(s.ID)
	if h != nil {
		log.Infof("Host found. Comparing tags: (%v, %v)", h.Tags, s.Tags)

		if sliceEq(s.Tags, h.Tags) {
			m.Registry.CacheMark(s.ID)

			// Tags are the same. Return
			return
		}

		log.Info("Tags changed. Re-registering")

		// Delete cache entry. It will be re-created below
		m.Registry.CacheDelete(s.ID)
	}

	m.Registry.Register(s)
}

func (m *Mesos) registerTask(t *state.Task, agent string) {
	var tags []string

	tname := cleanName(t.Name, m.Separator)
        log.Debugf("original TaskName : (%v)", tname)
	if t.Label("overrideTaskName") != "" {
		tname = cleanName(t.Label("overrideTaskName"), m.Separator)
		log.Debugf("overrideTaskName to : (%v)", tname)
	}
	if !m.TaskPrivilege.Allowed(tname) {
		// Task not allowed to be registered
		return
	}

	address := t.IP(m.IpOrder...)

	l := t.Label("tags")
	if l != "" {
		tags = strings.Split(t.Label("tags"), ",")
	} else {
		tags = []string{}
	}

	tags = buildRegisterTaskTags(tname, tags, m.taskTag)

	for key := range t.DiscoveryInfo.Ports.DiscoveryPorts {
		discoveryPort := state.DiscoveryPort(t.DiscoveryInfo.Ports.DiscoveryPorts[key])
		serviceName := discoveryPort.Name
		servicePort := strconv.Itoa(discoveryPort.Number)
		log.Debugf("%+v framework has %+v as a name for %+v port",
			t.Name,
			discoveryPort.Name,
			discoveryPort.Number)
		if discoveryPort.Name != "" {
			m.Registry.Register(&registry.Service{
				ID:      fmt.Sprintf("mesos-consul:%s:%s:%s:%d", agent, tname, address, discoveryPort.Number),
				Name:    tname,
				Port:    toPort(servicePort),
				Address: address,
				Tags:    append(tags, serviceName),
				Check: GetCheck(t, &CheckVar{
					Host: toIP(address),
					Port: servicePort,
				}),
				Agent: toIP(agent),
			})
		}
	}

	if t.Resources.PortRanges != "" {
		for _, port := range t.Resources.Ports() {
			m.Registry.Register(&registry.Service{
				ID:      fmt.Sprintf("mesos-consul:%s:%s:%s:%s", agent, tname, address, port),
				Name:    tname,
				Port:    toPort(port),
				Address: address,
				Tags:    tags,
				Check: GetCheck(t, &CheckVar{
					Host: toIP(address),
					Port: port,
				}),
				Agent: toIP(agent),
			})
		}
	} else {
		m.Registry.Register(&registry.Service{
			ID:      fmt.Sprintf("mesos-consul:%s-%s:address", agent, tname, address),
			Name:    tname,
			Address: address,
			Tags:    tags,
			Check: GetCheck(t, &CheckVar{
				Host: toIP(address),
			}),
			Agent: toIP(agent),
		})
	}
}

// buildRegisterTaskTags takes a cleaned task name, a slice of starting tags, and the processed
// taskTag map and returns a slice of tags that should be applied to this task.
func buildRegisterTaskTags(taskName string, startingTags []string, taskTag map[string][]string) []string {
	result := startingTags
	tnameLower := strings.ToLower(taskName)

	for pattern, taskTags := range taskTag {
		for _, tag := range taskTags {
			if strings.Contains(tnameLower, pattern) {
				if !sliceContainsString(result, tag) {
					log.WithField("task-tag", tnameLower).Debug("Task matches pattern for tag")
					result = append(result, tag)
				}
			}
		}
	}

	return result
}

func (m *Mesos) agentTags(ts ...string) []string {
	if len(m.ServiceTags) == 0 {
		return ts
	}

	rval := []string{}

	for _, tag := range m.ServiceTags {
		for _, t := range ts {
			rval = append(rval, fmt.Sprintf("%s.%s", t, tag))
		}
	}

	return rval
}
<|endoftext|>