<commit_before>package wkt

import (
	"errors"
	"strconv"
	"strings"

	"github.com/paulmach/orb"
)

var (
	errEmptyGeometry              = errors.New("empty geometry")
	errUnMarshalPoint             = errors.New("unmarshal point error")
	errUnMarshalMultiPoint        = errors.New("unmarshal multipoint error")
	errUnMarshaLineString         = errors.New("unmarshal linestring error")
	errUnMarshaMultiLineString    = errors.New("unmarshal multilinestring error")
	errUnMarshaPolygon            = errors.New("unmarshal polygon error")
	errUnMarshaMultiPolygon       = errors.New("unmarshal multipolygon error")
	errUnMarshaGeometryCollection = errors.New("unmarshal collection error")

	errConvertToPoint              = errors.New("convert to point error")
	errConvertToMultiPoint         = errors.New("convert to multi point error")
	errConvertToLineString         = errors.New("convert to line string error")
	errConvertToMultiLineString    = errors.New("convert to multi line string error")
	errConvertToPolygon            = errors.New("convert to polygon error")
	errConvertToMultiPolygon       = errors.New("convert to multi polygon error")
	errConvertToGeometryCollection = errors.New("convert to geometry collection error")
)

// errWrap errWarp
func errWrap(err error, es ...error) error {
	s := make([]string, 0)
	if err != nil {
		s = append(s, err.Error())
	}

	for _, e := range es {
		if e != nil {
			s = append(s, e.Error())
		}
	}

	return errors.New(strings.Join(s, "\n"))
}

// UnmarshalPoint return point by parse wkt point string
func UnmarshalPoint(s string) (p orb.Point, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.Point{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.Point)
	if !ok {
		return orb.Point{}, errWrap(err, errConvertToPoint)
	}
	return g, nil
}

// UnmarshalMultiPoint return multipoint by parse wkt multipoint string
func UnmarshalMultiPoint(s string) (p orb.MultiPoint, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.MultiPoint{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.MultiPoint)
	if !ok {
		return orb.MultiPoint{}, errWrap(err, errConvertToMultiPoint)
	}
	return g, nil
}

// UnmarshalLineString return linestring by parse wkt linestring string
func UnmarshalLineString(s string) (p orb.LineString, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.LineString{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.LineString)
	if !ok {
		return orb.LineString{}, errWrap(err, errConvertToLineString)
	}
	return g, nil
}

// UnmarshalMultiLineString return linestring by parse wkt multilinestring string
func UnmarshalMultiLineString(s string) (p orb.MultiLineString, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.MultiLineString{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.MultiLineString)
	if !ok {
		return orb.MultiLineString{}, errWrap(err, errConvertToMultiLineString)
	}
	return g, nil
}

// UnmarshalPolygon return linestring by parse wkt polygon string
func UnmarshalPolygon(s string) (p orb.Polygon, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.Polygon{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.Polygon)
	if !ok {
		return orb.Polygon{}, errWrap(err, errConvertToPolygon)
	}
	return g, nil
}

// UnmarshalMultiPolygon return linestring by parse wkt multipolygon string
func UnmarshalMultiPolygon(s string) (p orb.MultiPolygon, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.MultiPolygon{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.MultiPolygon)
	if !ok {
		return orb.MultiPolygon{}, errWrap(err, errConvertToMultiPolygon)
	}
	return g, nil
}

// UnmarshalCollection return linestring by parse wkt collection string
func UnmarshalCollection(s string) (p orb.Collection, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.Collection{}, errWrap(err, errEmptyGeometry)
	}
	g, ok := geom.(orb.Collection)
	if !ok {
		return orb.Collection{}, errWrap(err, errConvertToGeometryCollection)
	}
	return g, nil
}

// trimSpaceBrackets trim space and brackets
func trimSpaceBrackets(s string) string {
	s = strings.Trim(s, " ")
	if s[0] == '(' {
		s = s[1:]
	}
	if s[len(s)-1] == ')' {
		s = s[:len(s)-1]
	}
	s = strings.Trim(s, " ")
	return s
}

// parsePoint pase point by (x y)
func parsePoint(s string) (p orb.Point, err error) {
	ps := strings.Split(s, " ")
	if len(ps) != 2 {
		return orb.Point{}, errors.New("can't get x,y")
	}
	x, err := strconv.ParseFloat(ps[0], 64)
	if err != nil {
		return orb.Point{}, err
	}
	y, err := strconv.ParseFloat(ps[1], 64)
	if err != nil {
		return orb.Point{}, err
	}
	p = orb.Point{x, y}
	return p, nil
}

// splitGeometryCollection split GEOMETRYCOLLECTION to more geometry
func splitGeometryCollection(s string) (r []string) {
	r = make([]string, 0)
	stack := make([]rune, 0)
	l := len(s)
	for i, v := range s {
		if !strings.Contains(string(stack), "(") {
			stack = append(stack, v)
			continue
		}
		if v >= 'A' && v < 'Z' {
			t := string(stack)
			r = append(r, t[:len(t)-1])
			stack = make([]rune, 0)
			stack = append(stack, v)
			continue
		}
		if i == l-1 {
			r = append(r, string(stack))
			continue
		}
		stack = append(stack, v)
	}
	return
}

// Unmarshal return a geometry by parsing the WKT string.
func Unmarshal(s string) (geom orb.Geometry, err error) {
	s = strings.ToUpper(strings.Trim(s, " "))
	switch {
	case strings.Contains(s, "GEOMETRYCOLLECTION"):
		if s == "GEOMETRYCOLLECTION " {
			return orb.Collection{}, nil
		}
		s = strings.Replace(s, "GEOMETRYCOLLECTION", "", -1)
		c := orb.Collection{}
		ms := splitGeometryCollection(s)
		if len(ms) == 0 {
			return nil, errUnMarshaGeometryCollection
		}
		for _, v := range ms {
			if len(v) == 0 {
				continue
			}
			g, err := Unmarshal(v)
			if err != nil {
				return nil, errWrap(errUnMarshaGeometryCollection, err)
			}
			c = append(c, g)
		}
		geom = c

	case strings.Contains(s, "MULTIPOINT"):
		if s == "MULTIPOINT EMPTY" {
			return orb.MultiPoint{}, nil
		}
		s = strings.Replace(s, "MULTIPOINT", "", -1)
		s = trimSpaceBrackets(s)
		ps := strings.Split(s, ",")
		mp := orb.MultiPoint{}
		for _, p := range ps {
			tp, err := parsePoint(trimSpaceBrackets(p))
			if err != nil {
				return nil, errWrap(errUnMarshalPoint, err)
			}
			mp = append(mp, tp)
		}
		geom = mp

	case strings.Contains(s, "POINT"):
		s = strings.Replace(s, "POINT", "", -1)
		tp, err := parsePoint(trimSpaceBrackets(s))
		if err != nil {
			return nil, errWrap(errUnMarshalPoint, err)
		}
		geom = tp

	case strings.Contains(s, "MULTILINESTRING"):
		if s == "MULTILINESTRING EMPTY" {
			return orb.MultiLineString{}, nil
		}
		s = strings.Replace(s, "MULTILINESTRING", "", -1)
		ml := orb.MultiLineString{}
		for _, l := range strings.Split(trimSpaceBrackets(s), "),(") {
			tl := orb.LineString{}
			for _, p := range strings.Split(trimSpaceBrackets(l), ",") {
				tp, err := parsePoint(trimSpaceBrackets(p))
				if err != nil {
					return nil, errWrap(errUnMarshaMultiLineString, err)
				}
				tl = append(tl, tp)
			}
			ml = append(ml, tl)
		}
		geom = ml

	case strings.Contains(s, "LINESTRING"):
		if s == "LINESTRING EMPTY" {
			return orb.LineString{}, nil
		}
		s = strings.Replace(s, "LINESTRING", "", -1)
		s = trimSpaceBrackets(s)
		ps := strings.Split(s, ",")
		ls := orb.LineString{}
		for _, p := range ps {
			tp, err := parsePoint(trimSpaceBrackets(p))
			if err != nil {
				return nil, errWrap(errUnMarshaLineString, err)
			}
			ls = append(ls, tp)
		}
		geom = ls

	case strings.Contains(s, "MULTIPOLYGON"):
		if s == "MULTIPOLYGON EMPTY" {
			return orb.MultiPolygon{}, nil
		}
		s = strings.Replace(s, "MULTIPOLYGON", "", -1)
		mpol := orb.MultiPolygon{}
		for _, ps := range strings.Split(trimSpaceBrackets(s), ")),((") {
			pol := orb.Polygon{}
			for _, ls := range strings.Split(trimSpaceBrackets(ps), "),(") {
				ring := orb.Ring{}
				for _, p := range strings.Split(ls, ",") {
					tp, err := parsePoint(trimSpaceBrackets(p))
					if err != nil {
						return nil, errWrap(errUnMarshaMultiPolygon, err)
					}
					ring = append(ring, tp)
				}
				pol = append(pol, ring)
			}
			mpol = append(mpol, pol)
		}
		geom = mpol

	case strings.Contains(s, "POLYGON"):
		if s == "POLYGON EMPTY" {
			return orb.Polygon{}, nil
		}
		s = strings.Replace(s, "POLYGON", "", -1)
		s = trimSpaceBrackets(s)
		rs := strings.Split(s, "),(")
		if len(rs) == 1 {
			// ring
			ps := strings.Split(trimSpaceBrackets(s), ",")
			ring := orb.Ring{}
			for _, p := range ps {
				tp, err := parsePoint(trimSpaceBrackets(p))
				if err != nil {
					return nil, errWrap(errUnMarshaLineString, err)
				}
				ring = append(ring, tp)
			}
			geom = orb.Polygon{ring}
		} else {
			// more ring
			pol := orb.Polygon{}
			for _, r := range rs {
				ps := strings.Split(trimSpaceBrackets(r), ",")
				ring := orb.Ring{}
				for _, p := range ps {
					tp, err := parsePoint(trimSpaceBrackets(p))
					if err != nil {
						return nil, errWrap(errUnMarshaLineString, err)
					}
					ring = append(ring, tp)
				}
				pol = append(pol, ring)
			}
			geom = pol
		}
	default:
		return nil, errors.New("wkt: unsupported geometry")
	}

	return
}
<commit_msg>encoding/wkt: standardize errors to match other sub-packages<commit_after>package wkt

import (
	"errors"
	"strconv"
	"strings"

	"github.com/paulmach/orb"
)

var (
	// ErrNotWKT is returned when unmarshalling WKT and the data is not valid.
	ErrNotWKT = errors.New("wkt: invalid data")

	// ErrIncorrectGeometry is returned when unmarshalling WKT data into the wrong type.
	// For example, unmarshaling linestring data into a point.
	ErrIncorrectGeometry = errors.New("wkt: incorrect geometry")

	// ErrUnsupportedGeometry is returned when geometry type is not supported by this lib.
	ErrUnsupportedGeometry = errors.New("wkt: unsupported geometry")
)

