<commit_before>// Package alice implements a middleware chaining solution.
package alice

import (
	"net/http"
	"net/http/httptest"
	"testing"

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

// A constructor for middleware
// that writes its own "tag" into the RW and does nothing else.
// Useful in checking if a chain is behaving in the right order.
func tagMiddleware(tag string) Constructor {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(tag))
			h.ServeHTTP(w, r)
		})
	}
}

// Tests creating a new chain
func TestNew(t *testing.T) {
	c1 := func(h http.Handler) http.Handler {
		return nil
	}
	c2 := func(h http.Handler) http.Handler {
		return http.StripPrefix("potato", nil)
	}

	slice := []Constructor{c1, c2}

	chain := New(slice...)
	assert.Equal(t, chain.constructors[0], slice[0])
	assert.Equal(t, chain.constructors[1], slice[1])
}

func TestThenWorksWithNoMiddleware(t *testing.T) {
	assert.NotPanics(t, func() {
		New()
	})
}

func TestThenTreatsNilAsDefaultServeMux(t *testing.T) {
	chained := New().Then(nil)
	assert.Equal(t, chained, http.DefaultServeMux)
}

func TestThenOrdersHandlersRight(t *testing.T) {
	t1 := tagMiddleware("t1\n")
	t2 := tagMiddleware("t2\n")
	t3 := tagMiddleware("t3\n")
	app := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("app\n"))
	})

	chained := New(t1, t2, t3).Then(app)

	w := httptest.NewRecorder()
	r, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatal(err)
	}

	chained.ServeHTTP(w, r)

	assert.Equal(t, w.Body.String(), "t1\nt2\nt3\napp\n")
}
<commit_msg>Finished test<commit_after>// Package alice implements a middleware chaining solution.
package alice

import (
	"net/http"
	"net/http/httptest"
	"testing"

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

// A constructor for middleware
// that writes its own "tag" into the RW and does nothing else.
// Useful in checking if a chain is behaving in the right order.
func tagMiddleware(tag string) Constructor {
	return func(h http.Handler) http.Handler {
		return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			w.Write([]byte(tag))
			h.ServeHTTP(w, r)
		})
	}
}

// Tests creating a new chain
func TestNew(t *testing.T) {
	c1 := func(h http.Handler) http.Handler {
		return nil
	}
	c2 := func(h http.Handler) http.Handler {
		return http.StripPrefix("potato", nil)
	}

	slice := []Constructor{c1, c2}

	chain := New(slice...)
	assert.Equal(t, chain.constructors[0], slice[0])
	assert.Equal(t, chain.constructors[1], slice[1])
}

func TestThenWorksWithNoMiddleware(t *testing.T) {
	assert.NotPanics(t, func() {
		chain := New()
		app := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {})
		final := chain.Then(app)

		assert.Equal(t, final, app)
	})
}

func TestThenTreatsNilAsDefaultServeMux(t *testing.T) {
	chained := New().Then(nil)
	assert.Equal(t, chained, http.DefaultServeMux)
}

func TestThenOrdersHandlersRight(t *testing.T) {
	t1 := tagMiddleware("t1\n")
	t2 := tagMiddleware("t2\n")
	t3 := tagMiddleware("t3\n")
	app := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.Write([]byte("app\n"))
	})

	chained := New(t1, t2, t3).Then(app)

	w := httptest.NewRecorder()
	r, err := http.NewRequest("GET", "/", nil)
	if err != nil {
		t.Fatal(err)
	}

	chained.ServeHTTP(w, r)

	assert.Equal(t, w.Body.String(), "t1\nt2\nt3\napp\n")
}
<|endoftext|>


<commit_before>package memory

import (
	"fmt"
	"os"
	"strconv"
	//"sync"
	"testing"

	"github.com/grafana/metrictank/idx"
	"gopkg.in/raintank/schema.v1"
)

var (
	ix         idx.MetricIndex
	queries    []query
	tagQueries []tagQuery
)

type query struct {
	Pattern         string
	ExpectedResults int
}

type tagQuery struct {
	Expressions     []string
	ExpectedResults int
}

type metric struct {
	Name string
	Tags []string
}

func cpuMetrics(dcCount, hostCount, hostOffset, cpuCount int, prefix string) []metric {
	var series []metric
	for dc := 0; dc < dcCount; dc++ {
		for host := hostOffset; host < hostCount+hostOffset; host++ {
			for cpu := 0; cpu < cpuCount; cpu++ {
				p := prefix + ".dc" + strconv.Itoa(dc) + ".host" + strconv.Itoa(host) + ".cpu." + strconv.Itoa(cpu)
				for _, m := range []string{"idle", "interrupt", "nice", "softirq", "steal", "system", "user", "wait"} {
					series = append(series, metric{
						Name: p + "." + m,
						Tags: []string{
							"dc=dc" + strconv.Itoa(dc),
							"host=host" + strconv.Itoa(host),
							"device=cpu",
							"cpu=cpu" + strconv.Itoa(cpu),
							"metric=" + m,
						},
					})
				}
			}
		}
	}
	return series
}

func diskMetrics(dcCount, hostCount, hostOffset, diskCount int, prefix string) []metric {
	var series []metric
	for dc := 0; dc < dcCount; dc++ {
		for host := hostOffset; host < hostCount+hostOffset; host++ {
			for disk := 0; disk < diskCount; disk++ {
				p := prefix + ".dc" + strconv.Itoa(dc) + ".host" + strconv.Itoa(host) + ".disk.disk" + strconv.Itoa(disk)
				for _, m := range []string{"disk_merged", "disk_octets", "disk_ops", "disk_time"} {
					series = append(series, metric{
						Name: p + "." + m + ".read",
						Tags: []string{
							"dc=dc" + strconv.Itoa(dc),
							"host=host" + strconv.Itoa(host),
							"device=disk",
							"disk=disk" + strconv.Itoa(disk),
							"metric=" + m,
							"direction=read",
						},
					})
					series = append(series, metric{
						Name: p + "." + m + ".write",
						Tags: []string{
							"dc=dc" + strconv.Itoa(dc),
							"host=host" + strconv.Itoa(host),
							"device=disk",
							"disk=disk" + strconv.Itoa(disk),
							"metric=" + m,
							"direction=write",
						},
					})
				}
			}
		}
	}
	return series
}

func TestMain(m *testing.M) {
	_tagSupport := tagSupport
	defer func() { tagSupport = _tagSupport }()
	tagSupport = true

	Init()
	os.Exit(m.Run())
}

