<commit_before>package swarm

import (
	"context"
	"fmt"
	"sync"
	"testing"
	"time"

	peer "github.com/libp2p/go-libp2p-peer"
)

func getMockDialFunc() (DialFunc, func(), context.Context, <-chan struct{}) {
	dfcalls := make(chan struct{}, 512) // buffer it large enough that we won't care
	dialctx, cancel := context.WithCancel(context.Background())
	ch := make(chan struct{})
	f := func(ctx context.Context, p peer.ID) (*Conn, error) {
		dfcalls <- struct{}{}
		defer cancel()
		select {
		case <-ch:
			return new(Conn), nil
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}

	o := new(sync.Once)

	return f, func() { o.Do(func() { close(ch) }) }, dialctx, dfcalls
}

func TestBasicDialSync(t *testing.T) {
	df, done, _, callsch := getMockDialFunc()

	dsync := NewDialSync(df)

	p := peer.ID("testpeer")

	ctx := context.Background()

	finished := make(chan struct{})
	go func() {
		_, err := dsync.DialLock(ctx, p)
		if err != nil {
			t.Error(err)
		}
		finished <- struct{}{}
	}()

	go func() {
		_, err := dsync.DialLock(ctx, p)
		if err != nil {
			t.Error(err)
		}
		finished <- struct{}{}
	}()

	// short sleep just to make sure we've moved around in the scheduler
	time.Sleep(time.Millisecond * 20)
	done()

	<-finished
	<-finished

	if len(callsch) > 1 {
		t.Fatal("should only have called dial func once!")
	}
}

func TestDialSyncCancel(t *testing.T) {
	df, done, _, dcall := getMockDialFunc()

	dsync := NewDialSync(df)

	p := peer.ID("testpeer")

	ctx1, cancel1 := context.WithCancel(context.Background())

	finished := make(chan struct{})
	go func() {
		_, err := dsync.DialLock(ctx1, p)
		if err != ctx1.Err() {
			t.Error("should have gotten context error")
		}
		finished <- struct{}{}
	}()

	// make sure the above makes it through the wait code first
	select {
	case <-dcall:
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for dial to start")
	}

	// Add a second dialwait in so two actors are waiting on the same dial
	go func() {
		_, err := dsync.DialLock(context.Background(), p)
		if err != nil {
			t.Error(err)
		}
		finished <- struct{}{}
	}()

	time.Sleep(time.Millisecond * 20)

	// cancel the first dialwait, it should not affect the second at all
	cancel1()
	select {
	case <-finished:
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for wait to exit")
	}

	// short sleep just to make sure we've moved around in the scheduler
	time.Sleep(time.Millisecond * 20)
	done()

	<-finished
}

func TestDialSyncAllCancel(t *testing.T) {
	df, done, dctx, _ := getMockDialFunc()

	dsync := NewDialSync(df)

	p := peer.ID("testpeer")

	ctx1, cancel1 := context.WithCancel(context.Background())

	finished := make(chan struct{})
	go func() {
		_, err := dsync.DialLock(ctx1, p)
		if err != ctx1.Err() {
			t.Error("should have gotten context error")
		}
		finished <- struct{}{}
	}()

	// Add a second dialwait in so two actors are waiting on the same dial
	go func() {
		_, err := dsync.DialLock(ctx1, p)
		if err != ctx1.Err() {
			t.Error("should have gotten context error")
		}
		finished <- struct{}{}
	}()

	cancel1()
	for i := 0; i < 2; i++ {
		select {
		case <-finished:
		case <-time.After(time.Second):
			t.Fatal("timed out waiting for wait to exit")
		}
	}

	// the dial should have exited now
	select {
	case <-dctx.Done():
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for dial to return")
	}

	// should be able to successfully dial that peer again
	done()
	_, err := dsync.DialLock(context.Background(), p)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFailFirst(t *testing.T) {
	var count int
	f := func(ctx context.Context, p peer.ID) (*Conn, error) {
		if count > 0 {
			return new(Conn), nil
		}
		count++
		return nil, fmt.Errorf("gophers ate the modem")
	}

	ds := NewDialSync(f)

	p := peer.ID("testing")

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()

	_, err := ds.DialLock(ctx, p)
	if err == nil {
		t.Fatal("expected gophers to have eaten the modem")
	}

	c, err := ds.DialLock(ctx, p)
	if err != nil {
		t.Fatal(err)
	}

	if c == nil {
		t.Fatal("should have gotten a 'real' conn back")
	}
}
<commit_msg>add a test to stress the lock order race condition<commit_after>package swarm

import (
	"context"
	"fmt"
	"sync"
	"testing"
	"time"

	peer "github.com/libp2p/go-libp2p-peer"
)

func getMockDialFunc() (DialFunc, func(), context.Context, <-chan struct{}) {
	dfcalls := make(chan struct{}, 512) // buffer it large enough that we won't care
	dialctx, cancel := context.WithCancel(context.Background())
	ch := make(chan struct{})
	f := func(ctx context.Context, p peer.ID) (*Conn, error) {
		dfcalls <- struct{}{}
		defer cancel()
		select {
		case <-ch:
			return new(Conn), nil
		case <-ctx.Done():
			return nil, ctx.Err()
		}
	}

	o := new(sync.Once)

	return f, func() { o.Do(func() { close(ch) }) }, dialctx, dfcalls
}

func TestBasicDialSync(t *testing.T) {
	df, done, _, callsch := getMockDialFunc()

	dsync := NewDialSync(df)

	p := peer.ID("testpeer")

	ctx := context.Background()

	finished := make(chan struct{})
	go func() {
		_, err := dsync.DialLock(ctx, p)
		if err != nil {
			t.Error(err)
		}
		finished <- struct{}{}
	}()

	go func() {
		_, err := dsync.DialLock(ctx, p)
		if err != nil {
			t.Error(err)
		}
		finished <- struct{}{}
	}()

	// short sleep just to make sure we've moved around in the scheduler
	time.Sleep(time.Millisecond * 20)
	done()

	<-finished
	<-finished

	if len(callsch) > 1 {
		t.Fatal("should only have called dial func once!")
	}
}

func TestDialSyncCancel(t *testing.T) {
	df, done, _, dcall := getMockDialFunc()

	dsync := NewDialSync(df)

	p := peer.ID("testpeer")

	ctx1, cancel1 := context.WithCancel(context.Background())

	finished := make(chan struct{})
	go func() {
		_, err := dsync.DialLock(ctx1, p)
		if err != ctx1.Err() {
			t.Error("should have gotten context error")
		}
		finished <- struct{}{}
	}()

	// make sure the above makes it through the wait code first
	select {
	case <-dcall:
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for dial to start")
	}

	// Add a second dialwait in so two actors are waiting on the same dial
	go func() {
		_, err := dsync.DialLock(context.Background(), p)
		if err != nil {
			t.Error(err)
		}
		finished <- struct{}{}
	}()

	time.Sleep(time.Millisecond * 20)

	// cancel the first dialwait, it should not affect the second at all
	cancel1()
	select {
	case <-finished:
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for wait to exit")
	}

	// short sleep just to make sure we've moved around in the scheduler
	time.Sleep(time.Millisecond * 20)
	done()

	<-finished
}

func TestDialSyncAllCancel(t *testing.T) {
	df, done, dctx, _ := getMockDialFunc()

	dsync := NewDialSync(df)

	p := peer.ID("testpeer")

	ctx1, cancel1 := context.WithCancel(context.Background())

	finished := make(chan struct{})
	go func() {
		_, err := dsync.DialLock(ctx1, p)
		if err != ctx1.Err() {
			t.Error("should have gotten context error")
		}
		finished <- struct{}{}
	}()

	// Add a second dialwait in so two actors are waiting on the same dial
	go func() {
		_, err := dsync.DialLock(ctx1, p)
		if err != ctx1.Err() {
			t.Error("should have gotten context error")
		}
		finished <- struct{}{}
	}()

	cancel1()
	for i := 0; i < 2; i++ {
		select {
		case <-finished:
		case <-time.After(time.Second):
			t.Fatal("timed out waiting for wait to exit")
		}
	}

	// the dial should have exited now
	select {
	case <-dctx.Done():
	case <-time.After(time.Second):
		t.Fatal("timed out waiting for dial to return")
	}

	// should be able to successfully dial that peer again
	done()
	_, err := dsync.DialLock(context.Background(), p)
	if err != nil {
		t.Fatal(err)
	}
}

func TestFailFirst(t *testing.T) {
	var count int
	f := func(ctx context.Context, p peer.ID) (*Conn, error) {
		if count > 0 {
			return new(Conn), nil
		}
		count++
		return nil, fmt.Errorf("gophers ate the modem")
	}

	ds := NewDialSync(f)

	p := peer.ID("testing")

	ctx, cancel := context.WithTimeout(context.Background(), time.Second*5)
	defer cancel()

	_, err := ds.DialLock(ctx, p)
	if err == nil {
		t.Fatal("expected gophers to have eaten the modem")
	}

	c, err := ds.DialLock(ctx, p)
	if err != nil {
		t.Fatal(err)
	}

	if c == nil {
		t.Fatal("should have gotten a 'real' conn back")
	}
}

func TestStressActiveDial(t *testing.T) {
	ds := NewDialSync(func(ctx context.Context, p peer.ID) (*Conn, error) {
		return nil, nil
	})

	wg := sync.WaitGroup{}

	pid := peer.ID("foo")

	makeDials := func() {
		for i := 0; i < 10000; i++ {
			ds.DialLock(context.Background(), pid)
		}
		wg.Done()
	}

	for i := 0; i < 100; i++ {
		wg.Add(1)
		go makeDials()
	}

	wg.Wait()
}
<|endoftext|>


<commit_before>package graphite_test

import (
	"fmt"
	"net"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/influxdb/influxdb/cluster"
	"github.com/influxdb/influxdb/services/graphite"
	"github.com/influxdb/influxdb/toml"
	"github.com/influxdb/influxdb/tsdb"
)

func Test_DecodeNameAndTags(t *testing.T) {
	var tests = []struct {
		test      string
		str       string
		name      string
		tags      map[string]string
		position  string
		separator string
		err       string
	}{
		{test: "metric only", str: "cpu", name: "cpu"},
		{test: "metric with single series", str: "cpu.hostname.server01", name: "cpu", tags: map[string]string{"hostname": "server01"}},
		{test: "metric with multiple series", str: "cpu.region.us-west.hostname.server01", name: "cpu", tags: map[string]string{"hostname": "server01", "region": "us-west"}},
		{test: "no metric", tags: make(map[string]string), err: `no name specified for metric. ""`},
		{test: "wrong metric format", str: "foo.cpu", tags: make(map[string]string), err: `received "foo.cpu" which doesn't conform to format of key.value.key.value.name or name`},
	}

	for _, test := range tests {
		t.Logf("testing %q...", test.test)

		p := graphite.NewParser()
		if test.separator != "" {
			p.Separator = test.separator
		}

		name, tags, err := p.DecodeNameAndTags(test.str)
		if errstr(err) != test.err {
			t.Fatalf("err does not match.  expected %v, got %v", test.err, err)
		}
		if name != test.name {
			t.Fatalf("name parse failer.  expected %v, got %v", test.name, name)
		}
		if len(tags) != len(test.tags) {
			t.Fatalf("unexpected number of tags.  expected %d, got %d", len(test.tags), len(tags))
		}
		for k, v := range test.tags {
			if tags[k] != v {
				t.Fatalf("unexpected tag value for tags[%s].  expected %q, got %q", k, v, tags[k])
			}
		}
	}
}

func Test_DecodeMetric(t *testing.T) {
	testTime := time.Now().Round(time.Second)
	epochTime := testTime.Unix()
	strTime := strconv.FormatInt(epochTime, 10)

	var tests = []struct {
		test                string
		line                string
		name                string
		tags                map[string]string
		value               float64
		time                time.Time
		position, separator string
		err                 string
	}{
		{
			test:  "position first by default",
			line:  `cpu.foo.bar 50 ` + strTime,
			name:  "cpu",
			tags:  map[string]string{"foo": "bar"},
			value: 50,
			time:  testTime,
		},
		{
			test:     "position first if unable to determine",
			position: "foo",
			line:     `cpu.foo.bar 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{"foo": "bar"},
			value:    50,
			time:     testTime,
		},
		{
			test:     "position last if specified",
			position: "last",
			line:     `foo.bar.cpu 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{"foo": "bar"},
			value:    50,
			time:     testTime,
		},
		{
			test:     "position first if specified with no series",
			position: "first",
			line:     `cpu 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{},
			value:    50,
			time:     testTime,
		},
		{
			test:     "position last if specified with no series",
			position: "last",
			line:     `cpu 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{},
			value:    50,
			time:     testTime,
		},
		{
			test:  "separator is . by default",
			line:  `cpu.foo.bar 50 ` + strTime,
			name:  "cpu",
			tags:  map[string]string{"foo": "bar"},
			value: 50,
			time:  testTime,
		},
		{
			test:      "separator is . if specified",
			separator: ".",
			line:      `cpu.foo.bar 50 ` + strTime,
			name:      "cpu",
			tags:      map[string]string{"foo": "bar"},
			value:     50,
			time:      testTime,
		},
		{
			test:      "separator is - if specified",
			separator: "-",
			line:      `cpu-foo-bar 50 ` + strTime,
			name:      "cpu",
			tags:      map[string]string{"foo": "bar"},
			value:     50,
			time:      testTime,
		},
		{
			test:      "separator is boo if specified",
			separator: "boo",
			line:      `cpuboofooboobar 50 ` + strTime,
			name:      "cpu",
			tags:      map[string]string{"foo": "bar"},
			value:     50,
			time:      testTime,
		},

		{
			test:  "series + metric + integer value",
			line:  `cpu.foo.bar 50 ` + strTime,
			name:  "cpu",
			tags:  map[string]string{"foo": "bar"},
			value: 50,
			time:  testTime,
		},
		{
			test:  "metric only with float value",
			line:  `cpu 50.554 ` + strTime,
			name:  "cpu",
			value: 50.554,
			time:  testTime,
		},
		{
			test: "missing metric",
			line: `50.554 1419972457825`,
			err:  `received "50.554 1419972457825" which doesn't have three fields`,
		},
		{
			test: "should error on invalid key",
			line: `foo.cpu 50.554 1419972457825`,
			err:  `received "foo.cpu" which doesn't conform to format of key.value.key.value.name or name`,
		},
		{
			test: "should error parsing invalid float",
			line: `cpu 50.554z 1419972457825`,
			err:  `field "cpu" value: strconv.ParseFloat: parsing "50.554z": invalid syntax`,
		},
		{
			test: "should error parsing invalid int",
			line: `cpu 50z 1419972457825`,
			err:  `field "cpu" value: strconv.ParseFloat: parsing "50z": invalid syntax`,
		},
		{
			test: "should error parsing invalid time",
			line: `cpu 50.554 14199724z57825`,
			err:  `field "cpu" time: strconv.ParseFloat: parsing "14199724z57825": invalid syntax`,
		},
	}

	for _, test := range tests {
		t.Logf("testing %q...", test.test)

		p := graphite.NewParser()
		if test.separator != "" {
			p.Separator = test.separator
		}
		p.LastEnabled = (test.position == "last")

		point, err := p.Parse(test.line)
		if errstr(err) != test.err {
			t.Fatalf("err does not match.  expected %v, got %v", test.err, err)
		}
		if err != nil {
			// If we erred out,it was intended and the following tests won't work
			continue
		}
		if point.Name() != test.name {
			t.Fatalf("name parse failer.  expected %v, got %v", test.name, point.Name())
		}
		if len(point.Tags()) != len(test.tags) {
			t.Fatalf("tags len mismatch.  expected %d, got %d", len(test.tags), len(point.Tags()))
		}
		f := point.Fields()["value"].(float64)
		if point.Fields()["value"] != f {
			t.Fatalf("floatValue value mismatch.  expected %v, got %v", test.value, f)
		}
		if point.Time().UnixNano()/1000000 != test.time.UnixNano()/1000000 {
			t.Fatalf("time value mismatch.  expected %v, got %v", test.time.UnixNano(), point.Time().UnixNano())
		}
	}
}

func Test_ServerGraphiteTCP(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip()
	}

	now := time.Now().UTC().Round(time.Second)

	config := graphite.NewConfig()
	config.Database = "graphitedb"
	config.BatchSize = 0 // No batching.
	config.BatchTimeout = toml.Duration(time.Second)
	config.BindAddress = ":0"

	service := graphite.NewService(config)
	if service == nil {
		t.Fatal("failed to create Graphite service")
	}

	// Allow test to wait until points are written.
	var wg sync.WaitGroup
	wg.Add(1)

	pointsWriter := PointsWriter{
		WritePointsFn: func(req *cluster.WritePointsRequest) error {
			defer wg.Done()

			if req.Database != "graphitedb" {
				t.Fatalf("unexpected database: %s", req.Database)
			} else if req.RetentionPolicy != "" {
				t.Fatalf("unexpected retention policy: %s", req.RetentionPolicy)
			} else if !reflect.DeepEqual(req.Points, []tsdb.Point{
				tsdb.NewPoint(
					"cpu",
					map[string]string{},
					map[string]interface{}{"value": 23.456},
					time.Unix(now.Unix(), 0),
				),
			}) {
				spew.Dump(req.Points)
				t.Fatalf("unexpected points: %#v", req.Points)
			}
			return nil
		},
	}
	service.PointsWriter = &pointsWriter

	if err := service.Open(); err != nil {
		t.Fatalf("failed to open Graphite service: %s", err.Error())
	}

	// Connect to the graphite endpoint we just spun up
	conn, err := net.Dial("tcp", strings.TrimPrefix(service.Host(), "[::]"))
	if err != nil {
		t.Fatal(err)
	}
	data := []byte(`cpu 23.456 `)
	data = append(data, []byte(fmt.Sprintf("%d", now.Unix()))...)
	data = append(data, '\n')
	_, err = conn.Write(data)
	conn.Close()
	if err != nil {
		t.Fatal(err)
	}

	wg.Wait()
}

// PointsWriter represents a mock impl of PointsWriter.
type PointsWriter struct {
	WritePointsFn func(*cluster.WritePointsRequest) error
}

func (w *PointsWriter) WritePoints(p *cluster.WritePointsRequest) error {
	return w.WritePointsFn(p)
}

// Test Helpers
func errstr(err error) string {
	if err != nil {
		return err.Error()
	}
	return ""
}
<commit_msg>Add test of Graphite UDP input<commit_after>package graphite_test

import (
	"fmt"
	"net"
	"reflect"
	"strconv"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/influxdb/influxdb/cluster"
	"github.com/influxdb/influxdb/services/graphite"
	"github.com/influxdb/influxdb/toml"
	"github.com/influxdb/influxdb/tsdb"
)

func Test_DecodeNameAndTags(t *testing.T) {
	var tests = []struct {
		test      string
		str       string
		name      string
		tags      map[string]string
		position  string
		separator string
		err       string
	}{
		{test: "metric only", str: "cpu", name: "cpu"},
		{test: "metric with single series", str: "cpu.hostname.server01", name: "cpu", tags: map[string]string{"hostname": "server01"}},
		{test: "metric with multiple series", str: "cpu.region.us-west.hostname.server01", name: "cpu", tags: map[string]string{"hostname": "server01", "region": "us-west"}},
		{test: "no metric", tags: make(map[string]string), err: `no name specified for metric. ""`},
		{test: "wrong metric format", str: "foo.cpu", tags: make(map[string]string), err: `received "foo.cpu" which doesn't conform to format of key.value.key.value.name or name`},
	}

	for _, test := range tests {
		t.Logf("testing %q...", test.test)

		p := graphite.NewParser()
		if test.separator != "" {
			p.Separator = test.separator
		}

		name, tags, err := p.DecodeNameAndTags(test.str)
		if errstr(err) != test.err {
			t.Fatalf("err does not match.  expected %v, got %v", test.err, err)
		}
		if name != test.name {
			t.Fatalf("name parse failer.  expected %v, got %v", test.name, name)
		}
		if len(tags) != len(test.tags) {
			t.Fatalf("unexpected number of tags.  expected %d, got %d", len(test.tags), len(tags))
		}
		for k, v := range test.tags {
			if tags[k] != v {
				t.Fatalf("unexpected tag value for tags[%s].  expected %q, got %q", k, v, tags[k])
			}
		}
	}
}

func Test_DecodeMetric(t *testing.T) {
	testTime := time.Now().Round(time.Second)
	epochTime := testTime.Unix()
	strTime := strconv.FormatInt(epochTime, 10)

	var tests = []struct {
		test                string
		line                string
		name                string
		tags                map[string]string
		value               float64
		time                time.Time
		position, separator string
		err                 string
	}{
		{
			test:  "position first by default",
			line:  `cpu.foo.bar 50 ` + strTime,
			name:  "cpu",
			tags:  map[string]string{"foo": "bar"},
			value: 50,
			time:  testTime,
		},
		{
			test:     "position first if unable to determine",
			position: "foo",
			line:     `cpu.foo.bar 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{"foo": "bar"},
			value:    50,
			time:     testTime,
		},
		{
			test:     "position last if specified",
			position: "last",
			line:     `foo.bar.cpu 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{"foo": "bar"},
			value:    50,
			time:     testTime,
		},
		{
			test:     "position first if specified with no series",
			position: "first",
			line:     `cpu 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{},
			value:    50,
			time:     testTime,
		},
		{
			test:     "position last if specified with no series",
			position: "last",
			line:     `cpu 50 ` + strTime,
			name:     "cpu",
			tags:     map[string]string{},
			value:    50,
			time:     testTime,
		},
		{
			test:  "separator is . by default",
			line:  `cpu.foo.bar 50 ` + strTime,
			name:  "cpu",
			tags:  map[string]string{"foo": "bar"},
			value: 50,
			time:  testTime,
		},
		{
			test:      "separator is . if specified",
			separator: ".",
			line:      `cpu.foo.bar 50 ` + strTime,
			name:      "cpu",
			tags:      map[string]string{"foo": "bar"},
			value:     50,
			time:      testTime,
		},
		{
			test:      "separator is - if specified",
			separator: "-",
			line:      `cpu-foo-bar 50 ` + strTime,
			name:      "cpu",
			tags:      map[string]string{"foo": "bar"},
			value:     50,
			time:      testTime,
		},
		{
			test:      "separator is boo if specified",
			separator: "boo",
			line:      `cpuboofooboobar 50 ` + strTime,
			name:      "cpu",
			tags:      map[string]string{"foo": "bar"},
			value:     50,
			time:      testTime,
		},

		{
			test:  "series + metric + integer value",
			line:  `cpu.foo.bar 50 ` + strTime,
			name:  "cpu",
			tags:  map[string]string{"foo": "bar"},
			value: 50,
			time:  testTime,
		},
		{
			test:  "metric only with float value",
			line:  `cpu 50.554 ` + strTime,
			name:  "cpu",
			value: 50.554,
			time:  testTime,
		},
		{
			test: "missing metric",
			line: `50.554 1419972457825`,
			err:  `received "50.554 1419972457825" which doesn't have three fields`,
		},
		{
			test: "should error on invalid key",
			line: `foo.cpu 50.554 1419972457825`,
			err:  `received "foo.cpu" which doesn't conform to format of key.value.key.value.name or name`,
		},
		{
			test: "should error parsing invalid float",
			line: `cpu 50.554z 1419972457825`,
			err:  `field "cpu" value: strconv.ParseFloat: parsing "50.554z": invalid syntax`,
		},
		{
			test: "should error parsing invalid int",
			line: `cpu 50z 1419972457825`,
			err:  `field "cpu" value: strconv.ParseFloat: parsing "50z": invalid syntax`,
		},
		{
			test: "should error parsing invalid time",
			line: `cpu 50.554 14199724z57825`,
			err:  `field "cpu" time: strconv.ParseFloat: parsing "14199724z57825": invalid syntax`,
		},
	}

	for _, test := range tests {
		t.Logf("testing %q...", test.test)

		p := graphite.NewParser()
		if test.separator != "" {
			p.Separator = test.separator
		}
		p.LastEnabled = (test.position == "last")

		point, err := p.Parse(test.line)
		if errstr(err) != test.err {
			t.Fatalf("err does not match.  expected %v, got %v", test.err, err)
		}
		if err != nil {
			// If we erred out,it was intended and the following tests won't work
			continue
		}
		if point.Name() != test.name {
			t.Fatalf("name parse failer.  expected %v, got %v", test.name, point.Name())
		}
		if len(point.Tags()) != len(test.tags) {
			t.Fatalf("tags len mismatch.  expected %d, got %d", len(test.tags), len(point.Tags()))
		}
		f := point.Fields()["value"].(float64)
		if point.Fields()["value"] != f {
			t.Fatalf("floatValue value mismatch.  expected %v, got %v", test.value, f)
		}
		if point.Time().UnixNano()/1000000 != test.time.UnixNano()/1000000 {
			t.Fatalf("time value mismatch.  expected %v, got %v", test.time.UnixNano(), point.Time().UnixNano())
		}
	}
}

func Test_ServerGraphiteTCP(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip()
	}

	now := time.Now().UTC().Round(time.Second)

	config := graphite.NewConfig()
	config.Database = "graphitedb"
	config.BatchSize = 0 // No batching.
	config.BatchTimeout = toml.Duration(time.Second)
	config.BindAddress = ":0"

	service := graphite.NewService(config)
	if service == nil {
		t.Fatal("failed to create Graphite service")
	}

	// Allow test to wait until points are written.
	var wg sync.WaitGroup
	wg.Add(1)

	pointsWriter := PointsWriter{
		WritePointsFn: func(req *cluster.WritePointsRequest) error {
			defer wg.Done()

			if req.Database != "graphitedb" {
				t.Fatalf("unexpected database: %s", req.Database)
			} else if req.RetentionPolicy != "" {
				t.Fatalf("unexpected retention policy: %s", req.RetentionPolicy)
			} else if !reflect.DeepEqual(req.Points, []tsdb.Point{
				tsdb.NewPoint(
					"cpu",
					map[string]string{},
					map[string]interface{}{"value": 23.456},
					time.Unix(now.Unix(), 0),
				),
			}) {
				spew.Dump(req.Points)
				t.Fatalf("unexpected points: %#v", req.Points)
			}
			return nil
		},
	}
	service.PointsWriter = &pointsWriter

	if err := service.Open(); err != nil {
		t.Fatalf("failed to open Graphite service: %s", err.Error())
	}

	// Connect to the graphite endpoint we just spun up
	conn, err := net.Dial("tcp", strings.TrimPrefix(service.Host(), "[::]"))
	if err != nil {
		t.Fatal(err)
	}
	data := []byte(`cpu 23.456 `)
	data = append(data, []byte(fmt.Sprintf("%d", now.Unix()))...)
	data = append(data, '\n')
	_, err = conn.Write(data)
	conn.Close()
	if err != nil {
		t.Fatal(err)
	}

	wg.Wait()
}