// UnmarshalPoint return point by parse wkt point string
func UnmarshalPoint(s string) (p orb.Point, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.Point{}, err
	}
	g, ok := geom.(orb.Point)
	if !ok {
		return orb.Point{}, ErrIncorrectGeometry
	}
	return g, nil
}

// UnmarshalMultiPoint return multipoint by parse wkt multipoint string
func UnmarshalMultiPoint(s string) (p orb.MultiPoint, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return nil, err
	}

	g, ok := geom.(orb.MultiPoint)
	if !ok {
		return nil, ErrIncorrectGeometry
	}
	return g, nil
}

// UnmarshalLineString return linestring by parse wkt linestring string
func UnmarshalLineString(s string) (p orb.LineString, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return nil, err
	}
	g, ok := geom.(orb.LineString)
	if !ok {
		return nil, ErrIncorrectGeometry
	}
	return g, nil
}

// UnmarshalMultiLineString return linestring by parse wkt multilinestring string
func UnmarshalMultiLineString(s string) (p orb.MultiLineString, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return nil, err
	}
	g, ok := geom.(orb.MultiLineString)
	if !ok {
		return nil, ErrIncorrectGeometry
	}
	return g, nil
}

// UnmarshalPolygon return linestring by parse wkt polygon string
func UnmarshalPolygon(s string) (p orb.Polygon, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return nil, err
	}
	g, ok := geom.(orb.Polygon)
	if !ok {
		return nil, ErrIncorrectGeometry
	}
	return g, nil
}

// UnmarshalMultiPolygon return linestring by parse wkt multipolygon string
func UnmarshalMultiPolygon(s string) (p orb.MultiPolygon, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return nil, err
	}
	g, ok := geom.(orb.MultiPolygon)
	if !ok {
		return nil, ErrIncorrectGeometry
	}
	return g, nil
}

// UnmarshalCollection return linestring by parse wkt collection string
func UnmarshalCollection(s string) (p orb.Collection, err error) {
	geom, err := Unmarshal(s)
	if err != nil {
		return orb.Collection{}, err
	}
	g, ok := geom.(orb.Collection)
	if !ok {
		return nil, ErrIncorrectGeometry
	}
	return g, nil
}

// trimSpaceBrackets trim space and brackets
func trimSpaceBrackets(s string) string {
	s = strings.Trim(s, " ")
	if s[0] == '(' {
		s = s[1:]
	}
	if s[len(s)-1] == ')' {
		s = s[:len(s)-1]
	}

	return strings.Trim(s, " ")
}

// parsePoint pase point by (x y)
func parsePoint(s string) (p orb.Point, err error) {
	ps := strings.Split(s, " ")
	if len(ps) != 2 {
		return orb.Point{}, ErrNotWKT
	}

	x, err := strconv.ParseFloat(ps[0], 64)
	if err != nil {
		return orb.Point{}, err
	}

	y, err := strconv.ParseFloat(ps[1], 64)
	if err != nil {
		return orb.Point{}, err
	}

	return orb.Point{x, y}, nil
}

// splitGeometryCollection split GEOMETRYCOLLECTION to more geometry
func splitGeometryCollection(s string) (r []string) {
	r = make([]string, 0)
	stack := make([]rune, 0)
	l := len(s)
	for i, v := range s {
		if !strings.Contains(string(stack), "(") {
			stack = append(stack, v)
			continue
		}
		if v >= 'A' && v < 'Z' {
			t := string(stack)
			r = append(r, t[:len(t)-1])
			stack = make([]rune, 0)
			stack = append(stack, v)
			continue
		}
		if i == l-1 {
			r = append(r, string(stack))
			continue
		}
		stack = append(stack, v)
	}
	return
}

// Unmarshal return a geometry by parsing the WKT string.
func Unmarshal(s string) (geom orb.Geometry, err error) {
	s = strings.ToUpper(strings.Trim(s, " "))
	switch {
	case strings.Contains(s, "GEOMETRYCOLLECTION"):
		if s == "GEOMETRYCOLLECTION " {
			return orb.Collection{}, nil
		}
		s = strings.Replace(s, "GEOMETRYCOLLECTION", "", -1)
		c := orb.Collection{}
		ms := splitGeometryCollection(s)
		if len(ms) == 0 {
			return nil, err
		}
		for _, v := range ms {
			if len(v) == 0 {
				continue
			}
			g, err := Unmarshal(v)
			if err != nil {
				return nil, err
			}
			c = append(c, g)
		}
		geom = c

	case strings.Contains(s, "MULTIPOINT"):
		if s == "MULTIPOINT EMPTY" {
			return orb.MultiPoint{}, nil
		}
		s = strings.Replace(s, "MULTIPOINT", "", -1)
		s = trimSpaceBrackets(s)
		ps := strings.Split(s, ",")
		mp := orb.MultiPoint{}
		for _, p := range ps {
			tp, err := parsePoint(trimSpaceBrackets(p))
			if err != nil {
				return nil, err
			}
			mp = append(mp, tp)
		}
		geom = mp

	case strings.Contains(s, "POINT"):
		s = strings.Replace(s, "POINT", "", -1)
		tp, err := parsePoint(trimSpaceBrackets(s))
		if err != nil {
			return nil, err
		}
		geom = tp

	case strings.Contains(s, "MULTILINESTRING"):
		if s == "MULTILINESTRING EMPTY" {
			return orb.MultiLineString{}, nil
		}
		s = strings.Replace(s, "MULTILINESTRING", "", -1)
		ml := orb.MultiLineString{}
		for _, l := range strings.Split(trimSpaceBrackets(s), "),(") {
			tl := orb.LineString{}
			for _, p := range strings.Split(trimSpaceBrackets(l), ",") {
				tp, err := parsePoint(trimSpaceBrackets(p))
				if err != nil {
					return nil, err
				}
				tl = append(tl, tp)
			}
			ml = append(ml, tl)
		}
		geom = ml

	case strings.Contains(s, "LINESTRING"):
		if s == "LINESTRING EMPTY" {
			return orb.LineString{}, nil
		}
		s = strings.Replace(s, "LINESTRING", "", -1)
		s = trimSpaceBrackets(s)
		ps := strings.Split(s, ",")
		ls := orb.LineString{}
		for _, p := range ps {
			tp, err := parsePoint(trimSpaceBrackets(p))
			if err != nil {
				return nil, err
			}
			ls = append(ls, tp)
		}
		geom = ls

	case strings.Contains(s, "MULTIPOLYGON"):
		if s == "MULTIPOLYGON EMPTY" {
			return orb.MultiPolygon{}, nil
		}
		s = strings.Replace(s, "MULTIPOLYGON", "", -1)
		mpol := orb.MultiPolygon{}
		for _, ps := range strings.Split(trimSpaceBrackets(s), ")),((") {
			pol := orb.Polygon{}
			for _, ls := range strings.Split(trimSpaceBrackets(ps), "),(") {
				ring := orb.Ring{}
				for _, p := range strings.Split(ls, ",") {
					tp, err := parsePoint(trimSpaceBrackets(p))
					if err != nil {
						return nil, err
					}
					ring = append(ring, tp)
				}
				pol = append(pol, ring)
			}
			mpol = append(mpol, pol)
		}
		geom = mpol

	case strings.Contains(s, "POLYGON"):
		if s == "POLYGON EMPTY" {
			return orb.Polygon{}, nil
		}
		s = strings.Replace(s, "POLYGON", "", -1)
		s = trimSpaceBrackets(s)
		rs := strings.Split(s, "),(")
		if len(rs) == 1 {
			// ring
			ps := strings.Split(trimSpaceBrackets(s), ",")
			ring := orb.Ring{}
			for _, p := range ps {
				tp, err := parsePoint(trimSpaceBrackets(p))
				if err != nil {
					return nil, err
				}
				ring = append(ring, tp)
			}
			geom = orb.Polygon{ring}
		} else {
			// more ring
			pol := orb.Polygon{}
			for _, r := range rs {
				ps := strings.Split(trimSpaceBrackets(r), ",")
				ring := orb.Ring{}
				for _, p := range ps {
					tp, err := parsePoint(trimSpaceBrackets(p))
					if err != nil {
						return nil, err
					}
					ring = append(ring, tp)
				}
				pol = append(pol, ring)
			}
			geom = pol
		}
	default:
		return nil, ErrUnsupportedGeometry
	}

	return
}
<|endoftext|>


<commit_before>package api

import (
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/exercism/cli/debug"
)

var (
	// UserAgent lets the API know where the call is being made from.
	// It's overridden from the root command so that we can set the version.
	UserAgent = "github.com/exercism/cli"

	// DefaultHTTPClient configures a timeout to use by default.
	DefaultHTTPClient = &http.Client{Timeout: 10 * time.Second}
)

// Client is an http client that is configured for Exercism.
type Client struct {
	*http.Client
	ContentType string
	Token       string
	APIBaseURL  string
}

// NewClient returns an Exercism API client.
func NewClient(token, baseURL string) (*Client, error) {
	return &Client{
		Client:     DefaultHTTPClient,
		Token:      token,
		APIBaseURL: baseURL,
	}, nil
}

// NewRequest returns an http.Request with information for the Exercism API.
func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request, error) {
	if c.Client == nil {
		c.Client = DefaultHTTPClient
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", UserAgent)
	if c.ContentType == "" {
		req.Header.Set("Content-Type", "application/json")
	} else {
		req.Header.Set("Content-Type", c.ContentType)
	}
	if c.Token != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Token))
	}

	return req, nil
}

// Do performs an http.Request and optionally parses the response body into the given interface.
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	debug.DumpRequest(req)

	res, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}

	debug.DumpResponse(res)
	return res, nil
}

// TokenIsValid calls the API to determine whether the token is valid.
func (c *Client) TokenIsValid() (bool, error) {
	url := fmt.Sprintf("%s/validate_token", c.APIBaseURL)
	req, err := c.NewRequest("GET", url, nil)
	if err != nil {
		return false, err
	}
	resp, err := c.Do(req)
	if err != nil {
		return false, err
	}
	return resp.StatusCode == http.StatusOK, nil
}
<commit_msg>Add method to verify the base API url<commit_after>package api

import (
	"fmt"
	"io"
	"net/http"
	"time"

	"github.com/exercism/cli/debug"
)

var (
	// UserAgent lets the API know where the call is being made from.
	// It's overridden from the root command so that we can set the version.
	UserAgent = "github.com/exercism/cli"

	// DefaultHTTPClient configures a timeout to use by default.
	DefaultHTTPClient = &http.Client{Timeout: 10 * time.Second}
)

// Client is an http client that is configured for Exercism.
type Client struct {
	*http.Client
	ContentType string
	Token       string
	APIBaseURL  string
}

// NewClient returns an Exercism API client.
func NewClient(token, baseURL string) (*Client, error) {
	return &Client{
		Client:     DefaultHTTPClient,
		Token:      token,
		APIBaseURL: baseURL,
	}, nil
}

