<commit_before>// Package junit defines a JUnit XML report and includes convenience methods
// for working with these reports.
package junit

import (
	"encoding/xml"
	"fmt"
	"strings"
	"time"

	"github.com/jstemmer/go-junit-report/v2/gtr"
)

// Testsuites is a collection of JUnit testsuites.
type Testsuites struct {
	XMLName xml.Name `xml:"testsuites"`

	Name     string `xml:"name,attr,omitempty"`
	Time     string `xml:"time,attr,omitempty"` // total duration in seconds
	Tests    int    `xml:"tests,attr,omitempty"`
	Errors   int    `xml:"errors,attr,omitempty"`
	Failures int    `xml:"failures,attr,omitempty"`
	Skipped  int    `xml:"skipped,attr,omitempty"`
	Disabled int    `xml:"disabled,attr,omitempty"`

	Suites []Testsuite `xml:"testsuite,omitempty"`
}

// AddSuite adds a Testsuite and updates this testssuites' totals.
func (t *Testsuites) AddSuite(ts Testsuite) {
	t.Suites = append(t.Suites, ts)
	t.Tests += ts.Tests
	t.Errors += ts.Errors
	t.Failures += ts.Failures
	t.Skipped += ts.Skipped
	t.Disabled += ts.Disabled
}

// Testsuite is a single JUnit testsuite containing testcases.
type Testsuite struct {
	// required attributes
	Name     string `xml:"name,attr"`
	Tests    int    `xml:"tests,attr"`
	Failures int    `xml:"failures,attr"`
	Errors   int    `xml:"errors,attr"`
	ID       int    `xml:"id,attr"`

	// optional attributes
	Disabled  int    `xml:"disabled,attr,omitempty"`
	Hostname  string `xml:"hostname,attr,omitempty"`
	Package   string `xml:"package,attr,omitempty"`
	Skipped   int    `xml:"skipped,attr,omitempty"`
	Time      string `xml:"time,attr"`                // duration in seconds
	Timestamp string `xml:"timestamp,attr,omitempty"` // date and time in ISO8601

	Properties *[]Property `xml:"properties>property,omitempty"`
	Testcases  []Testcase  `xml:"testcase,omitempty"`
	SystemOut  *Output     `xml:"system-out,omitempty"`
	SystemErr  *Output     `xml:"system-err,omitempty"`
}

// AddProperty adds a property with the given name and value to this Testsuite.
func (t *Testsuite) AddProperty(name, value string) {
	prop := Property{Name: name, Value: value}
	if t.Properties == nil {
		t.Properties = &[]Property{prop}
		return
	}
	props := append(*t.Properties, prop)
	t.Properties = &props
}

// AddTestcase adds Testcase tc to this Testsuite.
func (t *Testsuite) AddTestcase(tc Testcase) {
	t.Testcases = append(t.Testcases, tc)
	t.Tests += 1

	if tc.Error != nil {
		t.Errors += 1
	}

	if tc.Failure != nil {
		t.Failures += 1
	}

	if tc.Skipped != nil {
		t.Skipped += 1
	}
}

// SetTimestamp sets the timestamp in this Testsuite.
func (ts *Testsuite) SetTimestamp(t time.Time) {
	ts.Timestamp = t.Format(time.RFC3339)
}

// Testcase represents a single test with its results.
type Testcase struct {
	// required attributes
	Name      string `xml:"name,attr"`
	Classname string `xml:"classname,attr"`

	// optional attributes
	Time   string `xml:"time,attr,omitempty"` // duration in seconds
	Status string `xml:"status,attr,omitempty"`

	Skipped   *Result `xml:"skipped,omitempty"`
	Error     *Result `xml:"error,omitempty"`
	Failure   *Result `xml:"failure,omitempty"`
	SystemOut *Output `xml:"system-out,omitempty"`
	SystemErr *Output `xml:"system-err,omitempty"`
}

// Property represents a key/value pair.
type Property struct {
	Name  string `xml:"name,attr"`
	Value string `xml:"value,attr"`
}

// Result represents the result of a single test.
type Result struct {
	Message string `xml:"message,attr"`
	Type    string `xml:"type,attr,omitempty"`
	Data    string `xml:",cdata"`
}

// Output represents output written to stdout or sderr.
type Output struct {
	Data string `xml:",cdata"`
}

// CreateFromReport creates a JUnit representation of the given gtr.Report.
func CreateFromReport(report gtr.Report, hostname string) Testsuites {
	var suites Testsuites
	for _, pkg := range report.Packages {
		var duration time.Duration
		suite := Testsuite{
			Name:     pkg.Name,
			Hostname: hostname,
			ID:       len(suites.Suites),
		}

		if !pkg.Timestamp.IsZero() {
			suite.SetTimestamp(pkg.Timestamp)
		}

		for k, v := range pkg.Properties {
			suite.AddProperty(k, v)
		}

		if len(pkg.Output) > 0 {
			suite.SystemOut = &Output{Data: formatOutput(pkg.Output, 0)}
		}

		if pkg.Coverage > 0 {
			suite.AddProperty("coverage.statements.pct", fmt.Sprintf("%.2f", pkg.Coverage))
		}

		for _, test := range pkg.Tests {
			duration += test.Duration
			suite.AddTestcase(createTestcaseForTest(pkg.Name, test))
		}

		// JUnit doesn't have a good way of dealing with build or runtime
		// errors that happen before a test has started, so we create a single
		// failing test that contains the build error details.
		if pkg.BuildError.Name != "" {
			tc := Testcase{
				Classname: pkg.BuildError.Name,
				Name:      pkg.BuildError.Cause,
				Time:      formatDuration(0),
				Error: &Result{
					Message: "Build error",
					Data:    strings.Join(pkg.BuildError.Output, "\n"),
				},
			}
			suite.AddTestcase(tc)
		}

		if pkg.RunError.Name != "" {
			tc := Testcase{
				Classname: pkg.RunError.Name,
				Name:      "Failure",
				Time:      formatDuration(0),
				Error: &Result{
					Message: "Runtime error",
					Data:    strings.Join(pkg.RunError.Output, "\n"),
				},
			}
			suite.AddTestcase(tc)
		}

		if (pkg.Duration) == 0 {
			suite.Time = formatDuration(duration)
		} else {
			suite.Time = formatDuration(pkg.Duration)
		}
		suites.AddSuite(suite)
	}
	return suites
}

func createTestcaseForTest(pkgName string, test gtr.Test) Testcase {
	tc := Testcase{
		Classname: pkgName,
		Name:      test.Name,
		Time:      formatDuration(test.Duration),
	}

	if test.Result == gtr.Fail {
		tc.Failure = &Result{
			Message: "Failed",
			Data:    formatOutput(test.Output, test.Level),
		}
	} else if test.Result == gtr.Skip {
		tc.Skipped = &Result{
			Message: "Skipped",
			Data:    formatOutput(test.Output, test.Level),
		}
	} else if test.Result == gtr.Unknown {
		tc.Error = &Result{
			Message: "No test result found",
			Data:    formatOutput(test.Output, test.Level),
		}
	} else if len(test.Output) > 0 {
		tc.SystemOut = &Output{Data: formatOutput(test.Output, test.Level)}
	}
	return tc
}

// formatDuration returns the JUnit string representation of the given
// duration.
func formatDuration(d time.Duration) string {
	return fmt.Sprintf("%.3f", d.Seconds())
}

// formatOutput combines the lines from the given output into a single string.
func formatOutput(output []string, indent int) string {
	return strings.Join(output, "\n")
}
<commit_msg>junit: Add File attribute to testsuite<commit_after>// Package junit defines a JUnit XML report and includes convenience methods
// for working with these reports.
package junit

import (
	"encoding/xml"
	"fmt"
	"strings"
	"time"

	"github.com/jstemmer/go-junit-report/v2/gtr"
)

// Testsuites is a collection of JUnit testsuites.
type Testsuites struct {
	XMLName xml.Name `xml:"testsuites"`

	Name     string `xml:"name,attr,omitempty"`
	Time     string `xml:"time,attr,omitempty"` // total duration in seconds
	Tests    int    `xml:"tests,attr,omitempty"`
	Errors   int    `xml:"errors,attr,omitempty"`
	Failures int    `xml:"failures,attr,omitempty"`
	Skipped  int    `xml:"skipped,attr,omitempty"`
	Disabled int    `xml:"disabled,attr,omitempty"`

	Suites []Testsuite `xml:"testsuite,omitempty"`
}

// AddSuite adds a Testsuite and updates this testssuites' totals.
func (t *Testsuites) AddSuite(ts Testsuite) {
	t.Suites = append(t.Suites, ts)
	t.Tests += ts.Tests
	t.Errors += ts.Errors
	t.Failures += ts.Failures
	t.Skipped += ts.Skipped
	t.Disabled += ts.Disabled
}

// Testsuite is a single JUnit testsuite containing testcases.
type Testsuite struct {
	// required attributes
	Name     string `xml:"name,attr"`
	Tests    int    `xml:"tests,attr"`
	Failures int    `xml:"failures,attr"`
	Errors   int    `xml:"errors,attr"`
	ID       int    `xml:"id,attr"`

	// optional attributes
	Disabled  int    `xml:"disabled,attr,omitempty"`
	Hostname  string `xml:"hostname,attr,omitempty"`
	Package   string `xml:"package,attr,omitempty"`
	Skipped   int    `xml:"skipped,attr,omitempty"`
	Time      string `xml:"time,attr"`                // duration in seconds
	Timestamp string `xml:"timestamp,attr,omitempty"` // date and time in ISO8601
	File      string `xml:"file,attr"`

	Properties *[]Property `xml:"properties>property,omitempty"`
	Testcases  []Testcase  `xml:"testcase,omitempty"`
	SystemOut  *Output     `xml:"system-out,omitempty"`
	SystemErr  *Output     `xml:"system-err,omitempty"`
}

// AddProperty adds a property with the given name and value to this Testsuite.
func (t *Testsuite) AddProperty(name, value string) {
	prop := Property{Name: name, Value: value}
	if t.Properties == nil {
		t.Properties = &[]Property{prop}
		return
	}
	props := append(*t.Properties, prop)
	t.Properties = &props
}

// AddTestcase adds Testcase tc to this Testsuite.
func (t *Testsuite) AddTestcase(tc Testcase) {
	t.Testcases = append(t.Testcases, tc)
	t.Tests += 1

	if tc.Error != nil {
		t.Errors += 1
	}

	if tc.Failure != nil {
		t.Failures += 1
	}

	if tc.Skipped != nil {
		t.Skipped += 1
	}
}

// SetTimestamp sets the timestamp in this Testsuite.
func (ts *Testsuite) SetTimestamp(t time.Time) {
	ts.Timestamp = t.Format(time.RFC3339)
}

// Testcase represents a single test with its results.
type Testcase struct {
	// required attributes
	Name      string `xml:"name,attr"`
	Classname string `xml:"classname,attr"`

	// optional attributes
	Time   string `xml:"time,attr,omitempty"` // duration in seconds
	Status string `xml:"status,attr,omitempty"`

	Skipped   *Result `xml:"skipped,omitempty"`
	Error     *Result `xml:"error,omitempty"`
	Failure   *Result `xml:"failure,omitempty"`
	SystemOut *Output `xml:"system-out,omitempty"`
	SystemErr *Output `xml:"system-err,omitempty"`
}

// Property represents a key/value pair.
type Property struct {
	Name  string `xml:"name,attr"`
	Value string `xml:"value,attr"`
}

// Result represents the result of a single test.
type Result struct {
	Message string `xml:"message,attr"`
	Type    string `xml:"type,attr,omitempty"`
	Data    string `xml:",cdata"`
}

// Output represents output written to stdout or sderr.
type Output struct {
	Data string `xml:",cdata"`
}

// CreateFromReport creates a JUnit representation of the given gtr.Report.
func CreateFromReport(report gtr.Report, hostname string) Testsuites {
	var suites Testsuites
	for _, pkg := range report.Packages {
		var duration time.Duration
		suite := Testsuite{
			Name:     pkg.Name,
			Hostname: hostname,
			ID:       len(suites.Suites),
		}

		if !pkg.Timestamp.IsZero() {
			suite.SetTimestamp(pkg.Timestamp)
		}

		for k, v := range pkg.Properties {
			suite.AddProperty(k, v)
		}

		if len(pkg.Output) > 0 {
			suite.SystemOut = &Output{Data: formatOutput(pkg.Output, 0)}
		}

		if pkg.Coverage > 0 {
			suite.AddProperty("coverage.statements.pct", fmt.Sprintf("%.2f", pkg.Coverage))
		}

		for _, test := range pkg.Tests {
			duration += test.Duration
			suite.AddTestcase(createTestcaseForTest(pkg.Name, test))
		}

		// JUnit doesn't have a good way of dealing with build or runtime
		// errors that happen before a test has started, so we create a single
		// failing test that contains the build error details.
		if pkg.BuildError.Name != "" {
			tc := Testcase{
				Classname: pkg.BuildError.Name,
				Name:      pkg.BuildError.Cause,
				Time:      formatDuration(0),
				Error: &Result{
					Message: "Build error",
					Data:    strings.Join(pkg.BuildError.Output, "\n"),
				},
			}
			suite.AddTestcase(tc)
		}

		if pkg.RunError.Name != "" {
			tc := Testcase{
				Classname: pkg.RunError.Name,
				Name:      "Failure",
				Time:      formatDuration(0),
				Error: &Result{
					Message: "Runtime error",
					Data:    strings.Join(pkg.RunError.Output, "\n"),
				},
			}
			suite.AddTestcase(tc)
		}

		if (pkg.Duration) == 0 {
			suite.Time = formatDuration(duration)
		} else {
			suite.Time = formatDuration(pkg.Duration)
		}
		suites.AddSuite(suite)
	}
	return suites
}