func Test_ServerGraphiteUDP(t *testing.T) {
	t.Parallel()
	if testing.Short() {
		t.Skip()
	}

	now := time.Now().UTC().Round(time.Second)

	config := graphite.NewConfig()
	config.Database = "graphitedb"
	config.BatchSize = 0 // No batching.
	config.BatchTimeout = toml.Duration(time.Second)
	config.BindAddress = ":10000"
	config.Protocol = "udp"

	service := graphite.NewService(config)
	if service == nil {
		t.Fatal("failed to create Graphite service")
	}

	// Allow test to wait until points are written.
	var wg sync.WaitGroup
	wg.Add(1)

	pointsWriter := PointsWriter{
		WritePointsFn: func(req *cluster.WritePointsRequest) error {
			defer wg.Done()

			if req.Database != "graphitedb" {
				t.Fatalf("unexpected database: %s", req.Database)
			} else if req.RetentionPolicy != "" {
				t.Fatalf("unexpected retention policy: %s", req.RetentionPolicy)
			} else if !reflect.DeepEqual(req.Points, []tsdb.Point{
				tsdb.NewPoint(
					"cpu",
					map[string]string{},
					map[string]interface{}{"value": 23.456},
					time.Unix(now.Unix(), 0),
				),
			}) {
				spew.Dump(req.Points)
				t.Fatalf("unexpected points: %#v", req.Points)
			}
			return nil
		},
	}
	service.PointsWriter = &pointsWriter

	if err := service.Open(); err != nil {
		t.Fatalf("failed to open Graphite service: %s", err.Error())
	}

	// Connect to the graphite endpoint we just spun up
	conn, err := net.Dial("udp", strings.TrimPrefix(service.Host(), "[::]"))
	if err != nil {
		t.Fatal(err)
	}
	data := []byte(`cpu 23.456 `)
	data = append(data, []byte(fmt.Sprintf("%d", now.Unix()))...)
	data = append(data, '\n')
	_, err = conn.Write(data)
	if err != nil {
		t.Fatal(err)
	}

	wg.Wait()
	conn.Close()
}