func Init() {
	ix = New()
	ix.Init()

	var data *schema.MetricData

	for _, series := range cpuMetrics(5, 1000, 0, 32, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    1,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	for _, series := range diskMetrics(5, 1000, 0, 10, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    1,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	// orgId has 1,680,000 series

	for _, series := range cpuMetrics(5, 100, 950, 32, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    2,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	for _, series := range diskMetrics(5, 100, 950, 10, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    2,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	//orgId 2 has 168,000 mertics

	queries = []query{
		//LEAF queries
		{Pattern: "collectd.dc1.host960.disk.disk1.disk_ops.read", ExpectedResults: 1},
		{Pattern: "collectd.dc1.host960.disk.disk1.disk_ops.*", ExpectedResults: 2},
		{Pattern: "collectd.*.host960.disk.disk1.disk_ops.read", ExpectedResults: 5},
		{Pattern: "collectd.*.host960.disk.disk1.disk_ops.*", ExpectedResults: 10},
		{Pattern: "collectd.d*.host960.disk.disk1.disk_ops.*", ExpectedResults: 10},
		{Pattern: "collectd.[abcd]*.host960.disk.disk1.disk_ops.*", ExpectedResults: 10},
		{Pattern: "collectd.{dc1,dc50}.host960.disk.disk1.disk_ops.*", ExpectedResults: 2},

		{Pattern: "collectd.dc3.host960.cpu.1.idle", ExpectedResults: 1},
		{Pattern: "collectd.dc30.host960.cpu.1.idle", ExpectedResults: 0},
		{Pattern: "collectd.dc3.host960.*.*.idle", ExpectedResults: 32},
		{Pattern: "collectd.dc3.host960.*.*.idle", ExpectedResults: 32},

		{Pattern: "collectd.dc3.host96[0-9].cpu.1.idle", ExpectedResults: 10},
		{Pattern: "collectd.dc30.host96[0-9].cpu.1.idle", ExpectedResults: 0},
		{Pattern: "collectd.dc3.host96[0-9].*.*.idle", ExpectedResults: 320},
		{Pattern: "collectd.dc3.host96[0-9].*.*.idle", ExpectedResults: 320},

		{Pattern: "collectd.{dc1,dc2,dc3}.host960.cpu.1.idle", ExpectedResults: 3},
		{Pattern: "collectd.{dc*, a*}.host960.cpu.1.idle", ExpectedResults: 5},

		//Branch queries
		{Pattern: "collectd.dc1.host960.*", ExpectedResults: 2},
		{Pattern: "collectd.*.host960.disk.disk1.*", ExpectedResults: 20},
		{Pattern: "collectd.[abcd]*.host960.disk.disk1.*", ExpectedResults: 20},

		{Pattern: "collectd.*.host960.disk.*.*", ExpectedResults: 200},
		{Pattern: "*.dc3.host960.cpu.1.*", ExpectedResults: 8},
		{Pattern: "*.dc3.host96{1,3}.cpu.1.*", ExpectedResults: 16},
		{Pattern: "*.dc3.{host,server}96{1,3}.cpu.1.*", ExpectedResults: 16},

		{Pattern: "*.dc3.{host,server}9[6-9]{1,3}.cpu.1.*", ExpectedResults: 64},
	}

	tagQueries = []tagQuery{
		// simple matching
		{Expressions: []string{"dc=dc1", "host=host960", "disk=disk1", "metric=disk_ops"}, ExpectedResults: 2},
		{Expressions: []string{"dc=dc3", "host=host960", "disk=disk2", "direction=read"}, ExpectedResults: 4},

		// regular expressions
		{Expressions: []string{"dc=~dc[1-3]", "host=~host3[5-9]{2}", "metric=disk_ops"}, ExpectedResults: 1500},
		{Expressions: []string{"dc=~dc[0-9]", "host=~host97[0-9]", "disk=disk2", "metric=disk_ops"}, ExpectedResults: 100},

		// matching and filtering
		{Expressions: []string{"dc=dc1", "host=host666", "cpu=cpu12", "device=cpu", "metric!=softirq"}, ExpectedResults: 7},
		{Expressions: []string{"dc=dc1", "host=host966", "cpu!=cpu12", "device!=disk", "metric!=softirq"}, ExpectedResults: 217},

		// matching and filtering by regular expressions
		{Expressions: []string{"dc=dc1", "host=host666", "cpu!=~cpu[0-9]{2}", "device!=~d.*"}, ExpectedResults: 80},
		{Expressions: []string{"dc=dc1", "host!=~host10[0-9]{2}", "device!=~c.*"}, ExpectedResults: 1500},
	}
}

func ixFind(org, q int) {
	nodes, err := ix.Find(org, queries[q].Pattern, 0)
	if err != nil {
		panic(err)
	}
	if len(nodes) != queries[q].ExpectedResults {
		for _, n := range nodes {
			fmt.Println(n.Path)
		}
		panic(fmt.Sprintf("%s expected %d got %d results instead", queries[q].Pattern, queries[q].ExpectedResults, len(nodes)))
	}
}

func BenchmarkFind(b *testing.B) {
	if ix == nil {
		Init()
	}
	queryCount := len(queries)
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % queryCount
		org := (n % 2) + 1
		ixFind(org, q)
	}
}

type testQ struct {
	q   int
	org int
}

func BenchmarkConcurrent4Find(b *testing.B) {
	if ix == nil {
		Init()
	}

	queryCount := len(queries)
	if ix == nil {
		Init()
	}

	ch := make(chan testQ)
	for i := 0; i < 4; i++ {
		go func() {
			for q := range ch {
				ixFind(q.org, q.q)
			}
		}()
	}
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % queryCount
		org := (n % 2) + 1
		ch <- testQ{q: q, org: org}
	}
	close(ch)
}

func BenchmarkConcurrent8Find(b *testing.B) {
	if ix == nil {
		Init()
	}

	queryCount := len(queries)
	if ix == nil {
		Init()
	}

	ch := make(chan testQ)
	for i := 0; i < 8; i++ {
		go func() {
			for q := range ch {
				ixFind(q.org, q.q)
			}
		}()
	}
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % queryCount
		org := (n % 2) + 1
		ch <- testQ{q: q, org: org}
	}
	close(ch)
}

func ixFindByTag(org, q int) {
	series, err := ix.IdsByTagExpressions(org, tagQueries[q].Expressions)
	if err != nil {
		panic(err)
	}
	if len(series) != tagQueries[q].ExpectedResults {
		for _, s := range series {
			fmt.Println(s)
		}
		panic(fmt.Sprintf("%+v expected %d got %d results instead", tagQueries[q].Expressions, tagQueries[q].ExpectedResults, len(series)))
	}
}

func BenchmarkTagFindSimpleIntersect(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % 2
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagFindRegexIntersect(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := (n % 2) + 2
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagFindMatchingAndFiltering(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := (n % 2) + 4
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagFindMatchingAndFilteringRegex1(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		ixFindByTag(1, 6)
	}
}

func BenchmarkTagQueryFilterAndIntersect(b *testing.B) {
	q := tagQuery{Expressions: []string{"direction!=~read", "device!=", "host=~host9[0-9]0", "dc=dc1", "disk!=disk1", "metric=disk_time"}, ExpectedResults: 90}
	b.ReportAllocs()
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		series, err := ix.IdsByTagExpressions(1, q.Expressions)
		if err != nil {
			panic(err)
		}
		if len(series) != q.ExpectedResults {
			for _, s := range series {
				fmt.Println(s)
			}
			panic(fmt.Sprintf("%+v expected %d got %d results instead", q.Expressions, q.ExpectedResults, len(series)))
		}
	}
}
<commit_msg>test fix<commit_after>package memory

import (
	"fmt"
	"os"
	"strconv"
	//"sync"
	"testing"

	"github.com/grafana/metrictank/idx"
	"gopkg.in/raintank/schema.v1"
)

var (
	ix         idx.MetricIndex
	queries    []query
	tagQueries []tagQuery
)

type query struct {
	Pattern         string
	ExpectedResults int
}

type tagQuery struct {
	Expressions     []string
	ExpectedResults int
}

type metric struct {
	Name string
	Tags []string
}

func cpuMetrics(dcCount, hostCount, hostOffset, cpuCount int, prefix string) []metric {
	var series []metric
	for dc := 0; dc < dcCount; dc++ {
		for host := hostOffset; host < hostCount+hostOffset; host++ {
			for cpu := 0; cpu < cpuCount; cpu++ {
				p := prefix + ".dc" + strconv.Itoa(dc) + ".host" + strconv.Itoa(host) + ".cpu." + strconv.Itoa(cpu)
				for _, m := range []string{"idle", "interrupt", "nice", "softirq", "steal", "system", "user", "wait"} {
					series = append(series, metric{
						Name: p + "." + m,
						Tags: []string{
							"dc=dc" + strconv.Itoa(dc),
							"host=host" + strconv.Itoa(host),
							"device=cpu",
							"cpu=cpu" + strconv.Itoa(cpu),
							"metric=" + m,
						},
					})
				}
			}
		}
	}
	return series
}

func diskMetrics(dcCount, hostCount, hostOffset, diskCount int, prefix string) []metric {
	var series []metric
	for dc := 0; dc < dcCount; dc++ {
		for host := hostOffset; host < hostCount+hostOffset; host++ {
			for disk := 0; disk < diskCount; disk++ {
				p := prefix + ".dc" + strconv.Itoa(dc) + ".host" + strconv.Itoa(host) + ".disk.disk" + strconv.Itoa(disk)
				for _, m := range []string{"disk_merged", "disk_octets", "disk_ops", "disk_time"} {
					series = append(series, metric{
						Name: p + "." + m + ".read",
						Tags: []string{
							"dc=dc" + strconv.Itoa(dc),
							"host=host" + strconv.Itoa(host),
							"device=disk",
							"disk=disk" + strconv.Itoa(disk),
							"metric=" + m,
							"direction=read",
						},
					})
					series = append(series, metric{
						Name: p + "." + m + ".write",
						Tags: []string{
							"dc=dc" + strconv.Itoa(dc),
							"host=host" + strconv.Itoa(host),
							"device=disk",
							"disk=disk" + strconv.Itoa(disk),
							"metric=" + m,
							"direction=write",
						},
					})
				}
			}
		}
	}
	return series
}

func TestMain(m *testing.M) {
	_tagSupport := tagSupport
	defer func() { tagSupport = _tagSupport }()
	tagSupport = true

	Init()
	os.Exit(m.Run())
}

func Init() {
	ix = New()
	ix.Init()

	var data *schema.MetricData

	for _, series := range cpuMetrics(5, 1000, 0, 32, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    1,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	for _, series := range diskMetrics(5, 1000, 0, 10, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    1,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	// orgId has 1,680,000 series

	for _, series := range cpuMetrics(5, 100, 950, 32, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    2,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	for _, series := range diskMetrics(5, 100, 950, 10, "collectd") {
		data = &schema.MetricData{
			Name:     series.Name,
			Metric:   series.Name,
			Tags:     series.Tags,
			Interval: 10,
			OrgId:    2,
		}
		data.SetId()
		ix.AddOrUpdate(data, 1)
	}
	//orgId 2 has 168,000 mertics

	queries = []query{
		//LEAF queries
		{Pattern: "collectd.dc1.host960.disk.disk1.disk_ops.read", ExpectedResults: 1},
		{Pattern: "collectd.dc1.host960.disk.disk1.disk_ops.*", ExpectedResults: 2},
		{Pattern: "collectd.*.host960.disk.disk1.disk_ops.read", ExpectedResults: 5},
		{Pattern: "collectd.*.host960.disk.disk1.disk_ops.*", ExpectedResults: 10},
		{Pattern: "collectd.d*.host960.disk.disk1.disk_ops.*", ExpectedResults: 10},
		{Pattern: "collectd.[abcd]*.host960.disk.disk1.disk_ops.*", ExpectedResults: 10},
		{Pattern: "collectd.{dc1,dc50}.host960.disk.disk1.disk_ops.*", ExpectedResults: 2},

		{Pattern: "collectd.dc3.host960.cpu.1.idle", ExpectedResults: 1},
		{Pattern: "collectd.dc30.host960.cpu.1.idle", ExpectedResults: 0},
		{Pattern: "collectd.dc3.host960.*.*.idle", ExpectedResults: 32},
		{Pattern: "collectd.dc3.host960.*.*.idle", ExpectedResults: 32},

		{Pattern: "collectd.dc3.host96[0-9].cpu.1.idle", ExpectedResults: 10},
		{Pattern: "collectd.dc30.host96[0-9].cpu.1.idle", ExpectedResults: 0},
		{Pattern: "collectd.dc3.host96[0-9].*.*.idle", ExpectedResults: 320},
		{Pattern: "collectd.dc3.host96[0-9].*.*.idle", ExpectedResults: 320},

		{Pattern: "collectd.{dc1,dc2,dc3}.host960.cpu.1.idle", ExpectedResults: 3},
		{Pattern: "collectd.{dc*, a*}.host960.cpu.1.idle", ExpectedResults: 5},

		//Branch queries
		{Pattern: "collectd.dc1.host960.*", ExpectedResults: 2},
		{Pattern: "collectd.*.host960.disk.disk1.*", ExpectedResults: 20},
		{Pattern: "collectd.[abcd]*.host960.disk.disk1.*", ExpectedResults: 20},

		{Pattern: "collectd.*.host960.disk.*.*", ExpectedResults: 200},
		{Pattern: "*.dc3.host960.cpu.1.*", ExpectedResults: 8},
		{Pattern: "*.dc3.host96{1,3}.cpu.1.*", ExpectedResults: 16},
		{Pattern: "*.dc3.{host,server}96{1,3}.cpu.1.*", ExpectedResults: 16},

		{Pattern: "*.dc3.{host,server}9[6-9]{1,3}.cpu.1.*", ExpectedResults: 64},
	}

	tagQueries = []tagQuery{
		// simple matching
		{Expressions: []string{"dc=dc1", "host=host960", "disk=disk1", "metric=disk_ops"}, ExpectedResults: 2},
		{Expressions: []string{"dc=dc3", "host=host960", "disk=disk2", "direction=read"}, ExpectedResults: 4},

		// regular expressions
		{Expressions: []string{"dc=~dc[1-3]", "host=~host3[5-9]{2}", "metric=disk_ops"}, ExpectedResults: 1500},
		{Expressions: []string{"dc=~dc[0-9]", "host=~host97[0-9]", "disk=disk2", "metric=disk_ops"}, ExpectedResults: 100},

		// matching and filtering
		{Expressions: []string{"dc=dc1", "host=host666", "cpu=cpu12", "device=cpu", "metric!=softirq"}, ExpectedResults: 7},
		{Expressions: []string{"dc=dc1", "host=host966", "cpu!=cpu12", "device!=disk", "metric!=softirq"}, ExpectedResults: 217},

		// matching and filtering by regular expressions
		{Expressions: []string{"dc=dc1", "host=host666", "cpu!=~cpu[0-9]{2}", "device!=~d.*"}, ExpectedResults: 80},
		{Expressions: []string{"dc=dc1", "host!=~host10[0-9]{2}", "device!=~c.*"}, ExpectedResults: 4000},
	}
}

func ixFind(org, q int) {
	nodes, err := ix.Find(org, queries[q].Pattern, 0)
	if err != nil {
		panic(err)
	}
	if len(nodes) != queries[q].ExpectedResults {
		for _, n := range nodes {
			fmt.Println(n.Path)
		}
		panic(fmt.Sprintf("%s expected %d got %d results instead", queries[q].Pattern, queries[q].ExpectedResults, len(nodes)))
	}
}

func BenchmarkFind(b *testing.B) {
	if ix == nil {
		Init()
	}
	queryCount := len(queries)
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % queryCount
		org := (n % 2) + 1
		ixFind(org, q)
	}
}

type testQ struct {
	q   int
	org int
}

func BenchmarkConcurrent4Find(b *testing.B) {
	if ix == nil {
		Init()
	}

	queryCount := len(queries)
	if ix == nil {
		Init()
	}

	ch := make(chan testQ)
	for i := 0; i < 4; i++ {
		go func() {
			for q := range ch {
				ixFind(q.org, q.q)
			}
		}()
	}
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % queryCount
		org := (n % 2) + 1
		ch <- testQ{q: q, org: org}
	}
	close(ch)
}

func BenchmarkConcurrent8Find(b *testing.B) {
	if ix == nil {
		Init()
	}

	queryCount := len(queries)
	if ix == nil {
		Init()
	}

	ch := make(chan testQ)
	for i := 0; i < 8; i++ {
		go func() {
			for q := range ch {
				ixFind(q.org, q.q)
			}
		}()
	}
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % queryCount
		org := (n % 2) + 1
		ch <- testQ{q: q, org: org}
	}
	close(ch)
}

func ixFindByTag(org, q int) {
	series, err := ix.IdsByTagExpressions(org, tagQueries[q].Expressions)
	if err != nil {
		panic(err)
	}
	if len(series) != tagQueries[q].ExpectedResults {
		for _, s := range series {
			memoryIdx := ix.(*MemoryIdx)
			fmt.Println(memoryIdx.DefById[s].Tags)
		}
		panic(fmt.Sprintf("%+v expected %d got %d results instead", tagQueries[q].Expressions, tagQueries[q].ExpectedResults, len(series)))
	}
}

func BenchmarkTagFindSimpleIntersect(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := n % 2
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagFindRegexIntersect(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := (n % 2) + 2
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagFindMatchingAndFiltering(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := (n % 2) + 4
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagFindMatchingAndFilteringWithRegex(b *testing.B) {
	b.ReportAllocs()
	b.ResetTimer()
	for n := 0; n < b.N; n++ {
		q := (n % 2) + 6
		org := (n % 2) + 1
		ixFindByTag(org, q)
	}
}

func BenchmarkTagQueryFilterAndIntersect(b *testing.B) {
	q := tagQuery{Expressions: []string{"direction!=~read", "device!=", "host=~host9[0-9]0", "dc=dc1", "disk!=disk1", "metric=disk_time"}, ExpectedResults: 90}
	b.ReportAllocs()
	b.ResetTimer()

	for n := 0; n < b.N; n++ {
		series, err := ix.IdsByTagExpressions(1, q.Expressions)
		if err != nil {
			panic(err)
		}
		if len(series) != q.ExpectedResults {
			for _, s := range series {
				fmt.Println(s)
			}
			panic(fmt.Sprintf("%+v expected %d got %d results instead", q.Expressions, q.ExpectedResults, len(series)))
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2016-2017 Authors of Cilium
//
// 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 main

import (
	"os"
	"path/filepath"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/plugins/cilium-docker/driver"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var (
	log        = logging.DefaultLogger.WithField(logfields.LogSubsys, "cilium-docker")
	pluginPath string
	driverSock string
	debug      bool
	ciliumAPI  string
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "cilium-docker",
	Short: "Cilium plugin for Docker (libnetwork)",
	Long: `Cilium plugin for Docker (libnetwork)

Docker plugin implementing the networking and IPAM API.

The plugin handles requests from the local Docker runtime to provide
network connectivity and IP address management for containers that are
connected to a Docker network of type "cilium".`,
	Example: `  docker network create my_network --ipam-driver cilium --driver cilium
  docker run --net my_network hello-world
`,
	Run: func(cmd *cobra.Command, args []string) {
		if d, err := driver.NewDriver(ciliumAPI); err != nil {
			log.WithError(err).Fatal("Unable to create cilium-net driver")
		} else {
			log.WithField(logfields.Path, driverSock).Info("Listening for events from Docker")
			if err := d.Listen(driverSock); err != nil {
				log.Fatal(err)
			}
		}
	},
}

func main() {
	if err := RootCmd.Execute(); err != nil {
		log.Fatal(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	flags := RootCmd.PersistentFlags()
	flags.BoolVarP(&debug, "debug", "D", false, "Enable debug messages")
	flags.StringVar(&ciliumAPI, "cilium-api", "", "URI to server-side API")
	flags.StringVar(&pluginPath, "docker-plugins", "/run/docker/plugins",
		"Path to Docker plugins directory")
}

func initConfig() {
	if debug {
		log.Logger.SetLevel(logrus.DebugLevel)
	} else {
		log.Logger.SetLevel(logrus.InfoLevel)
	}

	common.RequireRootPrivilege("cilium-docker")

	driverSock = filepath.Join(pluginPath, "cilium.sock")

	if err := os.MkdirAll(pluginPath, 0755); err != nil && !os.IsExist(err) {
		log.WithError(err).Fatal("Could not create net plugin path directory")
	}

	if _, err := os.Stat(driverSock); err == nil {
		log.WithField(logfields.Path, driverSock).Debug("socket file already exists, unlinking the old file handle.")
		os.RemoveAll(driverSock)
	}
}
<commit_msg>cilium-docker: Refactor global initialization<commit_after>// Copyright 2016-2017 Authors of Cilium
//
// 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 main

import (
	"os"
	"path/filepath"

	"github.com/cilium/cilium/common"
	"github.com/cilium/cilium/pkg/logging"
	"github.com/cilium/cilium/pkg/logging/logfields"
	"github.com/cilium/cilium/plugins/cilium-docker/driver"

	"github.com/sirupsen/logrus"
	"github.com/spf13/cobra"
)

var (
	log        = logging.DefaultLogger.WithField(logfields.LogSubsys, "cilium-docker")
	pluginPath string
	driverSock string
	debug      bool
	ciliumAPI  string
)

// RootCmd represents the base command when called without any subcommands
var RootCmd = &cobra.Command{
	Use:   "cilium-docker",
	Short: "Cilium plugin for Docker (libnetwork)",
	Long: `Cilium plugin for Docker (libnetwork)

Docker plugin implementing the networking and IPAM API.

The plugin handles requests from the local Docker runtime to provide
network connectivity and IP address management for containers that are
connected to a Docker network of type "cilium".`,
	Example: `  docker network create my_network --ipam-driver cilium --driver cilium
  docker run --net my_network hello-world
`,
	Run: func(cmd *cobra.Command, args []string) {
		common.RequireRootPrivilege("cilium-docker")

		createPluginSock()

		if d, err := driver.NewDriver(ciliumAPI); err != nil {
			log.WithError(err).Fatal("Unable to create cilium-net driver")
		} else {
			log.WithField(logfields.Path, driverSock).Info("Listening for events from Docker")
			if err := d.Listen(driverSock); err != nil {
				log.Fatal(err)
			}
		}
	},
}

func main() {
	if err := RootCmd.Execute(); err != nil {
		log.Fatal(err)
		os.Exit(-1)
	}
}

func init() {
	cobra.OnInitialize(initConfig)
	flags := RootCmd.PersistentFlags()
	flags.BoolVarP(&debug, "debug", "D", false, "Enable debug messages")
	flags.StringVar(&ciliumAPI, "cilium-api", "", "URI to server-side API")
	flags.StringVar(&pluginPath, "docker-plugins", "/run/docker/plugins",
		"Path to Docker plugins directory")
}

func initConfig() {
	if debug {
		log.Logger.SetLevel(logrus.DebugLevel)
	} else {
		log.Logger.SetLevel(logrus.InfoLevel)
	}
}

func createPluginSock() {
	driverSock = filepath.Join(pluginPath, "cilium.sock")

	if err := os.MkdirAll(pluginPath, 0755); err != nil && !os.IsExist(err) {
		log.WithError(err).Fatal("Could not create net plugin path directory")
	}

	if _, err := os.Stat(driverSock); err == nil {
		log.WithField(logfields.Path, driverSock).Debug("socket file already exists, unlinking the old file handle.")
		os.RemoveAll(driverSock)
	}
}
<|endoftext|>


<commit_before>package config

import (
	"archive/tar"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/dokku/dokku/plugins/common"
	"github.com/joho/godotenv"
	"github.com/ryanuber/columnize"
)

//ExportFormat types of possible exports
type ExportFormat int

const (
	//ExportFormatExports format: Sourceable exports
	ExportFormatExports ExportFormat = iota
	//ExportFormatEnvfile format: dotenv file
	ExportFormatEnvfile
	//ExportFormatDockerArgs format: --env KEY=VALUE args for docker
	ExportFormatDockerArgs
	//ExportFormatDockerArgsKeys format: --env KEY args for docker
	ExportFormatDockerArgsKeys
	//ExportFormatShell format: env arguments for shell
	ExportFormatShell
	//ExportFormatPretty format: pretty-printed in columns
	ExportFormatPretty
	//ExportFormatJSON format: json key/value output
	ExportFormatJSON
	//ExportFormatJSONList format: json output as a list of objects
	ExportFormatJSONList
)

//Env is a representation for global or app environment
type Env struct {
	name     string
	filename string
	env      map[string]string
}

//newEnvFromString creates an env from the given ENVFILE contents representation
func newEnvFromString(rep string) (env *Env, err error) {
	envMap, err := godotenv.Unmarshal(rep)
	env = &Env{
		name:     "<unknown>",
		filename: "",
		env:      envMap,
	}
	return
}

//LoadAppEnv loads an environment for the given app
func LoadAppEnv(appName string) (env *Env, err error) {
	err = common.VerifyAppName(appName)
	if err != nil {
		return
	}
	appfile, err := getAppFile(appName)
	if err != nil {
		return
	}
	return loadFromFile(appName, appfile)
}

//LoadMergedAppEnv loads an app environment merged with the global environment
func LoadMergedAppEnv(appName string) (env *Env, err error) {
	env, err = LoadAppEnv(appName)
	if err != nil {
		return
	}
	global, err := LoadGlobalEnv()
	if err != nil {
		common.LogFail(err.Error())
	}
	global.Merge(env)
	global.filename = ""
	global.name = env.name
	return global, err
}

//LoadGlobalEnv loads the global environment
func LoadGlobalEnv() (*Env, error) {
	return loadFromFile("<global>", getGlobalFile())
}

//Get an environment variable
func (e *Env) Get(key string) (value string, ok bool) {
	value, ok = e.env[key]
	return
}

//GetDefault an environment variable or a default if it doesn't exist
func (e *Env) GetDefault(key string, defaultValue string) string {
	v, ok := e.env[key]
	if !ok {
		return defaultValue
	}
	return v
}

//GetBoolDefault gets the bool value of the given key with the given default
//right now that is evaluated as `value != "0"`
func (e *Env) GetBoolDefault(key string, defaultValue bool) bool {
	v, ok := e.Get(key)
	if !ok {
		return defaultValue
	}
	return v != "0"
}

//Set an environment variable
func (e *Env) Set(key string, value string) {
	e.env[key] = value
}

//Unset an environment variable
func (e *Env) Unset(key string) {
	delete(e.env, key)
}

//Keys gets the keys in this environment
func (e *Env) Keys() (keys []string) {
	keys = make([]string, 0, len(e.env))
	for k := range e.env {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return
}

//Len returns the number of items in this environment
func (e *Env) Len() int {
	return len(e.env)
}

//Map returns the Env as a map
func (e *Env) Map() map[string]string {
	return e.env
}

func (e *Env) String() string {
	return e.EnvfileString()
}

//Merge merges the given environment on top of the receiver
func (e *Env) Merge(other *Env) {
	for _, k := range other.Keys() {
		e.Set(k, other.GetDefault(k, ""))
	}
}

//Write an Env back to the file it was read from as an exportfile
func (e *Env) Write() error {
	if e.filename == "" {
		return errors.New("this Env was created unbound to a file")
	}
	return godotenv.Write(e.Map(), e.filename)
}

//Export the Env in the given format
func (e *Env) Export(format ExportFormat) string {
	switch format {
	case ExportFormatExports:
		return e.ExportfileString()
	case ExportFormatEnvfile:
		return e.EnvfileString()
	case ExportFormatDockerArgs:
		return e.DockerArgsString()
	case ExportFormatDockerArgsKeys:
		return e.DockerArgsKeysString()
	case ExportFormatShell:
		return e.ShellString()
	case ExportFormatPretty:
		return prettyPrintEnvEntries("", e.Map())
	case ExportFormatJSON:
		return e.JSONString()
	case ExportFormatJSONList:
		return e.JSONListString()
	default:
		common.LogFail(fmt.Sprintf("Unknown export format: %v", format))
		return ""
	}
}

//EnvfileString returns the contents of this Env in dotenv format
func (e *Env) EnvfileString() string {
	rep, _ := godotenv.Marshal(e.Map())
	return rep
}

//ExportfileString returns the contents of this Env as bash exports
func (e *Env) ExportfileString() string {
	return e.stringWithPrefixAndSeparator("export ", "\n")
}

//DockerArgsString gets the contents of this Env in the form -env=KEY=VALUE --env...
func (e *Env) DockerArgsString() string {
	return e.stringWithPrefixAndSeparator("--env=", " ")
}

//DockerArgsStringKeys gets the contents of this Env in the form -env=KEY --env...
func (e *Env) DockerArgsKeysString() string {
	keys := e.Keys()
	entries := make([]string, len(keys))
	for i, k := range keys {
		entries[i] = fmt.Sprintf("%s%s", "--env=", k)
	}
	return strings.Join(entries, " ")
}

//JSONString returns the contents of this Env as a key/value json object
func (e *Env) JSONString() string {
	data, err := json.Marshal(e.Map())
	if err != nil {
		return "{}"
	}

	return string(data)
}

//JSONListString returns the contents of this Env as a json list of objects containing the name and the value of the env var
func (e *Env) JSONListString() string {
	var list []map[string]string
	for _, key := range e.Keys() {
		value, _ := e.Get(key)
		list = append(list, map[string]string{
			"name":  key,
			"value": value,
		})
	}

	data, err := json.Marshal(list)
	if err != nil {
		return "[]"
	}

	return string(data)
}

//ShellString gets the contents of this Env in the form "KEY='value' KEY2='value'"
// for passing the environment in the shell
func (e *Env) ShellString() string {
	return e.stringWithPrefixAndSeparator("", " ")
}

//ExportBundle writes a tarfile of the environment to the given io.Writer.
// for every environment variable there is a file with the variable's key
// with its content set to the variable's value
func (e *Env) ExportBundle(dest io.Writer) error {
	tarfile := tar.NewWriter(dest)
	defer tarfile.Close()

	for _, k := range e.Keys() {
		val, _ := e.Get(k)
		valbin := []byte(val)

		header := &tar.Header{
			Name: k,
			Mode: 0600,
			Size: int64(len(valbin)),
		}
		tarfile.WriteHeader(header)
		tarfile.Write(valbin)
	}
	return nil
}

//stringWithPrefixAndSeparator makes a string of the environment
// with the given prefix and separator for each entry
func (e *Env) stringWithPrefixAndSeparator(prefix string, separator string) string {
	keys := e.Keys()
	entries := make([]string, len(keys))
	for i, k := range keys {
		v := singleQuoteEscape(e.env[k])
		entries[i] = fmt.Sprintf("%s%s='%s'", prefix, k, v)
	}
	return strings.Join(entries, separator)
}

//singleQuoteEscape escapes the value as if it were shell-quoted in single quotes
func singleQuoteEscape(value string) string { // so that 'esc'aped' -> 'esc'\''aped'
	return strings.Replace(value, "'", "'\\''", -1)
}

//prettyPrintEnvEntries in columns
func prettyPrintEnvEntries(prefix string, entries map[string]string) string {
	colConfig := columnize.DefaultConfig()
	colConfig.Prefix = prefix
	colConfig.Delim = "\x00"

	//some keys may be prefixes of each other so we need to sort them rather than the resulting lines
	keys := make([]string, 0, len(entries))
	for k := range entries {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	lines := make([]string, 0, len(keys))
	for _, k := range keys {
		lines = append(lines, fmt.Sprintf("%s:\x00%s", k, entries[k]))
	}
	return columnize.Format(lines, colConfig)
}

func loadFromFile(name string, filename string) (env *Env, err error) {
	envMap := make(map[string]string)
	if _, err := os.Stat(filename); err == nil {
		envMap, err = godotenv.Read(filename)
	}

	dirty := false
	for k := range envMap {
		if err := validateKey(k); err != nil {
			common.LogInfo1(fmt.Sprintf("Deleting invalid key %s from config for %s", k, name))
			delete(envMap, k)
			dirty = true
		}
	}
	if dirty {
		if err := godotenv.Write(envMap, filename); err != nil {
			common.LogFail(fmt.Sprintf("Error writing back config for %s after removing invalid keys", name))
		}
	}

	env = &Env{
		name:     name,
		filename: filename,
		env:      envMap,
	}
	return
}

func getAppFile(appName string) (string, error) {
	err := common.VerifyAppName(appName)
	if err != nil {
		return "", err
	}
	return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), appName, "ENV"), nil
}

func getGlobalFile() string {
	return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), "ENV")
}
<commit_msg>fix: correct dockblock<commit_after>package config

import (
	"archive/tar"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"os"
	"path/filepath"
	"sort"
	"strings"

	"github.com/dokku/dokku/plugins/common"
	"github.com/joho/godotenv"
	"github.com/ryanuber/columnize"
)

//ExportFormat types of possible exports
type ExportFormat int

const (
	//ExportFormatExports format: Sourceable exports
	ExportFormatExports ExportFormat = iota
	//ExportFormatEnvfile format: dotenv file
	ExportFormatEnvfile
	//ExportFormatDockerArgs format: --env KEY=VALUE args for docker
	ExportFormatDockerArgs
	//ExportFormatDockerArgsKeys format: --env KEY args for docker
	ExportFormatDockerArgsKeys
	//ExportFormatShell format: env arguments for shell
	ExportFormatShell
	//ExportFormatPretty format: pretty-printed in columns
	ExportFormatPretty
	//ExportFormatJSON format: json key/value output
	ExportFormatJSON
	//ExportFormatJSONList format: json output as a list of objects
	ExportFormatJSONList
)

//Env is a representation for global or app environment
type Env struct {
	name     string
	filename string
	env      map[string]string
}

//newEnvFromString creates an env from the given ENVFILE contents representation
func newEnvFromString(rep string) (env *Env, err error) {
	envMap, err := godotenv.Unmarshal(rep)
	env = &Env{
		name:     "<unknown>",
		filename: "",
		env:      envMap,
	}
	return
}

//LoadAppEnv loads an environment for the given app
func LoadAppEnv(appName string) (env *Env, err error) {
	err = common.VerifyAppName(appName)
	if err != nil {
		return
	}
	appfile, err := getAppFile(appName)
	if err != nil {
		return
	}
	return loadFromFile(appName, appfile)
}

//LoadMergedAppEnv loads an app environment merged with the global environment
func LoadMergedAppEnv(appName string) (env *Env, err error) {
	env, err = LoadAppEnv(appName)
	if err != nil {
		return
	}
	global, err := LoadGlobalEnv()
	if err != nil {
		common.LogFail(err.Error())
	}
	global.Merge(env)
	global.filename = ""
	global.name = env.name
	return global, err
}

//LoadGlobalEnv loads the global environment
func LoadGlobalEnv() (*Env, error) {
	return loadFromFile("<global>", getGlobalFile())
}

//Get an environment variable
func (e *Env) Get(key string) (value string, ok bool) {
	value, ok = e.env[key]
	return
}

//GetDefault an environment variable or a default if it doesn't exist
func (e *Env) GetDefault(key string, defaultValue string) string {
	v, ok := e.env[key]
	if !ok {
		return defaultValue
	}
	return v
}

//GetBoolDefault gets the bool value of the given key with the given default
//right now that is evaluated as `value != "0"`
func (e *Env) GetBoolDefault(key string, defaultValue bool) bool {
	v, ok := e.Get(key)
	if !ok {
		return defaultValue
	}
	return v != "0"
}

//Set an environment variable
func (e *Env) Set(key string, value string) {
	e.env[key] = value
}

//Unset an environment variable
func (e *Env) Unset(key string) {
	delete(e.env, key)
}

//Keys gets the keys in this environment
func (e *Env) Keys() (keys []string) {
	keys = make([]string, 0, len(e.env))
	for k := range e.env {
		keys = append(keys, k)
	}
	sort.Strings(keys)
	return
}

//Len returns the number of items in this environment
func (e *Env) Len() int {
	return len(e.env)
}

//Map returns the Env as a map
func (e *Env) Map() map[string]string {
	return e.env
}

func (e *Env) String() string {
	return e.EnvfileString()
}

//Merge merges the given environment on top of the receiver
func (e *Env) Merge(other *Env) {
	for _, k := range other.Keys() {
		e.Set(k, other.GetDefault(k, ""))
	}
}

//Write an Env back to the file it was read from as an exportfile
func (e *Env) Write() error {
	if e.filename == "" {
		return errors.New("this Env was created unbound to a file")
	}
	return godotenv.Write(e.Map(), e.filename)
}

//Export the Env in the given format
func (e *Env) Export(format ExportFormat) string {
	switch format {
	case ExportFormatExports:
		return e.ExportfileString()
	case ExportFormatEnvfile:
		return e.EnvfileString()
	case ExportFormatDockerArgs:
		return e.DockerArgsString()
	case ExportFormatDockerArgsKeys:
		return e.DockerArgsKeysString()
	case ExportFormatShell:
		return e.ShellString()
	case ExportFormatPretty:
		return prettyPrintEnvEntries("", e.Map())
	case ExportFormatJSON:
		return e.JSONString()
	case ExportFormatJSONList:
		return e.JSONListString()
	default:
		common.LogFail(fmt.Sprintf("Unknown export format: %v", format))
		return ""
	}
}

//EnvfileString returns the contents of this Env in dotenv format
func (e *Env) EnvfileString() string {
	rep, _ := godotenv.Marshal(e.Map())
	return rep
}

//ExportfileString returns the contents of this Env as bash exports
func (e *Env) ExportfileString() string {
	return e.stringWithPrefixAndSeparator("export ", "\n")
}

//DockerArgsString gets the contents of this Env in the form -env=KEY=VALUE --env...
func (e *Env) DockerArgsString() string {
	return e.stringWithPrefixAndSeparator("--env=", " ")
}

//DockerArgsKeysString gets the contents of this Env in the form -env=KEY --env...
func (e *Env) DockerArgsKeysString() string {
	keys := e.Keys()
	entries := make([]string, len(keys))
	for i, k := range keys {
		entries[i] = fmt.Sprintf("%s%s", "--env=", k)
	}
	return strings.Join(entries, " ")
}

//JSONString returns the contents of this Env as a key/value json object
func (e *Env) JSONString() string {
	data, err := json.Marshal(e.Map())
	if err != nil {
		return "{}"
	}

	return string(data)
}

//JSONListString returns the contents of this Env as a json list of objects containing the name and the value of the env var
func (e *Env) JSONListString() string {
	var list []map[string]string
	for _, key := range e.Keys() {
		value, _ := e.Get(key)
		list = append(list, map[string]string{
			"name":  key,
			"value": value,
		})
	}

	data, err := json.Marshal(list)
	if err != nil {
		return "[]"
	}

	return string(data)
}

//ShellString gets the contents of this Env in the form "KEY='value' KEY2='value'"
// for passing the environment in the shell
func (e *Env) ShellString() string {
	return e.stringWithPrefixAndSeparator("", " ")
}

//ExportBundle writes a tarfile of the environment to the given io.Writer.
// for every environment variable there is a file with the variable's key
// with its content set to the variable's value
func (e *Env) ExportBundle(dest io.Writer) error {
	tarfile := tar.NewWriter(dest)
	defer tarfile.Close()

	for _, k := range e.Keys() {
		val, _ := e.Get(k)
		valbin := []byte(val)

		header := &tar.Header{
			Name: k,
			Mode: 0600,
			Size: int64(len(valbin)),
		}
		tarfile.WriteHeader(header)
		tarfile.Write(valbin)
	}
	return nil
}

//stringWithPrefixAndSeparator makes a string of the environment
// with the given prefix and separator for each entry
func (e *Env) stringWithPrefixAndSeparator(prefix string, separator string) string {
	keys := e.Keys()
	entries := make([]string, len(keys))
	for i, k := range keys {
		v := singleQuoteEscape(e.env[k])
		entries[i] = fmt.Sprintf("%s%s='%s'", prefix, k, v)
	}
	return strings.Join(entries, separator)
}

//singleQuoteEscape escapes the value as if it were shell-quoted in single quotes
func singleQuoteEscape(value string) string { // so that 'esc'aped' -> 'esc'\''aped'
	return strings.Replace(value, "'", "'\\''", -1)
}

//prettyPrintEnvEntries in columns
func prettyPrintEnvEntries(prefix string, entries map[string]string) string {
	colConfig := columnize.DefaultConfig()
	colConfig.Prefix = prefix
	colConfig.Delim = "\x00"

	//some keys may be prefixes of each other so we need to sort them rather than the resulting lines
	keys := make([]string, 0, len(entries))
	for k := range entries {
		keys = append(keys, k)
	}
	sort.Strings(keys)

	lines := make([]string, 0, len(keys))
	for _, k := range keys {
		lines = append(lines, fmt.Sprintf("%s:\x00%s", k, entries[k]))
	}
	return columnize.Format(lines, colConfig)
}

func loadFromFile(name string, filename string) (env *Env, err error) {
	envMap := make(map[string]string)
	if _, err := os.Stat(filename); err == nil {
		envMap, err = godotenv.Read(filename)
	}

	dirty := false
	for k := range envMap {
		if err := validateKey(k); err != nil {
			common.LogInfo1(fmt.Sprintf("Deleting invalid key %s from config for %s", k, name))
			delete(envMap, k)
			dirty = true
		}
	}
	if dirty {
		if err := godotenv.Write(envMap, filename); err != nil {
			common.LogFail(fmt.Sprintf("Error writing back config for %s after removing invalid keys", name))
		}
	}

	env = &Env{
		name:     name,
		filename: filename,
		env:      envMap,
	}
	return
}

func getAppFile(appName string) (string, error) {
	err := common.VerifyAppName(appName)
	if err != nil {
		return "", err
	}
	return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), appName, "ENV"), nil
}

func getGlobalFile() string {
	return filepath.Join(common.MustGetEnv("DOKKU_ROOT"), "ENV")
}
<|endoftext|>


<commit_before>package controllers

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"os"

	"github.com/NyaaPantsu/nyaa/config"
	"github.com/NyaaPantsu/nyaa/models"
	"github.com/NyaaPantsu/nyaa/models/activities"
	"github.com/NyaaPantsu/nyaa/models/comments"
	"github.com/NyaaPantsu/nyaa/models/notifications"
	"github.com/NyaaPantsu/nyaa/models/reports"
	"github.com/NyaaPantsu/nyaa/models/torrents"
	"github.com/NyaaPantsu/nyaa/utils/captcha"
	"github.com/NyaaPantsu/nyaa/utils/filelist"
	msg "github.com/NyaaPantsu/nyaa/utils/messages"
	"github.com/NyaaPantsu/nyaa/utils/sanitize"
	"github.com/NyaaPantsu/nyaa/utils/search/structs"
	"github.com/NyaaPantsu/nyaa/utils/upload"
	"github.com/NyaaPantsu/nyaa/utils/validator/torrent"
	"github.com/gin-gonic/gin"
)

// ViewHandler : Controller for displaying a torrent
func ViewHandler(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)
	messages := msg.GetMessages(c)
	user := getUser(c)

	if c.Request.URL.Query()["success"] != nil {
		messages.AddInfoT("infos", "torrent_uploaded")
	}
	if c.Request.URL.Query()["badcaptcha"] != nil {
		messages.AddErrorT("errors", "bad_captcha")
	}
	if c.Request.URL.Query()["reported"] != nil {
		messages.AddInfoTf("infos", "report_msg", id)
	}

	torrent, err := torrents.FindByID(uint(id))

	if c.Request.URL.Query()["notif"] != nil {
		notifications.ToggleReadNotification(torrent.Identifier(), user.ID)
	}

	if err != nil {
		NotFoundHandler(c)
		return
	}
	b := torrent.ToJSON()
	folder := filelist.FileListToFolder(torrent.FileList, "root")
	captchaID := ""
	if user.NeedsCaptcha() {
		captchaID = captcha.GetID()
	}
	torrentTemplate(c, b, folder, captchaID)
}

// ViewHeadHandler : Controller for checking a torrent
func ViewHeadHandler(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 32)
	if err != nil {
		return
	}

	_, err = torrents.FindRawByID(uint(id))

	if err != nil {
		NotFoundHandler(c)
		return
	}

	c.AbortWithStatus(http.StatusOK)
}

// PostCommentHandler : Controller for posting a comment
func PostCommentHandler(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)

	torrent, err := torrents.FindByID(uint(id))
	if err != nil {
		NotFoundHandler(c)
		return
	}

	currentUser := getUser(c)
	messages := msg.GetMessages(c)

	if currentUser.NeedsCaptcha() {
		userCaptcha := captcha.Extract(c)
		if !captcha.Authenticate(userCaptcha) {
			messages.AddErrorT("errors", "bad_captcha")
		}
	}
	content := sanitize.Sanitize(c.PostForm("comment"), "comment")

	if strings.TrimSpace(content) == "" {
		messages.AddErrorT("errors", "comment_empty")
	}
	if len(content) > config.Get().CommentLength {
		messages.AddErrorT("errors", "comment_toolong")
	}
	if !messages.HasErrors() {

		_, err := comments.Create(content, torrent, currentUser)
		if err != nil {
			messages.Error(err)
		}
	}
	ViewHandler(c)
}

// ReportTorrentHandler : Controller for sending a torrent report
func ReportTorrentHandler(c *gin.Context) {
	fmt.Println("report")
	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)
	messages := msg.GetMessages(c)
	captchaError := "?reported"
	currentUser := getUser(c)
	if currentUser.NeedsCaptcha() {
		userCaptcha := captcha.Extract(c)
		if !captcha.Authenticate(userCaptcha) {
			captchaError = "?badcaptcha"
			messages.AddErrorT("errors", "bad_captcha")
		}
	}
	torrent, err := torrents.FindByID(uint(id))
	if err != nil {
		messages.Error(err)
	}
	if !messages.HasErrors() {
		_, err := reports.Create(c.PostForm("report_type"), torrent, currentUser)
		messages.AddInfoTf("infos", "report_msg", id)
		if err != nil {
			messages.ImportFromError("errors", err)
		}
		c.Redirect(http.StatusSeeOther, "/view/"+strconv.Itoa(int(torrent.ID))+captchaError)
	} else {
		ReportViewTorrentHandler(c)
	}
}

// ReportTorrentHandler : Controller for sending a torrent report
func ReportViewTorrentHandler(c *gin.Context) {

	type Report struct {
		ID        uint
		CaptchaID string
	}

	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)
	messages := msg.GetMessages(c)
	currentUser := getUser(c)
	if currentUser.ID > 0 {
		torrent, err := torrents.FindByID(uint(id))
		if err != nil {
			messages.Error(err)
		}
		captchaID := ""
		if currentUser.NeedsCaptcha() {
			captchaID = captcha.GetID()
		}
		formTemplate(c, "site/torrents/report.jet.html", Report{torrent.ID, captchaID})
	} else {
		c.Status(404)
	}
}

