<commit_before>package expr

import (
	"reflect"
	"testing"

	"github.com/raintank/metrictank/api/models"
	"github.com/raintank/metrictank/consolidation"
)

// here we use smartSummarize because it has multiple optional arguments which allows us to test some interesting things
func TestArgs(t *testing.T) {

	from := uint32(1000)
	to := uint32(2000)
	stable := true

	cases := []struct {
		name      string
		args      []*expr
		namedArgs map[string]*expr
		expReq    []Req
		expErr    error
	}{
		{
			"2 args normal, 0 optional",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by position",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by key",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 1 by position, 1 by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 by position, 1 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"alignToFrom"},
		},
		{
			"2 args normal, 1 by position, 2 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"func"},
		},
		{
			"2 args normal, 0 by position, the first by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func": {etype: etString, str: "sum"},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 0 by position, the second by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"missing required argument",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
			},
			nil,
			nil,
			ErrMissingArg,
		},
	}

	fn := NewSmartSummarize()
	for i, c := range cases {
		e := &expr{
			etype:     etFunc,
			str:       "smartSummarize",
			args:      c.args,
			namedArgs: c.namedArgs,
		}
		req, err := newplanFunc(e, fn, Context{from: from, to: to}, stable, nil)
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.name, c.expErr, err)
		}
		if !reflect.DeepEqual(req, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.name, c.expReq, req)
		}
	}
}

// TestConsolidateBy tests for a variety of input targets, wether consolidateBy settings are correctly
// propagated down the tree (to fetch requests) and up the tree (to runtime consolidation of the output)
func TestConsolidateBy(t *testing.T) {
	from := uint32(1000)
	to := uint32(2000)
	stable := true
	cases := []struct {
		in     string
		expReq []Req // to verify consolidation settings of fetch requests
		expErr error
		expOut []models.Series // we only use QueryPatt and Consolidator field just to check consolidation settings of output series
	}{
		{
			"a",
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: "a", Consolidator: consolidation.Avg},
			},
		},
		{
			// consolidation flows both up and down the tree
			`consolidateBy(a, "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(a,"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrap with regular function -> consolidation goes both up and down
			`scale(consolidateBy(a, "sum"),1)`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `scale(consolidateBy(a,"sum"),1.000000)`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrapping by a special function does not affect fetch consolidation, but resets output consolidation
			`perSecond(consolidateBy(a, "sum"))`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `perSecond(consolidateBy(a,"sum"))`, Consolidator: consolidation.None},
			},
		},
		{
			// consolidation setting streams down and up unaffected by scale
			`consolidateBy(scale(a, 1), "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(scale(a,1.000000),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// perSecond changes data semantics, fetch consolidation should be reset to default
			`consolidateBy(perSecond(a), "sum")`,
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(perSecond(a),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			// TODO: I think it can be argued that the max here is only intended for the output, not to the inputs
			`consolidateBy(divideSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(divideSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			`consolidateBy(sumSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(sumSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
	}

	for i, c := range cases {
		// for the purpose of this test, we assume ParseMany works fine.
		exprs, _ := ParseMany([]string{c.in})
		plan, err := NewPlan(exprs, from, to, 800, stable, nil)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.in, c.expErr, err)
		}
		if !reflect.DeepEqual(plan.Reqs, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.in, c.expReq, plan.Reqs)
		}
		input := map[Req][]models.Series{
			NewReq("a", from, to, 0): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Avg, // emulate the fact that a by default will use avg
			}},
			NewReq("a", from, to, consolidation.Min): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Min,
			}},
			NewReq("a", from, to, consolidation.Sum): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Sum,
			}},
			NewReq("b", from, to, consolidation.Max): {{
				QueryPatt:    "b",
				Consolidator: consolidation.Max,
			}},
		}
		out, err := plan.Run(input)
		if err != nil {
			t.Fatal(err)
		}
		if len(out) != len(c.expOut) {
			t.Errorf("case %d: %q, expected %d series output, not %d", i, c.in, len(c.expOut), len(out))
		} else {
			for j, exp := range c.expOut {
				if exp.QueryPatt != out[j].QueryPatt || exp.Consolidator != out[j].Consolidator {
					t.Errorf("case %d: %q, output series mismatch at pos %d: expected %v-%v - got %v-%v", i, c.in, j, exp.QueryPatt, exp.Consolidator, out[j].QueryPatt, out[j].Consolidator)
				}
			}
		}
	}
}
<commit_msg>add new tests for target names after longer function chains<commit_after>package expr

import (
	"reflect"
	"testing"

	"github.com/raintank/metrictank/api/models"
	"github.com/raintank/metrictank/consolidation"
)

// here we use smartSummarize because it has multiple optional arguments which allows us to test some interesting things
func TestArgs(t *testing.T) {

	from := uint32(1000)
	to := uint32(2000)
	stable := true

	cases := []struct {
		name      string
		args      []*expr
		namedArgs map[string]*expr
		expReq    []Req
		expErr    error
	}{
		{
			"2 args normal, 0 optional",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by position",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			nil,
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 optional by key",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 1 by position, 1 by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 2 by position, 1 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
				{etype: etBool, bool: true},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"alignToFrom"},
		},
		{
			"2 args normal, 1 by position, 2 by keyword (duplicate!)",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
				{etype: etString, str: "sum"},
			},
			map[string]*expr{
				"func":        {etype: etString, str: "sum"},
				"alignToFrom": {etype: etBool, bool: true},
			},
			nil,
			ErrKwargSpecifiedTwice{"func"},
		},
		{
			"2 args normal, 0 by position, the first by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"func": {etype: etString, str: "sum"},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"2 args normal, 0 by position, the second by keyword",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
				{etype: etString, str: "1hour"},
			},
			map[string]*expr{
				"alignToFrom": {etype: etBool, bool: true},
			},
			[]Req{
				NewReq("foo.bar.*", from, to, 0),
			},
			nil,
		},
		{
			"missing required argument",
			[]*expr{
				{etype: etName, str: "foo.bar.*"},
			},
			nil,
			nil,
			ErrMissingArg,
		},
	}

	fn := NewSmartSummarize()
	for i, c := range cases {
		e := &expr{
			etype:     etFunc,
			str:       "smartSummarize",
			args:      c.args,
			namedArgs: c.namedArgs,
		}
		req, err := newplanFunc(e, fn, Context{from: from, to: to}, stable, nil)
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.name, c.expErr, err)
		}
		if !reflect.DeepEqual(req, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.name, c.expReq, req)
		}
	}
}