// NewRequest returns an http.Request with information for the Exercism API.
func (c *Client) NewRequest(method, url string, body io.Reader) (*http.Request, error) {
	if c.Client == nil {
		c.Client = DefaultHTTPClient
	}

	req, err := http.NewRequest(method, url, body)
	if err != nil {
		return nil, err
	}

	req.Header.Set("User-Agent", UserAgent)
	if c.ContentType == "" {
		req.Header.Set("Content-Type", "application/json")
	} else {
		req.Header.Set("Content-Type", c.ContentType)
	}
	if c.Token != "" {
		req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.Token))
	}

	return req, nil
}

// Do performs an http.Request and optionally parses the response body into the given interface.
func (c *Client) Do(req *http.Request) (*http.Response, error) {
	debug.DumpRequest(req)

	res, err := c.Client.Do(req)
	if err != nil {
		return nil, err
	}

	debug.DumpResponse(res)
	return res, nil
}

// TokenIsValid calls the API to determine whether the token is valid.
func (c *Client) TokenIsValid() (bool, error) {
	url := fmt.Sprintf("%s/validate_token", c.APIBaseURL)
	req, err := c.NewRequest("GET", url, nil)
	if err != nil {
		return false, err
	}
	resp, err := c.Do(req)
	if err != nil {
		return false, err
	}
	return resp.StatusCode == http.StatusOK, nil
}

// IsPingable calls the API /ping to determine whether the API can be reached.
func (c *Client) IsPingable() (bool, error) {
	url := fmt.Sprintf("%s/ping", c.APIBaseURL)
	req, err := c.NewRequest("GET", url, nil)
	if err != nil {
		return false, err
	}
	resp, err := c.Do(req)
	if err != nil {
		return false, err
	}
	return resp.StatusCode == http.StatusOK, nil
}
<|endoftext|>


<commit_before>package questagbot

import (
	"bytes"
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"

	"golang.org/x/net/context"

	hexapic "github.com/blan4/hexapic/core"
	martini "github.com/codegangsta/martini"
	binding "github.com/codegangsta/martini-contrib/binding"
	godotenv "github.com/joho/godotenv"
	appengine "google.golang.org/appengine"
	log "google.golang.org/appengine/log"
	urlfetch "google.golang.org/appengine/urlfetch"
)

// Update struct from telegram Webhook API
type Update struct {
	ID      int         `json:"update_id"`
	Message MessageMeta `json:"message"`
}

// MessageMeta struct from telegram Webhook API
type MessageMeta struct {
	ID   int    `json:"message_id"`
	Date int    `json:"date"`
	Text string `json:"text"`
	From User   `json:"from"`
	Chat User   `json:"chat"`
}

// User struct from telegram Webhook API
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`
}

// GetID return id as string
func (user User) GetID() string {
	return strconv.Itoa(user.ID)
}

// ReplyKeyboardMarkup is a object represents a custom keyboard
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`
	OneTimeKeyboard bool       `json:"one_time_keyboard"`
	Selective       bool       `json:"selective"`
}

// TextMessage is a object for sendMessage API method
type TextMessage struct {
	ChatID                int         `json:"chat_id"`
	Text                  string      `json:"text"`
	DisableWebPagePreview bool        `json:"disable_web_page_preview"`
	ReplyToMessageID      int         `json:"reply_to_message_id"`
	ReplyMarkup           interface{} `json:"reply_markup"`
}

// PhotoMessage is a object for sendPhoto API method
type PhotoMessage struct {
	ChatID           int         `json:"chat_id"`
	Photo            string      `json:"photo"`
	Caption          string      `json:"caption"`
	ReplyToMessageID int         `json:"reply_to_message_id"`
	ReplyMarkup      interface{} `json:"reply_markup"`
}

func appEngine(c martini.Context, r *http.Request) {
	c.Map(appengine.NewContext(r))
}

func init() {
	godotenv.Load("secrets.env")
	apiURL := fmt.Sprintf("https://api.telegram.org/bot%v/", os.Getenv("TELEGRAM_KEY"))

	m := martini.Classic()
	m.Use(appEngine)
	m.Use(martini.Logger())
	m.Get("/", func() string {
		return "Hello world"
	})
	m.Post("/bothook", binding.Bind(Update{}), func(c context.Context, update Update) string {
		log.Infof(c, "%v", update)
		//sendMessage(c, apiURL, update, "Hello")
		if err := sendChatAction(c, apiURL, update, "upload_photo"); err != nil {
			log.Criticalf(c, "Can't sendChatAction %v", err)
		}
		if err := sendPhoto(c, os.Getenv("INSTAGRAM_CLIENT_ID"), apiURL, update, "cat"); err != nil {
			log.Criticalf(c, "Can't sendPhoto %v", err)
		}
		return strconv.Itoa(update.ID)
	})
	http.Handle("/", m)
}

func sendMessage(c context.Context, apiURL string, data Update, text string) {
	httpClient := urlfetch.Client(c)
	query := url.Values{}
	query.Set("text", "Hello")
	query.Add("chat_id", strconv.Itoa(data.Message.Chat.ID))
	url := apiURL + "sendMessage"
	log.Infof(c, "%v", url)
	r, err := http.NewRequest("POST", url, bytes.NewBufferString(query.Encode()))
	if err != nil {
		log.Criticalf(c, "%v", err)
	}
	r.Header.Add("Content-Length", strconv.Itoa(len(query.Encode())))
	resp, err := httpClient.Do(r)
	defer resp.Body.Close()
	if err != nil {
		log.Criticalf(c, "%v", err)
	}
	log.Infof(c, "%v", resp)
}

func sendChatAction(c context.Context, apiURL string, data Update, action string) (err error) {
	httpClient := urlfetch.Client(c)
	query := url.Values{}
	query.Set("action", action)
	query.Add("chat_id", strconv.Itoa(data.Message.Chat.ID))
	url := apiURL + "sendChatAction"
	r, err := http.NewRequest("POST", url, bytes.NewBufferString(query.Encode()))
	if err != nil {
		return
	}
	r.Header.Add("Content-Length", strconv.Itoa(len(query.Encode())))
	resp, err := httpClient.Do(r)
	defer resp.Body.Close()
	log.Infof(c, "sendChatAction: %v %v", url, query.Encode())
	return
}

func sendPhoto(c context.Context, clientdID string, apiURL string, data Update, text string) (err error) {
	httpClient := urlfetch.Client(c)
	hexapicAPI := hexapic.NewSearchApi(clientdID, httpClient)
	hexapicAPI.Count = 4
	var (
		imageQuality = jpeg.Options{Quality: jpeg.DefaultQuality}
		b            bytes.Buffer
		fw           io.Writer
		imgs         []image.Image
	)
	w := multipart.NewWriter(&b)
	if fw, err = w.CreateFormField("chat_id"); err != nil {
		return
	}
	if _, err = fw.Write([]byte(data.Message.Chat.GetID())); err != nil {
		return
	}
	if fw, err = w.CreateFormField("caption"); err != nil {
		return
	}
	if _, err = fw.Write([]byte(text)); err != nil {
		return
	}
	if fw, err = w.CreateFormFile("photo", "image.jpg"); err != nil {
		return
	}
	imgs = hexapicAPI.SearchByTag("cat")
	img := hexapic.GenerateCollage(imgs, 2, 2)
	if err = jpeg.Encode(fw, img, &imageQuality); err != nil {
		return
	}
	w.Close()

	req, err := http.NewRequest("POST", apiURL+"sendPhoto", &b)
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	req.Header.Add("Content-Length", strconv.Itoa(b.Len()))
	res, err := httpClient.Do(req)
	defer res.Body.Close()
	if err != nil {
		return
	}
	if res.StatusCode != http.StatusOK {
		err = fmt.Errorf("bad status: %s", res.Status)
	}
	return
}
<commit_msg>add keyboard<commit_after>package questagbot

import (
	"bytes"
	"encoding/json"
	"fmt"
	"image"
	"image/jpeg"
	"io"
	"mime/multipart"
	"net/http"
	"net/url"
	"os"
	"strconv"

	"golang.org/x/net/context"

	hexapic "github.com/blan4/hexapic/core"
	martini "github.com/codegangsta/martini"
	binding "github.com/codegangsta/martini-contrib/binding"
	godotenv "github.com/joho/godotenv"
	appengine "google.golang.org/appengine"
	log "google.golang.org/appengine/log"
	urlfetch "google.golang.org/appengine/urlfetch"
)

// Update struct from telegram Webhook API
type Update struct {
	ID      int         `json:"update_id"`
	Message MessageMeta `json:"message"`
}

// MessageMeta struct from telegram Webhook API
type MessageMeta struct {
	ID   int    `json:"message_id"`
	Date int    `json:"date"`
	Text string `json:"text"`
	From User   `json:"from"`
	Chat User   `json:"chat"`
}

// User struct from telegram Webhook API
type User struct {
	ID        int    `json:"id"`
	FirstName string `json:"first_name"`
	LastName  string `json:"last_name"`
	Username  string `json:"username"`
}

// GetID return id as string
func (user User) GetID() string {
	return strconv.Itoa(user.ID)
}

// ReplyKeyboardMarkup is a object represents a custom keyboard
type ReplyKeyboardMarkup struct {
	Keyboard        [][]string `json:"keyboard"`
	ResizeKeyboard  bool       `json:"resize_keyboard"`
	OneTimeKeyboard bool       `json:"one_time_keyboard"`
	Selective       bool       `json:"selective"`
}

// TextMessage is a object for sendMessage API method
type TextMessage struct {
	ChatID                int         `json:"chat_id"`
	Text                  string      `json:"text"`
	DisableWebPagePreview bool        `json:"disable_web_page_preview"`
	ReplyToMessageID      int         `json:"reply_to_message_id"`
	ReplyMarkup           interface{} `json:"reply_markup"`
}

// PhotoMessage is a object for sendPhoto API method
type PhotoMessage struct {
	ChatID           int         `json:"chat_id"`
	Photo            string      `json:"photo"`
	Caption          string      `json:"caption"`
	ReplyToMessageID int         `json:"reply_to_message_id"`
	ReplyMarkup      interface{} `json:"reply_markup"`
}

func appEngine(c martini.Context, r *http.Request) {
	c.Map(appengine.NewContext(r))
}

func init() {
	godotenv.Load("secrets.env")
	apiURL := fmt.Sprintf("https://api.telegram.org/bot%v/", os.Getenv("TELEGRAM_KEY"))

	m := martini.Classic()
	m.Use(appEngine)
	m.Use(martini.Logger())
	m.Get("/", func() string {
		return "Hello world"
	})
	m.Post("/bothook", binding.Bind(Update{}), func(c context.Context, update Update) string {
		log.Infof(c, "%v", update)
		//sendMessage(c, apiURL, update, "Hello")
		if err := sendChatAction(c, apiURL, update, "upload_photo"); err != nil {
			log.Criticalf(c, "Can't sendChatAction %v", err)
		}
		if err := sendPhoto(c, os.Getenv("INSTAGRAM_CLIENT_ID"), apiURL, update, ""); err != nil {
			log.Criticalf(c, "Can't sendPhoto %v", err)
		}
		return strconv.Itoa(update.ID)
	})
	http.Handle("/", m)
}