func createTestcaseForTest(pkgName string, test gtr.Test) Testcase {
	tc := Testcase{
		Classname: pkgName,
		Name:      test.Name,
		Time:      formatDuration(test.Duration),
	}

	if test.Result == gtr.Fail {
		tc.Failure = &Result{
			Message: "Failed",
			Data:    formatOutput(test.Output, test.Level),
		}
	} else if test.Result == gtr.Skip {
		tc.Skipped = &Result{
			Message: "Skipped",
			Data:    formatOutput(test.Output, test.Level),
		}
	} else if test.Result == gtr.Unknown {
		tc.Error = &Result{
			Message: "No test result found",
			Data:    formatOutput(test.Output, test.Level),
		}
	} else if len(test.Output) > 0 {
		tc.SystemOut = &Output{Data: formatOutput(test.Output, test.Level)}
	}
	return tc
}

// formatDuration returns the JUnit string representation of the given
// duration.
func formatDuration(d time.Duration) string {
	return fmt.Sprintf("%.3f", d.Seconds())
}

// formatOutput combines the lines from the given output into a single string.
func formatOutput(output []string, indent int) string {
	return strings.Join(output, "\n")
}
<|endoftext|>


<commit_before>package environs_test

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	_ "launchpad.net/juju-core/environs/dummy"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
	"path/filepath"
)

type suite struct{}

var _ = Suite(suite{})

var invalidConfigTests = []struct {
	env string
	err string
}{
	{"'", "YAML error:.*"},
	{`
default: unknown
environments:
    only:
        type: unknown
`, `default environment .* does not exist`,
	},
}

func (suite) TestInvalidConfig(c *C) {
	for i, t := range invalidConfigTests {
		c.Logf("running test %v", i)
		_, err := environs.ReadEnvironsBytes([]byte(t.env))
		c.Check(err, ErrorMatches, t.err)
	}
}

var invalidEnvTests = []struct {
	env  string
	name string
	err  string
}{
	{`
environments:
    only:
        foo: bar
`, "", `environment "only" has no type`,
	}, {`
environments:
    only:
        foo: bar
`, "only", `environment "only" has no type`,
	}, {`
environments:
    only:
        foo: bar
        type: crazy
`, "only", `environment "only" has an unknown provider type "crazy"`,
	}, {`
environments:
    only:
        type: dummy
`, "only", `.*state-server: expected bool, got nothing`,
	}, {`
environments:
    only:
        type: dummy
        state-server: false
        unknown-value: causes-an-error
`, "only", `.*unknown-value: expected nothing, got "causes-an-error"`,
	},
}

func (suite) TestInvalidEnv(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore()
	for i, t := range invalidEnvTests {
		c.Logf("running test %v", i)
		es, err := environs.ReadEnvironsBytes([]byte(t.env))
		c.Check(err, IsNil)
		e, err := es.Open(t.name)
		c.Check(err, ErrorMatches, t.err)
		c.Check(e, IsNil)
	}
}

var configTests = []struct {
	env   string
	check func(c *C, es *environs.Environs)
}{
	{`
environments:
    only:
        type: dummy
        state-server: false
`, func(c *C, es *environs.Environs) {
		e, err := es.Open("")
		c.Assert(err, IsNil)
		c.Assert(e.Name(), Equals, "only")
	}}, {`
default:
    invalid
environments:
    valid:
        type: dummy
        state-server: false
    invalid:
        type: crazy
`, func(c *C, es *environs.Environs) {
		e, err := es.Open("")
		c.Assert(err, ErrorMatches, `environment "invalid" has an unknown provider type "crazy"`)
		c.Assert(e, IsNil)
		e, err = es.Open("valid")
		c.Assert(err, IsNil)
		c.Assert(e.Name(), Equals, "valid")
	}}, {`
environments:
    one:
        type: dummy
        state-server: false
    two:
        type: dummy
        state-server: false
`, func(c *C, es *environs.Environs) {
		e, err := es.Open("")
		c.Assert(err, ErrorMatches, `no default environment found`)
		c.Assert(e, IsNil)
	}},
}

func (suite) TestConfig(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "only", "valid", "one", "two").Restore()
	for i, t := range configTests {
		c.Logf("running test %v", i)
		es, err := environs.ReadEnvironsBytes([]byte(t.env))
		c.Assert(err, IsNil)
		t.check(c, es)
	}
}

func (suite) TestDefaultConfigFile(c *C) {
	defer testing.MakeEmptyFakeHome(c).Restore()

	env := `
environments:
    only:
        type: dummy
        state-server: false
        authorized-keys: i-am-a-key
`
	outfile, err := environs.WriteEnvirons("", env)
	c.Assert(err, IsNil)
	path := testing.HomePath(".juju", "environments.yaml")
	c.Assert(path, Equals, outfile)

	es, err := environs.ReadEnvirons("")
	c.Assert(err, IsNil)
	e, err := es.Open("")
	c.Assert(err, IsNil)
	c.Assert(e.Name(), Equals, "only")
}

func (suite) TestNamedConfigFile(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore()

	env := `
environments:
    only:
        type: dummy
        state-server: false
        authorized-keys: i-am-a-key
`
	path := filepath.Join(c.MkDir(), "a-file")
	outfile, err := environs.WriteEnvirons(path, env)
	c.Assert(err, IsNil)
	c.Assert(path, Equals, outfile)

	es, err := environs.ReadEnvirons(path)
	c.Assert(err, IsNil)
	e, err := es.Open("")
	c.Assert(err, IsNil)
	c.Assert(e.Name(), Equals, "only")
}

// TODO(mue) Handle unset $HOME and $JUJU_HOME status in environs/config.
// func (suite) TestWriteConfigNoHome(c *C) {
// 	defer testing.MakeFakeHomeNoEnvironments(c).Restore()
// 	os.Setenv("HOME", "")

// 	env := `
// environments:
//     only:
//         type: dummy
//         state-server: false
//         authorized-keys: i-am-a-key
// `
// 	_, err := environs.WriteEnvirons("", env)
// 	c.Assert(err, NotNil)
// 	c.Assert(err.Error(), Equals, "$HOME not set")
// }

func (suite) TestConfigRoundTrip(c *C) {
	cfg, err := config.New(map[string]interface{}{
		"name":            "bladaam",
		"type":            "dummy",
		"state-server":    false,
		"authorized-keys": "i-am-a-key",
		"ca-cert":         testing.CACert,
		"ca-private-key":  "",
	})
	c.Assert(err, IsNil)
	provider, err := environs.Provider(cfg.Type())
	c.Assert(err, IsNil)
	cfg, err = provider.Validate(cfg, nil)
	c.Assert(err, IsNil)
	env, err := environs.New(cfg)
	c.Assert(err, IsNil)
	c.Assert(cfg.AllAttrs(), DeepEquals, env.Config().AllAttrs())
}

func (suite) TestBootstrapConfig(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "bladaam").Restore()
	cfg, err := config.New(map[string]interface{}{
		"name":            "bladaam",
		"type":            "dummy",
		"state-server":    false,
		"admin-secret":    "highly",
		"secret":          "um",
		"authorized-keys": "i-am-a-key",
		"ca-cert":         testing.CACert,
		"ca-private-key":  testing.CAKey,
	})
	c.Assert(err, IsNil)
	provider, err := environs.Provider(cfg.Type())
	c.Assert(err, IsNil)

	tools := &state.Tools{
		URL:    "http://x",
		Binary: version.MustParseBinary("1.2.3-foo-bar"),
	}
	cfg1, err := environs.BootstrapConfig(provider, cfg, tools)
	c.Assert(err, IsNil)

	expect := cfg.AllAttrs()
	delete(expect, "secret")
	expect["admin-secret"] = ""
	expect["ca-private-key"] = ""
	expect["agent-version"] = "1.2.3"
	c.Assert(cfg1.AllAttrs(), DeepEquals, expect)
}
<commit_msg>config: removed a test which has been commented out before<commit_after>package environs_test

import (
	. "launchpad.net/gocheck"
	"launchpad.net/juju-core/environs"
	"launchpad.net/juju-core/environs/config"
	_ "launchpad.net/juju-core/environs/dummy"
	"launchpad.net/juju-core/state"
	"launchpad.net/juju-core/testing"
	"launchpad.net/juju-core/version"
	"path/filepath"
)

type suite struct{}

var _ = Suite(suite{})

var invalidConfigTests = []struct {
	env string
	err string
}{
	{"'", "YAML error:.*"},
	{`
default: unknown
environments:
    only:
        type: unknown
`, `default environment .* does not exist`,
	},
}

func (suite) TestInvalidConfig(c *C) {
	for i, t := range invalidConfigTests {
		c.Logf("running test %v", i)
		_, err := environs.ReadEnvironsBytes([]byte(t.env))
		c.Check(err, ErrorMatches, t.err)
	}
}

var invalidEnvTests = []struct {
	env  string
	name string
	err  string
}{
	{`
environments:
    only:
        foo: bar
`, "", `environment "only" has no type`,
	}, {`
environments:
    only:
        foo: bar
`, "only", `environment "only" has no type`,
	}, {`
environments:
    only:
        foo: bar
        type: crazy
`, "only", `environment "only" has an unknown provider type "crazy"`,
	}, {`
environments:
    only:
        type: dummy
`, "only", `.*state-server: expected bool, got nothing`,
	}, {`
environments:
    only:
        type: dummy
        state-server: false
        unknown-value: causes-an-error
`, "only", `.*unknown-value: expected nothing, got "causes-an-error"`,
	},
}

func (suite) TestInvalidEnv(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore()
	for i, t := range invalidEnvTests {
		c.Logf("running test %v", i)
		es, err := environs.ReadEnvironsBytes([]byte(t.env))
		c.Check(err, IsNil)
		e, err := es.Open(t.name)
		c.Check(err, ErrorMatches, t.err)
		c.Check(e, IsNil)
	}
}

var configTests = []struct {
	env   string
	check func(c *C, es *environs.Environs)
}{
	{`
environments:
    only:
        type: dummy
        state-server: false
`, func(c *C, es *environs.Environs) {
		e, err := es.Open("")
		c.Assert(err, IsNil)
		c.Assert(e.Name(), Equals, "only")
	}}, {`
default:
    invalid
environments:
    valid:
        type: dummy
        state-server: false
    invalid:
        type: crazy
`, func(c *C, es *environs.Environs) {
		e, err := es.Open("")
		c.Assert(err, ErrorMatches, `environment "invalid" has an unknown provider type "crazy"`)
		c.Assert(e, IsNil)
		e, err = es.Open("valid")
		c.Assert(err, IsNil)
		c.Assert(e.Name(), Equals, "valid")
	}}, {`
environments:
    one:
        type: dummy
        state-server: false
    two:
        type: dummy
        state-server: false
`, func(c *C, es *environs.Environs) {
		e, err := es.Open("")
		c.Assert(err, ErrorMatches, `no default environment found`)
		c.Assert(e, IsNil)
	}},
}

func (suite) TestConfig(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "only", "valid", "one", "two").Restore()
	for i, t := range configTests {
		c.Logf("running test %v", i)
		es, err := environs.ReadEnvironsBytes([]byte(t.env))
		c.Assert(err, IsNil)
		t.check(c, es)
	}
}

func (suite) TestDefaultConfigFile(c *C) {
	defer testing.MakeEmptyFakeHome(c).Restore()

	env := `
environments:
    only:
        type: dummy
        state-server: false
        authorized-keys: i-am-a-key
`
	outfile, err := environs.WriteEnvirons("", env)
	c.Assert(err, IsNil)
	path := testing.HomePath(".juju", "environments.yaml")
	c.Assert(path, Equals, outfile)

	es, err := environs.ReadEnvirons("")
	c.Assert(err, IsNil)
	e, err := es.Open("")
	c.Assert(err, IsNil)
	c.Assert(e.Name(), Equals, "only")
}

func (suite) TestNamedConfigFile(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "only").Restore()

	env := `
environments:
    only:
        type: dummy
        state-server: false
        authorized-keys: i-am-a-key
`
	path := filepath.Join(c.MkDir(), "a-file")
	outfile, err := environs.WriteEnvirons(path, env)
	c.Assert(err, IsNil)
	c.Assert(path, Equals, outfile)

	es, err := environs.ReadEnvirons(path)
	c.Assert(err, IsNil)
	e, err := es.Open("")
	c.Assert(err, IsNil)
	c.Assert(e.Name(), Equals, "only")
}

func (suite) TestConfigRoundTrip(c *C) {
	cfg, err := config.New(map[string]interface{}{
		"name":            "bladaam",
		"type":            "dummy",
		"state-server":    false,
		"authorized-keys": "i-am-a-key",
		"ca-cert":         testing.CACert,
		"ca-private-key":  "",
	})
	c.Assert(err, IsNil)
	provider, err := environs.Provider(cfg.Type())
	c.Assert(err, IsNil)
	cfg, err = provider.Validate(cfg, nil)
	c.Assert(err, IsNil)
	env, err := environs.New(cfg)
	c.Assert(err, IsNil)
	c.Assert(cfg.AllAttrs(), DeepEquals, env.Config().AllAttrs())
}