// TestConsolidateBy tests for a variety of input targets, wether consolidateBy settings are correctly
// propagated down the tree (to fetch requests) and up the tree (to runtime consolidation of the output)
func TestConsolidateBy(t *testing.T) {
	from := uint32(1000)
	to := uint32(2000)
	stable := true
	cases := []struct {
		in     string
		expReq []Req // to verify consolidation settings of fetch requests
		expErr error
		expOut []models.Series // we only use QueryPatt and Consolidator field just to check consolidation settings of output series
	}{
		{
			"a",
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: "a", Consolidator: consolidation.Avg},
			},
		},
		{
			// consolidation flows both up and down the tree
			`consolidateBy(a, "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(a,"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrap with regular function -> consolidation goes both up and down
			`scale(consolidateBy(a, "sum"),1)`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `scale(consolidateBy(a,"sum"),1.000000)`, Consolidator: consolidation.Sum},
			},
		},
		{
			// wrapping by a special function does not affect fetch consolidation, but resets output consolidation
			`perSecond(consolidateBy(a, "sum"))`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `perSecond(consolidateBy(a,"sum"))`, Consolidator: consolidation.None},
			},
		},
		{
			// consolidation setting streams down and up unaffected by scale
			`consolidateBy(scale(a, 1), "sum")`,
			[]Req{
				NewReq("a", from, to, consolidation.Sum),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(scale(a,1.000000),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// perSecond changes data semantics, fetch consolidation should be reset to default
			`consolidateBy(perSecond(a), "sum")`,
			[]Req{
				NewReq("a", from, to, 0),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(perSecond(a),"sum")`, Consolidator: consolidation.Sum},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			// TODO: I think it can be argued that the max here is only intended for the output, not to the inputs
			`consolidateBy(divideSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(divideSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
		{
			// data should be requested with fetch consolidation min, but runtime consolidation max
			`consolidateBy(sumSeries(consolidateBy(a, "min"), b), "max")`,
			[]Req{
				NewReq("a", from, to, consolidation.Min),
				NewReq("b", from, to, consolidation.Max),
			},
			nil,
			[]models.Series{
				{QueryPatt: `consolidateBy(sumSeries(consolidateBy(a,"min"),b),"max")`, Consolidator: consolidation.Max},
			},
		},
	}

	for i, c := range cases {
		// for the purpose of this test, we assume ParseMany works fine.
		exprs, _ := ParseMany([]string{c.in})
		plan, err := NewPlan(exprs, from, to, 800, stable, nil)
		if err != nil {
			t.Fatal(err)
		}
		if !reflect.DeepEqual(err, c.expErr) {
			t.Errorf("case %d: %q, expected error %v - got %v", i, c.in, c.expErr, err)
		}
		if !reflect.DeepEqual(plan.Reqs, c.expReq) {
			t.Errorf("case %d: %q, expected req %v - got %v", i, c.in, c.expReq, plan.Reqs)
		}
		input := map[Req][]models.Series{
			NewReq("a", from, to, 0): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Avg, // emulate the fact that a by default will use avg
			}},
			NewReq("a", from, to, consolidation.Min): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Min,
			}},
			NewReq("a", from, to, consolidation.Sum): {{
				QueryPatt:    "a",
				Consolidator: consolidation.Sum,
			}},
			NewReq("b", from, to, consolidation.Max): {{
				QueryPatt:    "b",
				Consolidator: consolidation.Max,
			}},
		}
		out, err := plan.Run(input)
		if err != nil {
			t.Fatal(err)
		}
		if len(out) != len(c.expOut) {
			t.Errorf("case %d: %q, expected %d series output, not %d", i, c.in, len(c.expOut), len(out))
		} else {
			for j, exp := range c.expOut {
				if exp.QueryPatt != out[j].QueryPatt || exp.Consolidator != out[j].Consolidator {
					t.Errorf("case %d: %q, output series mismatch at pos %d: expected %v-%v - got %v-%v", i, c.in, j, exp.QueryPatt, exp.Consolidator, out[j].QueryPatt, out[j].Consolidator)
				}
			}
		}
	}
}

// TestNamingChains tests whether series names (targets) are correct, after a processing chain of multiple functions
func TestNamingChains(t *testing.T) {
	from := uint32(1000)
	to := uint32(2000)
	stable := true
	cases := []struct {
		target string // target request "from user"
		keys   []string
		expOut []string
	}{
		// the first two are cases that we've seen in the wild
		// see https://github.com/raintank/metrictank/issues/648
		{
			`aliasSub(perSecond(metrictank.stats.*.*.input.*.metric_invalid.counter32),'.*\.([^\.]+)\.metric_invalid.*', '\1 metric invalid')`,
			[]string{
				"metrictank.stats.env.instance1.input.carbon.metric_invalid.counter32",
				"metrictank.stats.env.instance2.input.carbon.metric_invalid.counter32",
				"metrictank.stats.env.instance1.input.kafka.metric_invalid.counter32",
			},
			[]string{
				"carbon metric invalid",
				"carbon metric invalid",
				"kafka metric invalid",
			},
		},
		{
			`aliasSub(sumSeries(perSecond(metrictank.stats.*.*.input.*.metric_invalid.counter32)),'.*\.([^\.]+)\.metric_invalid.*', '\1 metric invalid')`,
			[]string{
				"metrictank.stats.env.instance1.input.carbon.metric_invalid.counter32",
				"metrictank.stats.env.instance2.input.carbon.metric_invalid.counter32",
				"metrictank.stats.env.instance1.input.kafka.metric_invalid.counter32",
			},
			[]string{
				"* metric invalid",
			},
		},
		{
			`aliasSub(sumSeries(perSecond(metrictank.stats.*.*.input.*.metric_invalid.counter32)),'.*\.([^\.]+)\.metric_invalid.*', '\1 metric invalid')`,
			[]string{
				"metrictank.stats.env.instance1.input.carbon.metric_invalid.counter32",
			},
			[]string{
				"* metric invalid", // could be argued that "carbon metric invalid" is more useful, but is less consistent. see #648
			},
		},
		// what follows here is a simplified, but comparable test case, for other alias functions
		{
			`aliasByNode(perSecond(*.bar), 0)`,
			[]string{
				"a.bar",
				"b.bar",
			},
			[]string{
				"a",
				"b",
			},
		},
		{
			`aliasByNode(avg(perSecond(*.bar)), 0)`,
			[]string{
				"a.bar",
				"b.bar",
			},
			[]string{
				"*",
			},
		},
		{
			`aliasByNode(avg(perSecond(*.bar)), 0)`,
			[]string{
				"a.bar",
			},
			[]string{
				"*", // "a" could be more useful but see #648
			},
		},
		{
			`alias(perSecond(*.bar), 'a')`,
			[]string{
				"a.bar",
				"b.bar",
			},
			[]string{
				"a",
				"a",
			},
		},
		{
			`alias(avg(perSecond(*.bar)), 'a')`,
			[]string{
				"a",
				"b",
			},
			[]string{
				"a",
			},
		},
		{
			`alias(avg(perSecond(*.bar)), 'a')`,
			[]string{
				"a",
			},
			[]string{
				"a",
			},
		},
	}

	for i, c := range cases {
		exprs, err := ParseMany([]string{c.target})
		if err != nil {
			t.Fatal(err)
		}
		plan, err := NewPlan(exprs, from, to, 800, stable, nil)
		if err != nil {
			t.Fatal(err)
		}

		// create input data
		series := make([]models.Series, len(c.keys))
		for j, key := range c.keys {
			series[j] = models.Series{
				QueryPatt: plan.Reqs[0].Query,
				Target:    key,
			}
		}
		input := map[Req][]models.Series{
			plan.Reqs[0]: series,
		}
		out, err := plan.Run(input)
		if err != nil {
			t.Fatal(err)
		}
		if len(out) != len(c.expOut) {
			t.Errorf("case %d:\n%q with %d inputs:\nexpected %d series output, not %d", i, c.target, len(c.keys), len(c.expOut), len(out))
		} else {
			for j, exp := range c.expOut {
				if out[j].Target != exp {
					t.Errorf("case %d:\n%q with %d inputs:\noutput series mismatch at pos %d:\nexp: %v\ngot: %v", i, c.target, len(c.keys), j, exp, out[j].Target)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>package antibody_test

import (
	"io/ioutil"
	"os"
	"testing"

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

func TestAntibody(t *testing.T) {
	assert := assert.New(t)
	home := home()
	defer os.RemoveAll(home)
	bundles := []string{
		"# comments also are allowed",
		"caarlos0/ports kind:path",
		"caarlos0/jvm kind:path branch:gh-pages",
		"caarlos0/zsh-open-pr     kind:zsh",
		"",
		"        ",
		"  # trick play",
		"/tmp kind:path",
	}
	sh, err := antibody.New(home, bundles).Bundle()
	assert.NoError(err)
	files, err := ioutil.ReadDir(home)
	assert.NoError(err)
	assert.Len(files, 3)
	assert.Contains(sh, `export PATH="/tmp:$PATH"`)
	assert.Contains(sh, `export PATH="`+home+`/https-COLON--SLASH--SLASH-github.com-SLASH-caarlos0-SLASH-ports:$PATH"`)
	assert.Contains(sh, `export PATH="`+home+`/https-COLON--SLASH--SLASH-github.com-SLASH-caarlos0-SLASH-jvm:$PATH"`)
	assert.Contains(sh, `source `+home+`/https-COLON--SLASH--SLASH-github.com-SLASH-caarlos0-SLASH-zsh-open-pr/open-pr.plugin.zsh`)
}

func TestAntibodyError(t *testing.T) {
	assert := assert.New(t)
	home := home()
	defer os.RemoveAll(home)
	bundles := []string{"invalid-repo"}
	sh, err := antibody.New(home, bundles).Bundle()
	assert.Error(err)
	assert.Empty(sh)
}

func TestHome(t *testing.T) {
	assert.Contains(t, antibody.Home(), "antibody")
}

func TestHomeFromEnvironmentVariable(t *testing.T) {
	os.Setenv("ANTIBODY_HOME", "/tmp")
	assert.Equal(t, "/tmp", antibody.Home())
}

func home() string {
	home, err := ioutil.TempDir(os.TempDir(), "antibody")
	if err != nil {
		panic(err.Error())
	}
	return home
}
<commit_msg>one more test<commit_after>package antibody_test

import (
	"io/ioutil"
	"os"
	"testing"

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

func TestAntibody(t *testing.T) {
	assert := assert.New(t)
	home := home()
	defer os.RemoveAll(home)
	bundles := []string{
		"# comments also are allowed",
		"caarlos0/ports kind:path # comment at the end of the line",
		"caarlos0/jvm kind:path branch:gh-pages",
		"caarlos0/zsh-open-pr     kind:zsh",
		"",
		"        ",
		"  # trick play",
		"/tmp kind:path",
	}
	sh, err := antibody.New(home, bundles).Bundle()
	assert.NoError(err)
	files, err := ioutil.ReadDir(home)
	assert.NoError(err)
	assert.Len(files, 3)
	assert.Contains(sh, `export PATH="/tmp:$PATH"`)
	assert.Contains(sh, `export PATH="`+home+`/https-COLON--SLASH--SLASH-github.com-SLASH-caarlos0-SLASH-ports:$PATH"`)
	assert.Contains(sh, `export PATH="`+home+`/https-COLON--SLASH--SLASH-github.com-SLASH-caarlos0-SLASH-jvm:$PATH"`)
	assert.Contains(sh, `source `+home+`/https-COLON--SLASH--SLASH-github.com-SLASH-caarlos0-SLASH-zsh-open-pr/open-pr.plugin.zsh`)
}

func TestAntibodyError(t *testing.T) {
	assert := assert.New(t)
	home := home()
	defer os.RemoveAll(home)
	bundles := []string{"invalid-repo"}
	sh, err := antibody.New(home, bundles).Bundle()
	assert.Error(err)
	assert.Empty(sh)
}

func TestHome(t *testing.T) {
	assert.Contains(t, antibody.Home(), "antibody")
}

func TestHomeFromEnvironmentVariable(t *testing.T) {
	os.Setenv("ANTIBODY_HOME", "/tmp")
	assert.Equal(t, "/tmp", antibody.Home())
}

func home() string {
	home, err := ioutil.TempDir(os.TempDir(), "antibody")
	if err != nil {
		panic(err.Error())
	}
	return home
}
<|endoftext|>


<commit_before>package addrs

import (
	"fmt"
)

// ModuleCall is the address of a call from the current module to a child
// module.
//
// There is no "Abs" version of ModuleCall because an absolute module path
// is represented by ModuleInstance.
type ModuleCall struct {
	referenceable
	Name string
}

func (c ModuleCall) String() string {
	return "module." + c.Name
}

// Instance returns the address of an instance of the receiver identified by
// the given key.
func (c ModuleCall) Instance(key InstanceKey) ModuleCallInstance {
	return ModuleCallInstance{
		Call: c,
		Key:  key,
	}
}

// ModuleCallInstance is the address of one instance of a module created from
// a module call, which might create multiple instances using "count" or
// "for_each" arguments.
type ModuleCallInstance struct {
	referenceable
	Call ModuleCall
	Key  InstanceKey
}

func (c ModuleCallInstance) String() string {
	if c.Key == NoKey {
		return c.Call.String()
	}
	return fmt.Sprintf("module.%s%s", c.Call.Name, c.Key)
}

// Output returns the address of an output of the receiver identified by its
// name.
func (c ModuleCallInstance) Output(name string) ModuleCallOutput {
	return ModuleCallOutput{
		Call: c,
		Name: name,
	}
}

// ModuleCallOutput is the address of a particular named output produced by
// an instance of a module call.
type ModuleCallOutput struct {
	referenceable
	Call ModuleCallInstance
	Name string
}

func (co ModuleCallOutput) String() string {
	return fmt.Sprintf("%s.%s", co.Call.String(), co.Name)
}
<commit_msg>addrs: Helper methods for converting module calls to absolute modules<commit_after>package addrs

import (
	"fmt"
)

// ModuleCall is the address of a call from the current module to a child
// module.
//
// There is no "Abs" version of ModuleCall because an absolute module path
// is represented by ModuleInstance.
type ModuleCall struct {
	referenceable
	Name string
}

func (c ModuleCall) String() string {
	return "module." + c.Name
}

// Instance returns the address of an instance of the receiver identified by
// the given key.
func (c ModuleCall) Instance(key InstanceKey) ModuleCallInstance {
	return ModuleCallInstance{
		Call: c,
		Key:  key,
	}
}

// ModuleCallInstance is the address of one instance of a module created from
// a module call, which might create multiple instances using "count" or
// "for_each" arguments.
type ModuleCallInstance struct {
	referenceable
	Call ModuleCall
	Key  InstanceKey
}

func (c ModuleCallInstance) String() string {
	if c.Key == NoKey {
		return c.Call.String()
	}
	return fmt.Sprintf("module.%s%s", c.Call.Name, c.Key)
}

// ModuleInstance returns the address of the module instance that corresponds
// to the receiving call instance when resolved in the given calling module.
// In other words, it returns the child module instance that the receving
// call instance creates.
func (c ModuleCallInstance) ModuleInstance(caller ModuleInstance) ModuleInstance {
	return caller.Child(c.Call.Name, c.Key)
}

// Output returns the address of an output of the receiver identified by its
// name.
func (c ModuleCallInstance) Output(name string) ModuleCallOutput {
	return ModuleCallOutput{
		Call: c,
		Name: name,
	}
}

// ModuleCallOutput is the address of a particular named output produced by
// an instance of a module call.
type ModuleCallOutput struct {
	referenceable
	Call ModuleCallInstance
	Name string
}

func (co ModuleCallOutput) String() string {
	return fmt.Sprintf("%s.%s", co.Call.String(), co.Name)
}

// AbsOutputValue returns the absolute output value address that corresponds
// to the receving module call output address, once resolved in the given
// calling module.
func (co ModuleCallOutput) AbsOutputValue(caller ModuleInstance) AbsOutputValue {
	moduleAddr := co.Call.ModuleInstance(caller)
	return moduleAddr.OutputValue(co.Name)
}
<|endoftext|>


<commit_before>package api

import "testing"

func TestCreateUrn(t *testing.T) {
	testcases := map[string]struct {
		org         string
		resource    string
		path        string
		name        string
		expectedUrn string
	}{
		"UserResource": {
			resource:    RESOURCE_USER,
			path:        "/mypath/",
			name:        "user",
			expectedUrn: "urn:iws:iam::user/mypath/user",
		},
		"GroupResource": {
			resource:    RESOURCE_GROUP,
			org:         "org1",
			path:        "/mygrouppath/",
			name:        "group",
			expectedUrn: "urn:iws:iam:org1:group/mygrouppath/group",
		},
		"PolicyResource": {
			resource:    RESOURCE_POLICY,
			org:         "org1",
			path:        "/policypath/",
			name:        "policy",
			expectedUrn: "urn:iws:iam:org1:policy/policypath/policy",
		},
	}

	for x, testcase := range testcases {
		urn := CreateUrn(testcase.org, testcase.resource, testcase.path, testcase.name)
		if urn != testcase.expectedUrn {
			t.Fatalf("Test %v failed. Received different urns (wanted: %v / received: %v)",
				x, testcase.expectedUrn, urn)
		}
	}
}

func TestGetUrnPrefix(t *testing.T) {
	testcases := map[string]struct {
		org         string
		resource    string
		path        string
		expectedUrn string
	}{
		"UserResourcePrefix": {
			resource:    RESOURCE_USER,
			path:        "/mypath/",
			expectedUrn: "urn:iws:iam::user/mypath/*",
		},
		"GroupResourcePrefix": {
			resource:    RESOURCE_GROUP,
			org:         "org1",
			path:        "/mygrouppath",
			expectedUrn: "urn:iws:iam:org1:group/mygrouppath*",
		},
		"PolicyResourcePrefix": {
			resource:    RESOURCE_POLICY,
			org:         "org1",
			path:        "/policypath/",
			expectedUrn: "urn:iws:iam:org1:policy/policypath/*",
		},
	}

	for x, testcase := range testcases {
		urn := GetUrnPrefix(testcase.org, testcase.resource, testcase.path)
		if urn != testcase.expectedUrn {
			t.Fatalf("Test %v failed. Received different urns (wanted: %v / received: %v)",
				x, testcase.expectedUrn, urn)
		}
	}
}

func TestIsValidUserExternalID(t *testing.T) {
	testcases := map[string]struct {
		externalID string
		valid      bool
	}{
		"Case1": {
			externalID: "",
			valid:      false,
		},
		"Case2": {
			externalID: "*",
			valid:      false,
		},
		"Case3": {
			externalID: "/",
			valid:      false,
		},
		"Case4": {
			externalID: "something/",
			valid:      false,
		},
		"Case5": {
			externalID: "prefix*",
			valid:      false,
		},
		"Case6": {
			externalID: "pre*fix",
			valid:      false,
		},
		"Case7": {
			externalID: "comma,",
			valid:      false,
		},
		"Case8": {
			externalID: GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), MAX_EXTERNAL_ID_LENGTH+1),
			valid:      false,
		},
		"Case9": {
			externalID: "good",
			valid:      true,
		},
		"Case10": {
			externalID: "123456",
			valid:      true,
		},
		"Case11": {
			externalID: "example-of-user123@email.com-that-is-valid",
			valid:      true,
		},
	}

	for x, testcase := range testcases {
		valid := IsValidUserExternalID(testcase.externalID)
		if valid != testcase.valid {
			t.Fatalf("Test %v failed. Received different values (wanted: %v / received: %v)",
				x, testcase.valid, valid)
		}
	}
}

func TestIsValidName(t *testing.T) {
	testcases := map[string]struct {
		name  string
		valid bool
	}{
		"Case1": {
			name:  "",
			valid: false,
		},
		"Case2": {
			name:  "name.value",
			valid: false,
		},
		"Case3": {
			name:  "@",
			valid: false,
		},
		"Case4": {
			name:  ",",
			valid: false,
		},
		"Case5": {
			name:  "/",
			valid: false,
		},
		"Case6": {
			name:  GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), MAX_NAME_LENGTH+1),
			valid: false,
		},
		"Case7": {
			name:  "validName",
			valid: true,
		},
	}

	for x, testcase := range testcases {
		valid := IsValidName(testcase.name)
		if valid != testcase.valid {
			t.Fatalf("Test %v failed. Received different values (wanted: %v / received: %v)",
				x, testcase.valid, valid)
		}
	}
}

func TestIsValidPath(t *testing.T) {
	testcases := map[string]struct {
		path  string
		valid bool
	}{
		"Case1": {
			path:  "",
			valid: false,
		},
		"Case2": {
			path:  "/path",
			valid: false,
		},
		"Case3": {
			path:  "path/",
			valid: false,
		},
		"Case4": {
			path:  ",",
			valid: false,
		},
		"Case5": {
			path:  "*",
			valid: false,
		},
		"Case6": {
			path:  "/*",
			valid: false,
		},
		"Case7": {
			path:  "*/",
			valid: false,
		},
		"Case8": {
			path:  "/*/",
			valid: false,
		},
		"Case9": {
			path:  "path.value",
			valid: false,
		},
		"Case10": {
			path:  "/" + GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), MAX_PATH_LENGTH-1) + "/",
			valid: false,
		},
		"Case11": {
			path:  "/",
			valid: true,
		},
		"Case12": {
			path:  "/path/",
			valid: true,
		},
	}

	for x, testcase := range testcases {
		valid := IsValidPath(testcase.path)
		if valid != testcase.valid {
			t.Fatalf("Test %v failed. Received different values (wanted: %v / received: %v)",
				x, testcase.valid, valid)
		}
	}
}

func TestIsValidEffect(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		effect string
		// Expected results
		wantError *Error
	}{
		"OKCaseAllow": {
			effect: "allow",
		},
		"OKCaseDeny": {
			effect: "deny",
		},
		"ErrorCaseInvalidEffect": {
			effect: "other",
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidEffect(testcase.effect)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Fatalf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
			}
			if apiError.Code != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed. Error: %v", x, err)
			}
		}
	}
}

func TestIsValidAction(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		actions []string
		// Expected results
		wantError *Error
	}{
		"OKCaseValidAction": {
			actions: []string{
				"iam:operation",
				"iam:*",
			},
		},
		"ErrorCaseMalformedAction": {
			actions: []string{
				"iam:",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseMalformedAction2": {
			actions: []string{
				"*",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidCharacters": {
			actions: []string{
				"iam:**",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidCharacters2": {
			actions: []string{
				"iam::",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseMaxLengthExceeded": {
			actions: []string{
				GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:*"), MAX_ACTION_LENGTH+1),
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidAction(testcase.actions)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Fatalf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
			}
			if apiError.Code != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed. Error: %v", x, err)
			}
		}
	}
}

func TestIsValidStatement(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		Statements *[]Statement
		// Expected results
		wantError *Error
	}{
		"OKCase": {
			Statements: &[]Statement{
				Statement{
					Effect: "allow",
					Action: []string{
						USER_ACTION_GET_USER,
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/"),
					},
				},
			},
		},
		"ErrorCaseInvalidEffect": {
			Statements: &[]Statement{
				Statement{
					Effect: "FAILallowZ",
					Action: []string{
						USER_ACTION_GET_USER,
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/"),
					},
				},
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidAction": {
			Statements: &[]Statement{
				Statement{
					Effect: "allow",
					Action: []string{
						"fail***",
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/"),
					},
				},
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidResource": {
			Statements: &[]Statement{
				Statement{
					Effect: "allow",
					Action: []string{
						USER_ACTION_GET_USER,
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/***"),
					},
				},
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidStatement(testcase.Statements)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Errorf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
				continue
			}
			if apiError.Code != testcase.wantError.Code {
				t.Errorf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
				continue
			}
		} else {
			if err != nil {
				t.Errorf("Test %v failed. Error: %v", x, err)
				continue
			}
		}
	}
}
<commit_msg>isValidResource test<commit_after>package api

import "testing"

func TestCreateUrn(t *testing.T) {
	testcases := map[string]struct {
		org         string
		resource    string
		path        string
		name        string
		expectedUrn string
	}{
		"UserResource": {
			resource:    RESOURCE_USER,
			path:        "/mypath/",
			name:        "user",
			expectedUrn: "urn:iws:iam::user/mypath/user",
		},
		"GroupResource": {
			resource:    RESOURCE_GROUP,
			org:         "org1",
			path:        "/mygrouppath/",
			name:        "group",
			expectedUrn: "urn:iws:iam:org1:group/mygrouppath/group",
		},
		"PolicyResource": {
			resource:    RESOURCE_POLICY,
			org:         "org1",
			path:        "/policypath/",
			name:        "policy",
			expectedUrn: "urn:iws:iam:org1:policy/policypath/policy",
		},
	}

	for x, testcase := range testcases {
		urn := CreateUrn(testcase.org, testcase.resource, testcase.path, testcase.name)
		if urn != testcase.expectedUrn {
			t.Fatalf("Test %v failed. Received different urns (wanted: %v / received: %v)",
				x, testcase.expectedUrn, urn)
		}
	}
}

func TestGetUrnPrefix(t *testing.T) {
	testcases := map[string]struct {
		org         string
		resource    string
		path        string
		expectedUrn string
	}{
		"UserResourcePrefix": {
			resource:    RESOURCE_USER,
			path:        "/mypath/",
			expectedUrn: "urn:iws:iam::user/mypath/*",
		},
		"GroupResourcePrefix": {
			resource:    RESOURCE_GROUP,
			org:         "org1",
			path:        "/mygrouppath",
			expectedUrn: "urn:iws:iam:org1:group/mygrouppath*",
		},
		"PolicyResourcePrefix": {
			resource:    RESOURCE_POLICY,
			org:         "org1",
			path:        "/policypath/",
			expectedUrn: "urn:iws:iam:org1:policy/policypath/*",
		},
	}

	for x, testcase := range testcases {
		urn := GetUrnPrefix(testcase.org, testcase.resource, testcase.path)
		if urn != testcase.expectedUrn {
			t.Fatalf("Test %v failed. Received different urns (wanted: %v / received: %v)",
				x, testcase.expectedUrn, urn)
		}
	}
}

func TestIsValidUserExternalID(t *testing.T) {
	testcases := map[string]struct {
		externalID string
		valid      bool
	}{
		"Case1": {
			externalID: "",
			valid:      false,
		},
		"Case2": {
			externalID: "*",
			valid:      false,
		},
		"Case3": {
			externalID: "/",
			valid:      false,
		},
		"Case4": {
			externalID: "something/",
			valid:      false,
		},
		"Case5": {
			externalID: "prefix*",
			valid:      false,
		},
		"Case6": {
			externalID: "pre*fix",
			valid:      false,
		},
		"Case7": {
			externalID: "comma,",
			valid:      false,
		},
		"Case8": {
			externalID: GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), MAX_EXTERNAL_ID_LENGTH+1),
			valid:      false,
		},
		"Case9": {
			externalID: "good",
			valid:      true,
		},
		"Case10": {
			externalID: "123456",
			valid:      true,
		},
		"Case11": {
			externalID: "example-of-user123@email.com-that-is-valid",
			valid:      true,
		},
	}

	for x, testcase := range testcases {
		valid := IsValidUserExternalID(testcase.externalID)
		if valid != testcase.valid {
			t.Fatalf("Test %v failed. Received different values (wanted: %v / received: %v)",
				x, testcase.valid, valid)
		}
	}
}

func TestIsValidName(t *testing.T) {
	testcases := map[string]struct {
		name  string
		valid bool
	}{
		"Case1": {
			name:  "",
			valid: false,
		},
		"Case2": {
			name:  "name.value",
			valid: false,
		},
		"Case3": {
			name:  "@",
			valid: false,
		},
		"Case4": {
			name:  ",",
			valid: false,
		},
		"Case5": {
			name:  "/",
			valid: false,
		},
		"Case6": {
			name:  GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), MAX_NAME_LENGTH+1),
			valid: false,
		},
		"Case7": {
			name:  "validName",
			valid: true,
		},
	}

	for x, testcase := range testcases {
		valid := IsValidName(testcase.name)
		if valid != testcase.valid {
			t.Fatalf("Test %v failed. Received different values (wanted: %v / received: %v)",
				x, testcase.valid, valid)
		}
	}
}

func TestIsValidPath(t *testing.T) {
	testcases := map[string]struct {
		path  string
		valid bool
	}{
		"Case1": {
			path:  "",
			valid: false,
		},
		"Case2": {
			path:  "/path",
			valid: false,
		},
		"Case3": {
			path:  "path/",
			valid: false,
		},
		"Case4": {
			path:  ",",
			valid: false,
		},
		"Case5": {
			path:  "*",
			valid: false,
		},
		"Case6": {
			path:  "/*",
			valid: false,
		},
		"Case7": {
			path:  "*/",
			valid: false,
		},
		"Case8": {
			path:  "/*/",
			valid: false,
		},
		"Case9": {
			path:  "path.value",
			valid: false,
		},
		"Case10": {
			path:  "/" + GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"), MAX_PATH_LENGTH-1) + "/",
			valid: false,
		},
		"Case11": {
			path:  "/",
			valid: true,
		},
		"Case12": {
			path:  "/path/",
			valid: true,
		},
	}

	for x, testcase := range testcases {
		valid := IsValidPath(testcase.path)
		if valid != testcase.valid {
			t.Fatalf("Test %v failed. Received different values (wanted: %v / received: %v)",
				x, testcase.valid, valid)
		}
	}
}

func TestIsValidEffect(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		effect string
		// Expected results
		wantError *Error
	}{
		"OKCaseAllow": {
			effect: "allow",
		},
		"OKCaseDeny": {
			effect: "deny",
		},
		"ErrorCaseInvalidEffect": {
			effect: "other",
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidEffect(testcase.effect)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Fatalf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
			}
			if apiError.Code != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed. Error: %v", x, err)
			}
		}
	}
}

func TestIsValidAction(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		actions []string
		// Expected results
		wantError *Error
	}{
		"OKCaseValidAction": {
			actions: []string{
				"iam:operation",
				"iam:*",
			},
		},
		"ErrorCaseMalformedAction": {
			actions: []string{
				"iam:",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseMalformedAction2": {
			actions: []string{
				"*",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidCharacters": {
			actions: []string{
				"iam:**",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidCharacters2": {
			actions: []string{
				"iam::",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseMaxLengthExceeded": {
			actions: []string{
				GetRandomString([]rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:*"), MAX_ACTION_LENGTH+1),
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidAction(testcase.actions)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Fatalf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
			}
			if apiError.Code != testcase.wantError.Code {
				t.Fatalf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
			}
		} else {
			if err != nil {
				t.Fatalf("Test %v failed. Error: %v", x, err)
			}
		}
	}
}

func TestIsValidStatement(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		Statements *[]Statement
		// Expected results
		wantError *Error
	}{
		"OKCase": {
			Statements: &[]Statement{
				Statement{
					Effect: "allow",
					Action: []string{
						USER_ACTION_GET_USER,
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/"),
					},
				},
			},
		},
		"ErrorCaseInvalidEffect": {
			Statements: &[]Statement{
				Statement{
					Effect: "FAILallowZ",
					Action: []string{
						USER_ACTION_GET_USER,
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/"),
					},
				},
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidAction": {
			Statements: &[]Statement{
				Statement{
					Effect: "allow",
					Action: []string{
						"fail***",
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/"),
					},
				},
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseInvalidResource": {
			Statements: &[]Statement{
				Statement{
					Effect: "allow",
					Action: []string{
						USER_ACTION_GET_USER,
					},
					Resources: []string{
						GetUrnPrefix("", RESOURCE_USER, "/path/***"),
					},
				},
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidStatement(testcase.Statements)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Errorf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
				continue
			}
			if apiError.Code != testcase.wantError.Code {
				t.Errorf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
				continue
			}
		} else {
			if err != nil {
				t.Errorf("Test %v failed. Error: %v", x, err)
				continue
			}
		}
	}
}

func TestIsValidResources(t *testing.T) {
	testcases := map[string]struct {
		// Method args
		Resources []string
		// Expected results
		wantError *Error
	}{
		"OKCase1block": {
			Resources: []string{
				"*",
			},
		},
		"ErrorCase1block": {
			Resources: []string{
				"fail",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"OKCase2block": {
			Resources: []string{
				"urn:*",
			},
		},
		"ErrorCase2blockNotUrn": {
			Resources: []string{
				"fail:asd",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCase2block": {
			Resources: []string{
				"urn:fail",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"OKCase3block": {
			Resources: []string{
				"urn:iws:*",
			},
		},
		"ErrorCase3blockBadString": {
			Resources: []string{
				"urn:iws***:something",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCase3block": {
			Resources: []string{
				"urn:iws:fail",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"OKCase4block": {
			Resources: []string{
				"urn:iws:iam:*",
			},
		},
		"ErrorCase4blockBadString": {
			Resources: []string{
				"urn:iws:some***:fail",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCase4block": {
			Resources: []string{
				"urn:iws:iam:fail",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"OKCase5block": {
			Resources: []string{
				"urn:iws:iam::sysadmins/user1",
			},
		},
		"ErrorCase5blockBadString": {
			Resources: []string{
				"urn:iws:iam:some***:fail",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCase5block": {
			Resources: []string{
				"urn:iws:iam:org1:fail**!^_#",
			},
			wantError: &Error{
				Code: REGEX_NO_MATCH,
			},
		},
		"ErrorCaseBadResource": {
			Resources: []string{
				"urn:iws:iam:org1:fail:fail:fail",
			},
			wantError: &Error{
				Code: INVALID_PARAMETER_ERROR,
			},
		},
	}

	for x, testcase := range testcases {
		err := IsValidResources(testcase.Resources)
		if testcase.wantError != nil {
			apiError, ok := err.(*Error)
			if !ok || apiError == nil {
				t.Errorf("Test %v failed. Unexpected data retrieved from error: %v", x, err)
				continue
			}
			if apiError.Code != testcase.wantError.Code {
				t.Errorf("Test %v failed. Got error %v, expected %v", x, apiError, testcase.wantError.Code)
				continue
			}
		} else {
			if err != nil {
				t.Errorf("Test %v failed. Error: %v", x, err)
				continue
			}
		}
	}
}
<|endoftext|>


<commit_before>// +build OMIT

package main

import "strings"

import "testing"

func TestToUpper(t *testing.T) {
	in := "loud noises"
	expected := "LOUD NOISES"
	got := strings.ToUpper(in)
	if got != want {
		t.Errorf("ToUpper(%v) = %v, want %v", in, got, expected)
	}
}

func TestContains(t *testing.T) {
	var tests = []struct {
		str, substr string
		expected    bool
	}{
		{"abc", "bc", true},
		{"abc", "bcd", false},
		{"abc", "", true},
		{"", "a", false},
	}
	for _, ct := range tests {
		if strings.Contains(ct.str, ct.substr) != ct.expected {
			t.Errorf("Contains(%s, %s) = %v, want %v",
				ct.str, ct.substr, !ct.expected, ct.expected)
		}
	}
}
<commit_msg>go.talks/2012/simple: fix bug on test code.<commit_after>// +build OMIT

package main

import "strings"

import "testing"

func TestToUpper(t *testing.T) {
	in := "loud noises"
	want := "LOUD NOISES"
	got := strings.ToUpper(in)
	if got != want {
		t.Errorf("ToUpper(%v) = %v, want %v", in, got, want)
	}
}

func TestContains(t *testing.T) {
	var tests = []struct {
		str, substr string
		expected    bool
	}{
		{"abc", "bc", true},
		{"abc", "bcd", false},
		{"abc", "", true},
		{"", "a", false},
	}
	for _, ct := range tests {
		if strings.Contains(ct.str, ct.substr) != ct.expected {
			t.Errorf("Contains(%s, %s) = %v, want %v",
				ct.str, ct.substr, !ct.expected, ct.expected)
		}
	}
}
<|endoftext|>


<commit_before>package class

func createVtable(class *Class) {
	superVtable := getSuperVtable(class)
	newVirtualMethodCount := countNewVirtualMethod(class)

	newCap := len(superVtable) + newVirtualMethodCount
	newVtable := make([]*Method, len(superVtable), newCap)
	copy(newVtable, superVtable)

	for _, m := range class.methods {
		if i := search(superVtable, m); i > -1 {
			newVtable[i] = m // override
		} else {
			newVtable = append(newVtable, m)
		}
	}
}

func getSuperVtable(class *Class) []*Method {
	if class.superClass != nil {
		return class.superClass.vtable
	} else {
		return nil
	}
}

func countNewVirtualMethod(class *Class) int {
	superVtable := getSuperVtable(class)

	count := 0
	for _, m := range class.methods {
		if isVirtualMethod(m) && search(superVtable, m) < 0 {
			count++
		}
	}
	return count
}

func isVirtualMethod(method *Method) bool {
	return !method.IsStatic() &&
		!method.IsFinal() &&
		!method.IsPrivate() &&
		method.Name() != constructorName
}

func search(vtable []*Method, m *Method) int {
	for i, vm := range vtable {
		if vm.name == m.name && vm.descriptor == m.descriptor {
			return i
		}
	}
	return -1
}
<commit_msg>vtable...<commit_after>package class

func getVslot(class *Class, name, descriptor string) int {
	for i, m := range class.vtable {
		if m.name == name && m.descriptor == descriptor {
			return i
		}
	}
	// todo
	return -1
}

func createVtable(class *Class) {
	superVtable := getSuperVtable(class)
	newVirtualMethodCount := countNewVirtualMethod(class)

	newCap := len(superVtable) + newVirtualMethodCount
	newVtable := make([]*Method, len(superVtable), newCap)
	copy(newVtable, superVtable)

	for _, m := range class.methods {
		if i := search(superVtable, m); i > -1 {
			newVtable[i] = m // override
		} else {
			newVtable = append(newVtable, m)
		}
	}

	class.vtable = newVtable
}

func getSuperVtable(class *Class) []*Method {
	if class.superClass != nil {
		return class.superClass.vtable
	} else {
		return nil
	}
}

func countNewVirtualMethod(class *Class) int {
	superVtable := getSuperVtable(class)

	count := 0
	for _, m := range class.methods {
		if isVirtualMethod(m) && search(superVtable, m) < 0 {
			count++
		}
	}
	return count
}

func isVirtualMethod(method *Method) bool {
	return !method.IsStatic() &&
		!method.IsFinal() &&
		!method.IsPrivate() &&
		method.Name() != constructorName
}

func search(vtable []*Method, m *Method) int {
	for i, vm := range vtable {
		if vm.name == m.name && vm.descriptor == m.descriptor {
			return i
		}
	}
	return -1
}
<|endoftext|>


<commit_before>package git

import (
	"bufio"
	"compress/flate"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"

	"encoding/binary"
)

type byteCounter struct {
	io.Reader
	n int64
}

func (r *byteCounter) Read(buf []byte) (int, error) {
	n, err := r.Reader.Read(buf)
	r.n += int64(n)
	return n, err
}

type flateCounter struct {
	flate.Reader
	n int64
}

func (r *flateCounter) Read(buf []byte) (int, error) {
	n, err := r.Reader.Read(buf)
	r.n += int64(n)
	return n, err
}

func (r *flateCounter) ReadByte() (byte, error) {
	r.n += 1
	return r.Reader.ReadByte()
}

type packIterator func(r io.ReaderAt, i, n int, loc int64, compsz int64, t PackEntryType, osz PackEntrySize, deltaref Sha1, deltaoffset ObjectOffset, rawdata []byte) error

func iteratePack(c *Client, r io.Reader, initcallback func(int), callback packIterator, trailerCB func(packtrailer Sha1) error) (*os.File, error) {
	// if the reader is not a file, tee it into a temp file to resolve
	// deltas from.
	var pack *os.File

	counter := &byteCounter{r, 0}
	if f, ok := r.(*os.File); ok && f != os.Stdin {
		pack = f
		r = counter
	} else {
		var err error
		pack, err = ioutil.TempFile(c.GitDir.File("objects/pack").String(), ".tmppackfile")
		if err != nil {
			return nil, err
		}
		// Do not defer pack.Close, it's the caller's responsibility to close it.

		// Only tee into the pack file if it's not a file
		r = io.TeeReader(counter, pack)
	}

	var p PackfileHeader
	if err := binary.Read(r, binary.BigEndian, &p); err != nil {
		return nil, err
	}

	if p.Signature != [4]byte{'P', 'A', 'C', 'K'} {
		return nil, fmt.Errorf("Invalid packfile: %+v", p.Signature)
	}
	if p.Version != 2 {
		return nil, fmt.Errorf("Unsupported packfile version: %d", p.Version)
	}

	loc := counter.n
	br := bufio.NewReader(r)
	initcallback(int(p.Size))

	var wg sync.WaitGroup
	for i := uint32(0); i < p.Size; i += 1 {
		wg.Add(1)
		t, sz, deltasha, deltaoff, rawheader := p.ReadHeaderSize(br)

		datacounter := flateCounter{br, 0}
		raw := p.readEntryDataStream1(&datacounter)

		compsize := int64(len(rawheader)) + int64(datacounter.n)

		go func(i int, psize int, loc int64, compsize int64, t PackEntryType, sz PackEntrySize, deltasha Sha1, deltaoff ObjectOffset, raw []byte) {
			defer wg.Done()
			if err := callback(pack, i, psize, loc, compsize, t, sz, deltasha, deltaoff, raw); err != nil {
				panic(err)
			}
		}(int(i), int(p.Size), loc, compsize, t, sz, deltasha, deltaoff, raw)

		loc += compsize
	}

	wg.Wait()
	// We need to read the packfile trailer so that it gets tee'd into
	// the temp file, or it won't be there for index-pack.
	var trailer PackfileIndexV2
	if err := binary.Read(br, binary.BigEndian, &trailer.Packfile); err != nil {
		return nil, err
	}
	trailerCB(trailer.Packfile)

	return pack, nil
}
<commit_msg>Remove parallel indexing of packfiles (#265)<commit_after>package git

import (
	"bufio"
	"compress/flate"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"sync"

	"encoding/binary"
)

type byteCounter struct {
	io.Reader
	n int64
}

func (r *byteCounter) Read(buf []byte) (int, error) {
	n, err := r.Reader.Read(buf)
	r.n += int64(n)
	return n, err
}

type flateCounter struct {
	flate.Reader
	n int64
}

func (r *flateCounter) Read(buf []byte) (int, error) {
	n, err := r.Reader.Read(buf)
	r.n += int64(n)
	return n, err
}

func (r *flateCounter) ReadByte() (byte, error) {
	r.n += 1
	return r.Reader.ReadByte()
}

type packIterator func(r io.ReaderAt, i, n int, loc int64, compsz int64, t PackEntryType, osz PackEntrySize, deltaref Sha1, deltaoffset ObjectOffset, rawdata []byte) error

func iteratePack(c *Client, r io.Reader, initcallback func(int), callback packIterator, trailerCB func(packtrailer Sha1) error) (*os.File, error) {
	// if the reader is not a file, tee it into a temp file to resolve
	// deltas from.
	var pack *os.File

	counter := &byteCounter{r, 0}
	if f, ok := r.(*os.File); ok && f != os.Stdin {
		pack = f
		r = counter
	} else {
		var err error
		pack, err = ioutil.TempFile(c.GitDir.File("objects/pack").String(), ".tmppackfile")
		if err != nil {
			return nil, err
		}
		// Do not defer pack.Close, it's the caller's responsibility to close it.

		// Only tee into the pack file if it's not a file
		r = io.TeeReader(counter, pack)
	}

	var p PackfileHeader
	if err := binary.Read(r, binary.BigEndian, &p); err != nil {
		return nil, err
	}

	if p.Signature != [4]byte{'P', 'A', 'C', 'K'} {
		return nil, fmt.Errorf("Invalid packfile: %+v", p.Signature)
	}
	if p.Version != 2 {
		return nil, fmt.Errorf("Unsupported packfile version: %d", p.Version)
	}

	loc := counter.n
	br := bufio.NewReader(r)
	initcallback(int(p.Size))

	var wg sync.WaitGroup
	for i := uint32(0); i < p.Size; i += 1 {
		wg.Add(1)
		t, sz, deltasha, deltaoff, rawheader := p.ReadHeaderSize(br)

		datacounter := flateCounter{br, 0}
		raw := p.readEntryDataStream1(&datacounter)

		compsize := int64(len(rawheader)) + int64(datacounter.n)

		// Doing this in goroutines seems to crash 9front and provides very
		// little performance gain, so for now only do 1 a time.
		func(i int, psize int, loc int64, compsize int64, t PackEntryType, sz PackEntrySize, deltasha Sha1, deltaoff ObjectOffset, raw []byte) {
			//go func(i int, psize int, loc int64, compsize int64, t PackEntryType, sz PackEntrySize, deltasha Sha1, deltaoff ObjectOffset, raw []byte) {
			defer wg.Done()
			if err := callback(pack, i, psize, loc, compsize, t, sz, deltasha, deltaoff, raw); err != nil {
				panic(err)
			}
		}(int(i), int(p.Size), loc, compsize, t, sz, deltasha, deltaoff, raw)

		loc += compsize
	}

	wg.Wait()
	// We need to read the packfile trailer so that it gets tee'd into
	// the temp file, or it won't be there for index-pack.
	var trailer PackfileIndexV2
	if err := binary.Read(br, binary.BigEndian, &trailer.Packfile); err != nil {
		return nil, err
	}
	trailerCB(trailer.Packfile)

	return pack, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitea

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// CreateUserOption create user options
type CreateUserOption struct {
	SourceID  int64  `json:"source_id"`
	LoginName string `json:"login_name"`
	// required: true
	Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(40)"`
	FullName string `json:"full_name" binding:"MaxSize(100)"`
	// required: true
	// swagger:strfmt email
	Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
	// required: true
	Password           string `json:"password" binding:"Required;MaxSize(255)"`
	MustChangePassword bool   `json:"must_change_password"`
	SendNotify         bool   `json:"send_notify"`
}