// PointsWriter represents a mock impl of PointsWriter.
type PointsWriter struct {
	WritePointsFn func(*cluster.WritePointsRequest) error
}

func (w *PointsWriter) WritePoints(p *cluster.WritePointsRequest) error {
	return w.WritePointsFn(p)
}

// Test Helpers
func errstr(err error) string {
	if err != nil {
		return err.Error()
	}
	return ""
}
<|endoftext|>


<commit_before>// Copyright 2016 The Minimal Configuration Manager 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 shlib_test

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/zombiezen/mcm/catalog"
	"github.com/zombiezen/mcm/internal/catpogs"
	"github.com/zombiezen/mcm/shellify/shlib"
)

func TestIntegration(t *testing.T) {
	bashPath, err := exec.LookPath("bash")
	if err != nil {
		t.Skipf("Can't find bash: %v", err)
	}
	t.Logf("using %s for bash", bashPath)
	t.Run("Empty", func(t *testing.T) { emptyTest(t, bashPath) })
	t.Run("File", func(t *testing.T) { fileTest(t, bashPath) })
	t.Run("Link", func(t *testing.T) { linkTest(t, bashPath) })
	t.Run("Relink", func(t *testing.T) { relinkTest(t, bashPath) })
	t.Run("SkipFail", func(t *testing.T) { skipFailTest(t, bashPath) })
}

func emptyTest(t *testing.T, bashPath string) {
	c, err := new(catpogs.Catalog).ToCapnp()
	if err != nil {
		t.Fatalf("build empty catalog: %v", err)
	}
	_, err = runCatalog(bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}
}

func fileTest(t *testing.T, bashPath string) {
	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	fpath := filepath.Join(root, "foo.txt")
	const fileContent = "Hello!\n"
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      42,
				Comment: "file",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(fpath, []byte(fileContent)),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	_, err = runCatalog(bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}
	gotContent, err := ioutil.ReadFile(fpath)
	if err != nil {
		t.Errorf("read %s: %v", fpath, err)
	}
	if !bytes.Equal(gotContent, []byte(fileContent)) {
		t.Errorf("content of %s = %q; want %q", fpath, gotContent, fileContent)
	}
}

func linkTest(t *testing.T, bashPath string) {
	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	fpath := filepath.Join(root, "foo")
	lpath := filepath.Join(root, "link")
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      42,
				Comment: "file",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(fpath, []byte("Hello")),
			},
			{
				ID:      100,
				Deps:    []uint64{42},
				Comment: "link",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.SymlinkFile(fpath, lpath),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	_, err = runCatalog(bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}

	if info, err := os.Lstat(lpath); err == nil {
		if info.Mode()&os.ModeType != os.ModeSymlink {
			t.Errorf("os.Lstat(%q).Mode() = %v; want symlink", lpath, info.Mode())
		}
	} else {
		t.Errorf("os.Lstat(%q): %v", lpath, err)
	}
	if target, err := os.Readlink(lpath); err == nil {
		if target != fpath {
			t.Errorf("os.Readlink(%q) = %q; want %q", lpath, target, fpath)
		}
	} else {
		t.Errorf("os.Readlink(%q): %v", lpath, err)
	}
}

func relinkTest(t *testing.T, bashPath string) {
	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	f1path := filepath.Join(root, "foo")
	f2path := filepath.Join(root, "bar")
	lpath := filepath.Join(root, "link")
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      42,
				Comment: "link",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.SymlinkFile(f2path, lpath),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	if err := ioutil.WriteFile(f1path, []byte("File 1"), 0666); err != nil {
		t.Fatal("WriteFile 1:", err)
	}
	if err := ioutil.WriteFile(f2path, []byte("File 2"), 0666); err != nil {
		t.Fatal("WriteFile 2:", err)
	}
	if err := os.Symlink(f1path, lpath); err != nil {
		t.Fatalf("os.Symlink %s -> %s: %v", lpath, f1path, err)
	}
	_, err = runCatalog(bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}

	if info, err := os.Lstat(lpath); err == nil {
		if info.Mode()&os.ModeType != os.ModeSymlink {
			t.Errorf("os.Lstat(%q).Mode() = %v; want symlink", lpath, info.Mode())
		}
	} else {
		t.Errorf("os.Lstat(%q): %v", lpath, err)
	}
	if target, err := os.Readlink(lpath); err == nil {
		if target != f2path {
			t.Errorf("os.Readlink(%q) = %q; want %q", lpath, target, f2path)
		}
	} else {
		t.Errorf("os.Readlink(%q): %v", lpath, err)
	}
}

func skipFailTest(t *testing.T, bashPath string) {
	t.Skip("TODO(now): implement")

	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	f1path := filepath.Join(root, "foo")
	f2path := filepath.Join(root, "bar")
	f3path := filepath.Join(root, "baz")
	canaryPath := filepath.Join(root, "canary")
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      101,
				Comment: "file 1",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(f1path, []byte("foo")),
			},
			{
				ID:      102,
				Deps:    []uint64{101},
				Comment: "file 2",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(f2path, []byte("bar")),
			},
			{
				ID:      103,
				Deps:    []uint64{102},
				Comment: "file 3",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(f3path, []byte("baz")),
			},
			{
				ID:      200,
				Comment: "canary file - not dependent on other files",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(canaryPath, []byte("tweet!")),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	// Create a directory to cause file 1 to fail.
	if err := os.Mkdir(f1path, 0777); err != nil {
		t.Fatal("mkdir:", err)
	}
	_, err = runCatalog(bashPath, t, c)
	t.Logf("run catalog: %v", err)
	if err == nil {
		t.Error("run catalog did not return an error")
	}

	if _, err := os.Lstat(f2path); !os.IsNotExist(err) {
		t.Errorf("os.Lstat(%q) = %v; want not exist", f2path, err)
	}
	if _, err := os.Lstat(f3path); !os.IsNotExist(err) {
		t.Errorf("os.Lstat(%q) = %v; want not exist", f3path, err)
	}
	if _, err := os.Lstat(canaryPath); err != nil {
		t.Errorf("os.Lstat(%q) = %v; want nil", canaryPath, err)
	}
}