func (suite) TestBootstrapConfig(c *C) {
	defer testing.MakeFakeHomeNoEnvironments(c, "bladaam").Restore()
	cfg, err := config.New(map[string]interface{}{
		"name":            "bladaam",
		"type":            "dummy",
		"state-server":    false,
		"admin-secret":    "highly",
		"secret":          "um",
		"authorized-keys": "i-am-a-key",
		"ca-cert":         testing.CACert,
		"ca-private-key":  testing.CAKey,
	})
	c.Assert(err, IsNil)
	provider, err := environs.Provider(cfg.Type())
	c.Assert(err, IsNil)

	tools := &state.Tools{
		URL:    "http://x",
		Binary: version.MustParseBinary("1.2.3-foo-bar"),
	}
	cfg1, err := environs.BootstrapConfig(provider, cfg, tools)
	c.Assert(err, IsNil)

	expect := cfg.AllAttrs()
	delete(expect, "secret")
	expect["admin-secret"] = ""
	expect["ca-private-key"] = ""
	expect["agent-version"] = "1.2.3"
	c.Assert(cfg1.AllAttrs(), DeepEquals, expect)
}
<|endoftext|>


<commit_before>package errorcollector

import (
	"errors"
	"testing"
)

func TestErrorCollector(t *testing.T) {
	tests := []struct {
		funcs         []func() error
		notNil        bool
		expectedError string
	}{
		{
			[]func() error{},
			false,
			"",
		},
		{
			[]func() error{
				func() error { return nil },
				func() error { return nil },
				func() error { return nil },
			},
			false,
			"",
		},
		{
			[]func() error{
				func() error { return errors.New("beep") },
				func() error { return nil },
				func() error { return nil },
			},
			true,
			"collected errors: beep",
		},
		{
			[]func() error{
				func() error { return errors.New("beep") },
				func() error { return nil },
				func() error { return errors.New("boop") },
			},
			true,
			"collected errors: beep, boop",
		},
		{
			[]func() error{
				func() error {
					collector := New()
					subCollector := New()
					collector.Collect(errors.New("beep"))
					collector.Collect(errors.New("boop"))
					subCollector.Collect(errors.New("biip"))
					subCollector.Collect(nil)
					collector.Collect(subCollector)
					return collector
				},
				func() error { return nil },
				func() error { return errors.New("baap") },
			},
			true,
			"collected errors: beep, boop, biip, baap",
		},
		{
			[]func() error{
				func() error {
					collector := New()
					subCollector := New()
					collector.Collect(subCollector)
					return collector
				},
				func() error { return nil },
				func() error { return nil },
			},
			false,
			"",
		},
	}

	for _, test := range tests {
		errors := New()
		for _, fn := range test.funcs {
			errors.Collect(fn())
		}
		if (errors != nil) != test.notNil {
			t.Errorf(
				"Expected error to be %v, error was %v",
				test.notNil,
				errors)
		}
		if errors != nil && errors.Error() != test.expectedError {
			t.Errorf(
				"Expected error to return %v, got %v",
				test.expectedError,
				errors.Error())
		}

	}
}

func BenchmarkErrorCollector(b *testing.B) {
	for i := 0; i < b.N; i++ {
		collector := New()
		childCollector := New()
		collector.Collect(errors.New("beep"))
		childCollector.Collect(errors.New("boop"))
		collector.Collect(childCollector)
	}
}
<commit_msg>improve benchmark<commit_after>package errorcollector

import (
	"errors"
	"testing"
)

func TestErrorCollector(t *testing.T) {
	tests := []struct {
		funcs         []func() error
		notNil        bool
		expectedError string
	}{
		{
			[]func() error{},
			false,
			"",
		},
		{
			[]func() error{
				func() error { return nil },
				func() error { return nil },
				func() error { return nil },
			},
			false,
			"",
		},
		{
			[]func() error{
				func() error { return errors.New("beep") },
				func() error { return nil },
				func() error { return nil },
			},
			true,
			"collected errors: beep",
		},
		{
			[]func() error{
				func() error { return errors.New("beep") },
				func() error { return nil },
				func() error { return errors.New("boop") },
			},
			true,
			"collected errors: beep, boop",
		},
		{
			[]func() error{
				func() error {
					collector := New()
					subCollector := New()
					collector.Collect(errors.New("beep"))
					collector.Collect(errors.New("boop"))
					subCollector.Collect(errors.New("biip"))
					subCollector.Collect(nil)
					collector.Collect(subCollector)
					return collector
				},
				func() error { return nil },
				func() error { return errors.New("baap") },
			},
			true,
			"collected errors: beep, boop, biip, baap",
		},
		{
			[]func() error{
				func() error {
					collector := New()
					subCollector := New()
					collector.Collect(subCollector)
					return collector
				},
				func() error { return nil },
				func() error { return nil },
			},
			false,
			"",
		},
	}

	for _, test := range tests {
		errors := New()
		for _, fn := range test.funcs {
			errors.Collect(fn())
		}
		if (errors != nil) != test.notNil {
			t.Errorf(
				"Expected error to be %v, error was %v",
				test.notNil,
				errors)
		}
		if errors != nil && errors.Error() != test.expectedError {
			t.Errorf(
				"Expected error to return %v, got %v",
				test.expectedError,
				errors.Error())
		}

	}
}

func BenchmarkErrorCollector(b *testing.B) {
	for i := 0; i < b.N; i++ {
		collector := New()
		childCollector := New()
		collector.Collect(errors.New("beep"))
		childCollector.Collect(errors.New("boop"))
		collector.Collect(childCollector)
		collector.Error()
	}
}
<|endoftext|>


<commit_before>package peerstore

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

	"github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
)

// PeerInfo is a small struct used to pass around a peer with
// a set of addresses (and later, keys?). This is not meant to be
// a complete view of the system, but rather to model updates to
// the peerstore. It is used by things like the routing system.
type PeerInfo struct {
	ID    peer.ID
	Addrs []ma.Multiaddr
}

var ErrInvalidAddr = fmt.Errorf("invalid p2p multiaddr")

func InfoFromP2pAddr(m ma.Multiaddr) (*PeerInfo, error) {
	if m == nil {
		return nil, ErrInvalidAddr
	}

	// make sure it's an IPFS addr
	parts := ma.Split(m)
	if len(parts) < 1 {
		return nil, ErrInvalidAddr
	}

	// TODO(lgierth): we shouldn't assume /ipfs is the last part
	ipfspart := parts[len(parts)-1]
	if ipfspart.Protocols()[0].Code != ma.P_IPFS {
		return nil, ErrInvalidAddr
	}

	// make sure the /ipfs value parses as a peer.ID
	peerIdParts := strings.Split(ipfspart.String(), "/")
	peerIdStr := peerIdParts[len(peerIdParts)-1]
	id, err := peer.IDB58Decode(peerIdStr)
	if err != nil {
		return nil, err
	}

	// we might have received just an /ipfs part, which means there's no addr.
	var addrs []ma.Multiaddr
	if len(parts) > 1 {
		addrs = append(addrs, ma.Join(parts[:len(parts)-1]...))
	}

	return &PeerInfo{
		ID:    id,
		Addrs: addrs,
	}, nil
}

func InfoToP2pAddrs(pi *PeerInfo) ([]ma.Multiaddr, error) {
	var addrs []ma.Multiaddr
	tpl := "/" + ma.ProtocolWithCode(ma.P_IPFS).Name + "/"
	for _, addr := range pi.Addrs {
		p2paddr, err := ma.NewMultiaddr(tpl + peer.IDB58Encode(pi.ID))
		if err != nil {
			return nil, err
		}
		addrs = append(addrs, addr.Encapsulate(p2paddr))
	}
	return addrs, nil
}

func (pi *PeerInfo) Loggable() map[string]interface{} {
	return map[string]interface{}{
		"peerID": pi.ID.Pretty(),
		"addrs":  pi.Addrs,
	}
}

func (pi PeerInfo) MarshalJSON() ([]byte, error) {
	out := make(map[string]interface{})
	out["ID"] = pi.ID.Pretty()
	var addrs []string
	for _, a := range pi.Addrs {
		addrs = append(addrs, a.String())
	}
	out["Addrs"] = addrs
	return json.Marshal(out)
}

func (pi *PeerInfo) UnmarshalJSON(b []byte) error {
	var data map[string]interface{}
	err := json.Unmarshal(b, &data)
	if err != nil {
		return err
	}
	pid, err := peer.IDB58Decode(data["ID"].(string))
	if err != nil {
		return err
	}
	pi.ID = pid
	addrs, ok := data["Addrs"].([]interface{})
	if ok {
		for _, a := range addrs {
			pi.Addrs = append(pi.Addrs, ma.StringCast(a.(string)))
		}
	}
	return nil
}
<commit_msg>Implement fmt.Stringer for PeerInfo<commit_after>package peerstore

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

	peer "github.com/libp2p/go-libp2p-peer"
	ma "github.com/multiformats/go-multiaddr"
)

// PeerInfo is a small struct used to pass around a peer with
// a set of addresses (and later, keys?). This is not meant to be
// a complete view of the system, but rather to model updates to
// the peerstore. It is used by things like the routing system.
type PeerInfo struct {
	ID    peer.ID
	Addrs []ma.Multiaddr
}

var _ fmt.Stringer = PeerInfo{}

func (pi PeerInfo) String() string {
	return fmt.Sprintf("{%v: %v}", pi.ID, pi.Addrs)
}

var ErrInvalidAddr = fmt.Errorf("invalid p2p multiaddr")

func InfoFromP2pAddr(m ma.Multiaddr) (*PeerInfo, error) {
	if m == nil {
		return nil, ErrInvalidAddr
	}

	// make sure it's an IPFS addr
	parts := ma.Split(m)
	if len(parts) < 1 {
		return nil, ErrInvalidAddr
	}

	// TODO(lgierth): we shouldn't assume /ipfs is the last part
	ipfspart := parts[len(parts)-1]
	if ipfspart.Protocols()[0].Code != ma.P_IPFS {
		return nil, ErrInvalidAddr
	}

	// make sure the /ipfs value parses as a peer.ID
	peerIdParts := strings.Split(ipfspart.String(), "/")
	peerIdStr := peerIdParts[len(peerIdParts)-1]
	id, err := peer.IDB58Decode(peerIdStr)
	if err != nil {
		return nil, err
	}

	// we might have received just an /ipfs part, which means there's no addr.
	var addrs []ma.Multiaddr
	if len(parts) > 1 {
		addrs = append(addrs, ma.Join(parts[:len(parts)-1]...))
	}

	return &PeerInfo{
		ID:    id,
		Addrs: addrs,
	}, nil
}

func InfoToP2pAddrs(pi *PeerInfo) ([]ma.Multiaddr, error) {
	var addrs []ma.Multiaddr
	tpl := "/" + ma.ProtocolWithCode(ma.P_IPFS).Name + "/"
	for _, addr := range pi.Addrs {
		p2paddr, err := ma.NewMultiaddr(tpl + peer.IDB58Encode(pi.ID))
		if err != nil {
			return nil, err
		}
		addrs = append(addrs, addr.Encapsulate(p2paddr))
	}
	return addrs, nil
}

func (pi *PeerInfo) Loggable() map[string]interface{} {
	return map[string]interface{}{
		"peerID": pi.ID.Pretty(),
		"addrs":  pi.Addrs,
	}
}

func (pi PeerInfo) MarshalJSON() ([]byte, error) {
	out := make(map[string]interface{})
	out["ID"] = pi.ID.Pretty()
	var addrs []string
	for _, a := range pi.Addrs {
		addrs = append(addrs, a.String())
	}
	out["Addrs"] = addrs
	return json.Marshal(out)
}