// AdminCreateUser create a user
func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	user := new(User)
	return user, c.getParsedResponse("POST", "/admin/users", jsonHeader, bytes.NewReader(body), user)
}

// EditUserOption edit user options
type EditUserOption struct {
	SourceID  int64  `json:"source_id"`
	LoginName string `json:"login_name"`
	FullName  string `json:"full_name" binding:"MaxSize(100)"`
	// required: true
	// swagger:strfmt email
	Email                   string `json:"email" binding:"Required;Email;MaxSize(254)"`
	Password                string `json:"password" binding:"MaxSize(255)"`
	MustChangePassword      bool   `json:"must_change_password"`
	Website                 string `json:"website" binding:"MaxSize(50)"`
	Location                string `json:"location" binding:"MaxSize(50)"`
	Active                  *bool  `json:"active"`
	Admin                   *bool  `json:"admin"`
	AllowGitHook            *bool  `json:"allow_git_hook"`
	AllowImportLocal        *bool  `json:"allow_import_local"`
	MaxRepoCreation         *int   `json:"max_repo_creation"`
	ProhibitLogin           *bool  `json:"prohibit_login"`
	AllowCreateOrganization *bool  `json:"allow_create_organization"`
}

// AdminEditUser modify user informations
func (c *Client) AdminEditUser(user string, opt EditUserOption) error {
	body, err := json.Marshal(&opt)
	if err != nil {
		return err
	}
	_, err = c.getResponse("PATCH", fmt.Sprintf("/admin/users/%s", user), jsonHeader, bytes.NewReader(body))
	return err
}