func sendMessage(c context.Context, apiURL string, data Update, text string) {
	httpClient := urlfetch.Client(c)
	query := url.Values{}
	query.Set("text", "Hello")
	query.Add("chat_id", strconv.Itoa(data.Message.Chat.ID))
	url := apiURL + "sendMessage"
	log.Infof(c, "%v", url)
	r, err := http.NewRequest("POST", url, bytes.NewBufferString(query.Encode()))
	if err != nil {
		log.Criticalf(c, "%v", err)
	}
	r.Header.Add("Content-Length", strconv.Itoa(len(query.Encode())))
	resp, err := httpClient.Do(r)
	defer resp.Body.Close()
	if err != nil {
		log.Criticalf(c, "%v", err)
	}
	log.Infof(c, "%v", resp)
}

func sendChatAction(c context.Context, apiURL string, data Update, action string) (err error) {
	httpClient := urlfetch.Client(c)
	query := url.Values{}
	query.Set("action", action)
	query.Add("chat_id", strconv.Itoa(data.Message.Chat.ID))
	url := apiURL + "sendChatAction"
	r, err := http.NewRequest("POST", url, bytes.NewBufferString(query.Encode()))
	if err != nil {
		return
	}
	r.Header.Add("Content-Length", strconv.Itoa(len(query.Encode())))
	resp, err := httpClient.Do(r)
	defer resp.Body.Close()
	log.Infof(c, "sendChatAction: %v %v", url, query.Encode())
	return
}

func sendPhoto(c context.Context, clientdID string, apiURL string, data Update, text string) (err error) {
	httpClient := urlfetch.Client(c)
	hexapicAPI := hexapic.NewSearchApi(clientdID, httpClient)
	hexapicAPI.Count = 4
	var (
		imageQuality = jpeg.Options{Quality: jpeg.DefaultQuality}
		b            bytes.Buffer
		fw           io.Writer
		imgs         []image.Image
	)
	w := multipart.NewWriter(&b)
	if fw, err = w.CreateFormField("chat_id"); err != nil {
		return
	}
	if _, err = fw.Write([]byte(data.Message.Chat.GetID())); err != nil {
		return
	}
	if text != "" {
		if fw, err = w.CreateFormField("caption"); err != nil {
			return
		}
		if _, err = fw.Write([]byte(text)); err != nil {
			return
		}
	}
	if fw, err = w.CreateFormField("reply_markup"); err != nil {
		return
	}
	json, err := keyboardJSON()
	if err != nil {
		return
	}
	if _, err = fw.Write(json); err != nil {
		return
	}
	if fw, err = w.CreateFormFile("photo", "image.jpg"); err != nil {
		return
	}
	imgs = hexapicAPI.SearchByTag("cat")
	img := hexapic.GenerateCollage(imgs, 2, 2)
	if err = jpeg.Encode(fw, img, &imageQuality); err != nil {
		return
	}
	w.Close()

	req, err := http.NewRequest("POST", apiURL+"sendPhoto", &b)
	if err != nil {
		return
	}
	req.Header.Set("Content-Type", w.FormDataContentType())
	req.Header.Add("Content-Length", strconv.Itoa(b.Len()))
	res, err := httpClient.Do(req)
	defer res.Body.Close()
	if err != nil {
		return
	}
	if res.StatusCode != http.StatusOK {
		err = fmt.Errorf("bad status: %s", res.Status)
	}
	return
}

// keyboardJSON create json object for ReplyKeyboardMarkup
func keyboardJSON() (b []byte, err error) {
	km := &ReplyKeyboardMarkup{
		Keyboard:        [][]string{{"cat", "dog"}, {"nya", "chick"}},
		ResizeKeyboard:  true,
		OneTimeKeyboard: true,
		Selective:       false,
	}
	b, err = json.Marshal(km)
	return
}
<|endoftext|>


<commit_before>package mysql

import (
	"database/sql"
	"fmt"

	"github.com/M-O-S-E-S/mgm/mgm"
	//import mysql driver
	_ "github.com/go-sql-driver/mysql"
	"github.com/satori/go.uuid"
)

// GetRegions gets all region records from the database
func (db db) GetRegions() ([]mgm.Region, error) {
	con, err := sql.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:3306)/%v", db.user, db.password, db.host, db.database))
	if err != nil {
		return nil, err
	}
	defer con.Close()

	rows, err := con.Query(
		"Select uuid, name, size, httpPort, consolePort, consoleUname, consolePass, locX, locY, externalAddress, slaveAddress, isRunning, EstateName, status from regions, estate_map, estate_settings " +
			"where estate_map.RegionID = regions.uuid AND estate_map.EstateID = estate_settings.EstateID")
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var regions []mgm.Region
	for rows.Next() {
		r := mgm.Region{}
		err = rows.Scan(
			&r.UUID,
			&r.Name,
			&r.Size,
			&r.HTTPPort,
			&r.ConsolePort,
			&r.ConsoleUname,
			&r.ConsolePass,
			&r.LocX,
			&r.LocY,
			&r.ExternalAddress,
			&r.SlaveAddress,
			&r.IsRunning,
			&r.EstateName,
			&r.Status,
		)
		if err != nil {
			rows.Close()
			fmt.Println(err)
			return nil, err
		}
		regions = append(regions, r)
	}
	return regions, nil
}

// GetRegionsOnHost retrieves all region records for a specified host
func (db db) GetRegionsOnHost(host mgm.Host) ([]mgm.Region, error) {
	con, err := sql.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:3306)/%v", db.user, db.password, db.host, db.database))
	if err != nil {
		return nil, err
	}
	defer con.Close()

	rows, err := con.Query(
		"Select uuid, name, size, httpPort, consolePort, consoleUname, consolePass, locX, locY, externalAddress, slaveAddress, isRunning, status from regions "+
			"where slaveAddress=?", host.Address)
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var regions []mgm.Region
	for rows.Next() {
		r := mgm.Region{}
		err = rows.Scan(
			&r.UUID,
			&r.Name,
			&r.Size,
			&r.HTTPPort,
			&r.ConsolePort,
			&r.ConsoleUname,
			&r.ConsolePass,
			&r.LocX,
			&r.LocY,
			&r.ExternalAddress,
			&r.SlaveAddress,
			&r.IsRunning,
			&r.Status,
		)
		if err != nil {
			rows.Close()
			fmt.Println(err)
			return nil, err
		}
		regions = append(regions, r)
	}
	return regions, nil
}

// GetRegionsForUser retrieves region records for a user where the user owns the estate they are in, or is a manager for said estate
func (db db) GetRegionsForUser(guid uuid.UUID) ([]mgm.Region, error) {
	con, err := sql.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:3306)/%v", db.user, db.password, db.host, db.database))
	if err != nil {
		return nil, err
	}
	defer con.Close()

	rows, err := con.Query(
		"Select uuid, name, size, httpPort, consolePort, consoleUname, consolePass, locX, locY, externalAddress, slaveAddress, isRunning, EstateName, status from regions, estate_map, estate_settings " +
			"where estate_map.RegionID = regions.uuid AND estate_map.EstateID = estate_settings.EstateID AND uuid in " +
			"(SELECT RegionID FROM estate_map WHERE " +
			"EstateID in (SELECT EstateID FROM estate_settings WHERE EstateOwner=\"" + guid.String() + "\") OR " +
			"EstateID in (SELECT EstateID from estate_managers WHERE uuid=\"" + guid.String() + "\"))")
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var regions []mgm.Region
	for rows.Next() {
		r := mgm.Region{}
		err = rows.Scan(
			&r.UUID,
			&r.Name,
			&r.Size,
			&r.HTTPPort,
			&r.ConsolePort,
			&r.ConsoleUname,
			&r.ConsolePass,
			&r.LocX,
			&r.LocY,
			&r.ExternalAddress,
			&r.SlaveAddress,
			&r.IsRunning,
			&r.EstateName,
			&r.Status,
		)
		if err != nil {
			rows.Close()
			fmt.Println(err)
			return nil, err
		}
		regions = append(regions, r)
	}
	return regions, nil
}
<commit_msg>Removing status field from regions records<commit_after>package mysql

import (
	"database/sql"
	"fmt"

	"github.com/M-O-S-E-S/mgm/mgm"
	//import mysql driver
	_ "github.com/go-sql-driver/mysql"
	"github.com/satori/go.uuid"
)

// GetRegions gets all region records from the database
func (db db) GetRegions() ([]mgm.Region, error) {
	con, err := sql.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:3306)/%v", db.user, db.password, db.host, db.database))
	if err != nil {
		return nil, err
	}
	defer con.Close()

	rows, err := con.Query(
		"Select uuid, name, size, httpPort, consolePort, consoleUname, consolePass, locX, locY, externalAddress, slaveAddress, isRunning, EstateName from regions, estate_map, estate_settings " +
			"where estate_map.RegionID = regions.uuid AND estate_map.EstateID = estate_settings.EstateID")
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var regions []mgm.Region
	for rows.Next() {
		r := mgm.Region{}
		err = rows.Scan(
			&r.UUID,
			&r.Name,
			&r.Size,
			&r.HTTPPort,
			&r.ConsolePort,
			&r.ConsoleUname,
			&r.ConsolePass,
			&r.LocX,
			&r.LocY,
			&r.ExternalAddress,
			&r.SlaveAddress,
			&r.IsRunning,
			&r.EstateName,
		)
		if err != nil {
			rows.Close()
			fmt.Println(err)
			return nil, err
		}
		regions = append(regions, r)
	}
	return regions, nil
}

// GetRegionsOnHost retrieves all region records for a specified host
func (db db) GetRegionsOnHost(host mgm.Host) ([]mgm.Region, error) {
	con, err := sql.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:3306)/%v", db.user, db.password, db.host, db.database))
	if err != nil {
		return nil, err
	}
	defer con.Close()

	rows, err := con.Query(
		"Select uuid, name, size, httpPort, consolePort, consoleUname, consolePass, locX, locY, externalAddress, slaveAddress, isRunning from regions "+
			"where slaveAddress=?", host.Address)
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var regions []mgm.Region
	for rows.Next() {
		r := mgm.Region{}
		err = rows.Scan(
			&r.UUID,
			&r.Name,
			&r.Size,
			&r.HTTPPort,
			&r.ConsolePort,
			&r.ConsoleUname,
			&r.ConsolePass,
			&r.LocX,
			&r.LocY,
			&r.ExternalAddress,
			&r.SlaveAddress,
			&r.IsRunning,
		)
		if err != nil {
			rows.Close()
			fmt.Println(err)
			return nil, err
		}
		regions = append(regions, r)
	}
	return regions, nil
}