func (pi *PeerInfo) UnmarshalJSON(b []byte) error {
	var data map[string]interface{}
	err := json.Unmarshal(b, &data)
	if err != nil {
		return err
	}
	pid, err := peer.IDB58Decode(data["ID"].(string))
	if err != nil {
		return err
	}
	pi.ID = pid
	addrs, ok := data["Addrs"].([]interface{})
	if ok {
		for _, a := range addrs {
			pi.Addrs = append(pi.Addrs, ma.StringCast(a.(string)))
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package kd

import (
	"errors"
	"fmt"
	"koding/newkite/kd/util"
	"koding/newkite/kodingkey"
	"time"
)

const KeyLength = 64

type Register struct{}

func NewRegister() *Register {
	return &Register{}
}

func (r *Register) Definition() string {
	return "Register this host to Koding"
}

func (r *Register) Exec(args []string) error {
	authServer := util.AuthServer

	// change authServer address if debug mode is enabled
	if len(args) == 1 && (args[0] == "--debug" || args[0] == "-d") {
		authServer = util.AuthServerLocal
	}

	hostID, err := util.HostID()
	if err != nil {
		return err
	}

	var key string
	keyExist := false

	key, err = util.GetKey()
	if err != nil {
		k, err := kodingkey.NewKodingKey()
		if err != nil {
			return err
		}

		key = k.String()
	} else {
		fmt.Printf("Found a key under '%s'. Going to use it to register\n", util.GetKdPath())
		keyExist = true
	}

	registerUrl := fmt.Sprintf("%s/-/auth/register/%s/%s", authServer, hostID, key)

	// first check if the user is alrady registered
	err = util.CheckKey(authServer, key)
	if err == nil {
		fmt.Printf("... you are already registered.\n")
		return nil
	}

	fmt.Printf("Please open the following url for authentication:\n\n")
	fmt.Println(registerUrl)
	fmt.Printf("\nwaiting . ")

	// .. if not let the user register himself
	err = checker(authServer, key)
	if err != nil {
		return err
	}
	fmt.Println("successfully authenticated.")

	if keyExist {
		return nil
	}

	err = util.WriteKey(key)
	if err != nil {
		return err
	}

	return nil
}

// checker checks if the user has browsed the register URL by polling the check URL.
func checker(authServer, key string) error {
	// check the result every two seconds
	ticker := time.NewTicker(2 * time.Second).C

	// wait for three minutes, if not successfull abort it
	timeout := time.After(3 * time.Minute)

	for {
		select {
		case <-ticker:
			err := util.CheckKey(authServer, key)
			if err != nil {
				// we didn't get OK message, continue until timout
				fmt.Printf(". ") // animation
				continue
			}

			return nil
		case <-timeout:
			return errors.New("timeout")
		}
	}
}
<commit_msg>kd/register: add an option to register to different servers<commit_after>package kd

import (
	"errors"
	"fmt"
	"koding/newkite/kd/util"
	"koding/newkite/kodingkey"
	"time"
)

const KeyLength = 64

type Register struct{}

func NewRegister() *Register {
	return &Register{}
}

func (r *Register) Definition() string {
	return "Register this host to Koding"
}

func (r *Register) Exec(args []string) error {
	authServer := util.AuthServer

	// change authServer address if debug mode is enabled
	if len(args) == 1 && (args[0] == "--debug" || args[0] == "-d") {
		authServer = util.AuthServerLocal
	}

	// i.e: kd register to latest.koding.com
	//  	kd register to localhost:4000
	if len(args) == 2 && args[0] == "to" {
		authServer = fmt.Sprintf("http://%s", args[1])
	}

	hostID, err := util.HostID()
	if err != nil {
		return err
	}

	var key string
	keyExist := false

	key, err = util.GetKey()
	if err != nil {
		k, err := kodingkey.NewKodingKey()
		if err != nil {
			return err
		}

		key = k.String()
	} else {
		fmt.Printf("Found a key under '%s'. Going to use it to register\n", util.GetKdPath())
		keyExist = true
	}

	registerUrl := fmt.Sprintf("%s/-/auth/register/%s/%s", authServer, hostID, key)

	// first check if the user is alrady registered
	err = util.CheckKey(authServer, key)
	if err == nil {
		fmt.Printf("... you are already registered.\n")
		return nil
	}

	fmt.Printf("Please open the following url for authentication:\n\n")
	fmt.Println(registerUrl)
	fmt.Printf("\nwaiting . ")

	// .. if not let the user register himself
	err = checker(authServer, key)
	if err != nil {
		return err
	}
	fmt.Println("successfully authenticated.")

	if keyExist {
		return nil
	}

	err = util.WriteKey(key)
	if err != nil {
		return err
	}

	return nil
}

// checker checks if the user has browsed the register URL by polling the
// check URL.
func checker(authServer, key string) error {
	// check the result every two seconds
	ticker := time.NewTicker(2 * time.Second).C

	// wait for three minutes, if not successfull abort it
	timeout := time.After(3 * time.Minute)

	for {
		select {
		case <-ticker:
			err := util.CheckKey(authServer, key)
			if err != nil {
				// we didn't get OK message, continue until timout
				fmt.Printf(". ") // animation
				continue
			}

			return nil
		case <-timeout:
			return errors.New("timeout")
		}
	}
}
<|endoftext|>


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

package main

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/duration"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"

	meshconfig "istio.io/api/mesh/v1alpha1"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/proxy"
	envoy "istio.io/istio/pilot/pkg/proxy/envoy/v1"
	"istio.io/istio/pilot/pkg/serviceregistry"
	"istio.io/istio/pkg/cmd"
	"istio.io/istio/pkg/collateral"
	"istio.io/istio/pkg/log"
	"istio.io/istio/pkg/version"
)

var (
	role     model.Proxy
	registry serviceregistry.ServiceRegistry

	// proxy config flags (named identically)
	configPath               string
	binaryPath               string
	serviceCluster           string
	availabilityZone         string
	drainDuration            time.Duration
	parentShutdownDuration   time.Duration
	discoveryAddress         string
	discoveryRefreshDelay    time.Duration
	zipkinAddress            string
	connectTimeout           time.Duration
	statsdUDPAddress         string
	proxyAdminPort           int
	controlPlaneAuthPolicy   string
	customConfigFile         string
	proxyLogLevel            string
	concurrency              int
	bootstrapv2              bool
	templateFile             string
	disableInternalTelemetry bool

	loggingOptions = log.DefaultOptions()

	rootCmd = &cobra.Command{
		Use:   "pilot-agent",
		Short: "Istio Pilot agent",
		Long:  "Istio Pilot agent runs in the side car or gateway container and bootstraps envoy.",
	}

	proxyCmd = &cobra.Command{
		Use:   "proxy",
		Short: "Envoy proxy agent",
		RunE: func(c *cobra.Command, args []string) error {
			if err := log.Configure(loggingOptions); err != nil {
				return err
			}
			log.Infof("Version %s", version.Info.String())
			role.Type = model.Sidecar
			if len(args) > 0 {
				role.Type = model.NodeType(args[0])
			}

			// set values from registry platform
			if len(role.IPAddress) == 0 {
				if registry == serviceregistry.KubernetesRegistry {
					role.IPAddress = os.Getenv("INSTANCE_IP")
				} else {
					ipAddr := "127.0.0.1"
					if ok := proxy.WaitForPrivateNetwork(); ok {
						ipAddr = proxy.GetPrivateIP().String()
						log.Infof("Obtained private IP %v", ipAddr)
					}

					role.IPAddress = ipAddr
				}
			}
			if len(role.ID) == 0 {
				if registry == serviceregistry.KubernetesRegistry {
					role.ID = os.Getenv("POD_NAME") + "." + os.Getenv("POD_NAMESPACE")
				} else if registry == serviceregistry.ConsulRegistry {
					role.ID = role.IPAddress + ".service.consul"
				} else {
					role.ID = role.IPAddress
				}
			}
			pilotDomain := role.Domain
			if len(role.Domain) == 0 {
				if registry == serviceregistry.KubernetesRegistry {
					role.Domain = os.Getenv("POD_NAMESPACE") + ".svc.cluster.local"
					pilotDomain = "cluster.local"
				} else if registry == serviceregistry.ConsulRegistry {
					role.Domain = "service.consul"
				} else {
					role.Domain = ""
				}
			}

			log.Infof("Proxy role: %#v", role)

			proxyConfig := meshconfig.ProxyConfig{}

			// set all flags
			proxyConfig.AvailabilityZone = availabilityZone
			proxyConfig.CustomConfigFile = customConfigFile
			proxyConfig.ConfigPath = configPath
			proxyConfig.BinaryPath = binaryPath
			proxyConfig.ServiceCluster = serviceCluster
			proxyConfig.DrainDuration = ptypes.DurationProto(drainDuration)
			proxyConfig.ParentShutdownDuration = ptypes.DurationProto(parentShutdownDuration)
			proxyConfig.DiscoveryAddress = discoveryAddress
			proxyConfig.DiscoveryRefreshDelay = ptypes.DurationProto(discoveryRefreshDelay)
			proxyConfig.ZipkinAddress = zipkinAddress
			proxyConfig.ConnectTimeout = ptypes.DurationProto(connectTimeout)
			proxyConfig.StatsdUdpAddress = statsdUDPAddress
			proxyConfig.ProxyAdminPort = int32(proxyAdminPort)
			proxyConfig.Concurrency = int32(concurrency)

			var pilotSAN []string
			switch controlPlaneAuthPolicy {
			case meshconfig.AuthenticationPolicy_NONE.String():
				proxyConfig.ControlPlaneAuthPolicy = meshconfig.AuthenticationPolicy_NONE
			case meshconfig.AuthenticationPolicy_MUTUAL_TLS.String():
				var ns string
				proxyConfig.ControlPlaneAuthPolicy = meshconfig.AuthenticationPolicy_MUTUAL_TLS
				if registry == serviceregistry.KubernetesRegistry {
					partDiscoveryAddress := strings.Split(discoveryAddress, ":")
					discoveryHostname := partDiscoveryAddress[0]
					parts := strings.Split(discoveryHostname, ".")
					if len(parts) == 1 {
						// namespace of pilot is not part of discovery address use
						// pod namespace e.g. istio-pilot:15005
						ns = os.Getenv("POD_NAMESPACE")
					} else {
						// namespace is found in the discovery address
						// e.g. istio-pilot.istio-system:15005
						ns = parts[1]
					}
				}
				pilotSAN = envoy.GetPilotSAN(pilotDomain, ns)
			}

			// resolve statsd address
			if proxyConfig.StatsdUdpAddress != "" {
				addr, err := proxy.ResolveAddr(proxyConfig.StatsdUdpAddress)
				if err == nil {
					proxyConfig.StatsdUdpAddress = addr
				}
				// If istio-mixer.istio-system can't be resolved, skip generating the statsd config.
				// (instead of crashing). Mixer is optional.
			}

			if err := model.ValidateProxyConfig(&proxyConfig); err != nil {
				return err
			}

			if out, err := model.ToYAML(&proxyConfig); err != nil {
				log.Infof("Failed to serialize to YAML: %v", err)
			} else {
				log.Infof("Effective config: %s", out)
			}

			certs := []envoy.CertSource{
				{
					Directory: model.AuthCertsPath,
					Files:     []string{model.CertChainFilename, model.KeyFilename, model.RootCertFilename},
				},
			}

			if role.Type == model.Ingress {
				certs = append(certs, envoy.CertSource{
					Directory: model.IngressCertsPath,
					Files:     []string{model.IngressCertFilename, model.IngressKeyFilename},
				})
			}

			log.Infof("Monitored certs: %#v", certs)

			if templateFile != "" && proxyConfig.CustomConfigFile == "" {
				opts := make(map[string]string)
				opts["PodName"] = os.Getenv("POD_NAME")
				opts["PodNamespace"] = os.Getenv("POD_NAMESPACE")
				if proxyConfig.ControlPlaneAuthPolicy == meshconfig.AuthenticationPolicy_MUTUAL_TLS {
					opts["ControlPlaneAuth"] = "enable"
				}
				if disableInternalTelemetry {
					opts["DisableReportCalls"] = "true"
				}
				tmpl, err := template.ParseFiles(templateFile)
				if err != nil {
					return err
				}
				var buffer bytes.Buffer
				err = tmpl.Execute(&buffer, opts)
				if err != nil {
					return err
				}
				content := buffer.Bytes()
				log.Infof("Static config:\n%s", string(content))
				proxyConfig.CustomConfigFile = proxyConfig.ConfigPath + "/envoy.yaml"
				err = ioutil.WriteFile(proxyConfig.CustomConfigFile, content, 0644)
				if err != nil {
					return err
				}
			}

			var envoyProxy proxy.Proxy
			if bootstrapv2 {
				// Using a different constructor - the code will likely be refactored / split from the v1,
				// but may expose same interface to minimize risks
				envoyProxy = envoy.NewV2Proxy(proxyConfig, role.ServiceNode(), proxyLogLevel, pilotSAN)
			} else {
				envoyProxy = envoy.NewProxy(proxyConfig, role.ServiceNode(), proxyLogLevel)
			}
			agent := proxy.NewAgent(envoyProxy, proxy.DefaultRetry)
			watcher := envoy.NewWatcher(proxyConfig, agent, role, certs, pilotSAN)
			ctx, cancel := context.WithCancel(context.Background())
			go watcher.Run(ctx)

			stop := make(chan struct{})
			cmd.WaitSignal(stop)
			<-stop
			cancel()
			return nil
		},
	}
)

func timeDuration(dur *duration.Duration) time.Duration {
	out, err := ptypes.Duration(dur)
	if err != nil {
		log.Warna(err)
	}
	return out
}

func init() {
	proxyCmd.PersistentFlags().StringVar((*string)(&registry), "serviceregistry",
		string(serviceregistry.KubernetesRegistry),
		fmt.Sprintf("Select the platform for service registry, options are {%s, %s, %s, %s, %s}",
			serviceregistry.KubernetesRegistry, serviceregistry.ConsulRegistry, serviceregistry.EurekaRegistry,
			serviceregistry.CloudFoundryRegistry, serviceregistry.MockRegistry))
	proxyCmd.PersistentFlags().StringVar(&role.IPAddress, "ip", "",
		"Proxy IP address. If not provided uses ${INSTANCE_IP} environment variable.")
	proxyCmd.PersistentFlags().StringVar(&role.ID, "id", "",
		"Proxy unique ID. If not provided uses ${POD_NAME}.${POD_NAMESPACE} from environment variables")
	proxyCmd.PersistentFlags().StringVar(&role.Domain, "domain", "",
		"DNS domain suffix. If not provided uses ${POD_NAMESPACE}.svc.cluster.local")

	// Flags for proxy configuration
	values := model.DefaultProxyConfig()
	proxyCmd.PersistentFlags().StringVar(&configPath, "configPath", values.ConfigPath,
		"Path to the generated configuration file directory")
	proxyCmd.PersistentFlags().StringVar(&binaryPath, "binaryPath", values.BinaryPath,
		"Path to the proxy binary")
	proxyCmd.PersistentFlags().StringVar(&serviceCluster, "serviceCluster", values.ServiceCluster,
		"Service cluster")
	proxyCmd.PersistentFlags().StringVar(&availabilityZone, "availabilityZone", values.AvailabilityZone,
		"Availability zone")
	proxyCmd.PersistentFlags().DurationVar(&drainDuration, "drainDuration",
		timeDuration(values.DrainDuration),
		"The time in seconds that Envoy will drain connections during a hot restart")
	proxyCmd.PersistentFlags().DurationVar(&parentShutdownDuration, "parentShutdownDuration",
		timeDuration(values.ParentShutdownDuration),
		"The time in seconds that Envoy will wait before shutting down the parent process during a hot restart")
	proxyCmd.PersistentFlags().StringVar(&discoveryAddress, "discoveryAddress", values.DiscoveryAddress,
		"Address of the discovery service exposing xDS (e.g. istio-pilot:8080)")
	proxyCmd.PersistentFlags().DurationVar(&discoveryRefreshDelay, "discoveryRefreshDelay",
		timeDuration(values.DiscoveryRefreshDelay),
		"Polling interval for service discovery (used by EDS, CDS, LDS, but not RDS)")
	proxyCmd.PersistentFlags().StringVar(&zipkinAddress, "zipkinAddress", values.ZipkinAddress,
		"Address of the Zipkin service (e.g. zipkin:9411)")
	proxyCmd.PersistentFlags().DurationVar(&connectTimeout, "connectTimeout",
		timeDuration(values.ConnectTimeout),
		"Connection timeout used by Envoy for supporting services")
	proxyCmd.PersistentFlags().StringVar(&statsdUDPAddress, "statsdUdpAddress", values.StatsdUdpAddress,
		"IP Address and Port of a statsd UDP listener (e.g. 10.75.241.127:9125)")
	proxyCmd.PersistentFlags().IntVar(&proxyAdminPort, "proxyAdminPort", int(values.ProxyAdminPort),
		"Port on which Envoy should listen for administrative commands")
	proxyCmd.PersistentFlags().StringVar(&controlPlaneAuthPolicy, "controlPlaneAuthPolicy",
		values.ControlPlaneAuthPolicy.String(), "Control Plane Authentication Policy")
	proxyCmd.PersistentFlags().StringVar(&customConfigFile, "customConfigFile", values.CustomConfigFile,
		"Path to the custom configuration file")
	// Log levels are provided by the library https://github.com/gabime/spdlog, used by Envoy.
	proxyCmd.PersistentFlags().StringVar(&proxyLogLevel, "proxyLogLevel", "info",
		fmt.Sprintf("The log level used to start the Envoy proxy (choose from {%s, %s, %s, %s, %s, %s, %s})",
			"trace", "debug", "info", "warn", "err", "critical", "off"))
	proxyCmd.PersistentFlags().IntVar(&concurrency, "concurrency", int(values.Concurrency),
		"number of worker threads to run")
	proxyCmd.PersistentFlags().BoolVar(&bootstrapv2, "bootstrapv2", true,
		"Use bootstrap v2")
	proxyCmd.PersistentFlags().StringVar(&templateFile, "templateFile", "",
		"Go template bootstrap config")
	proxyCmd.PersistentFlags().BoolVar(&disableInternalTelemetry, "disableInternalTelemetry", false,
		"Disable internal telemetry")

	// Attach the Istio logging options to the command.
	loggingOptions.AttachCobraFlags(rootCmd)

	cmd.AddFlags(rootCmd)

	rootCmd.AddCommand(proxyCmd)
	rootCmd.AddCommand(version.CobraCommand())

	rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{
		Title:   "Istio Pilot Agent",
		Section: "pilot-agent CLI",
		Manual:  "Istio Pilot Agent",
	}))
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		log.Errora(err)
		os.Exit(-1)
	}
}
<commit_msg>Change envoy to default to log level warn #5559 (#5597)<commit_after>// Copyright 2017 Istio Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"bytes"
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"text/template"
	"time"

	"github.com/golang/protobuf/ptypes"
	"github.com/golang/protobuf/ptypes/duration"
	"github.com/spf13/cobra"
	"github.com/spf13/cobra/doc"

	meshconfig "istio.io/api/mesh/v1alpha1"
	"istio.io/istio/pilot/pkg/model"
	"istio.io/istio/pilot/pkg/proxy"
	envoy "istio.io/istio/pilot/pkg/proxy/envoy/v1"
	"istio.io/istio/pilot/pkg/serviceregistry"
	"istio.io/istio/pkg/cmd"
	"istio.io/istio/pkg/collateral"
	"istio.io/istio/pkg/log"
	"istio.io/istio/pkg/version"
)