// AdminDeleteUser delete one user according name
func (c *Client) AdminDeleteUser(user string) error {
	_, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil)
	return err
}

// AdminCreateUserPublicKey create one user with options
func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	key := new(PublicKey)
	return key, c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/keys", user), jsonHeader, bytes.NewReader(body), key)
}
<commit_msg>MustChangePassword must be nillable (#150)<commit_after>// Copyright 2015 The Gogs Authors. All rights reserved.
// Copyright 2019 The Gitea Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package gitea

import (
	"bytes"
	"encoding/json"
	"fmt"
)

// CreateUserOption create user options
type CreateUserOption struct {
	SourceID  int64  `json:"source_id"`
	LoginName string `json:"login_name"`
	// required: true
	Username string `json:"username" binding:"Required;AlphaDashDot;MaxSize(40)"`
	FullName string `json:"full_name" binding:"MaxSize(100)"`
	// required: true
	// swagger:strfmt email
	Email string `json:"email" binding:"Required;Email;MaxSize(254)"`
	// required: true
	Password           string `json:"password" binding:"Required;MaxSize(255)"`
	MustChangePassword *bool  `json:"must_change_password"`
	SendNotify         bool   `json:"send_notify"`
}

// AdminCreateUser create a user
func (c *Client) AdminCreateUser(opt CreateUserOption) (*User, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	user := new(User)
	return user, c.getParsedResponse("POST", "/admin/users", jsonHeader, bytes.NewReader(body), user)
}

// EditUserOption edit user options
type EditUserOption struct {
	SourceID  int64  `json:"source_id"`
	LoginName string `json:"login_name"`
	FullName  string `json:"full_name" binding:"MaxSize(100)"`
	// required: true
	// swagger:strfmt email
	Email                   string `json:"email" binding:"Required;Email;MaxSize(254)"`
	Password                string `json:"password" binding:"MaxSize(255)"`
	MustChangePassword      *bool  `json:"must_change_password"`
	Website                 string `json:"website" binding:"MaxSize(50)"`
	Location                string `json:"location" binding:"MaxSize(50)"`
	Active                  *bool  `json:"active"`
	Admin                   *bool  `json:"admin"`
	AllowGitHook            *bool  `json:"allow_git_hook"`
	AllowImportLocal        *bool  `json:"allow_import_local"`
	MaxRepoCreation         *int   `json:"max_repo_creation"`
	ProhibitLogin           *bool  `json:"prohibit_login"`
	AllowCreateOrganization *bool  `json:"allow_create_organization"`
}

// AdminEditUser modify user informations
func (c *Client) AdminEditUser(user string, opt EditUserOption) error {
	body, err := json.Marshal(&opt)
	if err != nil {
		return err
	}
	_, err = c.getResponse("PATCH", fmt.Sprintf("/admin/users/%s", user), jsonHeader, bytes.NewReader(body))
	return err
}

// AdminDeleteUser delete one user according name
func (c *Client) AdminDeleteUser(user string) error {
	_, err := c.getResponse("DELETE", fmt.Sprintf("/admin/users/%s", user), nil, nil)
	return err
}

// AdminCreateUserPublicKey create one user with options
func (c *Client) AdminCreateUserPublicKey(user string, opt CreateKeyOption) (*PublicKey, error) {
	body, err := json.Marshal(&opt)
	if err != nil {
		return nil, err
	}
	key := new(PublicKey)
	return key, c.getParsedResponse("POST", fmt.Sprintf("/admin/users/%s/keys", user), jsonHeader, bytes.NewReader(body), key)
}
<|endoftext|>


<commit_before>// Copyright 2014, Hǎiliàng Wáng. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package core

// group implements the core algorithm of nested test groups.
type group struct {
	dst    Path
	cur    serialStack
	next   Serial
	done   bool
	runNew runFunc
}
type runFunc func(Path)

func newGroup(dst Path, run runFunc) *group {
	return &group{dst: dst, runNew: run}
}

// visit method calls each test group closure along the path defined by
// group.dst, ignoring the node not on path. Once a leaf node is visited, it
// stops calling the rest closures on path, but provides the path of them
// through group.runNew.
func (g *group) visit(f func(cur Path)) {
	g.cur.push(g.next)
	g.next = 0
	if len(g.dst) > len(g.cur.Path) {
		g.next = g.dst[len(g.cur.Path)]
	}

	defer func() { g.next = g.cur.pop() + 1 }()
	if !g.cur.onPath(g.dst) {
		return
	} else if g.done {
		//g.runNew(g.cur.Path)
		g.done = false
		f(g.cur.Path)
		return
	}
	defer func() { g.done = true }()
	f(g.cur.Path)
}
<commit_msg>mutil test<commit_after>// Copyright 2014, Hǎiliàng Wáng. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package core

// group implements the core algorithm of nested test groups.
type group struct {
	dst    Path
	cur    serialStack
	next   Serial
	done   bool
	runNew runFunc
}
type runFunc func(Path)

func newGroup(dst Path, run runFunc) *group {
	return &group{dst: dst, runNew: run}
}

// visit method calls each test group closure along the path defined by
// group.dst, ignoring the node not on path. Once a leaf node is visited, it
// stops calling the rest closures on path, but provides the path of them
// through group.runNew.
func (g *group) visit(f func(cur Path)) {
	if len(g.cur.Path) == 0 {
		g.next = g.dst[0]
	}

	g.cur.push(g.next)
	g.next = 0

	defer func() { g.next = g.cur.pop() + 1 }()
	if !g.cur.onPath(g.dst) {
		return
	} else if g.done {
		//g.runNew(g.cur.Path)
		g.done = false
		f(g.cur.Path)
		return
	}
	defer func() { g.done = true }()
	f(g.cur.Path)
}
<|endoftext|>


<commit_before>package checks

import (
	"context"
	"crypto/tls"
	"fmt"
	"strings"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	hv1 "google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/resolver"
)

var ErrGRPCUnhealthy = fmt.Errorf("gRPC application didn't report service healthy")

// GrpcHealthProbe connects to gRPC application and queries health service for application/service status.
type GrpcHealthProbe struct {
	server      string
	request     *hv1.HealthCheckRequest
	timeout     time.Duration
	dialOptions []grpc.DialOption
}

// NewGrpcHealthProbe constructs GrpcHealthProbe from target string in format
// server[/service]
// If service is omitted, health of the entire application is probed
func NewGrpcHealthProbe(target string, timeout time.Duration, tlsConfig *tls.Config) *GrpcHealthProbe {
	serverAndService := strings.SplitN(target, "/", 2)

	request := hv1.HealthCheckRequest{}
	if len(serverAndService) > 1 {
		request.Service = serverAndService[1]
	}

	var dialOptions = []grpc.DialOption{}

	if tlsConfig != nil {
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
	} else {
		dialOptions = append(dialOptions, grpc.WithInsecure())
	}

	return &GrpcHealthProbe{
		request:     &request,
		timeout:     timeout,
		dialOptions: dialOptions,
	}
}

// Check if the target of this GrpcHealthProbe is healthy
// If nil is returned, target is healthy, otherwise target is not healthy
func (probe *GrpcHealthProbe) Check(target string) error {
	serverAndService := strings.SplitN(target, "/", 2)
	serverWithScheme := fmt.Sprintf("%s:///%s", resolver.GetDefaultScheme(), serverAndService[0])

	ctx, cancel := context.WithTimeout(context.Background(), probe.timeout)
	defer cancel()

	connection, err := grpc.DialContext(ctx, serverWithScheme, probe.dialOptions...)
	if err != nil {
		return err
	}
	defer connection.Close()

	client := hv1.NewHealthClient(connection)
	response, err := client.Check(ctx, probe.request)
	if err != nil {
		return err
	}
	if response == nil || response.Status != hv1.HealthCheckResponse_SERVING {
		return ErrGRPCUnhealthy
	}

	return nil
}
<commit_msg>Return grpc serving status in health check errors<commit_after>package checks

import (
	"context"
	"crypto/tls"
	"fmt"
	"strings"
	"time"

	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	hv1 "google.golang.org/grpc/health/grpc_health_v1"
	"google.golang.org/grpc/resolver"
)

// GrpcHealthProbe connects to gRPC application and queries health service for application/service status.
type GrpcHealthProbe struct {
	server      string
	request     *hv1.HealthCheckRequest
	timeout     time.Duration
	dialOptions []grpc.DialOption
}

// NewGrpcHealthProbe constructs GrpcHealthProbe from target string in format
// server[/service]
// If service is omitted, health of the entire application is probed
func NewGrpcHealthProbe(target string, timeout time.Duration, tlsConfig *tls.Config) *GrpcHealthProbe {
	serverAndService := strings.SplitN(target, "/", 2)

	request := hv1.HealthCheckRequest{}
	if len(serverAndService) > 1 {
		request.Service = serverAndService[1]
	}

	var dialOptions = []grpc.DialOption{}

	if tlsConfig != nil {
		dialOptions = append(dialOptions, grpc.WithTransportCredentials(credentials.NewTLS(tlsConfig)))
	} else {
		dialOptions = append(dialOptions, grpc.WithInsecure())
	}

	return &GrpcHealthProbe{
		request:     &request,
		timeout:     timeout,
		dialOptions: dialOptions,
	}
}

// Check if the target of this GrpcHealthProbe is healthy
// If nil is returned, target is healthy, otherwise target is not healthy
func (probe *GrpcHealthProbe) Check(target string) error {
	serverAndService := strings.SplitN(target, "/", 2)
	serverWithScheme := fmt.Sprintf("%s:///%s", resolver.GetDefaultScheme(), serverAndService[0])

	ctx, cancel := context.WithTimeout(context.Background(), probe.timeout)
	defer cancel()

	connection, err := grpc.DialContext(ctx, serverWithScheme, probe.dialOptions...)
	if err != nil {
		return err
	}
	defer connection.Close()

	client := hv1.NewHealthClient(connection)
	response, err := client.Check(ctx, probe.request)
	if err != nil {
		return err
	}
	if response.Status != hv1.HealthCheckResponse_SERVING {
		return fmt.Errorf("gRPC %s serving status: %s", target, response.Status)
	}

	return nil
}
<|endoftext|>


<commit_before>package agent

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/buildkite/agent/api"
	"github.com/buildkite/agent/logger"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/googleapi"
	storage "google.golang.org/api/storage/v1"
)

