<commit_before>package openapi_test

import (
	"testing"

	openapi "github.com/nasa9084/go-openapi"
)

func TestOAuthFlowValidate(t *testing.T) {
	mockScopes := map[string]string{"foo": "bar"}

	empty := openapi.OAuthFlow{}
	aURL := openapi.OAuthFlow{AuthorizationURL: exampleCom}
	tURL := openapi.OAuthFlow{TokenURL: exampleCom}
	rURL := openapi.OAuthFlow{RefreshURL: exampleCom}
	scopes := openapi.OAuthFlow{Scopes: mockScopes}
	atURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom}
	arURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom}
	aURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, Scopes: mockScopes}
	trURL := openapi.OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom}
	tURLscopes := openapi.OAuthFlow{TokenURL: exampleCom, Scopes: mockScopes}
	rURLscopes := openapi.OAuthFlow{RefreshURL: exampleCom, Scopes: mockScopes}
	atrURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom}
	atURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, Scopes: mockScopes}
	arURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	trURLscopes := openapi.OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	atrURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	invalidURL := openapi.OAuthFlow{AuthorizationURL: "foobar", TokenURL: "foobar", RefreshURL: "foobar", Scopes: mockScopes}
	zeroMap := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: map[string]string{}}

	candidates := []struct {
		label   string
		in      openapi.OAuthFlow
		haveErr [4]bool
	}{
		{"empty", empty, [4]bool{true, true, true, true}},
		{"aURL", aURL, [4]bool{true, true, true, true}},
		{"tURL", tURL, [4]bool{true, true, true, true}},
		{"rURL", rURL, [4]bool{true, true, true, true}},
		{"scopes", scopes, [4]bool{true, true, true, true}},
		{"aURL/tURL", atURL, [4]bool{true, true, true, true}},
		{"aURL/rURL", arURL, [4]bool{true, true, true, true}},
		{"aURL/scopes", aURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL", trURL, [4]bool{true, true, true, true}},
		{"tURL/scopes", tURLscopes, [4]bool{true, false, false, true}},
		{"rURL/scopes", rURLscopes, [4]bool{true, true, true, true}},
		{"aURL/tURL/rURL", atrURL, [4]bool{true, true, true, true}},
		{"aURL/tURL/scopes", atURLscopes, [4]bool{false, false, false, false}},
		{"aURL/rURL/scopes", arURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL/scopes", trURLscopes, [4]bool{true, false, false, true}},
		{"aURL/tURL/rURL/scopes", atrURLscopes, [4]bool{false, false, false, false}},

		{"invalidURL", invalidURL, [4]bool{true, true, true, true}},
		{"zero length map", zeroMap, [4]bool{true, true, true, true}},
	}
	for _, c := range candidates {
		testOAuthFlowValidate(t, c.label, c.in, c.haveErr)
	}
}

var flowTypes = []string{"implicit", "password", "clientCredentials", "authorizationCode"}

func testOAuthFlowValidate(t *testing.T, label string, oauthFlow openapi.OAuthFlow, haveErr [4]bool) {
	if err := oauthFlow.Validate(""); err == nil {
		t.Logf("%s-empty", label)
		t.Error("error should be occurred, but not")
	}
	if err := oauthFlow.Validate("foobar"); err == nil {
		t.Logf("%s-wrongtype", label)
		t.Error("error should be occurred, but not")
	}
	for i, flowType := range flowTypes {
		if err := oauthFlow.Validate(flowType); (err != nil) != haveErr[i] {
			t.Logf("%s-%s", label, flowType)
			if haveErr[i] {
				t.Error("error should be occurred, but not")
				continue
			}
			t.Error("error should not be occurred, but occurred")
			t.Log(err)
		}
	}
}
<commit_msg>add testcases for oauthflow<commit_after>package openapi_test

import (
	"testing"

	openapi "github.com/nasa9084/go-openapi"
)

func TestOAuthFlowValidate(t *testing.T) {
	mockScopes := map[string]string{"foo": "bar"}

	empty := openapi.OAuthFlow{}
	aURL := openapi.OAuthFlow{AuthorizationURL: exampleCom}
	tURL := openapi.OAuthFlow{TokenURL: exampleCom}
	rURL := openapi.OAuthFlow{RefreshURL: exampleCom}
	scopes := openapi.OAuthFlow{Scopes: mockScopes}
	atURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom}
	arURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom}
	aURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, Scopes: mockScopes}
	trURL := openapi.OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom}
	tURLscopes := openapi.OAuthFlow{TokenURL: exampleCom, Scopes: mockScopes}
	rURLscopes := openapi.OAuthFlow{RefreshURL: exampleCom, Scopes: mockScopes}
	atrURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom}
	atURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, Scopes: mockScopes}
	arURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	trURLscopes := openapi.OAuthFlow{TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	atrURLscopes := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	invalidaURL := openapi.OAuthFlow{AuthorizationURL: "foobar", TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: mockScopes}
	invalidtURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: "foobar", RefreshURL: exampleCom, Scopes: mockScopes}
	invalidrURL := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: "foobar", Scopes: mockScopes}
	zeroMap := openapi.OAuthFlow{AuthorizationURL: exampleCom, TokenURL: exampleCom, RefreshURL: exampleCom, Scopes: map[string]string{}}

	candidates := []struct {
		label   string
		in      openapi.OAuthFlow
		haveErr [4]bool
	}{
		{"empty", empty, [4]bool{true, true, true, true}},
		{"aURL", aURL, [4]bool{true, true, true, true}},
		{"tURL", tURL, [4]bool{true, true, true, true}},
		{"rURL", rURL, [4]bool{true, true, true, true}},
		{"scopes", scopes, [4]bool{true, true, true, true}},
		{"aURL/tURL", atURL, [4]bool{true, true, true, true}},
		{"aURL/rURL", arURL, [4]bool{true, true, true, true}},
		{"aURL/scopes", aURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL", trURL, [4]bool{true, true, true, true}},
		{"tURL/scopes", tURLscopes, [4]bool{true, false, false, true}},
		{"rURL/scopes", rURLscopes, [4]bool{true, true, true, true}},
		{"aURL/tURL/rURL", atrURL, [4]bool{true, true, true, true}},
		{"aURL/tURL/scopes", atURLscopes, [4]bool{false, false, false, false}},
		{"aURL/rURL/scopes", arURLscopes, [4]bool{false, true, true, true}},
		{"tURL/rURL/scopes", trURLscopes, [4]bool{true, false, false, true}},
		{"aURL/tURL/rURL/scopes", atrURLscopes, [4]bool{false, false, false, false}},

		{"invalidaURL", invalidaURL, [4]bool{true, false, false, true}},
		{"invalidtURL", invalidtURL, [4]bool{false, true, true, true}},
		{"invalidrURL", invalidrURL, [4]bool{true, true, true, true}},
		{"zero length map", zeroMap, [4]bool{true, true, true, true}},
	}
	for _, c := range candidates {
		testOAuthFlowValidate(t, c.label, c.in, c.haveErr)
	}
}

var flowTypes = []string{"implicit", "password", "clientCredentials", "authorizationCode"}

func testOAuthFlowValidate(t *testing.T, label string, oauthFlow openapi.OAuthFlow, haveErr [4]bool) {
	if err := oauthFlow.Validate(""); err == nil {
		t.Logf("%s-empty", label)
		t.Error("error should be occurred, but not")
	}
	if err := oauthFlow.Validate("foobar"); err == nil {
		t.Logf("%s-wrongtype", label)
		t.Error("error should be occurred, but not")
	}
	for i, flowType := range flowTypes {
		if err := oauthFlow.Validate(flowType); (err != nil) != haveErr[i] {
			t.Logf("%s-%s", label, flowType)
			if haveErr[i] {
				t.Error("error should be occurred, but not")
				continue
			}
			t.Error("error should not be occurred, but occurred")
			t.Log(err)
		}
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package flowcontrol

import (
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/utils/integer"
)

type backoffEntry struct {
	backoff    time.Duration
	lastUpdate time.Time
}

type Backoff struct {
	sync.Mutex
	Clock           clock.Clock
	defaultDuration time.Duration
	maxDuration     time.Duration
	perItemBackoff  map[string]*backoffEntry
}

func NewFakeBackOff(initial, max time.Duration, tc *clock.FakeClock) *Backoff {
	return &Backoff{
		perItemBackoff:  map[string]*backoffEntry{},
		Clock:           tc,
		defaultDuration: initial,
		maxDuration:     max,
	}
}

func NewBackOff(initial, max time.Duration) *Backoff {
	return &Backoff{
		perItemBackoff:  map[string]*backoffEntry{},
		Clock:           clock.RealClock{},
		defaultDuration: initial,
		maxDuration:     max,
	}
}

// Get the current backoff Duration
func (p *Backoff) Get(id string) time.Duration {
	p.Lock()
	defer p.Unlock()
	var delay time.Duration
	entry, ok := p.perItemBackoff[id]
	if ok {
		delay = entry.backoff
	}
	return delay
}

// move backoff to the next mark, capping at maxDuration
func (p *Backoff) Next(id string, eventTime time.Time) {
	p.Lock()
	defer p.Unlock()
	entry, ok := p.perItemBackoff[id]
	if !ok || hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
		entry = p.initEntryUnsafe(id)
	} else {
		delay := entry.backoff * 2 // exponential
		entry.backoff = time.Duration(integer.Int64Min(int64(delay), int64(p.maxDuration)))
	}
	entry.lastUpdate = p.Clock.Now()
}

// Reset forces clearing of all backoff data for a given key.
func (p *Backoff) Reset(id string) {
	p.Lock()
	defer p.Unlock()
	delete(p.perItemBackoff, id)
}

// Returns True if the elapsed time since eventTime is smaller than the current backoff window
func (p *Backoff) IsInBackOffSince(id string, eventTime time.Time) bool {
	p.Lock()
	defer p.Unlock()
	entry, ok := p.perItemBackoff[id]
	if !ok {
		return false
	}
	if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
		return false
	}
	return p.Clock.Since(eventTime) < entry.backoff
}

// Returns True if time since lastupdate is less than the current backoff window.
func (p *Backoff) IsInBackOffSinceUpdate(id string, eventTime time.Time) bool {
	p.Lock()
	defer p.Unlock()
	entry, ok := p.perItemBackoff[id]
	if !ok {
		return false
	}
	if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
		return false
	}
	return eventTime.Sub(entry.lastUpdate) < entry.backoff
}