const tmpDirEnv = "TEST_TMPDIR"

func runCatalog(bashPath string, log logger, c catalog.Catalog, args ...string) ([]byte, error) {
	sc, err := ioutil.TempFile(os.Getenv(tmpDirEnv), "shlib_testscript")
	if err != nil {
		return nil, err
	}
	scriptPath := sc.Name()
	defer func() {
		if err := os.Remove(scriptPath); err != nil {
			log.Logf("removing temporary script file: %v", err)
		}
	}()
	err = shlib.WriteScript(sc, c)
	cerr := sc.Close()
	if err != nil {
		return nil, err
	}
	if cerr != nil {
		return nil, cerr
	}
	log.Logf("%s -- %s %s", bashPath, scriptPath, strings.Join(args, " "))
	cmd := exec.Command(bashPath, append([]string{"--", scriptPath}, args...)...)
	stdout := new(bytes.Buffer)
	cmd.Stdout = stdout
	stderr := new(bytes.Buffer)
	cmd.Stderr = stderr
	if err := cmd.Run(); err != nil {
		return stdout.Bytes(), fmt.Errorf("bash failed: %v; stderr:\n%s", err, stderr.Bytes())
	}
	return stdout.Bytes(), nil
}

func makeTempDir(log logger) (path string, done func(), err error) {
	path, err = ioutil.TempDir(os.Getenv(tmpDirEnv), "shlib_testdir")
	if err != nil {
		return "", nil, err
	}
	return path, func() {
		if err := os.RemoveAll(path); err != nil {
			log.Logf("removing temporary directory: %v", err)
		}
	}, nil
}

type logger interface {
	Logf(string, ...interface{})
}
<commit_msg>shellify: add -keep_scripts flag to integration test<commit_after>// Copyright 2016 The Minimal Configuration Manager 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 shlib_test

import (
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"

	"github.com/zombiezen/mcm/catalog"
	"github.com/zombiezen/mcm/internal/catpogs"
	"github.com/zombiezen/mcm/shellify/shlib"
)

var keepScripts = flag.Bool("keep_scripts", false, "do not remove generated scripts from temporary directory")

func TestIntegration(t *testing.T) {
	bashPath, err := exec.LookPath("bash")
	if err != nil {
		t.Skipf("Can't find bash: %v", err)
	}
	t.Logf("using %s for bash", bashPath)
	t.Run("Empty", func(t *testing.T) { emptyTest(t, bashPath) })
	t.Run("File", func(t *testing.T) { fileTest(t, bashPath) })
	t.Run("Link", func(t *testing.T) { linkTest(t, bashPath) })
	t.Run("Relink", func(t *testing.T) { relinkTest(t, bashPath) })
	t.Run("SkipFail", func(t *testing.T) { skipFailTest(t, bashPath) })
}

func emptyTest(t *testing.T, bashPath string) {
	c, err := new(catpogs.Catalog).ToCapnp()
	if err != nil {
		t.Fatalf("build empty catalog: %v", err)
	}
	_, err = runCatalog("empty", bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}
}

func fileTest(t *testing.T, bashPath string) {
	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	fpath := filepath.Join(root, "foo.txt")
	const fileContent = "Hello!\n"
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      42,
				Comment: "file",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(fpath, []byte(fileContent)),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	_, err = runCatalog("file", bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}
	gotContent, err := ioutil.ReadFile(fpath)
	if err != nil {
		t.Errorf("read %s: %v", fpath, err)
	}
	if !bytes.Equal(gotContent, []byte(fileContent)) {
		t.Errorf("content of %s = %q; want %q", fpath, gotContent, fileContent)
	}
}

func linkTest(t *testing.T, bashPath string) {
	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	fpath := filepath.Join(root, "foo")
	lpath := filepath.Join(root, "link")
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      42,
				Comment: "file",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(fpath, []byte("Hello")),
			},
			{
				ID:      100,
				Deps:    []uint64{42},
				Comment: "link",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.SymlinkFile(fpath, lpath),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	_, err = runCatalog("link", bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}

	if info, err := os.Lstat(lpath); err == nil {
		if info.Mode()&os.ModeType != os.ModeSymlink {
			t.Errorf("os.Lstat(%q).Mode() = %v; want symlink", lpath, info.Mode())
		}
	} else {
		t.Errorf("os.Lstat(%q): %v", lpath, err)
	}
	if target, err := os.Readlink(lpath); err == nil {
		if target != fpath {
			t.Errorf("os.Readlink(%q) = %q; want %q", lpath, target, fpath)
		}
	} else {
		t.Errorf("os.Readlink(%q): %v", lpath, err)
	}
}

func relinkTest(t *testing.T, bashPath string) {
	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	f1path := filepath.Join(root, "foo")
	f2path := filepath.Join(root, "bar")
	lpath := filepath.Join(root, "link")
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      42,
				Comment: "link",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.SymlinkFile(f2path, lpath),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	if err := ioutil.WriteFile(f1path, []byte("File 1"), 0666); err != nil {
		t.Fatal("WriteFile 1:", err)
	}
	if err := ioutil.WriteFile(f2path, []byte("File 2"), 0666); err != nil {
		t.Fatal("WriteFile 2:", err)
	}
	if err := os.Symlink(f1path, lpath); err != nil {
		t.Fatalf("os.Symlink %s -> %s: %v", lpath, f1path, err)
	}
	_, err = runCatalog("relink", bashPath, t, c)
	if err != nil {
		t.Errorf("run catalog: %v", err)
	}

	if info, err := os.Lstat(lpath); err == nil {
		if info.Mode()&os.ModeType != os.ModeSymlink {
			t.Errorf("os.Lstat(%q).Mode() = %v; want symlink", lpath, info.Mode())
		}
	} else {
		t.Errorf("os.Lstat(%q): %v", lpath, err)
	}
	if target, err := os.Readlink(lpath); err == nil {
		if target != f2path {
			t.Errorf("os.Readlink(%q) = %q; want %q", lpath, target, f2path)
		}
	} else {
		t.Errorf("os.Readlink(%q): %v", lpath, err)
	}
}

func skipFailTest(t *testing.T, bashPath string) {
	t.Skip("TODO(now): implement")

	root, deleteTempDir, err := makeTempDir(t)
	if err != nil {
		t.Fatalf("temp directory: %v", err)
	}
	defer deleteTempDir()
	f1path := filepath.Join(root, "foo")
	f2path := filepath.Join(root, "bar")
	f3path := filepath.Join(root, "baz")
	canaryPath := filepath.Join(root, "canary")
	c, err := (&catpogs.Catalog{
		Resources: []*catpogs.Resource{
			{
				ID:      101,
				Comment: "file 1",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(f1path, []byte("foo")),
			},
			{
				ID:      102,
				Deps:    []uint64{101},
				Comment: "file 2",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(f2path, []byte("bar")),
			},
			{
				ID:      103,
				Deps:    []uint64{102},
				Comment: "file 3",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(f3path, []byte("baz")),
			},
			{
				ID:      200,
				Comment: "canary file - not dependent on other files",
				Which:   catalog.Resource_Which_file,
				File:    catpogs.PlainFile(canaryPath, []byte("tweet!")),
			},
		},
	}).ToCapnp()
	if err != nil {
		t.Fatalf("build catalog: %v", err)
	}
	// Create a directory to cause file 1 to fail.
	if err := os.Mkdir(f1path, 0777); err != nil {
		t.Fatal("mkdir:", err)
	}
	_, err = runCatalog("skipFail", bashPath, t, c)
	t.Logf("run catalog: %v", err)
	if err == nil {
		t.Error("run catalog did not return an error")
	}

	if _, err := os.Lstat(f2path); !os.IsNotExist(err) {
		t.Errorf("os.Lstat(%q) = %v; want not exist", f2path, err)
	}
	if _, err := os.Lstat(f3path); !os.IsNotExist(err) {
		t.Errorf("os.Lstat(%q) = %v; want not exist", f3path, err)
	}
	if _, err := os.Lstat(canaryPath); err != nil {
		t.Errorf("os.Lstat(%q) = %v; want nil", canaryPath, err)
	}
}

const tmpDirEnv = "TEST_TMPDIR"

func runCatalog(scriptName string, bashPath string, log logger, c catalog.Catalog, args ...string) ([]byte, error) {
	sc, err := ioutil.TempFile(os.Getenv(tmpDirEnv), "shlib_testscript_"+scriptName)
	if err != nil {
		return nil, err
	}
	scriptPath := sc.Name()
	if !*keepScripts {
		defer func() {
			if err := os.Remove(scriptPath); err != nil {
				log.Logf("removing temporary script file: %v", err)
			}
		}()
	}
	err = shlib.WriteScript(sc, c)
	cerr := sc.Close()
	if err != nil {
		return nil, err
	}
	if cerr != nil {
		return nil, cerr
	}
	log.Logf("%s -- %s %s", bashPath, scriptPath, strings.Join(args, " "))
	cmd := exec.Command(bashPath, append([]string{"--", scriptPath}, args...)...)
	stdout := new(bytes.Buffer)
	cmd.Stdout = stdout
	stderr := new(bytes.Buffer)
	cmd.Stderr = stderr
	if err := cmd.Run(); err != nil {
		return stdout.Bytes(), fmt.Errorf("bash failed: %v; stderr:\n%s", err, stderr.Bytes())
	}
	return stdout.Bytes(), nil
}

func makeTempDir(log logger) (path string, done func(), err error) {
	path, err = ioutil.TempDir(os.Getenv(tmpDirEnv), "shlib_testdir")
	if err != nil {
		return "", nil, err
	}
	return path, func() {
		if err := os.RemoveAll(path); err != nil {
			log.Logf("removing temporary directory: %v", err)
		}
	}, nil
}

type logger interface {
	Logf(string, ...interface{})
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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.

// Perform a hermetic build of gcsfuse at a particular version, producing
// release binaries and packages.
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
)

var fVersion = flag.String("version", "", "Version number of the release.")
var fCommit = flag.String("commit", "", "Commit at which to build.")
var fOS = flag.String("os", "", "OS for which to build, e.g. linux or darwin.")
var fArch = flag.String("arch", "amd64", "Architecture for which to build.")
var fOutputDir = flag.String("output_dir", "", "Where to write outputs.")

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func getSettings() (version, commit, osys, arch string, err error) {
	if *fVersion == "" {
		err = errors.New("You must set --version.")
		return
	}
	version = *fVersion

	if *fCommit == "" {
		err = errors.New("You must set --commit.")
		return
	}
	commit = *fCommit

	if *fOS == "" {
		err = errors.New("You must set --os.")
		return
	}
	osys = *fOS

	if *fArch == "" {
		err = errors.New("You must set --arch.")
		return
	}
	arch = *fArch

	return
}

////////////////////////////////////////////////////////////////////////
// main logic
////////////////////////////////////////////////////////////////////////

func run() (err error) {
	// Ensure that all of the tools we need are present.
	err = checkForTools()
	if err != nil {
		return
	}

	// Read flags.
	version, commit, osys, arch, err := getSettings()
	if err != nil {
		return
	}

	// Build release binaries.
	binDir, err := buildBinaries(version, commit, osys, arch)
	if err != nil {
		err = fmt.Errorf("buildBinaries: %v", err)
		return
	}

	defer os.RemoveAll(binDir)

	// Write out a tarball.
	err = packageTarball(binDir, version, osys, arch, *fOutputDir)
	if err != nil {
		err = fmt.Errorf("packageTarball: %v", err)
		return
	}

	// Write out a .deb file.
	err = packageDeb(binDir, version, osys, arch, *fOutputDir)
	if err != nil {
		err = fmt.Errorf("packageDeb: %v", err)
		return
	}

	return
}