type GSUploaderConfig struct {
	// The destination which includes the GS bucket name and the path.
	// gs://my-bucket-name/foo/bar
	Destination string

	// Whether or not HTTP calls shoud be debugged
	DebugHTTP bool
}

type GSUploader struct {
	// The gs bucket path set from the destination
	BucketPath string

	// The gs bucket name set from the destination
	BucketName string

	// The configuration
	conf GSUploaderConfig

	// The logger instance to use
	logger logger.Logger

	// The GS service
	service *storage.Service
}

func NewGSUploader(l logger.Logger, c GSUploaderConfig) (*GSUploader, error) {
	client, err := newGoogleClient(storage.DevstorageFullControlScope)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error creating Google Cloud Storage client: %v", err))
	}
	service, err := storage.New(client)
	if err != nil {
		return nil, err
	}
	bucketName, bucketPath := ParseGSDestination(c.Destination)
	return &GSUploader{
		BucketPath: bucketPath,
		BucketName: bucketName,
		conf:       c,
		logger:     l,
		service:    service,
	}, nil
}

func ParseGSDestination(destination string) (name string, path string) {
	parts := strings.Split(strings.TrimPrefix(string(destination), "gs://"), "/")
	path = strings.Join(parts[1:len(parts)], "/")
	name = parts[0]
	return
}