// GetRegionsForUser retrieves region records for a user where the user owns the estate they are in, or is a manager for said estate
func (db db) GetRegionsForUser(guid uuid.UUID) ([]mgm.Region, error) {
	con, err := sql.Open("mysql", fmt.Sprintf("%v:%v@tcp(%v:3306)/%v", db.user, db.password, db.host, db.database))
	if err != nil {
		return nil, err
	}
	defer con.Close()

	rows, err := con.Query(
		"Select uuid, name, size, httpPort, consolePort, consoleUname, consolePass, locX, locY, externalAddress, slaveAddress, isRunning, EstateName from regions, estate_map, estate_settings " +
			"where estate_map.RegionID = regions.uuid AND estate_map.EstateID = estate_settings.EstateID AND uuid in " +
			"(SELECT RegionID FROM estate_map WHERE " +
			"EstateID in (SELECT EstateID FROM estate_settings WHERE EstateOwner=\"" + guid.String() + "\") OR " +
			"EstateID in (SELECT EstateID from estate_managers WHERE uuid=\"" + guid.String() + "\"))")
	defer rows.Close()
	if err != nil {
		fmt.Println(err)
		return nil, err
	}

	var regions []mgm.Region
	for rows.Next() {
		r := mgm.Region{}
		err = rows.Scan(
			&r.UUID,
			&r.Name,
			&r.Size,
			&r.HTTPPort,
			&r.ConsolePort,
			&r.ConsoleUname,
			&r.ConsolePass,
			&r.LocX,
			&r.LocY,
			&r.ExternalAddress,
			&r.SlaveAddress,
			&r.IsRunning,
			&r.EstateName,
		)
		if err != nil {
			rows.Close()
			fmt.Println(err)
			return nil, err
		}
		regions = append(regions, r)
	}
	return regions, nil
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"strings"
	"time"

	"github.com/soniah/gosnmp"
	"gopkg.in/yaml.v2"
)

func LoadFile(filename string) (*Config, error) {
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	cfg := &Config{}
	err = yaml.UnmarshalStrict(content, cfg)
	if err != nil {
		return nil, err
	}
	return cfg, nil
}

var (
	DefaultAuth = Auth{
		Community:     "public",
		SecurityLevel: "noAuthNoPriv",
		AuthProtocol:  "MD5",
		PrivProtocol:  "DES",
	}
	DefaultWalkParams = WalkParams{
		Version:        2,
		MaxRepetitions: 25,
		Retries:        3,
		Timeout:        time.Second * 20,
		Auth:           DefaultAuth,
	}
	DefaultModule = Module{
		WalkParams: DefaultWalkParams,
	}
	DefaultRegexpExtract = RegexpExtract{
		Value: "$1",
	}
)

// Config for the snmp_exporter.
type Config map[string]*Module

type WalkParams struct {
	Version        int           `yaml:"version,omitempty"`
	MaxRepetitions uint8         `yaml:"max_repetitions,omitempty"`
	Retries        int           `yaml:"retries,omitempty"`
	Timeout        time.Duration `yaml:"timeout,omitempty"`
	Auth           Auth          `yaml:"auth,omitempty"`
}

type Module struct {
	// A list of OIDs.
	Walk       []string   `yaml:"walk,omitempty"`
	Get        []string   `yaml:"get,omitempty"`
	Metrics    []*Metric  `yaml:"metrics"`
	WalkParams WalkParams `yaml:",inline"`
}

func (c *WalkParams) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultWalkParams
	type plain WalkParams
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}

	if c.Version < 1 || c.Version > 3 {
		return fmt.Errorf("SNMP version must be 1, 2 or 3. Got: %d", c.Version)
	}
	if c.Version == 3 {
		if c.Auth.Username == "" {
			return fmt.Errorf("Auth username is missing, required for SNMPv3")
		}
		if c.Auth.SecurityLevel != "authPriv" &&
			c.Auth.SecurityLevel != "authNoPriv" && c.Auth.SecurityLevel != "noAuthNoPriv" {
			return fmt.Errorf("Security level must be one of authPriv, authNoPriv or noAuthNoPriv")
		}
		if c.Auth.Password == "" && c.Auth.SecurityLevel != "noAuthNoPriv" {
			return fmt.Errorf("Auth password is missing, required for SNMPv3 with auth.")
		}
		if c.Auth.AuthProtocol != "MD5" && c.Auth.AuthProtocol != "SHA" {
			return fmt.Errorf("Auth protocol must be SHA or MD5.")
		}
		if c.Auth.PrivProtocol != "DES" && c.Auth.PrivProtocol != "AES" {
			return fmt.Errorf("Priv protocol must be DES or AES.")
		}
		if c.Auth.PrivPassword == "" && c.Auth.SecurityLevel == "authPriv" {
			return fmt.Errorf("Priv password is missing, required for SNMPv3 with priv.")
		}
	}
	return nil
}

func (c *Module) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultModule
	type plain Module
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

// configureSNMP sets the various version and auth settings.
func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
	switch c.Version {
	case 1:
		g.Version = gosnmp.Version1
	case 2:
		g.Version = gosnmp.Version2c
	case 3:
		g.Version = gosnmp.Version3
	}
	g.Community = string(c.Auth.Community)
	g.ContextName = string(c.Auth.ContextName)

	// v3 security settings.
	g.SecurityModel = gosnmp.UserSecurityModel
	switch c.Auth.SecurityLevel {
	case "noAuthNoPriv":
		g.MsgFlags = gosnmp.NoAuthNoPriv
	case "authNoPriv":
		g.MsgFlags = gosnmp.AuthNoPriv
	case "authPriv":
		g.MsgFlags = gosnmp.AuthPriv
	}
	usm := &gosnmp.UsmSecurityParameters{
		UserName:                 c.Auth.Username,
		AuthenticationPassphrase: string(c.Auth.Password),
		PrivacyPassphrase:        string(c.Auth.PrivPassword),
	}
	switch c.Auth.AuthProtocol {
	case "SHA":
		usm.AuthenticationProtocol = gosnmp.SHA
	case "MD5":
		usm.AuthenticationProtocol = gosnmp.MD5
	}
	switch c.Auth.PrivProtocol {
	case "DES":
		usm.PrivacyProtocol = gosnmp.DES
	case "AES":
		usm.PrivacyProtocol = gosnmp.AES
	}
	g.SecurityParameters = usm
}

type Metric struct {
	Name           string                     `yaml:"name"`
	Oid            string                     `yaml:"oid"`
	Type           string                     `yaml:"type"`
	Help           string                     `yaml:"help"`
	Indexes        []*Index                   `yaml:"indexes,omitempty"`
	Lookups        []*Lookup                  `yaml:"lookups,omitempty"`
	RegexpExtracts map[string][]RegexpExtract `yaml:"regex_extracts,omitempty"`
}

func (c *Metric) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain Metric
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

type Index struct {
	Labelname string `yaml:"labelname"`
	Type      string `yaml:"type"`
	FixedSize int    `yaml:"fixed_size,omitempty"`
}

func (c *Index) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain Index
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

type Lookup struct {
	Labels    []string `yaml:"labels"`
	Labelname string   `yaml:"labelname"`
	Oid       string   `yaml:"oid"`
	Type      string   `yaml:"type"`
}

func (c *Lookup) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain Lookup
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

// Secret is a string that must not be revealed on marshaling.
type Secret string

// Hack for creating snmp.yml with the secret.
var (
	DoNotHideSecrets = false
)

// MarshalYAML implements the yaml.Marshaler interface.
func (s Secret) MarshalYAML() (interface{}, error) {
	if DoNotHideSecrets {
		return string(s), nil
	}
	if s != "" {
		return "<secret>", nil
	}
	return nil, nil
}

//UnmarshalYAML implements the yaml.Unmarshaler interface for Secrets.
func (s *Secret) UnmarshalYAML(unmarshal func(interface{}) error) error {
	type plain Secret
	return unmarshal((*plain)(s))
}

type Auth struct {
	Community     Secret `yaml:"community,omitempty"`
	SecurityLevel string `yaml:"security_level,omitempty"`
	Username      string `yaml:"username,omitempty"`
	Password      Secret `yaml:"password,omitempty"`
	AuthProtocol  string `yaml:"auth_protocol,omitempty"`
	PrivProtocol  string `yaml:"priv_protocol,omitempty"`
	PrivPassword  Secret `yaml:"priv_password,omitempty"`
	ContextName   string `yaml:"context_name,omitempty"`
}

func (c *Auth) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultAuth
	type plain Auth
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

type RegexpExtract struct {
	Value string `yaml:"value"`
	Regex Regexp `yaml:"regex"`
}

func (c *RegexpExtract) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultRegexpExtract
	type plain RegexpExtract
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

// Regexp encapsulates a regexp.Regexp and makes it YAML marshalable.
type Regexp struct {
	*regexp.Regexp
}

// MarshalYAML implements the yaml.Marshaler interface.
func (re Regexp) MarshalYAML() (interface{}, error) {
	if re.Regexp != nil {
		return re.String(), nil
	}
	return nil, nil
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	regex, err := regexp.Compile("^(?:" + s + ")$")
	if err != nil {
		return err
	}
	re.Regexp = regex
	return nil
}

func CheckOverflow(m map[string]interface{}, ctx string) error {
	if len(m) > 0 {
		var keys []string
		for k := range m {
			keys = append(keys, k)
		}
		return fmt.Errorf("unknown fields in %s: %s", ctx, strings.Join(keys, ", "))
	}
	return nil
}
<commit_msg>Small fixes and code clean-up (#293)<commit_after>package config

import (
	"fmt"
	"io/ioutil"
	"regexp"
	"time"

	"github.com/soniah/gosnmp"
	"gopkg.in/yaml.v2"
)

func LoadFile(filename string) (*Config, error) {
	content, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, err
	}
	cfg := &Config{}
	err = yaml.UnmarshalStrict(content, cfg)
	if err != nil {
		return nil, err
	}
	return cfg, nil
}

var (
	DefaultAuth = Auth{
		Community:     "public",
		SecurityLevel: "noAuthNoPriv",
		AuthProtocol:  "MD5",
		PrivProtocol:  "DES",
	}
	DefaultWalkParams = WalkParams{
		Version:        2,
		MaxRepetitions: 25,
		Retries:        3,
		Timeout:        time.Second * 20,
		Auth:           DefaultAuth,
	}
	DefaultModule = Module{
		WalkParams: DefaultWalkParams,
	}
	DefaultRegexpExtract = RegexpExtract{
		Value: "$1",
	}
)

// Config for the snmp_exporter.
type Config map[string]*Module

type WalkParams struct {
	Version        int           `yaml:"version,omitempty"`
	MaxRepetitions uint8         `yaml:"max_repetitions,omitempty"`
	Retries        int           `yaml:"retries,omitempty"`
	Timeout        time.Duration `yaml:"timeout,omitempty"`
	Auth           Auth          `yaml:"auth,omitempty"`
}

type Module struct {
	// A list of OIDs.
	Walk       []string   `yaml:"walk,omitempty"`
	Get        []string   `yaml:"get,omitempty"`
	Metrics    []*Metric  `yaml:"metrics"`
	WalkParams WalkParams `yaml:",inline"`
}