// Garbage collect records that have aged past maxDuration. Backoff users are expected
// to invoke this periodically.
func (p *Backoff) GC() {
	p.Lock()
	defer p.Unlock()
	now := p.Clock.Now()
	for id, entry := range p.perItemBackoff {
		if now.Sub(entry.lastUpdate) > p.maxDuration*2 {
			// GC when entry has not been updated for 2*maxDuration
			delete(p.perItemBackoff, id)
		}
	}
}

func (p *Backoff) DeleteEntry(id string) {
	p.Lock()
	defer p.Unlock()
	delete(p.perItemBackoff, id)
}

// Take a lock on *Backoff, before calling initEntryUnsafe
func (p *Backoff) initEntryUnsafe(id string) *backoffEntry {
	entry := &backoffEntry{backoff: p.defaultDuration}
	p.perItemBackoff[id] = entry
	return entry
}

// After 2*maxDuration we restart the backoff factor to the beginning
func hasExpired(eventTime time.Time, lastUpdate time.Time, maxDuration time.Duration) bool {
	return eventTime.Sub(lastUpdate) > maxDuration*2 // consider stable if it's ok for twice the maxDuration
}
<commit_msg>Utilize RWMutex for efficient backoff operations<commit_after>/*
Copyright 2015 The Kubernetes Authors.

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

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

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

package flowcontrol

import (
	"sync"
	"time"

	"k8s.io/apimachinery/pkg/util/clock"
	"k8s.io/utils/integer"
)

type backoffEntry struct {
	backoff    time.Duration
	lastUpdate time.Time
}

type Backoff struct {
	sync.RWMutex
	Clock           clock.Clock
	defaultDuration time.Duration
	maxDuration     time.Duration
	perItemBackoff  map[string]*backoffEntry
}

func NewFakeBackOff(initial, max time.Duration, tc *clock.FakeClock) *Backoff {
	return &Backoff{
		perItemBackoff:  map[string]*backoffEntry{},
		Clock:           tc,
		defaultDuration: initial,
		maxDuration:     max,
	}
}

func NewBackOff(initial, max time.Duration) *Backoff {
	return &Backoff{
		perItemBackoff:  map[string]*backoffEntry{},
		Clock:           clock.RealClock{},
		defaultDuration: initial,
		maxDuration:     max,
	}
}

// Get the current backoff Duration
func (p *Backoff) Get(id string) time.Duration {
	p.RLock()
	defer p.RUnlock()
	var delay time.Duration
	entry, ok := p.perItemBackoff[id]
	if ok {
		delay = entry.backoff
	}
	return delay
}

// move backoff to the next mark, capping at maxDuration
func (p *Backoff) Next(id string, eventTime time.Time) {
	p.Lock()
	defer p.Unlock()
	entry, ok := p.perItemBackoff[id]
	if !ok || hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
		entry = p.initEntryUnsafe(id)
	} else {
		delay := entry.backoff * 2 // exponential
		entry.backoff = time.Duration(integer.Int64Min(int64(delay), int64(p.maxDuration)))
	}
	entry.lastUpdate = p.Clock.Now()
}

// Reset forces clearing of all backoff data for a given key.
func (p *Backoff) Reset(id string) {
	p.Lock()
	defer p.Unlock()
	delete(p.perItemBackoff, id)
}

// Returns True if the elapsed time since eventTime is smaller than the current backoff window
func (p *Backoff) IsInBackOffSince(id string, eventTime time.Time) bool {
	p.RLock()
	defer p.RUnlock()
	entry, ok := p.perItemBackoff[id]
	if !ok {
		return false
	}
	if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
		return false
	}
	return p.Clock.Since(eventTime) < entry.backoff
}

// Returns True if time since lastupdate is less than the current backoff window.
func (p *Backoff) IsInBackOffSinceUpdate(id string, eventTime time.Time) bool {
	p.RLock()
	defer p.RUnlock()
	entry, ok := p.perItemBackoff[id]
	if !ok {
		return false
	}
	if hasExpired(eventTime, entry.lastUpdate, p.maxDuration) {
		return false
	}
	return eventTime.Sub(entry.lastUpdate) < entry.backoff
}

// Garbage collect records that have aged past maxDuration. Backoff users are expected
// to invoke this periodically.
func (p *Backoff) GC() {
	p.Lock()
	defer p.Unlock()
	now := p.Clock.Now()
	for id, entry := range p.perItemBackoff {
		if now.Sub(entry.lastUpdate) > p.maxDuration*2 {
			// GC when entry has not been updated for 2*maxDuration
			delete(p.perItemBackoff, id)
		}
	}
}

func (p *Backoff) DeleteEntry(id string) {
	p.Lock()
	defer p.Unlock()
	delete(p.perItemBackoff, id)
}

// Take a lock on *Backoff, before calling initEntryUnsafe
func (p *Backoff) initEntryUnsafe(id string) *backoffEntry {
	entry := &backoffEntry{backoff: p.defaultDuration}
	p.perItemBackoff[id] = entry
	return entry
}

// After 2*maxDuration we restart the backoff factor to the beginning
func hasExpired(eventTime time.Time, lastUpdate time.Time, maxDuration time.Duration) bool {
	return eventTime.Sub(lastUpdate) > maxDuration*2 // consider stable if it's ok for twice the maxDuration
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.101"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.68", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<commit_msg>fnlb: 0.0.102 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.102"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.69", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.217"

func main() {
	// XXX (reed): normalize
	level, err := logrus.ParseLevel(os.Getenv("LOG_LEVEL"))
	if err != nil {
		level = logrus.InfoLevel
	}
	logrus.SetLevel(level)

	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.184", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory; use k8s for kuberneted")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.StringVar(&conf.MgmtListen, "mgmt-listen", ":8081", "management port to run on")
	flag.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", 0, "graceful shutdown timeout")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckHealthy, "hc-healthy", 1, "threshold of success checks to declare node healthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.StringVar(&conf.Namespace, "namespace", "", "kubernetes namespace to monitor")
	flag.StringVar(&conf.LabelSelector, "label-selector", "", "kubernetes label selector to monitor")
	flag.IntVar(&conf.TargetPort, "target-port", 8080, "kubernetes port to target on selected pods")

	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	db, err := lb.NewDB(conf) // Handles case where DBurl == "k8s"
	if err != nil {
		logrus.WithError(err).Fatal("error setting up database")
	}
	defer db.Close()

	g, err := lb.NewAllGrouper(conf, db)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	servers := make([]*http.Server, 0, 1)
	handler := lb.NewProxy(k, g, r, conf)

	// a separate mgmt listener is requested? then let's create a LB traffic only server
	if conf.Listen != conf.MgmtListen {
		servers = append(servers, &http.Server{Addr: conf.Listen, Handler: handler})
		handler = lb.NullHandler()
	}

	// add mgmt endpoints to the handler
	handler = g.Wrap(handler) // add/del/list endpoints
	handler = r.Wrap(handler) // stats / dash endpoint

	servers = append(servers, &http.Server{Addr: conf.MgmtListen, Handler: handler})
	serve(servers, &conf)
}

func serve(servers []*http.Server, conf *lb.Config) {

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)

	for i := 0; i < len(servers); i++ {
		go func(idx int) {
			err := servers[idx].ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				logrus.WithFields(logrus.Fields{"server_id": idx}).WithError(err).Fatal("server error")
			} else {
				logrus.WithFields(logrus.Fields{"server_id": idx}).Info("server stopped")
			}
		}(i)
	}

	sig := <-ch
	logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")

	for i := 0; i < len(servers); i++ {

		ctx := context.Background()

		if conf.ShutdownTimeout > 0 {
			tmpCtx, cancel := context.WithTimeout(context.Background(), time.Duration(conf.ShutdownTimeout)*time.Second)
			ctx = tmpCtx
			defer cancel()
		}

		err := servers[i].Shutdown(ctx) // safe shutdown
		if err != nil {
			logrus.WithFields(logrus.Fields{"server_id": i}).WithError(err).Fatal("server shutdown error")
		} else {
			logrus.WithFields(logrus.Fields{"server_id": i}).Info("server shutdown")
		}
	}
}
<commit_msg>fnlb: 0.0.218 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.218"

func main() {
	// XXX (reed): normalize
	level, err := logrus.ParseLevel(os.Getenv("LOG_LEVEL"))
	if err != nil {
		level = logrus.InfoLevel
	}
	logrus.SetLevel(level)

	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.185", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory; use k8s for kuberneted")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.StringVar(&conf.MgmtListen, "mgmt-listen", ":8081", "management port to run on")
	flag.IntVar(&conf.ShutdownTimeout, "shutdown-timeout", 0, "graceful shutdown timeout")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckHealthy, "hc-healthy", 1, "threshold of success checks to declare node healthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.StringVar(&conf.Namespace, "namespace", "", "kubernetes namespace to monitor")
	flag.StringVar(&conf.LabelSelector, "label-selector", "", "kubernetes label selector to monitor")
	flag.IntVar(&conf.TargetPort, "target-port", 8080, "kubernetes port to target on selected pods")

	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	db, err := lb.NewDB(conf) // Handles case where DBurl == "k8s"
	if err != nil {
		logrus.WithError(err).Fatal("error setting up database")
	}
	defer db.Close()

	g, err := lb.NewAllGrouper(conf, db)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	servers := make([]*http.Server, 0, 1)
	handler := lb.NewProxy(k, g, r, conf)

	// a separate mgmt listener is requested? then let's create a LB traffic only server
	if conf.Listen != conf.MgmtListen {
		servers = append(servers, &http.Server{Addr: conf.Listen, Handler: handler})
		handler = lb.NullHandler()
	}

	// add mgmt endpoints to the handler
	handler = g.Wrap(handler) // add/del/list endpoints
	handler = r.Wrap(handler) // stats / dash endpoint

	servers = append(servers, &http.Server{Addr: conf.MgmtListen, Handler: handler})
	serve(servers, &conf)
}

func serve(servers []*http.Server, conf *lb.Config) {

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)

	for i := 0; i < len(servers); i++ {
		go func(idx int) {
			err := servers[idx].ListenAndServe()
			if err != nil && err != http.ErrServerClosed {
				logrus.WithFields(logrus.Fields{"server_id": idx}).WithError(err).Fatal("server error")
			} else {
				logrus.WithFields(logrus.Fields{"server_id": idx}).Info("server stopped")
			}
		}(i)
	}

	sig := <-ch
	logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")

	for i := 0; i < len(servers); i++ {

		ctx := context.Background()

		if conf.ShutdownTimeout > 0 {
			tmpCtx, cancel := context.WithTimeout(context.Background(), time.Duration(conf.ShutdownTimeout)*time.Second)
			ctx = tmpCtx
			defer cancel()
		}

		err := servers[i].Shutdown(ctx) // safe shutdown
		if err != nil {
			logrus.WithFields(logrus.Fields{"server_id": i}).WithError(err).Fatal("server shutdown error")
		} else {
			logrus.WithFields(logrus.Fields{"server_id": i}).Info("server shutdown")
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.147"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.114", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<commit_msg>fnlb: 0.0.148 release [skip ci]<commit_after>package main

import (
	"context"
	"crypto/tls"
	"flag"
	"net"
	"net/http"
	"os"
	"os/signal"
	"strings"
	"syscall"
	"time"

	"github.com/coreos/go-semver/semver"
	"github.com/fnproject/fn/fnlb/lb"
	"github.com/sirupsen/logrus"
)

const VERSION = "0.0.148"

func main() {
	// XXX (reed): normalize
	fnodes := flag.String("nodes", "", "comma separated list of functions nodes")
	minAPIVersion := flag.String("min-api-version", "0.0.115", "minimal node API to accept")

	var conf lb.Config
	flag.StringVar(&conf.DBurl, "db", "sqlite3://:memory:", "backend to store nodes, default to in memory")
	flag.StringVar(&conf.Listen, "listen", ":8081", "port to run on")
	flag.IntVar(&conf.HealthcheckInterval, "hc-interval", 3, "how often to check f(x) nodes, in seconds")
	flag.StringVar(&conf.HealthcheckEndpoint, "hc-path", "/version", "endpoint to determine node health")
	flag.IntVar(&conf.HealthcheckUnhealthy, "hc-unhealthy", 2, "threshold of failed checks to declare node unhealthy")
	flag.IntVar(&conf.HealthcheckTimeout, "hc-timeout", 5, "timeout of healthcheck endpoint, in seconds")
	flag.StringVar(&conf.ZipkinURL, "zipkin", "", "zipkin endpoint to send traces")
	flag.Parse()

	conf.MinAPIVersion = semver.New(*minAPIVersion)

	if len(*fnodes) > 0 {
		// starting w/o nodes is fine too
		conf.Nodes = strings.Split(*fnodes, ",")
	}

	conf.Transport = &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   10 * time.Second,
			KeepAlive: 120 * time.Second,
		}).Dial,
		MaxIdleConnsPerHost: 512,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig: &tls.Config{
			ClientSessionCache: tls.NewLRUClientSessionCache(4096),
		},
	}

	g, err := lb.NewAllGrouper(conf)
	if err != nil {
		logrus.WithError(err).Fatal("error setting up grouper")
	}

	r := lb.NewConsistentRouter(conf)
	k := func(r *http.Request) (string, error) {
		return r.URL.Path, nil
	}

	h := lb.NewProxy(k, g, r, conf)
	h = g.Wrap(h) // add/del/list endpoints
	h = r.Wrap(h) // stats / dash endpoint

	err = serve(conf.Listen, h)
	if err != nil {
		logrus.WithError(err).Fatal("server error")
	}
}

func serve(addr string, handler http.Handler) error {
	server := &http.Server{Addr: addr, Handler: handler}

	ch := make(chan os.Signal, 1)
	signal.Notify(ch, syscall.SIGQUIT, syscall.SIGINT)
	go func() {
		for sig := range ch {
			logrus.WithFields(logrus.Fields{"signal": sig}).Info("received signal")
			server.Shutdown(context.Background()) // safe shutdown
			return
		}
	}()
	return server.ListenAndServe()
}
<|endoftext|>


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

// +build !cluster_proxy

package integration

import (
	"context"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/integration"
	"github.com/coreos/etcd/pkg/testutil"
)

// TestNetworkPartitionBalancerPut tests when one member becomes isolated,
// first Put request fails, and following retry succeeds with client balancer
// switching to others.
func TestNetworkPartitionBalancerPut(t *testing.T) {
	testNetworkPartitionBalancer(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Put(ctx, "a", "b")
		return err
	})
}

// TestNetworkPartitionBalancerGet tests when one member becomes isolated,
// first Get request fails, and following retry succeeds with client balancer
// switching to others.
func TestNetworkPartitionBalancerGet(t *testing.T) {
	testNetworkPartitionBalancer(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Get(ctx, "a")
		return err
	})
}

func testNetworkPartitionBalancer(t *testing.T, op func(*clientv3.Client, context.Context) error) {
	defer testutil.AfterTest(t)

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{
		Size:                 3,
		GRPCKeepAliveMinTime: time.Millisecond, // avoid too_many_pings
		SkipCreatingClient:   true,
	})
	defer clus.Terminate(t)

	// expect pin ep[0]
	ccfg := clientv3.Config{
		Endpoints:            []string{clus.Members[0].GRPCAddr()},
		DialTimeout:          3 * time.Second,
		DialKeepAliveTime:    2 * time.Second,
		DialKeepAliveTimeout: 2 * time.Second,
	}
	cli, err := clientv3.New(ccfg)
	if err != nil {
		t.Fatal(err)
	}
	defer cli.Close()

	// add other endpoints for later endpoint switch
	cli.SetEndpoints(clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr())
	clus.Members[0].InjectPartition(t, clus.Members[1:])

	for i := 0; i < 2; i++ {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		err = op(cli, ctx)
		cancel()
		if err == nil {
			break
		}
		// TODO: separate put and get test for error checking.
		// we do not really expect ErrTimeout on get.
		if err != context.DeadlineExceeded && err != rpctypes.ErrTimeout {
			t.Errorf("#%d: expected %v or %v, got %v", i, context.DeadlineExceeded, rpctypes.ErrTimeout, err)
		}
		// give enough time for endpoint switch
		// TODO: remove random sleep by syncing directly with balancer
		if i == 0 {
			time.Sleep(5 * time.Second)
		}
	}
	if err != nil {
		t.Errorf("balancer did not switch in time (%v)", err)
	}
}
<commit_msg>clientv3/integration: rename partition tests<commit_after>// Copyright 2017 The etcd Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// +build !cluster_proxy

package integration

import (
	"context"
	"testing"
	"time"

	"github.com/coreos/etcd/clientv3"
	"github.com/coreos/etcd/etcdserver/api/v3rpc/rpctypes"
	"github.com/coreos/etcd/integration"
	"github.com/coreos/etcd/pkg/testutil"
)

// TestBalancerUnderNetworkPartitionPut tests when one member becomes isolated,
// first Put request fails, and following retry succeeds with client balancer
// switching to others.
func TestBalancerUnderNetworkPartitionPut(t *testing.T) {
	testBalancerUnderNetworkPartition(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Put(ctx, "a", "b")
		return err
	})
}

// TestBalancerUnderNetworkPartitionGet tests when one member becomes isolated,
// first Get request fails, and following retry succeeds with client balancer
// switching to others.
func TestBalancerUnderNetworkPartitionGet(t *testing.T) {
	testBalancerUnderNetworkPartition(t, func(cli *clientv3.Client, ctx context.Context) error {
		_, err := cli.Get(ctx, "a")
		return err
	})
}

func testBalancerUnderNetworkPartition(t *testing.T, op func(*clientv3.Client, context.Context) error) {
	defer testutil.AfterTest(t)

	clus := integration.NewClusterV3(t, &integration.ClusterConfig{
		Size:                 3,
		GRPCKeepAliveMinTime: time.Millisecond, // avoid too_many_pings
		SkipCreatingClient:   true,
	})
	defer clus.Terminate(t)

	// expect pin ep[0]
	ccfg := clientv3.Config{
		Endpoints:            []string{clus.Members[0].GRPCAddr()},
		DialTimeout:          3 * time.Second,
		DialKeepAliveTime:    2 * time.Second,
		DialKeepAliveTimeout: 2 * time.Second,
	}
	cli, err := clientv3.New(ccfg)
	if err != nil {
		t.Fatal(err)
	}
	defer cli.Close()

	// add other endpoints for later endpoint switch
	cli.SetEndpoints(clus.Members[0].GRPCAddr(), clus.Members[1].GRPCAddr(), clus.Members[2].GRPCAddr())
	clus.Members[0].InjectPartition(t, clus.Members[1:])

	for i := 0; i < 2; i++ {
		ctx, cancel := context.WithTimeout(context.Background(), time.Second)
		err = op(cli, ctx)
		cancel()
		if err == nil {
			break
		}
		// TODO: separate put and get test for error checking.
		// we do not really expect ErrTimeout on get.
		if err != context.DeadlineExceeded && err != rpctypes.ErrTimeout {
			t.Errorf("#%d: expected %v or %v, got %v", i, context.DeadlineExceeded, rpctypes.ErrTimeout, err)
		}
		// give enough time for endpoint switch
		// TODO: remove random sleep by syncing directly with balancer
		if i == 0 {
			time.Sleep(5 * time.Second)
		}
	}
	if err != nil {
		t.Errorf("balancer did not switch in time (%v)", err)
	}
}
<|endoftext|>


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

// Google's Omaha application update protocol, version 3.
//
// Omaha is a poll based protocol using XML. Requests are made by clients to
// check for updates or report events of an update process. Responses are given
// by the server to provide update information, if any, or to simply
// acknowledge the receipt of event status.
//
// https://github.com/google/omaha/blob/master/doc/ServerProtocolV3.md
package omaha

import (
	"encoding/xml"
)

// Request sent by the Omaha client
type Request struct {
	XMLName       xml.Name      `xml:"request" json:"-"`
	OS            *OS           `xml:"os"`
	Apps          []*AppRequest `xml:"app"`
	Protocol      string        `xml:"protocol,attr"`
	Version       string        `xml:"version,attr,omitempty"`
	IsMachine     string        `xml:"ismachine,attr,omitempty"`
	RequestId     string        `xml:"requestid,attr,omitempty"`
	SessionId     string        `xml:"sessionid,attr,omitempty"`
	UserId        string        `xml:"userid,attr,omitempty"`
	InstallSource string        `xml:"installsource,attr,omitempty"`
	TestSource    string        `xml:"testsource,attr,omitempty"`

	// update engine extension, duplicates the version attribute.
	UpdaterVersion string `xml:"updaterversion,attr,omitempty"`
}

func NewRequest() *Request {
	return &Request{
		Protocol: "3.0",
		// TODO(marineam) set a default client Version
		OS: &OS{
			Platform: LocalPlatform(),
			Arch:     LocalArch(),
			// TODO(marineam): Version and ServicePack
		},
	}
}

func (r *Request) AddApp(id, version string) *AppRequest {
	a := &AppRequest{Id: id, Version: version}
	r.Apps = append(r.Apps, a)
	return a
}

type AppRequest struct {
	Ping        *PingRequest    `xml:"ping"`
	UpdateCheck *UpdateRequest  `xml:"updatecheck"`
	Events      []*EventRequest `xml:"event" json:",omitempty"`
	Id          string          `xml:"appid,attr,omitempty"`
	Version     string          `xml:"version,attr,omitempty"`
	NextVersion string          `xml:"nextversion,attr,omitempty"`
	Lang        string          `xml:"lang,attr,omitempty"`
	Client      string          `xml:"client,attr,omitempty"`
	InstallAge  string          `xml:"installage,attr,omitempty"`

	// update engine extensions
	Track     string `xml:"track,attr,omitempty"`
	FromTrack string `xml:"from_track,attr,omitempty"`
	Board     string `xml:"board,attr,omitempty"`
	DeltaOK   bool   `xml:"delta_okay,attr,omitempty"`

	// coreos update engine extensions
	BootId       string `xml:"bootid,attr,omitempty"`
	MachineID    string `xml:"machineid,attr,omitempty"`
	OEM          string `xml:"oem,attr,omitempty"`
	OEMVersion   string `xml:"oemversion,attr,omitempty"`
	AlephVersion string `xml:"alephversion,attr,omitempty"`
}

func (a *AppRequest) AddUpdateCheck() *UpdateRequest {
	a.UpdateCheck = &UpdateRequest{}
	return a.UpdateCheck
}

func (a *AppRequest) AddPing() *PingRequest {
	a.Ping = &PingRequest{Active: 1}
	return a.Ping
}

func (a *AppRequest) AddEvent() *EventRequest {
	event := &EventRequest{}
	a.Events = append(a.Events, event)
	return event
}

type UpdateRequest struct {
	TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"`
}

type PingRequest struct {
	Active               int  `xml:"active,attr,omitempty"`
	LastActiveReportDays *int `xml:"a,attr,omitempty"`
	LastReportDays       int  `xml:"r,attr,omitempty"`
}

type EventRequest struct {
	Type            EventType   `xml:"eventtype,attr"`
	Result          EventResult `xml:"eventresult,attr"`
	NextVersion     string      `xml:"nextversion,attr,omitempty"`
	PreviousVersion string      `xml:"previousversion,attr,omitempty"`
	ErrorCode       string      `xml:"errorcode,attr,omitempty"`
}

// Response sent by the Omaha server
type Response struct {
	XMLName  xml.Name       `xml:"response" json:"-"`
	DayStart DayStart       `xml:"daystart"`
	Apps     []*AppResponse `xml:"app"`
	Protocol string         `xml:"protocol,attr"`
	Server   string         `xml:"server,attr"`
}

func NewResponse() *Response {
	return &Response{
		Protocol: "3.0",
		Server:   "go-omaha",
		DayStart: DayStart{ElapsedSeconds: "0"},
	}
}

type DayStart struct {
	ElapsedSeconds string `xml:"elapsed_seconds,attr"`
}

func (r *Response) AddApp(id string, status AppStatus) *AppResponse {
	a := &AppResponse{Id: id, Status: status}
	r.Apps = append(r.Apps, a)
	return a
}

type AppResponse struct {
	Ping        *PingResponse    `xml:"ping"`
	UpdateCheck *UpdateResponse  `xml:"updatecheck"`
	Events      []*EventResponse `xml:"event" json:",omitempty"`
	Id          string           `xml:"appid,attr,omitempty"`
	Status      AppStatus        `xml:"status,attr,omitempty"`
}

func (a *AppResponse) AddUpdateCheck(status UpdateStatus) *UpdateResponse {
	a.UpdateCheck = &UpdateResponse{Status: status}
	return a.UpdateCheck
}

func (a *AppResponse) AddPing() *PingResponse {
	a.Ping = &PingResponse{"ok"}
	return a.Ping
}

func (a *AppResponse) AddEvent() *EventResponse {
	event := &EventResponse{"ok"}
	a.Events = append(a.Events, event)
	return event
}

type UpdateResponse struct {
	URLs     []*URL       `xml:"urls>url" json:",omitempty"`
	Manifest *Manifest    `xml:"manifest"`
	Status   UpdateStatus `xml:"status,attr,omitempty"`
}

func (u *UpdateResponse) AddURL(codebase string) *URL {
	url := &URL{CodeBase: codebase}
	u.URLs = append(u.URLs, url)
	return url
}

func (u *UpdateResponse) AddManifest(version string) *Manifest {
	u.Manifest = &Manifest{Version: version}
	return u.Manifest
}

type PingResponse struct {
	Status string `xml:"status,attr"` // Always "ok".
}

type EventResponse struct {
	Status string `xml:"status,attr"` // Always "ok".
}

type OS struct {
	Platform    string `xml:"platform,attr,omitempty"`
	Version     string `xml:"version,attr,omitempty"`
	ServicePack string `xml:"sp,attr,omitempty"`
	Arch        string `xml:"arch,attr,omitempty"`
}

type Event struct {
	Type            EventType   `xml:"eventtype,attr"`
	Result          EventResult `xml:"eventresult,attr"`
	PreviousVersion string      `xml:"previousversion,attr,omitempty"`
	ErrorCode       string      `xml:"errorcode,attr,omitempty"`
	Status          string      `xml:"status,attr,omitempty"`
}

type URL struct {
	CodeBase string `xml:"codebase,attr"`
}

type Manifest struct {
	Packages []*Package `xml:"packages>package"`
	Actions  []*Action  `xml:"actions>action"`
	Version  string     `xml:"version,attr"`
}

func (m *Manifest) AddPackage() *Package {
	p := &Package{}
	m.Packages = append(m.Packages, p)
	return p
}

func (m *Manifest) AddPackageFromPath(path string) (*Package, error) {
	p := &Package{}
	if err := p.FromPath(path); err != nil {
		return nil, err
	}
	m.Packages = append(m.Packages, p)
	return p, nil
}

func (m *Manifest) AddAction(event string) *Action {
	a := &Action{Event: event}
	m.Actions = append(m.Actions, a)
	return a
}

type Action struct {
	Event string `xml:"event,attr"`

	// update engine extensions for event="postinstall"
	DisplayVersion        string `xml:"DisplayVersion,attr,omitempty"`
	Sha256                string `xml:"sha256,attr,omitempty"`
	NeedsAdmin            bool   `xml:"needsadmin,attr,omitempty"`
	IsDeltaPayload        bool   `xml:"IsDeltaPayload,attr,omitempty"`
	DisablePayloadBackoff bool   `xml:"DisablePayloadBackoff,attr,omitempty"`
	MaxFailureCountPerURL uint   `xml:"MaxFailureCountPerUrl,attr,omitempty"`
	MetadataSignatureRsa  string `xml:"MetadataSignatureRsa,attr,omitempty"`
	MetadataSize          string `xml:"MetadataSize,attr,omitempty"`
	Deadline              string `xml:"deadline,attr,omitempty"`
	MoreInfo              string `xml:"MoreInfo,attr,omitempty"`
	Prompt                bool   `xml:"Prompt,attr,omitempty"`
}
<commit_msg>protocol: sort xml attributes<commit_after>// Copyright 2013-2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Google's Omaha application update protocol, version 3.
//
// Omaha is a poll based protocol using XML. Requests are made by clients to
// check for updates or report events of an update process. Responses are given
// by the server to provide update information, if any, or to simply
// acknowledge the receipt of event status.
//
// https://github.com/google/omaha/blob/master/doc/ServerProtocolV3.md
package omaha

import (
	"encoding/xml"
)

// Request sent by the Omaha client
type Request struct {
	XMLName       xml.Name      `xml:"request" json:"-"`
	OS            *OS           `xml:"os"`
	Apps          []*AppRequest `xml:"app"`
	Protocol      string        `xml:"protocol,attr"`
	InstallSource string        `xml:"installsource,attr,omitempty"`
	IsMachine     string        `xml:"ismachine,attr,omitempty"`
	RequestId     string        `xml:"requestid,attr,omitempty"`
	SessionId     string        `xml:"sessionid,attr,omitempty"`
	TestSource    string        `xml:"testsource,attr,omitempty"`
	UserId        string        `xml:"userid,attr,omitempty"`
	Version       string        `xml:"version,attr,omitempty"`

	// update engine extension, duplicates the version attribute.
	UpdaterVersion string `xml:"updaterversion,attr,omitempty"`
}

func NewRequest() *Request {
	return &Request{
		Protocol: "3.0",
		// TODO(marineam) set a default client Version
		OS: &OS{
			Platform: LocalPlatform(),
			Arch:     LocalArch(),
			// TODO(marineam): Version and ServicePack
		},
	}
}

func (r *Request) AddApp(id, version string) *AppRequest {
	a := &AppRequest{Id: id, Version: version}
	r.Apps = append(r.Apps, a)
	return a
}

type AppRequest struct {
	Ping        *PingRequest    `xml:"ping"`
	UpdateCheck *UpdateRequest  `xml:"updatecheck"`
	Events      []*EventRequest `xml:"event" json:",omitempty"`
	Id          string          `xml:"appid,attr,omitempty"`
	Client      string          `xml:"client,attr,omitempty"`
	InstallAge  string          `xml:"installage,attr,omitempty"`
	Lang        string          `xml:"lang,attr,omitempty"`
	NextVersion string          `xml:"nextversion,attr,omitempty"`
	Version     string          `xml:"version,attr,omitempty"`

	// update engine extensions
	Board     string `xml:"board,attr,omitempty"`
	DeltaOK   bool   `xml:"delta_okay,attr,omitempty"`
	FromTrack string `xml:"from_track,attr,omitempty"`
	Track     string `xml:"track,attr,omitempty"`

	// coreos update engine extensions
	AlephVersion string `xml:"alephversion,attr,omitempty"`
	BootId       string `xml:"bootid,attr,omitempty"`
	MachineID    string `xml:"machineid,attr,omitempty"`
	OEM          string `xml:"oem,attr,omitempty"`
	OEMVersion   string `xml:"oemversion,attr,omitempty"`
}

func (a *AppRequest) AddUpdateCheck() *UpdateRequest {
	a.UpdateCheck = &UpdateRequest{}
	return a.UpdateCheck
}

func (a *AppRequest) AddPing() *PingRequest {
	a.Ping = &PingRequest{Active: 1}
	return a.Ping
}

func (a *AppRequest) AddEvent() *EventRequest {
	event := &EventRequest{}
	a.Events = append(a.Events, event)
	return event
}

type UpdateRequest struct {
	TargetVersionPrefix string `xml:"targetversionprefix,attr,omitempty"`
}

type PingRequest struct {
	Active               int  `xml:"active,attr,omitempty"`
	LastActiveReportDays *int `xml:"a,attr,omitempty"`
	LastReportDays       int  `xml:"r,attr,omitempty"`
}

type EventRequest struct {
	Type            EventType   `xml:"eventtype,attr"`
	Result          EventResult `xml:"eventresult,attr"`
	ErrorCode       string      `xml:"errorcode,attr,omitempty"`
	NextVersion     string      `xml:"nextversion,attr,omitempty"`
	PreviousVersion string      `xml:"previousversion,attr,omitempty"`
}

// Response sent by the Omaha server
type Response struct {
	XMLName  xml.Name       `xml:"response" json:"-"`
	DayStart DayStart       `xml:"daystart"`
	Apps     []*AppResponse `xml:"app"`
	Protocol string         `xml:"protocol,attr"`
	Server   string         `xml:"server,attr"`
}

func NewResponse() *Response {
	return &Response{
		Protocol: "3.0",
		Server:   "go-omaha",
		DayStart: DayStart{ElapsedSeconds: "0"},
	}
}

type DayStart struct {
	ElapsedSeconds string `xml:"elapsed_seconds,attr"`
}

func (r *Response) AddApp(id string, status AppStatus) *AppResponse {
	a := &AppResponse{Id: id, Status: status}
	r.Apps = append(r.Apps, a)
	return a
}

type AppResponse struct {
	Ping        *PingResponse    `xml:"ping"`
	UpdateCheck *UpdateResponse  `xml:"updatecheck"`
	Events      []*EventResponse `xml:"event" json:",omitempty"`
	Id          string           `xml:"appid,attr,omitempty"`
	Status      AppStatus        `xml:"status,attr,omitempty"`
}

func (a *AppResponse) AddUpdateCheck(status UpdateStatus) *UpdateResponse {
	a.UpdateCheck = &UpdateResponse{Status: status}
	return a.UpdateCheck
}

func (a *AppResponse) AddPing() *PingResponse {
	a.Ping = &PingResponse{"ok"}
	return a.Ping
}

func (a *AppResponse) AddEvent() *EventResponse {
	event := &EventResponse{"ok"}
	a.Events = append(a.Events, event)
	return event
}

type UpdateResponse struct {
	URLs     []*URL       `xml:"urls>url" json:",omitempty"`
	Manifest *Manifest    `xml:"manifest"`
	Status   UpdateStatus `xml:"status,attr,omitempty"`
}

func (u *UpdateResponse) AddURL(codebase string) *URL {
	url := &URL{CodeBase: codebase}
	u.URLs = append(u.URLs, url)
	return url
}

func (u *UpdateResponse) AddManifest(version string) *Manifest {
	u.Manifest = &Manifest{Version: version}
	return u.Manifest
}

type PingResponse struct {
	Status string `xml:"status,attr"` // Always "ok".
}

type EventResponse struct {
	Status string `xml:"status,attr"` // Always "ok".
}

type OS struct {
	Platform    string `xml:"platform,attr,omitempty"`
	Version     string `xml:"version,attr,omitempty"`
	ServicePack string `xml:"sp,attr,omitempty"`
	Arch        string `xml:"arch,attr,omitempty"`
}

type Event struct {
	Type            EventType   `xml:"eventtype,attr"`
	Result          EventResult `xml:"eventresult,attr"`
	PreviousVersion string      `xml:"previousversion,attr,omitempty"`
	ErrorCode       string      `xml:"errorcode,attr,omitempty"`
	Status          string      `xml:"status,attr,omitempty"`
}

type URL struct {
	CodeBase string `xml:"codebase,attr"`
}

type Manifest struct {
	Packages []*Package `xml:"packages>package"`
	Actions  []*Action  `xml:"actions>action"`
	Version  string     `xml:"version,attr"`
}

func (m *Manifest) AddPackage() *Package {
	p := &Package{}
	m.Packages = append(m.Packages, p)
	return p
}

func (m *Manifest) AddPackageFromPath(path string) (*Package, error) {
	p := &Package{}
	if err := p.FromPath(path); err != nil {
		return nil, err
	}
	m.Packages = append(m.Packages, p)
	return p, nil
}

func (m *Manifest) AddAction(event string) *Action {
	a := &Action{Event: event}
	m.Actions = append(m.Actions, a)
	return a
}

type Action struct {
	Event string `xml:"event,attr"`

	// update engine extensions for event="postinstall"
	DisplayVersion        string `xml:"DisplayVersion,attr,omitempty"`
	Sha256                string `xml:"sha256,attr,omitempty"`
	NeedsAdmin            bool   `xml:"needsadmin,attr,omitempty"`
	IsDeltaPayload        bool   `xml:"IsDeltaPayload,attr,omitempty"`
	DisablePayloadBackoff bool   `xml:"DisablePayloadBackoff,attr,omitempty"`
	MaxFailureCountPerURL uint   `xml:"MaxFailureCountPerUrl,attr,omitempty"`
	MetadataSignatureRsa  string `xml:"MetadataSignatureRsa,attr,omitempty"`
	MetadataSize          string `xml:"MetadataSize,attr,omitempty"`
	Deadline              string `xml:"deadline,attr,omitempty"`
	MoreInfo              string `xml:"MoreInfo,attr,omitempty"`
	Prompt                bool   `xml:"Prompt,attr,omitempty"`
}
<|endoftext|>


<commit_before>package url

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"github.com/dimiro1/health"
)

func Test_Checker_Check_Up(t *testing.T) {
	mux := http.NewServeMux()

	server := httptest.NewServer(mux)

	checker := NewChecker(fmt.Sprintf("%s/up/", server.URL))

	handler := health.NewHandler()
	handler.AddChecker("Up", checker)

	mux.Handle("/health/", handler)
	mux.HandleFunc("/up/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "UP")
	})

	resp, _ := http.Get(fmt.Sprintf("%s/health/", server.URL))

	wants := `{"Up":{"code":200,"status":"UP"},"status":"UP"}`

	check(t, resp, wants, http.StatusOK)
}

func Test_Checker_Check_Down(t *testing.T) {
	mux := http.NewServeMux()

	server := httptest.NewServer(mux)

	checker := NewChecker(fmt.Sprintf("%s/down/", server.URL))

	handler := health.NewHandler()
	handler.AddChecker("Down", checker)

	mux.Handle("/health/", handler)
	mux.HandleFunc("/down/", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Down")
	})

	resp, _ := http.Get(fmt.Sprintf("%s/health/", server.URL))

	wants := `{"Down":{"code":500,"status":"DOWN"},"status":"DOWN"}`

	check(t, resp, wants, http.StatusServiceUnavailable)
}

func Test_Checker_Check_Down_invalid(t *testing.T) {
	mux := http.NewServeMux()

	server := httptest.NewServer(mux)

	checker := NewChecker("")

	handler := health.NewHandler()
	handler.AddChecker("Down", checker)

	mux.Handle("/health/", handler)

	resp, _ := http.Get(fmt.Sprintf("%s/health/", server.URL))

	wants := `{"Down":{"code":400,"status":"DOWN"},"status":"DOWN"}`
	check(t, resp, wants, http.StatusServiceUnavailable)
}

func check(t *testing.T, resp *http.Response, wants string, code int) {
	jsonbytes, _ := ioutil.ReadAll(resp.Body)
	jsonstring := strings.TrimSpace(string(jsonbytes))

	if jsonstring != wants {
		t.Errorf("jsonstring == %s, wants %s", jsonstring, wants)
	}

	contentType := resp.Header.Get("Content-Type")
	wants = "application/json"

	if contentType != wants {
		t.Errorf("type == %s, wants %s", contentType, wants)
	}

	if resp.StatusCode != code {
		t.Errorf("resp.StatusCode == %d, wants %d", resp.StatusCode, code)
	}
}
<commit_msg>Added test for NewCheckerWithTimeout<commit_after>package url

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/dimiro1/health"
)

func Test_NewCheckerWithTimeout(t *testing.T) {
	timeout := 2 * time.Second
	url := "http://www.google.com/"

	c := NewCheckerWithTimeout(url, timeout)

	if c.Timeout != timeout {
		t.Errorf("NewCheckerWithTimeout().Timeout == %d, wants %d", c.Timeout, timeout)
	}

	if c.URL != url {
		t.Errorf("NewCheckerWithTimeout().URL == %d, wants %d", c.URL, url)
	}
}

func Test_Checker_Check_Up(t *testing.T) {
	mux := http.NewServeMux()

	server := httptest.NewServer(mux)

	checker := NewChecker(fmt.Sprintf("%s/up/", server.URL))

	handler := health.NewHandler()
	handler.AddChecker("Up", checker)

	mux.Handle("/health/", handler)
	mux.HandleFunc("/up/", func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprintln(w, "UP")
	})

	resp, _ := http.Get(fmt.Sprintf("%s/health/", server.URL))

	wants := `{"Up":{"code":200,"status":"UP"},"status":"UP"}`

	check(t, resp, wants, http.StatusOK)
}

func Test_Checker_Check_Down(t *testing.T) {
	mux := http.NewServeMux()

	server := httptest.NewServer(mux)

	checker := NewChecker(fmt.Sprintf("%s/down/", server.URL))

	handler := health.NewHandler()
	handler.AddChecker("Down", checker)

	mux.Handle("/health/", handler)
	mux.HandleFunc("/down/", func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusInternalServerError)
		fmt.Fprintln(w, "Down")
	})

	resp, _ := http.Get(fmt.Sprintf("%s/health/", server.URL))

	wants := `{"Down":{"code":500,"status":"DOWN"},"status":"DOWN"}`

	check(t, resp, wants, http.StatusServiceUnavailable)
}

func Test_Checker_Check_Down_invalid(t *testing.T) {
	mux := http.NewServeMux()

	server := httptest.NewServer(mux)

	checker := NewChecker("")

	handler := health.NewHandler()
	handler.AddChecker("Down", checker)

	mux.Handle("/health/", handler)

	resp, _ := http.Get(fmt.Sprintf("%s/health/", server.URL))

	wants := `{"Down":{"code":400,"status":"DOWN"},"status":"DOWN"}`
	check(t, resp, wants, http.StatusServiceUnavailable)
}

func check(t *testing.T, resp *http.Response, wants string, code int) {
	jsonbytes, _ := ioutil.ReadAll(resp.Body)
	jsonstring := strings.TrimSpace(string(jsonbytes))

	if jsonstring != wants {
		t.Errorf("jsonstring == %s, wants %s", jsonstring, wants)
	}

	contentType := resp.Header.Get("Content-Type")
	wants = "application/json"

	if contentType != wants {
		t.Errorf("type == %s, wants %s", contentType, wants)
	}

	if resp.StatusCode != code {
		t.Errorf("resp.StatusCode == %d, wants %d", resp.StatusCode, code)
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package commitlog

import (
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/m3db/m3/src/dbnode/persist/fs"
	"github.com/m3db/m3/src/dbnode/ts"
	"github.com/m3db/m3x/context"
	"github.com/m3db/m3x/ident"
	xtime "github.com/m3db/m3x/time"

	"github.com/stretchr/testify/require"
)

func TestFiles(t *testing.T) {
	dir, err := ioutil.TempDir("", "commitlogs")
	require.NoError(t, err)
	defer os.RemoveAll(dir)

	createTestCommitLogFiles(t, dir, 10*time.Minute, 5)

	opts := NewOptions()
	opts = opts.SetFilesystemOptions(
		opts.FilesystemOptions().
			SetFilePathPrefix(dir),
	)
	files, err := Files(opts)
	require.NoError(t, err)
	require.Equal(t, 5, len(files))

	// Make sure its sorted
	var lastFileStart time.Time
	for _, file := range files {
		require.Equal(t, 10*time.Minute, file.Duration)
		require.Equal(t, int64(0), file.Index)
		require.True(t, strings.Contains(file.FilePath, dir))
		if lastFileStart.IsZero() {
			lastFileStart = file.Start
			continue
		}

		require.True(t, file.Start.After(lastFileStart))
	}
}

// createTestCommitLogFiles creates the specified number of commit log files
// on disk with the appropriate block size. Commit log files will be valid
// and contain readable metadata.
func createTestCommitLogFiles(
	t *testing.T, filePathPrefix string, blockSize time.Duration, numBlocks int) {
	require.True(t, numBlocks >= 2)

	var (
		nowLock = sync.RWMutex{}
		now     = time.Now().Truncate(blockSize)
		nowFn   = func() time.Time {
			nowLock.RLock()
			n := now
			nowLock.RUnlock()
			return n
		}
		setNowFn = func(t time.Time) {
			nowLock.Lock()
			now = t
			nowLock.Unlock()
		}
		opts = NewOptions().
			SetBlockSize(blockSize).
			SetClockOptions(NewOptions().ClockOptions().SetNowFn(nowFn)).
			SetFilesystemOptions(fs.NewOptions().SetFilePathPrefix(filePathPrefix))
		commitLogsDir = fs.CommitLogsDirPath(filePathPrefix)
	)

	commitLog, err := NewCommitLog(opts)
	require.NoError(t, err)
	require.NoError(t, commitLog.Open())
	series := Series{
		UniqueIndex: 0,
		Namespace:   ident.StringID("some-namespace"),
		ID:          ident.StringID("some-id"),
	}
	// Commit log writer is asynchronous and performs batching so getting the exact number
	// of files that we want is tricky. The implementation below loops infinitely, writing
	// a single datapoint and increasing the time after each iteration until numBlocks -1
	// files are on disk. After that, it terminates, and the final batch flush from calling
	// commitlog.Close() will generate the last file.
	for {
		files, err := fs.SortedCommitLogFiles(commitLogsDir)
		require.NoError(t, err)
		if len(files) == numBlocks-1 {
			break
		}
		err = commitLog.Write(context.NewContext(), series, ts.Datapoint{}, xtime.Second, nil)
		require.NoError(t, err)
		setNowFn(nowFn().Add(blockSize))
	}

	require.NoError(t, commitLog.Close())
	files, err := fs.SortedCommitLogFiles(commitLogsDir)
	require.NoError(t, err)
	require.Equal(t, numBlocks, len(files))
}
<commit_msg>Disable TestFiles until can be reliably ran (#828)<commit_after>// Copyright (c) 2018 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package commitlog

import (
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/m3db/m3/src/dbnode/persist/fs"
	"github.com/m3db/m3/src/dbnode/ts"
	"github.com/m3db/m3x/context"
	"github.com/m3db/m3x/ident"
	xtime "github.com/m3db/m3x/time"

	"github.com/stretchr/testify/require"
)

func TestFiles(t *testing.T) {
	// TODO(r): Find some time/people to help investigate this flakey test.
	t.Skip()

	dir, err := ioutil.TempDir("", "commitlogs")
	require.NoError(t, err)
	defer os.RemoveAll(dir)

	createTestCommitLogFiles(t, dir, 10*time.Minute, 5)

	opts := NewOptions()
	opts = opts.SetFilesystemOptions(
		opts.FilesystemOptions().
			SetFilePathPrefix(dir),
	)
	files, err := Files(opts)
	require.NoError(t, err)
	require.Equal(t, 5, len(files))

	// Make sure its sorted
	var lastFileStart time.Time
	for _, file := range files {
		require.Equal(t, 10*time.Minute, file.Duration)
		require.Equal(t, int64(0), file.Index)
		require.True(t, strings.Contains(file.FilePath, dir))
		if lastFileStart.IsZero() {
			lastFileStart = file.Start
			continue
		}

		require.True(t, file.Start.After(lastFileStart))
	}
}

// createTestCommitLogFiles creates the specified number of commit log files
// on disk with the appropriate block size. Commit log files will be valid
// and contain readable metadata.
func createTestCommitLogFiles(
	t *testing.T, filePathPrefix string, blockSize time.Duration, numBlocks int) {
	require.True(t, numBlocks >= 2)

	var (
		nowLock = sync.RWMutex{}
		now     = time.Now().Truncate(blockSize)
		nowFn   = func() time.Time {
			nowLock.RLock()
			n := now
			nowLock.RUnlock()
			return n
		}
		setNowFn = func(t time.Time) {
			nowLock.Lock()
			now = t
			nowLock.Unlock()
		}
		opts = NewOptions().
			SetBlockSize(blockSize).
			SetClockOptions(NewOptions().ClockOptions().SetNowFn(nowFn)).
			SetFilesystemOptions(fs.NewOptions().SetFilePathPrefix(filePathPrefix))
		commitLogsDir = fs.CommitLogsDirPath(filePathPrefix)
	)

	commitLog, err := NewCommitLog(opts)
	require.NoError(t, err)
	require.NoError(t, commitLog.Open())
	series := Series{
		UniqueIndex: 0,
		Namespace:   ident.StringID("some-namespace"),
		ID:          ident.StringID("some-id"),
	}
	// Commit log writer is asynchronous and performs batching so getting the exact number
	// of files that we want is tricky. The implementation below loops infinitely, writing
	// a single datapoint and increasing the time after each iteration until numBlocks -1
	// files are on disk. After that, it terminates, and the final batch flush from calling
	// commitlog.Close() will generate the last file.
	for {
		files, err := fs.SortedCommitLogFiles(commitLogsDir)
		require.NoError(t, err)
		if len(files) == numBlocks-1 {
			break
		}
		err = commitLog.Write(context.NewContext(), series, ts.Datapoint{}, xtime.Second, nil)
		require.NoError(t, err)
		setNowFn(nowFn().Add(blockSize))
	}

	require.NoError(t, commitLog.Close())
	files, err := fs.SortedCommitLogFiles(commitLogsDir)
	require.NoError(t, err)
	require.Equal(t, numBlocks, len(files))
}
<|endoftext|>


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

import (
	"fmt"
	"strconv"
	"time"

	"github.com/aliyun/aliyun-cli/cli"
	"github.com/aliyun/aliyun-cli/i18n"
)

var WaiterFlag = &cli.Flag{Category: "helper",
	Name:         "waiter",
	AssignedMode: cli.AssignedRepeatable,
	Short: i18n.T(
		"use `--waiter expr=<jmesPath> to=<value>` to pull api until result equal to expected value",
		"使用 `--waiter expr=<jmesPath> to=<value>` 来轮询调用OpenAPI，知道返回期望的值"),
	Long: i18n.T(
		"",
		""),
	Fields: []cli.Field{
		{Key: "expr", Required: true, Short: i18n.T("", "")},
		{Key: "to", Required: true, Short: i18n.T("", "")},
		{Key: "timeout", DefaultValue: "180", Short: i18n.T("", "")},
		{Key: "interval", DefaultValue: "5", Short: i18n.T("", "")},
	},
	ExcludeWith: []string{"pager"},
}

type Waiter struct {
	expr string
	to   string
	//	timeout  time.Duration	TODO use Flag.Field to validate
	//	interval time.Duration  TODO use Flag.Field to validate
}

func GetWaiter() *Waiter {
	if !WaiterFlag.IsAssigned() {
		return nil
	}

	waiter := &Waiter{}
	waiter.expr, _ = WaiterFlag.GetFieldValue("expr")
	waiter.to, _ = WaiterFlag.GetFieldValue("to")
	//waiter.timeout = time.Duration(time.Second * 180)
	//waiter.interval = time.Duration(time.Second * 5)

	return waiter
}

func (a *Waiter) CallWith(invoker Invoker) (string, error) {
	//
	// timeout is 1-600 seconds, default is 180
	timeout := time.Duration(time.Second * 180)
	if s, ok := WaiterFlag.GetFieldValue("timeout"); ok {
		if n, err := strconv.Atoi(s); err == nil {
			if n <= 0 && n > 600 {
				return "", fmt.Errorf("--waiter timeout=%s must between 1-600 (seconds)", s)
			}
			timeout = time.Duration(time.Second * time.Duration(n))
		} else {
			return "", fmt.Errorf("--waiter timeout=%s must be integer", s)
		}
	}
	//
	// interval is 2-10 seconds, default is 5
	interval := time.Duration(time.Second * 5)
	if s, ok := WaiterFlag.GetFieldValue("interval"); ok {
		if n, err := strconv.Atoi(s); err == nil {
			if n <= 1 && n > 10 {
				return "", fmt.Errorf("--waiter interval=%s must between 2-10 (seconds)", s)
			}
			interval = time.Duration(time.Second * time.Duration(n))
		} else {
			return "", fmt.Errorf("--waiter interval=%s must be integer", s)
		}
	}

	begin := time.Now()
	for {
		resp, err := invoker.Call()
		if err != nil {
			return "", err
		}

		v, err := evaluateExpr(resp.GetHttpContentBytes(), a.expr)
		if err != nil {
			return "", err
		}

		if v == a.to {
			return resp.GetHttpContentString(), nil
		}
		duration := time.Now().Sub(begin)
		if duration > timeout {
			return "", fmt.Errorf("wait '%s' to '%s' timeout(%dseconds), last='%s'",
				a.expr, a.to, timeout/time.Second, v)
		}
		time.Sleep(interval)
	}
}
<commit_msg>修复拼写错误<commit_after>// Copyright (c) 2009-present, Alibaba Cloud All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package openapi

import (
	"fmt"
	"strconv"
	"time"

	"github.com/aliyun/aliyun-cli/cli"
	"github.com/aliyun/aliyun-cli/i18n"
)

var WaiterFlag = &cli.Flag{Category: "helper",
	Name:         "waiter",
	AssignedMode: cli.AssignedRepeatable,
	Short: i18n.T(
		"use `--waiter expr=<jmesPath> to=<value>` to pull api until result equal to expected value",
		"使用 `--waiter expr=<jmesPath> to=<value>` 来轮询调用OpenAPI，直到返回期望的值"),
	Long: i18n.T(
		"",
		""),
	Fields: []cli.Field{
		{Key: "expr", Required: true, Short: i18n.T("", "")},
		{Key: "to", Required: true, Short: i18n.T("", "")},
		{Key: "timeout", DefaultValue: "180", Short: i18n.T("", "")},
		{Key: "interval", DefaultValue: "5", Short: i18n.T("", "")},
	},
	ExcludeWith: []string{"pager"},
}

type Waiter struct {
	expr string
	to   string
	//	timeout  time.Duration	TODO use Flag.Field to validate
	//	interval time.Duration  TODO use Flag.Field to validate
}

func GetWaiter() *Waiter {
	if !WaiterFlag.IsAssigned() {
		return nil
	}

	waiter := &Waiter{}
	waiter.expr, _ = WaiterFlag.GetFieldValue("expr")
	waiter.to, _ = WaiterFlag.GetFieldValue("to")
	//waiter.timeout = time.Duration(time.Second * 180)
	//waiter.interval = time.Duration(time.Second * 5)

	return waiter
}

func (a *Waiter) CallWith(invoker Invoker) (string, error) {
	//
	// timeout is 1-600 seconds, default is 180
	timeout := time.Duration(time.Second * 180)
	if s, ok := WaiterFlag.GetFieldValue("timeout"); ok {
		if n, err := strconv.Atoi(s); err == nil {
			if n <= 0 && n > 600 {
				return "", fmt.Errorf("--waiter timeout=%s must between 1-600 (seconds)", s)
			}
			timeout = time.Duration(time.Second * time.Duration(n))
		} else {
			return "", fmt.Errorf("--waiter timeout=%s must be integer", s)
		}
	}
	//
	// interval is 2-10 seconds, default is 5
	interval := time.Duration(time.Second * 5)
	if s, ok := WaiterFlag.GetFieldValue("interval"); ok {
		if n, err := strconv.Atoi(s); err == nil {
			if n <= 1 && n > 10 {
				return "", fmt.Errorf("--waiter interval=%s must between 2-10 (seconds)", s)
			}
			interval = time.Duration(time.Second * time.Duration(n))
		} else {
			return "", fmt.Errorf("--waiter interval=%s must be integer", s)
		}
	}

	begin := time.Now()
	for {
		resp, err := invoker.Call()
		if err != nil {
			return "", err
		}

		v, err := evaluateExpr(resp.GetHttpContentBytes(), a.expr)
		if err != nil {
			return "", err
		}

		if v == a.to {
			return resp.GetHttpContentString(), nil
		}
		duration := time.Now().Sub(begin)
		if duration > timeout {
			return "", fmt.Errorf("wait '%s' to '%s' timeout(%dseconds), last='%s'",
				a.expr, a.to, timeout/time.Second, v)
		}
		time.Sleep(interval)
	}
}
<|endoftext|>


<commit_before>package consul

import (
	"errors"
	"net/url"
	"strings"

	"github.com/x-cray/marathon-service-registrator/types"

	log "github.com/Sirupsen/logrus"
	consulAPI "github.com/hashicorp/consul/api"
)

type consulAdapter struct {
	client *consulAPI.Client
	dryRun bool
}

func New(uri *url.URL, dryRun bool) (*consulAdapter, error) {
	config := consulAPI.DefaultConfig()
	config.Address = uri.Host
	config.Scheme = uri.Scheme

	log.WithField("prefix", "consul").Infof("Connecting to Consul at %v", uri)
	client, err := consulAPI.NewClient(config)
	if err != nil {
		return nil, err
	}

	return &consulAdapter{
		client: client,
		dryRun: dryRun,
	}, nil
}

// Ping will try to connect to consul by attempting to retrieve the current leader.
func (r *consulAdapter) Ping() error {
	status := r.client.Status()
	leader, err := status.Leader()
	if err != nil {
		return err
	}
	log.WithField("prefix", "consul").Debugf("Current leader %s", leader)

	return nil
}

func (r *consulAdapter) Register(group *types.ServiceGroup) error {
	for _, service := range group.Services {
		if r.dryRun {
			log.WithFields(log.Fields{
				"prefix": "consul",
				"ip":     group.IP,
				"name":   service.Name,
				"port":   service.ExposedPort,
			}).Info("[dry-run] Would register service")
			continue
		}

		log.WithFields(log.Fields{
			"prefix": "consul",
			"ip":     group.IP,
			"name":   service.Name,
			"port":   service.ExposedPort,
		}).Info("Registering service")

		registration := new(consulAPI.AgentServiceRegistration)
		registration.Address = group.IP
		registration.ID = service.ID
		registration.Name = service.Name
		registration.Tags = service.Tags
		registration.Port = service.ExposedPort

		err := r.client.Agent().ServiceRegister(registration)
		if err != nil {
			return err
		}
	}

	return nil
}

func (r *consulAdapter) Deregister(group *types.ServiceGroup) error {
	for _, service := range group.Services {
		if r.dryRun {
			log.WithFields(log.Fields{
				"prefix": "consul",
				"ip":     group.IP,
				"id":     service.ID,
				"name":   service.Name,
				"port":   service.ExposedPort,
			}).Info("[dry-run] Would deregister service")
			continue
		}

		log.WithFields(log.Fields{
			"prefix": "consul",
			"ip":     group.IP,
			"id":     service.ID,
			"name":   service.Name,
			"port":   service.ExposedPort,
		}).Info("Deregistering service")

		err := r.client.Agent().ServiceDeregister(service.ID)
		if err != nil {
			return err
		}
	}

	return nil
}

func (r *consulAdapter) AdvertiseAddr() (string, error) {
	info, err := r.client.Agent().Self()
	if err != nil {
		return "", err
	}

	config := info["Config"]
	if config != nil {
		address, ok := config["AdvertiseAddr"].(string)
		if ok {
			return address, nil
		}
	}

	return "", errors.New("Advertized address was not found")
}

func groupID(serviceID string) string {
	i := strings.LastIndex(serviceID, ":")
	if i > 0 {
		return serviceID[:i]
	}

	return serviceID
}

func (r *consulAdapter) Services() ([]*types.ServiceGroup, error) {
	services, err := r.client.Agent().Services()
	if err != nil {
		return nil, err
	}

	out := make([]*types.ServiceGroup, len(services))
	i := 0
	for _, v := range services {
		group := &types.ServiceGroup{
			ID:   groupID(v.ID),
			IP:   v.Address,
			Services: []*types.Service{
				&types.Service{
					ID:          v.ID,
					Name:        v.Service,
					Tags:        v.Tags,
					ExposedPort: v.Port,
				},
			},
		}
		out[i] = group
		i++

		log.WithFields(log.Fields{
			"prefix": "consul",
			"id":     v.ID,
			"name":   v.Service,
			"ip":     v.Address,
			"port":   v.Port,
		}).Debugf("Service")
	}

	return out, nil
}
<commit_msg>Add service ID to registeration log message<commit_after>package consul

import (
	"errors"
	"net/url"
	"strings"

	"github.com/x-cray/marathon-service-registrator/types"

	log "github.com/Sirupsen/logrus"
	consulAPI "github.com/hashicorp/consul/api"
)

type consulAdapter struct {
	client *consulAPI.Client
	dryRun bool
}

func New(uri *url.URL, dryRun bool) (*consulAdapter, error) {
	config := consulAPI.DefaultConfig()
	config.Address = uri.Host
	config.Scheme = uri.Scheme

	log.WithField("prefix", "consul").Infof("Connecting to Consul at %v", uri)
	client, err := consulAPI.NewClient(config)
	if err != nil {
		return nil, err
	}

	return &consulAdapter{
		client: client,
		dryRun: dryRun,
	}, nil
}

// Ping will try to connect to consul by attempting to retrieve the current leader.
func (r *consulAdapter) Ping() error {
	status := r.client.Status()
	leader, err := status.Leader()
	if err != nil {
		return err
	}
	log.WithField("prefix", "consul").Debugf("Current leader %s", leader)

	return nil
}

func (r *consulAdapter) Register(group *types.ServiceGroup) error {
	for _, service := range group.Services {
		if r.dryRun {
			log.WithFields(log.Fields{
				"prefix": "consul",
				"ip":     group.IP,
				"id":     service.ID,
				"name":   service.Name,
				"port":   service.ExposedPort,
			}).Info("[dry-run] Would register service")
			continue
		}

		log.WithFields(log.Fields{
			"prefix": "consul",
			"ip":     group.IP,
			"id":     service.ID,
			"name":   service.Name,
			"port":   service.ExposedPort,
		}).Info("Registering service")

		registration := new(consulAPI.AgentServiceRegistration)
		registration.Address = group.IP
		registration.ID = service.ID
		registration.Name = service.Name
		registration.Tags = service.Tags
		registration.Port = service.ExposedPort

		err := r.client.Agent().ServiceRegister(registration)
		if err != nil {
			return err
		}
	}

	return nil
}

func (r *consulAdapter) Deregister(group *types.ServiceGroup) error {
	for _, service := range group.Services {
		if r.dryRun {
			log.WithFields(log.Fields{
				"prefix": "consul",
				"ip":     group.IP,
				"id":     service.ID,
				"name":   service.Name,
				"port":   service.ExposedPort,
			}).Info("[dry-run] Would deregister service")
			continue
		}

		log.WithFields(log.Fields{
			"prefix": "consul",
			"ip":     group.IP,
			"id":     service.ID,
			"name":   service.Name,
			"port":   service.ExposedPort,
		}).Info("Deregistering service")

		err := r.client.Agent().ServiceDeregister(service.ID)
		if err != nil {
			return err
		}
	}

	return nil
}

func (r *consulAdapter) AdvertiseAddr() (string, error) {
	info, err := r.client.Agent().Self()
	if err != nil {
		return "", err
	}

	config := info["Config"]
	if config != nil {
		address, ok := config["AdvertiseAddr"].(string)
		if ok {
			return address, nil
		}
	}

	return "", errors.New("Advertized address was not found")
}

func groupID(serviceID string) string {
	i := strings.LastIndex(serviceID, ":")
	if i > 0 {
		return serviceID[:i]
	}

	return serviceID
}

func (r *consulAdapter) Services() ([]*types.ServiceGroup, error) {
	services, err := r.client.Agent().Services()
	if err != nil {
		return nil, err
	}

	out := make([]*types.ServiceGroup, len(services))
	i := 0
	for _, v := range services {
		group := &types.ServiceGroup{
			ID:   groupID(v.ID),
			IP:   v.Address,
			Services: []*types.Service{
				&types.Service{
					ID:          v.ID,
					Name:        v.Service,
					Tags:        v.Tags,
					ExposedPort: v.Port,
				},
			},
		}
		out[i] = group
		i++

		log.WithFields(log.Fields{
			"prefix": "consul",
			"id":     v.ID,
			"name":   v.Service,
			"ip":     v.Address,
			"port":   v.Port,
		}).Debugf("Service")
	}

	return out, nil
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package kvm

// This file contains wrappers around the following executables:
//   uvt-simplestreams-libvirt
//   uvt-kvm
//   virsh
// Those executables are found in the following packages:
//   uvtool-libvirt
//   libvirt-bin
//
// These executables provide Juju's interface to dealing with kvm containers.
// The define how we start, stop and list running containers on the host

import (
	"fmt"
	"regexp"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/juju/network"
	"github.com/juju/utils"
)

var (
	// The regular expression for breaking up the results of 'virsh list'
	// (?m) - specify that this is a multiline regex
	// first part is the opaque identifier we don't care about
	// then the hostname, and lastly the status.
	machineListPattern = regexp.MustCompile(`(?m)^\s+\d+\s+(?P<hostname>[-\w]+)\s+(?P<status>.+)\s*$`)
)

// run the command and return the combined output.
func run(command string, args ...string) (output string, err error) {
	logger.Tracef("%s %v", command, args)
	output, err = utils.RunCommand(command, args...)
	logger.Tracef("output: %v", output)
	return output, err
}

// SyncImages updates the local cached images by reading the simplestreams
// data and downloading the cloud images to the uvtool pool (used by libvirt).
func SyncImages(series, arch, source string) error {

	args := []string{
		"sync",
		fmt.Sprintf("arch=%s", arch),
		fmt.Sprintf("release=%s", series),
	}

	if source != "" {
		args = append(args, fmt.Sprintf("--source=%s", source))
	}

	_, err := run("uvt-simplestreams-libvirt", args...)
	return err
}

type CreateMachineParams struct {
	Hostname      string
	Series        string
	Arch          string
	UserDataFile  string
	NetworkBridge string
	Memory        uint64
	CpuCores      uint64
	RootDisk      uint64
	Interfaces    []*network.InterfaceInfo
}

// CreateMachine creates a virtual machine and starts it.
func CreateMachine(params CreateMachineParams) error {
	if params.Hostname == "" {
		return fmt.Errorf("Hostname is required")
	}
	args := []string{
		"create",
		"--log-console-output", // do wonder where this goes...
	}
	if params.UserDataFile != "" {
		args = append(args, "--user-data", params.UserDataFile)
	}
	if params.NetworkBridge != "" {
		args = append(args, "--bridge", params.NetworkBridge)
	}
	if params.Memory != 0 {
		args = append(args, "--memory", fmt.Sprint(params.Memory))
	}
	if params.CpuCores != 0 {
		args = append(args, "--cpu", fmt.Sprint(params.CpuCores))
	}
	if params.RootDisk != 0 {
		args = append(args, "--disk", fmt.Sprint(params.RootDisk))
	}
	if len(params.Interfaces) != 0 {
		somePath := ""
		err := WriteTemplate(somePath, params)
		if err != nil {
			return errors.Trace(err)
		}
		args = append(args, "--template", somePath)
	}

	args = append(args, params.Hostname)
	if params.Series != "" {
		args = append(args, fmt.Sprintf("release=%s", params.Series))
	}
	if params.Arch != "" {
		args = append(args, fmt.Sprintf("arch=%s", params.Arch))
	}
	output, err := run("uvt-kvm", args...)
	logger.Debugf("is this the logged output?:\n%s", output)
	return err
}

// DestroyMachine destroys the virtual machine identified by hostname.
func DestroyMachine(hostname string) error {
	_, err := run("uvt-kvm", "destroy", hostname)
	return err
}

// AutostartMachine indicates that the virtual machines should automatically
// restart when the host restarts.
func AutostartMachine(hostname string) error {
	_, err := run("virsh", "autostart", hostname)
	return err
}

// ListMachines returns a map of machine name to state, where state is one of:
// running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended.
func ListMachines() (map[string]string, error) {
	output, err := run("virsh", "-q", "list", "--all")
	if err != nil {
		return nil, err
	}
	// Split the output into lines.
	// Regex matching is the easiest way to match the lines.
	//   id hostname status
	// separated by whitespace, with whitespace at the start too.
	result := make(map[string]string)
	for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) {
		hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s)
		parts := strings.SplitN(string(hostnameAndStatus), " ", 2)
		result[parts[0]] = parts[1]
	}
	return result, nil
}
<commit_msg>Temporary directory creation for template generation<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package kvm

// This file contains wrappers around the following executables:
//   uvt-simplestreams-libvirt
//   uvt-kvm
//   virsh
// Those executables are found in the following packages:
//   uvtool-libvirt
//   libvirt-bin
//
// These executables provide Juju's interface to dealing with kvm containers.
// The define how we start, stop and list running containers on the host

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/juju/errors"
	"github.com/juju/juju/network"
	"github.com/juju/utils"
)

var (
	// The regular expression for breaking up the results of 'virsh list'
	// (?m) - specify that this is a multiline regex
	// first part is the opaque identifier we don't care about
	// then the hostname, and lastly the status.
	machineListPattern = regexp.MustCompile(`(?m)^\s+\d+\s+(?P<hostname>[-\w]+)\s+(?P<status>.+)\s*$`)
)

// run the command and return the combined output.
func run(command string, args ...string) (output string, err error) {
	logger.Tracef("%s %v", command, args)
	output, err = utils.RunCommand(command, args...)
	logger.Tracef("output: %v", output)
	return output, err
}

// SyncImages updates the local cached images by reading the simplestreams
// data and downloading the cloud images to the uvtool pool (used by libvirt).
func SyncImages(series, arch, source string) error {

	args := []string{
		"sync",
		fmt.Sprintf("arch=%s", arch),
		fmt.Sprintf("release=%s", series),
	}

	if source != "" {
		args = append(args, fmt.Sprintf("--source=%s", source))
	}

	_, err := run("uvt-simplestreams-libvirt", args...)
	return err
}

type CreateMachineParams struct {
	Hostname      string
	Series        string
	Arch          string
	UserDataFile  string
	NetworkBridge string
	Memory        uint64
	CpuCores      uint64
	RootDisk      uint64
	Interfaces    []*network.InterfaceInfo
}

// CreateMachine creates a virtual machine and starts it.
func CreateMachine(params CreateMachineParams) error {
	if params.Hostname == "" {
		return fmt.Errorf("Hostname is required")
	}
	args := []string{
		"create",
		"--log-console-output", // do wonder where this goes...
	}
	if params.UserDataFile != "" {
		args = append(args, "--user-data", params.UserDataFile)
	}
	if params.NetworkBridge != "" {
		args = append(args, "--bridge", params.NetworkBridge)
	}
	if params.Memory != 0 {
		args = append(args, "--memory", fmt.Sprint(params.Memory))
	}
	if params.CpuCores != 0 {
		args = append(args, "--cpu", fmt.Sprint(params.CpuCores))
	}
	if params.RootDisk != 0 {
		args = append(args, "--disk", fmt.Sprint(params.RootDisk))
	}
	if len(params.Interfaces) != 0 {
		tempDir, err := ioutil.TempDir("", "kvm")
		if err != nil {
			return errors.Trace(err)
		}
		defer os.RemoveAll(tempDir)

		templatePath := filepath.Join(tempDir, "kvm.xml")
		err = WriteTemplate(templatePath, params)
		if err != nil {
			return errors.Trace(err)
		}
		args = append(args, "--template", templatePath)
	}

	args = append(args, params.Hostname)
	if params.Series != "" {
		args = append(args, fmt.Sprintf("release=%s", params.Series))
	}
	if params.Arch != "" {
		args = append(args, fmt.Sprintf("arch=%s", params.Arch))
	}
	output, err := run("uvt-kvm", args...)
	logger.Debugf("is this the logged output?:\n%s", output)
	return err
}

// DestroyMachine destroys the virtual machine identified by hostname.
func DestroyMachine(hostname string) error {
	_, err := run("uvt-kvm", "destroy", hostname)
	return err
}

// AutostartMachine indicates that the virtual machines should automatically
// restart when the host restarts.
func AutostartMachine(hostname string) error {
	_, err := run("virsh", "autostart", hostname)
	return err
}

// ListMachines returns a map of machine name to state, where state is one of:
// running, idle, paused, shutdown, shut off, crashed, dying, pmsuspended.
func ListMachines() (map[string]string, error) {
	output, err := run("virsh", "-q", "list", "--all")
	if err != nil {
		return nil, err
	}
	// Split the output into lines.
	// Regex matching is the easiest way to match the lines.
	//   id hostname status
	// separated by whitespace, with whitespace at the start too.
	result := make(map[string]string)
	for _, s := range machineListPattern.FindAllStringSubmatchIndex(output, -1) {
		hostnameAndStatus := machineListPattern.ExpandString(nil, "$hostname $status", output, s)
		parts := strings.SplitN(string(hostnameAndStatus), " ", 2)
		result[parts[0]] = parts[1]
	}
	return result, nil
}
<|endoftext|>