func newGoogleClient(scope string) (*http.Client, error) {
	if os.Getenv("BUILDKITE_GS_APPLICATION_CREDENTIALS") != "" {
		data, err := ioutil.ReadFile(os.Getenv("BUILDKITE_GS_APPLICATION_CREDENTIALS"))
		if err != nil {
			return nil, err
		}
		conf, err := google.JWTConfigFromJSON(data, scope)
		if err != nil {
			return nil, err
		}
		return conf.Client(oauth2.NoContext), nil
	}
	return google.DefaultClient(context.Background(), scope)
}

func (u *GSUploader) URL(artifact *api.Artifact) string {
	host := "storage.googleapis.com"
	path := fmt.Sprintf("%s/%s", u.BucketName, u.artifactPath(artifact))

	// Override the default host if required.
	if envHost, ok := os.LookupEnv("BUILDKITE_GCS_ACCESS_HOST"); ok {
		host = envHost
	}

	// If this is set, we trust the user to supply a valid path prefix, and don't use a default GCS path
	if prefix, ok := os.LookupEnv("BUILDKITE_GCS_PATH_PREFIX"); ok {
		path = fmt.Sprintf("%s%s", prefix, u.artifactPath(artifact))
	}

	var artifactURL = &url.URL{
		Scheme: "https",
		Host:   host,
		Path:   path,
	}
	return artifactURL.String()
}

func (u *GSUploader) Upload(artifact *api.Artifact) error {
	permission := os.Getenv("BUILDKITE_GS_ACL")

	// The dirtiest validation method ever...
	if permission != "" &&
		permission != "authenticatedRead" &&
		permission != "private" &&
		permission != "projectPrivate" &&
		permission != "publicRead" &&
		permission != "publicReadWrite" {
		return fmt.Errorf("Invalid GS ACL `%s`", permission)
	}

	if permission == "" {
		u.logger.Debug("Uploading \"%s\" to bucket \"%s\" with default permission",
			u.artifactPath(artifact), u.BucketName)
	} else {
		u.logger.Debug("Uploading \"%s\" to bucket \"%s\" with permission \"%s\"",
			u.artifactPath(artifact), u.BucketName, permission)
	}
	object := &storage.Object{
		Name:               u.artifactPath(artifact),
		ContentType:        artifact.ContentType,
		ContentDisposition: u.contentDisposition(artifact),
	}
	file, err := os.Open(artifact.AbsolutePath)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to open file \"%q\" (%v)", artifact.AbsolutePath, err))
	}
	call := u.service.Objects.Insert(u.BucketName, object)
	if permission != "" {
		call = call.PredefinedAcl(permission)
	}
	if res, err := call.Media(file, googleapi.ContentType("")).Do(); err == nil {
		u.logger.Debug("Created object %v at location %v\n\n", res.Name, res.SelfLink)
	} else {
		return errors.New(fmt.Sprintf("Failed to PUT file \"%s\" (%v)", u.artifactPath(artifact), err))
	}

	return nil
}

func (u *GSUploader) artifactPath(artifact *api.Artifact) string {
	parts := []string{u.BucketPath, artifact.Path}

	return strings.Join(parts, "/")
}

func (u *GSUploader) contentDisposition(a *api.Artifact) string {
	return fmt.Sprintf("inline; filename=\"%s\"", filepath.Base(a.Path))
}
<commit_msg>Clean up path prefix and ensure it's always valid.<commit_after>package agent

import (
	"context"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"os"
	"path/filepath"
	"strings"

	"github.com/buildkite/agent/api"
	"github.com/buildkite/agent/logger"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/googleapi"
	storage "google.golang.org/api/storage/v1"
)

type GSUploaderConfig struct {
	// The destination which includes the GS bucket name and the path.
	// gs://my-bucket-name/foo/bar
	Destination string

	// Whether or not HTTP calls shoud be debugged
	DebugHTTP bool
}