var (
	role     model.Proxy
	registry serviceregistry.ServiceRegistry

	// proxy config flags (named identically)
	configPath               string
	binaryPath               string
	serviceCluster           string
	availabilityZone         string
	drainDuration            time.Duration
	parentShutdownDuration   time.Duration
	discoveryAddress         string
	discoveryRefreshDelay    time.Duration
	zipkinAddress            string
	connectTimeout           time.Duration
	statsdUDPAddress         string
	proxyAdminPort           int
	controlPlaneAuthPolicy   string
	customConfigFile         string
	proxyLogLevel            string
	concurrency              int
	bootstrapv2              bool
	templateFile             string
	disableInternalTelemetry bool

	loggingOptions = log.DefaultOptions()

	rootCmd = &cobra.Command{
		Use:   "pilot-agent",
		Short: "Istio Pilot agent",
		Long:  "Istio Pilot agent runs in the side car or gateway container and bootstraps envoy.",
	}

	proxyCmd = &cobra.Command{
		Use:   "proxy",
		Short: "Envoy proxy agent",
		RunE: func(c *cobra.Command, args []string) error {
			if err := log.Configure(loggingOptions); err != nil {
				return err
			}
			log.Infof("Version %s", version.Info.String())
			role.Type = model.Sidecar
			if len(args) > 0 {
				role.Type = model.NodeType(args[0])
			}

			// set values from registry platform
			if len(role.IPAddress) == 0 {
				if registry == serviceregistry.KubernetesRegistry {
					role.IPAddress = os.Getenv("INSTANCE_IP")
				} else {
					ipAddr := "127.0.0.1"
					if ok := proxy.WaitForPrivateNetwork(); ok {
						ipAddr = proxy.GetPrivateIP().String()
						log.Infof("Obtained private IP %v", ipAddr)
					}

					role.IPAddress = ipAddr
				}
			}
			if len(role.ID) == 0 {
				if registry == serviceregistry.KubernetesRegistry {
					role.ID = os.Getenv("POD_NAME") + "." + os.Getenv("POD_NAMESPACE")
				} else if registry == serviceregistry.ConsulRegistry {
					role.ID = role.IPAddress + ".service.consul"
				} else {
					role.ID = role.IPAddress
				}
			}
			pilotDomain := role.Domain
			if len(role.Domain) == 0 {
				if registry == serviceregistry.KubernetesRegistry {
					role.Domain = os.Getenv("POD_NAMESPACE") + ".svc.cluster.local"
					pilotDomain = "cluster.local"
				} else if registry == serviceregistry.ConsulRegistry {
					role.Domain = "service.consul"
				} else {
					role.Domain = ""
				}
			}

			log.Infof("Proxy role: %#v", role)

			proxyConfig := meshconfig.ProxyConfig{}

			// set all flags
			proxyConfig.AvailabilityZone = availabilityZone
			proxyConfig.CustomConfigFile = customConfigFile
			proxyConfig.ConfigPath = configPath
			proxyConfig.BinaryPath = binaryPath
			proxyConfig.ServiceCluster = serviceCluster
			proxyConfig.DrainDuration = ptypes.DurationProto(drainDuration)
			proxyConfig.ParentShutdownDuration = ptypes.DurationProto(parentShutdownDuration)
			proxyConfig.DiscoveryAddress = discoveryAddress
			proxyConfig.DiscoveryRefreshDelay = ptypes.DurationProto(discoveryRefreshDelay)
			proxyConfig.ZipkinAddress = zipkinAddress
			proxyConfig.ConnectTimeout = ptypes.DurationProto(connectTimeout)
			proxyConfig.StatsdUdpAddress = statsdUDPAddress
			proxyConfig.ProxyAdminPort = int32(proxyAdminPort)
			proxyConfig.Concurrency = int32(concurrency)

			var pilotSAN []string
			switch controlPlaneAuthPolicy {
			case meshconfig.AuthenticationPolicy_NONE.String():
				proxyConfig.ControlPlaneAuthPolicy = meshconfig.AuthenticationPolicy_NONE
			case meshconfig.AuthenticationPolicy_MUTUAL_TLS.String():
				var ns string
				proxyConfig.ControlPlaneAuthPolicy = meshconfig.AuthenticationPolicy_MUTUAL_TLS
				if registry == serviceregistry.KubernetesRegistry {
					partDiscoveryAddress := strings.Split(discoveryAddress, ":")
					discoveryHostname := partDiscoveryAddress[0]
					parts := strings.Split(discoveryHostname, ".")
					if len(parts) == 1 {
						// namespace of pilot is not part of discovery address use
						// pod namespace e.g. istio-pilot:15005
						ns = os.Getenv("POD_NAMESPACE")
					} else {
						// namespace is found in the discovery address
						// e.g. istio-pilot.istio-system:15005
						ns = parts[1]
					}
				}
				pilotSAN = envoy.GetPilotSAN(pilotDomain, ns)
			}

			// resolve statsd address
			if proxyConfig.StatsdUdpAddress != "" {
				addr, err := proxy.ResolveAddr(proxyConfig.StatsdUdpAddress)
				if err == nil {
					proxyConfig.StatsdUdpAddress = addr
				}
				// If istio-mixer.istio-system can't be resolved, skip generating the statsd config.
				// (instead of crashing). Mixer is optional.
			}

			if err := model.ValidateProxyConfig(&proxyConfig); err != nil {
				return err
			}

			if out, err := model.ToYAML(&proxyConfig); err != nil {
				log.Infof("Failed to serialize to YAML: %v", err)
			} else {
				log.Infof("Effective config: %s", out)
			}

			certs := []envoy.CertSource{
				{
					Directory: model.AuthCertsPath,
					Files:     []string{model.CertChainFilename, model.KeyFilename, model.RootCertFilename},
				},
			}

			if role.Type == model.Ingress {
				certs = append(certs, envoy.CertSource{
					Directory: model.IngressCertsPath,
					Files:     []string{model.IngressCertFilename, model.IngressKeyFilename},
				})
			}

			log.Infof("Monitored certs: %#v", certs)

			if templateFile != "" && proxyConfig.CustomConfigFile == "" {
				opts := make(map[string]string)
				opts["PodName"] = os.Getenv("POD_NAME")
				opts["PodNamespace"] = os.Getenv("POD_NAMESPACE")
				if proxyConfig.ControlPlaneAuthPolicy == meshconfig.AuthenticationPolicy_MUTUAL_TLS {
					opts["ControlPlaneAuth"] = "enable"
				}
				if disableInternalTelemetry {
					opts["DisableReportCalls"] = "true"
				}
				tmpl, err := template.ParseFiles(templateFile)
				if err != nil {
					return err
				}
				var buffer bytes.Buffer
				err = tmpl.Execute(&buffer, opts)
				if err != nil {
					return err
				}
				content := buffer.Bytes()
				log.Infof("Static config:\n%s", string(content))
				proxyConfig.CustomConfigFile = proxyConfig.ConfigPath + "/envoy.yaml"
				err = ioutil.WriteFile(proxyConfig.CustomConfigFile, content, 0644)
				if err != nil {
					return err
				}
			}

			var envoyProxy proxy.Proxy
			if bootstrapv2 {
				// Using a different constructor - the code will likely be refactored / split from the v1,
				// but may expose same interface to minimize risks
				envoyProxy = envoy.NewV2Proxy(proxyConfig, role.ServiceNode(), proxyLogLevel, pilotSAN)
			} else {
				envoyProxy = envoy.NewProxy(proxyConfig, role.ServiceNode(), proxyLogLevel)
			}
			agent := proxy.NewAgent(envoyProxy, proxy.DefaultRetry)
			watcher := envoy.NewWatcher(proxyConfig, agent, role, certs, pilotSAN)
			ctx, cancel := context.WithCancel(context.Background())
			go watcher.Run(ctx)

			stop := make(chan struct{})
			cmd.WaitSignal(stop)
			<-stop
			cancel()
			return nil
		},
	}
)

func timeDuration(dur *duration.Duration) time.Duration {
	out, err := ptypes.Duration(dur)
	if err != nil {
		log.Warna(err)
	}
	return out
}