// TorrentEditUserPanel : Controller for editing a user torrent by a user, after GET request
func TorrentEditUserPanel(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	torrent, _ := torrents.FindByID(uint(id))
	currentUser := getUser(c)
	if currentUser.CurrentOrAdmin(torrent.UploaderID) {
		uploadForm := torrentValidator.TorrentRequest{}
		uploadForm.Name = torrent.Name
		uploadForm.Category = strconv.Itoa(torrent.Category) + "_" + strconv.Itoa(torrent.SubCategory)
		uploadForm.Remake = torrent.Status == models.TorrentStatusRemake
		uploadForm.WebsiteLink = string(torrent.WebsiteLink)
		uploadForm.Description = string(torrent.Description)
		uploadForm.Hidden = torrent.Hidden
		uploadForm.Languages = torrent.Languages
		formTemplate(c, "site/torrents/edit.jet.html", uploadForm)
	} else {
		NotFoundHandler(c)
	}
}

// TorrentPostEditUserPanel : Controller for editing a user torrent by a user, after post request
func TorrentPostEditUserPanel(c *gin.Context) {
	var uploadForm torrentValidator.UpdateRequest
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	uploadForm.ID = uint(id)
	messages := msg.GetMessages(c)
	torrent, _ := torrents.FindByID(uint(id))
	currentUser := getUser(c)
	if torrent.ID > 0 && currentUser.CurrentOrAdmin(torrent.UploaderID) {
		errUp := upload.ExtractEditInfo(c, &uploadForm.Update)
		if errUp != nil {
			messages.AddErrorT("errors", "fail_torrent_update")
		}
		if !messages.HasErrors() {
			upload.UpdateTorrent(&uploadForm, torrent, currentUser).Update(currentUser.HasAdmin())
			messages.AddInfoT("infos", "torrent_updated")
		}
		formTemplate(c, "site/torrents/edit.jet.html", uploadForm.Update)
	} else {
		NotFoundHandler(c)
	}
}