type GSUploader struct {
	// The gs bucket path set from the destination
	BucketPath string

	// The gs bucket name set from the destination
	BucketName string

	// The configuration
	conf GSUploaderConfig

	// The logger instance to use
	logger logger.Logger

	// The GS service
	service *storage.Service
}

func NewGSUploader(l logger.Logger, c GSUploaderConfig) (*GSUploader, error) {
	client, err := newGoogleClient(storage.DevstorageFullControlScope)
	if err != nil {
		return nil, errors.New(fmt.Sprintf("Error creating Google Cloud Storage client: %v", err))
	}
	service, err := storage.New(client)
	if err != nil {
		return nil, err
	}
	bucketName, bucketPath := ParseGSDestination(c.Destination)
	return &GSUploader{
		BucketPath: bucketPath,
		BucketName: bucketName,
		conf:       c,
		logger:     l,
		service:    service,
	}, nil
}

func ParseGSDestination(destination string) (name string, path string) {
	parts := strings.Split(strings.TrimPrefix(string(destination), "gs://"), "/")
	path = strings.Join(parts[1:len(parts)], "/")
	name = parts[0]
	return
}

func newGoogleClient(scope string) (*http.Client, error) {
	if os.Getenv("BUILDKITE_GS_APPLICATION_CREDENTIALS") != "" {
		data, err := ioutil.ReadFile(os.Getenv("BUILDKITE_GS_APPLICATION_CREDENTIALS"))
		if err != nil {
			return nil, err
		}
		conf, err := google.JWTConfigFromJSON(data, scope)
		if err != nil {
			return nil, err
		}
		return conf.Client(oauth2.NoContext), nil
	}
	return google.DefaultClient(context.Background(), scope)
}

func (u *GSUploader) URL(artifact *api.Artifact) string {
	// Set host and pathPrefix to default values
	host := "storage.googleapis.com"
	pathPrefix := u.BucketName

	// Override default host if required.
	if envHost, ok := os.LookupEnv("BUILDKITE_GCS_ACCESS_HOST"); ok {
		host = envHost
	}

	// If this is set, we trust the user to supply a valid path prefix, instead of using the default GCS path
	if prefix, ok := os.LookupEnv("BUILDKITE_GCS_PATH_PREFIX"); ok {
		pathPrefix = prefix
	}

	// Build the path from the prefix and the artifactPath
	// Also ensure that we always have exactly one / between prefix and artifactPath
	path := fmt.Sprintf("%s/%s", strings.TrimSuffix(pathPrefix, "/"), u.artifactPath(artifact))

	var artifactURL = &url.URL{
		Scheme: "https",
		Host:   host,
		Path:   path,
	}
	return artifactURL.String()
}

func (u *GSUploader) Upload(artifact *api.Artifact) error {
	permission := os.Getenv("BUILDKITE_GS_ACL")

	// The dirtiest validation method ever...
	if permission != "" &&
		permission != "authenticatedRead" &&
		permission != "private" &&
		permission != "projectPrivate" &&
		permission != "publicRead" &&
		permission != "publicReadWrite" {
		return fmt.Errorf("Invalid GS ACL `%s`", permission)
	}

	if permission == "" {
		u.logger.Debug("Uploading \"%s\" to bucket \"%s\" with default permission",
			u.artifactPath(artifact), u.BucketName)
	} else {
		u.logger.Debug("Uploading \"%s\" to bucket \"%s\" with permission \"%s\"",
			u.artifactPath(artifact), u.BucketName, permission)
	}
	object := &storage.Object{
		Name:               u.artifactPath(artifact),
		ContentType:        artifact.ContentType,
		ContentDisposition: u.contentDisposition(artifact),
	}
	file, err := os.Open(artifact.AbsolutePath)
	if err != nil {
		return errors.New(fmt.Sprintf("Failed to open file \"%q\" (%v)", artifact.AbsolutePath, err))
	}
	call := u.service.Objects.Insert(u.BucketName, object)
	if permission != "" {
		call = call.PredefinedAcl(permission)
	}
	if res, err := call.Media(file, googleapi.ContentType("")).Do(); err == nil {
		u.logger.Debug("Created object %v at location %v\n\n", res.Name, res.SelfLink)
	} else {
		return errors.New(fmt.Sprintf("Failed to PUT file \"%s\" (%v)", u.artifactPath(artifact), err))
	}

	return nil
}

func (u *GSUploader) artifactPath(artifact *api.Artifact) string {
	parts := []string{u.BucketPath, artifact.Path}

	return strings.Join(parts, "/")
}