func init() {
	proxyCmd.PersistentFlags().StringVar((*string)(&registry), "serviceregistry",
		string(serviceregistry.KubernetesRegistry),
		fmt.Sprintf("Select the platform for service registry, options are {%s, %s, %s, %s, %s}",
			serviceregistry.KubernetesRegistry, serviceregistry.ConsulRegistry, serviceregistry.EurekaRegistry,
			serviceregistry.CloudFoundryRegistry, serviceregistry.MockRegistry))
	proxyCmd.PersistentFlags().StringVar(&role.IPAddress, "ip", "",
		"Proxy IP address. If not provided uses ${INSTANCE_IP} environment variable.")
	proxyCmd.PersistentFlags().StringVar(&role.ID, "id", "",
		"Proxy unique ID. If not provided uses ${POD_NAME}.${POD_NAMESPACE} from environment variables")
	proxyCmd.PersistentFlags().StringVar(&role.Domain, "domain", "",
		"DNS domain suffix. If not provided uses ${POD_NAMESPACE}.svc.cluster.local")

	// Flags for proxy configuration
	values := model.DefaultProxyConfig()
	proxyCmd.PersistentFlags().StringVar(&configPath, "configPath", values.ConfigPath,
		"Path to the generated configuration file directory")
	proxyCmd.PersistentFlags().StringVar(&binaryPath, "binaryPath", values.BinaryPath,
		"Path to the proxy binary")
	proxyCmd.PersistentFlags().StringVar(&serviceCluster, "serviceCluster", values.ServiceCluster,
		"Service cluster")
	proxyCmd.PersistentFlags().StringVar(&availabilityZone, "availabilityZone", values.AvailabilityZone,
		"Availability zone")
	proxyCmd.PersistentFlags().DurationVar(&drainDuration, "drainDuration",
		timeDuration(values.DrainDuration),
		"The time in seconds that Envoy will drain connections during a hot restart")
	proxyCmd.PersistentFlags().DurationVar(&parentShutdownDuration, "parentShutdownDuration",
		timeDuration(values.ParentShutdownDuration),
		"The time in seconds that Envoy will wait before shutting down the parent process during a hot restart")
	proxyCmd.PersistentFlags().StringVar(&discoveryAddress, "discoveryAddress", values.DiscoveryAddress,
		"Address of the discovery service exposing xDS (e.g. istio-pilot:8080)")
	proxyCmd.PersistentFlags().DurationVar(&discoveryRefreshDelay, "discoveryRefreshDelay",
		timeDuration(values.DiscoveryRefreshDelay),
		"Polling interval for service discovery (used by EDS, CDS, LDS, but not RDS)")
	proxyCmd.PersistentFlags().StringVar(&zipkinAddress, "zipkinAddress", values.ZipkinAddress,
		"Address of the Zipkin service (e.g. zipkin:9411)")
	proxyCmd.PersistentFlags().DurationVar(&connectTimeout, "connectTimeout",
		timeDuration(values.ConnectTimeout),
		"Connection timeout used by Envoy for supporting services")
	proxyCmd.PersistentFlags().StringVar(&statsdUDPAddress, "statsdUdpAddress", values.StatsdUdpAddress,
		"IP Address and Port of a statsd UDP listener (e.g. 10.75.241.127:9125)")
	proxyCmd.PersistentFlags().IntVar(&proxyAdminPort, "proxyAdminPort", int(values.ProxyAdminPort),
		"Port on which Envoy should listen for administrative commands")
	proxyCmd.PersistentFlags().StringVar(&controlPlaneAuthPolicy, "controlPlaneAuthPolicy",
		values.ControlPlaneAuthPolicy.String(), "Control Plane Authentication Policy")
	proxyCmd.PersistentFlags().StringVar(&customConfigFile, "customConfigFile", values.CustomConfigFile,
		"Path to the custom configuration file")
	// Log levels are provided by the library https://github.com/gabime/spdlog, used by Envoy.
	proxyCmd.PersistentFlags().StringVar(&proxyLogLevel, "proxyLogLevel", "warn",
		fmt.Sprintf("The log level used to start the Envoy proxy (choose from {%s, %s, %s, %s, %s, %s, %s})",
			"trace", "debug", "info", "warn", "err", "critical", "off"))
	proxyCmd.PersistentFlags().IntVar(&concurrency, "concurrency", int(values.Concurrency),
		"number of worker threads to run")
	proxyCmd.PersistentFlags().BoolVar(&bootstrapv2, "bootstrapv2", true,
		"Use bootstrap v2")
	proxyCmd.PersistentFlags().StringVar(&templateFile, "templateFile", "",
		"Go template bootstrap config")
	proxyCmd.PersistentFlags().BoolVar(&disableInternalTelemetry, "disableInternalTelemetry", false,
		"Disable internal telemetry")

	// Attach the Istio logging options to the command.
	loggingOptions.AttachCobraFlags(rootCmd)

	cmd.AddFlags(rootCmd)

	rootCmd.AddCommand(proxyCmd)
	rootCmd.AddCommand(version.CobraCommand())

	rootCmd.AddCommand(collateral.CobraCommand(rootCmd, &doc.GenManHeader{
		Title:   "Istio Pilot Agent",
		Section: "pilot-agent CLI",
		Manual:  "Istio Pilot Agent",
	}))
}

func main() {
	if err := rootCmd.Execute(); err != nil {
		log.Errora(err)
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package flowdock

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	gitlab "github.com/plouc/go-gitlab-client"
	helper "github.com/rande/gitlab-ci-helper"
	"html/template"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type CiFlowdockStatusCommand struct {
	Ui           cli.Ui
	Verbose      bool
	Last         bool
	BuildRef     string
	BuildName    string
	BuildRefName string
	Project      string
}

func (c *CiFlowdockStatusCommand) Run(args []string) int {

	config := &FlowdockConfig{
		Server: "https://api.flowdock.com",
	}

	levels := map[string]int{
		"pending":  BUILD_PENDING,
		"running":  BUILD_RUNNING,
		"success":  BUILD_SUCCESS,
		"failed":   BUILD_FAILED,
		"canceled": BUILD_CANCELLED,
	}

	// flowdock colors: red, green, yellow, cyan, orange, grey, black, lime, purple, blue
	colors := map[int]string{
		BUILD_PENDING:   "black",
		BUILD_RUNNING:   "orange",
		BUILD_SUCCESS:   "green",
		BUILD_FAILED:    "red",
		BUILD_CANCELLED: "grey",
	}

	cmdFlags := flag.NewFlagSet("flowdock:thread", flag.ContinueOnError)
	cmdFlags.Usage = func() {
		c.Ui.Output(c.Help())
	}

	cmdFlags.BoolVar(&c.Verbose, "verbose", false, "")
	cmdFlags.BoolVar(&c.Last, "last", false, "")
	cmdFlags.StringVar(&config.Token, "token", os.Getenv("FLOWDOCK_SOURCE_TOKEN"), "The room's token (default: env var FLOWDOCK_SOURCE_TOKEN)")
	cmdFlags.StringVar(&c.BuildRef, "ref", os.Getenv("CI_BUILD_REF"), "The commit related to the build (default: env var CI_BUILD_REF)")
	cmdFlags.StringVar(&c.Project, "project", os.Getenv("CI_PROJECT_ID"), "The project related to the build (default: env var CI_PROJECT_ID)")
	cmdFlags.StringVar(&c.BuildName, "name", os.Getenv("CI_BUILD_NAME"), "The build's name (default: env var CI_BUILD_NAME)")
	cmdFlags.StringVar(&c.BuildRefName, "ref-name", os.Getenv("CI_BUILD_REF_NAME"), "The reference name (default: env var CI_BUILD_REF_NAME)")

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()

	if len(args) != 2 {
		c.Ui.Error("Invalid number of arguments\n")
		cmdFlags.Usage()

		return 1
	}

	config.Organization = args[0]
	config.Flow = args[1]

	gitlabConfig := helper.NewConfig()
	gitLabClient := gitlab.NewGitlab(gitlabConfig.Gitlab.Host, gitlabConfig.Gitlab.ApiPath, gitlabConfig.Gitlab.Token)

	project, err := helper.GetProject(c.Project, gitLabClient)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to fetch the project: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found project: %s/%s (id: %d)", project.Namespace.Name, project.Name, project.Id))

	builds, err := gitLabClient.ProjectCommitBuilds(strconv.FormatInt(int64(project.Id), 10), c.BuildRef)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	var level = 0
	var status = "-"

	activeBuilds := make(map[string]*gitlab.Build, 0)

	for _, b := range builds {
		if levels[b.Status] > level {
			level = levels[b.Status]
			status = b.Status
		}

		if ib, ok := activeBuilds[b.Name]; !ok {
			activeBuilds[b.Name] = b
		} else {
			if ib.Id > b.Id {
				activeBuilds[b.Name] = ib
			}
		}
	}

	for _, b := range activeBuilds {
		if levels[b.Status] > level {
			level = levels[b.Status]
			status = b.Status
		}
	}

	if c.Last && level == BUILD_RUNNING {
		status = "success"
		level = BUILD_SUCCESS

		activeBuilds[c.BuildName].Status = "success"
	}

	tpl := `
{{ .Project.Namespace.Name }} / {{ .Project.Name }}

Project: {{ .Project.WebUrl }}

Author: {{ .Build.Commit.Author_Name }}

Title: {{ .Build.Commit.Title }}

Builds commit: {{ .Project.WebUrl }}/commit/{{ .Build.Commit.Id }}/builds

                Status
                Name
                Stage

            {{ range .Builds }}

                        {{ $k := index $.Levels .Status }}
                        {{ $color := index $.Colors $k }}

                        ✪</style> #{{ .Id }} - {{ .Status }}

                        {{ if gt .ArtifactsFile.Size 0 }}
                             📦
                        {{ end }}

                    {{ .Name }}
                    {{ .Stage }}

            {{ end }}

`

	t, err := template.New("foo").Parse(tpl)

	body := bytes.NewBuffer([]byte(""))

	err = t.Execute(body, map[string]interface{}{
		"Builds":  activeBuilds,
		"Build":   builds[0],
		"Project": project,
		"Levels":  levels,
		"Colors":  colors,
	})

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	message := &FlowdockMessage{
		Event:      "activity",
		ExternalId: fmt.Sprintf("gitlab:%s", c.BuildRef),
		Title:      fmt.Sprintf("Update status, job:%s", c.BuildName),
		Thread: &FlowdockThread{
			Status: &FlowdockThreadStatus{
				Color: colors[level],
				Value: status,
			},
			Title:       fmt.Sprintf("Jobs for %s - %s", project.Name, c.BuildRefName),
			Body:        body.String(),
			ExternalUrl: fmt.Sprintf("%s/commit/%s/builds", project.WebUrl, c.BuildRef),
		},
		Author: &FlowdockAuthor{
			Name:   "GitlabCi",
			Avatar: "https://about.gitlab.com/images/gitlab_logo.png",
		},
	}

	message.Token = config.Token
	message.Flow = config.Flow

	buf := bytes.NewBuffer([]byte(""))
	e := json.NewEncoder(buf)

	e.Encode(message)

	client := &http.Client{}
	c.Ui.Info(buf.String())

	r, _ := http.NewRequest("POST", fmt.Sprintf("%s/flows/%s/%s/messages", config.Server, config.Organization, config.Flow), buf)
	r.Header.Add("Content-Type", "application/json")

	_, err = client.Do(r)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Err: %s", err.Error()))

		return 1
	}

	return 0
}

func (c *CiFlowdockStatusCommand) Synopsis() string {
	return "Send a message to one Flowdock's room."
}

func (c *CiFlowdockStatusCommand) Help() string {
	helpText := fmt.Sprintf(`
Usage: gitlab-ci-helper flowdock:message [options] organisation flow

  Build a flowdock thread from the current build. Information are retrieved from
  environment variables.

  The external thread id is: gitlab:sha1

  You can use the -last option to indicate that the current job is the last one.

Arguments:
  organisation        The organisation name
  flow                The flow reference

Options:
  -ref                The commit related to the build (default: env var CI_BUILD_REF)
  -project            The project related to the build (default: env var CI_PROJECT_ID)
  -name               The build's name (default: env var CI_BUILD_NAME)
  -ref-name           The reference name (default: env var CI_BUILD_REF_NAME)
  -last               Indicate if the current build is the last one
  -token              The flow's token (default: env var FLOWDOCK_SOURCE_TOKEN)
  -verbose            Add verbose information to the output

%s

Gitlab's credentials are retrieved from environment:

  GITLAB_HOST         The gitlab host
  GITLAB_TOKEN        The user's token
  GITLAB_API_PATH     (optional) the api path, default to: "/api/v3"

`, flowdockConfiguration)

	return strings.TrimSpace(helpText)
}
<commit_msg>fix(flowdock): adjust build reporting<commit_after>// Copyright © 2016 Thomas Rabaix <thomas.rabaix@gmail.com>.
//
// Use of this source code is governed by an MIT-style
// license that can be found in the LICENSE file.

package flowdock

import (
	"bytes"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/mitchellh/cli"
	gitlab "github.com/plouc/go-gitlab-client"
	helper "github.com/rande/gitlab-ci-helper"
	"html/template"
	"net/http"
	"os"
	"strconv"
	"strings"
)

type CiFlowdockStatusCommand struct {
	Ui           cli.Ui
	Verbose      bool
	Last         bool
	BuildRef     string
	BuildName    string
	BuildRefName string
	Project      string
}

func (c *CiFlowdockStatusCommand) Run(args []string) int {

	config := &FlowdockConfig{
		Server: "https://api.flowdock.com",
	}

	levels := map[string]int{
		"pending":  BUILD_PENDING,
		"running":  BUILD_RUNNING,
		"success":  BUILD_SUCCESS,
		"failed":   BUILD_FAILED,
		"canceled": BUILD_CANCELLED,
	}

	// flowdock colors: red, green, yellow, cyan, orange, grey, black, lime, purple, blue
	colors := map[int]string{
		BUILD_PENDING:   "black",
		BUILD_RUNNING:   "orange",
		BUILD_SUCCESS:   "green",
		BUILD_FAILED:    "red",
		BUILD_CANCELLED: "grey",
	}

	cmdFlags := flag.NewFlagSet("flowdock:thread", flag.ContinueOnError)
	cmdFlags.Usage = func() {
		c.Ui.Output(c.Help())
	}

	cmdFlags.BoolVar(&c.Verbose, "verbose", false, "")
	cmdFlags.BoolVar(&c.Last, "last", false, "")
	cmdFlags.StringVar(&config.Token, "token", os.Getenv("FLOWDOCK_SOURCE_TOKEN"), "The room's token (default: env var FLOWDOCK_SOURCE_TOKEN)")
	cmdFlags.StringVar(&c.BuildRef, "ref", os.Getenv("CI_BUILD_REF"), "The commit related to the build (default: env var CI_BUILD_REF)")
	cmdFlags.StringVar(&c.Project, "project", os.Getenv("CI_PROJECT_ID"), "The project related to the build (default: env var CI_PROJECT_ID)")
	cmdFlags.StringVar(&c.BuildName, "name", os.Getenv("CI_BUILD_NAME"), "The build's name (default: env var CI_BUILD_NAME)")
	cmdFlags.StringVar(&c.BuildRefName, "ref-name", os.Getenv("CI_BUILD_REF_NAME"), "The reference name (default: env var CI_BUILD_REF_NAME)")

	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	args = cmdFlags.Args()

	if len(args) != 2 {
		c.Ui.Error("Invalid number of arguments\n")
		cmdFlags.Usage()

		return 1
	}

	config.Organization = args[0]
	config.Flow = args[1]

	gitlabConfig := helper.NewConfig()
	gitLabClient := gitlab.NewGitlab(gitlabConfig.Gitlab.Host, gitlabConfig.Gitlab.ApiPath, gitlabConfig.Gitlab.Token)

	project, err := helper.GetProject(c.Project, gitLabClient)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Unable to fetch the project: %s", err.Error()))

		return 1
	}

	c.Ui.Output(fmt.Sprintf("Found project: %s/%s (id: %d)", project.Namespace.Name, project.Name, project.Id))

	builds, err := gitLabClient.ProjectCommitBuilds(strconv.FormatInt(int64(project.Id), 10), c.BuildRef)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	var level = 0
	var status = "-"

	activeBuilds := make(map[string]*gitlab.Build, 0)

	// filter builds to only get active ones
	for _, b := range builds {
		if ib, ok := activeBuilds[b.Name]; !ok {
			activeBuilds[b.Name] = b
		} else {
			if ib.Id > b.Id {
				activeBuilds[b.Name] = ib
			}
		}
	}

	// found the valid status build
	for _, b := range activeBuilds {
		if levels[b.Status] > level {
			level = levels[b.Status]
			status = b.Status
		}
	}

	// if it the last pass with no previous error, we force the final job to be green
	// the command should be the last one (reporting ...)
	if c.Last && level == BUILD_RUNNING {
		status = "success"
		level = BUILD_SUCCESS

		activeBuilds[c.BuildName].Status = "success"
	}

	tpl := `
{{ .Project.Namespace.Name }} / {{ .Project.Name }}

Project: {{ .Project.WebUrl }}

Author: {{ .Build.Commit.Author_Name }}

Title: {{ .Build.Commit.Title }}

Builds commit: {{ .Project.WebUrl }}/commit/{{ .Build.Commit.Id }}/builds

                Status
                Name
                Stage

            {{ range .Builds }}

                        {{ $k := index $.Levels .Status }}
                        {{ $color := index $.Colors $k }}

                        ✪</style> #{{ .Id }} - {{ .Status }}

                        {{ if gt .ArtifactsFile.Size 0 }}
                             📦
                        {{ end }}

                    {{ .Name }}
                    {{ .Stage }}

            {{ end }}

`

	t, err := template.New("foo").Parse(tpl)

	body := bytes.NewBuffer([]byte(""))

	err = t.Execute(body, map[string]interface{}{
		"Builds":  activeBuilds,
		"Build":   builds[0],
		"Project": project,
		"Levels":  levels,
		"Colors":  colors,
	})

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error: %s", err.Error()))

		return 1
	}

	message := &FlowdockMessage{
		Event:      "activity",
		ExternalId: fmt.Sprintf("gitlab:%s", c.BuildRef),
		Title:      fmt.Sprintf("Update status, job:%s", c.BuildName),
		Thread: &FlowdockThread{
			Status: &FlowdockThreadStatus{
				Color: colors[level],
				Value: status,
			},
			Title:       fmt.Sprintf("Jobs for %s - %s", project.Name, c.BuildRefName),
			Body:        body.String(),
			ExternalUrl: fmt.Sprintf("%s/commit/%s/builds", project.WebUrl, c.BuildRef),
		},
		Author: &FlowdockAuthor{
			Name:   "GitlabCi",
			Avatar: "https://about.gitlab.com/images/gitlab_logo.png",
		},
	}

	message.Token = config.Token
	message.Flow = config.Flow

	buf := bytes.NewBuffer([]byte(""))
	e := json.NewEncoder(buf)

	e.Encode(message)

	client := &http.Client{}
	c.Ui.Info(buf.String())

	r, _ := http.NewRequest("POST", fmt.Sprintf("%s/flows/%s/%s/messages", config.Server, config.Organization, config.Flow), buf)
	r.Header.Add("Content-Type", "application/json")

	_, err = client.Do(r)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Err: %s", err.Error()))

		return 1
	}

	return 0
}