// TorrentDeleteUserPanel : Controller for deleting a user torrent by a user
func TorrentDeleteUserPanel(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	currentUser := getUser(c)
	torrent, _ := torrents.FindByID(uint(id))
	if currentUser.CurrentOrAdmin(torrent.UploaderID) {
		_, _, err := torrent.Delete(false)
		if err == nil {
			if torrent.Uploader == nil {
				torrent.Uploader = &models.User{}
			}
			_, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
			if currentUser.HasAdmin() { // We hide username on log activity if user is not admin and torrent is hidden
				activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username)
			} else {
				activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username)
			}
			//delete reports of torrent
			whereParams := structs.CreateWhereParams("torrent_id = ?", id)
			torrentReports, _, _ := reports.FindOrderBy(&whereParams, "", 0, 0)
			for _, report := range torrentReports {
				report.Delete(false)
			}
		}
		c.Redirect(http.StatusSeeOther, "/?deleted")
	} else {
		NotFoundHandler(c)
	}
}

// DownloadTorrent : Controller for downloading a torrent
func DownloadTorrent(c *gin.Context) {
	hash := c.Param("hash")

	if hash == "" && len(config.Get().Torrents.FileStorage) == 0 {
		//File not found, send 404
		c.AbortWithError(http.StatusNotFound, errors.New("File not found"))
		return
	}

	//Check if file exists and open
	Openfile, err := os.Open(fmt.Sprintf("%s%c%s.torrent", config.Get().Torrents.FileStorage, os.PathSeparator, hash))
	if err != nil {
		//File not found, send 404
		c.AbortWithError(http.StatusNotFound, errors.New("File not found"))
		return
	}
	defer Openfile.Close() //Close after function return

	//Get the file size
	FileStat, _ := Openfile.Stat()                     //Get info from file
	FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string

	torrent, err := torrents.FindRawByHash(hash)

	if err != nil {
		//File not found, send 404
		c.AbortWithError(http.StatusNotFound, errors.New("File not found"))
		return
	}

	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.torrent\"", torrent.Name))
	c.Header("Content-Type", "application/x-bittorrent")
	c.Header("Content-Length", FileSize)
	//Send the file
	// We reset the offset to 0
	Openfile.Seek(0, 0)
	io.Copy(c.Writer, Openfile) //'Copy' the file to the client
}
<commit_msg>Direct to torrent after comment<commit_after>package controllers