func (u *GSUploader) contentDisposition(a *api.Artifact) string {
	return fmt.Sprintf("inline; filename=\"%s\"", filepath.Base(a.Path))
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Pani Networks
// 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 main

import (
	"context"
	"flag"
	"os"
	"strings"
	"time"

	"github.com/romana/core/agent/enforcer"
	utilexec "github.com/romana/core/agent/exec"
	"github.com/romana/core/agent/internal/cache/policycache"
	"github.com/romana/core/agent/internal/controllers/policycontroller"
	"github.com/romana/core/agent/simple/internal/rtable"
	"github.com/romana/core/agent/simple/internal/sysctl"
	"github.com/romana/core/common"
	"github.com/romana/core/common/api"
	"github.com/romana/core/common/client"
	log "github.com/romana/rlog"

	"github.com/vishvananda/netlink"
)

const (
	DefaultRouteTableId = 10
	DefaultGwIP         = "172.142.0.1"
)

var (
	kernelParameter = []string{
		"/proc/sys/net/ipv4/conf/default/proxy_arp",
		"/proc/sys/net/ipv4/conf/all/proxy_arp",
		"/proc/sys/net/ipv4/ip_forward",
	}
)

func main() {
	var err error

	etcdEndpoints := flag.String("endpoints", "", "csv list of etcd endpoints to romana storage")
	etcdPrefix := flag.String("prefix", "", "string that prefixes all romana keys in etcd")
	hostname := flag.String("hostname", "", "name of the host in romana database")
	provisionIface := flag.Bool("provision-iface", false, "create romana-gw interface and ip")
	provisionIfaceGwIp := flag.String("provision-iface-gw-ip", DefaultGwIP, "specifies ip address for gateway interface")
	provisionSysctls := flag.Bool("provision-sysctls", false, "configure routing sysctls")
	romanaRouteTableId := flag.Int("route-table-id", DefaultRouteTableId,
		"id that romana route table should have in /etc/iproute2/rt_tables")
	multihop := flag.Bool("multihop-blocks", false, "allows multihop blocks")
	policyEnforcer := flag.Bool("policy", false, "enable romana policies")
	flag.Parse()

	romanaConfig := common.Config{
		EtcdEndpoints: strings.Split(*etcdEndpoints, ","),
		EtcdPrefix:    *etcdPrefix,
	}

	if *hostname == "" {
		*hostname, err = os.Hostname()
		if err != nil {
			panic(err)
		}
	}

	romanaClient, err := client.NewClient(&romanaConfig)
	if err != nil {
		log.Errorf("Failed to initialize romana client: %v", err)
		os.Exit(2)
	}

	if *provisionIface {
		err := CreateRomanaGW()
		if err != nil {
			log.Errorf("Failed to create romana-gw interface. %s", err)
			os.Exit(2)
		}

		err = SetRomanaGwIP(*provisionIfaceGwIp)
		if err != nil {
			log.Errorf("Failed to install ip address on romana-gw interface. %s", err)
			os.Exit(2)
		}

	}

	if *provisionSysctls {
		err := setSysctls()
		if err != nil {
			log.Errorf("Failed to set sysctls %s", err)
			os.Exit(2)
		}
	}

	ok, err := checkSysctls()
	if err != nil {
		log.Errorf("Failed verify the state of essential systls %s", err)
		os.Exit(2)
	}
	if !ok {
		log.Errorf("Essential sysctls not set, consider using -provision-sysctls flag and ensure you are running from root")
		os.Exit(2)
	}

	err = rtable.EnsureRouteTableExist(*romanaRouteTableId)
	if err != nil {
		log.Errorf("Failed to make `romana` alias for route table=%d, %s. Unable to continue", *romanaRouteTableId, err)
		os.Exit(2)
	}

	nlHandle, err := netlink.NewHandle()
	if err != nil {
		log.Errorf("Failed to create netlink handle %s", err)
		os.Exit(2)
	}
	defer nlHandle.Delete()

	err = rtable.EnsureRomanaRouteRule(*romanaRouteTableId, nlHandle)
	if err != nil {
		log.Errorf("Failed to install route rule for romana routing table, %s", err)
		os.Exit(2)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	blocksChannel, err := romanaClient.WatchBlocks(ctx.Done())
	if err != nil {
		log.Errorf("Failed to subscribe to Romana blocks updates, %s", err)
		os.Exit(2)
	}

	if *policyEnforcer {
		ctx := context.Background()
		policyCache := policycache.New()
		var policyEtdKey = "/romana/policies"
		policies, err := policycontroller.Run(ctx, policyEtdKey, romanaClient, policyCache)
		if err != nil {
			log.Errorf("Failed to start policy controller, %s", err)
			os.Exit(2)
		}

		blocksList := romanaClient.IPAM.ListAllBlocks()

		// blocks are needed in both, route agent and policy agent
		// this duplicates blocks channel into the 2 new channels, one
		// used here for policies and another one passed down for routes.
		var extraBlocksChannel <-chan api.IPAMBlocksResponse
		blocksChannel, extraBlocksChannel = fanOut(ctx, blocksChannel)

		enforcer, err := enforcer.New(policyCache, policies, *blocksList, extraBlocksChannel, *hostname, new(utilexec.DefaultExecutor), 10)
		if err != nil {
			log.Errorf("Failed to create policy enforcer, %s", err)
			os.Exit(2)
		}

		enforcer.Run(ctx)

	}

	hostsChannel, err := romanaClient.WatchHosts(ctx.Done())
	if err != nil {
		log.Errorf("Failed to start watching for blocks, %s", err)
		os.Exit(2)
	}

	hosts := IpamHosts{}
	for {
		select {
		case blocks := <-blocksChannel:
			startTime := time.Now()
			err := rtable.FlushRomanaTable()
			if err != nil {
				log.Errorf("failed to flush romana route table err=(%s)", err)
				continue
			}

			createRouteToBlocks(blocks.Blocks, hosts, *romanaRouteTableId, *hostname, *multihop, nlHandle)
			runTime := time.Now().Sub(startTime)
			log.Tracef(4, "Time between route table flush and route table rebuild %s", runTime)

		case newHosts := <-hostsChannel:
			// TODO need mutex for this.
			hosts = IpamHosts(newHosts.Hosts)
		}
	}
}

// fanOut duplicates data from one channel into 2 identical channels.
func fanOut(ctx context.Context, in <-chan api.IPAMBlocksResponse) (<-chan api.IPAMBlocksResponse, <-chan api.IPAMBlocksResponse) {
	out1 := make(chan api.IPAMBlocksResponse, 1)
	out2 := make(chan api.IPAMBlocksResponse, 1)

	go func() {
		for {
			select {
			case b := <-in:
				log.Trace(5, "Blocks fan out tick")
				out1 <- b
				out2 <- b
			case <-ctx.Done():
				close(out1)
				close(out2)
				return
			}

		}

	}()

	return out1, out2
}

// IpamHosts is a collection of hosts with Get method.
type IpamHosts []api.Host

func (hosts IpamHosts) GetHost(hostname string) *api.Host {
	for hid, h := range hosts {
		if h.Name == hostname {
			return &hosts[hid]
		}
	}
	return nil
}

// checkSysctls checks that esseantial sysctl options are set.
func checkSysctls() (ok bool, err error) {
	for _, path := range kernelParameter {
		ok, err = sysctl.Check(path)
		if !ok || err != nil {
			break
		}
	}

	return ok, err
}

// setSysctls sets essential sysctl options.
func setSysctls() (err error) {
	for _, path := range kernelParameter {
		err = sysctl.Set(path)
		if err != nil {
			break
		}
	}
	return err
}
<commit_msg>Etd -> Etcd<commit_after>// Copyright (c) 2017 Pani Networks
// 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 main

import (
	"context"
	"flag"
	"os"
	"strings"
	"time"

	"github.com/romana/core/agent/enforcer"
	utilexec "github.com/romana/core/agent/exec"
	"github.com/romana/core/agent/internal/cache/policycache"
	"github.com/romana/core/agent/internal/controllers/policycontroller"
	"github.com/romana/core/agent/simple/internal/rtable"
	"github.com/romana/core/agent/simple/internal/sysctl"
	"github.com/romana/core/common"
	"github.com/romana/core/common/api"
	"github.com/romana/core/common/client"
	log "github.com/romana/rlog"

	"github.com/vishvananda/netlink"
)

const (
	DefaultRouteTableId = 10
	DefaultGwIP         = "172.142.0.1"
)

var (
	kernelParameter = []string{
		"/proc/sys/net/ipv4/conf/default/proxy_arp",
		"/proc/sys/net/ipv4/conf/all/proxy_arp",
		"/proc/sys/net/ipv4/ip_forward",
	}
)

func main() {
	var err error

	etcdEndpoints := flag.String("endpoints", "", "csv list of etcd endpoints to romana storage")
	etcdPrefix := flag.String("prefix", "", "string that prefixes all romana keys in etcd")
	hostname := flag.String("hostname", "", "name of the host in romana database")
	provisionIface := flag.Bool("provision-iface", false, "create romana-gw interface and ip")
	provisionIfaceGwIp := flag.String("provision-iface-gw-ip", DefaultGwIP, "specifies ip address for gateway interface")
	provisionSysctls := flag.Bool("provision-sysctls", false, "configure routing sysctls")
	romanaRouteTableId := flag.Int("route-table-id", DefaultRouteTableId,
		"id that romana route table should have in /etc/iproute2/rt_tables")
	multihop := flag.Bool("multihop-blocks", false, "allows multihop blocks")
	policyEnforcer := flag.Bool("policy", false, "enable romana policies")
	flag.Parse()

	romanaConfig := common.Config{
		EtcdEndpoints: strings.Split(*etcdEndpoints, ","),
		EtcdPrefix:    *etcdPrefix,
	}

	if *hostname == "" {
		*hostname, err = os.Hostname()
		if err != nil {
			panic(err)
		}
	}

	romanaClient, err := client.NewClient(&romanaConfig)
	if err != nil {
		log.Errorf("Failed to initialize romana client: %v", err)
		os.Exit(2)
	}

	if *provisionIface {
		err := CreateRomanaGW()
		if err != nil {
			log.Errorf("Failed to create romana-gw interface. %s", err)
			os.Exit(2)
		}

		err = SetRomanaGwIP(*provisionIfaceGwIp)
		if err != nil {
			log.Errorf("Failed to install ip address on romana-gw interface. %s", err)
			os.Exit(2)
		}

	}

	if *provisionSysctls {
		err := setSysctls()
		if err != nil {
			log.Errorf("Failed to set sysctls %s", err)
			os.Exit(2)
		}
	}

	ok, err := checkSysctls()
	if err != nil {
		log.Errorf("Failed verify the state of essential systls %s", err)
		os.Exit(2)
	}
	if !ok {
		log.Errorf("Essential sysctls not set, consider using -provision-sysctls flag and ensure you are running from root")
		os.Exit(2)
	}

	err = rtable.EnsureRouteTableExist(*romanaRouteTableId)
	if err != nil {
		log.Errorf("Failed to make `romana` alias for route table=%d, %s. Unable to continue", *romanaRouteTableId, err)
		os.Exit(2)
	}

	nlHandle, err := netlink.NewHandle()
	if err != nil {
		log.Errorf("Failed to create netlink handle %s", err)
		os.Exit(2)
	}
	defer nlHandle.Delete()

	err = rtable.EnsureRomanaRouteRule(*romanaRouteTableId, nlHandle)
	if err != nil {
		log.Errorf("Failed to install route rule for romana routing table, %s", err)
		os.Exit(2)
	}

	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	blocksChannel, err := romanaClient.WatchBlocks(ctx.Done())
	if err != nil {
		log.Errorf("Failed to subscribe to Romana blocks updates, %s", err)
		os.Exit(2)
	}

	if *policyEnforcer {
		ctx := context.Background()
		policyCache := policycache.New()
		var policyEtcdKey = "/romana/policies"
		policies, err := policycontroller.Run(ctx, policyEtcdKey, romanaClient, policyCache)
		if err != nil {
			log.Errorf("Failed to start policy controller, %s", err)
			os.Exit(2)
		}

		blocksList := romanaClient.IPAM.ListAllBlocks()

		// blocks are needed in both, route agent and policy agent
		// this duplicates blocks channel into the 2 new channels, one
		// used here for policies and another one passed down for routes.
		var extraBlocksChannel <-chan api.IPAMBlocksResponse
		blocksChannel, extraBlocksChannel = fanOut(ctx, blocksChannel)

		enforcer, err := enforcer.New(policyCache, policies, *blocksList, extraBlocksChannel, *hostname, new(utilexec.DefaultExecutor), 10)
		if err != nil {
			log.Errorf("Failed to create policy enforcer, %s", err)
			os.Exit(2)
		}

		enforcer.Run(ctx)

	}

	hostsChannel, err := romanaClient.WatchHosts(ctx.Done())
	if err != nil {
		log.Errorf("Failed to start watching for blocks, %s", err)
		os.Exit(2)
	}

	hosts := IpamHosts{}
	for {
		select {
		case blocks := <-blocksChannel:
			startTime := time.Now()
			err := rtable.FlushRomanaTable()
			if err != nil {
				log.Errorf("failed to flush romana route table err=(%s)", err)
				continue
			}

			createRouteToBlocks(blocks.Blocks, hosts, *romanaRouteTableId, *hostname, *multihop, nlHandle)
			runTime := time.Now().Sub(startTime)
			log.Tracef(4, "Time between route table flush and route table rebuild %s", runTime)

		case newHosts := <-hostsChannel:
			// TODO need mutex for this.
			hosts = IpamHosts(newHosts.Hosts)
		}
	}
}

// fanOut duplicates data from one channel into 2 identical channels.
func fanOut(ctx context.Context, in <-chan api.IPAMBlocksResponse) (<-chan api.IPAMBlocksResponse, <-chan api.IPAMBlocksResponse) {
	out1 := make(chan api.IPAMBlocksResponse, 1)
	out2 := make(chan api.IPAMBlocksResponse, 1)

	go func() {
		for {
			select {
			case b := <-in:
				log.Trace(5, "Blocks fan out tick")
				out1 <- b
				out2 <- b
			case <-ctx.Done():
				close(out1)
				close(out2)
				return
			}

		}

	}()

	return out1, out2
}

// IpamHosts is a collection of hosts with Get method.
type IpamHosts []api.Host

func (hosts IpamHosts) GetHost(hostname string) *api.Host {
	for hid, h := range hosts {
		if h.Name == hostname {
			return &hosts[hid]
		}
	}
	return nil
}

// checkSysctls checks that esseantial sysctl options are set.
func checkSysctls() (ok bool, err error) {
	for _, path := range kernelParameter {
		ok, err = sysctl.Check(path)
		if !ok || err != nil {
			break
		}
	}

	return ok, err
}

// setSysctls sets essential sysctl options.
func setSysctls() (err error) {
	for _, path := range kernelParameter {
		err = sysctl.Set(path)
		if err != nil {
			break
		}
	}
	return err
}
<|endoftext|>


<commit_before>// Copyright 2014-2016 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 stats

import (
	"math"
	"regexp"
	"runtime"
	"time"

	"github.com/cihub/seelog"
	"github.com/docker/docker/api/types"
)

// networkStatsErrorPattern defines the pattern that is used to evaluate
// if there's an error reading network stats.
const networkStatsErrorPattern = "open /sys/class/net/veth.*: no such file or directory"

var numCores = uint64(runtime.NumCPU())

// nan32 returns a 32bit NaN.
func nan32() float32 {
	return (float32)(math.NaN())
}

// parseNanoTime returns the time object from a string formatted with RFC3339Nano layout.
func parseNanoTime(value string) time.Time {
	ts, _ := time.Parse(time.RFC3339Nano, value)
	return ts
}

// isNetworkStatsError returns if the error indicates that files in /sys/class/net
// could not be opened.
func isNetworkStatsError(err error) bool {
	matched, mErr := regexp.MatchString(networkStatsErrorPattern, err.Error())
	if mErr != nil {
		seelog.Debugf("Error matching string: %v", mErr)
		return false
	}

	return matched
}

func getNetworkStats(dockerStats *types.StatsJSON) *NetworkStats {
	if dockerStats.Networks == nil {
		seelog.Debug("Network stats not reported for container")
		return nil
	}
	networkStats := &NetworkStats{}
	for _, netStats := range dockerStats.Networks {
		networkStats.RxBytes += netStats.RxBytes
		networkStats.RxDropped += netStats.RxDropped
		networkStats.RxErrors += netStats.RxErrors
		networkStats.RxPackets += netStats.RxPackets

		networkStats.TxBytes += netStats.TxBytes
		networkStats.TxDropped += netStats.TxDropped
		networkStats.TxErrors += netStats.TxErrors
		networkStats.TxPackets += netStats.TxPackets
	}
	return networkStats
}
<commit_msg>stats:remove unwanted debug log<commit_after>// Copyright 2014-2016 Amazon.com, Inc. or its affiliates. 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. A copy of the
// License is located at
//
//	http://aws.amazon.com/apache2.0/
//
// or in the "license" file accompanying this file. This file 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 stats

import (
	"math"
	"regexp"
	"runtime"
	"time"

	"github.com/cihub/seelog"
	"github.com/docker/docker/api/types"
)

// networkStatsErrorPattern defines the pattern that is used to evaluate
// if there's an error reading network stats.
const networkStatsErrorPattern = "open /sys/class/net/veth.*: no such file or directory"

var numCores = uint64(runtime.NumCPU())

// nan32 returns a 32bit NaN.
func nan32() float32 {
	return (float32)(math.NaN())
}

// parseNanoTime returns the time object from a string formatted with RFC3339Nano layout.
func parseNanoTime(value string) time.Time {
	ts, _ := time.Parse(time.RFC3339Nano, value)
	return ts
}

// isNetworkStatsError returns if the error indicates that files in /sys/class/net
// could not be opened.
func isNetworkStatsError(err error) bool {
	matched, mErr := regexp.MatchString(networkStatsErrorPattern, err.Error())
	if mErr != nil {
		seelog.Debugf("Error matching string: %v", mErr)
		return false
	}

	return matched
}

func getNetworkStats(dockerStats *types.StatsJSON) *NetworkStats {
	if dockerStats.Networks == nil {
		return nil
	}
	networkStats := &NetworkStats{}
	for _, netStats := range dockerStats.Networks {
		networkStats.RxBytes += netStats.RxBytes
		networkStats.RxDropped += netStats.RxDropped
		networkStats.RxErrors += netStats.RxErrors
		networkStats.RxPackets += netStats.RxPackets

		networkStats.TxBytes += netStats.TxBytes
		networkStats.TxDropped += netStats.TxDropped
		networkStats.TxErrors += netStats.TxErrors
		networkStats.TxPackets += netStats.TxPackets
	}
	return networkStats
}
<|endoftext|>