<commit_before>package tests

import (
	"bytes"
	"fmt"
	"github.com/aryann/difflib"
	"html/template"
	"log"
	"net/http"
	"strings"
)

type ResultType int

const (
	Failed ResultType = iota
	Skipped
	Success
)

func (self *Report) AddError(err interface{}) {
	if self.Result != Failed {
		//self.messages = append(self.messages, fmt.Sprint(err))
		self.Result = Failed
	}
}

func (self *Report) Skip(message string) {
	self.SkippedBy = append(self.SkippedBy, message)
	self.Result = Skipped
}

func (self *Report) Checkpoint(message string) bool {
	if self.Result == Failed {
		return false
	}
	self.Pass = append(self.Pass, ReportPass{Name: message})
	log.Println("  " + message)
	return true
}

func (self *Report) RoundTrip(req *http.Request, res *http.Response, example *http.Response, delta *[]difflib.DiffRecord) {
	if self.Result == Failed {
		return
	}
	msg := ""
	if delta != nil {
		msg += "!!! ERROR:\n"
		msg += DeltaToText(*delta)
		if !strings.HasSuffix(msg, "\n") {
			msg += "\n"
		}
	}
	msg += ">>> REQUEST:\n"
	msg += RequestToText(req)
	if res != nil {
		msg += "<<< ACTUAL RESPONSE:\n"
		msg += ResponseToText(res)
	}
	if delta != nil {
		if example != nil {
			msg += "<<< EXPECTED RESPONSE EXAMPLE:\n"
			msg += ResponseToText(example)
		}
		self.Result = Failed
	}
	if len(self.Pass) == 0 {
		self.Pass = []ReportPass{{Name: ""}}
	}
	pass := &self.Pass[len(self.Pass)-1]

	reportMessage := ReportMessage{
		Url:      req.URL.String(),
		Request:  RequestInfo(req),
		Response: ResponseInfo(res),
		Example:  ResponseInfo(example),
	}
	if delta != nil {
		reportMessage.Delta = template.HTML(DeltaToHtml(*delta))
	}
	pass.Messages = append(pass.Messages, reportMessage)
}

type Report struct {
	Checker   Checker
	Pass      []ReportPass
	SkippedBy []string
	Result    ResultType
}

type ReportPass struct {
	Name     string
	Messages []ReportMessage
}

type ReportHttp struct {
	Title  string
	Header http.Header
	Body   string
}

type ReportMessage struct {
	Url      string
	Delta    template.HTML
	Request  *ReportHttp
	Response *ReportHttp
	Example  *ReportHttp
}

func DeltaToText(delta []difflib.DiffRecord) string {
	result := make([]string, len(delta))
	for i, item := range delta {
		switch item.Delta {
		case difflib.LeftOnly:
			result[i] = Colorize(31, item.String())
		case difflib.RightOnly:
			result[i] = Colorize(32, item.String())
		default:
			result[i] = item.String()
		}
	}
	return strings.Join(result, "\n")
}