func (c *CiFlowdockStatusCommand) Synopsis() string {
	return "Send a message to one Flowdock's room."
}

func (c *CiFlowdockStatusCommand) Help() string {
	helpText := fmt.Sprintf(`
Usage: gitlab-ci-helper flowdock:message [options] organisation flow

  Build a flowdock thread from the current build. Information are retrieved from
  environment variables.

  The external thread id is: gitlab:sha1

  You can use the -last option to indicate that the current job is the last one.

Arguments:
  organisation        The organisation name
  flow                The flow reference

Options:
  -ref                The commit related to the build (default: env var CI_BUILD_REF)
  -project            The project related to the build (default: env var CI_PROJECT_ID)
  -name               The build's name (default: env var CI_BUILD_NAME)
  -ref-name           The reference name (default: env var CI_BUILD_REF_NAME)
  -last               Indicate if the current build is the last one
  -token              The flow's token (default: env var FLOWDOCK_SOURCE_TOKEN)
  -verbose            Add verbose information to the output

%s

Gitlab's credentials are retrieved from environment:

  GITLAB_HOST         The gitlab host
  GITLAB_TOKEN        The user's token
  GITLAB_API_PATH     (optional) the api path, default to: "/api/v3"

`, flowdockConfiguration)

	return strings.TrimSpace(helpText)
}
<|endoftext|>


<commit_before>package groups

import (
	"context"
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/manicminer/hamilton/msgraph"
	"github.com/manicminer/hamilton/odata"

	"github.com/hashicorp/terraform-provider-azuread/internal/clients"
	"github.com/hashicorp/terraform-provider-azuread/internal/tf"
	"github.com/hashicorp/terraform-provider-azuread/internal/validate"
)

func groupsDataSource() *schema.Resource {
	return &schema.Resource{
		ReadContext: groupsDataSourceRead,

		Timeouts: &schema.ResourceTimeout{
			Read: schema.DefaultTimeout(5 * time.Minute),
		},

		Schema: map[string]*schema.Schema{
			"object_ids": {
				Description:  "The object IDs of the groups",
				Type:         schema.TypeList,
				Optional:     true,
				Computed:     true,
				ExactlyOneOf: []string{"display_names", "object_ids", "return_all"},
				Elem: &schema.Schema{
					Type:             schema.TypeString,
					ValidateDiagFunc: validate.UUID,
				},
			},

			"display_names": {
				Description:  "The display names of the groups",
				Type:         schema.TypeList,
				Optional:     true,
				Computed:     true,
				ExactlyOneOf: []string{"display_names", "object_ids", "return_all"},
				Elem: &schema.Schema{
					Type:             schema.TypeString,
					ValidateDiagFunc: validate.NoEmptyStrings,
				},
			},

			"mail_enabled": {
				Description:   "Whether the groups are mail-enabled",
				Type:          schema.TypeBool,
				Optional:      true,
				Computed:      true,
				ConflictsWith: []string{"object_ids"},
			},

			"return_all": {
				Description:  "Retrieve all groups with no filter",
				Type:         schema.TypeBool,
				Optional:     true,
				ExactlyOneOf: []string{"display_names", "object_ids", "return_all"},
			},

			"security_enabled": {
				Description:   "Whether the groups are security-enabled",
				Type:          schema.TypeBool,
				Optional:      true,
				Computed:      true,
				ConflictsWith: []string{"object_ids"},
			},
		},
	}
}

func groupsDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*clients.Client).Groups.GroupsClient
	client.BaseClient.DisableRetries = true

	var groups []msgraph.Group
	var expectedCount int
	var returnAll = d.Get("return_all").(bool)

	var displayNames []interface{}
	if v, ok := d.GetOk("display_names"); ok {
		displayNames = v.([]interface{})
	}

	var filter []string

	if v, ok := d.GetOkExists("mail_enabled"); ok { //nolint:staticcheck // needed to detect unset booleans
		filter = append(filter, fmt.Sprintf("mailEnabled eq %t", v.(bool)))
	}
	if v, ok := d.GetOkExists("security_enabled"); ok { //nolint:staticcheck // needed to detect unset booleans
		filter = append(filter, fmt.Sprintf("securityEnabled eq %t", v.(bool)))
	}

	if returnAll {
		result, _, err := client.List(ctx, odata.Query{Filter: strings.Join(filter, " and ")})
		if err != nil {
			return tf.ErrorDiagF(err, "Could not retrieve groups")
		}
		if result == nil {
			return tf.ErrorDiagF(errors.New("API returned nil result"), "Bad API Response")
		}
		if len(*result) == 0 {
			return tf.ErrorDiagPathF(err, "return_all", "No groups found")
		}

		groups = append(groups, *result...)
	} else if len(displayNames) > 0 {
		expectedCount = len(displayNames)
		for _, v := range displayNames {
			displayName := v.(string)
			filter = append(filter, fmt.Sprintf("displayName eq '%s'", displayName))
			query := odata.Query{Filter: strings.Join(filter, " and ")}
			result, _, err := client.List(ctx, query)
			if err != nil {
				return tf.ErrorDiagPathF(err, "display_names", "No group found with display name: %q", displayName)
			}

			count := len(*result)
			if count > 1 {
				return tf.ErrorDiagPathF(err, "display_names", "More than one group found with display name: %q", displayName)
			} else if count == 0 {
				return tf.ErrorDiagPathF(err, "display_names", "No group found with display name: %q", displayName)
			}

			groups = append(groups, (*result)[0])
		}
	} else if objectIds, ok := d.Get("object_ids").([]interface{}); ok && len(objectIds) > 0 {
		expectedCount = len(objectIds)
		for _, v := range objectIds {
			objectId := v.(string)
			group, status, err := client.Get(ctx, objectId, odata.Query{})
			if err != nil {
				if status == http.StatusNotFound {
					return tf.ErrorDiagPathF(err, "object_id", "No group found with object ID: %q", objectId)
				}
				return tf.ErrorDiagPathF(err, "object_id", "Retrieving group with object ID: %q", objectId)
			}

			groups = append(groups, *group)
		}
	}

	if !returnAll && len(groups) != expectedCount {
		return tf.ErrorDiagF(fmt.Errorf("Expected: %d, Actual: %d", expectedCount, len(groups)), "Unexpected number of groups returned")
	}

	newDisplayNames := make([]string, 0)
	newObjectIds := make([]string, 0)
	for _, group := range groups {
		if group.ID == nil {
			return tf.ErrorDiagF(errors.New("API returned group with nil object ID"), "Bad API response")
		}
		if group.DisplayName == nil {
			return tf.ErrorDiagF(errors.New("API returned group with nil displayName"), "Bad API response")
		}

		newObjectIds = append(newObjectIds, *group.ID)
		newDisplayNames = append(newDisplayNames, *group.DisplayName)
	}

	h := sha1.New()
	if _, err := h.Write([]byte(strings.Join(newDisplayNames, "-"))); err != nil {
		return tf.ErrorDiagF(err, "Unable to compute hash for names")
	}

	d.SetId("groups#" + base64.URLEncoding.EncodeToString(h.Sum(nil)))

	tf.Set(d, "object_ids", newObjectIds)
	tf.Set(d, "display_names", newDisplayNames)

	return nil
}
<commit_msg>Fix unintended append of filter<commit_after>package groups

import (
	"context"
	"crypto/sha1"
	"encoding/base64"
	"errors"
	"fmt"
	"net/http"
	"strings"
	"time"

	"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
	"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
	"github.com/manicminer/hamilton/msgraph"
	"github.com/manicminer/hamilton/odata"

	"github.com/hashicorp/terraform-provider-azuread/internal/clients"
	"github.com/hashicorp/terraform-provider-azuread/internal/tf"
	"github.com/hashicorp/terraform-provider-azuread/internal/validate"
)

func groupsDataSource() *schema.Resource {
	return &schema.Resource{
		ReadContext: groupsDataSourceRead,

		Timeouts: &schema.ResourceTimeout{
			Read: schema.DefaultTimeout(5 * time.Minute),
		},

		Schema: map[string]*schema.Schema{
			"object_ids": {
				Description:  "The object IDs of the groups",
				Type:         schema.TypeList,
				Optional:     true,
				Computed:     true,
				ExactlyOneOf: []string{"display_names", "object_ids", "return_all"},
				Elem: &schema.Schema{
					Type:             schema.TypeString,
					ValidateDiagFunc: validate.UUID,
				},
			},

			"display_names": {
				Description:  "The display names of the groups",
				Type:         schema.TypeList,
				Optional:     true,
				Computed:     true,
				ExactlyOneOf: []string{"display_names", "object_ids", "return_all"},
				Elem: &schema.Schema{
					Type:             schema.TypeString,
					ValidateDiagFunc: validate.NoEmptyStrings,
				},
			},

			"mail_enabled": {
				Description:   "Whether the groups are mail-enabled",
				Type:          schema.TypeBool,
				Optional:      true,
				Computed:      true,
				ConflictsWith: []string{"object_ids"},
			},

			"return_all": {
				Description:  "Retrieve all groups with no filter",
				Type:         schema.TypeBool,
				Optional:     true,
				ExactlyOneOf: []string{"display_names", "object_ids", "return_all"},
			},

			"security_enabled": {
				Description:   "Whether the groups are security-enabled",
				Type:          schema.TypeBool,
				Optional:      true,
				Computed:      true,
				ConflictsWith: []string{"object_ids"},
			},
		},
	}
}

func groupsDataSourceRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics {
	client := meta.(*clients.Client).Groups.GroupsClient
	client.BaseClient.DisableRetries = true

	var groups []msgraph.Group
	var expectedCount int
	var returnAll = d.Get("return_all").(bool)

	var displayNames []interface{}
	if v, ok := d.GetOk("display_names"); ok {
		displayNames = v.([]interface{})
	}

	var filter []string

	if v, ok := d.GetOkExists("mail_enabled"); ok { //nolint:staticcheck // needed to detect unset booleans
		filter = append(filter, fmt.Sprintf("mailEnabled eq %t", v.(bool)))
	}
	if v, ok := d.GetOkExists("security_enabled"); ok { //nolint:staticcheck // needed to detect unset booleans
		filter = append(filter, fmt.Sprintf("securityEnabled eq %t", v.(bool)))
	}

	if returnAll {
		result, _, err := client.List(ctx, odata.Query{Filter: strings.Join(filter, " and ")})
		if err != nil {
			return tf.ErrorDiagF(err, "Could not retrieve groups")
		}
		if result == nil {
			return tf.ErrorDiagF(errors.New("API returned nil result"), "Bad API Response")
		}
		if len(*result) == 0 {
			return tf.ErrorDiagPathF(err, "return_all", "No groups found")
		}

		groups = append(groups, *result...)
	} else if len(displayNames) > 0 {
		expectedCount = len(displayNames)
		for _, v := range displayNames {
			displayName := v.(string)
			query := odata.Query{Filter: strings.Join(append(filter, fmt.Sprintf("displayName eq '%s'", displayName)), " and ")}
			result, _, err := client.List(ctx, query)
			if err != nil {
				return tf.ErrorDiagPathF(err, "display_names", "No group found with display name: %q", displayName)
			}

			count := len(*result)
			if count > 1 {
				return tf.ErrorDiagPathF(err, "display_names", "More than one group found with display name: %q", displayName)
			} else if count == 0 {
				return tf.ErrorDiagPathF(err, "display_names", "No group found with display name: %q", displayName)
			}

			groups = append(groups, (*result)[0])
		}
	} else if objectIds, ok := d.Get("object_ids").([]interface{}); ok && len(objectIds) > 0 {
		expectedCount = len(objectIds)
		for _, v := range objectIds {
			objectId := v.(string)
			group, status, err := client.Get(ctx, objectId, odata.Query{})
			if err != nil {
				if status == http.StatusNotFound {
					return tf.ErrorDiagPathF(err, "object_id", "No group found with object ID: %q", objectId)
				}
				return tf.ErrorDiagPathF(err, "object_id", "Retrieving group with object ID: %q", objectId)
			}

			groups = append(groups, *group)
		}
	}

	if !returnAll && len(groups) != expectedCount {
		return tf.ErrorDiagF(fmt.Errorf("Expected: %d, Actual: %d", expectedCount, len(groups)), "Unexpected number of groups returned")
	}

	newDisplayNames := make([]string, 0)
	newObjectIds := make([]string, 0)
	for _, group := range groups {
		if group.ID == nil {
			return tf.ErrorDiagF(errors.New("API returned group with nil object ID"), "Bad API response")
		}
		if group.DisplayName == nil {
			return tf.ErrorDiagF(errors.New("API returned group with nil displayName"), "Bad API response")
		}

		newObjectIds = append(newObjectIds, *group.ID)
		newDisplayNames = append(newDisplayNames, *group.DisplayName)
	}

	h := sha1.New()
	if _, err := h.Write([]byte(strings.Join(newDisplayNames, "-"))); err != nil {
		return tf.ErrorDiagF(err, "Unable to compute hash for names")
	}

	d.SetId("groups#" + base64.URLEncoding.EncodeToString(h.Sum(nil)))

	tf.Set(d, "object_ids", newObjectIds)
	tf.Set(d, "display_names", newDisplayNames)

	return nil
}
<|endoftext|>


<commit_before>package kite

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"koding/newKite/kd/util"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type Install struct{}

func NewInstall() *Install {
	return &Install{}
}

func (*Install) Definition() string {
	return "Install kite from Koding repository"
}

const s3URL = "http://koding-kites.s3.amazonaws.com/"

func (*Install) Exec() error {
	flag.Parse()
	if flag.NArg() != 1 {
		return errors.New("You should give a kite name")
	}

	// Generate download URL
	kiteFullName := flag.Arg(0)
	kiteName, kiteVersion, err := splitVersion(kiteFullName, true)
	if err != nil {
		kiteName, kiteVersion = kiteFullName, "latest"
	}
	kiteURL := s3URL + kiteName + "-" + kiteVersion + ".kite.tar.gz"
	log.Println(kiteURL)

	// Make download request
	fmt.Println("Downloading...")
	res, err := http.Get(kiteURL)
	if err != nil {
		return err
	}
	defer res.Body.Close()
	if res.StatusCode == 404 {
		return errors.New("Package is not found on the server.")
	}
	if res.StatusCode != 200 {
		return fmt.Errorf("Unexpected response from server: %d", res.StatusCode)
	}

	// Extract gzip
	gz, err := gzip.NewReader(res.Body)
	if err != nil {
		return err
	}
	defer gz.Close()

	// Extract tar
	tempKitePath, err := ioutil.TempDir("", "kd-kite-install-")
	log.Println("Created temp dir:", tempKitePath)
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempKitePath)
	err = extractTar(gz, tempKitePath)
	if err != nil {
		return err
	}

	// Move kite from tmp to kites folder (~/.kd/kites)
	dirs, err := ioutil.ReadDir(tempKitePath)
	if err != nil {
		return err
	}
	if len(dirs) != 1 {
		return errors.New("Invalid package: Package must contain only one directory.")
	}
	// found prefix means we got it from extracted tar.
	// We should assert that they are expected.
	foundKiteBundleName := dirs[0].Name() // Example: asdf-1.2.3.kite
	if !strings.HasSuffix(foundKiteBundleName, ".kite") {
		return errors.New("Invalid package: Direcory name must end with \".kite\".")
	}
	foundKiteFullName := strings.TrimSuffix(foundKiteBundleName, ".kite") // Example: asdf-1.2.3
	foundKiteName, foundKiteVersion, err := splitVersion(foundKiteFullName, false)
	if err != nil {
		return errors.New("Invalid package: No version number in Kite bundle")
	}
	if foundKiteName != kiteName {
		return fmt.Errorf("Invalid package: Bundle name does not match with package name: %s != %s", foundKiteName, kiteName)
	}
	tempKitePath = filepath.Join(tempKitePath, foundKiteBundleName)
	kitesPath := filepath.Join(util.GetKdPath(), "kites")
	os.MkdirAll(kitesPath, 0700)
	kitePath := filepath.Join(kitesPath, foundKiteBundleName)
	log.Println("Moving from:", tempKitePath, "to:", kitePath)
	err = os.Rename(tempKitePath, kitePath)
	if err != nil {
		return err
	}

	fmt.Println("Installed successfully:", foundKiteName+"-"+foundKiteVersion)
	return nil
}

// extractTar reads from the io.Reader and writes the files into the directory.
func extractTar(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			return err
		}

		fi := hdr.FileInfo()
		name := fi.Name()
		path := filepath.Join(dir, name)

		// TODO make the binary under /bin executable

		if fi.IsDir() {
			os.MkdirAll(path, 0700)
		} else {
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
			if err != nil {
				return err
			}

			if _, err := io.Copy(f, tr); err != nil {
				return err
			}
		}
	}
	return nil
}

// splitVersion takes a name like "asdf-1.2.3" and
// returns the name "asdf" and version "1.2.3" seperately.
// If allowLatest is true, then the version must not be numeric and can be "latest".
func splitVersion(fullname string, allowLatest bool) (name, version string, err error) {
	notFound := errors.New("name does not contain a version number")

	parts := strings.Split(fullname, "-")
	n := len(parts)
	if n < 2 {
		return "", "", notFound
	}

	name = strings.Join(parts[:n-1], "-")
	version = parts[n-1]

	if allowLatest && version == "latest" {
		return name, version, nil
	}

	versionParts := strings.Split(version, ".")
	for _, v := range versionParts {
		if _, err := strconv.ParseUint(v, 10, 64); err != nil {
			return "", "", notFound
		}
	}

	return name, version, nil
}
<commit_msg>refactor kd kite install command<commit_after>package kite

import (
	"archive/tar"
	"compress/gzip"
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"koding/newKite/kd/util"
	"log"
	"net/http"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

type Install struct{}

func NewInstall() *Install {
	return &Install{}
}

func (*Install) Definition() string {
	return "Install kite from Koding repository"
}

const S3URL = "http://koding-kites.s3.amazonaws.com/"

func (*Install) Exec() error {
	// Parse kite name
	flag.Parse()
	if flag.NArg() != 1 {
		return errors.New("You should give a kite name")
	}
	kiteFullName := flag.Arg(0)
	kiteName, kiteVersion, err := splitVersion(kiteFullName, true)
	if err != nil {
		kiteName, kiteVersion = kiteFullName, "latest"
	}

	// Make download request
	fmt.Println("Downloading...")
	targz, err := requestPackage(kiteName, kiteVersion)
	if err != nil {
		return err
	}
	defer targz.Close()

	// Extract gzip
	gz, err := gzip.NewReader(targz)
	if err != nil {
		return err
	}
	defer gz.Close()

	// Extract tar
	tempKitePath, err := ioutil.TempDir("", "kd-kite-install-")
	log.Println("Created temp dir:", tempKitePath)
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempKitePath)
	err = extractTar(gz, tempKitePath)
	if err != nil {
		return err
	}

	// Move kite from tmp to kites folder (~/.kd/kites)
	kiteFullName, err = moveFromTempToHome(kiteName, tempKitePath)
	if err != nil {
		return err
	}

	fmt.Println("Installed successfully:", kiteFullName)
	return nil
}

// requestPackage makes a request to the kite repository and returns
// a io.ReadCloser. The caller must close the returned io.ReadCloser.
func requestPackage(kiteName, kiteVersion string) (io.ReadCloser, error) {
	kiteURL := S3URL + kiteName + "-" + kiteVersion + ".kite.tar.gz"
	log.Println(kiteURL)

	res, err := http.Get(kiteURL)
	if err != nil {
		return nil, err
	}
	if res.StatusCode == 404 {
		res.Body.Close()
		return nil, errors.New("Package is not found on the server.")
	}
	if res.StatusCode != 200 {
		res.Body.Close()
		return nil, fmt.Errorf("Unexpected response from server: %d", res.StatusCode)
	}
	return res.Body, nil
}

// extractTar reads from the io.Reader and writes the files into the directory.
func extractTar(r io.Reader, dir string) error {
	tr := tar.NewReader(r)
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		}
		if err != nil {
			return err
		}

		fi := hdr.FileInfo()
		name := fi.Name()
		path := filepath.Join(dir, name)

		// TODO make the binary under /bin executable

		if fi.IsDir() {
			os.MkdirAll(path, 0700)
		} else {
			f, err := os.OpenFile(path, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, 0600)
			if err != nil {
				return err
			}

			if _, err := io.Copy(f, tr); err != nil {
				return err
			}
		}
	}
	return nil
}

// moveFromTempToHome make some assertions about the bundle extracted from
// package, then it moves the .kite bundle into ~/kd/kites.
// Returns the full kite name moved.
func moveFromTempToHome(kiteName, tempKitePath string) (string, error) {
	dirs, err := ioutil.ReadDir(tempKitePath)
	if err != nil {
		return "", err
	}
	if len(dirs) != 1 {
		return "", errors.New("Invalid package: Package must contain only one directory.")
	}
	// found prefix means we got it from extracted tar.
	// We should assert that they are expected.
	foundKiteBundleName := dirs[0].Name() // Example: asdf-1.2.3.kite
	if !strings.HasSuffix(foundKiteBundleName, ".kite") {
		return "", errors.New("Invalid package: Direcory name must end with \".kite\".")
	}
	foundKiteFullName := strings.TrimSuffix(foundKiteBundleName, ".kite") // Example: asdf-1.2.3
	foundKiteName, _, err := splitVersion(foundKiteFullName, false)
	if err != nil {
		return "", errors.New("Invalid package: No version number in Kite bundle")
	}
	if foundKiteName != kiteName {
		return "", fmt.Errorf("Invalid package: Bundle name does not match with package name: %s != %s", foundKiteName, kiteName)
	}
	tempKitePath = filepath.Join(tempKitePath, foundKiteBundleName)
	kitesPath := filepath.Join(util.GetKdPath(), "kites")
	os.MkdirAll(kitesPath, 0700)
	kitePath := filepath.Join(kitesPath, foundKiteBundleName)
	log.Println("Moving from:", tempKitePath, "to:", kitePath)
	err = os.Rename(tempKitePath, kitePath)
	if err != nil {
		return "", err
	}
	return foundKiteFullName, nil
}

// splitVersion takes a name like "asdf-1.2.3" and
// returns the name "asdf" and version "1.2.3" seperately.
// If allowLatest is true, then the version must not be numeric and can be "latest".
func splitVersion(fullname string, allowLatest bool) (name, version string, err error) {
	notFound := errors.New("name does not contain a version number")

	parts := strings.Split(fullname, "-")
	n := len(parts)
	if n < 2 {
		return "", "", notFound
	}

	name = strings.Join(parts[:n-1], "-")
	version = parts[n-1]

	if allowLatest && version == "latest" {
		return name, version, nil
	}

	versionParts := strings.Split(version, ".")
	for _, v := range versionParts {
		if _, err := strconv.ParseUint(v, 10, 64); err != nil {
			return "", "", notFound
		}
	}

	return name, version, nil
}
<|endoftext|>