import (
	"errors"
	"fmt"
	"io"
	"net/http"
	"strconv"
	"strings"

	"os"

	"github.com/NyaaPantsu/nyaa/config"
	"github.com/NyaaPantsu/nyaa/models"
	"github.com/NyaaPantsu/nyaa/models/activities"
	"github.com/NyaaPantsu/nyaa/models/comments"
	"github.com/NyaaPantsu/nyaa/models/notifications"
	"github.com/NyaaPantsu/nyaa/models/reports"
	"github.com/NyaaPantsu/nyaa/models/torrents"
	"github.com/NyaaPantsu/nyaa/utils/captcha"
	"github.com/NyaaPantsu/nyaa/utils/filelist"
	msg "github.com/NyaaPantsu/nyaa/utils/messages"
	"github.com/NyaaPantsu/nyaa/utils/sanitize"
	"github.com/NyaaPantsu/nyaa/utils/search/structs"
	"github.com/NyaaPantsu/nyaa/utils/upload"
	"github.com/NyaaPantsu/nyaa/utils/validator/torrent"
	"github.com/gin-gonic/gin"
)

// ViewHandler : Controller for displaying a torrent
func ViewHandler(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)
	messages := msg.GetMessages(c)
	user := getUser(c)

	if c.Request.URL.Query()["success"] != nil {
		messages.AddInfoT("infos", "torrent_uploaded")
	}
	if c.Request.URL.Query()["badcaptcha"] != nil {
		messages.AddErrorT("errors", "bad_captcha")
	}
	if c.Request.URL.Query()["reported"] != nil {
		messages.AddInfoTf("infos", "report_msg", id)
	}

	torrent, err := torrents.FindByID(uint(id))

	if c.Request.URL.Query()["notif"] != nil {
		notifications.ToggleReadNotification(torrent.Identifier(), user.ID)
	}

	if err != nil {
		NotFoundHandler(c)
		return
	}
	b := torrent.ToJSON()
	folder := filelist.FileListToFolder(torrent.FileList, "root")
	captchaID := ""
	if user.NeedsCaptcha() {
		captchaID = captcha.GetID()
	}
	torrentTemplate(c, b, folder, captchaID)
}