func main() {
	log.SetFlags(log.Lmicroseconds)
	flag.Parse()

	err := run()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<commit_msg>Support empty output dir.<commit_after>// Copyright 2015 Google Inc. 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.

// Perform a hermetic build of gcsfuse at a particular version, producing
// release binaries and packages.
package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
)

var fVersion = flag.String("version", "", "Version number of the release.")
var fCommit = flag.String("commit", "", "Commit at which to build.")
var fOS = flag.String("os", "", "OS for which to build, e.g. linux or darwin.")
var fArch = flag.String("arch", "amd64", "Architecture for which to build.")
var fOutputDir = flag.String("output_dir", "", "Where to write outputs.")

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

func getSettings() (version, commit, osys, arch, outputDir string, err error) {
	if *fVersion == "" {
		err = errors.New("You must set --version.")
		return
	}
	version = *fVersion

	if *fCommit == "" {
		err = errors.New("You must set --commit.")
		return
	}
	commit = *fCommit

	if *fOS == "" {
		err = errors.New("You must set --os.")
		return
	}
	osys = *fOS

	if *fArch == "" {
		err = errors.New("You must set --arch.")
		return
	}
	arch = *fArch

	// Output dir
	outputDir = *fOutputDir
	if outputDir == "" {
		outputDir, err = os.Getwd()
		if err != nil {
			err = fmt.Errorf("Getwd: %v", err)
			return
		}
	}

	return
}

////////////////////////////////////////////////////////////////////////
// main logic
////////////////////////////////////////////////////////////////////////

func run() (err error) {
	// Ensure that all of the tools we need are present.
	err = checkForTools()
	if err != nil {
		return
	}

	// Read flags.
	version, commit, osys, arch, outputDir, err := getSettings()
	if err != nil {
		return
	}

	// Build release binaries.
	binDir, err := buildBinaries(version, commit, osys, arch)
	if err != nil {
		err = fmt.Errorf("buildBinaries: %v", err)
		return
	}

	defer os.RemoveAll(binDir)

	// Write out a tarball.
	err = packageTarball(binDir, version, osys, arch, outputDir)
	if err != nil {
		err = fmt.Errorf("packageTarball: %v", err)
		return
	}

	// Write out a .deb file.
	err = packageDeb(binDir, version, osys, arch, *fOutputDir)
	if err != nil {
		err = fmt.Errorf("packageDeb: %v", err)
		return
	}

	return
}

func main() {
	log.SetFlags(log.Lmicroseconds)
	flag.Parse()

	err := run()
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 The kube-etcd-controller 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 e2e

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"testing"
	"time"

	"github.com/coreos/kube-etcd-controller/pkg/spec"
	"github.com/coreos/kube-etcd-controller/pkg/util/k8sutil"
	"github.com/coreos/kube-etcd-controller/test/e2e/framework"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util/wait"
	"k8s.io/kubernetes/pkg/watch"
)

func TestCreateCluster(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 3))
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
	}
}

func TestResizeCluster3to5(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 3))
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 3 members cluster")

	testEtcd.Spec.Size = 5
	if err := updateEtcdCluster(f, testEtcd); err != nil {
		t.Fatal(err)
	}

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 5, 60); err != nil {
		t.Fatalf("failed to resize to 5 members etcd cluster: %v", err)
	}
}

func TestResizeCluster5to3(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 5))
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 5, 90); err != nil {
		t.Fatalf("failed to create 5 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 5 members cluster")

	testEtcd.Spec.Size = 3
	if err := updateEtcdCluster(f, testEtcd); err != nil {
		t.Fatal(err)
	}

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to resize to 3 members etcd cluster: %v", err)
	}
}

func TestOneMemberRecovery(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 3))
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	names, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60)
	if err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 3 members cluster")

	if err := killMembers(f, names[0]); err != nil {
		t.Fatal(err)
	}
	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to resize to 3 members etcd cluster: %v", err)
	}
}

func TestDisasterRecovery(t *testing.T) {
	f := framework.Global
	backupPolicy := &spec.BackupPolicy{
		SnapshotIntervalInSecond: 120,
		MaxSnapshot:              5,
		VolumeSizeInMB:           512,
		StorageType:              spec.BackupStorageTypePersistentVolume,
		CleanupBackupIfDeleted:   true,
	}
	origEtcd := makeEtcdCluster("test-etcd-", 3)
	origEtcd = etcdClusterWithBackup(origEtcd, backupPolicy)
	testEtcd, err := createEtcdCluster(f, origEtcd)
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
		// TODO: add checking of removal of backup pod
	}()

	names, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60)
	if err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 3 members cluster")
	if err := waitBackupPodUp(f, testEtcd.Name, 60*time.Second); err != nil {
		t.Fatalf("failed to create backup pod: %v", err)
	}
	// TODO: There might be race that controller will recover members between
	// 		these members are deleted individually.
	if err := killMembers(f, names[0], names[1]); err != nil {
		t.Fatal(err)
	}
	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 120); err != nil {
		t.Fatalf("failed to resize to 3 members etcd cluster: %v", err)
	}
	// TODO: add checking of data in etcd
}

func waitBackupPodUp(f *framework.Framework, clusterName string, timeout time.Duration) error {
	w, err := f.KubeClient.Pods(f.Namespace.Name).Watch(api.ListOptions{
		LabelSelector: labels.SelectorFromSet(map[string]string{
			"app":          k8sutil.BackupPodSelectorAppField,
			"etcd_cluster": clusterName,
		}),
	})
	_, err = watch.Until(timeout, w, k8sclient.PodRunning)
	return err
}

func waitUntilSizeReached(f *framework.Framework, clusterName string, size, timeout int) ([]string, error) {
	return waitSizeReachedWithFilter(f, clusterName, size, timeout, func(*api.Pod) bool { return true })
}

func waitSizeReachedWithFilter(f *framework.Framework, clusterName string, size, timeout int, filterPod func(*api.Pod) bool) ([]string, error) {
	var names []string
	err := wait.Poll(5*time.Second, time.Duration(timeout)*time.Second, func() (done bool, err error) {
		podList, err := f.KubeClient.Pods(f.Namespace.Name).List(k8sutil.EtcdPodListOpt(clusterName))
		if err != nil {
			return false, err
		}
		names = nil
		for i := range podList.Items {
			pod := &podList.Items[i]
			if pod.Status.Phase == api.PodRunning {
				names = append(names, pod.Name)
			}
		}
		fmt.Printf("waiting size (%d), etcd pods: %v\n", size, names)
		if len(names) != size {
			return false, nil
		}
		// TODO: check etcd member membership
		return true, nil
	})
	if err != nil {
		return nil, err
	}
	return names, nil
}

func killMembers(f *framework.Framework, names ...string) error {
	for _, name := range names {
		err := f.KubeClient.Pods(f.Namespace.Name).Delete(name, nil)
		if err != nil {
			return err
		}
	}
	return nil
}

func makeEtcdCluster(genName string, size int) *spec.EtcdCluster {
	return &spec.EtcdCluster{
		TypeMeta: unversioned.TypeMeta{
			Kind:       "EtcdCluster",
			APIVersion: "coreos.com/v1",
		},
		ObjectMeta: api.ObjectMeta{
			GenerateName: genName,
		},
		Spec: spec.ClusterSpec{
			Size: size,
		},
	}
}

func etcdClusterWithBackup(ec *spec.EtcdCluster, backupPolicy *spec.BackupPolicy) *spec.EtcdCluster {
	ec.Spec.Backup = backupPolicy
	return ec
}
func etcdClusterWithVersion(ec *spec.EtcdCluster, version string) *spec.EtcdCluster {
	ec.Spec.Version = version
	return ec
}

func createEtcdCluster(f *framework.Framework, e *spec.EtcdCluster) (*spec.EtcdCluster, error) {
	b, err := json.Marshal(e)
	if err != nil {
		return nil, err
	}
	resp, err := f.KubeClient.Client.Post(
		fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters", f.MasterHost, f.Namespace.Name),
		"application/json", bytes.NewReader(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated {
		return nil, fmt.Errorf("unexpected status: %v", resp.Status)
	}
	decoder := json.NewDecoder(resp.Body)
	res := &spec.EtcdCluster{}
	if err := decoder.Decode(res); err != nil {
		return nil, err
	}
	fmt.Printf("created etcd cluster: %v\n", res.Name)
	return res, nil
}

func updateEtcdCluster(f *framework.Framework, e *spec.EtcdCluster) error {
	b, err := json.Marshal(e)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("PUT",
		fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters/%s", f.MasterHost, f.Namespace.Name, e.Name),
		bytes.NewReader(b))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := f.KubeClient.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status: %v", resp.Status)
	}
	return nil
}

func deleteEtcdCluster(f *framework.Framework, name string) error {
	fmt.Printf("deleting etcd cluster: %v\n", name)
	podList, err := f.KubeClient.Pods(f.Namespace.Name).List(k8sutil.EtcdPodListOpt(name))
	if err != nil {
		return err
	}
	fmt.Println("etcd pods ======")
	for i := range podList.Items {
		pod := &podList.Items[i]
		fmt.Printf("pod (%v): status (%v)\n", pod.Name, pod.Status.Phase)
		buf := bytes.NewBuffer(nil)

		if pod.Status.Phase == api.PodFailed {
			if err := getLogs(f.KubeClient, f.Namespace.Name, pod.Name, "etcd", buf); err != nil {
				return err
			}
			fmt.Println(pod.Name, "logs ===")
			fmt.Println(buf.String())
			fmt.Println(pod.Name, "logs END ===")
		}
	}

	buf := bytes.NewBuffer(nil)
	if err := getLogs(f.KubeClient, f.Namespace.Name, "kube-etcd-controller", "kube-etcd-controller", buf); err != nil {
		return err
	}
	fmt.Println("kube-etcd-controller logs ===")
	fmt.Println(buf.String())
	fmt.Println("kube-etcd-controller logs END ===")

	req, err := http.NewRequest("DELETE",
		fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters/%s", f.MasterHost, f.Namespace.Name, name), nil)
	if err != nil {
		return err
	}
	resp, err := f.KubeClient.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status: %v", resp.Status)
	}
	return nil
}

func getLogs(kubecli *k8sclient.Client, ns, p, c string, out io.Writer) error {
	req := kubecli.RESTClient.Get().
		Namespace(ns).
		Resource("pods").
		Name(p).
		SubResource("log").
		Param("container", c).
		Param("tailLines", "20")

	readCloser, err := req.Stream()
	if err != nil {
		return err
	}
	defer readCloser.Close()

	_, err = io.Copy(out, readCloser)
	return err
}
<commit_msg>e2e: fix waitBackupPodUp<commit_after>// Copyright 2016 The kube-etcd-controller 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 e2e

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"testing"
	"time"

	"github.com/coreos/kube-etcd-controller/pkg/spec"
	"github.com/coreos/kube-etcd-controller/pkg/util/k8sutil"
	"github.com/coreos/kube-etcd-controller/test/e2e/framework"
	"k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/api/unversioned"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
	"k8s.io/kubernetes/pkg/labels"
	"k8s.io/kubernetes/pkg/util/wait"
)

func TestCreateCluster(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 3))
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
	}
}