func (c *Module) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultModule
	type plain Module
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}

	wp := c.WalkParams

	if wp.Version < 1 || wp.Version > 3 {
		return fmt.Errorf("SNMP version must be 1, 2 or 3. Got: %d", wp.Version)
	}
	if wp.Version == 3 {
		switch wp.Auth.SecurityLevel {
		case "authPriv":
			if wp.Auth.PrivPassword == "" {
				return fmt.Errorf("Priv password is missing, required for SNMPv3 with priv.")
			}
			if wp.Auth.PrivProtocol != "DES" && wp.Auth.PrivProtocol != "AES" {
				return fmt.Errorf("Priv protocol must be DES or AES.")
			}
			fallthrough
		case "authNoPriv":
			if wp.Auth.Password == "" {
				return fmt.Errorf("Auth password is missing, required for SNMPv3 with auth.")
			}
			if wp.Auth.AuthProtocol != "MD5" && wp.Auth.AuthProtocol != "SHA" {
				return fmt.Errorf("Auth protocol must be SHA or MD5.")
			}
			fallthrough
		case "noAuthNoPriv":
			if wp.Auth.Username == "" {
				return fmt.Errorf("Auth username is missing, required for SNMPv3")
			}
		default:
			return fmt.Errorf("Security level must be one of authPriv, authNoPriv or noAuthNoPriv")
		}
	}
	return nil
}

// ConfigureSNMP sets the various version and auth settings.
func (c WalkParams) ConfigureSNMP(g *gosnmp.GoSNMP) {
	switch c.Version {
	case 1:
		g.Version = gosnmp.Version1
	case 2:
		g.Version = gosnmp.Version2c
	case 3:
		g.Version = gosnmp.Version3
	}
	g.Community = string(c.Auth.Community)
	g.ContextName = string(c.Auth.ContextName)

	// v3 security settings.
	g.SecurityModel = gosnmp.UserSecurityModel
	usm := &gosnmp.UsmSecurityParameters{
		UserName: c.Auth.Username,
	}
	auth, priv := false, false
	switch c.Auth.SecurityLevel {
	case "noAuthNoPriv":
		g.MsgFlags = gosnmp.NoAuthNoPriv
	case "authNoPriv":
		g.MsgFlags = gosnmp.AuthNoPriv
		auth = true
	case "authPriv":
		g.MsgFlags = gosnmp.AuthPriv
		auth = true
		priv = true
	}
	if auth {
		usm.AuthenticationPassphrase = string(c.Auth.Password)
		switch c.Auth.AuthProtocol {
		case "SHA":
			usm.AuthenticationProtocol = gosnmp.SHA
		case "MD5":
			usm.AuthenticationProtocol = gosnmp.MD5
		}
	}
	if priv {
		usm.PrivacyPassphrase = string(c.Auth.PrivPassword)
		switch c.Auth.PrivProtocol {
		case "DES":
			usm.PrivacyProtocol = gosnmp.DES
		case "AES":
			usm.PrivacyProtocol = gosnmp.AES
		}
	}
	g.SecurityParameters = usm
}

type Metric struct {
	Name           string                     `yaml:"name"`
	Oid            string                     `yaml:"oid"`
	Type           string                     `yaml:"type"`
	Help           string                     `yaml:"help"`
	Indexes        []*Index                   `yaml:"indexes,omitempty"`
	Lookups        []*Lookup                  `yaml:"lookups,omitempty"`
	RegexpExtracts map[string][]RegexpExtract `yaml:"regex_extracts,omitempty"`
}

type Index struct {
	Labelname string `yaml:"labelname"`
	Type      string `yaml:"type"`
	FixedSize int    `yaml:"fixed_size,omitempty"`
}

type Lookup struct {
	Labels    []string `yaml:"labels"`
	Labelname string   `yaml:"labelname"`
	Oid       string   `yaml:"oid"`
	Type      string   `yaml:"type"`
}

// Secret is a string that must not be revealed on marshaling.
type Secret string

// Hack for creating snmp.yml with the secret.
var (
	DoNotHideSecrets = false
)

// MarshalYAML implements the yaml.Marshaler interface.
func (s Secret) MarshalYAML() (interface{}, error) {
	if DoNotHideSecrets {
		return string(s), nil
	}
	if s != "" {
		return "<secret>", nil
	}
	return nil, nil
}

type Auth struct {
	Community     Secret `yaml:"community,omitempty"`
	SecurityLevel string `yaml:"security_level,omitempty"`
	Username      string `yaml:"username,omitempty"`
	Password      Secret `yaml:"password,omitempty"`
	AuthProtocol  string `yaml:"auth_protocol,omitempty"`
	PrivProtocol  string `yaml:"priv_protocol,omitempty"`
	PrivPassword  Secret `yaml:"priv_password,omitempty"`
	ContextName   string `yaml:"context_name,omitempty"`
}

type RegexpExtract struct {
	Value string `yaml:"value"`
	Regex Regexp `yaml:"regex"`
}

func (c *RegexpExtract) UnmarshalYAML(unmarshal func(interface{}) error) error {
	*c = DefaultRegexpExtract
	type plain RegexpExtract
	if err := unmarshal((*plain)(c)); err != nil {
		return err
	}
	return nil
}

// Regexp encapsulates a regexp.Regexp and makes it YAML marshalable.
type Regexp struct {
	*regexp.Regexp
}

// MarshalYAML implements the yaml.Marshaler interface.
func (re Regexp) MarshalYAML() (interface{}, error) {
	if re.Regexp != nil {
		return re.String(), nil
	}
	return nil, nil
}