// ViewHeadHandler : Controller for checking a torrent
func ViewHeadHandler(c *gin.Context) {
	id, err := strconv.ParseInt(c.Param("id"), 10, 32)
	if err != nil {
		return
	}

	_, err = torrents.FindRawByID(uint(id))

	if err != nil {
		NotFoundHandler(c)
		return
	}

	c.AbortWithStatus(http.StatusOK)
}

// PostCommentHandler : Controller for posting a comment
func PostCommentHandler(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)

	torrent, err := torrents.FindByID(uint(id))
	if err != nil {
		NotFoundHandler(c)
		return
	}

	currentUser := getUser(c)
	messages := msg.GetMessages(c)

	if currentUser.NeedsCaptcha() {
		userCaptcha := captcha.Extract(c)
		if !captcha.Authenticate(userCaptcha) {
			messages.AddErrorT("errors", "bad_captcha")
		}
	}
	content := sanitize.Sanitize(c.PostForm("comment"), "comment")

	if strings.TrimSpace(content) == "" {
		messages.AddErrorT("errors", "comment_empty")
	}
	if len(content) > config.Get().CommentLength {
		messages.AddErrorT("errors", "comment_toolong")
	}
	if !messages.HasErrors() {

		_, err := comments.Create(content, torrent, currentUser)
		if err != nil {
			messages.Error(err)
		}
	}
	url := "/view/" + strconv.FormatUint(uint64(torrent.ID), 10)
	c.Redirect(302, url+"?success")
}

// ReportTorrentHandler : Controller for sending a torrent report
func ReportTorrentHandler(c *gin.Context) {
	fmt.Println("report")
	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)
	messages := msg.GetMessages(c)
	captchaError := "?reported"
	currentUser := getUser(c)
	if currentUser.NeedsCaptcha() {
		userCaptcha := captcha.Extract(c)
		if !captcha.Authenticate(userCaptcha) {
			captchaError = "?badcaptcha"
			messages.AddErrorT("errors", "bad_captcha")
		}
	}
	torrent, err := torrents.FindByID(uint(id))
	if err != nil {
		messages.Error(err)
	}
	if !messages.HasErrors() {
		_, err := reports.Create(c.PostForm("report_type"), torrent, currentUser)
		messages.AddInfoTf("infos", "report_msg", id)
		if err != nil {
			messages.ImportFromError("errors", err)
		}
		c.Redirect(http.StatusSeeOther, "/view/"+strconv.Itoa(int(torrent.ID))+captchaError)
	} else {
		ReportViewTorrentHandler(c)
	}
}

// ReportTorrentHandler: Controller for sending a torrent report
func ReportViewTorrentHandler(c *gin.Context) {

	type Report struct {
		ID        uint
		CaptchaID string
	}

	id, _ := strconv.ParseInt(c.Param("id"), 10, 32)
	messages := msg.GetMessages(c)
	currentUser := getUser(c)
	if currentUser.ID > 0 {
		torrent, err := torrents.FindByID(uint(id))
		if err != nil {
			messages.Error(err)
		}
		captchaID := ""
		if currentUser.NeedsCaptcha() {
			captchaID = captcha.GetID()
		}
		formTemplate(c, "site/torrents/report.jet.html", Report{torrent.ID, captchaID})
	} else {
		c.Status(404)
	}
}

// TorrentEditUserPanel : Controller for editing a user torrent by a user, after GET request
func TorrentEditUserPanel(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	torrent, _ := torrents.FindByID(uint(id))
	currentUser := getUser(c)
	if currentUser.CurrentOrAdmin(torrent.UploaderID) {
		uploadForm := torrentValidator.TorrentRequest{}
		uploadForm.Name = torrent.Name
		uploadForm.Category = strconv.Itoa(torrent.Category) + "_" + strconv.Itoa(torrent.SubCategory)
		uploadForm.Remake = torrent.Status == models.TorrentStatusRemake
		uploadForm.WebsiteLink = string(torrent.WebsiteLink)
		uploadForm.Description = string(torrent.Description)
		uploadForm.Hidden = torrent.Hidden
		uploadForm.Languages = torrent.Languages
		formTemplate(c, "site/torrents/edit.jet.html", uploadForm)
	} else {
		NotFoundHandler(c)
	}
}

// TorrentPostEditUserPanel : Controller for editing a user torrent by a user, after post request
func TorrentPostEditUserPanel(c *gin.Context) {
	var uploadForm torrentValidator.UpdateRequest
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	uploadForm.ID = uint(id)
	messages := msg.GetMessages(c)
	torrent, _ := torrents.FindByID(uint(id))
	currentUser := getUser(c)
	if torrent.ID > 0 && currentUser.CurrentOrAdmin(torrent.UploaderID) {
		errUp := upload.ExtractEditInfo(c, &uploadForm.Update)
		if errUp != nil {
			messages.AddErrorT("errors", "fail_torrent_update")
		}
		if !messages.HasErrors() {
			upload.UpdateTorrent(&uploadForm, torrent, currentUser).Update(currentUser.HasAdmin())
			messages.AddInfoT("infos", "torrent_updated")
		}
		formTemplate(c, "site/torrents/edit.jet.html", uploadForm.Update)
	} else {
		NotFoundHandler(c)
	}
}

// TorrentDeleteUserPanel : Controller for deleting a user torrent by a user
func TorrentDeleteUserPanel(c *gin.Context) {
	id, _ := strconv.ParseInt(c.Query("id"), 10, 32)
	currentUser := getUser(c)
	torrent, _ := torrents.FindByID(uint(id))
	if currentUser.CurrentOrAdmin(torrent.UploaderID) {
		_, _, err := torrent.Delete(false)
		if err == nil {
			if torrent.Uploader == nil {
				torrent.Uploader = &models.User{}
			}
			_, username := torrents.HideUser(torrent.UploaderID, torrent.Uploader.Username, torrent.Hidden)
			if currentUser.HasAdmin() { // We hide username on log activity if user is not admin and torrent is hidden
				activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, currentUser.Username)
			} else {
				activities.Log(&models.User{}, torrent.Identifier(), "delete", "torrent_deleted_by", strconv.Itoa(int(torrent.ID)), username, username)
			}
			//delete reports of torrent
			whereParams := structs.CreateWhereParams("torrent_id = ?", id)
			torrentReports, _, _ := reports.FindOrderBy(&whereParams, "", 0, 0)
			for _, report := range torrentReports {
				report.Delete(false)
			}
		}
		c.Redirect(http.StatusSeeOther, "/?deleted")
	} else {
		NotFoundHandler(c)
	}
}

// DownloadTorrent : Controller for downloading a torrent
func DownloadTorrent(c *gin.Context) {
	hash := c.Param("hash")

	if hash == "" && len(config.Get().Torrents.FileStorage) == 0 {
		//File not found, send 404
		c.AbortWithError(http.StatusNotFound, errors.New("File not found"))
		return
	}

	//Check if file exists and open
	Openfile, err := os.Open(fmt.Sprintf("%s%c%s.torrent", config.Get().Torrents.FileStorage, os.PathSeparator, hash))
	if err != nil {
		//File not found, send 404
		c.AbortWithError(http.StatusNotFound, errors.New("File not found"))
		return
	}
	defer Openfile.Close() //Close after function return

	//Get the file size
	FileStat, _ := Openfile.Stat()                     //Get info from file
	FileSize := strconv.FormatInt(FileStat.Size(), 10) //Get file size as a string

	torrent, err := torrents.FindRawByHash(hash)

	if err != nil {
		//File not found, send 404
		c.AbortWithError(http.StatusNotFound, errors.New("File not found"))
		return
	}

	c.Header("Content-Disposition", fmt.Sprintf("attachment; filename=\"%s.torrent\"", torrent.Name))
	c.Header("Content-Type", "application/x-bittorrent")
	c.Header("Content-Length", FileSize)
	//Send the file
	// We reset the offset to 0
	Openfile.Seek(0, 0)
	io.Copy(c.Writer, Openfile) //'Copy' the file to the client
}
<|endoftext|>


<commit_before>package nsm

import (
	"context"
	"fmt"

	"github.com/sirupsen/logrus"

	local_connection "github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/local/connection"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/nsm"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/registry"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/model"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/serviceregistry"
)

type networkServiceEndpointManager interface {
	getEndpoint(ctx context.Context, requestConnection nsm.NSMConnection, ignore_endpoints map[string]*registry.NSERegistration) (*registry.NSERegistration, error)
	createNSEClient(ctx context.Context, endpoint *registry.NSERegistration) (nsm.NetworkServiceClient, error)
	isLocalEndpoint(endpoint *registry.NSERegistration) bool
	checkUpdateNSE(ctx context.Context, reg *registry.NSERegistration) bool
}

type nseManager struct {
	serviceRegistry serviceregistry.ServiceRegistry
	model           model.Model
	properties      *nsm.NsmProperties
}