func TestResizeCluster3to5(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 3))
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 3 members cluster")

	testEtcd.Spec.Size = 5
	if err := updateEtcdCluster(f, testEtcd); err != nil {
		t.Fatal(err)
	}

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 5, 60); err != nil {
		t.Fatalf("failed to resize to 5 members etcd cluster: %v", err)
	}
}

func TestResizeCluster5to3(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 5))
	if err != nil {
		t.Fatal(err)
	}

	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 5, 90); err != nil {
		t.Fatalf("failed to create 5 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 5 members cluster")

	testEtcd.Spec.Size = 3
	if err := updateEtcdCluster(f, testEtcd); err != nil {
		t.Fatal(err)
	}

	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to resize to 3 members etcd cluster: %v", err)
	}
}

func TestOneMemberRecovery(t *testing.T) {
	f := framework.Global
	testEtcd, err := createEtcdCluster(f, makeEtcdCluster("test-etcd-", 3))
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
	}()

	names, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60)
	if err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 3 members cluster")

	if err := killMembers(f, names[0]); err != nil {
		t.Fatal(err)
	}
	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60); err != nil {
		t.Fatalf("failed to resize to 3 members etcd cluster: %v", err)
	}
}

func TestDisasterRecovery(t *testing.T) {
	f := framework.Global
	backupPolicy := &spec.BackupPolicy{
		SnapshotIntervalInSecond: 120,
		MaxSnapshot:              5,
		VolumeSizeInMB:           512,
		StorageType:              spec.BackupStorageTypePersistentVolume,
		CleanupBackupIfDeleted:   true,
	}
	origEtcd := makeEtcdCluster("test-etcd-", 3)
	origEtcd = etcdClusterWithBackup(origEtcd, backupPolicy)
	testEtcd, err := createEtcdCluster(f, origEtcd)
	if err != nil {
		t.Fatal(err)
	}
	defer func() {
		if err := deleteEtcdCluster(f, testEtcd.Name); err != nil {
			t.Fatal(err)
		}
		// TODO: add checking of removal of backup pod
	}()

	names, err := waitUntilSizeReached(f, testEtcd.Name, 3, 60)
	if err != nil {
		t.Fatalf("failed to create 3 members etcd cluster: %v", err)
		return
	}
	fmt.Println("reached to 3 members cluster")
	if err := waitBackupPodUp(f, testEtcd.Name, 60*time.Second); err != nil {
		t.Fatalf("failed to create backup pod: %v", err)
	}
	// TODO: There might be race that controller will recover members between
	// 		these members are deleted individually.
	if err := killMembers(f, names[0], names[1]); err != nil {
		t.Fatal(err)
	}
	if _, err := waitUntilSizeReached(f, testEtcd.Name, 3, 120); err != nil {
		t.Fatalf("failed to resize to 3 members etcd cluster: %v", err)
	}
	// TODO: add checking of data in etcd
}

func waitBackupPodUp(f *framework.Framework, clusterName string, timeout time.Duration) error {
	return wait.Poll(5*time.Second, timeout, func() (done bool, err error) {
		podList, err := f.KubeClient.Pods(f.Namespace.Name).List(api.ListOptions{
			LabelSelector: labels.SelectorFromSet(map[string]string{
				"app":          k8sutil.BackupPodSelectorAppField,
				"etcd_cluster": clusterName,
			})})
		if err != nil {
			return false, err
		}
		return len(podList.Items) > 0, nil
	})
}

func waitUntilSizeReached(f *framework.Framework, clusterName string, size, timeout int) ([]string, error) {
	return waitSizeReachedWithFilter(f, clusterName, size, timeout, func(*api.Pod) bool { return true })
}

func waitSizeReachedWithFilter(f *framework.Framework, clusterName string, size, timeout int, filterPod func(*api.Pod) bool) ([]string, error) {
	var names []string
	err := wait.Poll(5*time.Second, time.Duration(timeout)*time.Second, func() (done bool, err error) {
		podList, err := f.KubeClient.Pods(f.Namespace.Name).List(k8sutil.EtcdPodListOpt(clusterName))
		if err != nil {
			return false, err
		}
		names = nil
		for i := range podList.Items {
			pod := &podList.Items[i]
			if pod.Status.Phase == api.PodRunning {
				names = append(names, pod.Name)
			}
		}
		fmt.Printf("waiting size (%d), etcd pods: %v\n", size, names)
		if len(names) != size {
			return false, nil
		}
		// TODO: check etcd member membership
		return true, nil
	})
	if err != nil {
		return nil, err
	}
	return names, nil
}

func killMembers(f *framework.Framework, names ...string) error {
	for _, name := range names {
		err := f.KubeClient.Pods(f.Namespace.Name).Delete(name, nil)
		if err != nil {
			return err
		}
	}
	return nil
}

func makeEtcdCluster(genName string, size int) *spec.EtcdCluster {
	return &spec.EtcdCluster{
		TypeMeta: unversioned.TypeMeta{
			Kind:       "EtcdCluster",
			APIVersion: "coreos.com/v1",
		},
		ObjectMeta: api.ObjectMeta{
			GenerateName: genName,
		},
		Spec: spec.ClusterSpec{
			Size: size,
		},
	}
}

func etcdClusterWithBackup(ec *spec.EtcdCluster, backupPolicy *spec.BackupPolicy) *spec.EtcdCluster {
	ec.Spec.Backup = backupPolicy
	return ec
}
func etcdClusterWithVersion(ec *spec.EtcdCluster, version string) *spec.EtcdCluster {
	ec.Spec.Version = version
	return ec
}

func createEtcdCluster(f *framework.Framework, e *spec.EtcdCluster) (*spec.EtcdCluster, error) {
	b, err := json.Marshal(e)
	if err != nil {
		return nil, err
	}
	resp, err := f.KubeClient.Client.Post(
		fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters", f.MasterHost, f.Namespace.Name),
		"application/json", bytes.NewReader(b))
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusCreated {
		return nil, fmt.Errorf("unexpected status: %v", resp.Status)
	}
	decoder := json.NewDecoder(resp.Body)
	res := &spec.EtcdCluster{}
	if err := decoder.Decode(res); err != nil {
		return nil, err
	}
	fmt.Printf("created etcd cluster: %v\n", res.Name)
	return res, nil
}

func updateEtcdCluster(f *framework.Framework, e *spec.EtcdCluster) error {
	b, err := json.Marshal(e)
	if err != nil {
		return err
	}
	req, err := http.NewRequest("PUT",
		fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters/%s", f.MasterHost, f.Namespace.Name, e.Name),
		bytes.NewReader(b))
	if err != nil {
		return err
	}
	req.Header.Set("Content-Type", "application/json")
	resp, err := f.KubeClient.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status: %v", resp.Status)
	}
	return nil
}

func deleteEtcdCluster(f *framework.Framework, name string) error {
	fmt.Printf("deleting etcd cluster: %v\n", name)
	podList, err := f.KubeClient.Pods(f.Namespace.Name).List(k8sutil.EtcdPodListOpt(name))
	if err != nil {
		return err
	}
	fmt.Println("etcd pods ======")
	for i := range podList.Items {
		pod := &podList.Items[i]
		fmt.Printf("pod (%v): status (%v)\n", pod.Name, pod.Status.Phase)
		buf := bytes.NewBuffer(nil)

		if pod.Status.Phase == api.PodFailed {
			if err := getLogs(f.KubeClient, f.Namespace.Name, pod.Name, "etcd", buf); err != nil {
				return err
			}
			fmt.Println(pod.Name, "logs ===")
			fmt.Println(buf.String())
			fmt.Println(pod.Name, "logs END ===")
		}
	}

	buf := bytes.NewBuffer(nil)
	if err := getLogs(f.KubeClient, f.Namespace.Name, "kube-etcd-controller", "kube-etcd-controller", buf); err != nil {
		return err
	}
	fmt.Println("kube-etcd-controller logs ===")
	fmt.Println(buf.String())
	fmt.Println("kube-etcd-controller logs END ===")

	req, err := http.NewRequest("DELETE",
		fmt.Sprintf("%s/apis/coreos.com/v1/namespaces/%s/etcdclusters/%s", f.MasterHost, f.Namespace.Name, name), nil)
	if err != nil {
		return err
	}
	resp, err := f.KubeClient.Client.Do(req)
	if err != nil {
		return err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return fmt.Errorf("unexpected status: %v", resp.Status)
	}
	return nil
}

func getLogs(kubecli *k8sclient.Client, ns, p, c string, out io.Writer) error {
	req := kubecli.RESTClient.Get().
		Namespace(ns).
		Resource("pods").
		Name(p).
		SubResource("log").
		Param("container", c).
		Param("tailLines", "20")

	readCloser, err := req.Stream()
	if err != nil {
		return err
	}
	defer readCloser.Close()

	_, err = io.Copy(out, readCloser)
	return err
}
<|endoftext|>


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

// These tests all do one conflict-free operation while a user is unstaged.

package test

import (
	"testing"
	"time"
)

// bob creates a file while running the journal.
func TestJournalSimple(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			mkfile("a/b", "hello"),
			// Check the data -- this should read from the journal if
			// it hasn't flushed yet.
			lsdir("a/", m{"b$": "FILE"}),
			read("a/b", "hello"),
			flushJournal(),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE"}),
			read("a/b", "hello"),
		),
	)
}

// bob exclusively creates a file while running the journal.  For now
// this is treated like a normal file create.
func TestJournalExclWrite(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			mkfile("a/c", "hello"),
			mkfileexcl("a/b"),
			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
			flushJournal(),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
		),
	)
}

// bob creates a conflicting file while running the journal.
func TestJournalCRSimple(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			pauseJournal(),
			mkfile("a/b", "uh oh"),
			// Don't flush yet.
		),
		as(alice,
			mkfile("a/b", "hello"),
		),
		as(bob, noSync(),
			resumeJournal(),
			// This should kick off conflict resolution.
			flushJournal(),
		),
		as(bob,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
		),
	)
}

// bob writes a multi-block file that conflicts with a file created by
// alice when journaling is on.
func TestJournalCrConflictUnmergedWriteMultiblockFile(t *testing.T) {
	test(t, journal(), blockSize(20),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			disableUpdates(),
		),
		as(alice,
			write("a/b", "hello"),
		),
		as(bob, noSync(),
			write("a/b", ntimesString(15, "0123456789")),
			flushJournal(),
			reenableUpdates(),
		),
		as(bob,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), ntimesString(15, "0123456789")),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), ntimesString(15, "0123456789")),
		),
	)
}

// Check that simple quota reclamation works when journaling is enabled.
func TestJournalQRSimple(t *testing.T) {
	test(t, journal(),
		users("alice"),
		as(alice,
			mkfile("a", "hello"),
			addTime(1*time.Minute),
			enableJournal(),
			mkfile("b", "hello2"),
			rm("b"),
			addTime(2*time.Minute),
			flushJournal(),
			pauseJournal(),
			addTime(2*time.Minute),
			mkfile("c", "hello3"),
			mkfile("d", "hello4"),
			addTime(2*time.Minute),
			forceQuotaReclamation(),
			resumeJournal(),
		),
	)
}