// UnmarshalYAML implements the yaml.Unmarshaler interface.
func (re *Regexp) UnmarshalYAML(unmarshal func(interface{}) error) error {
	var s string
	if err := unmarshal(&s); err != nil {
		return err
	}
	regex, err := regexp.Compile("^(?:" + s + ")$")
	if err != nil {
		return err
	}
	re.Regexp = regex
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Config is used for reading a config file and flags.
// Inspired from spf13/viper.
package config

import (
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/bogem/nehm/applescript"
	"github.com/bogem/nehm/ui"
	"github.com/spf13/pflag"
)

var (
	configHash = make(map[string]string)
	configPath = path.Join(os.Getenv("HOME"), ".nehmconfig")
	configRead bool
	flags      = make(map[string]*pflag.Flag)
)

// Get has the behavior of returning the value associated with the first
// place from where it is set. Get will check value in the following order:
// flag, config file.
//
// Get returns a string. For a specific value you can use one of the Get____ methods.
func Get(key string) string {
	if !configRead {
		configRead = true
		read()
	}

	// flags first
	flag, exists := flags[key]
	if exists && flag.Changed {
		return flag.Value.String()
	}

	return configHash[key]
}

// read will discover and load the config file from disk.
func read() {
	configFile, err := os.Open(configPath)
	if os.IsNotExist(err) {
		ui.Error(nil, "There is no config file in your home directory")
		return
	}
	if err != nil {
		ui.Term(err, "couldn't open the config file")
	}

	configData, err := ioutil.ReadAll(configFile)
	if err != nil {
		ui.Term(err, "couldn't read the config file")
	}

	if err := yaml.Unmarshal(configData, configHash); err != nil {
		ui.Term(err, "couldn't unmarshal the config file")
	}
}

// GetPermalink returns the value associated with the key "permalink".
// It guarantees that will be returned non-blank string.
func GetPermalink() string {
	permalink := Get("permalink")
	if permalink == "" {
		ui.Term(nil, "You didn't set a permalink. Use flag '-p' or set permalink in config file.\nTo know, what is permalink, read FAQ.")
	}
	return permalink
}

// GetPermalink returns the value associated with the key "dl_folder".
// If key "dl_folder" is blank in config, then it returns path to
// home directory.
func GetDLFolder() string {
	dlFolder := Get("dl_folder")
	if dlFolder == "" {
		ui.Warning("You didn't set a download folder. Tracks will be downloaded to your home directory.")
		return os.Getenv("HOME")
	}
	return dlFolder
}

// GetItunesPlaylist returns the value associated with
// the key "itunes_playlist".
// If the OS of this computer isn't macOS, then it returns blank string.
func GetItunesPlaylist() string {
	playlist := ""
	if runtime.GOOS == "darwin" {
		playlist = Get("itunes_playlist")

		if playlist == "" {
			ui.Warning("You didn't set an iTunes playlist. Tracks won't be added to iTunes.")
			return playlist
		}

		playlistsList := applescript.ListOfPlaylists()
		if !strings.Contains(playlistsList, playlist) {
			ui.Term(nil, "Playlist "+playlist+" doesn't exist. Please enter correct name")
		}
	}
	return playlist
}

// Bind a specific key to a pflag (as used by cobra).
func BindPFlag(key string, flag *pflag.Flag) {
	flags[key] = flag
}
<commit_msg>Remove flags and override map to config<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

// Config is used for reading a config file and flags.
// Inspired from spf13/viper.
package config

import (
	"io/ioutil"
	"os"
	"path"
	"runtime"
	"strings"

	"gopkg.in/yaml.v2"

	"github.com/bogem/nehm/applescript"
	"github.com/bogem/nehm/ui"
)

var (
	override = make(map[string]string)
	config   = make(map[string]string)

	configPath = path.Join(os.Getenv("HOME"), ".nehmconfig")
	configRead bool
)

// Get has the behavior of returning the value associated with the first
// place from where it is set. Get will check value in the following order:
// flag, config file.
//
// Get returns a string. For a specific value you can use one of the Get____ methods.
func Get(key string) string {
	if value, exists := override[key]; exists {
		return value
	}

	if !configRead {
		configRead = true
		read()
	}
	return config[key]
}

// read will discover and load the config file from disk.
func read() {
	configFile, err := os.Open(configPath)
	if os.IsNotExist(err) {
		ui.Error(nil, "There is no config file in your home directory")
		return
	}
	if err != nil {
		ui.Term(err, "couldn't open the config file")
	}

	configData, err := ioutil.ReadAll(configFile)
	if err != nil {
		ui.Term(err, "couldn't read the config file")
	}

	if err := yaml.Unmarshal(configData, config); err != nil {
		ui.Term(err, "couldn't unmarshal the config file")
	}
}

// GetPermalink returns the value associated with the key "permalink".
// It guarantees that will be returned non-blank string.
func GetPermalink() string {
	permalink := Get("permalink")
	if permalink == "" {
		ui.Term(nil, "You didn't set a permalink. Use flag '-p' or set permalink in config file.\nTo know, what is permalink, read FAQ.")
	}
	return permalink
}

// GetPermalink returns the value associated with the key "dl_folder".
// If key "dl_folder" is blank in config, then it returns path to
// home directory.
func GetDLFolder() string {
	dlFolder := Get("dl_folder")
	if dlFolder == "" {
		ui.Warning("You didn't set a download folder. Tracks will be downloaded to your home directory.")
		return os.Getenv("HOME")
	}
	return dlFolder
}

// GetItunesPlaylist returns the value associated with
// the key "itunes_playlist".
// If the OS of this computer isn't macOS, then it returns blank string.
func GetItunesPlaylist() string {
	playlist := ""
	if runtime.GOOS == "darwin" {
		playlist = Get("itunes_playlist")

		if playlist == "" {
			ui.Warning("You didn't set an iTunes playlist. Tracks won't be added to iTunes.")
			return playlist
		}

		playlistsList := applescript.ListOfPlaylists()
		if !strings.Contains(playlistsList, playlist) {
			ui.Term(nil, "Playlist "+playlist+" doesn't exist. Please enter correct name")
		}
	}
	return playlist
}

// Sets the value for the key in the override regiser.
func Set(key, value string) {
	override[key] = value
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/spf13/viper"
)

var config *Config

// Config contains the configuration values of the application
type Config struct {
	Mode     Mode
	Address  string
	Port     int
	Database Database
}

// Mode is how is started the server, eg. production or development
type Mode string

const (
	// Production mode
	Production Mode = "production"
	// Development mode
	Development Mode = "development"
)

// Database contains the configuration values of the database
type Database struct {
	URL string
}

// GetConfig returns the configured instance of Config
func GetConfig() *Config {
	return config
}

// UseViper sets the configured instance of Config
func UseViper(viper *viper.Viper) {
	config = &Config{
		Mode: parseMode(viper.GetString("mode")),
		Port: viper.GetInt("port"),
		Database: Database{
			URL: viper.GetString("database.url"),
		},
	}
}

func parseMode(mode string) Mode {
	if mode == "production" {
		return Production
	}

	return Development
}
<commit_msg>Fix: read `address` from Viper<commit_after>package config

import (
	"github.com/spf13/viper"
)

var config *Config

// Config contains the configuration values of the application
type Config struct {
	Mode     Mode
	Address  string
	Port     int
	Database Database
}

// Mode is how is started the server, eg. production or development
type Mode string

const (
	// Production mode
	Production Mode = "production"
	// Development mode
	Development Mode = "development"
)

// Database contains the configuration values of the database
type Database struct {
	URL string
}

// GetConfig returns the configured instance of Config
func GetConfig() *Config {
	return config
}

// UseViper sets the configured instance of Config
func UseViper(viper *viper.Viper) {
	config = &Config{
		Mode:    parseMode(viper.GetString("mode")),
		Address: viper.GetString("address"),
		Port:    viper.GetInt("port"),
		Database: Database{
			URL: viper.GetString("database.url"),
		},
	}
}

func parseMode(mode string) Mode {
	if mode == "production" {
		return Production
	}

	return Development
}
<|endoftext|>


<commit_before>package config

import (
	"github.com/BurntSushi/toml"
	"github.com/mackerelio/mackerel-agent/logging"
)

var configLogger = logging.GetLogger("config")

type Config struct {
	Apibase         string
	Apikey          string
	Root            string
	Pidfile         string
	Roles           []string
	Verbose         bool
	Plugin          map[string]PluginConfigs
	DeprecatedSensu map[string]PluginConfigs `toml:"sensu"` // DEPRECATED this is for backward compatibility
}

type PluginConfigs map[string]PluginConfig

type PluginConfig struct {
	Command string
}

var DefaultConfig = &Config{
	Apibase: "https://mackerel.io",
	Root:    "/var/lib/mackerel-agent",
	Pidfile: "/var/run/mackerel-agent.pid",
	Roles:   []string{},
	Verbose: false,
}

func LoadConfig(conffile string) (Config, error) {
	config, err := LoadConfigFile(conffile)

	// set default values if config does not have values
	if config.Apibase == "" {
		config.Apibase = DefaultConfig.Apibase
	}
	if config.Root == "" {
		config.Root = DefaultConfig.Root
	}
	if config.Pidfile == "" {
		config.Pidfile = DefaultConfig.Pidfile
	}
	if config.Verbose == false {
		config.Verbose = DefaultConfig.Verbose
	}

	return config, err
}

func LoadConfigFile(file string) (Config, error) {
	var config Config
	if _, err := toml.DecodeFile(file, &config); err != nil {
		return config, err
	}

	// for backward compatibility
	// merges sensu configs to plugin configs
	if _, ok := config.DeprecatedSensu["checks"]; ok {
		configLogger.Warningf("'sensu.checks.*' config format is DEPRECATED. Please use 'plugin.metrics.*' format.")

		if config.Plugin == nil {
			config.Plugin = map[string]PluginConfigs{}
		}
		if _, ok := config.Plugin["metrics"]; !ok {
			config.Plugin["metrics"] = PluginConfigs{}
		}
		for k, v := range config.DeprecatedSensu["checks"] {
			config.Plugin["metrics"]["DEPRECATED-sensu-"+k] = v
		}
	}

	return config, nil
}
<commit_msg>Add config file name<commit_after>package config

import (
	"github.com/BurntSushi/toml"
	"github.com/mackerelio/mackerel-agent/logging"
)

var configLogger = logging.GetLogger("config")

type Config struct {
	Apibase         string
	Apikey          string
	Root            string
	Pidfile         string
	Conffile        string
	Roles           []string
	Verbose         bool
	Plugin          map[string]PluginConfigs
	DeprecatedSensu map[string]PluginConfigs `toml:"sensu"` // DEPRECATED this is for backward compatibility
}

type PluginConfigs map[string]PluginConfig

type PluginConfig struct {
	Command string
}

func LoadConfig(conffile string) (Config, error) {
	config, err := LoadConfigFile(conffile)

	// set default values if config does not have values
	if config.Apibase == "" {
		config.Apibase = DefaultConfig.Apibase
	}
	if config.Root == "" {
		config.Root = DefaultConfig.Root
	}
	if config.Pidfile == "" {
		config.Pidfile = DefaultConfig.Pidfile
	}
	if config.Verbose == false {
		config.Verbose = DefaultConfig.Verbose
	}

	return config, err
}

func LoadConfigFile(file string) (Config, error) {
	var config Config
	if _, err := toml.DecodeFile(file, &config); err != nil {
		return config, err
	}

	// for backward compatibility
	// merges sensu configs to plugin configs
	if _, ok := config.DeprecatedSensu["checks"]; ok {
		configLogger.Warningf("'sensu.checks.*' config format is DEPRECATED. Please use 'plugin.metrics.*' format.")

		if config.Plugin == nil {
			config.Plugin = map[string]PluginConfigs{}
		}
		if _, ok := config.Plugin["metrics"]; !ok {
			config.Plugin["metrics"] = PluginConfigs{}
		}
		for k, v := range config.DeprecatedSensu["checks"] {
			config.Plugin["metrics"]["DEPRECATED-sensu-"+k] = v
		}
	}

	return config, nil
}
<|endoftext|>


<commit_before>package config

import (
    "encoding/json"
    "io/ioutil"
)

type Config struct {
    Nick        string
    Host        string
    Networks    []string
    Servers     map[string] string
    Channels    map[string] string
    Passwords   map[string] string
    Plugins     []string
    Ignore      []string
}

func ReadConfig(path string) (Config, error) {
    var config Config

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

    err = json.Unmarshal(data, &config)
    if err != nil {
        return config, err
    }

    return config, nil
}
<commit_msg>Actually, these are arrays<commit_after>package config

import (
    "encoding/json"
    "io/ioutil"
)

type Config struct {
    Nick        string
    Host        string
    Networks    []string
    Servers     map[string] []string
    Channels    map[string] []string
    Passwords   map[string] string
    Plugins     []string
    Ignore      []string
}

func ReadConfig(path string) (Config, error) {
    var config Config

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

    err = json.Unmarshal(data, &config)
    if err != nil {
        return config, err
    }

    return config, nil
}
<|endoftext|>


<commit_before>package isolated

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("set-space-role command", func() {
	Describe("help text and argument validation", func() {
		When("--help flag is set", func() {
			It("Displays command usage to output", func() {
				session := helpers.CF("set-space-role", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say("set-space-role - Assign a space role to a user"))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say("cf set-space-role USERNAME ORG SPACE ROLE"))
				Eventually(session).Should(Say("ROLES:"))
				Eventually(session).Should(Say("'SpaceManager' - Invite and manage users, and enable features for a given space"))
				Eventually(session).Should(Say("'SpaceDeveloper' - Create and manage apps and services, and see logs and reports"))
				Eventually(session).Should(Say("'SpaceAuditor' - View logs, reports, and settings on this space"))
				Eventually(session).Should(Say("SEE ALSO:"))
				Eventually(session).Should(Say("space-users"))
				Eventually(session).Should(Exit(0))
			})
		})

		When("the role does not exit", func() {
			It("prints a useful error, prints help text, and exits 1", func() {
				session := helpers.CF("set-space-role", "some-user", "some-org", "some-space", "NotARealRole")
				Eventually(session.Err).Should(Say(`Incorrect Usage: ROLE must be "SpaceManager", "SpaceDeveloper" and "SpaceAuditor"`))
				Eventually(session).Should(Say(`NAME:`))
				Eventually(session).Should(Say(`\s+set-space-role - Assign a space role to a user`))
				Eventually(session).Should(Say(`USAGE:`))
				Eventually(session).Should(Say(`\s+cf set-space-role USERNAME ORG SPACE ROLE`))
				Eventually(session).Should(Say(`ROLES:`))
				Eventually(session).Should(Say(`\s+'SpaceManager' - Invite and manage users, and enable features for a given space`))
				Eventually(session).Should(Say(`\s+'SpaceDeveloper' - Create and manage apps and services, and see logs and reports`))
				Eventually(session).Should(Say(`\s+'SpaceAuditor' - View logs, reports, and settings on this space`))
				Eventually(session).Should(Say(`SEE ALSO:`))
				Eventually(session).Should(Say(`\s+space-users`))
				Eventually(session).Should(Exit(1))
			})
		})

		When("too few arguments are passed", func() {
			It("prints a useful error, prints help text, and exits 1", func() {
				session := helpers.CF("set-space-role", "not-enough", "arguments")
				Eventually(session.Err).Should(Say("Incorrect Usage: the required arguments `SPACE` and `ROLE` were not provided"))
				Eventually(session).Should(Say(`NAME:`))
				Eventually(session).Should(Say(`\s+set-space-role - Assign a space role to a user`))
				Eventually(session).Should(Say(`USAGE:`))
				Eventually(session).Should(Say(`\s+cf set-space-role USERNAME ORG SPACE ROLE`))
				Eventually(session).Should(Say(`ROLES:`))
				Eventually(session).Should(Say(`\s+'SpaceManager' - Invite and manage users, and enable features for a given space`))
				Eventually(session).Should(Say(`\s+'SpaceDeveloper' - Create and manage apps and services, and see logs and reports`))
				Eventually(session).Should(Say(`\s+'SpaceAuditor' - View logs, reports, and settings on this space`))
				Eventually(session).Should(Say(`SEE ALSO:`))
				Eventually(session).Should(Say(`\s+space-users`))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	When("logged in as admin", func() {
		var (
			orgName   string
			spaceName string
		)

		BeforeEach(func() {
			helpers.LoginCF()
			orgName = ReadOnlyOrg
			spaceName = ReadOnlySpace
		})

		When("the user exists", func() {
			var username string

			BeforeEach(func() {
				username, _ = helpers.CreateUser()
			})

			It("sets the space role for the user", func() {
				session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceAuditor")
				Eventually(session).Should(Say("Assigning role RoleSpaceAuditor to user %s in org %s / space %s as admin...", username, orgName, spaceName))
				Eventually(session).Should(Say("OK"))
				Eventually(session).Should(Exit(0))
			})

			When("the logged in user has insufficient permissions", func() {
				BeforeEach(func() {
					helpers.SwitchToSpaceRole(orgName, spaceName, "SpaceAuditor")
				})

				It("prints out the error message from CC API and exits 1", func() {
					session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceAuditor")
					Eventually(session).Should(Say("FAILED"))
					Eventually(session).Should(Say("Server error, status code: 403, error code: 10003, message: You are not authorized to perform the requested action"))
					Eventually(session).Should(Exit(1))
				})
			})

			When("the user already has the desired role", func() {
				BeforeEach(func() {
					session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceDeveloper")
					Eventually(session).Should(Say("Assigning role RoleSpaceDeveloper to user %s in org %s / space %s as admin...", username, orgName, spaceName))
					Eventually(session).Should(Exit(0))
				})

				It("is idempotent", func() {
					session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceDeveloper")
					Eventually(session).Should(Say("Assigning role RoleSpaceDeveloper to user %s in org %s / space %s as admin...", username, orgName, spaceName))
					Eventually(session).Should(Exit(0))
				})
			})

			When("the org does not exist", func() {
				It("prints an appropriate error and exits 1", func() {
					session := helpers.CF("set-space-role", username, "invalid-org", spaceName, "SpaceAuditor")
					Eventually(session).Should(Say("FAILED"))
					Eventually(session).Should(Say("Organization invalid-org not found"))
					Eventually(session).Should(Exit(1))
				})
			})

			When("the space does not exist", func() {
				It("prints an appropriate error and exits 1", func() {
					session := helpers.CF("set-space-role", username, orgName, "invalid-space", "SpaceAuditor")
					Eventually(session).Should(Say("FAILED"))
					Eventually(session).Should(Say("Space invalid-space not found"))
					Eventually(session).Should(Exit(1))
				})
			})
		})

		When("the user does not exist", func() {
			It("prints an appropriate error and exits 1", func() {
				session := helpers.CF("set-space-role", "not-exists", orgName, spaceName, "SpaceAuditor")
				Eventually(session).Should(Say("Assigning role RoleSpaceAuditor to user not-exists in org %s / space %s as admin...", orgName, spaceName))
				Eventually(session).Should(Say("FAILED"))
				Eventually(session).Should(Say("Server error, status code: 404, error code: 20003, message: The user could not be found: not-exists"))
				Eventually(session).Should(Exit(1))
			})
		})
	})
})
<commit_msg>Test set-space-role with extra args<commit_after>package isolated

import (
	"code.cloudfoundry.org/cli/integration/helpers"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	. "github.com/onsi/gomega/gbytes"
	. "github.com/onsi/gomega/gexec"
)

var _ = Describe("set-space-role command", func() {
	Describe("help text and argument validation", func() {
		When("--help flag is set", func() {
			It("Displays command usage to output", func() {
				session := helpers.CF("set-space-role", "--help")
				Eventually(session).Should(Say("NAME:"))
				Eventually(session).Should(Say("set-space-role - Assign a space role to a user"))
				Eventually(session).Should(Say("USAGE:"))
				Eventually(session).Should(Say("cf set-space-role USERNAME ORG SPACE ROLE"))
				Eventually(session).Should(Say("ROLES:"))
				Eventually(session).Should(Say("'SpaceManager' - Invite and manage users, and enable features for a given space"))
				Eventually(session).Should(Say("'SpaceDeveloper' - Create and manage apps and services, and see logs and reports"))
				Eventually(session).Should(Say("'SpaceAuditor' - View logs, reports, and settings on this space"))
				Eventually(session).Should(Say("SEE ALSO:"))
				Eventually(session).Should(Say("space-users"))
				Eventually(session).Should(Exit(0))
			})
		})

		When("the role does not exit", func() {
			It("prints a useful error, prints help text, and exits 1", func() {
				session := helpers.CF("set-space-role", "some-user", "some-org", "some-space", "NotARealRole")
				Eventually(session.Err).Should(Say(`Incorrect Usage: ROLE must be "SpaceManager", "SpaceDeveloper" and "SpaceAuditor"`))
				Eventually(session).Should(Say(`NAME:`))
				Eventually(session).Should(Say(`\s+set-space-role - Assign a space role to a user`))
				Eventually(session).Should(Say(`USAGE:`))
				Eventually(session).Should(Say(`\s+cf set-space-role USERNAME ORG SPACE ROLE`))
				Eventually(session).Should(Say(`ROLES:`))
				Eventually(session).Should(Say(`\s+'SpaceManager' - Invite and manage users, and enable features for a given space`))
				Eventually(session).Should(Say(`\s+'SpaceDeveloper' - Create and manage apps and services, and see logs and reports`))
				Eventually(session).Should(Say(`\s+'SpaceAuditor' - View logs, reports, and settings on this space`))
				Eventually(session).Should(Say(`SEE ALSO:`))
				Eventually(session).Should(Say(`\s+space-users`))
				Eventually(session).Should(Exit(1))
			})
		})

		When("too few arguments are passed", func() {
			It("prints a useful error, prints help text, and exits 1", func() {
				session := helpers.CF("set-space-role", "not-enough", "arguments")
				Eventually(session.Err).Should(Say("Incorrect Usage: the required arguments `SPACE` and `ROLE` were not provided"))
				Eventually(session).Should(Say(`NAME:`))
				Eventually(session).Should(Say(`\s+set-space-role - Assign a space role to a user`))
				Eventually(session).Should(Say(`USAGE:`))
				Eventually(session).Should(Say(`\s+cf set-space-role USERNAME ORG SPACE ROLE`))
				Eventually(session).Should(Say(`ROLES:`))
				Eventually(session).Should(Say(`\s+'SpaceManager' - Invite and manage users, and enable features for a given space`))
				Eventually(session).Should(Say(`\s+'SpaceDeveloper' - Create and manage apps and services, and see logs and reports`))
				Eventually(session).Should(Say(`\s+'SpaceAuditor' - View logs, reports, and settings on this space`))
				Eventually(session).Should(Say(`SEE ALSO:`))
				Eventually(session).Should(Say(`\s+space-users`))
				Eventually(session).Should(Exit(1))
			})
		})

		When("too many arguments are passed", func() {
			It("prints a useful error, prints help text, and exits 1", func() {
				session := helpers.CF("set-space-role", "some-user", "some-org", "some-space", "SpaceAuditor", "some-extra-argument")
				Eventually(session).Should(Say("Incorrect Usage. Requires USERNAME, ORG, SPACE, ROLE as arguments"))
				Eventually(session).Should(Say(`NAME:`))
				Eventually(session).Should(Say(`\s+set-space-role - Assign a space role to a user`))
				Eventually(session).Should(Say(`USAGE:`))
				Eventually(session).Should(Say(`\s+cf set-space-role USERNAME ORG SPACE ROLE`))
				Eventually(session).Should(Say(`ROLES:`))
				Eventually(session).Should(Say(`\s+'SpaceManager' - Invite and manage users, and enable features for a given space`))
				Eventually(session).Should(Say(`\s+'SpaceDeveloper' - Create and manage apps and services, and see logs and reports`))
				Eventually(session).Should(Say(`\s+'SpaceAuditor' - View logs, reports, and settings on this space`))
				Eventually(session).Should(Exit(1))
			})
		})
	})

	When("logged in as admin", func() {
		var (
			orgName   string
			spaceName string
		)

		BeforeEach(func() {
			helpers.LoginCF()
			orgName = ReadOnlyOrg
			spaceName = ReadOnlySpace
		})

		When("the user exists", func() {
			var username string

			BeforeEach(func() {
				username, _ = helpers.CreateUser()
			})

			It("sets the space role for the user", func() {
				session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceAuditor")
				Eventually(session).Should(Say("Assigning role RoleSpaceAuditor to user %s in org %s / space %s as admin...", username, orgName, spaceName))
				Eventually(session).Should(Say("OK"))
				Eventually(session).Should(Exit(0))
			})

			When("the logged in user has insufficient permissions", func() {
				BeforeEach(func() {
					helpers.SwitchToSpaceRole(orgName, spaceName, "SpaceAuditor")
				})

				It("prints out the error message from CC API and exits 1", func() {
					session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceAuditor")
					Eventually(session).Should(Say("FAILED"))
					Eventually(session).Should(Say("Server error, status code: 403, error code: 10003, message: You are not authorized to perform the requested action"))
					Eventually(session).Should(Exit(1))
				})
			})

			When("the user already has the desired role", func() {
				BeforeEach(func() {
					session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceDeveloper")
					Eventually(session).Should(Say("Assigning role RoleSpaceDeveloper to user %s in org %s / space %s as admin...", username, orgName, spaceName))
					Eventually(session).Should(Exit(0))
				})

				It("is idempotent", func() {
					session := helpers.CF("set-space-role", username, orgName, spaceName, "SpaceDeveloper")
					Eventually(session).Should(Say("Assigning role RoleSpaceDeveloper to user %s in org %s / space %s as admin...", username, orgName, spaceName))
					Eventually(session).Should(Exit(0))
				})
			})

			When("the org does not exist", func() {
				It("prints an appropriate error and exits 1", func() {
					session := helpers.CF("set-space-role", username, "invalid-org", spaceName, "SpaceAuditor")
					Eventually(session).Should(Say("FAILED"))
					Eventually(session).Should(Say("Organization invalid-org not found"))
					Eventually(session).Should(Exit(1))
				})
			})

			When("the space does not exist", func() {
				It("prints an appropriate error and exits 1", func() {
					session := helpers.CF("set-space-role", username, orgName, "invalid-space", "SpaceAuditor")
					Eventually(session).Should(Say("FAILED"))
					Eventually(session).Should(Say("Space invalid-space not found"))
					Eventually(session).Should(Exit(1))
				})
			})
		})

		When("the user does not exist", func() {
			It("prints an appropriate error and exits 1", func() {
				session := helpers.CF("set-space-role", "not-exists", orgName, spaceName, "SpaceAuditor")
				Eventually(session).Should(Say("Assigning role RoleSpaceAuditor to user not-exists in org %s / space %s as admin...", orgName, spaceName))
				Eventually(session).Should(Say("FAILED"))
				Eventually(session).Should(Say("Server error, status code: 404, error code: 20003, message: The user could not be found: not-exists"))
				Eventually(session).Should(Exit(1))
			})
		})
	})
})
<|endoftext|>