func (nsem *nseManager) getEndpoint(ctx context.Context, requestConnection nsm.NSMConnection, ignore_endpoints map[string]*registry.NSERegistration) (*registry.NSERegistration, error) {

	// Handle case we are remote NSM and asked for particular endpoint to connect to.
	targetEndpoint := requestConnection.GetNetworkServiceEndpointName()
	if len(targetEndpoint) > 0 {
		endpoint := nsem.model.GetEndpoint(targetEndpoint)
		if endpoint != nil && ignore_endpoints[endpoint.EndpointName()] == nil {
			return endpoint.Endpoint, nil
		} else {
			return nil, fmt.Errorf("Could not find endpoint with name: %s at local registry", targetEndpoint)
		}
	}

	// Get endpoints, do it every time since we do not know if list are changed or not.
	discoveryClient, err := nsem.serviceRegistry.DiscoveryClient()
	if err != nil {
		logrus.Error(err)
		return nil, err
	}
	nseRequest := &registry.FindNetworkServiceRequest{
		NetworkServiceName: requestConnection.GetNetworkService(),
	}
	endpointResponse, err := discoveryClient.FindNetworkService(ctx, nseRequest)
	if err != nil {
		logrus.Error(err)
		return nil, err
	}
	endpoints := filterEndpoints(endpointResponse.GetNetworkServiceEndpoints(), ignore_endpoints)

	if len(endpoints) == 0 {
		return nil, fmt.Errorf("Failed to find NSE for NetworkService %s. Checked: %d of total NSEs: %d",
			requestConnection.GetNetworkService(), len(ignore_endpoints), len(endpoints))
	}

	endpoint := nsem.model.GetSelector().SelectEndpoint(requestConnection.(*local_connection.Connection), endpointResponse.GetNetworkService(), endpoints)
	if endpoint == nil {
		return nil, err
	}
	return &registry.NSERegistration{
		NetworkServiceManager:  endpointResponse.GetNetworkServiceManagers()[endpoint.GetNetworkServiceManagerName()],
		NetworkserviceEndpoint: endpoint,
		NetworkService:         endpointResponse.GetNetworkService(),
	}, nil
}

/**
ctx - we assume it is big enought to perform connection.
*/
func (nsem *nseManager) createNSEClient(ctx context.Context, endpoint *registry.NSERegistration) (nsm.NetworkServiceClient, error) {
	if nsem.isLocalEndpoint(endpoint) {
		modelEp := nsem.model.GetEndpoint(endpoint.GetNetworkserviceEndpoint().GetEndpointName())
		if modelEp == nil {
			return nil, fmt.Errorf("Endpoint not found: %v", endpoint)
		}
		logrus.Infof("Create local NSE connection to endpoint: %v", modelEp)
		client, conn, err := nsem.serviceRegistry.EndpointConnection(ctx, modelEp)
		if err != nil {
			// We failed to connect to local NSE.
			nsem.cleanupNSE(modelEp)
			return nil, err
		}
		return &endpointClient{connection: conn, client: client}, nil
	} else {
		logrus.Infof("Create remote NSE connection to endpoint: %v", endpoint)
		client, conn, err := nsem.serviceRegistry.RemoteNetworkServiceClient(ctx, endpoint.GetNetworkServiceManager())
		if err != nil {
			return nil, err
		}
		return &nsmClient{client: client, connection: conn}, nil
	}
}

func (nsem *nseManager) isLocalEndpoint(endpoint *registry.NSERegistration) bool {
	return nsem.model.GetNsm().GetName() == endpoint.GetNetworkserviceEndpoint().GetNetworkServiceManagerName()
}

func (nsem *nseManager) checkUpdateNSE(ctx context.Context, reg *registry.NSERegistration) bool {
	pingCtx, pingCancel := context.WithTimeout(ctx, nsem.properties.HealRequestConnectCheckTimeout)
	defer pingCancel()

	client, err := nsem.createNSEClient(pingCtx, reg)
	if err == nil && client != nil {
		_ = client.Cleanup()
		return true
	}

	return false
}