// bob rekeys for charlie while journaling, but gets converted to a
// conflict branch so the rekey fails.
func TestJournalRekeyErrorAfterConflict(t *testing.T) {
	test(t, journal(),
		users("alice", "bob", "charlie"),
		inPrivateTlf("alice,bob,charlie@twitter"),
		as(alice,
			mkfile("a", "hello"),
		),
		as(bob,
			enableJournal(),
			mkfile("b", "hello2"),
			read("a", "hello"),
			read("b", "hello2"),
			disableUpdates(),
		),
		as(alice,
			// Cause conflict with bob.
			mkfile("c", "hello3"),
		),
		as(bob, noSync(),
			pauseJournal(),
			mkfile("d", "hello4"),
		),
		as(bob, noSync(), stallDelegateOnMDPut()),
		addNewAssertion("charlie", "charlie@twitter"),
		parallel(
			as(bob, noSync(),
				expectError(rekey(),
					"Conflict during a rekey, not retrying"),
			),
			// Don't let the journal flush until the rekey is about to
			// hit it, so that bob is not staged yet.
			as(bob, noSync(),
				waitForStalledMDPut(),
				resumeJournal(),
				undoStallOnMDPut(),
				reenableUpdates(),
				flushJournal(),
			),
		),
		as(charlie,
			expectError(initRoot(), "MDServer Unauthorized"),
		),
		as(alice,
			rekey(),
		),
		inPrivateTlfNonCanonical("alice,bob,charlie@twitter",
			"alice,bob,charlie"),
		as(bob, // ensure bob syncs his resolution
			read("a", "hello"),
			read("b", "hello2"),
			read("c", "hello3"),
			read("d", "hello4"),
		),
		as(charlie,
			read("a", "hello"),
			read("b", "hello2"),
			read("c", "hello3"),
			read("d", "hello4"),
		),
	)
}
<commit_msg>test: multiple CRs when journaling is enabled<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

// These tests all do one conflict-free operation while a user is unstaged.

package test

import (
	"testing"
	"time"
)

// bob creates a file while running the journal.
func TestJournalSimple(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			mkfile("a/b", "hello"),
			// Check the data -- this should read from the journal if
			// it hasn't flushed yet.
			lsdir("a/", m{"b$": "FILE"}),
			read("a/b", "hello"),
			flushJournal(),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE"}),
			read("a/b", "hello"),
		),
	)
}

// bob exclusively creates a file while running the journal.  For now
// this is treated like a normal file create.
func TestJournalExclWrite(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			mkfile("a/c", "hello"),
			mkfileexcl("a/b"),
			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
			flushJournal(),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", "c$": "FILE"}),
		),
	)
}

// bob creates a conflicting file while running the journal.
func TestJournalCrSimple(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			pauseJournal(),
			mkfile("a/b", "uh oh"),
			// Don't flush yet.
		),
		as(alice,
			mkfile("a/b", "hello"),
		),
		as(bob, noSync(),
			resumeJournal(),
			// This should kick off conflict resolution.
			flushJournal(),
		),
		as(bob,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
		),
	)
}

// bob creates a conflicting file while running the journal.
func TestJournalDoubleCrSimple(t *testing.T) {
	test(t, journal(),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			pauseJournal(),
			mkfile("a/b", "uh oh"),
			// Don't flush yet.
		),
		as(alice,
			mkfile("a/b", "hello"),
		),
		as(bob, noSync(),
			resumeJournal(),
			// This should kick off conflict resolution.
			flushJournal(),
		),
		as(bob,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
		),
		as(bob,
			pauseJournal(),
			mkfile("a/c", "uh oh"),
			// Don't flush yet.
		),
		as(alice,
			mkfile("a/c", "hello"),
		),
		as(bob, noSync(),
			resumeJournal(),
			// This should kick off conflict resolution.
			flushJournal(),
		),
		as(bob,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
			read("a/c", "hello"),
			read(crname("a/c", bob), "uh oh"),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE", "c$": "FILE", crnameEsc("c", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), "uh oh"),
			read("a/c", "hello"),
			read(crname("a/c", bob), "uh oh"),
		),
	)
}

// bob writes a multi-block file that conflicts with a file created by
// alice when journaling is on.
func TestJournalCrConflictUnmergedWriteMultiblockFile(t *testing.T) {
	test(t, journal(), blockSize(20),
		users("alice", "bob"),
		as(alice,
			mkdir("a"),
		),
		as(bob,
			enableJournal(),
			disableUpdates(),
		),
		as(alice,
			write("a/b", "hello"),
		),
		as(bob, noSync(),
			write("a/b", ntimesString(15, "0123456789")),
			flushJournal(),
			reenableUpdates(),
		),
		as(bob,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), ntimesString(15, "0123456789")),
		),
		as(alice,
			lsdir("a/", m{"b$": "FILE", crnameEsc("b", bob): "FILE"}),
			read("a/b", "hello"),
			read(crname("a/b", bob), ntimesString(15, "0123456789")),
		),
	)
}

// Check that simple quota reclamation works when journaling is enabled.
func TestJournalQRSimple(t *testing.T) {
	test(t, journal(),
		users("alice"),
		as(alice,
			mkfile("a", "hello"),
			addTime(1*time.Minute),
			enableJournal(),
			mkfile("b", "hello2"),
			rm("b"),
			addTime(2*time.Minute),
			flushJournal(),
			pauseJournal(),
			addTime(2*time.Minute),
			mkfile("c", "hello3"),
			mkfile("d", "hello4"),
			addTime(2*time.Minute),
			forceQuotaReclamation(),
			resumeJournal(),
		),
	)
}

// bob rekeys for charlie while journaling, but gets converted to a
// conflict branch so the rekey fails.
func TestJournalRekeyErrorAfterConflict(t *testing.T) {
	test(t, journal(),
		users("alice", "bob", "charlie"),
		inPrivateTlf("alice,bob,charlie@twitter"),
		as(alice,
			mkfile("a", "hello"),
		),
		as(bob,
			enableJournal(),
			mkfile("b", "hello2"),
			read("a", "hello"),
			read("b", "hello2"),
			disableUpdates(),
		),
		as(alice,
			// Cause conflict with bob.
			mkfile("c", "hello3"),
		),
		as(bob, noSync(),
			pauseJournal(),
			mkfile("d", "hello4"),
		),
		as(bob, noSync(), stallDelegateOnMDPut()),
		addNewAssertion("charlie", "charlie@twitter"),
		parallel(
			as(bob, noSync(),
				expectError(rekey(),
					"Conflict during a rekey, not retrying"),
			),
			// Don't let the journal flush until the rekey is about to
			// hit it, so that bob is not staged yet.
			as(bob, noSync(),
				waitForStalledMDPut(),
				resumeJournal(),
				undoStallOnMDPut(),
				reenableUpdates(),
				flushJournal(),
			),
		),
		as(charlie,
			expectError(initRoot(), "MDServer Unauthorized"),
		),
		as(alice,
			rekey(),
		),
		inPrivateTlfNonCanonical("alice,bob,charlie@twitter",
			"alice,bob,charlie"),
		as(bob, // ensure bob syncs his resolution
			read("a", "hello"),
			read("b", "hello2"),
			read("c", "hello3"),
			read("d", "hello4"),
		),
		as(charlie,
			read("a", "hello"),
			read("b", "hello2"),
			read("c", "hello3"),
			read("d", "hello4"),
		),
	)
}
<|endoftext|>


<commit_before>package test

import (
	"sync"
	"testing"
	"time"

	"golang.org/x/net/websocket"

	"github.com/neptulon/neptulon"
	"github.com/neptulon/neptulon/middleware"
	"github.com/neptulon/randstr"
)

var (
	msg1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
	msg2 = "In sit amet lectus felis, at pellentesque turpis."
	msg3 = "Nunc urna enim, cursus varius aliquet ac, imperdiet eget tellus."
	msg4 = randstr.Get(45 * 1000)       // 0.45 MB
	msg5 = randstr.Get(5 * 1000 * 1000) // 5.0 MB
)

type echoMsg struct {
	Message string `json:"message"`
}

func TestEcho(t *testing.T) {
	sh := NewServerHelper(t).Start()
	defer sh.Close()

	rout := middleware.NewRouter()
	sh.Middleware(rout.Middleware)
	rout.Request("echo", middleware.Echo)

	ch := sh.GetConnHelper().Connect()
	defer ch.Close()

	ch.SendRequest("echo", echoMsg{Message: "Hello!"}, func(ctx *neptulon.ResCtx) error {
		var msg echoMsg
		if err := ctx.Result(&msg); err != nil {
			t.Fatal(err)
		}
		if msg.Message != "Hello!" {
			t.Fatalf("expected: %v got: %v", "Hello!", msg.Message)
		}
		return nil
	})
}

func TestEchoBasic(t *testing.T) { // not using any test helper at all
	s := neptulon.NewServer("127.0.0.1:3010")
	go s.Start()
	defer s.Close()
	time.Sleep(time.Millisecond)

	var wg sync.WaitGroup
	s.Middleware(func(ctx *neptulon.ReqCtx) error {
		defer wg.Done()
		t.Log("Request received:", ctx.Method)
		ctx.Res = "response-wow!"
		return ctx.Next()
	})

	wg.Add(1)

	origin := "http://127.0.0.1"
	url := "ws://127.0.0.1:3010"
	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		t.Fatal(err)
	}
	if err := websocket.JSON.Send(ws, neptulon.Request{ID: "123", Method: "test"}); err != nil {
		t.Fatal(err)
	}
	var res neptulon.Response
	if err := websocket.JSON.Receive(ws, &res); err != nil {
		t.Fatal(err)
	}
	t.Log("Got response:", res)

	wg.Wait()
}

func TestTLS(t *testing.T) {
	// todo: client cert etc.
}
<commit_msg>add placeholder tls test<commit_after>package test

import (
	"sync"
	"testing"
	"time"

	"golang.org/x/net/websocket"

	"github.com/neptulon/neptulon"
	"github.com/neptulon/neptulon/middleware"
	"github.com/neptulon/randstr"
)

var (
	msg1 = "Lorem ipsum dolor sit amet, consectetur adipiscing elit."
	msg2 = "In sit amet lectus felis, at pellentesque turpis."
	msg3 = "Nunc urna enim, cursus varius aliquet ac, imperdiet eget tellus."
	msg4 = randstr.Get(45 * 1000)       // 0.45 MB
	msg5 = randstr.Get(5 * 1000 * 1000) // 5.0 MB
)

type echoMsg struct {
	Message string `json:"message"`
}

func TestEcho(t *testing.T) {
	sh := NewServerHelper(t).Start()
	defer sh.Close()

	rout := middleware.NewRouter()
	sh.Middleware(rout.Middleware)
	rout.Request("echo", middleware.Echo)

	ch := sh.GetConnHelper().Connect()
	defer ch.Close()

	ch.SendRequest("echo", echoMsg{Message: "Hello!"}, func(ctx *neptulon.ResCtx) error {
		var msg echoMsg
		if err := ctx.Result(&msg); err != nil {
			t.Fatal(err)
		}
		if msg.Message != "Hello!" {
			t.Fatalf("expected: %v got: %v", "Hello!", msg.Message)
		}
		return nil
	})
}

func TestEchoWithoutTestHelpers(t *testing.T) {
	s := neptulon.NewServer("127.0.0.1:3010")
	go s.Start()
	defer s.Close()
	time.Sleep(time.Millisecond)

	var wg sync.WaitGroup
	s.Middleware(func(ctx *neptulon.ReqCtx) error {
		defer wg.Done()
		t.Log("Request received:", ctx.Method)
		ctx.Res = "response-wow!"
		return ctx.Next()
	})

	wg.Add(1)

	origin := "http://127.0.0.1"
	url := "ws://127.0.0.1:3010"
	ws, err := websocket.Dial(url, "", origin)
	if err != nil {
		t.Fatal(err)
	}
	if err := websocket.JSON.Send(ws, neptulon.Request{ID: "123", Method: "test"}); err != nil {
		t.Fatal(err)
	}
	var res neptulon.Response
	if err := websocket.JSON.Receive(ws, &res); err != nil {
		t.Fatal(err)
	}
	t.Log("Got response:", res)

	wg.Wait()
}