func DeltaToHtml(delta []difflib.DiffRecord) string {
	buf := bytes.NewBufferString("")
	i, j := 0, 0
	for _, d := range delta {
		buf.WriteString(`<tr><td class="line-num">`)
		if d.Delta == difflib.Common || d.Delta == difflib.LeftOnly {
			i++
			fmt.Fprintf(buf, "%d	<td", i)
			if d.Delta == difflib.LeftOnly {
				fmt.Fprint(buf, ` class="deleted"`)
			}
			fmt.Fprintf(buf, ">
%s
", d.Payload)
		} else {
			buf.WriteString("	")
		}
		buf.WriteString("	<td")
		if d.Delta == difflib.Common || d.Delta == difflib.RightOnly {
			j++
			if d.Delta == difflib.RightOnly {
				fmt.Fprint(buf, ` class="added"`)
			}
			fmt.Fprintf(buf, `><pre>%s</pre></td><td class="line-num">%d`, d.Payload, j)
		} else {
			buf.WriteString(">	")
		}
		buf.WriteString("
\n")
	}
	return buf.String()
}

func (self *Report) Show() {
	/*for _, message := range self.messages {
		log.Println(message)
	}*/
}

func RequestInfo(req *http.Request) *ReportHttp {
	context, err := GetBody(&req.Body)
	body := ""
	if err == nil {
		body += string(context)
	}
	if len(body) > 0 && !strings.HasSuffix(body, "\n") {
		body += "\n"
	}
	return &ReportHttp{
		Title:  req.Method + " " + req.URL.String() + " " + req.Proto,
		Header: req.Header,
		Body:   body,
	}
}

func ResponseInfo(res *http.Response) *ReportHttp {
	if res == nil {
		return nil
	}
	context, err := GetBody(&res.Body)
	body := ""
	if err == nil {
		body += string(context)
	}
	if len(body) > 0 && !strings.HasSuffix(body, "\n") {
		body += "\n"
	}
	return &ReportHttp{
		Title:  res.Proto + " " + res.Status,
		Header: res.Header,
		Body:   body,
	}
}

func RequestToText(req *http.Request) string {
	msg := req.Method + " " + req.URL.String() + " " + req.Proto + "\n"
	for key, vals := range req.Header {
		for _, val := range vals {
			msg += key + ": " + val + "\n"
		}
	}
	msg += "\n"

	body, err := GetBody(&req.Body)
	if err == nil {
		msg += string(body)
	}
	if !strings.HasSuffix(msg, "\n") {
		msg += "\n"
	}
	return msg
}

func ResponseToText(res *http.Response) string {
	msg := res.Proto + " " + res.Status + "\n"
	for key, vals := range res.Header {
		for _, val := range vals {
			msg += key + ": " + val + "\n"
		}
	}
	msg += "\n"

	body, err := GetBody(&res.Body)
	if err == nil {
		msg += string(body)
	}
	if !strings.HasSuffix(msg, "\n") {
		msg += "\n"
	}
	return msg
}
<commit_msg>Немного порефакторил код<commit_after>package tests

import (
	"bytes"
	"fmt"
	"github.com/aryann/difflib"
	"html/template"
	"log"
	"net/http"
	"strings"
)

type ResultType int

const (
	Failed ResultType = iota
	Skipped
	Success
)

func (self *Report) AddError(err interface{}) {
	if self.Result != Failed {
		//self.messages = append(self.messages, fmt.Sprint(err))
		self.Result = Failed
	}
}

func (self *Report) Skip(message string) {
	self.SkippedBy = append(self.SkippedBy, message)
	self.Result = Skipped
}

func (self *Report) Checkpoint(message string) bool {
	if self.Result == Failed {
		return false
	}
	self.Pass = append(self.Pass, ReportPass{Name: message})
	log.Println("  " + message)
	return true
}

func (self *Report) RoundTrip(req *http.Request, res *http.Response, example *http.Response, delta *[]difflib.DiffRecord) {
	if self.Result == Failed {
		return
	}

	reportMessage := ReportMessage{
		Url:      req.URL.String(),
		Request:  RequestInfo(req),
		Response: ResponseInfo(res),
		Example:  ResponseInfo(example),
	}
	if delta != nil {
		reportMessage.Delta = template.HTML(DeltaToHtml(*delta))
	}

	msg := ""
	if delta != nil {
		msg += "!!! ERROR:\n"
		msg += DeltaToText(*delta)
		if !strings.HasSuffix(msg, "\n") {
			msg += "\n"
		}
	}
	msg += ">>> REQUEST:\n"
	msg += reportMessage.Request.String()
	if res != nil {
		msg += "<<< ACTUAL RESPONSE:\n"
		msg += reportMessage.Response.String()
	}
	if delta != nil {
		if example != nil {
			msg += "<<< EXPECTED RESPONSE EXAMPLE:\n"
			msg += reportMessage.Example.String()
		}
		self.Result = Failed
	}
	// Добавляем сообщение в отчет
	if len(self.Pass) == 0 {
		self.Pass = []ReportPass{{Name: ""}}
	}
	pass := &self.Pass[len(self.Pass)-1]
	pass.Messages = append(pass.Messages, reportMessage)
}

type Report struct {
	Checker   Checker
	Pass      []ReportPass
	SkippedBy []string
	Result    ResultType
}

type ReportPass struct {
	Name     string
	Messages []ReportMessage
}

type ReportHttp struct {
	Title  string
	Header http.Header
	Body   string
}

type ReportMessage struct {
	Url      string
	Delta    template.HTML
	Request  *ReportHttp
	Response *ReportHttp
	Example  *ReportHttp
}

func DeltaToText(delta []difflib.DiffRecord) string {
	result := make([]string, len(delta))
	for i, item := range delta {
		switch item.Delta {
		case difflib.LeftOnly:
			result[i] = Colorize(31, item.String())
		case difflib.RightOnly:
			result[i] = Colorize(32, item.String())
		default:
			result[i] = item.String()
		}
	}
	return strings.Join(result, "\n")
}

func DeltaToHtml(delta []difflib.DiffRecord) string {
	buf := bytes.NewBufferString("")
	i, j := 0, 0
	for _, d := range delta {
		buf.WriteString(`<tr><td class="line-num">`)
		if d.Delta == difflib.Common || d.Delta == difflib.LeftOnly {
			i++
			fmt.Fprintf(buf, "%d	<td", i)
			if d.Delta == difflib.LeftOnly {
				fmt.Fprint(buf, ` class="deleted"`)
			}
			fmt.Fprintf(buf, ">
%s
", d.Payload)
		} else {
			buf.WriteString("	")
		}
		buf.WriteString("	<td")
		if d.Delta == difflib.Common || d.Delta == difflib.RightOnly {
			j++
			if d.Delta == difflib.RightOnly {
				fmt.Fprint(buf, ` class="added"`)
			}
			fmt.Fprintf(buf, `><pre>%s</pre></td><td class="line-num">%d`, d.Payload, j)
		} else {
			buf.WriteString(">	")
		}
		buf.WriteString("
\n")
	}
	return buf.String()
}

func (self *Report) Show() {
	/*for _, message := range self.messages {
		log.Println(message)
	}*/
}

func RequestInfo(req *http.Request) *ReportHttp {
	context, err := GetBody(&req.Body)
	body := ""
	if err == nil {
		body += string(context)
	}
	if len(body) > 0 && !strings.HasSuffix(body, "\n") {
		body += "\n"
	}
	return &ReportHttp{
		Title:  req.Method + " " + req.URL.String() + " " + req.Proto,
		Header: req.Header,
		Body:   body,
	}
}

func ResponseInfo(res *http.Response) *ReportHttp {
	if res == nil {
		return nil
	}
	context, err := GetBody(&res.Body)
	if err != nil {
		panic(err)
	}
	return &ReportHttp{
		Title:  res.Proto + " " + res.Status,
		Header: res.Header,
		Body:   string(context),
	}
}

func (self *ReportHttp) String() string {
	msg := self.Title + "\n"
	for key, vals := range self.Header {
		for _, val := range vals {
			msg += key + ": " + val + "\n"
		}
	}
	msg += "\n"
	msg += self.Body
	if !strings.HasSuffix(msg, "\n") {
		msg += "\n"
	}
	return msg
}
<|endoftext|>


<commit_before>package platform

import (
	"fmt"
	"strings"

	"github.com/scrapli/scrapligo/assets"

	"github.com/scrapli/scrapligo/driver/generic"
	"github.com/scrapli/scrapligo/driver/network"
	"github.com/scrapli/scrapligo/driver/options"

	"github.com/scrapli/scrapligo/util"

	"gopkg.in/yaml.v3"
)

const (
	// AristaEos is a constant representing the platform string/name for Arista EOS devices.
	AristaEos = "arista_eos"
	// CiscoIosxe is a constant representing the platform string/name for Cisco IOSXE devices.
	CiscoIosxe = "cisco_iosxe"
	// CiscoIosxr is a constant representing the platform string/name for Cisco IOSXR devices.
	CiscoIosxr = "cisco_iosxr"
	// CiscoNxos is a constant representing the platform string/name for Cisco NXOS devices.
	CiscoNxos = "cisco_nxos"
	// JuniperJunos is a constant representing the platform string/name for Juniper JunOS devices.
	JuniperJunos = "juniper_junos"
	// NokiaSrl is a constant representing the platform string/name for Nokia SRL/SRLinux devices.
	NokiaSrl = "nokia_srl"
)

// GetPlatformNames is used to get the "core" (as in embedded in assets and used in testing)
// platform names.
func GetPlatformNames() []string {
	return []string{
		AristaEos, CiscoIosxe, CiscoIosxr, CiscoNxos, JuniperJunos, NokiaSrl,
	}
}

func loadPlatformDefinitionFromAssets(f string) ([]byte, error) {
	if !strings.HasSuffix(f, ".yaml") {
		f += ".yaml"
	}

	return assets.Assets.ReadFile(fmt.Sprintf("platforms/%s", f))
}

func loadPlatformDefinition(f string) (*Definition, error) {
	b, err := loadPlatformDefinitionFromAssets(f)
	if err != nil {
		b, err = util.ResolveAtFileOrURL(f)
		if err != nil {
			return nil, err
		}
	}

	pd := &Definition{}

	err = yaml.Unmarshal(b, pd)
	if err != nil {
		return nil, err
	}

	return pd, nil
}

func setDriver(host string, p *Platform, opts ...util.Option) error {
	finalOpts := p.AsOptions()
	finalOpts = append(finalOpts, opts...)

	var err error

	switch p.DriverType {
	case "generic":
		var d *generic.Driver

		d, err = generic.NewDriver(host, finalOpts...)
		if err != nil {
			return err
		}

		p.genericDriver = d
	case "network":
		var d *network.Driver

		d, err = network.NewDriver(host, finalOpts...)
		if err != nil {
			return err
		}

		p.networkDriver = d
	}

	return err
}

// NewPlatformVariant returns an instance of Platform from the platform definition file f. The
// provided variant data is merged back into the "base" platform definition. The host and
// any provided options are stored and will be applied when fetching the generic or network driver
// via the GetGenericDriver or GetNetworkDriver methods.
func NewPlatformVariant(f, variant, host string, opts ...util.Option) (*Platform, error) {
	pd, err := loadPlatformDefinition(f)
	if err != nil {
		return nil, err
	}

	p := pd.Default

	vp, ok := pd.Variants[variant]
	if !ok {
		return nil, fmt.Errorf("%w: no variant '%s' in platform", util.ErrPlatformError, variant)
	}

	p.mergeVariant(vp)

	err = setDriver(host, p, opts...)
	if err != nil {
		return nil, err
	}

	return p, nil
}

// NewPlatform returns an instance of Platform from the platform definition file f. The host and
// any provided options are stored and will be applied when fetching the generic or network driver
// via the GetGenericDriver or GetNetworkDriver methods.
func NewPlatform(f, host string, opts ...util.Option) (*Platform, error) {
	pd, err := loadPlatformDefinition(f)
	if err != nil {
		return nil, err
	}

	p := pd.Default

	err = setDriver(host, p, opts...)
	if err != nil {
		return nil, err
	}

	return p, nil
}

// Definition is a struct representing a JSON or YAML platform definition file.
type Definition struct {
	Default  *Platform            `json:"default"  yaml:"default"`
	Variants map[string]*Platform `json:"variants" yaml:"variants"`
}

// Platform is a struct that contains JSON or YAML data that represent the attributes required to
// create a generic or network driver to connect to a given device type.
type Platform struct {
	// DriverType generic||network
	DriverType string `yaml:"driver-type"`

	FailedWhenContains []string       `json:"failed-when-contains" yaml:"failed-when-contains"`
	OnOpen             onXDefinitions `json:"on-open"              yaml:"on-open"`
	OnClose            onXDefinitions `json:"on-close"             yaml:"on-close"`

	PrivilegeLevels              network.PrivilegeLevels `json:"privilege-levels"                yaml:"privilege-levels"`
	DefaultDesiredPrivilegeLevel string                  `json:"default-desired-privilege-level" yaml:"default-desired-privilege-level"`
	NetworkOnOpen                onXDefinitions          `json:"network-on-open"                 yaml:"network-on-open"`
	NetworkOnClose               onXDefinitions          `json:"network-on-close"                yaml:"network-on-close"`

	Options optionDefinitions `json:"options" yaml:"options"`

	genericDriver *generic.Driver
	networkDriver *network.Driver
}

func (p *Platform) mergeVariant(v *Platform) {
	if v.DriverType != "" {
		p.DriverType = v.DriverType
	}

	if len(v.FailedWhenContains) > 0 {
		p.FailedWhenContains = v.FailedWhenContains
	}

	if v.OnOpen != nil {
		p.OnOpen = v.OnOpen
	}

	if v.OnClose != nil {
		p.OnClose = v.OnClose
	}

	if len(v.PrivilegeLevels) > 0 {
		p.PrivilegeLevels = v.PrivilegeLevels
	}

	if v.DefaultDesiredPrivilegeLevel != "" {
		p.DefaultDesiredPrivilegeLevel = v.DefaultDesiredPrivilegeLevel
	}

	if v.NetworkOnOpen != nil {
		p.NetworkOnOpen = v.NetworkOnOpen
	}

	if v.NetworkOnClose != nil {
		p.NetworkOnClose = v.NetworkOnClose
	}
}

// GetGenericDriver returns an instance of generic.Driver built from the Platform data. If the
// platform data (JSON/YAML) specifies a network driver type this will return an error.
func (p *Platform) GetGenericDriver() (*generic.Driver, error) {
	if p.genericDriver == nil {
		return nil, fmt.Errorf(
			"%w: requested generic driver, but generic driver is nil",
			util.ErrPlatformError,
		)
	}

	return p.genericDriver, nil
}

// GetNetworkDriver returns an instance of network.Driver built from the Platform data. If the
// platform data (JSON/YAML) specifies a generic driver type this will return an error.
func (p *Platform) GetNetworkDriver() (*network.Driver, error) {
	if p.networkDriver == nil {
		return nil, fmt.Errorf(
			"%w: requested network driver, but network driver is nil",
			util.ErrPlatformError,
		)
	}

	return p.networkDriver, nil
}

func (p *Platform) genericOptions() []util.Option {
	opts := make([]util.Option, 0)

	if len(p.FailedWhenContains) > 0 {
		opts = append(opts, options.WithFailedWhenContains(p.FailedWhenContains))
	}

	if len(p.OnOpen) > 0 {
		opts = append(opts, options.WithOnOpen(p.OnOpen.asGenericOnX()))
	}

	if len(p.OnClose) > 0 {
		opts = append(opts, options.WithOnClose(p.OnClose.asGenericOnX()))
	}

	return opts
}

// AsOptions returns a slice of options that the platform represents.
func (p *Platform) AsOptions() []util.Option {
	opts := p.genericOptions()

	opts = append(
		opts,
		options.WithPrivilegeLevels(p.PrivilegeLevels),
		options.WithDefaultDesiredPriv(p.DefaultDesiredPrivilegeLevel),
	)

	if len(p.NetworkOnOpen) > 0 {
		opts = append(opts, options.WithNetworkOnOpen(p.NetworkOnOpen.asNetworkOnX()))
	}

	if len(p.NetworkOnClose) > 0 {
		opts = append(opts, options.WithNetworkOnClose(p.NetworkOnClose.asNetworkOnX()))
	}

	opts = append(opts, p.Options.asOptions()...)

	return opts
}
<commit_msg>accept yaml bytes or string filepath/url<commit_after>package platform

import (
	"fmt"
	"strings"

	"github.com/scrapli/scrapligo/assets"

	"github.com/scrapli/scrapligo/driver/generic"
	"github.com/scrapli/scrapligo/driver/network"
	"github.com/scrapli/scrapligo/driver/options"

	"github.com/scrapli/scrapligo/util"

	"gopkg.in/yaml.v3"
)

const (
	// AristaEos is a constant representing the platform string/name for Arista EOS devices.
	AristaEos = "arista_eos"
	// CiscoIosxe is a constant representing the platform string/name for Cisco IOSXE devices.
	CiscoIosxe = "cisco_iosxe"
	// CiscoIosxr is a constant representing the platform string/name for Cisco IOSXR devices.
	CiscoIosxr = "cisco_iosxr"
	// CiscoNxos is a constant representing the platform string/name for Cisco NXOS devices.
	CiscoNxos = "cisco_nxos"
	// JuniperJunos is a constant representing the platform string/name for Juniper JunOS devices.
	JuniperJunos = "juniper_junos"
	// NokiaSrl is a constant representing the platform string/name for Nokia SRL/SRLinux devices.
	NokiaSrl = "nokia_srl"
)

// GetPlatformNames is used to get the "core" (as in embedded in assets and used in testing)
// platform names.
func GetPlatformNames() []string {
	return []string{
		AristaEos, CiscoIosxe, CiscoIosxr, CiscoNxos, JuniperJunos, NokiaSrl,
	}
}

func loadPlatformDefinitionFromAssets(f string) ([]byte, error) {
	if !strings.HasSuffix(f, ".yaml") {
		f += ".yaml"
	}

	return assets.Assets.ReadFile(fmt.Sprintf("platforms/%s", f))
}

func loadPlatformDefinition(f string) (*Definition, error) {
	b, err := loadPlatformDefinitionFromAssets(f)
	if err != nil {
		b, err = util.ResolveAtFileOrURL(f)
		if err != nil {
			return nil, err
		}
	}

	return loadPlatformDefinitionFromBytes(b)
}

func loadPlatformDefinitionFromBytes(b []byte) (*Definition, error) {
	pd := &Definition{}

	err := yaml.Unmarshal(b, pd)
	if err != nil {
		return nil, err
	}

	return pd, nil
}

func setDriver(host string, p *Platform, opts ...util.Option) error {
	finalOpts := p.AsOptions()
	finalOpts = append(finalOpts, opts...)

	var err error

	switch p.DriverType {
	case "generic":
		var d *generic.Driver

		d, err = generic.NewDriver(host, finalOpts...)
		if err != nil {
			return err
		}

		p.genericDriver = d
	case "network":
		var d *network.Driver

		d, err = network.NewDriver(host, finalOpts...)
		if err != nil {
			return err
		}

		p.networkDriver = d
	}

	return err
}

// NewPlatformVariant returns an instance of Platform from the platform definition f where f may be
// a string representing a filepath or URL, or a byte slice of an already loaded YAML definition.
// The provided variant data is merged back into the "base" platform definition. The host and
// any provided options are stored and will be applied when fetching the generic or network driver
// via the GetGenericDriver or GetNetworkDriver methods.
func NewPlatformVariant(
	f interface{},
	variant, host string,
	opts ...util.Option,
) (*Platform, error) {
	var pd *Definition

	var err error

	switch t := f.(type) {
	case string:
		pd, err = loadPlatformDefinition(t)
	case []byte:
		pd, err = loadPlatformDefinitionFromBytes(t)
	}

	if err != nil {
		return nil, err
	}

	p := pd.Default

	vp, ok := pd.Variants[variant]
	if !ok {
		return nil, fmt.Errorf("%w: no variant '%s' in platform", util.ErrPlatformError, variant)
	}

	p.mergeVariant(vp)

	err = setDriver(host, p, opts...)
	if err != nil {
		return nil, err
	}

	return p, nil
}

// NewPlatform returns an instance of Platform from the platform definition f where f may be
// a string representing a filepath or URL, or a byte slice of an already loaded YAML definition.
// The host and any provided options are stored and will be applied when fetching the generic or
// network driver via the GetGenericDriver or GetNetworkDriver methods.
func NewPlatform(f interface{}, host string, opts ...util.Option) (*Platform, error) {
	var pd *Definition

	var err error

	switch t := f.(type) {
	case string:
		pd, err = loadPlatformDefinition(t)
	case []byte:
		pd, err = loadPlatformDefinitionFromBytes(t)
	}

	if err != nil {
		return nil, err
	}

	p := pd.Default

	err = setDriver(host, p, opts...)
	if err != nil {
		return nil, err
	}

	return p, nil
}

// Definition is a struct representing a JSON or YAML platform definition file.
type Definition struct {
	Default  *Platform            `json:"default"  yaml:"default"`
	Variants map[string]*Platform `json:"variants" yaml:"variants"`
}

// Platform is a struct that contains JSON or YAML data that represent the attributes required to
// create a generic or network driver to connect to a given device type.
type Platform struct {
	// DriverType generic||network
	DriverType string `yaml:"driver-type"`

	FailedWhenContains []string       `json:"failed-when-contains" yaml:"failed-when-contains"`
	OnOpen             onXDefinitions `json:"on-open"              yaml:"on-open"`
	OnClose            onXDefinitions `json:"on-close"             yaml:"on-close"`

	PrivilegeLevels              network.PrivilegeLevels `json:"privilege-levels"                yaml:"privilege-levels"`
	DefaultDesiredPrivilegeLevel string                  `json:"default-desired-privilege-level" yaml:"default-desired-privilege-level"`
	NetworkOnOpen                onXDefinitions          `json:"network-on-open"                 yaml:"network-on-open"`
	NetworkOnClose               onXDefinitions          `json:"network-on-close"                yaml:"network-on-close"`

	Options optionDefinitions `json:"options" yaml:"options"`

	genericDriver *generic.Driver
	networkDriver *network.Driver
}

func (p *Platform) mergeVariant(v *Platform) {
	if v.DriverType != "" {
		p.DriverType = v.DriverType
	}

	if len(v.FailedWhenContains) > 0 {
		p.FailedWhenContains = v.FailedWhenContains
	}

	if v.OnOpen != nil {
		p.OnOpen = v.OnOpen
	}

	if v.OnClose != nil {
		p.OnClose = v.OnClose
	}

	if len(v.PrivilegeLevels) > 0 {
		p.PrivilegeLevels = v.PrivilegeLevels
	}

	if v.DefaultDesiredPrivilegeLevel != "" {
		p.DefaultDesiredPrivilegeLevel = v.DefaultDesiredPrivilegeLevel
	}

	if v.NetworkOnOpen != nil {
		p.NetworkOnOpen = v.NetworkOnOpen
	}

	if v.NetworkOnClose != nil {
		p.NetworkOnClose = v.NetworkOnClose
	}
}

// GetGenericDriver returns an instance of generic.Driver built from the Platform data. If the
// platform data (JSON/YAML) specifies a network driver type this will return an error.
func (p *Platform) GetGenericDriver() (*generic.Driver, error) {
	if p.genericDriver == nil {
		return nil, fmt.Errorf(
			"%w: requested generic driver, but generic driver is nil",
			util.ErrPlatformError,
		)
	}

	return p.genericDriver, nil
}

// GetNetworkDriver returns an instance of network.Driver built from the Platform data. If the
// platform data (JSON/YAML) specifies a generic driver type this will return an error.
func (p *Platform) GetNetworkDriver() (*network.Driver, error) {
	if p.networkDriver == nil {
		return nil, fmt.Errorf(
			"%w: requested network driver, but network driver is nil",
			util.ErrPlatformError,
		)
	}

	return p.networkDriver, nil
}

func (p *Platform) genericOptions() []util.Option {
	opts := make([]util.Option, 0)

	if len(p.FailedWhenContains) > 0 {
		opts = append(opts, options.WithFailedWhenContains(p.FailedWhenContains))
	}

	if len(p.OnOpen) > 0 {
		opts = append(opts, options.WithOnOpen(p.OnOpen.asGenericOnX()))
	}

	if len(p.OnClose) > 0 {
		opts = append(opts, options.WithOnClose(p.OnClose.asGenericOnX()))
	}

	return opts
}

// AsOptions returns a slice of options that the platform represents.
func (p *Platform) AsOptions() []util.Option {
	opts := p.genericOptions()

	opts = append(
		opts,
		options.WithPrivilegeLevels(p.PrivilegeLevels),
		options.WithDefaultDesiredPriv(p.DefaultDesiredPrivilegeLevel),
	)

	if len(p.NetworkOnOpen) > 0 {
		opts = append(opts, options.WithNetworkOnOpen(p.NetworkOnOpen.asNetworkOnX()))
	}

	if len(p.NetworkOnClose) > 0 {
		opts = append(opts, options.WithNetworkOnClose(p.NetworkOnClose.asNetworkOnX()))
	}

	opts = append(opts, p.Options.asOptions()...)

	return opts
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package libvmi

import (
	"encoding/base64"

	kvirtv1 "kubevirt.io/api/core/v1"
)

// WithCloudInitNoCloudUserData adds cloud-init no-cloud user data.
func WithCloudInitNoCloudUserData(data string, b64Encoding bool) Option {
	return func(vmi *kvirtv1.VirtualMachineInstance) {
		diskName, bus := "disk1", "virtio"
		addDiskVolumeWithCloudInitNoCloud(vmi, diskName, bus)

		volume := getVolume(vmi, diskName)
		if b64Encoding {
			encodedData := base64.StdEncoding.EncodeToString([]byte(data))
			volume.CloudInitNoCloud.UserDataBase64 = encodedData
		} else {
			volume.CloudInitNoCloud.UserData = data
		}
	}
}

// WithCloudInitNoCloudNetworkData adds cloud-init no-cloud network data.
func WithCloudInitNoCloudNetworkData(data string, b64Encoding bool) Option {
	return func(vmi *kvirtv1.VirtualMachineInstance) {
		diskName, bus := "disk1", "virtio"
		addDiskVolumeWithCloudInitNoCloud(vmi, diskName, bus)

		volume := getVolume(vmi, diskName)
		if b64Encoding {
			encodedData := base64.StdEncoding.EncodeToString([]byte(data))
			volume.CloudInitNoCloud.NetworkDataBase64 = encodedData
		} else {
			volume.CloudInitNoCloud.NetworkData = data
		}
	}
}

func addDiskVolumeWithCloudInitNoCloud(vmi *kvirtv1.VirtualMachineInstance, diskName, bus string) {
	addDisk(vmi, newDisk(diskName, bus))
	v := newVolume(diskName)
	setCloudInitNoCloud(&v, &kvirtv1.CloudInitNoCloudSource{})
	addVolume(vmi, v)
}

func setCloudInitNoCloud(volume *kvirtv1.Volume, source *kvirtv1.CloudInitNoCloudSource) {
	volume.VolumeSource = kvirtv1.VolumeSource{CloudInitNoCloud: source}
}
<commit_msg>libvmi/cloudinit: allow using WithCloudInitNoCloudUserData twice<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package libvmi

import (
	"encoding/base64"

	kvirtv1 "kubevirt.io/api/core/v1"
)

// WithCloudInitNoCloudUserData adds cloud-init no-cloud user data.
func WithCloudInitNoCloudUserData(data string, b64Encoding bool) Option {
	return func(vmi *kvirtv1.VirtualMachineInstance) {
		diskName, bus := "disk1", "virtio"
		addDiskVolumeWithCloudInitNoCloud(vmi, diskName, bus)

		volume := getVolume(vmi, diskName)
		if b64Encoding {
			encodedData := base64.StdEncoding.EncodeToString([]byte(data))
			volume.CloudInitNoCloud.UserData = ""
			volume.CloudInitNoCloud.UserDataBase64 = encodedData
		} else {
			volume.CloudInitNoCloud.UserData = data
			volume.CloudInitNoCloud.UserDataBase64 = ""
		}
	}
}

// WithCloudInitNoCloudNetworkData adds cloud-init no-cloud network data.
func WithCloudInitNoCloudNetworkData(data string, b64Encoding bool) Option {
	return func(vmi *kvirtv1.VirtualMachineInstance) {
		diskName, bus := "disk1", "virtio"
		addDiskVolumeWithCloudInitNoCloud(vmi, diskName, bus)

		volume := getVolume(vmi, diskName)
		if b64Encoding {
			encodedData := base64.StdEncoding.EncodeToString([]byte(data))
			volume.CloudInitNoCloud.NetworkDataBase64 = encodedData
		} else {
			volume.CloudInitNoCloud.NetworkData = data
		}
	}
}

func addDiskVolumeWithCloudInitNoCloud(vmi *kvirtv1.VirtualMachineInstance, diskName, bus string) {
	addDisk(vmi, newDisk(diskName, bus))
	v := newVolume(diskName)
	setCloudInitNoCloud(&v, &kvirtv1.CloudInitNoCloudSource{})
	addVolume(vmi, v)
}

func setCloudInitNoCloud(volume *kvirtv1.Volume, source *kvirtv1.CloudInitNoCloudSource) {
	volume.VolumeSource = kvirtv1.VolumeSource{CloudInitNoCloud: source}
}
<|endoftext|>


<commit_before>/*
Package system runs system-level testing of rqlite. This includes testing of single nodes, and multi-node clusters.
*/
package system

import (
	"testing"
)

func Test_SingleNode(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("fiona")`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO bar(name) VALUES("fiona")`,
			expected: `{"results":[{"error":"no such table: bar"}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT blah blah`,
			expected: `{"results":[{"error":"near \"blah\": syntax error"}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"]]}]}`,
			execute:  false,
		},
		{
			stmt:     `DROP TABLE bar`,
			expected: `{"results":[{"error":"no such table: bar"}]}`,
			execute:  true,
		},
		{
			stmt:     `DROP TABLE foo`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.Execute(tt.stmt)
		} else {
			r, err = node.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}

func Test_SingleNodeMulti(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `CREATE TABLE bar (id integer not null primary key, sequence integer)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("fiona")`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("declan")`,
			expected: `{"results":[{"last_insert_id":2,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO bar(sequence) VALUES(5)`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.Execute(tt.stmt)
		} else {
			r, err = node.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}

	r, err := node.QueryMulti([]string{"SELECT * FROM foo", "SELECT * FROM bar"})
	if err != nil {
		t.Fatalf("failed to run multiple queries: %s", err.Error())
	}
	if r != `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"],[2,"declan"]]},{"columns":["id","sequence"],"types":["integer","integer"],"values":[[1,5]]}]}` {
		t.Fatalf("test received wrong result got %s", r)
	}
}

func Test_SingleParameterizedNode(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     []interface{}
		expected string
		execute  bool
	}{
		{
			stmt:     []interface{}{"CREATE TABLE foo (id integer not null primary key, name text, age integer)"},
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     []interface{}{"INSERT INTO foo(name, age) VALUES(?, ?)", "fiona", 20},
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     []interface{}{"SELECT * FROM foo WHERE NAME=?", "fiona"},
			expected: `{"results":[{"columns":["id","name","age"],"types":["integer","text","integer"],"values":[[1,"fiona",20]]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.ExecuteParameterized(tt.stmt)
		} else {
			r, err = node.QueryParameterized(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}

func Test_SingleNodeCoverage(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	// Access endpoints to ensure the code is covered.
	var err error
	var str string

	str, err = node.Status()
	if err != nil {
		t.Fatalf("failed to access status endpoint: %s", err.Error())
	}
	if !isJSON(str) {
		t.Fatalf("output from status endpoint is not valid JSON: %s", str)
	}

	str, err = node.Expvar()
	if err != nil {
		t.Fatalf("failed to access expvar endpoint: %s", err.Error())
	}
	if !isJSON(str) {
		t.Fatalf("output from expvar endpoint is not valid JSON: %s", str)
	}
}
<commit_msg>Add SQL injection demonstration tests<commit_after>/*
Package system runs system-level testing of rqlite. This includes testing of single nodes, and multi-node clusters.
*/
package system

import (
	"fmt"
	"testing"
)

func Test_SingleNode(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("fiona")`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO bar(name) VALUES("fiona")`,
			expected: `{"results":[{"error":"no such table: bar"}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT blah blah`,
			expected: `{"results":[{"error":"near \"blah\": syntax error"}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"]]}]}`,
			execute:  false,
		},
		{
			stmt:     `DROP TABLE bar`,
			expected: `{"results":[{"error":"no such table: bar"}]}`,
			execute:  true,
		},
		{
			stmt:     `DROP TABLE foo`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.Execute(tt.stmt)
		} else {
			r, err = node.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}

func Test_SingleNodeMulti(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `CREATE TABLE bar (id integer not null primary key, sequence integer)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("fiona")`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO foo(name) VALUES("declan")`,
			expected: `{"results":[{"last_insert_id":2,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     `INSERT INTO bar(sequence) VALUES(5)`,
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.Execute(tt.stmt)
		} else {
			r, err = node.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}

	r, err := node.QueryMulti([]string{"SELECT * FROM foo", "SELECT * FROM bar"})
	if err != nil {
		t.Fatalf("failed to run multiple queries: %s", err.Error())
	}
	if r != `{"results":[{"columns":["id","name"],"types":["integer","text"],"values":[[1,"fiona"],[2,"declan"]]},{"columns":["id","sequence"],"types":["integer","integer"],"values":[[1,5]]}]}` {
		t.Fatalf("test received wrong result got %s", r)
	}
}

func Test_SingleParameterizedNode(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     []interface{}
		expected string
		execute  bool
	}{
		{
			stmt:     []interface{}{"CREATE TABLE foo (id integer not null primary key, name text, age integer)"},
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     []interface{}{"INSERT INTO foo(name, age) VALUES(?, ?)", "fiona", 20},
			expected: `{"results":[{"last_insert_id":1,"rows_affected":1}]}`,
			execute:  true,
		},
		{
			stmt:     []interface{}{"SELECT * FROM foo WHERE NAME=?", "fiona"},
			expected: `{"results":[{"columns":["id","name","age"],"types":["integer","text","integer"],"values":[[1,"fiona",20]]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.ExecuteParameterized(tt.stmt)
		} else {
			r, err = node.QueryParameterized(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}

// Test_SingleNodeSQLInjection demonstrates that using the non-parameterized API is vulnerable to
// SQL injection attacks.
func Test_SingleNodeSQLInjection(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     string
		expected string
		execute  bool
	}{
		{
			stmt:     `CREATE TABLE foo (id integer not null primary key, name text)`,
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     `SELECT * FROM foo WHERE name="baz"`,
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"]}]}`,
			execute:  false,
		},
		{
			stmt:     fmt.Sprintf(`SELECT * FROM foo WHERE name=%s`, `"baz";DROP TABLE FOO`),
			expected: `{"results":[{}]}`,
			execute:  false,
		},
		{
			stmt:     `SELECT * FROM foo`,
			expected: `{"results":[{"error":"no such table: foo"}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.Execute(tt.stmt)
		} else {
			r, err = node.Query(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}

// Test_SingleNodeNoSQLInjection demonstrates that using the parameterized API protects
// against SQL injection attacks.
func Test_SingleNodeNoSQLInjection(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	tests := []struct {
		stmt     []interface{}
		expected string
		execute  bool
	}{
		{
			stmt:     []interface{}{"CREATE TABLE foo (id integer not null primary key, name text)"},
			expected: `{"results":[{}]}`,
			execute:  true,
		},
		{
			stmt:     []interface{}{`SELECT * FROM foo WHERE name="baz"`},
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"]}]}`,
			execute:  false,
		},
		{
			stmt:     []interface{}{`SELECT * FROM foo WHERE name=?`, `"baz";DROP TABLE FOO`},
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"]}]}`,
			execute:  false,
		},
		{
			stmt:     []interface{}{`SELECT * FROM foo`},
			expected: `{"results":[{"columns":["id","name"],"types":["integer","text"]}]}`,
			execute:  false,
		},
	}

	for i, tt := range tests {
		var r string
		var err error
		if tt.execute {
			r, err = node.ExecuteParameterized(tt.stmt)
		} else {
			r, err = node.QueryParameterized(tt.stmt)
		}
		if err != nil {
			t.Fatalf(`test %d failed "%s": %s`, i, tt.stmt, err.Error())
		}
		if r != tt.expected {
			t.Fatalf(`test %d received wrong result "%s" got: %s exp: %s`, i, tt.stmt, r, tt.expected)
		}
	}
}

func Test_SingleNodeCoverage(t *testing.T) {
	node := mustNewLeaderNode()
	defer node.Deprovision()

	// Access endpoints to ensure the code is covered.
	var err error
	var str string

	str, err = node.Status()
	if err != nil {
		t.Fatalf("failed to access status endpoint: %s", err.Error())
	}
	if !isJSON(str) {
		t.Fatalf("output from status endpoint is not valid JSON: %s", str)
	}

	str, err = node.Expvar()
	if err != nil {
		t.Fatalf("failed to access expvar endpoint: %s", err.Error())
	}
	if !isJSON(str) {
		t.Fatalf("output from expvar endpoint is not valid JSON: %s", str)
	}
}
<|endoftext|>


<commit_before>package hdfs

import (
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"os"
	"testing"
	"fmt"
)

func TestReadDir(t *testing.T) {
	client := getClient(t)

	mkdirp(t, "/_test/fulldir")
	mkdirp(t, "/_test/fulldir/dir")
	touch(t, "/_test/fulldir/1")
	touch(t, "/_test/fulldir/2")
	touch(t, "/_test/fulldir/3")

	res, err := client.ReadDir("/_test/fulldir")
	assert.Nil(t, err)
	require.Equal(t, len(res), 4)

	assert.Equal(t, "1", res[0].Name())
	assert.False(t, res[0].IsDir())

	assert.Equal(t, "2", res[1].Name())
	assert.False(t, res[1].IsDir())

	assert.Equal(t, "3", res[2].Name())
	assert.False(t, res[2].IsDir())

	assert.Equal(t, "dir", res[3].Name())
	assert.True(t, res[3].IsDir())
}

func TestReadDirMany(t *testing.T) {
	client := getClient(t)

	mkdirp(t, "/_test/hugedir")
	for i := 1; i <= 1000; i++ {
		touch(t, fmt.Sprintf("/_test/hugedir/%d", i))
	}

	res, err := client.ReadDir("/_test/hugedir")
	assert.Nil(t, err)
	require.Equal(t, len(res), 1000)
}

func TestReadDirTrailingSlash(t *testing.T) {
	client := getClient(t)

	mkdirp(t, "/_test/fulldir2")
	mkdirp(t, "/_test/fulldir2/dir")
	touch(t, "/_test/fulldir2/1")
	touch(t, "/_test/fulldir2/2")
	touch(t, "/_test/fulldir2/3")

	res, err := client.ReadDir("/_test/fulldir2/")
	assert.Nil(t, err)
	require.Equal(t, len(res), 4)

	assert.Equal(t, "1", res[0].Name())
	assert.False(t, res[0].IsDir())

	assert.Equal(t, "2", res[1].Name())
	assert.False(t, res[1].IsDir())

	assert.Equal(t, "3", res[2].Name())
	assert.False(t, res[2].IsDir())

	assert.Equal(t, "dir", res[3].Name())
	assert.True(t, res[3].IsDir())
}

func TestReadEmptyDir(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/emptydir")
	mkdirp(t, "/_test/emptydir")

	res, err := client.ReadDir("/_test/emptydir")
	assert.Nil(t, err)
	assert.Equal(t, 0, len(res))
}

func TestReadDirNonexistent(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/nonexistent")

	res, err := client.ReadDir("/_test/nonexistent")
	assert.Equal(t, os.ErrNotExist, err)
	assert.Nil(t, res)
}

func TestReadDirWithoutPermission(t *testing.T) {
	mkdirp(t, "/_test/accessdenied")
	touch(t, "/_test/accessdenied/foo")

	client := getClientForUser(t, "other")

	res, err := client.ReadDir("/_test/accessdenied")
	assert.Equal(t, os.ErrPermission, err)
	assert.Nil(t, res)
}
<commit_msg>gofmt<commit_after>package hdfs

import (
	"fmt"
	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/require"
	"os"
	"testing"
)

func TestReadDir(t *testing.T) {
	client := getClient(t)

	mkdirp(t, "/_test/fulldir")
	mkdirp(t, "/_test/fulldir/dir")
	touch(t, "/_test/fulldir/1")
	touch(t, "/_test/fulldir/2")
	touch(t, "/_test/fulldir/3")

	res, err := client.ReadDir("/_test/fulldir")
	assert.Nil(t, err)
	require.Equal(t, len(res), 4)

	assert.Equal(t, "1", res[0].Name())
	assert.False(t, res[0].IsDir())

	assert.Equal(t, "2", res[1].Name())
	assert.False(t, res[1].IsDir())

	assert.Equal(t, "3", res[2].Name())
	assert.False(t, res[2].IsDir())

	assert.Equal(t, "dir", res[3].Name())
	assert.True(t, res[3].IsDir())
}

func TestReadDirMany(t *testing.T) {
	client := getClient(t)

	mkdirp(t, "/_test/hugedir")
	for i := 1; i <= 1000; i++ {
		touch(t, fmt.Sprintf("/_test/hugedir/%d", i))
	}

	res, err := client.ReadDir("/_test/hugedir")
	assert.Nil(t, err)
	require.Equal(t, len(res), 1000)
}

func TestReadDirTrailingSlash(t *testing.T) {
	client := getClient(t)

	mkdirp(t, "/_test/fulldir2")
	mkdirp(t, "/_test/fulldir2/dir")
	touch(t, "/_test/fulldir2/1")
	touch(t, "/_test/fulldir2/2")
	touch(t, "/_test/fulldir2/3")

	res, err := client.ReadDir("/_test/fulldir2/")
	assert.Nil(t, err)
	require.Equal(t, len(res), 4)

	assert.Equal(t, "1", res[0].Name())
	assert.False(t, res[0].IsDir())

	assert.Equal(t, "2", res[1].Name())
	assert.False(t, res[1].IsDir())

	assert.Equal(t, "3", res[2].Name())
	assert.False(t, res[2].IsDir())

	assert.Equal(t, "dir", res[3].Name())
	assert.True(t, res[3].IsDir())
}

func TestReadEmptyDir(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/emptydir")
	mkdirp(t, "/_test/emptydir")

	res, err := client.ReadDir("/_test/emptydir")
	assert.Nil(t, err)
	assert.Equal(t, 0, len(res))
}

func TestReadDirNonexistent(t *testing.T) {
	client := getClient(t)

	baleet(t, "/_test/nonexistent")

	res, err := client.ReadDir("/_test/nonexistent")
	assert.Equal(t, os.ErrNotExist, err)
	assert.Nil(t, res)
}

func TestReadDirWithoutPermission(t *testing.T) {
	mkdirp(t, "/_test/accessdenied")
	touch(t, "/_test/accessdenied/foo")

	client := getClientForUser(t, "other")

	res, err := client.ReadDir("/_test/accessdenied")
	assert.Equal(t, os.ErrPermission, err)
	assert.Nil(t, res)
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-toggl AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package toggl

import (
	"errors"
	"fmt"
	"time"
)

// ProjectsService handles communication with the workspace related
// methods of the Toggl API.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/workspaces.md
type ProjectsService struct {
	client *Client
}

// Project represents project on a workspace.
type Project struct {
	ID          int        `json:"id,omitempty"`
	Name        string     `json:"name,omitempty"`
	WorkspaceID int        `json:"wid,omitempty"`
	ClientID    int        `json:"cid,omitempty"`
	Active      bool       `json:"active,omitempty"`
	IsPrivate   bool       `json:"is_private,omitempty"`
	Template    bool       `json:"template,omitempty"`
	TemplateID  int        `json:"template_id,omitempty"`
	Billable    bool       `json:"billable,omitempty"`
	At          *time.Time `json:"at,omitempty"`
}

// ProjectResponse acts as a response wrapper where response returns
// in format of "data": Project's object.
type ProjectResponse struct {
	Data *Project `json:"data,omitempty"`
}

// ProjectCreate represents posted data to be sent to projects endpoint.
type ProjectCreate struct {
	Project *Project `json:"project,omitempty"`
}

// Create a project.
//
// https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#create-project
func (s *ProjectsService) Create(p *Project) (*Project, error) {
	u := "projects"
	pc := &ProjectCreate{Project: p}
	req, err := s.client.NewRequest("POST", u, pc)
	if err != nil {
		return nil, err
	}

	data := new(ProjectResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Get project data.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-data
func (s *ProjectsService) Get(id int) (*Project, error) {
	u := fmt.Sprintf("projects/%v", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	data := new(ProjectResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Update project data.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/clients.md#update-a-client
func (s *ProjectsService) Update(p *Project) (*Project, error) {
	if p == nil {
		return nil, errors.New("Project cannot be nil")
	}
	if p.ID <= 0 {
		return nil, errors.New("Invalid Project.ID")
	}

	u := fmt.Sprintf("projects/%v", p.ID)

	pc := &ProjectCreate{p}
	req, err := s.client.NewRequest("PUT", u, pc)
	if err != nil {
		return nil, err
	}

	data := new(ProjectResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Todo:
// Projects.ProjectUsers(projectID int)
<commit_msg>Added ProjectUsers method for Projects service.<commit_after>// Copyright 2013 The go-toggl AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package toggl

import (
	"errors"
	"fmt"
	"time"
)

// ProjectsService handles communication with the workspace related
// methods of the Toggl API.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/workspaces.md
type ProjectsService struct {
	client *Client
}

// Project represents project on a workspace.
type Project struct {
	ID          int        `json:"id,omitempty"`
	Name        string     `json:"name,omitempty"`
	WorkspaceID int        `json:"wid,omitempty"`
	ClientID    int        `json:"cid,omitempty"`
	Active      bool       `json:"active,omitempty"`
	IsPrivate   bool       `json:"is_private,omitempty"`
	Template    bool       `json:"template,omitempty"`
	TemplateID  int        `json:"template_id,omitempty"`
	Billable    bool       `json:"billable,omitempty"`
	At          *time.Time `json:"at,omitempty"`
}

// ProjectResponse acts as a response wrapper where response returns
// in format of "data": Project's object.
type ProjectResponse struct {
	Data *Project `json:"data,omitempty"`
}

// ProjectCreate represents posted data to be sent to projects endpoint.
type ProjectCreate struct {
	Project *Project `json:"project,omitempty"`
}

// Create a project.
//
// https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#create-project
func (s *ProjectsService) Create(p *Project) (*Project, error) {
	u := "projects"
	pc := &ProjectCreate{Project: p}
	req, err := s.client.NewRequest("POST", u, pc)
	if err != nil {
		return nil, err
	}

	data := new(ProjectResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Get project data.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-data
func (s *ProjectsService) Get(id int) (*Project, error) {
	u := fmt.Sprintf("projects/%v", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	data := new(ProjectResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// Update project data.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/clients.md#update-a-client
func (s *ProjectsService) Update(p *Project) (*Project, error) {
	if p == nil {
		return nil, errors.New("Project cannot be nil")
	}
	if p.ID <= 0 {
		return nil, errors.New("Invalid Project.ID")
	}

	u := fmt.Sprintf("projects/%v", p.ID)

	pc := &ProjectCreate{p}
	req, err := s.client.NewRequest("PUT", u, pc)
	if err != nil {
		return nil, err
	}

	data := new(ProjectResponse)
	_, err = s.client.Do(req, data)

	return data.Data, err
}

// ProjectUsers gets project users.
//
// Toggl API docs: https://github.com/toggl/toggl_api_docs/blob/master/chapters/projects.md#get-project-users
func (s *ProjectsService) ProjectUsers(id int) ([]ProjectUser, error) {
	u := fmt.Sprintf("projects/%v/project_users", id)
	req, err := s.client.NewRequest("GET", u, nil)
	if err != nil {
		return nil, err
	}

	data := new([]ProjectUser)
	_, err = s.client.Do(req, data)
	return *data, err
}
<|endoftext|>


<commit_before>package python

import (
	"encoding/json"
	"path/filepath"

	"github.com/kr/fs"
	"sourcegraph.com/sourcegraph/srcgraph/config"
	"sourcegraph.com/sourcegraph/srcgraph/container"
	"sourcegraph.com/sourcegraph/srcgraph/scan"
	"sourcegraph.com/sourcegraph/srcgraph/unit"
)

func init() {
	scan.Register("python", scan.DockerScanner{defaultPythonEnv})
	unit.Register("python", &DistPackage{})
}

func (p *pythonEnv) BuildScanner(dir string, c *config.Repository) (*container.Command, error) {
	pythonFiles, hasSetupPy := pythonSourceFiles(dir)

	dockerfile, err := p.pydepDockerfile()
	if err != nil {
		return nil, err
	}
	var cmd []string
	if hasSetupPy {
		cmd = []string{"pydep-run.py", "info", srcRoot}
	} else {
		cmd = []string{"echo", "-n", "."} // kludge
	}
	return &container.Command{
		Container: container.Container{
			Dockerfile: dockerfile,
			RunOptions: []string{"-v", dir + ":" + srcRoot},
			Cmd:        cmd,
		},
		Transform: func(orig []byte) ([]byte, error) {
			if len(orig) == 0 {
				return nil, nil
			}

			var info pkgInfo
			err := json.Unmarshal(orig, &info)
			if err != nil {
				return nil, err
			}
			units := []*DistPackage{
				{ProjectName: info.ProjectName, Files: pythonFiles},
			}
			return json.Marshal(units)
		},
	}, nil
}

func (p *pythonEnv) UnmarshalSourceUnits(data []byte) ([]unit.SourceUnit, error) {
	if len(data) == 0 {
		return nil, nil
	}

	var distPackages []*DistPackage
	err := json.Unmarshal(data, &distPackages)
	if err != nil {
		return nil, err
	}

	units := make([]unit.SourceUnit, len(distPackages))
	for i, p := range distPackages {
		units[i] = p
	}

	return units, nil
}

func pythonSourceFiles(dir string) (files []string, hasSetupPy bool) {
	walker := fs.Walk(dir)
	for walker.Step() {
		if err := walker.Err(); err == nil && !walker.Stat().IsDir() && filepath.Ext(walker.Path()) == ".py" {
			if filepath.Base(walker.Path()) == "setup.py" {
				hasSetupPy = true
			}
			file, _ := filepath.Rel(dir, walker.Path())
			files = append(files, file)
		}
	}
	return
}
<commit_msg>unkludge<commit_after>package python

import (
	"encoding/json"
	"path/filepath"

	"github.com/kr/fs"
	"sourcegraph.com/sourcegraph/srcgraph/config"
	"sourcegraph.com/sourcegraph/srcgraph/container"
	"sourcegraph.com/sourcegraph/srcgraph/scan"
	"sourcegraph.com/sourcegraph/srcgraph/unit"
)

func init() {
	scan.Register("python", scan.DockerScanner{defaultPythonEnv})
	unit.Register("python", &DistPackage{})
}

func (p *pythonEnv) BuildScanner(dir string, c *config.Repository) (*container.Command, error) {
	pythonFiles, hasSetupPy := pythonSourceFiles(dir)

	dockerfile, err := p.pydepDockerfile()
	if err != nil {
		return nil, err
	}
	var cmd []string
	if hasSetupPy {
		cmd = []string{"pydep-run.py", "info", srcRoot}
	} else {
		cmd = []string{"echo", "-n"} // kludge
	}
	return &container.Command{
		Container: container.Container{
			Dockerfile: dockerfile,
			RunOptions: []string{"-v", dir + ":" + srcRoot},
			Cmd:        cmd,
		},
		Transform: func(orig []byte) ([]byte, error) {
			if len(orig) == 0 {
				return nil, nil
			}

			var info pkgInfo
			err := json.Unmarshal(orig, &info)
			if err != nil {
				return nil, err
			}
			units := []*DistPackage{
				{ProjectName: info.ProjectName, Files: pythonFiles},
			}
			return json.Marshal(units)
		},
	}, nil
}

func (p *pythonEnv) UnmarshalSourceUnits(data []byte) ([]unit.SourceUnit, error) {
	if len(data) == 0 {
		return nil, nil
	}

	var distPackages []*DistPackage
	err := json.Unmarshal(data, &distPackages)
	if err != nil {
		return nil, err
	}

	units := make([]unit.SourceUnit, len(distPackages))
	for i, p := range distPackages {
		units[i] = p
	}

	return units, nil
}

func pythonSourceFiles(dir string) (files []string, hasSetupPy bool) {
	walker := fs.Walk(dir)
	for walker.Step() {
		if err := walker.Err(); err == nil && !walker.Stat().IsDir() && filepath.Ext(walker.Path()) == ".py" {
			if filepath.Base(walker.Path()) == "setup.py" {
				hasSetupPy = true
			}
			file, _ := filepath.Rel(dir, walker.Path())
			files = append(files, file)
		}
	}
	return
}
<|endoftext|>


<commit_before>package meander

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"sync"
	"time"
)

// APIKey is the Google Places API key that will be used
// to make requests to the service.
var APIKey string

// Place represents a single place.
type Place struct {
	*googleGeometry `json:"geometry"`
	Name            string         `json:"name"`
	Icon            string         `json:"icon"`
	Photos          []*googlePhoto `json:"photos"`
	Vicinity        string         `json:"vicinity"`
}

// Public gets a public view of a Place.
func (p *Place) Public() interface{} {
	return map[string]interface{}{
		"name":     p.Name,
		"icon":     p.Icon,
		"photos":   p.Photos,
		"vicinity": p.Vicinity,
		"lat":      p.Lat,
		"lng":      p.Lng,
	}
}

// Query represents a runnable Query against the
// Google Places API.
type Query struct {
	Lat          float64
	Lng          float64
	Journey      []string
	Radius       int
	CostRangeStr string
}

func (q *Query) find(types string) (*googleResponse, error) {
	u := "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
	u = fmt.Sprintf("%s?location=%g,%g&radius=%d", u, q.Lat, q.Lng, q.Radius)
	u = fmt.Sprintf("%s&types=%s", u, types)
	u = fmt.Sprintf("%s&key=%s", u, APIKey)
	if len(q.CostRangeStr) > 0 {
		r := ParseCostRange(q.CostRangeStr)
		u = fmt.Sprintf("%s&minprice=%d&maxprice=%d", u, int(r.From)-1, int(r.To)-1)
	}
	res, err := http.Get(u)
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	var response googleResponse
	if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
		return nil, err
	}
	return &response, nil
}

// Run runs the query concurrently, and returns the results.
func (q *Query) Run() []interface{} {
	rand.Seed(time.Now().UnixNano())
	var w sync.WaitGroup
	var placesL sync.Mutex
	places := make([]interface{}, len(q.Journey))
	for i, r := range q.Journey {
		w.Add(1)
		go func(types string, i int) {
			defer w.Done()
			response, err := q.find(types)
			if err != nil {
				log.Println("Failed to find places:", err)
				return
			}
			if len(response.Results) == 0 {
				log.Println("No places found for", types)
				return
			}
			for _, result := range response.Results {
				for _, photo := range result.Photos {
					photo.URL = "https://maps.googleapis.com/maps/api/place/photo?" +
						"maxwidth=1000&photoreference=" + photo.PhotoRef + "&key=" + APIKey
				}
			}
			placesL.Lock()
			defer placesL.Unlock()
			randI := rand.Intn(len(response.Results))
			places[i] = response.Results[randI]
		}(r, i)
	}
	w.Wait() // wait for everything to finish
	return places
}

type googleResponse struct {
	Results []*Place `json:"results"`
}
type googleGeometry struct {
	*googleLocation `json:"location"`
}
type googleLocation struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}
type googlePhoto struct {
	Height   int    `json:"height"`
	Width    int    `json:"width"`
	PhotoRef string `json:"photo_reference"`
	URL      string `json:"url"`
}
<commit_msg>using url.Values as per @tylerb suggestion<commit_after>package meander

import (
	"encoding/json"
	"fmt"
	"log"
	"math/rand"
	"net/http"
	"net/url"
	"sync"
	"time"
)

// APIKey is the Google Places API key that will be used
// to make requests to the service.
var APIKey string

// Place represents a single place.
type Place struct {
	*googleGeometry `json:"geometry"`
	Name            string         `json:"name"`
	Icon            string         `json:"icon"`
	Photos          []*googlePhoto `json:"photos"`
	Vicinity        string         `json:"vicinity"`
}

// Public gets a public view of a Place.
func (p *Place) Public() interface{} {
	return map[string]interface{}{
		"name":     p.Name,
		"icon":     p.Icon,
		"photos":   p.Photos,
		"vicinity": p.Vicinity,
		"lat":      p.Lat,
		"lng":      p.Lng,
	}
}

// Query represents a runnable Query against the
// Google Places API.
type Query struct {
	Lat          float64
	Lng          float64
	Journey      []string
	Radius       int
	CostRangeStr string
}

func (q *Query) find(types string) (*googleResponse, error) {
	u := "https://maps.googleapis.com/maps/api/place/nearbysearch/json"
	vals := make(url.Values)
	vals.Set("location", fmt.Sprintf("%g,%g", q.Lat, q.Lng))
	vals.Set("radius", fmt.Sprintf("%d", q.Radius))
	vals.Set("types", types)
	vals.Set("key", APIKey)
	if len(q.CostRangeStr) > 0 {
		r := ParseCostRange(q.CostRangeStr)
		vals.Set("minprice", fmt.Sprintf("%d", int(r.From)-1))
		vals.Set("maxprice", fmt.Sprintf("%d", int(r.To)-1))
	}
	res, err := http.Get(u + "?" + vals.Encode())
	if err != nil {
		return nil, err
	}
	defer res.Body.Close()
	var response googleResponse
	if err := json.NewDecoder(res.Body).Decode(&response); err != nil {
		return nil, err
	}
	return &response, nil
}

// Run runs the query concurrently, and returns the results.
func (q *Query) Run() []interface{} {
	rand.Seed(time.Now().UnixNano())
	var w sync.WaitGroup
	var l sync.Mutex
	places := make([]interface{}, len(q.Journey))
	for i, r := range q.Journey {
		w.Add(1)
		go func(types string, i int) {
			defer w.Done()
			response, err := q.find(types)
			if err != nil {
				log.Println("Failed to find places:", err)
				return
			}
			if len(response.Results) == 0 {
				log.Println("No places found for", types)
				return
			}
			for _, result := range response.Results {
				for _, photo := range result.Photos {
					photo.URL = "https://maps.googleapis.com/maps/api/place/photo?" +
						"maxwidth=1000&photoreference=" + photo.PhotoRef + "&key=" + APIKey
				}
			}
			randI := rand.Intn(len(response.Results))
			l.Lock()
			places[i] = response.Results[randI]
			l.Unlock()
		}(r, i)
	}
	w.Wait() // wait for everything to finish
	return places
}

type googleResponse struct {
	Results []*Place `json:"results"`
}
type googleGeometry struct {
	*googleLocation `json:"location"`
}
type googleLocation struct {
	Lat float64 `json:"lat"`
	Lng float64 `json:"lng"`
}
type googlePhoto struct {
	Height   int    `json:"height"`
	Width    int    `json:"width"`
	PhotoRef string `json:"photo_reference"`
	URL      string `json:"url"`
}
<|endoftext|>


<commit_before>package utils

import (
	"crypto/x509"
	"encoding/pem"
	"encoding/xml"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/mitchellh/go-homedir"
	"io/ioutil"
	"net/url"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
)

const windowsServerConfigFile = "c:\\concerto\\client.xml"
const nixServerConfigFile = "/etc/concerto/client.xml"
const defaultConcertoEndpoint = "https://clients.concerto.io:886/"

// Config stores configuration file contents
type Config struct {
	XMLName      xml.Name `xml:"concerto"`
	APIEndpoint  string   `xml:"server,attr"`
	LogFile      string   `xml:"log_file,attr"`
	LogLevel     string   `xml:"log_level,attr"`
	Certificate  Cert     `xml:"ssl"`
	ConfLocation string
	ConfFile     string
	IsHost       bool
	ConcertoURL  string
}

// Cert stores cert files location
type Cert struct {
	Cert string `xml:"cert,attr"`
	Key  string `xml:"key,attr"`
	Ca   string `xml:"server_ca,attr"`
}

var cachedConfig *Config

// GetConcertoConfig returns concerto configuration
func GetConcertoConfig() (*Config, error) {
	if cachedConfig == nil {
		return nil, fmt.Errorf("Configuration hasn't been initialized")
	}
	return cachedConfig, nil
}

// InitializeConcertoConfig creates the concerto configuration structure
func InitializeConcertoConfig(c *cli.Context) (*Config, error) {
	log.Debug("InitializeConcertoConfig")
	if cachedConfig != nil {
		return cachedConfig, nil
	}

	// where config file must me
	cachedConfig = &Config{}
	err := cachedConfig.evaluateConcertoConfigFile(c)
	if err != nil {
		return nil, err
	}

	// read config contents
	log.Debugf("Reading configuration from %s", cachedConfig.ConfFile)
	err = cachedConfig.readConcertoConfig(c)
	if err != nil {
		return nil, err
	}

	// add login URL. Needed for setup
	err = cachedConfig.readConcertoURL()
	if err != nil {
		return nil, err
	}

	// check if isHost. Needed to show appropiate options
	err = cachedConfig.evaluateCertificate()
	if err != nil {
		return nil, err
	}

	debugShowConfig()
	return cachedConfig, nil
}

func debugShowConfig() {
	if log.GetLevel() < log.DebugLevel {
		return
	}

	if cachedConfig == nil {
		log.Debug("Concerto configuration not loaded")
	}

	debugStruct("", *cachedConfig)
	// c := reflect.ValueOf(*cachedConfig)
	// for i := 0; i < c.NumField(); i++ {
	// 	if c.Type().Field(i).Type.String() != "xml.Name" {
	// 		log.WithField(c.Type().Field(i).Name, c.Field(i).Interface()).Debug("Configuration item")
	// 	}
	// }
}

// debugStruct iterates struct and show in debug console all items and subitems
func debugStruct(prefix string, item interface{}) {
	c := reflect.ValueOf(item)
	for i := 0; i < c.NumField(); i++ {
		if c.Type().Field(i).Type.String() != "xml.Name" {

			name := c.Type().Field(i).Name
			value := c.Field(i).Interface()

			// if value is struct, iterate with recursion
			if c.Type().Field(i).Type.Kind() == reflect.Struct {
				debugStruct(name, value)
			} else {
				if prefix != "" {
					name = fmt.Sprintf("%s.%s", prefix, name)
				}
				log.WithField(name, value).Debug("Configuration item")
			}
		}
	}
}

// IsConfigReady returns whether configurations items are filled
func (config *Config) IsConfigReady() bool {
	if config.APIEndpoint == "" ||
		config.Certificate.Cert == "" ||
		config.Certificate.Key == "" ||
		config.Certificate.Ca == "" {
		return false
	}
	return true
}

// IsConfigReadySetup returns whether we can use setup command
func (config *Config) IsConfigReadySetup() bool {
	return config.ConcertoURL != ""
}

// readConcertoConfig reads Concerto config file located at fileLocation
func (config *Config) readConcertoConfig(c *cli.Context) error {
	log.Debug("Reading Concerto Configuration")
	if FileExists(config.ConfFile) {
		// file exists, read it's contents

		xmlFile, err := os.Open(config.ConfFile)
		if err != nil {
			return err
		}
		defer xmlFile.Close()
		b, err := ioutil.ReadAll(xmlFile)
		if err != nil {
			return fmt.Errorf("Configuration File %s couldn't be read.", config.ConfFile)
		}

		if err = xml.Unmarshal(b, &config); err != nil {
			return fmt.Errorf("Configuration File %s does not have valid XML format.", config.ConfFile)
		}

	} else {
		log.Debugf("Configuration File %s does not exist. Reading environment variables", config.ConfFile)
	}

	// overwrite with environment/arguments vars
	if overwEP := c.String("concerto-endpoint"); overwEP != "" {
		log.Debug("Concerto APIEndpoint taken from env/args")
		config.APIEndpoint = overwEP
	}

	if overwCert := c.String("client-cert"); overwCert != "" {
		log.Debug("Certificate path taken from env/args")
		config.Certificate.Cert = overwCert
	}

	if overwKey := c.String("client-key"); overwKey != "" {
		log.Debug("Certificate key path taken from env/args")
		config.Certificate.Key = overwKey
	}

	if overwCa := c.String("ca-cert"); overwCa != "" {
		log.Debug("CA certificate path taken from env/args")
		config.Certificate.Ca = overwCa
	}

	// if endpoint empty set default
	// we can't set the default from flags, because it would overwrite config file
	if config.APIEndpoint == "" {
		config.APIEndpoint = defaultConcertoEndpoint
	}

	return nil
}

// evaluateConcertoConfigFile returns path to concerto config file
func (config *Config) evaluateConcertoConfigFile(c *cli.Context) error {
	log.Debug("evaluateConcertoConfigFile")

	if configFile := c.String("concerto-config"); configFile != "" {

		log.Debug("Concerto configuration file location taken from env/args")
		config.ConfFile = configFile

	} else {

		currUser, err := user.Current()
		if err != nil {
			log.Debugf("Couldn't use os.user to get user details: %s", err.Error())
			dir, err := homedir.Dir()
			if err != nil {
				return fmt.Errorf("Couldn't get home dir for current user: %s", err.Error())
			}
			currUser = &user.User{
				Username: getUsername(),
				HomeDir:  dir,
			}
		}

		log.Debugf("Current user is %+v", currUser)
		if runtime.GOOS == "windows" {
			log.Debugf("Running on windows, removing domain from user nama")
			currUser.Username = currUser.Username[strings.LastIndex(currUser.Username, "\\")+1:]

			if (currUser.Gid == "S-1-5-32-544" || currUser.Username == "Administrator") && FileExists(windowsServerConfigFile) {
				log.Debug("Current user is administrator, setting config file as %s", windowsServerConfigFile)
				config.ConfFile = windowsServerConfigFile
			} else {
				// User mode Windows
				log.Debug("Current user is regular user")
				config.ConfFile = filepath.Join(currUser.HomeDir, ".concerto/client.xml")
			}
		} else {
			// Server mode *nix
			if currUser.Uid == "0" || currUser.Username == "root" && FileExists(nixServerConfigFile) {
				config.ConfFile = nixServerConfigFile
			} else {
				// User mode *nix
				config.ConfFile = filepath.Join(currUser.HomeDir, ".concerto/client.xml")
			}
		}
	}
	config.ConfLocation = path.Dir(config.ConfFile)
	return nil
}

// getUsername gets username by env variable.
// os.user is dependant on cgo, so cross compiling won't work
func getUsername() string {
	log.Debug("getUsername")
	u := "unknown"
	osUser := ""

	switch runtime.GOOS {
	case "darwin", "linux":
		osUser = os.Getenv("USER")
	case "windows":
		osUser = os.Getenv("USERNAME")

		// remove domain
		osUser = osUser[strings.LastIndex(osUser, "\\")+1:]
		log.Debugf("Windows user has been transformed into %s", osUser)

		// HACK ugly ... if localized administrator, translate to administrator
		if osUser == "Järjestelmänvalvoja" ||
			osUser == "Administrateur" ||
			osUser == "Rendszergazda" ||
			osUser == "Administrador" ||
			osUser == "Администратор" ||
			osUser == "Administratör" {
			osUser = "Administrator"
		}
	}

	if osUser != "" {
		u = osUser
	}
	return u
}

// readConcertoURL reads URL from CONCERTO_URL envrionment or calculates using API URL
func (config *Config) readConcertoURL() error {

	if config.ConcertoURL != "" {
		return nil
	}

	if overwURL := os.Getenv("CONCERTO_URL"); overwURL != "" {
		config.ConcertoURL = overwURL
		log.Debug("Concerto URL taken from CONCERTO_URL")
		return nil
	}

	cURL, err := url.Parse(config.APIEndpoint)
	if err != nil {
		return err
	}

	tokenHost := strings.Split(cURL.Host, ":")
	tokenFqdn := strings.Split(tokenHost[0], ".")

	if !strings.Contains(cURL.Host, "staging") {
		tokenFqdn[0] = "start"
	}

	config.ConcertoURL = fmt.Sprintf("%s://%s/", cURL.Scheme, strings.Join(tokenFqdn, "."))
	return nil
}

// evaluateCertificate determines if a certificate has been issued for a host
func (config *Config) evaluateCertificate() error {
	if FileExists(config.Certificate.Cert) {

		data, err := ioutil.ReadFile(config.Certificate.Cert)
		if err != nil {
			return err
		}

		block, _ := pem.Decode(data)

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return err
		}

		if len(cert.Subject.OrganizationalUnit) > 0 {
			if cert.Subject.OrganizationalUnit[0] == "Hosts" {
				config.IsHost = true
				return nil
			}
		} else if len(cert.Issuer.Organization) > 0 {
			if cert.Issuer.Organization[0] == "Tapp" {
				config.IsHost = true
				return nil
			}
		}
	}
	config.IsHost = false
	return nil
}
<commit_msg>localizated admin for windows<commit_after>package utils

import (
	"crypto/x509"
	"encoding/pem"
	"encoding/xml"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/codegangsta/cli"
	"github.com/mitchellh/go-homedir"
	"io/ioutil"
	"net/url"
	"os"
	"os/user"
	"path"
	"path/filepath"
	"reflect"
	"runtime"
	"strings"
)

const windowsServerConfigFile = "c:\\concerto\\client.xml"
const nixServerConfigFile = "/etc/concerto/client.xml"
const defaultConcertoEndpoint = "https://clients.concerto.io:886/"

// Config stores configuration file contents
type Config struct {
	XMLName      xml.Name `xml:"concerto"`
	APIEndpoint  string   `xml:"server,attr"`
	LogFile      string   `xml:"log_file,attr"`
	LogLevel     string   `xml:"log_level,attr"`
	Certificate  Cert     `xml:"ssl"`
	ConfLocation string
	ConfFile     string
	IsHost       bool
	ConcertoURL  string
}

// Cert stores cert files location
type Cert struct {
	Cert string `xml:"cert,attr"`
	Key  string `xml:"key,attr"`
	Ca   string `xml:"server_ca,attr"`
}

var cachedConfig *Config

// GetConcertoConfig returns concerto configuration
func GetConcertoConfig() (*Config, error) {
	if cachedConfig == nil {
		return nil, fmt.Errorf("Configuration hasn't been initialized")
	}
	return cachedConfig, nil
}

// InitializeConcertoConfig creates the concerto configuration structure
func InitializeConcertoConfig(c *cli.Context) (*Config, error) {
	log.Debug("InitializeConcertoConfig")
	if cachedConfig != nil {
		return cachedConfig, nil
	}

	// where config file must me
	cachedConfig = &Config{}
	err := cachedConfig.evaluateConcertoConfigFile(c)
	if err != nil {
		return nil, err
	}

	// read config contents
	log.Debugf("Reading configuration from %s", cachedConfig.ConfFile)
	err = cachedConfig.readConcertoConfig(c)
	if err != nil {
		return nil, err
	}

	// add login URL. Needed for setup
	err = cachedConfig.readConcertoURL()
	if err != nil {
		return nil, err
	}

	// check if isHost. Needed to show appropiate options
	err = cachedConfig.evaluateCertificate()
	if err != nil {
		return nil, err
	}

	debugShowConfig()
	return cachedConfig, nil
}

func debugShowConfig() {
	if log.GetLevel() < log.DebugLevel {
		return
	}

	if cachedConfig == nil {
		log.Debug("Concerto configuration not loaded")
	}

	debugStruct("", *cachedConfig)
	// c := reflect.ValueOf(*cachedConfig)
	// for i := 0; i < c.NumField(); i++ {
	// 	if c.Type().Field(i).Type.String() != "xml.Name" {
	// 		log.WithField(c.Type().Field(i).Name, c.Field(i).Interface()).Debug("Configuration item")
	// 	}
	// }
}

// debugStruct iterates struct and show in debug console all items and subitems
func debugStruct(prefix string, item interface{}) {
	c := reflect.ValueOf(item)
	for i := 0; i < c.NumField(); i++ {
		if c.Type().Field(i).Type.String() != "xml.Name" {

			name := c.Type().Field(i).Name
			value := c.Field(i).Interface()

			// if value is struct, iterate with recursion
			if c.Type().Field(i).Type.Kind() == reflect.Struct {
				debugStruct(name, value)
			} else {
				if prefix != "" {
					name = fmt.Sprintf("%s.%s", prefix, name)
				}
				log.WithField(name, value).Debug("Configuration item")
			}
		}
	}
}

// IsConfigReady returns whether configurations items are filled
func (config *Config) IsConfigReady() bool {
	if config.APIEndpoint == "" ||
		config.Certificate.Cert == "" ||
		config.Certificate.Key == "" ||
		config.Certificate.Ca == "" {
		return false
	}
	return true
}

// IsConfigReadySetup returns whether we can use setup command
func (config *Config) IsConfigReadySetup() bool {
	return config.ConcertoURL != ""
}

// readConcertoConfig reads Concerto config file located at fileLocation
func (config *Config) readConcertoConfig(c *cli.Context) error {
	log.Debug("Reading Concerto Configuration")
	if FileExists(config.ConfFile) {
		// file exists, read it's contents

		xmlFile, err := os.Open(config.ConfFile)
		if err != nil {
			return err
		}
		defer xmlFile.Close()
		b, err := ioutil.ReadAll(xmlFile)
		if err != nil {
			return fmt.Errorf("Configuration File %s couldn't be read.", config.ConfFile)
		}

		if err = xml.Unmarshal(b, &config); err != nil {
			return fmt.Errorf("Configuration File %s does not have valid XML format.", config.ConfFile)
		}

	} else {
		log.Debugf("Configuration File %s does not exist. Reading environment variables", config.ConfFile)
	}

	// overwrite with environment/arguments vars
	if overwEP := c.String("concerto-endpoint"); overwEP != "" {
		log.Debug("Concerto APIEndpoint taken from env/args")
		config.APIEndpoint = overwEP
	}

	if overwCert := c.String("client-cert"); overwCert != "" {
		log.Debug("Certificate path taken from env/args")
		config.Certificate.Cert = overwCert
	}

	if overwKey := c.String("client-key"); overwKey != "" {
		log.Debug("Certificate key path taken from env/args")
		config.Certificate.Key = overwKey
	}

	if overwCa := c.String("ca-cert"); overwCa != "" {
		log.Debug("CA certificate path taken from env/args")
		config.Certificate.Ca = overwCa
	}

	// if endpoint empty set default
	// we can't set the default from flags, because it would overwrite config file
	if config.APIEndpoint == "" {
		config.APIEndpoint = defaultConcertoEndpoint
	}

	return nil
}

// evaluateConcertoConfigFile returns path to concerto config file
func (config *Config) evaluateConcertoConfigFile(c *cli.Context) error {
	log.Debug("evaluateConcertoConfigFile")

	if configFile := c.String("concerto-config"); configFile != "" {

		log.Debug("Concerto configuration file location taken from env/args")
		config.ConfFile = configFile

	} else {

		currUser, err := user.Current()
		if err != nil {
			log.Debugf("Couldn't use os.user to get user details: %s", err.Error())
			dir, err := homedir.Dir()
			if err != nil {
				return fmt.Errorf("Couldn't get home dir for current user: %s", err.Error())
			}
			currUser = &user.User{
				Username: getUsername(),
				HomeDir:  dir,
			}
		}

		if runtime.GOOS == "windows" {
			currUser.Username = currUser.Username[strings.LastIndex(currUser.Username, "\\")+1:]
			log.Debugf("Windows username is %s", currUser.Username)

			if (currUser.Gid == "S-1-5-32-544" || isWinAdministrator(currUser.Username)) && FileExists(windowsServerConfigFile) {
				log.Debug("Current user is administrator, setting config file as %s", windowsServerConfigFile)
				config.ConfFile = windowsServerConfigFile
			} else {
				// User mode Windows
				log.Debugf("Current user is regular user: %s", currUser.Username)
				config.ConfFile = filepath.Join(currUser.HomeDir, ".concerto/client.xml")
			}
		} else {
			// Server mode *nix
			if currUser.Uid == "0" || currUser.Username == "root" && FileExists(nixServerConfigFile) {
				config.ConfFile = nixServerConfigFile
			} else {
				// User mode *nix
				config.ConfFile = filepath.Join(currUser.HomeDir, ".concerto/client.xml")
			}
		}
	}
	config.ConfLocation = path.Dir(config.ConfFile)
	return nil
}

// getUsername gets username by env variable.
// os.user is dependant on cgo, so cross compiling won't work
func getUsername() string {
	log.Debug("getUsername")
	u := "unknown"
	osUser := ""

	switch runtime.GOOS {
	case "darwin", "linux":
		osUser = os.Getenv("USER")
	case "windows":
		osUser = os.Getenv("USERNAME")

		// remove domain
		osUser = osUser[strings.LastIndex(osUser, "\\")+1:]
		log.Debugf("Windows user has been transformed into %s", osUser)

		// HACK ugly ... if localized administrator, translate to administrator
		if isWinAdministrator(osUser) {
			osUser = "Administrator"
		}
	}

	if osUser != "" {
		u = osUser
	}
	return u
}

func isWinAdministrator(user string) bool {
	return user == "Järjestelmänvalvoja" ||
		user == "Administrateur" ||
		user == "Rendszergazda" ||
		user == "Administrador" ||
		user == "Администратор" ||
		user == "Administratör"
}

// readConcertoURL reads URL from CONCERTO_URL envrionment or calculates using API URL
func (config *Config) readConcertoURL() error {

	if config.ConcertoURL != "" {
		return nil
	}

	if overwURL := os.Getenv("CONCERTO_URL"); overwURL != "" {
		config.ConcertoURL = overwURL
		log.Debug("Concerto URL taken from CONCERTO_URL")
		return nil
	}

	cURL, err := url.Parse(config.APIEndpoint)
	if err != nil {
		return err
	}

	tokenHost := strings.Split(cURL.Host, ":")
	tokenFqdn := strings.Split(tokenHost[0], ".")

	if !strings.Contains(cURL.Host, "staging") {
		tokenFqdn[0] = "start"
	}

	config.ConcertoURL = fmt.Sprintf("%s://%s/", cURL.Scheme, strings.Join(tokenFqdn, "."))
	return nil
}

// evaluateCertificate determines if a certificate has been issued for a host
func (config *Config) evaluateCertificate() error {
	if FileExists(config.Certificate.Cert) {

		data, err := ioutil.ReadFile(config.Certificate.Cert)
		if err != nil {
			return err
		}

		block, _ := pem.Decode(data)

		cert, err := x509.ParseCertificate(block.Bytes)
		if err != nil {
			return err
		}

		if len(cert.Subject.OrganizationalUnit) > 0 {
			if cert.Subject.OrganizationalUnit[0] == "Hosts" {
				config.IsHost = true
				return nil
			}
		} else if len(cert.Issuer.Organization) > 0 {
			if cert.Issuer.Organization[0] == "Tapp" {
				config.IsHost = true
				return nil
			}
		}
	}
	config.IsHost = false
	return nil
}
<|endoftext|>


<commit_before>package monit

import (
	"fmt"
	"log"
	"os"
	"runtime/debug"
	"sync"
	"time"

	"github.com/tolexo/aero/conf"
)

var (
	logFp      *os.File
	fileErr    error
	logger     *log.Logger
	prevDay    int
	currentDay int
	isLog      bool
	syncOnce   sync.Once
)

func createPanicLog(sTime time.Time, panicMsg interface{}) {
	path := conf.String("logs.panic_log", "panic_log")
	path = fmt.Sprintf("%s_%d-%d-%d.log", path, sTime.Day(), sTime.Month(), sTime.Year())
	if logFp, fileErr = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666); fileErr != nil {
		fmt.Println("Could not create the panic log file")
		panic(panicMsg)
	}
}

func logPanic(panicMsg interface{}, serviceID, requestURI string, curTime time.Time) {
	logger = log.New(logFp, "", log.Lshortfile)
	logFp.WriteString("\n")
	logger.Print("serviceID: ", serviceID, "  requestURI: ", requestURI, "  Time: ", curTime)
	logger.Print(string(debug.Stack()))
	logger.Panic(panicMsg)
}

func PanicLogger(panicMsg interface{}, serviceID, requestURI string, curTime time.Time) {

	syncOnce.Do(func() {
		isLog = conf.Bool("monitor.panic_log", false)
	})

	if isLog == true {
		sTime := time.Now().UTC()
		currentDay = sTime.Day()
		if currentDay != prevDay {
			if prevDay != 0 && logFp != nil {
				logFp.Close()
			}
			createPanicLog(sTime, panicMsg)
			prevDay = currentDay
		}
		if logFp != nil {
			logPanic(panicMsg, serviceID, requestURI, curTime)
		} else {
			fmt.Println("lost the pointer to the log file, creating again")
			createPanicLog(sTime, panicMsg)
			logPanic(panicMsg, serviceID, requestURI, curTime)
		}
	} else {
		panic(panicMsg)
	}
}
<commit_msg>TTA-226A logging format change<commit_after>package monit

import (
	"fmt"
	"log"
	"os"
	"runtime/debug"
	"sync"
	"time"

	"github.com/tolexo/aero/conf"
)

var (
	logFp      *os.File
	fileErr    error
	logger     *log.Logger
	prevDay    int
	currentDay int
	isLog      bool
	syncOnce   sync.Once
)

func createPanicLog(sTime time.Time, panicMsg interface{}) {
	path := conf.String("logs.panic_log", "panic_log")
	path = fmt.Sprintf("%s_%d-%d-%d.log", path, sTime.Day(), sTime.Month(), sTime.Year())
	if logFp, fileErr = os.OpenFile(path, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666); fileErr != nil {
		fmt.Println("Could not create the panic log file")
		panic(panicMsg)
	}
}

func logPanic(panicMsg interface{}, serviceID string, requestURI string, curTime time.Time) {
	logger = log.New(logFp, "", log.Lshortfile)
	logFp.WriteString("\n")
	logger.Print("Time: ", curTime, "  serviceID: ", serviceID, "  requestURI: ", requestURI)
	logger.Print(string(debug.Stack()))
	logger.Panic(panicMsg)
}

func PanicLogger(panicMsg interface{}, serviceID string, requestURI string, curTime time.Time) {

	syncOnce.Do(func() {
		isLog = conf.Bool("monitor.panic_log", false)
	})

	if isLog == true {
		sTime := time.Now().UTC()
		currentDay = sTime.Day()
		if currentDay != prevDay {
			if prevDay != 0 && logFp != nil {
				logFp.Close()
			}
			createPanicLog(sTime, panicMsg)
			prevDay = currentDay
		}
		if logFp != nil {
			logPanic(panicMsg, serviceID, requestURI, curTime)
		} else {
			fmt.Println("lost the pointer to the log file, creating again")
			createPanicLog(sTime, panicMsg)
			logPanic(panicMsg, serviceID, requestURI, curTime)
		}
	} else {
		panic(panicMsg)
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 The Serviced 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 utils

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const buffersize = 1024

type ParseError string

func (err ParseError) Error() string {
	return fmt.Sprintf("could not parse line: %s", err)
}

type ConfigReader interface {
	StringVal(key, dflt string) string
	StringSlice(key string, dflt []string) []string
	IntVal(key string, dflt int) int
	BoolVal(key string, dflt bool) bool
}

type EnvironConfigReader struct {
	prefix string
}

func NewEnvironConfigReader(filename, prefix string) (*EnvironConfigReader, error) {
	r := &EnvironConfigReader{prefix}

	file, err := os.Open(filename)
	if err != nil {
		return r, err
	}
	defer file.Close()
	if err := r.parse(file); err != nil {
		return r, err
	}
	return r, nil
}

// NewEnvironOnlyConfigReader creates an EnvironConfigReader without parsing
// a file first.
func NewEnvironOnlyConfigReader(prefix string) *EnvironConfigReader {
	return &EnvironConfigReader{prefix}
}

// parse is a really dumb reader parser.  It maps only key values in the form
// of key=value and strips whitespaces surrounding either field.  If the format
// does not match, then and error will return.
func (p *EnvironConfigReader) parse(reader io.Reader) error {
	var (
		line string
		err  error
	)

	bufReader := bufio.NewReader(reader)
	for err != io.EOF {
		line, err = bufReader.ReadString('\n')
		if err != nil && err != io.EOF {
			return err
		}

		line = strings.TrimSpace(strings.Split(line, "#")[0])
		if err := keyvalue([]byte(line)); err != nil {
			return err
		}
	}
	return nil
}

func (p *EnvironConfigReader) StringVal(name string, defaultval string) string {
	if val := os.Getenv(p.prefix + name); val != "" {
		return val
	} else {
		return defaultval
	}
}

func (p *EnvironConfigReader) StringSlice(name string, defaultval []string) []string {
	strval := p.StringVal(name, "")
	if strval != "" {
		return strings.Split(strval, ",")
	}
	return defaultval
}

func (p *EnvironConfigReader) IntVal(name string, defaultval int) int {
	strval := p.StringVal(name, "")
	if strval != "" {
		if val, err := strconv.Atoi(strval); err == nil {
			return val
		}
	}
	return defaultval
}

func (p *EnvironConfigReader) BoolVal(name string, defaultval bool) bool {
	strval := p.StringVal(name, "")
	if strval != "" {
		val := strings.ToLower(strval)

		trues := []string{"1", "true", "t", "yes"}
		for _, t := range trues {
			if val == t {
				return true
			}
		}

		falses := []string{"0", "false", "f", "no"}
		for _, f := range falses {
			if val == f {
				return false
			}
		}
	}
	return defaultval
}

func keyvalue(line []byte) error {
	pair := string(line)
	if idx := strings.Index(pair, "="); idx >= 0 {
		key, value := strings.TrimSpace(pair[:idx]), translate(strings.TrimSpace(pair[idx+1:]))
		if err := os.Setenv(key, value); err != nil {
			return err
		}
	} else if pair != "" {
		return ParseError(pair)
	}
	return nil
}

func translate(value string) string {
	result, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("echo -n \"%s\"", value)).Output()
	if err != nil {
		return ""
	}
	return string(result)
}<commit_msg>Prevent recursive call to ParseError string conversion.<commit_after>// Copyright 2015 The Serviced 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 utils

import (
	"bufio"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"
)

const buffersize = 1024

type ParseError struct {
	String string
}

func (err ParseError) Error() string {
	return fmt.Sprintf("could not parse line: %s", err.String)
}

type ConfigReader interface {
	StringVal(key, dflt string) string
	StringSlice(key string, dflt []string) []string
	IntVal(key string, dflt int) int
	BoolVal(key string, dflt bool) bool
}

type EnvironConfigReader struct {
	prefix string
}

func NewEnvironConfigReader(filename, prefix string) (*EnvironConfigReader, error) {
	r := &EnvironConfigReader{prefix}

	file, err := os.Open(filename)
	if err != nil {
		return r, err
	}
	defer file.Close()
	if err := r.parse(file); err != nil {
		return r, err
	}
	return r, nil
}

// NewEnvironOnlyConfigReader creates an EnvironConfigReader without parsing
// a file first.
func NewEnvironOnlyConfigReader(prefix string) *EnvironConfigReader {
	return &EnvironConfigReader{prefix}
}

// parse is a really dumb reader parser.  It maps only key values in the form
// of key=value and strips whitespaces surrounding either field.  If the format
// does not match, then and error will return.
func (p *EnvironConfigReader) parse(reader io.Reader) error {
	var (
		line string
		err  error
	)

	bufReader := bufio.NewReader(reader)
	for err != io.EOF {
		line, err = bufReader.ReadString('\n')
		if err != nil && err != io.EOF {
			return err
		}

		line = strings.TrimSpace(strings.Split(line, "#")[0])
		if err := keyvalue([]byte(line)); err != nil {
			return err
		}
	}
	return nil
}

func (p *EnvironConfigReader) StringVal(name string, defaultval string) string {
	if val := os.Getenv(p.prefix + name); val != "" {
		return val
	} else {
		return defaultval
	}
}

func (p *EnvironConfigReader) StringSlice(name string, defaultval []string) []string {
	strval := p.StringVal(name, "")
	if strval != "" {
		return strings.Split(strval, ",")
	}
	return defaultval
}

func (p *EnvironConfigReader) IntVal(name string, defaultval int) int {
	strval := p.StringVal(name, "")
	if strval != "" {
		if val, err := strconv.Atoi(strval); err == nil {
			return val
		}
	}
	return defaultval
}

func (p *EnvironConfigReader) BoolVal(name string, defaultval bool) bool {
	strval := p.StringVal(name, "")
	if strval != "" {
		val := strings.ToLower(strval)

		trues := []string{"1", "true", "t", "yes"}
		for _, t := range trues {
			if val == t {
				return true
			}
		}

		falses := []string{"0", "false", "f", "no"}
		for _, f := range falses {
			if val == f {
				return false
			}
		}
	}
	return defaultval
}

func keyvalue(line []byte) error {
	pair := string(line)
	if idx := strings.Index(pair, "="); idx >= 0 {
		key, value := strings.TrimSpace(pair[:idx]), translate(strings.TrimSpace(pair[idx+1:]))
		if err := os.Setenv(key, value); err != nil {
			return err
		}
	} else if pair != "" {
		return ParseError{pair}
	}
	return nil
}

func translate(value string) string {
	result, err := exec.Command("/bin/sh", "-c", fmt.Sprintf("echo -n \"%s\"", value)).Output()
	if err != nil {
		return ""
	}
	return string(result)
}<|endoftext|>