func (nsem *nseManager) cleanupNSE(endpoint *model.Endpoint) {
	// Remove endpoint from model and put workspace into BAD state.
	_ = nsem.model.DeleteEndpoint(endpoint.EndpointName())
	logrus.Infof("NSM: Remove Endpoint since it is not available... %v", endpoint)
}
<commit_msg>Return error correctly (#1134)<commit_after>package nsm

import (
	"context"
	"fmt"

	"github.com/sirupsen/logrus"

	local_connection "github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/local/connection"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/nsm"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/apis/registry"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/model"
	"github.com/networkservicemesh/networkservicemesh/controlplane/pkg/serviceregistry"
)

type networkServiceEndpointManager interface {
	getEndpoint(ctx context.Context, requestConnection nsm.NSMConnection, ignore_endpoints map[string]*registry.NSERegistration) (*registry.NSERegistration, error)
	createNSEClient(ctx context.Context, endpoint *registry.NSERegistration) (nsm.NetworkServiceClient, error)
	isLocalEndpoint(endpoint *registry.NSERegistration) bool
	checkUpdateNSE(ctx context.Context, reg *registry.NSERegistration) bool
}

type nseManager struct {
	serviceRegistry serviceregistry.ServiceRegistry
	model           model.Model
	properties      *nsm.NsmProperties
}

func (nsem *nseManager) getEndpoint(ctx context.Context, requestConnection nsm.NSMConnection, ignore_endpoints map[string]*registry.NSERegistration) (*registry.NSERegistration, error) {

	// Handle case we are remote NSM and asked for particular endpoint to connect to.
	targetEndpoint := requestConnection.GetNetworkServiceEndpointName()
	if len(targetEndpoint) > 0 {
		endpoint := nsem.model.GetEndpoint(targetEndpoint)
		if endpoint != nil && ignore_endpoints[endpoint.EndpointName()] == nil {
			return endpoint.Endpoint, nil
		} else {
			return nil, fmt.Errorf("Could not find endpoint with name: %s at local registry", targetEndpoint)
		}
	}

	// Get endpoints, do it every time since we do not know if list are changed or not.
	discoveryClient, err := nsem.serviceRegistry.DiscoveryClient()
	if err != nil {
		logrus.Error(err)
		return nil, err
	}
	nseRequest := &registry.FindNetworkServiceRequest{
		NetworkServiceName: requestConnection.GetNetworkService(),
	}
	endpointResponse, err := discoveryClient.FindNetworkService(ctx, nseRequest)
	if err != nil {
		logrus.Error(err)
		return nil, err
	}
	endpoints := filterEndpoints(endpointResponse.GetNetworkServiceEndpoints(), ignore_endpoints)

	if len(endpoints) == 0 {
		return nil, fmt.Errorf("failed to find NSE for NetworkService %s. Checked: %d of total NSEs: %d",
			requestConnection.GetNetworkService(), len(ignore_endpoints), len(endpoints))
	}

	endpoint := nsem.model.GetSelector().SelectEndpoint(requestConnection.(*local_connection.Connection), endpointResponse.GetNetworkService(), endpoints)
	if endpoint == nil {
		return nil, fmt.Errorf("failed to find NSE for NetworkService %s. Checked: %d of total NSEs: %d",
			requestConnection.GetNetworkService(), len(ignore_endpoints), len(endpoints))
	}

	return &registry.NSERegistration{
		NetworkServiceManager:  endpointResponse.GetNetworkServiceManagers()[endpoint.GetNetworkServiceManagerName()],
		NetworkserviceEndpoint: endpoint,
		NetworkService:         endpointResponse.GetNetworkService(),
	}, nil
}

/**
ctx - we assume it is big enought to perform connection.
*/
func (nsem *nseManager) createNSEClient(ctx context.Context, endpoint *registry.NSERegistration) (nsm.NetworkServiceClient, error) {
	if nsem.isLocalEndpoint(endpoint) {
		modelEp := nsem.model.GetEndpoint(endpoint.GetNetworkserviceEndpoint().GetEndpointName())
		if modelEp == nil {
			return nil, fmt.Errorf("Endpoint not found: %v", endpoint)
		}
		logrus.Infof("Create local NSE connection to endpoint: %v", modelEp)
		client, conn, err := nsem.serviceRegistry.EndpointConnection(ctx, modelEp)
		if err != nil {
			// We failed to connect to local NSE.
			nsem.cleanupNSE(modelEp)
			return nil, err
		}
		return &endpointClient{connection: conn, client: client}, nil
	} else {
		logrus.Infof("Create remote NSE connection to endpoint: %v", endpoint)
		client, conn, err := nsem.serviceRegistry.RemoteNetworkServiceClient(ctx, endpoint.GetNetworkServiceManager())
		if err != nil {
			return nil, err
		}
		return &nsmClient{client: client, connection: conn}, nil
	}
}

func (nsem *nseManager) isLocalEndpoint(endpoint *registry.NSERegistration) bool {
	return nsem.model.GetNsm().GetName() == endpoint.GetNetworkserviceEndpoint().GetNetworkServiceManagerName()
}

func (nsem *nseManager) checkUpdateNSE(ctx context.Context, reg *registry.NSERegistration) bool {
	pingCtx, pingCancel := context.WithTimeout(ctx, nsem.properties.HealRequestConnectCheckTimeout)
	defer pingCancel()

	client, err := nsem.createNSEClient(pingCtx, reg)
	if err == nil && client != nil {
		_ = client.Cleanup()
		return true
	}

	return false
}

func (nsem *nseManager) cleanupNSE(endpoint *model.Endpoint) {
	// Remove endpoint from model and put workspace into BAD state.
	_ = nsem.model.DeleteEndpoint(endpoint.EndpointName())
	logrus.Infof("NSM: Remove Endpoint since it is not available... %v", endpoint)
}
<|endoftext|>


<commit_before>// +build fvtests

// Copyright (c) 2017 Tigera, 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.

package fv_test

import (
	"time"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/projectcalico/felix/fv/containers"
	"github.com/projectcalico/felix/fv/metrics"
	"github.com/projectcalico/felix/fv/utils"
	"github.com/projectcalico/felix/fv/workload"
	"github.com/projectcalico/libcalico-go/lib/api"
	"github.com/projectcalico/libcalico-go/lib/client"
	"github.com/projectcalico/libcalico-go/lib/numorstring"
	log "github.com/sirupsen/logrus"
)

func RunEtcd() *containers.Container {
	return containers.Run("etcd-fv",
		"quay.io/coreos/etcd",
		"etcd",
		"--advertise-client-urls", "http://127.0.0.1:2379",
		"--listen-client-urls", "http://0.0.0.0:2379")
}

func RunFelix(etcdIP string) *containers.Container {
	return containers.Run("felix-fv",
		"--privileged",
		"-e", "CALICO_DATASTORE_TYPE=etcdv2",
		"-e", "FELIX_DATASTORETYPE=etcdv2",
		"-e", "FELIX_ETCDENDPOINTS=http://"+etcdIP+":2379",
		"-e", "FELIX_PROMETHEUSMETRICSENABLED=true",
		"-e", "FELIX_USAGEREPORTINGENABLED=false",
		"-e", "FELIX_IPV6SUPPORT=false",
		"calico/felix:latest")
}

func GetEtcdClient(etcdIP string) *client.Client {
	client, err := client.New(api.CalicoAPIConfig{
		Spec: api.CalicoAPIConfigSpec{
			DatastoreType: api.EtcdV2,
			EtcdConfig: api.EtcdConfig{
				EtcdEndpoints: "http://" + etcdIP + ":2379",
			},
		},
	})
	Expect(err).NotTo(HaveOccurred())
	return client
}

func PrintDiags(etcdName, felixName string) {
}

func MetricsPortReachable(felixName, felixIP string) bool {
	// Delete existing conntrack state for the metrics port.
	utils.Run("docker", "exec", felixName,
		"conntrack", "-L")
	utils.Run("docker", "exec", felixName,
		"conntrack", "-L", "-p", "tcp", "--dport", metrics.PortString())
	utils.RunMayFail("docker", "exec", felixName,
		"conntrack", "-D", "-p", "tcp", "--orig-port-dst", metrics.PortString())

	// Now try to get a metric.
	m, err := metrics.GetFelixMetric(felixIP, "felix_active_local_endpoints")
	if err != nil {
		log.WithError(err).Info("Metrics port not reachable")
		return false
	}
	log.WithField("felix_active_local_endpoints", m).Info("Metrics port reachable")
	return true
}

// Here we test reachability to a port number running on a Calico host itself, specifically Felix's
// metrics port 9091, and how that is affected by policy, host endpoint and workload endpoint
// configuration.
//
// - When there is no policy or endpoint configuration, the port should be reachable.
//
// - When there is a local workload endpoint, the port should be reachable.  (Existence of workload
//   endpoints should make no difference to reachability to ports on the host itself.)
//
// - When a host endpoint is configured for the host's interface (eth0), but not yet any policy, the
//   port should be unreachable.
//
//   - When pre-DNAT policy is then configured, to allow ingress to that port, it should be
//     reachable again.

var _ = Context("with initialized Felix and etcd datastore", func() {

	var (
		etcd   *containers.Container
		felix  *containers.Container
		client *client.Client
	)

	BeforeEach(func() {

		etcd = RunEtcd()

		felix = RunFelix(etcd.IP)

		client = GetEtcdClient(etcd.IP)
		err := client.EnsureInitialized()
		Expect(err).NotTo(HaveOccurred())

		felixNode := api.NewNode()
		felixNode.Metadata.Name = felix.Hostname
		_, err = client.Nodes().Create(felixNode)
		Expect(err).NotTo(HaveOccurred())
	})

	AfterEach(func() {

		if CurrentGinkgoTestDescription().Failed {
			utils.Run("docker", "logs", felix.Name)
			utils.Run("docker", "exec", felix.Name, "iptables-save", "-c")
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			utils.Run("docker", "exec", etcd.Name, "etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	It("with no endpoints or policy, port should be reachable", func() {
		time.Sleep(3 * time.Second)
		Expect(MetricsPortReachable(felix.Name, felix.IP)).To(BeTrue())
	})

	It("with a local workload, port should be reachable", func() {
		w := workload.Run(felix, "cali12345", "10.65.0.2", "8055")
		w.Configure(client)
		time.Sleep(10 * time.Second)
		Expect(MetricsPortReachable(felix.Name, felix.IP)).To(BeTrue())
		w.Stop()
		Expect(MetricsPortReachable(felix.Name, felix.IP)).To(BeTrue())
	})

	Context("with host endpoint defined", func() {

		BeforeEach(func() {
			hostEp := api.NewHostEndpoint()
			hostEp.Metadata.Name = "host-endpoint-1"
			hostEp.Metadata.Node = felix.Hostname
			hostEp.Metadata.Labels = map[string]string{"host-endpoint": "true"}
			hostEp.Spec.InterfaceName = "eth0"
			_, err := client.HostEndpoints().Create(hostEp)
			Expect(err).NotTo(HaveOccurred())
		})

		It("port should not be reachable", func() {
			time.Sleep(3 * time.Second)
			Expect(MetricsPortReachable(felix.Name, felix.IP)).To(BeFalse())
		})

		Context("with pre-DNAT policy defined", func() {

			BeforeEach(func() {
				policy := api.NewPolicy()
				policy.Metadata.Name = "pre-dnat-policy-1"
				policy.Spec.PreDNAT = true
				protocol := numorstring.ProtocolFromString("tcp")
				allowMetricsPortRule := api.Rule{
					Action:   "allow",
					Protocol: &protocol,
					Destination: api.EntityRule{
						Ports: []numorstring.Port{numorstring.SinglePort(uint16(metrics.Port))},
					},
				}
				policy.Spec.IngressRules = []api.Rule{allowMetricsPortRule}
				policy.Spec.Selector = "host-endpoint=='true'"
				_, err := client.Policies().Create(policy)
				Expect(err).NotTo(HaveOccurred())
			})

			It("port should be reachable", func() {
				time.Sleep(3 * time.Second)
				Expect(MetricsPortReachable(felix.Name, felix.IP)).To(BeTrue())
			})
		})
	})
})

// Setup for planned further FV tests:
//
//     | +-----------+ +-----------+ |  | +-----------+ +-----------+ |
//     | | service A | | service B | |  | | service C | | service D | |
//     | | 10.65.0.2 | | 10.65.0.3 | |  | | 10.65.0.4 | | 10.65.0.5 | |
//     | | port 9002 | | port 9003 | |  | | port 9004 | | port 9005 | |
//     | | np 109002 | | port 9003 | |  | | port 9004 | | port 9005 | |
//     | +-----------+ +-----------+ |  | +-----------+ +-----------+ |
//     +-----------------------------+  +-----------------------------+
<commit_msg>Review markups<commit_after>// +build fvtests

// Copyright (c) 2017 Tigera, 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.

package fv_test

import (
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/projectcalico/felix/fv/containers"
	"github.com/projectcalico/felix/fv/metrics"
	"github.com/projectcalico/felix/fv/utils"
	"github.com/projectcalico/felix/fv/workload"
	"github.com/projectcalico/libcalico-go/lib/api"
	"github.com/projectcalico/libcalico-go/lib/client"
	"github.com/projectcalico/libcalico-go/lib/numorstring"
	log "github.com/sirupsen/logrus"
)

func RunEtcd() *containers.Container {
	return containers.Run("etcd-fv",
		"quay.io/coreos/etcd",
		"etcd",
		"--advertise-client-urls", "http://127.0.0.1:2379",
		"--listen-client-urls", "http://0.0.0.0:2379")
}

func RunFelix(etcdIP string) *containers.Container {
	return containers.Run("felix-fv",
		"--privileged",
		"-e", "CALICO_DATASTORE_TYPE=etcdv2",
		"-e", "FELIX_DATASTORETYPE=etcdv2",
		"-e", "FELIX_ETCDENDPOINTS=http://"+etcdIP+":2379",
		"-e", "FELIX_PROMETHEUSMETRICSENABLED=true",
		"-e", "FELIX_USAGEREPORTINGENABLED=false",
		"-e", "FELIX_IPV6SUPPORT=false",
		"calico/felix:latest")
}

func GetEtcdClient(etcdIP string) *client.Client {
	client, err := client.New(api.CalicoAPIConfig{
		Spec: api.CalicoAPIConfigSpec{
			DatastoreType: api.EtcdV2,
			EtcdConfig: api.EtcdConfig{
				EtcdEndpoints: "http://" + etcdIP + ":2379",
			},
		},
	})
	Expect(err).NotTo(HaveOccurred())
	return client
}

func PrintDiags(etcdName, felixName string) {
}

func MetricsPortReachable(felixName, felixIP string) bool {
	// Delete existing conntrack state for the metrics port.
	utils.Run("docker", "exec", felixName,
		"conntrack", "-L")
	utils.Run("docker", "exec", felixName,
		"conntrack", "-L", "-p", "tcp", "--dport", metrics.PortString())
	utils.RunMayFail("docker", "exec", felixName,
		"conntrack", "-D", "-p", "tcp", "--orig-port-dst", metrics.PortString())

	// Now try to get a metric.
	m, err := metrics.GetFelixMetric(felixIP, "felix_active_local_endpoints")
	if err != nil {
		log.WithError(err).Info("Metrics port not reachable")
		return false
	}
	log.WithField("felix_active_local_endpoints", m).Info("Metrics port reachable")
	return true
}

// Here we test reachability to a port number running on a Calico host itself, specifically Felix's
// metrics port 9091, and how that is affected by policy, host endpoint and workload endpoint
// configuration.
//
// - When there is no policy or endpoint configuration, the port should be reachable.
//
// - When there is a local workload endpoint, the port should be reachable.  (Existence of workload
//   endpoints should make no difference to reachability to ports on the host itself.)
//
// - When a host endpoint is configured for the host's interface (eth0), but not yet any policy, the
//   port should be unreachable.
//
//   - When pre-DNAT policy is then configured, to allow ingress to that port, it should be
//     reachable again.

var _ = Context("with initialized Felix and etcd datastore", func() {

	var (
		etcd                 *containers.Container
		felix                *containers.Container
		client               *client.Client
		metricsPortReachable func() bool
	)

	BeforeEach(func() {

		etcd = RunEtcd()

		felix = RunFelix(etcd.IP)

		client = GetEtcdClient(etcd.IP)
		err := client.EnsureInitialized()
		Expect(err).NotTo(HaveOccurred())

		felixNode := api.NewNode()
		felixNode.Metadata.Name = felix.Hostname
		_, err = client.Nodes().Create(felixNode)
		Expect(err).NotTo(HaveOccurred())

		metricsPortReachable = func() bool {
			return MetricsPortReachable(felix.Name, felix.IP)
		}
	})

	AfterEach(func() {

		if CurrentGinkgoTestDescription().Failed {
			utils.Run("docker", "logs", felix.Name)
			utils.Run("docker", "exec", felix.Name, "iptables-save", "-c")
		}
		felix.Stop()

		if CurrentGinkgoTestDescription().Failed {
			utils.Run("docker", "exec", etcd.Name, "etcdctl", "ls", "--recursive", "/")
		}
		etcd.Stop()
	})

	It("with no endpoints or policy, port should be reachable", func() {
		Eventually(metricsPortReachable, "10s", "1s").Should(BeTrue())
	})

	It("with a local workload, port should be reachable", func() {
		w := workload.Run(felix, "cali12345", "10.65.0.2", "8055")
		w.Configure(client)
		Eventually(metricsPortReachable, "10s", "1s").Should(BeTrue())
		w.Stop()
		Eventually(metricsPortReachable, "10s", "1s").Should(BeTrue())
	})

	Context("with host endpoint defined", func() {

		BeforeEach(func() {
			hostEp := api.NewHostEndpoint()
			hostEp.Metadata.Name = "host-endpoint-1"
			hostEp.Metadata.Node = felix.Hostname
			hostEp.Metadata.Labels = map[string]string{"host-endpoint": "true"}
			hostEp.Spec.InterfaceName = "eth0"
			_, err := client.HostEndpoints().Create(hostEp)
			Expect(err).NotTo(HaveOccurred())
		})

		It("port should not be reachable", func() {
			Eventually(metricsPortReachable, "10s", "1s").Should(BeFalse())
		})

		Context("with pre-DNAT policy defined", func() {

			BeforeEach(func() {
				policy := api.NewPolicy()
				policy.Metadata.Name = "pre-dnat-policy-1"
				policy.Spec.PreDNAT = true
				protocol := numorstring.ProtocolFromString("tcp")
				allowMetricsPortRule := api.Rule{
					Action:   "allow",
					Protocol: &protocol,
					Destination: api.EntityRule{
						Ports: []numorstring.Port{numorstring.SinglePort(uint16(metrics.Port))},
					},
				}
				policy.Spec.IngressRules = []api.Rule{allowMetricsPortRule}
				policy.Spec.Selector = "host-endpoint=='true'"
				_, err := client.Policies().Create(policy)
				Expect(err).NotTo(HaveOccurred())
			})

			It("port should be reachable", func() {
				Eventually(metricsPortReachable, "10s", "1s").Should(BeTrue())
			})
		})
	})
})

// Setup for planned further FV tests:
//
//     | +-----------+ +-----------+ |  | +-----------+ +-----------+ |
//     | | service A | | service B | |  | | service C | | service D | |
//     | | 10.65.0.2 | | 10.65.0.3 | |  | | 10.65.0.4 | | 10.65.0.5 | |
//     | | port 9002 | | port 9003 | |  | | port 9004 | | port 9005 | |
//     | | np 109002 | | port 9003 | |  | | port 9004 | | port 9005 | |
//     | +-----------+ +-----------+ |  | +-----------+ +-----------+ |
//     +-----------------------------+  +-----------------------------+
<|endoftext|>