func TestTLS(t *testing.T) {
	// sh := NewServerHelper(t).UseTLS().Start()
	// defer sh.Close()
	//
	// rout := middleware.NewRouter()
	// sh.Middleware(rout.Middleware)
	// rout.Request("echo", middleware.Echo)
	//
	// ch := sh.GetConnHelper().UseTLS().Connect()
	// defer ch.Close()
	//
	// ch.SendRequest("echo", echoMsg{Message: "Hello!"}, func(ctx *neptulon.ResCtx) error {
	// 	var msg echoMsg
	// 	if err := ctx.Result(&msg); err != nil {
	// 		t.Fatal(err)
	// 	}
	// 	if msg.Message != "Hello!" {
	// 		t.Fatalf("expected: %v got: %v", "Hello!", msg.Message)
	// 	}
	// 	return nil
	// })
}
<|endoftext|>


<commit_before>package test

import (
	"runtime"
	"testing"
	"time"

	"github.com/nats-io/nats"
)

func TestBadChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	if err := ec.BindSendChan("foo", "not a chan"); err == nil {
		t.Fatalf("Expected an Error when sending a non-channel\n")
	}

	if _, err := ec.BindRecvChan("foo", "not a chan"); err == nil {
		t.Fatalf("Expected an Error when sending a non-channel\n")
	}

	if err := ec.BindSendChan("foo", "not a chan"); err != nats.ErrChanArg {
		t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
	}

	if _, err := ec.BindRecvChan("foo", "not a chan"); err != nats.ErrChanArg {
		t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
	}
}

func TestSimpleSendChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	recv := make(chan bool)

	numSent := int32(22)
	ch := make(chan int32)

	if err := ec.BindSendChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	ec.Subscribe("foo", func(num int32) {
		if num != numSent {
			t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
		}
		recv <- true
	})

	// Send to 'foo'
	ch <- numSent

	if e := Wait(recv); e != nil {
		if ec.LastError() != nil {
			e = ec.LastError()
		}
		t.Fatalf("Did not receive the message: %s", e)
	}
	close(ch)
}

func TestFailedChannelSend(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	nc := ec.Conn
	ch := make(chan bool)
	wch := make(chan bool)

	nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
		wch <- true
	}

	if err := ec.BindSendChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a receive channel: %v\n", err)
	}

	nc.Flush()
	nc.Close()

	// This should trigger an async error.
	ch <- true

	if e := Wait(wch); e != nil {
		t.Fatal("Failed to call async err handler")
	}
}

func TestSimpleRecvChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	numSent := int32(22)
	ch := make(chan int32)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a receive channel: %v\n", err)
	}

	ec.Publish("foo", numSent)

	// Receive from 'foo'
	select {
	case num := <-ch:
		if num != numSent {
			t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
		}
	case <-time.After(1 * time.Second):
		t.Fatalf("Failed to receive a value, timed-out\n")
	}
	close(ch)
}

func TestQueueRecvChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	numSent := int32(22)
	ch := make(chan int32)

	if _, err := ec.BindRecvQueueChan("foo", "bar", ch); err != nil {
		t.Fatalf("Failed to bind to a queue receive channel: %v\n", err)
	}

	ec.Publish("foo", numSent)

	// Receive from 'foo'
	select {
	case num := <-ch:
		if num != numSent {
			t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
		}
	case <-time.After(1 * time.Second):
		t.Fatalf("Failed to receive a value, timed-out\n")
	}
	close(ch)
}

func TestDecoderErrRecvChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()
	nc := ec.Conn
	wch := make(chan bool)

	nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
		wch <- true
	}

	ch := make(chan *int32)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	ec.Publish("foo", "Hello World")

	if e := Wait(wch); e != nil {
		t.Fatal("Failed to call async err handler")
	}
}

func TestRecvChanPanicOnClosedChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	ch := make(chan int)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	close(ch)
	ec.Publish("foo", 22)
	ec.Flush()
}

func TestRecvChanAsyncLeakGoRoutines(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	before := runtime.NumGoroutine()

	ch := make(chan int)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	// Close the receive Channel
	close(ch)

	// The publish will trugger the close and shutdown of the Go routines
	ec.Publish("foo", 22)
	ec.Flush()

	time.Sleep(50 * time.Millisecond)

	after := runtime.NumGoroutine()

	if before != after {
		t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", after-before)
	}
}

func TestRecvChanLeakGoRoutines(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	before := runtime.NumGoroutine()

	ch := make(chan int)

	sub, err := ec.BindRecvChan("foo", ch)
	if err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}
	sub.Unsubscribe()

	// Sleep a bit to wait for the Go routine to exit.
	time.Sleep(100 * time.Millisecond)

	delta := (runtime.NumGoroutine() - before)

	if delta > 0 {
		t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", delta)
	}
}

func TestRecvChanMultipleMessages(t *testing.T) {
	// Make sure we can receive more than one message.
	// In response to #25, which is a bug from fixing #22.

	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	// Num to send, should == len of messages queued.
	size := 10

	ch := make(chan int, size)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	for i := 0; i < size; i++ {
		ec.Publish("foo", 22)
	}
	ec.Flush()
	time.Sleep(10 * time.Millisecond)

	if lch := len(ch); lch != size {
		t.Fatalf("Expected %d messages queued, got %d.", size, lch)
	}
}

func BenchmarkPublishSpeedViaChan(b *testing.B) {
	b.StopTimer()

	s := RunDefaultServer()
	defer s.Shutdown()

	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		b.Fatalf("Could not connect: %v\n", err)
	}
	ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
	defer ec.Close()

	ch := make(chan int32, 1024)
	if err := ec.BindSendChan("foo", ch); err != nil {
		b.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	b.StartTimer()

	num := int32(22)

	for i := 0; i < b.N; i++ {
		ch <- num
	}
	// Make sure they are all processed.
	nc.Flush()
	b.StopTimer()
}
<commit_msg>sleep adjustment<commit_after>package test

import (
	"runtime"
	"testing"
	"time"

	"github.com/nats-io/nats"
)

func TestBadChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	if err := ec.BindSendChan("foo", "not a chan"); err == nil {
		t.Fatalf("Expected an Error when sending a non-channel\n")
	}

	if _, err := ec.BindRecvChan("foo", "not a chan"); err == nil {
		t.Fatalf("Expected an Error when sending a non-channel\n")
	}

	if err := ec.BindSendChan("foo", "not a chan"); err != nats.ErrChanArg {
		t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
	}

	if _, err := ec.BindRecvChan("foo", "not a chan"); err != nats.ErrChanArg {
		t.Fatalf("Expected an ErrChanArg when sending a non-channel\n")
	}
}

func TestSimpleSendChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	recv := make(chan bool)

	numSent := int32(22)
	ch := make(chan int32)

	if err := ec.BindSendChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	ec.Subscribe("foo", func(num int32) {
		if num != numSent {
			t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
		}
		recv <- true
	})

	// Send to 'foo'
	ch <- numSent

	if e := Wait(recv); e != nil {
		if ec.LastError() != nil {
			e = ec.LastError()
		}
		t.Fatalf("Did not receive the message: %s", e)
	}
	close(ch)
}

func TestFailedChannelSend(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	nc := ec.Conn
	ch := make(chan bool)
	wch := make(chan bool)

	nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
		wch <- true
	}

	if err := ec.BindSendChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a receive channel: %v\n", err)
	}

	nc.Flush()
	nc.Close()

	// This should trigger an async error.
	ch <- true

	if e := Wait(wch); e != nil {
		t.Fatal("Failed to call async err handler")
	}
}

func TestSimpleRecvChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	numSent := int32(22)
	ch := make(chan int32)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a receive channel: %v\n", err)
	}

	ec.Publish("foo", numSent)

	// Receive from 'foo'
	select {
	case num := <-ch:
		if num != numSent {
			t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
		}
	case <-time.After(1 * time.Second):
		t.Fatalf("Failed to receive a value, timed-out\n")
	}
	close(ch)
}

func TestQueueRecvChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	numSent := int32(22)
	ch := make(chan int32)

	if _, err := ec.BindRecvQueueChan("foo", "bar", ch); err != nil {
		t.Fatalf("Failed to bind to a queue receive channel: %v\n", err)
	}

	ec.Publish("foo", numSent)

	// Receive from 'foo'
	select {
	case num := <-ch:
		if num != numSent {
			t.Fatalf("Failed to receive correct value: %d vs %d\n", num, numSent)
		}
	case <-time.After(1 * time.Second):
		t.Fatalf("Failed to receive a value, timed-out\n")
	}
	close(ch)
}

func TestDecoderErrRecvChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()
	nc := ec.Conn
	wch := make(chan bool)

	nc.Opts.AsyncErrorCB = func(c *nats.Conn, s *nats.Subscription, e error) {
		wch <- true
	}

	ch := make(chan *int32)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	ec.Publish("foo", "Hello World")

	if e := Wait(wch); e != nil {
		t.Fatal("Failed to call async err handler")
	}
}

func TestRecvChanPanicOnClosedChan(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	ch := make(chan int)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	close(ch)
	ec.Publish("foo", 22)
	ec.Flush()
}

func TestRecvChanAsyncLeakGoRoutines(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	before := runtime.NumGoroutine()

	ch := make(chan int)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	// Close the receive Channel
	close(ch)

	// The publish will trigger the close and shutdown of the Go routines
	ec.Publish("foo", 22)
	ec.Flush()

	time.Sleep(100 * time.Millisecond)

	after := runtime.NumGoroutine()

	if before != after {
		t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", after-before)
	}
}

func TestRecvChanLeakGoRoutines(t *testing.T) {
	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	before := runtime.NumGoroutine()

	ch := make(chan int)

	sub, err := ec.BindRecvChan("foo", ch)
	if err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}
	sub.Unsubscribe()

	// Sleep a bit to wait for the Go routine to exit.
	time.Sleep(100 * time.Millisecond)

	delta := (runtime.NumGoroutine() - before)

	if delta > 0 {
		t.Fatalf("Leaked Go routine(s) : %d, closing channel should have closed them\n", delta)
	}
}

func TestRecvChanMultipleMessages(t *testing.T) {
	// Make sure we can receive more than one message.
	// In response to #25, which is a bug from fixing #22.

	s := RunDefaultServer()
	defer s.Shutdown()

	ec := NewEConn(t)
	defer ec.Close()

	// Num to send, should == len of messages queued.
	size := 10

	ch := make(chan int, size)

	if _, err := ec.BindRecvChan("foo", ch); err != nil {
		t.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	for i := 0; i < size; i++ {
		ec.Publish("foo", 22)
	}
	ec.Flush()
	time.Sleep(10 * time.Millisecond)

	if lch := len(ch); lch != size {
		t.Fatalf("Expected %d messages queued, got %d.", size, lch)
	}
}

func BenchmarkPublishSpeedViaChan(b *testing.B) {
	b.StopTimer()

	s := RunDefaultServer()
	defer s.Shutdown()

	nc, err := nats.Connect(nats.DefaultURL)
	if err != nil {
		b.Fatalf("Could not connect: %v\n", err)
	}
	ec, err := nats.NewEncodedConn(nc, nats.DEFAULT_ENCODER)
	defer ec.Close()

	ch := make(chan int32, 1024)
	if err := ec.BindSendChan("foo", ch); err != nil {
		b.Fatalf("Failed to bind to a send channel: %v\n", err)
	}

	b.StartTimer()

	num := int32(22)

	for i := 0; i < b.N; i++ {
		ch <- num
	}
	// Make sure they are all processed.
	nc.Flush()
	b.StopTimer()
}
<|endoftext|>