<commit_before>/*
 * Go Solr, a Solr library written in Go.
 * Original author Rich Taylor, 2012 - http://rsty.org/, http://github.com/rtt/
 * Released under the "do whatever the fuck you want" license. http://sam.zoy.org/wtfpl/
 */

package solr

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

/*
 * Represents a "connection"; actually just a host and port
 * (and probably at some point a Solr Core name)
 */
type Connection struct {
	Host    string
	Port    int
	Core    string
	Version []int
}

/*
 * Represents a Solr document, as returned by Select queries
 */
type Document struct {
	Fields map[string]interface{}
}

/*
 * Represents a FacetCount for a Facet
 */
type FacetCount struct {
	Value string
	Count int
}

/* chunked size of facet solr return format */
var facet_chunk_size int = 2

/*
 * Represents a Facet with a name and count
 */
type Facet struct {
	Name   string       // accepts_4x4s
	Counts []FacetCount // a set of values
}

/*
 * Represents a collection of solr documents
 * and various other metrics
 */
type DocumentCollection struct {
	Facets     []Facet
	Collection []Document
	NumFacets  int // convenience...
	NumFound   int
	Start      int
}

/*
 * Represents a Solr response
 */
type SelectResponse struct {
	Results *DocumentCollection
	Status  int
	QTime   int
	// TODO: Debug info as well?
}

/*
 * Represents an error from Solr
 */
type ErrorResponse struct {
	Message string
	Status  int
}

type UpdateResponse struct {
	Success bool
}

/*
 * Holds URL parameters
 */
type URLParamMap map[string][]string

/*
 * Query represents a query with various params
 */
type Query struct {
	Params     URLParamMap
	Rows       int
	Start      int
	Sort       string
	DefType    string
	Debug      bool
	OmitHeader bool
}

/*
 * Query.String() returns the Query in solr query string format
 */
func (q *Query) String() string {
	// TODO: this is kinda ugly
	s := []string{}

	if len(q.Params) > 0 {
		s = append(s, EncodeURLParamMap(&q.Params))
	}

	if q.Rows != 0 {
		s = append(s, fmt.Sprintf("rows=%d", q.Rows))
	}

	if q.Start != 0 {
		s = append(s, fmt.Sprintf("start=%d", q.Start))
	}

	if q.Sort != "" {
		s = append(s, fmt.Sprintf("sort=%s", q.Sort))
	}

	if q.DefType != "" {
		s = append(s, fmt.Sprintf("deftype=%s", q.DefType))
	}

	if q.Debug {
		s = append(s, fmt.Sprintf("debugQuery=true"))
	}

	if q.OmitHeader {
		s = append(s, fmt.Sprintf("omitHeader=true"))
	}

	return strings.Join(s, "&")
}

/*
 * DocumentCollection.Get() returns the document in the collection
 * at position i
 */
func (d *DocumentCollection) Get(i int) *Document {
	return &d.Collection[i]
}

/*
 * DocumentCollection.Len() returns the amount of documents
 * in the collection
 */
func (d *DocumentCollection) Len() int {
	return len(d.Collection)
}

/*
 * Document.Field() returns the value of the given field name in the document
 */
func (document Document) Field(field string) interface{} {
	r, _ := document.Fields[field]
	return r
}

/*
 * Document.Doc() returns the raw document (map)
 */
func (document Document) Doc() map[string]interface{} {
	return document.Fields
}

func (r SelectResponse) String() string {
	return fmt.Sprintf("SelectResponse: %d Results, Status: %d, QTime: %d", r.Results.Len(), r.Status, r.QTime)
}

func (r ErrorResponse) String() string {
	return fmt.Sprintf("Solr Error: [code: %d, msg: \"%s\"]", r.Status, r.Message)
}

func (r UpdateResponse) String() string {
	if r.Success {
		return fmt.Sprintf("UpdateResponse: OK")
	}
	return fmt.Sprintf("UpdateResponse: FAIL")
}

/*
 * Performs a GET request to the given url
 * Returns a []byte containing the response body
 */
func HTTPGet(url string) ([]byte, error) {

	r, err := http.Get(url)
	defer r.Body.Close()

	if err != nil {
		return nil, fmt.Errorf("GET failed (%s)", url)
	}

	// read the response and check
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, fmt.Errorf("Response read failed")
	}

	return body, nil
}

/*
 * Performs a HTTP Post request. Takes:
 * * A url
 * * Headers, in the format [][]string{} (e.g., [[key, val], [key, val], ...])
 * * A payload (post request body) which can be nil
 * * Returns the body of the response and an error if necessary
 */
func HTTPPost(url string, headers [][]string, payload *[]byte) ([]byte, error) {
	// setup post client
	client := &http.Client{}
	req, err := http.NewRequest("POST", url, bytes.NewReader(*payload))

	// add headers
	if len(headers) > 0 {
		for i := range headers {
			req.Header.Add(headers[i][0], headers[i][1])
		}
	}

	// perform request
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if err != nil {
		return nil, fmt.Errorf(fmt.Sprintf("POST request failed: %s", err))
	}

	// read response, check & return
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return body, nil
}

/*
 * Returns a URLEncoded version of a Param Map
 * E.g., ParamMap[foo:bar omg:wtf] => "foo=bar&omg=wtf"
 * TODO: This isn't exactly safe and there's probably a library pkg to do this already...
 */
func EncodeURLParamMap(m *URLParamMap) string {
	r := []string{}

	for k, v := range *m {
		l := len(v)
		for x := 0; x < l; x++ {
			r = append(r, fmt.Sprintf("%s=%s", k, v[x]))
		}
	}

	return strings.Join(r, "&")
}

/*
 * Generates a Solr query string from a connection and a query string
 */
func SolrSelectString(c *Connection, q string) string {
	return fmt.Sprintf("http://%s:%d/solr/select?wt=json&%s", c.Host, c.Port, q)
}

/*
 * Generates a Solr update query string. Adds ?commit=true
 * if commit arg is true.
 */
func SolrUpdateString(c *Connection, commit bool) string {
	s := fmt.Sprintf("http://%s:%d/solr/update", c.Host, c.Port)
	if commit {
		return fmt.Sprintf("%s?commit=true", s)
	}
	return s
}

/*
 * Decodes a json formatted []byte into an interface{} type
 */
func BytesToJSON(b *[]byte) (*interface{}, error) {
	var container interface{}
	err := json.Unmarshal(*b, &container)

	if err != nil {
		return nil, fmt.Errorf("Response decode error")
	}

	return &container, nil
}

/*
 * Encodes a map[string]interface{} to bytes and returns
 * a pointer to said bytes
 */
func JSONToBytes(m map[string]interface{}) (*[]byte, error) {
	b, err := json.Marshal(m)
	if err != nil {
		return nil, fmt.Errorf("Failed to encode JSON")
	}

	return &b, nil
}

/*
 * Takes a JSON formatted Solr response (interface{}, not []byte)
 * And returns a *Response
 */
func BuildResponse(j *interface{}) (*SelectResponse, error) {

	// look for a response element, bail if not present
	response_root := (*j).(map[string]interface{})
	response := response_root["response"]
	if response == nil {
		return nil, fmt.Errorf("Supplied interface appears invalid (missing response)")
	}

	// begin Response creation
	r := SelectResponse{}

	// do status & qtime, if possible
	r_header := (*j).(map[string]interface{})["responseHeader"].(map[string]interface{})
	if r_header != nil {
		r.Status = int(r_header["status"].(float64))
		r.QTime = int(r_header["QTime"].(float64))
	}

	// now do docs, if they exist in the response
	docs := response.(map[string]interface{})["docs"].([]interface{})
	if docs != nil {
		// the total amount of results, irrespective of the amount returned in the response
		num_found := int(response.(map[string]interface{})["numFound"].(float64))

		// and the amount actually returned
		num_results := len(docs)

		coll := DocumentCollection{}
		coll.NumFound = num_found

		ds := []Document{}

		for i := 0; i < num_results; i++ {
			ds = append(ds, Document{docs[i].(map[string]interface{})})
		}

		coll.Collection = ds
		r.Results = &coll
	}

	// facets
	facet_counts := response_root["facet_counts"].(map[string]interface{})
	if facet_counts != nil {
		// do counts if they exist
		facet_fields := facet_counts["facet_fields"].(map[string]interface{})
		facets := []Facet{}
		if facet_fields != nil {
			// iterate over each facet field, create facet & counts for each field
			for k, v := range facet_fields {
				f := Facet{Name: k}
				chunked := chunk(v.([]interface{}), facet_chunk_size)
				lc := len(chunked)
				for i := 0; i < lc; i++ {
					f.Counts = append(f.Counts, FacetCount{
						Value: chunked[i][0].(string),
						Count: int(chunked[i][1].(float64)),
					})
				}
				facets = append(facets, f)
			}
		}

		// add Facets to collection
		r.Results.Facets = facets
		r.Results.NumFacets = len(facets)
	}

	return &r, nil
}

/*
 * Decodes a HTTP (Solr) response and returns a Response
 */
func SelectResponseFromHTTPResponse(b []byte) (*SelectResponse, error) {
	j, err := BytesToJSON(&b)
	if err != nil {
		return nil, fmt.Errorf("Unable to decode")
	}

	resp, err := BuildResponse(j)
	if err != nil {
		return nil, fmt.Errorf("Error building response")
	}

	return resp, nil
}

/*
 * Determines whether a decoded response from Solr
 * is an error response or not. Returns a bool (true if error)
 * and an ErrorResponse (if the response is an error response)
 * otherwise nil
 */
func SolrErrorResponse(m map[string]interface{}) (bool, *ErrorResponse) {
	// check for existance of "error" key
	if _, found := m["error"]; found {
		error := m["error"].(map[string]interface{})
		return true, &ErrorResponse{
			Message: error["msg"].(string),
			Status:  int(error["code"].(float64)),
		}
	}
	return false, nil
}

/*
 * Similar to python's itertools.izip_longest;
 * takes an array and chunks it according to a given splice size
 * eg: chnunk([1,2,3,4,5,6], 2) == [[1,2], [3,4], [5,6]]
 */
func chunk(s []interface{}, sz int) [][]interface{} {
	r := [][]interface{}{}
	j := len(s)
	for i := 0; i < j; i += sz {
		r = append(r, s[i:i+sz])
	}
	return r
}

/*
 * Inits a new Connection to a Solr instance
 * Note: this doesn't actually hold a connection, its just
 *       a container for holding a hostname & port
 */
func Init(host string, port int) (*Connection, error) {

	if len(host) == 0 {
		return nil, fmt.Errorf("Invalid hostname (must be length >= 1)")
	}

	if port <= 0 || port > 65535 {
		return nil, fmt.Errorf("Invalid port (must be 1..65535")
	}

	return &Connection{Host: host, Port: port}, nil
}

/*
 * Performs a Select query given a Query
 */
func (c *Connection) Select(q *Query) (*SelectResponse, error) {
	body, err := HTTPGet(SolrSelectString(c, q.String()))

	if err != nil {
		return nil, fmt.Errorf("Some sort of http failure") // TODO: investigate how net/http fails
	}

	r, err := SelectResponseFromHTTPResponse(body)

	if err != nil {
		return nil, err
	}

	return r, nil
}

/*
 * Performs a raw Select query given a raw query string
 */
func (c *Connection) SelectRaw(q string) (*SelectResponse, error) {
	body, err := HTTPGet(SolrSelectString(c, q))

	if err != nil {
		return nil, fmt.Errorf("Some sort of http failure") // TODO: investigate how net/http fails
	}

	r, err := SelectResponseFromHTTPResponse(body)

	if err != nil {
		return nil, err
	}

	return r, nil
}

/*
 * Performs a Solr Update query against a given update document
 * specified in a map[string]interface{} type
 * NOTE: Requires JSON updates to be enabled, see;
 * http://wiki.apache.org/solr/UpdateJSON
 * FUTURE: Will ask for solr version details in Connection and
 * act appropriately
 */
func (c *Connection) Update(m map[string]interface{}, commit bool) (*UpdateResponse, error) {

	// encode "json" to a byte array & check
	payload, err := JSONToBytes(m)
	if err != nil {
		return nil, err
	}

	// perform request
	resp, err := HTTPPost(
		SolrUpdateString(c, commit),
		[][]string{{"Content-Type", "application/json"}},
		payload)

	if err != nil {
		return nil, err
	}

	// decode the response & check
	decoded, err := BytesToJSON(&resp)
	if err != nil {
		return nil, err
	}

	error, report := SolrErrorResponse((*decoded).(map[string]interface{}))
	if error {
		return nil, fmt.Errorf(fmt.Sprintf("%s", *report))
	}

	return &UpdateResponse{true}, nil
}

// func (c *Connection) Commit() (*UpdateResponse, error) {

// }
<commit_msg>prevent panic mode is solr is not found. prevent panic mode if facets are not part of the query<commit_after>/*
 * Go Solr, a Solr library written in Go.
 * Original author Rich Taylor, 2012 - http://rsty.org/, http://github.com/rtt/
 * Released under the "do whatever the fuck you want" license. http://sam.zoy.org/wtfpl/
 */

package solr

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
)

/*
 * Represents a "connection"; actually just a host and port
 * (and probably at some point a Solr Core name)
 */
type Connection struct {
	Host    string
	Port    int
	Core    string
	Version []int
}

/*
 * Represents a Solr document, as returned by Select queries
 */
type Document struct {
	Fields map[string]interface{}
}

/*
 * Represents a FacetCount for a Facet
 */
type FacetCount struct {
	Value string
	Count int
}

/* chunked size of facet solr return format */
var facet_chunk_size int = 2

/*
 * Represents a Facet with a name and count
 */
type Facet struct {
	Name   string       // accepts_4x4s
	Counts []FacetCount // a set of values
}

/*
 * Represents a collection of solr documents
 * and various other metrics
 */
type DocumentCollection struct {
	Facets     []Facet
	Collection []Document
	NumFacets  int // convenience...
	NumFound   int
	Start      int
}

/*
 * Represents a Solr response
 */
type SelectResponse struct {
	Results *DocumentCollection
	Status  int
	QTime   int
	// TODO: Debug info as well?
}

/*
 * Represents an error from Solr
 */
type ErrorResponse struct {
	Message string
	Status  int
}

type UpdateResponse struct {
	Success bool
}

/*
 * Holds URL parameters
 */
type URLParamMap map[string][]string

/*
 * Query represents a query with various params
 */
type Query struct {
	Params     URLParamMap
	Rows       int
	Start      int
	Sort       string
	DefType    string
	Debug      bool
	OmitHeader bool
}

/*
 * Query.String() returns the Query in solr query string format
 */
func (q *Query) String() string {
	// TODO: this is kinda ugly
	s := []string{}

	if len(q.Params) > 0 {
		s = append(s, EncodeURLParamMap(&q.Params))
	}

	if q.Rows != 0 {
		s = append(s, fmt.Sprintf("rows=%d", q.Rows))
	}

	if q.Start != 0 {
		s = append(s, fmt.Sprintf("start=%d", q.Start))
	}

	if q.Sort != "" {
		s = append(s, fmt.Sprintf("sort=%s", q.Sort))
	}

	if q.DefType != "" {
		s = append(s, fmt.Sprintf("deftype=%s", q.DefType))
	}

	if q.Debug {
		s = append(s, fmt.Sprintf("debugQuery=true"))
	}

	if q.OmitHeader {
		s = append(s, fmt.Sprintf("omitHeader=true"))
	}

	return strings.Join(s, "&")
}

/*
 * DocumentCollection.Get() returns the document in the collection
 * at position i
 */
func (d *DocumentCollection) Get(i int) *Document {
	return &d.Collection[i]
}

/*
 * DocumentCollection.Len() returns the amount of documents
 * in the collection
 */
func (d *DocumentCollection) Len() int {
	return len(d.Collection)
}

/*
 * Document.Field() returns the value of the given field name in the document
 */
func (document Document) Field(field string) interface{} {
	r, _ := document.Fields[field]
	return r
}

/*
 * Document.Doc() returns the raw document (map)
 */
func (document Document) Doc() map[string]interface{} {
	return document.Fields
}

func (r SelectResponse) String() string {
	return fmt.Sprintf("SelectResponse: %d Results, Status: %d, QTime: %d", r.Results.Len(), r.Status, r.QTime)
}

func (r ErrorResponse) String() string {
	return fmt.Sprintf("Solr Error: [code: %d, msg: \"%s\"]", r.Status, r.Message)
}

func (r UpdateResponse) String() string {
	if r.Success {
		return fmt.Sprintf("UpdateResponse: OK")
	}
	return fmt.Sprintf("UpdateResponse: FAIL")
}

/*
 * Performs a GET request to the given url
 * Returns a []byte containing the response body
 */
func HTTPGet(url string) ([]byte, error) {

	r, err := http.Get(url)

	if err != nil {
		return nil, err
	}

	defer r.Body.Close()

	if err != nil {
		return nil, fmt.Errorf("GET failed (%s)", url)
	}

	// read the response and check
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return nil, fmt.Errorf("Response read failed")
	}

	return body, nil
}

/*
 * Performs a HTTP Post request. Takes:
 * * A url
 * * Headers, in the format [][]string{} (e.g., [[key, val], [key, val], ...])
 * * A payload (post request body) which can be nil
 * * Returns the body of the response and an error if necessary
 */
func HTTPPost(url string, headers [][]string, payload *[]byte) ([]byte, error) {
	// setup post client
	client := &http.Client{}
	req, err := http.NewRequest("POST", url, bytes.NewReader(*payload))

	// add headers
	if len(headers) > 0 {
		for i := range headers {
			req.Header.Add(headers[i][0], headers[i][1])
		}
	}

	// perform request
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	defer resp.Body.Close()

	if err != nil {
		return nil, fmt.Errorf(fmt.Sprintf("POST request failed: %s", err))
	}

	// read response, check & return
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	return body, nil
}

/*
 * Returns a URLEncoded version of a Param Map
 * E.g., ParamMap[foo:bar omg:wtf] => "foo=bar&omg=wtf"
 * TODO: This isn't exactly safe and there's probably a library pkg to do this already...
 */
func EncodeURLParamMap(m *URLParamMap) string {
	r := []string{}

	for k, v := range *m {
		l := len(v)
		for x := 0; x < l; x++ {
			r = append(r, fmt.Sprintf("%s=%s", k, v[x]))
		}
	}

	return strings.Join(r, "&")
}

/*
 * Generates a Solr query string from a connection and a query string
 */
func SolrSelectString(c *Connection, q string) string {
	return fmt.Sprintf("http://%s:%d/solr/select?wt=json&%s", c.Host, c.Port, q)
}

/*
 * Generates a Solr update query string. Adds ?commit=true
 * if commit arg is true.
 */
func SolrUpdateString(c *Connection, commit bool) string {
	s := fmt.Sprintf("http://%s:%d/solr/update", c.Host, c.Port)
	if commit {
		return fmt.Sprintf("%s?commit=true", s)
	}
	return s
}

/*
 * Decodes a json formatted []byte into an interface{} type
 */
func BytesToJSON(b *[]byte) (*interface{}, error) {
	var container interface{}
	err := json.Unmarshal(*b, &container)

	if err != nil {
		return nil, fmt.Errorf("Response decode error")
	}

	return &container, nil
}

/*
 * Encodes a map[string]interface{} to bytes and returns
 * a pointer to said bytes
 */
func JSONToBytes(m map[string]interface{}) (*[]byte, error) {
	b, err := json.Marshal(m)
	if err != nil {
		return nil, fmt.Errorf("Failed to encode JSON")
	}

	return &b, nil
}

/*
 * Takes a JSON formatted Solr response (interface{}, not []byte)
 * And returns a *Response
 */
func BuildResponse(j *interface{}) (*SelectResponse, error) {

	// look for a response element, bail if not present
	response_root := (*j).(map[string]interface{})
	response := response_root["response"]
	if response == nil {
		return nil, fmt.Errorf("Supplied interface appears invalid (missing response)")
	}

	// begin Response creation
	r := SelectResponse{}

	// do status & qtime, if possible
	r_header := (*j).(map[string]interface{})["responseHeader"].(map[string]interface{})
	if r_header != nil {
		r.Status = int(r_header["status"].(float64))
		r.QTime = int(r_header["QTime"].(float64))
	}

	// now do docs, if they exist in the response
	docs := response.(map[string]interface{})["docs"].([]interface{})
	if docs != nil {
		// the total amount of results, irrespective of the amount returned in the response
		num_found := int(response.(map[string]interface{})["numFound"].(float64))

		// and the amount actually returned
		num_results := len(docs)

		coll := DocumentCollection{}
		coll.NumFound = num_found

		ds := []Document{}

		for i := 0; i < num_results; i++ {
			ds = append(ds, Document{docs[i].(map[string]interface{})})
		}

		coll.Collection = ds
		r.Results = &coll
	}

	// facets
	facet_response, ok := response_root["facet_counts"].(interface{})
	if ok == true {
		facet_counts := facet_response.(map[string]interface{})
		if facet_counts != nil {
			// do counts if they exist
			facet_fields := facet_counts["facet_fields"].(map[string]interface{})
			facets := []Facet{}
			if facet_fields != nil {
				// iterate over each facet field, create facet & counts for each field
				for k, v := range facet_fields {
					f := Facet{Name: k}
					chunked := chunk(v.([]interface{}), facet_chunk_size)
					lc := len(chunked)
					for i := 0; i < lc; i++ {
						f.Counts = append(f.Counts, FacetCount{
							Value: chunked[i][0].(string),
							Count: int(chunked[i][1].(float64)),
						})
					}
					facets = append(facets, f)
				}
			}

			// add Facets to collection
			r.Results.Facets = facets
			r.Results.NumFacets = len(facets)

		}
	}

	return &r, nil
}

/*
 * Decodes a HTTP (Solr) response and returns a Response
 */
func SelectResponseFromHTTPResponse(b []byte) (*SelectResponse, error) {
	j, err := BytesToJSON(&b)
	if err != nil {
		return nil, fmt.Errorf("Unable to decode")
	}

	resp, err := BuildResponse(j)
	if err != nil {
		return nil, fmt.Errorf("Error building response")
	}

	return resp, nil
}

/*
 * Determines whether a decoded response from Solr
 * is an error response or not. Returns a bool (true if error)
 * and an ErrorResponse (if the response is an error response)
 * otherwise nil
 */
func SolrErrorResponse(m map[string]interface{}) (bool, *ErrorResponse) {
	// check for existance of "error" key
	if _, found := m["error"]; found {
		error := m["error"].(map[string]interface{})
		return true, &ErrorResponse{
			Message: error["msg"].(string),
			Status:  int(error["code"].(float64)),
		}
	}
	return false, nil
}

/*
 * Similar to python's itertools.izip_longest;
 * takes an array and chunks it according to a given splice size
 * eg: chnunk([1,2,3,4,5,6], 2) == [[1,2], [3,4], [5,6]]
 */
func chunk(s []interface{}, sz int) [][]interface{} {
	r := [][]interface{}{}
	j := len(s)
	for i := 0; i < j; i += sz {
		r = append(r, s[i:i+sz])
	}
	return r
}

/*
 * Inits a new Connection to a Solr instance
 * Note: this doesn't actually hold a connection, its just
 *       a container for holding a hostname & port
 */
func Init(host string, port int) (*Connection, error) {

	if len(host) == 0 {
		return nil, fmt.Errorf("Invalid hostname (must be length >= 1)")
	}

	if port <= 0 || port > 65535 {
		return nil, fmt.Errorf("Invalid port (must be 1..65535")
	}

	return &Connection{Host: host, Port: port}, nil
}

/*
 * Performs a Select query given a Query
 */
func (c *Connection) Select(q *Query) (*SelectResponse, error) {
	body, err := HTTPGet(SolrSelectString(c, q.String()))

	if err != nil {
		return nil, fmt.Errorf("Some sort of http failure") // TODO: investigate how net/http fails
	}

	r, err := SelectResponseFromHTTPResponse(body)

	if err != nil {
		return nil, err
	}

	return r, nil
}

/*
 * Performs a raw Select query given a raw query string
 */
func (c *Connection) SelectRaw(q string) (*SelectResponse, error) {
	body, err := HTTPGet(SolrSelectString(c, q))

	if err != nil {
		return nil, fmt.Errorf("Some sort of http failure") // TODO: investigate how net/http fails
	}

	r, err := SelectResponseFromHTTPResponse(body)

	if err != nil {
		return nil, err
	}

	return r, nil
}

/*
 * Performs a Solr Update query against a given update document
 * specified in a map[string]interface{} type
 * NOTE: Requires JSON updates to be enabled, see;
 * http://wiki.apache.org/solr/UpdateJSON
 * FUTURE: Will ask for solr version details in Connection and
 * act appropriately
 */
func (c *Connection) Update(m map[string]interface{}, commit bool) (*UpdateResponse, error) {

	// encode "json" to a byte array & check
	payload, err := JSONToBytes(m)
	if err != nil {
		return nil, err
	}

	// perform request
	resp, err := HTTPPost(
		SolrUpdateString(c, commit),
		[][]string{{"Content-Type", "application/json"}},
		payload)

	if err != nil {
		return nil, err
	}

	// decode the response & check
	decoded, err := BytesToJSON(&resp)
	if err != nil {
		return nil, err
	}

	error, report := SolrErrorResponse((*decoded).(map[string]interface{}))
	if error {
		return nil, fmt.Errorf(fmt.Sprintf("%s", *report))
	}

	return &UpdateResponse{true}, nil
}

// func (c *Connection) Commit() (*UpdateResponse, error) {

// }
<|endoftext|>


<commit_before>package instana

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

const (
	agentDiscoveryURL = "/com.instana.plugin.golang.discovery"
	agentTracesURL    = "/com.instana.plugin.golang/traces."
	agentDataURL      = "/com.instana.plugin.golang."
	agentEventURL     = "/com.instana.plugin.generic.event"
	agentProfilesURL  = "/com.instana.plugin.golang/profiles."
	agentDefaultHost  = "localhost"
	agentDefaultPort  = 42699
	agentHeader       = "Instana Agent"
)

type agentResponse struct {
	Pid    uint32 `json:"pid"`
	HostID string `json:"agentUuid"`
}

type discoveryS struct {
	PID   int      `json:"pid"`
	Name  string   `json:"name"`
	Args  []string `json:"args"`
	Fd    string   `json:"fd"`
	Inode string   `json:"inode"`
}

type fromS struct {
	PID    string `json:"e"`
	HostID string `json:"h"`
}

type agentS struct {
	sensor *sensorS
	fsm    *fsmS
	from   *fromS
	host   string
	client *http.Client
	logger LeveledLogger
}

func newAgent(sensor *sensorS) *agentS {
	sensor.logger.Debug("initializing agent")

	agent := &agentS{
		sensor: sensor,
		from:   &fromS{},
		client: &http.Client{Timeout: 5 * time.Second},
		logger: sensor.logger,
	}
	agent.fsm = newFSM(agent)

	return agent
}

func (r *agentS) setLogger(l LeveledLogger) {
	r.logger = l
}

func (r *agentS) makeURL(prefix string) string {
	return r.makeHostURL(r.host, prefix)
}

func (r *agentS) makeHostURL(host string, prefix string) string {
	var buffer bytes.Buffer

	buffer.WriteString("http://")
	buffer.WriteString(host)
	buffer.WriteString(":")
	buffer.WriteString(strconv.Itoa(r.sensor.options.AgentPort))
	buffer.WriteString(prefix)
	if prefix[len(prefix)-1:] == "." && r.from.PID != "" {
		buffer.WriteString(r.from.PID)
	}

	return buffer.String()
}

func (r *agentS) head(url string) (string, error) {
	return r.request(url, "HEAD", nil)
}

func (r *agentS) request(url string, method string, data interface{}) (string, error) {
	return r.fullRequestResponse(url, method, data, nil, "")
}

func (r *agentS) requestResponse(url string, method string, data interface{}, ret interface{}) (string, error) {
	return r.fullRequestResponse(url, method, data, ret, "")
}

func (r *agentS) requestHeader(url string, method string, header string) (string, error) {
	return r.fullRequestResponse(url, method, nil, nil, header)
}

func (r *agentS) fullRequestResponse(url string, method string, data interface{}, body interface{}, header string) (string, error) {
	var j []byte
	var ret string
	var err error
	var resp *http.Response
	var req *http.Request
	if data != nil {
		j, err = json.Marshal(data)
	}

	if err == nil {
		if j != nil {
			req, err = http.NewRequest(method, url, bytes.NewBuffer(j))
		} else {
			req, err = http.NewRequest(method, url, nil)
		}

		// Uncomment this to dump json payloads
		// log.debug(bytes.NewBuffer(j))

		if err == nil {
			req.Header.Set("Content-Type", "application/json")
			resp, err = r.client.Do(req)
			if err == nil {
				defer resp.Body.Close()

				if resp.StatusCode < 200 || resp.StatusCode >= 300 {
					err = errors.New(resp.Status)
				} else {
					if body != nil {
						var b []byte
						b, err = ioutil.ReadAll(resp.Body)
						json.Unmarshal(b, body)
					}

					if header != "" {
						ret = resp.Header.Get(header)
					}
				}
			}
		}
	}

	if err != nil {
		// Ignore errors while in announced stated (before ready) as
		// this is the time where the entity is registering in the Instana
		// backend and it will return 404 until it's done.
		if !r.fsm.fsm.Is("announced") {
			r.logger.Info(err, url)
		}
	}

	return ret, err
}

func (r *agentS) setFrom(from *fromS) {
	r.from = from
}

func (r *agentS) setHost(host string) {
	r.host = host
}

func (r *agentS) reset() {
	r.fsm.reset()
}
<commit_msg>Preserve the agent port within instana.agentS<commit_after>package instana

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"
)

const (
	agentDiscoveryURL = "/com.instana.plugin.golang.discovery"
	agentTracesURL    = "/com.instana.plugin.golang/traces."
	agentDataURL      = "/com.instana.plugin.golang."
	agentEventURL     = "/com.instana.plugin.generic.event"
	agentProfilesURL  = "/com.instana.plugin.golang/profiles."
	agentDefaultHost  = "localhost"
	agentDefaultPort  = 42699
	agentHeader       = "Instana Agent"
)

type agentResponse struct {
	Pid    uint32 `json:"pid"`
	HostID string `json:"agentUuid"`
}

type discoveryS struct {
	PID   int      `json:"pid"`
	Name  string   `json:"name"`
	Args  []string `json:"args"`
	Fd    string   `json:"fd"`
	Inode string   `json:"inode"`
}

type fromS struct {
	PID    string `json:"e"`
	HostID string `json:"h"`
}

type agentS struct {
	sensor *sensorS
	fsm    *fsmS
	from   *fromS
	host   string
	port   string
	client *http.Client
	logger LeveledLogger
}

func newAgent(sensor *sensorS) *agentS {
	sensor.logger.Debug("initializing agent")

	agent := &agentS{
		sensor: sensor,
		from:   &fromS{},
		port:   strconv.Itoa(sensor.options.AgentPort),
		client: &http.Client{Timeout: 5 * time.Second},
		logger: sensor.logger,
	}
	agent.fsm = newFSM(agent)

	return agent
}

func (r *agentS) setLogger(l LeveledLogger) {
	r.logger = l
}

func (r *agentS) makeURL(prefix string) string {
	return r.makeHostURL(r.host, prefix)
}

func (r *agentS) makeHostURL(host string, prefix string) string {
	var buffer bytes.Buffer

	buffer.WriteString("http://")
	buffer.WriteString(host)
	buffer.WriteString(":")
	buffer.WriteString(r.port)
	buffer.WriteString(prefix)
	if prefix[len(prefix)-1:] == "." && r.from.PID != "" {
		buffer.WriteString(r.from.PID)
	}

	return buffer.String()
}

func (r *agentS) head(url string) (string, error) {
	return r.request(url, "HEAD", nil)
}

func (r *agentS) request(url string, method string, data interface{}) (string, error) {
	return r.fullRequestResponse(url, method, data, nil, "")
}

func (r *agentS) requestResponse(url string, method string, data interface{}, ret interface{}) (string, error) {
	return r.fullRequestResponse(url, method, data, ret, "")
}

func (r *agentS) requestHeader(url string, method string, header string) (string, error) {
	return r.fullRequestResponse(url, method, nil, nil, header)
}

func (r *agentS) fullRequestResponse(url string, method string, data interface{}, body interface{}, header string) (string, error) {
	var j []byte
	var ret string
	var err error
	var resp *http.Response
	var req *http.Request
	if data != nil {
		j, err = json.Marshal(data)
	}

	if err == nil {
		if j != nil {
			req, err = http.NewRequest(method, url, bytes.NewBuffer(j))
		} else {
			req, err = http.NewRequest(method, url, nil)
		}

		// Uncomment this to dump json payloads
		// log.debug(bytes.NewBuffer(j))

		if err == nil {
			req.Header.Set("Content-Type", "application/json")
			resp, err = r.client.Do(req)
			if err == nil {
				defer resp.Body.Close()

				if resp.StatusCode < 200 || resp.StatusCode >= 300 {
					err = errors.New(resp.Status)
				} else {
					if body != nil {
						var b []byte
						b, err = ioutil.ReadAll(resp.Body)
						json.Unmarshal(b, body)
					}

					if header != "" {
						ret = resp.Header.Get(header)
					}
				}
			}
		}
	}

	if err != nil {
		// Ignore errors while in announced stated (before ready) as
		// this is the time where the entity is registering in the Instana
		// backend and it will return 404 until it's done.
		if !r.fsm.fsm.Is("announced") {
			r.logger.Info(err, url)
		}
	}

	return ret, err
}

func (r *agentS) setFrom(from *fromS) {
	r.from = from
}

func (r *agentS) setHost(host string) {
	r.host = host
}

func (r *agentS) reset() {
	r.fsm.reset()
}
<|endoftext|>


<commit_before>package main

import (
  "os"
  "fmt"
  "github.com/codegangsta/cli"
  "github.com/buildboxhq/buildbox-agent/buildbox"
)

var AppHelpTemplate = `The agent performs builds and sends the results back to Buildbox.

Usage:

  {{.Name}} command [arguments]

The comamnds are:

  {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Use "buildbox-agent help [command]" for more information about a command.

`

var CommandHelpTemplate = `Usage: buildbox-agent {{.Name}} [command options] [arguments...]

{{.Description}}

Options:
   {{range .Flags}}{{.}}
   {{end}}
`

var StartHelpDescription = `When a job is ready to run it will call the "bootstrap-script"
and pass it all the environment variables required for the job to run.
This script is responsible for checking out the code, and running the
actual build script defined in the project.

The agent will run any jobs within a PTY (Pseudo terminal) if available.

Example:

buildbox-agent start --access-token [access-token] \
                     --bootstrap-script ~/.buildbox/bootstrap.sh`

var RunHelpDescription = `Manually run a scheduled job. If the job has already assigned
to another agent, it will not run.

Example:

buildbox-agent run [job-id] --access-token [access-token]`

var BootstrapScriptDefault = "$HOME/.buildbox/bootstrap.sh"

var AgentAccessTokenEnv = "BUILDBOX_AGENT_ACCESS_TOKEN"
var AgentAccessTokenDefault = "$" + AgentAccessTokenEnv

func main() {
  cli.AppHelpTemplate = AppHelpTemplate
  cli.CommandHelpTemplate = CommandHelpTemplate

  app := cli.NewApp()
  app.Name = "buildbox-agent"
  app.Version = buildbox.Version

  // Define the actions for our CLI
  app.Commands = []cli.Command{
    {
      Name:  "start",
      Usage: "Starts the Buildbox agent",
      Description: StartHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", AgentAccessTokenDefault, "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "start")

        // Setup signal monitoring
        agent.MonitorSignals()

        // Start the agent
        agent.Start()
      },
    },
    {
      Name:  "run",
      Usage: "Manually run a scheduled job",
      Description: RunHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", "", "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "run")

        // Grab the first argument and use as the job id
        id := c.Args().First()

        // Validate the job id
        if id == "" {
          fmt.Printf("buildbox-agent: no job id specified.\nSee 'buildbox-agent help run'")
          os.Exit(1)
        }

        agent.Run(id)
      },
    },
  }

  // Default the default action
  app.Action = func(c *cli.Context) {
    cli.ShowAppHelp(c)
    os.Exit(1)
  }

  app.Run(os.Args)
}

func setupAgentFromCli(c *cli.Context, command string) *buildbox.Agent {
  // Init debugging
  if c.Bool("debug") {
    buildbox.LoggerInitDebug()
  }

  agentAccessToken := c.String("access-token")

  // Should we look to the environment for the agent access token?
  if agentAccessToken == AgentAccessTokenDefault {
    agentAccessToken = os.Getenv(AgentAccessTokenEnv)
  }

  if agentAccessToken == "" {
    fmt.Println("buildbox-agent: missing agent access token\nSee 'buildbox-agent start --help'")
    os.Exit(1)
  }

  bootstrapScript := c.String("bootstrap-script")

  // Expand the envionment variable.
  if bootstrapScript == BootstrapScriptDefault {
    bootstrapScript = os.ExpandEnv(bootstrapScript)
  }

  // Make sure the boostrap script exists.
  if _, err := os.Stat(bootstrapScript); os.IsNotExist(err) {
    print("buildbox-agent: no such file " + bootstrapScript + "\n")
    os.Exit(1)
  }

  // Set the agent options
  var agent buildbox.Agent;
  agent.BootstrapScript = bootstrapScript

  // Client specific options
  agent.Client.AgentAccessToken = agentAccessToken
  agent.Client.URL = c.String("url")

  // Setup the agent
  agent.Setup()

  // A nice welcome message
  buildbox.Logger.Infof("Started buildbox-agent `%s` (version %s)", agent.Name, buildbox.Version)

  return &agent
}
<commit_msg>Show PID in the logs when booting up.<commit_after>package main

import (
  "os"
  "fmt"
  "github.com/codegangsta/cli"
  "github.com/buildboxhq/buildbox-agent/buildbox"
)

var AppHelpTemplate = `The agent performs builds and sends the results back to Buildbox.

Usage:

  {{.Name}} command [arguments]

The comamnds are:

  {{range .Commands}}{{.Name}}{{with .ShortName}}, {{.}}{{end}}{{ "\t" }}{{.Usage}}
  {{end}}
Use "buildbox-agent help [command]" for more information about a command.

`

var CommandHelpTemplate = `Usage: buildbox-agent {{.Name}} [command options] [arguments...]

{{.Description}}

Options:
   {{range .Flags}}{{.}}
   {{end}}
`

var StartHelpDescription = `When a job is ready to run it will call the "bootstrap-script"
and pass it all the environment variables required for the job to run.
This script is responsible for checking out the code, and running the
actual build script defined in the project.

The agent will run any jobs within a PTY (Pseudo terminal) if available.

Example:

buildbox-agent start --access-token [access-token] \
                     --bootstrap-script ~/.buildbox/bootstrap.sh`

var RunHelpDescription = `Manually run a scheduled job. If the job has already assigned
to another agent, it will not run.

Example:

buildbox-agent run [job-id] --access-token [access-token]`

var BootstrapScriptDefault = "$HOME/.buildbox/bootstrap.sh"

var AgentAccessTokenEnv = "BUILDBOX_AGENT_ACCESS_TOKEN"
var AgentAccessTokenDefault = "$" + AgentAccessTokenEnv

func main() {
  cli.AppHelpTemplate = AppHelpTemplate
  cli.CommandHelpTemplate = CommandHelpTemplate

  app := cli.NewApp()
  app.Name = "buildbox-agent"
  app.Version = buildbox.Version

  // Define the actions for our CLI
  app.Commands = []cli.Command{
    {
      Name:  "start",
      Usage: "Starts the Buildbox agent",
      Description: StartHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", AgentAccessTokenDefault, "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "start")

        // Setup signal monitoring
        agent.MonitorSignals()

        // Start the agent
        agent.Start()
      },
    },
    {
      Name:  "run",
      Usage: "Manually run a scheduled job",
      Description: RunHelpDescription,
      Flags: []cli.Flag {
        cli.StringFlag{"access-token", "", "The access token used to identify the agent."},
        cli.StringFlag{"bootstrap-script", BootstrapScriptDefault, "Path to the bootstrap script."},
        cli.StringFlag{"url", "https://agent.buildbox.io/v1", "The Agent API endpoint."},
        cli.BoolFlag{"debug", "Enable debug mode."},
      },
      Action: func(c *cli.Context) {
        // Create the agent from the CLI options
        agent := setupAgentFromCli(c, "run")

        // Grab the first argument and use as the job id
        id := c.Args().First()

        // Validate the job id
        if id == "" {
          fmt.Printf("buildbox-agent: no job id specified.\nSee 'buildbox-agent help run'")
          os.Exit(1)
        }

        agent.Run(id)
      },
    },
  }

  // Default the default action
  app.Action = func(c *cli.Context) {
    cli.ShowAppHelp(c)
    os.Exit(1)
  }

  app.Run(os.Args)
}

func setupAgentFromCli(c *cli.Context, command string) *buildbox.Agent {
  // Init debugging
  if c.Bool("debug") {
    buildbox.LoggerInitDebug()
  }

  agentAccessToken := c.String("access-token")

  // Should we look to the environment for the agent access token?
  if agentAccessToken == AgentAccessTokenDefault {
    agentAccessToken = os.Getenv(AgentAccessTokenEnv)
  }

  if agentAccessToken == "" {
    fmt.Println("buildbox-agent: missing agent access token\nSee 'buildbox-agent start --help'")
    os.Exit(1)
  }

  bootstrapScript := c.String("bootstrap-script")

  // Expand the envionment variable.
  if bootstrapScript == BootstrapScriptDefault {
    bootstrapScript = os.ExpandEnv(bootstrapScript)
  }

  // Make sure the boostrap script exists.
  if _, err := os.Stat(bootstrapScript); os.IsNotExist(err) {
    print("buildbox-agent: no such file " + bootstrapScript + "\n")
    os.Exit(1)
  }

  // Set the agent options
  var agent buildbox.Agent;
  agent.BootstrapScript = bootstrapScript

  // Client specific options
  agent.Client.AgentAccessToken = agentAccessToken
  agent.Client.URL = c.String("url")

  // Setup the agent
  agent.Setup()

  // A nice welcome message
  buildbox.Logger.Infof("Started buildbox-agent `%s` (Version: %s, PID: %d)", agent.Name, buildbox.Version, os.Getpid())

  return &agent
}
<|endoftext|>


<commit_before>package cmd

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"

	"cloud.google.com/go/storage"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"

	"github.com/drud/drud-go/secrets"
	"github.com/drud/drud-go/utils"
	"github.com/fatih/color"
	"github.com/fsouza/go-dockerclient"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

// NormalizePath prefixes secret paths with secret when necessary
func NormalizePath(sPath string) (newPath string) {
	newPath = sPath
	if !strings.HasPrefix(sPath, "secret/") || !strings.HasPrefix(sPath, "cubbyhole/") {
		if strings.HasPrefix(sPath, "/") {
			newPath = filepath.Join("secret", sPath[1:])
		} else {
			newPath = filepath.Join("secret", sPath)
		}
	}
	return
}

func askForConfirmation() bool {
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		log.Fatal(err)
	}
	okayResponses := []string{"y", "yes"}
	nokayResponses := []string{"n", "no"}
	responseLower := strings.ToLower(response)

	if containsString(okayResponses, responseLower) {
		return true
	} else if containsString(nokayResponses, responseLower) {
		return false
	} else {
		fmt.Println("Please type yes or no and then press enter:")
		return askForConfirmation()
	}
}

// posString returns the first index of element in slice.
// If slice does not contain element, returns -1.
func posString(slice []string, element string) int {
	for index, elem := range slice {
		if elem == element {
			return index
		}
	}
	return -1
}

// containsString returns true iff slice contains element
func containsString(slice []string, element string) bool {
	return !(posString(slice, element) == -1)
}

// SetHomedir gets homedir and sets it to global homedir
func SetHomedir() {
	var err error
	homedir, err = utils.GetHomeDir()
	if err != nil {
		log.Fatal(err)
	}
}

// PrepConf sets global drudconf with abs path to conf file
// and then creates a default config file if one does not exist
func PrepConf() {
	// figure out where the config is for reals
	var err error
	if cfgFile == "" {
		drudconf = filepath.Join(homedir, "drud.yaml")
	} else {
		if strings.HasPrefix(cfgFile, "/") {
			drudconf = cfgFile
		} else {
			drudconf, err = filepath.Abs(cfgFile)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	// create a blank drud config if one does not exist
	if _, err := os.Stat(drudconf); os.IsNotExist(err) {
		f, ferr := os.Create(drudconf)
		if ferr != nil {
			log.Fatal(ferr)
		}
		defer f.Close()
		//default drud.yaml contents
		f.WriteString(`Client: 1fee
Dev: false
DrudHost: drudapi.drud.io
Protocol: https
VaultHost: https://sanctuary.drud.io:8200
Version: v0.1
`)

	}
}

// EnableAvailablePackages gets token info and determines what functionality to enable based on policies
func EnableAvailablePackages() error {
	data, err := secrets.GetTokenDetails()
	if err != nil {
		return err
	}

	policies := data["policies"].([]interface{})
	// @todo: make policies like [files, hosting, etc] instead of using client name
	for _, p := range policies {
		switch p.(string) {
		case "root", "admin":
			drudAccess = true
			filesAccess = true
			clientCreateAccess = true
		case "templatefit":
			drudAccess = true
			filesAccess = true
		case "1fee":
			filesAccess = true
		case "newmedia":
			filesAccess = true
		}
	}

	if drudAccess {
		RootCmd.AddCommand(HostingCmd)
		RootCmd.AddCommand(LocalCmd)
	}
	if filesAccess {
		RootCmd.AddCommand(FileCmd)
	}
	if clientCreateAccess {
		CreateCmd.AddCommand(clientCreateCmd)
		DeleteCmd.AddCommand(clientDeleteCmd)
		ListCmd.AddCommand(clientListCmd)
	}

	return nil

}

func getClientJWT(client string) ([]byte, error) {
	sobj := secrets.Secret{
		Path: fmt.Sprintf("secret/gce/%s-jwt", client),
	}

	err := sobj.Read()
	if err != nil {
		return []byte(""), err
	}

	b, err := json.Marshal(sobj.Data)
	if err != nil {
		return []byte(""), err
	}

	return b, nil
}

func getStorageClient(jwt []byte, ctx context.Context) (*storage.Client, error) {
	conf, err := google.JWTConfigFromJSON(
		jwt,
		storage.ScopeReadOnly,
	)
	if err != nil {
		return nil, err
	}

	client, err := storage.NewClient(ctx, option.WithTokenSource(conf.TokenSource(ctx)))
	if err != nil {
		return nil, err
	}

	return client, nil
}

// PrepLocalSiteDirs creates a site's directories for local dev in ~/.drud/client/site
func PrepLocalSiteDirs(base string) error {
	err := os.MkdirAll(base, os.FileMode(int(0774)))
	if err != nil {
		return err
	}

	dirs := []string{
		"src",
		"files",
		"data",
	}
	for _, d := range dirs {
		dirPath := path.Join(base, d)
		err := os.Mkdir(dirPath, os.FileMode(int(0774)))
		if err != nil {
			if !strings.Contains(err.Error(), "file exists") {
				return err
			}
		}
	}

	return nil
}

// getMAC returns the mac address for interface en0 or the first in the list otherwise
func getMAC() (string, error) {
	var macADDR string
	ifs, _ := net.Interfaces()
	for _, v := range ifs {
		h := v.HardwareAddr.String()
		if len(h) == 0 {
			continue
		}
		if v.Name == "en0" {
			macADDR = h
		}
	}
	if macADDR == "" {
		macADDR = ifs[0].HardwareAddr.String()
	}
	if macADDR == "" {
		return macADDR, fmt.Errorf("No MAC Addr found!")
	}
	return macADDR, nil
}

// ParseConfigFlag is needed in order to get teh value of the flag before cobra can
func ParseConfigFlag() string {
	value := ""

	for i, arg := range os.Args {
		if strings.HasPrefix(arg, "--config=") {
			value = strings.TrimPrefix(arg, "--config=")
		} else if arg == "--config" {
			value = os.Args[i+1]
		}
	}

	return value
}

// Failed will print an red error message and exit with failure.
func Failed(format string, a ...interface{}) {
	color.Red(format, a...)
	os.Exit(1)
}

// NetExists checks to see if the docker network for DRUD local development exists.
func NetExists(client *docker.Client, name string) bool {
	nets, _ := client.ListNetworks()
	for _, n := range nets {
		if n.Name == name {
			return true
		}
	}
	return false
}

// EnsureNetwork will ensure the docker network for DRUD local development is created.
func EnsureNetwork(client *docker.Client, name string) error {
	if !NetExists(client, name) {
		netOptions := docker.CreateNetworkOptions{
			Name:     name,
			Driver:   "bridge",
			Internal: false,
		}
		_, err := client.CreateNetwork(netOptions)
		if err != nil {
			return err
		}
		log.Println("Network", name, "created")

	}
	return nil
}
<commit_msg>Update roles that are allowed full cli access (#202)<commit_after>package cmd

import (
	"encoding/json"
	"fmt"
	"log"
	"net"
	"os"
	"path"
	"path/filepath"
	"strings"

	"cloud.google.com/go/storage"
	"golang.org/x/net/context"
	"golang.org/x/oauth2/google"
	"google.golang.org/api/option"

	"github.com/drud/drud-go/secrets"
	"github.com/drud/drud-go/utils"
	"github.com/fatih/color"
	"github.com/fsouza/go-dockerclient"
)

func check(e error) {
	if e != nil {
		panic(e)
	}
}

// NormalizePath prefixes secret paths with secret when necessary
func NormalizePath(sPath string) (newPath string) {
	newPath = sPath
	if !strings.HasPrefix(sPath, "secret/") || !strings.HasPrefix(sPath, "cubbyhole/") {
		if strings.HasPrefix(sPath, "/") {
			newPath = filepath.Join("secret", sPath[1:])
		} else {
			newPath = filepath.Join("secret", sPath)
		}
	}
	return
}

func askForConfirmation() bool {
	var response string
	_, err := fmt.Scanln(&response)
	if err != nil {
		log.Fatal(err)
	}
	okayResponses := []string{"y", "yes"}
	nokayResponses := []string{"n", "no"}
	responseLower := strings.ToLower(response)

	if containsString(okayResponses, responseLower) {
		return true
	} else if containsString(nokayResponses, responseLower) {
		return false
	} else {
		fmt.Println("Please type yes or no and then press enter:")
		return askForConfirmation()
	}
}

// posString returns the first index of element in slice.
// If slice does not contain element, returns -1.
func posString(slice []string, element string) int {
	for index, elem := range slice {
		if elem == element {
			return index
		}
	}
	return -1
}

// containsString returns true iff slice contains element
func containsString(slice []string, element string) bool {
	return !(posString(slice, element) == -1)
}

// SetHomedir gets homedir and sets it to global homedir
func SetHomedir() {
	var err error
	homedir, err = utils.GetHomeDir()
	if err != nil {
		log.Fatal(err)
	}
}

// PrepConf sets global drudconf with abs path to conf file
// and then creates a default config file if one does not exist
func PrepConf() {
	// figure out where the config is for reals
	var err error
	if cfgFile == "" {
		drudconf = filepath.Join(homedir, "drud.yaml")
	} else {
		if strings.HasPrefix(cfgFile, "/") {
			drudconf = cfgFile
		} else {
			drudconf, err = filepath.Abs(cfgFile)
			if err != nil {
				log.Fatal(err)
			}
		}
	}

	// create a blank drud config if one does not exist
	if _, err := os.Stat(drudconf); os.IsNotExist(err) {
		f, ferr := os.Create(drudconf)
		if ferr != nil {
			log.Fatal(ferr)
		}
		defer f.Close()
		//default drud.yaml contents
		f.WriteString(`Client: 1fee
Dev: false
DrudHost: drudapi.drud.io
Protocol: https
VaultHost: https://sanctuary.drud.io:8200
Version: v0.1
`)

	}
}

// EnableAvailablePackages gets token info and determines what functionality to enable based on policies
func EnableAvailablePackages() error {
	data, err := secrets.GetTokenDetails()
	if err != nil {
		return err
	}

	policies := data["policies"].([]interface{})
	// @todo: make policies like [files, hosting, etc] instead of using client name
	for _, p := range policies {
		switch p.(string) {
		case "root", "admin", "drud_team", "drud_test_users":
			drudAccess = true
			filesAccess = true
			clientCreateAccess = true
		case "templatefit":
			drudAccess = true
			filesAccess = true
		case "1fee":
			filesAccess = true
		case "newmedia":
			filesAccess = true
		}
	}

	if drudAccess {
		RootCmd.AddCommand(HostingCmd)
		RootCmd.AddCommand(LocalCmd)
	}
	if filesAccess {
		RootCmd.AddCommand(FileCmd)
	}
	if clientCreateAccess {
		CreateCmd.AddCommand(clientCreateCmd)
		DeleteCmd.AddCommand(clientDeleteCmd)
		ListCmd.AddCommand(clientListCmd)
	}

	return nil

}

func getClientJWT(client string) ([]byte, error) {
	sobj := secrets.Secret{
		Path: fmt.Sprintf("secret/gce/%s-jwt", client),
	}

	err := sobj.Read()
	if err != nil {
		return []byte(""), err
	}

	b, err := json.Marshal(sobj.Data)
	if err != nil {
		return []byte(""), err
	}

	return b, nil
}

func getStorageClient(jwt []byte, ctx context.Context) (*storage.Client, error) {
	conf, err := google.JWTConfigFromJSON(
		jwt,
		storage.ScopeReadOnly,
	)
	if err != nil {
		return nil, err
	}

	client, err := storage.NewClient(ctx, option.WithTokenSource(conf.TokenSource(ctx)))
	if err != nil {
		return nil, err
	}

	return client, nil
}

// PrepLocalSiteDirs creates a site's directories for local dev in ~/.drud/client/site
func PrepLocalSiteDirs(base string) error {
	err := os.MkdirAll(base, os.FileMode(int(0774)))
	if err != nil {
		return err
	}

	dirs := []string{
		"src",
		"files",
		"data",
	}
	for _, d := range dirs {
		dirPath := path.Join(base, d)
		err := os.Mkdir(dirPath, os.FileMode(int(0774)))
		if err != nil {
			if !strings.Contains(err.Error(), "file exists") {
				return err
			}
		}
	}

	return nil
}

// getMAC returns the mac address for interface en0 or the first in the list otherwise
func getMAC() (string, error) {
	var macADDR string
	ifs, _ := net.Interfaces()
	for _, v := range ifs {
		h := v.HardwareAddr.String()
		if len(h) == 0 {
			continue
		}
		if v.Name == "en0" {
			macADDR = h
		}
	}
	if macADDR == "" {
		macADDR = ifs[0].HardwareAddr.String()
	}
	if macADDR == "" {
		return macADDR, fmt.Errorf("No MAC Addr found!")
	}
	return macADDR, nil
}

// ParseConfigFlag is needed in order to get teh value of the flag before cobra can
func ParseConfigFlag() string {
	value := ""

	for i, arg := range os.Args {
		if strings.HasPrefix(arg, "--config=") {
			value = strings.TrimPrefix(arg, "--config=")
		} else if arg == "--config" {
			value = os.Args[i+1]
		}
	}

	return value
}

// Failed will print an red error message and exit with failure.
func Failed(format string, a ...interface{}) {
	color.Red(format, a...)
	os.Exit(1)
}

// NetExists checks to see if the docker network for DRUD local development exists.
func NetExists(client *docker.Client, name string) bool {
	nets, _ := client.ListNetworks()
	for _, n := range nets {
		if n.Name == name {
			return true
		}
	}
	return false
}

// EnsureNetwork will ensure the docker network for DRUD local development is created.
func EnsureNetwork(client *docker.Client, name string) error {
	if !NetExists(client, name) {
		netOptions := docker.CreateNetworkOptions{
			Name:     name,
			Driver:   "bridge",
			Internal: false,
		}
		_, err := client.CreateNetwork(netOptions)
		if err != nil {
			return err
		}
		log.Println("Network", name, "created")

	}
	return nil
}
<|endoftext|>


<commit_before>package cmdex

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/bitrise-io/go-utils/errorutil"
)

// ----------

// CommandModel ...
type CommandModel struct {
	cmd *exec.Cmd
}

// NewCommand ...
func NewCommand(name string, args ...string) *CommandModel {
	return &CommandModel{
		cmd: exec.Command(name, args...),
	}
}

// NewCommandFromSlice ...
func NewCommandFromSlice(cmdSlice []string) (*CommandModel, error) {
	if len(cmdSlice) == 0 {
		return nil, errors.New("no command provided")
	} else if len(cmdSlice) == 1 {
		return NewCommand(cmdSlice[0]), nil
	}

	return NewCommand(cmdSlice[0], cmdSlice[1:]...), nil
}

// NewCommandWithCmd ...
func NewCommandWithCmd(cmd *exec.Cmd) *CommandModel {
	return &CommandModel{
		cmd: cmd,
	}
}

// GetCmd ...
func (command *CommandModel) GetCmd() *exec.Cmd {
	return command.cmd
}

// SetDir ...
func (command *CommandModel) SetDir(dir string) *CommandModel {
	command.cmd.Dir = dir
	return command
}

// SetEnvs ...
func (command *CommandModel) SetEnvs(envs []string) *CommandModel {
	command.cmd.Env = envs
	return command
}

// AppendEnvs - appends the envs to the current os.Environ()
// Calling this multiple times will NOT appens the envs one by one,
// only the last "envs" set will be appended to os.Environ()!
func (command *CommandModel) AppendEnvs(envs []string) *CommandModel {
	return command.SetEnvs(append(os.Environ(), envs...))
}

// SetStdin ...
func (command *CommandModel) SetStdin(in io.Reader) *CommandModel {
	command.cmd.Stdin = in
	return command
}

// SetStdout ...
func (command *CommandModel) SetStdout(out io.Writer) *CommandModel {
	command.cmd.Stdout = out
	return command
}

// SetStderr ...
func (command *CommandModel) SetStderr(err io.Writer) *CommandModel {
	command.cmd.Stderr = err
	return command
}

// Run ...
func (command CommandModel) Run() error {
	return command.cmd.Run()
}

// RunAndReturnExitCode ...
func (command CommandModel) RunAndReturnExitCode() (int, error) {
	return RunCmdAndReturnExitCode(command.cmd)
}

// RunAndReturnTrimmedOutput ...
func (command CommandModel) RunAndReturnTrimmedOutput() (string, error) {
	return RunCmdAndReturnTrimmedOutput(command.cmd)
}

// RunAndReturnTrimmedCombinedOutput ...
func (command CommandModel) RunAndReturnTrimmedCombinedOutput() (string, error) {
	return RunCmdAndReturnTrimmedCombinedOutput(command.cmd)
}

// ----------

// PrintableCommandArgs ...
func PrintableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string {
	cmdArgsDecorated := []string{}
	for idx, anArg := range fullCommandArgs {
		quotedArg := strconv.Quote(anArg)
		if idx == 0 && !isQuoteFirst {
			quotedArg = anArg
		}
		cmdArgsDecorated = append(cmdArgsDecorated, quotedArg)
	}

	return strings.Join(cmdArgsDecorated, " ")
}

// RunCmdAndReturnExitCode ...
func RunCmdAndReturnExitCode(cmd *exec.Cmd) (int, error) {
	err := cmd.Run()
	if err != nil {
		exitCode, castErr := errorutil.CmdExitCodeFromError(err)
		if castErr != nil {
			return 1, fmt.Errorf("failed get exit code from error: %s, error: %s", err, castErr)
		}

		return exitCode, err
	}

	return 0, nil
}

// RunCmdAndReturnTrimmedOutput ...
func RunCmdAndReturnTrimmedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.Output()
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCmdAndReturnTrimmedCombinedOutput ...
func RunCmdAndReturnTrimmedCombinedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.CombinedOutput()
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCommandWithReaderAndWriters ...
func RunCommandWithReaderAndWriters(inReader io.Reader, outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = inReader
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandWithWriters ...
func RunCommandWithWriters(outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandInDirWithEnvsAndReturnExitCode ...
func RunCommandInDirWithEnvsAndReturnExitCode(envs []string, dir, name string, args ...string) (int, error) {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	if len(envs) > 0 {
		cmd.Env = envs
	}

	return RunCmdAndReturnExitCode(cmd)
}

// RunCommandInDirAndReturnExitCode ...
func RunCommandInDirAndReturnExitCode(dir, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode([]string{}, dir, name, args...)
}

// RunCommandWithEnvsAndReturnExitCode ...
func RunCommandWithEnvsAndReturnExitCode(envs []string, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode(envs, "", name, args...)
}

// RunCommandInDir ...
func RunCommandInDir(dir, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	return cmd.Run()
}

// RunCommand ...
func RunCommand(name string, args ...string) error {
	return RunCommandInDir("", name, args...)
}

// RunCommandAndReturnStdout ..
func RunCommandAndReturnStdout(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	return RunCmdAndReturnTrimmedOutput(cmd)
}

// RunCommandInDirAndReturnCombinedStdoutAndStderr ...
func RunCommandInDirAndReturnCombinedStdoutAndStderr(dir, name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	if dir != "" {
		cmd.Dir = dir
	}
	return RunCmdAndReturnTrimmedCombinedOutput(cmd)
}

// RunCommandAndReturnCombinedStdoutAndStderr ..
func RunCommandAndReturnCombinedStdoutAndStderr(name string, args ...string) (string, error) {
	return RunCommandInDirAndReturnCombinedStdoutAndStderr("", name, args...)
}

// RunBashCommand ...
func RunBashCommand(cmdStr string) error {
	return RunCommand("bash", "-c", cmdStr)
}

// RunBashCommandLines ...
func RunBashCommandLines(cmdLines []string) error {
	for _, aLine := range cmdLines {
		if err := RunCommand("bash", "-c", aLine); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>cmdex/run.go : NewCommandWithStandardOuts and CommandModel.PrintableCommandArgs<commit_after>package cmdex

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/bitrise-io/go-utils/errorutil"
)

// ----------

// CommandModel ...
type CommandModel struct {
	cmd *exec.Cmd
}

// NewCommand ...
func NewCommand(name string, args ...string) *CommandModel {
	return &CommandModel{
		cmd: exec.Command(name, args...),
	}
}

// NewCommandWithStandardOuts - same as NewCommand, but sets the command's
// stdout and stderr to the standard (OS) out (os.Stdout) and err (os.Stderr)
func NewCommandWithStandardOuts(name string, args ...string) *CommandModel {
	return NewCommand(name, args...).SetStdout(os.Stdout).SetStderr(os.Stderr)
}

// NewCommandFromSlice ...
func NewCommandFromSlice(cmdSlice []string) (*CommandModel, error) {
	if len(cmdSlice) == 0 {
		return nil, errors.New("no command provided")
	} else if len(cmdSlice) == 1 {
		return NewCommand(cmdSlice[0]), nil
	}

	return NewCommand(cmdSlice[0], cmdSlice[1:]...), nil
}

// NewCommandWithCmd ...
func NewCommandWithCmd(cmd *exec.Cmd) *CommandModel {
	return &CommandModel{
		cmd: cmd,
	}
}

// GetCmd ...
func (command *CommandModel) GetCmd() *exec.Cmd {
	return command.cmd
}

// SetDir ...
func (command *CommandModel) SetDir(dir string) *CommandModel {
	command.cmd.Dir = dir
	return command
}

// SetEnvs ...
func (command *CommandModel) SetEnvs(envs []string) *CommandModel {
	command.cmd.Env = envs
	return command
}

// AppendEnvs - appends the envs to the current os.Environ()
// Calling this multiple times will NOT appens the envs one by one,
// only the last "envs" set will be appended to os.Environ()!
func (command *CommandModel) AppendEnvs(envs []string) *CommandModel {
	return command.SetEnvs(append(os.Environ(), envs...))
}

// SetStdin ...
func (command *CommandModel) SetStdin(in io.Reader) *CommandModel {
	command.cmd.Stdin = in
	return command
}

// SetStdout ...
func (command *CommandModel) SetStdout(out io.Writer) *CommandModel {
	command.cmd.Stdout = out
	return command
}

// SetStderr ...
func (command *CommandModel) SetStderr(err io.Writer) *CommandModel {
	command.cmd.Stderr = err
	return command
}

// Run ...
func (command CommandModel) Run() error {
	return command.cmd.Run()
}

// RunAndReturnExitCode ...
func (command CommandModel) RunAndReturnExitCode() (int, error) {
	return RunCmdAndReturnExitCode(command.cmd)
}

// RunAndReturnTrimmedOutput ...
func (command CommandModel) RunAndReturnTrimmedOutput() (string, error) {
	return RunCmdAndReturnTrimmedOutput(command.cmd)
}

// RunAndReturnTrimmedCombinedOutput ...
func (command CommandModel) RunAndReturnTrimmedCombinedOutput() (string, error) {
	return RunCmdAndReturnTrimmedCombinedOutput(command.cmd)
}

// PrintableCommandArgs ...
func (command CommandModel) PrintableCommandArgs() string {
	return PrintableCommandArgs(false, command.cmd.Args)
}

// ----------

// PrintableCommandArgs ...
func PrintableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string {
	cmdArgsDecorated := []string{}
	for idx, anArg := range fullCommandArgs {
		quotedArg := strconv.Quote(anArg)
		if idx == 0 && !isQuoteFirst {
			quotedArg = anArg
		}
		cmdArgsDecorated = append(cmdArgsDecorated, quotedArg)
	}

	return strings.Join(cmdArgsDecorated, " ")
}

// RunCmdAndReturnExitCode ...
func RunCmdAndReturnExitCode(cmd *exec.Cmd) (int, error) {
	err := cmd.Run()
	if err != nil {
		exitCode, castErr := errorutil.CmdExitCodeFromError(err)
		if castErr != nil {
			return 1, fmt.Errorf("failed get exit code from error: %s, error: %s", err, castErr)
		}

		return exitCode, err
	}

	return 0, nil
}

// RunCmdAndReturnTrimmedOutput ...
func RunCmdAndReturnTrimmedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.Output()
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCmdAndReturnTrimmedCombinedOutput ...
func RunCmdAndReturnTrimmedCombinedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.CombinedOutput()
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCommandWithReaderAndWriters ...
func RunCommandWithReaderAndWriters(inReader io.Reader, outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = inReader
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandWithWriters ...
func RunCommandWithWriters(outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandInDirWithEnvsAndReturnExitCode ...
func RunCommandInDirWithEnvsAndReturnExitCode(envs []string, dir, name string, args ...string) (int, error) {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	if len(envs) > 0 {
		cmd.Env = envs
	}

	return RunCmdAndReturnExitCode(cmd)
}

// RunCommandInDirAndReturnExitCode ...
func RunCommandInDirAndReturnExitCode(dir, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode([]string{}, dir, name, args...)
}

// RunCommandWithEnvsAndReturnExitCode ...
func RunCommandWithEnvsAndReturnExitCode(envs []string, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode(envs, "", name, args...)
}

// RunCommandInDir ...
func RunCommandInDir(dir, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	return cmd.Run()
}

// RunCommand ...
func RunCommand(name string, args ...string) error {
	return RunCommandInDir("", name, args...)
}

// RunCommandAndReturnStdout ..
func RunCommandAndReturnStdout(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	return RunCmdAndReturnTrimmedOutput(cmd)
}

// RunCommandInDirAndReturnCombinedStdoutAndStderr ...
func RunCommandInDirAndReturnCombinedStdoutAndStderr(dir, name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	if dir != "" {
		cmd.Dir = dir
	}
	return RunCmdAndReturnTrimmedCombinedOutput(cmd)
}

// RunCommandAndReturnCombinedStdoutAndStderr ..
func RunCommandAndReturnCombinedStdoutAndStderr(name string, args ...string) (string, error) {
	return RunCommandInDirAndReturnCombinedStdoutAndStderr("", name, args...)
}

// RunBashCommand ...
func RunBashCommand(cmdStr string) error {
	return RunCommand("bash", "-c", cmdStr)
}

// RunBashCommandLines ...
func RunBashCommandLines(cmdLines []string) error {
	for _, aLine := range cmdLines {
		if err := RunCommand("bash", "-c", aLine); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 cmds

import (
	"errors"
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/yunify/snips/capsules"
	"github.com/yunify/snips/generator"
	"github.com/yunify/snips/metadata"
	"github.com/yunify/snips/specs"
	"github.com/yunify/snips/templates"
	"github.com/yunify/snips/utils"
)

var (
	flagVersion           bool
	codeServiceModule     string
	codeServiceAPIVersion string
	codeSpecDirectory     string
	codeSpecFormat        string
	codeTemplateDirectory string
	codeOutputDirectory   string
)

func init() {
	RootCMD.Flags().BoolVarP(&flagVersion, "version", "v", false, "Show version.")
	RootCMD.Flags().StringVarP(&codeServiceModule, "service", "m", "", "Service to use.")
	RootCMD.Flags().StringVarP(&codeServiceAPIVersion, "service-api-version", "n", "latest", "Service API version to use.")
	RootCMD.Flags().StringVarP(&codeSpecDirectory, "spec", "s", "", "Spec files directory.")
	RootCMD.Flags().StringVarP(&codeSpecFormat, "spec-format", "", "Swagger-v2.0", "Format of spec file.")
	RootCMD.Flags().StringVarP(&codeTemplateDirectory, "template", "t", "", "Template files directory.")
	RootCMD.Flags().StringVarP(&codeOutputDirectory, "output", "o", "", "Output files directory.")
}

// RootCMD represents the base command when called without any subcommands.
var RootCMD = &cobra.Command{
	Use:   "snips",
	Short: "A code generator for QingCloud & QingStor SDKs.",
	Long: `A code generator for QingCloud & QingStor SDKs.
It is used to generate code from our public APIs currently.

For example:
  $ snips -m QingStor -n latest \
          -s ./specs -t ./templates/qingstor/go \
          -o ./publish/qingstor-sdk-go/service
  $ ...
  $ snips --service=QingStor \
          --service-api-version=latest \
          --spec=./specs \
          --template=./templates/qingstor/ruby \
          --output=./publish/qingstor-sdk-ruby/lib/qingstor/sdk/service
  $ ...

Copyright (C) 2016 Yunify, Inc.`,
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if flagVersion {
			return nil
		}

		if codeSpecDirectory == "" {
			return errors.New("please specify spec files directory")
		}
		if _, err := os.Stat(codeSpecDirectory); err != nil {
			return errors.New("please make sure the specs directory exists")
		}

		if codeTemplateDirectory == "" {
			return errors.New("please specify templates directory")
		}
		if _, err := os.Stat(codeTemplateDirectory); err != nil {
			return errors.New("please make sure the templates directory exists")
		}

		if codeOutputDirectory == "" {
			return errors.New("please specify output files directory")
		}

		return nil
	},
	Run: func(cmd *cobra.Command, args []string) {
		if flagVersion {
			fmt.Println("snips version " + metadata.Version)
			return
		}

		loadedTemplates, _, err := templates.LoadTemplates(codeTemplateDirectory)
		utils.CheckErrorForExit(err)
		fmt.Println("Loaded templates from " + codeTemplateDirectory)
		fmt.Println(len(loadedTemplates), "template(s) detected.")

		service, err := specs.LoadServices(codeSpecDirectory, codeSpecFormat, codeServiceModule)
		utils.CheckErrorForExit(err)
		apiVersion := service.APIVersions[codeServiceAPIVersion]
		if apiVersion == nil {
			utils.CheckErrorForExit(fmt.Errorf(
				"API version \"%s\" of service \"%s\" not found",
				codeServiceAPIVersion,
				utils.SnakeCaseToCamelCase(service.Filename),
			))
		}

		fmt.Printf(
			"Loaded service %s (%s) from %s\n\n",
			utils.SnakeCaseToCamelCase(service.Filename),
			service.LatestAPIVersion.Filename,
			codeSpecDirectory)

		if spec := apiVersion.Spec; spec != nil {
			codeCapsule := &capsules.BaseCapsule{CapsulePowder: &capsules.CapsulePowder{}}
			codeGenerator := generator.New()

			codeCapsule.SetData(spec.Data)
			codeCapsule.SetVersioning(codeServiceAPIVersion != "latest")

			sharedTemplateContent := ""
			if template := loadedTemplates["shared"]; template != nil {
				sharedTemplateContent = template.FileContent
			}

			if template := loadedTemplates["service"]; template != nil {
				template.FileContent += sharedTemplateContent
				template.UpdateOutputFilename(spec.Data.Service.Name, template.OutputFileNaming.Style)
				template.UpdateOutputFilePath(codeOutputDirectory)
				codeCapsule.SetMode("", "")
				codeGenerator.Set(codeCapsule, template)
				err = codeGenerator.Run()
				utils.CheckErrorForExit(err)
			}

			if template := loadedTemplates["sub_service"]; template != nil {
				template.FileContent += sharedTemplateContent
				for _, subService := range spec.Data.SubServices {
					template.UpdateOutputFilename(subService.Name, template.OutputFileNaming.Style)
					template.UpdateOutputFilePath(codeOutputDirectory)
					codeCapsule.SetMode(template.ID, subService.ID)
					codeGenerator.Set(codeCapsule, template)
					err = codeGenerator.Run()
					utils.CheckErrorForExit(err)
				}
			}

			if template := loadedTemplates["types"]; template != nil {
				template.FileContent += sharedTemplateContent
				template.UpdateOutputFilename("types", template.OutputFileNaming.Style)
				template.UpdateOutputFilePath(codeOutputDirectory)
				codeCapsule.SetMode("", "")
				codeGenerator.Set(codeCapsule, template)
				err = codeGenerator.Run()
				utils.CheckErrorForExit(err)
			}
		}

		fmt.Println("\nEverything looks fine.")
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by entrance of the application.
// It only needs to happen once to the rootCMD.
func Execute() {
	if err := RootCMD.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<commit_msg>Update description of cmd options<commit_after>// +-------------------------------------------------------------------------
// | Copyright (C) 2016 Yunify, Inc.
// +-------------------------------------------------------------------------
// | Licensed under the Apache License, Version 2.0 (the "License");
// | you may not use this work except in compliance with the License.
// | You may obtain a copy of the License in the LICENSE file, or 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 cmds

import (
	"errors"
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/yunify/snips/capsules"
	"github.com/yunify/snips/generator"
	"github.com/yunify/snips/metadata"
	"github.com/yunify/snips/specs"
	"github.com/yunify/snips/templates"
	"github.com/yunify/snips/utils"
)

var (
	flagVersion           bool
	codeServiceModule     string
	codeServiceAPIVersion string
	codeSpecDirectory     string
	codeSpecFormat        string
	codeTemplateDirectory string
	codeOutputDirectory   string
)

func init() {
	RootCMD.Flags().BoolVarP(
		&flagVersion, "version", "v", false,
		"Show version.",
	)
	RootCMD.Flags().StringVarP(
		&codeServiceModule, "service", "m", "",
		"Choose the service to use.",
	)
	RootCMD.Flags().StringVarP(
		&codeServiceAPIVersion, "service-api-version", "n", "latest",
		"Choose the service API version to use.",
	)
	RootCMD.Flags().StringVarP(
		&codeSpecDirectory, "spec", "s", "",
		"Specify spec files directory.",
	)
	RootCMD.Flags().StringVarP(
		&codeSpecFormat, "spec-format", "", "Swagger-v2.0",
		"Specify the format of spec file.",
	)
	RootCMD.Flags().StringVarP(
		&codeTemplateDirectory, "template", "t", "",
		"Specify template files directory.",
	)
	RootCMD.Flags().StringVarP(
		&codeOutputDirectory, "output", "o", "",
		"Specify the output directory.",
	)
}

// RootCMD represents the base command when called without any subcommands.
var RootCMD = &cobra.Command{
	Use:   "snips",
	Short: "A code generator for QingCloud & QingStor SDKs.",
	Long: `A code generator for QingCloud & QingStor SDKs.
It is used to generate code from our public APIs currently.

For example:
  $ snips -m QingStor -n latest \
          -s ./specs -t ./templates/qingstor/go \
          -o ./publish/qingstor-sdk-go/service
  $ ...
  $ snips --service=QingStor \
          --service-api-version=latest \
          --spec=./specs \
          --template=./templates/qingstor/ruby \
          --output=./publish/qingstor-sdk-ruby/lib/qingstor/sdk/service
  $ ...

Copyright (C) 2016 Yunify, Inc.`,
	PreRunE: func(cmd *cobra.Command, args []string) error {
		if flagVersion {
			return nil
		}

		if codeSpecDirectory == "" {
			return errors.New("please specify spec files directory")
		}
		if _, err := os.Stat(codeSpecDirectory); err != nil {
			return errors.New("please make sure the specs directory exists")
		}

		if codeTemplateDirectory == "" {
			return errors.New("please specify templates directory")
		}
		if _, err := os.Stat(codeTemplateDirectory); err != nil {
			return errors.New("please make sure the templates directory exists")
		}

		if codeOutputDirectory == "" {
			return errors.New("please specify output files directory")
		}

		return nil
	},
	Run: func(cmd *cobra.Command, args []string) {
		if flagVersion {
			fmt.Println("snips version " + metadata.Version)
			return
		}

		loadedTemplates, _, err := templates.LoadTemplates(codeTemplateDirectory)
		utils.CheckErrorForExit(err)
		fmt.Println("Loaded templates from " + codeTemplateDirectory)
		fmt.Println(len(loadedTemplates), "template(s) detected.")

		service, err := specs.LoadServices(codeSpecDirectory, codeSpecFormat, codeServiceModule)
		utils.CheckErrorForExit(err)
		apiVersion := service.APIVersions[codeServiceAPIVersion]
		if apiVersion == nil {
			utils.CheckErrorForExit(fmt.Errorf(
				"API version \"%s\" of service \"%s\" not found",
				codeServiceAPIVersion,
				utils.SnakeCaseToCamelCase(service.Filename),
			))
		}

		fmt.Printf(
			"Loaded service %s (%s) from %s\n\n",
			utils.SnakeCaseToCamelCase(service.Filename),
			service.LatestAPIVersion.Filename,
			codeSpecDirectory)

		if spec := apiVersion.Spec; spec != nil {
			codeCapsule := &capsules.BaseCapsule{CapsulePowder: &capsules.CapsulePowder{}}
			codeGenerator := generator.New()

			codeCapsule.SetData(spec.Data)
			codeCapsule.SetVersioning(codeServiceAPIVersion != "latest")

			sharedTemplateContent := ""
			if template := loadedTemplates["shared"]; template != nil {
				sharedTemplateContent = template.FileContent
			}

			if template := loadedTemplates["service"]; template != nil {
				template.FileContent += sharedTemplateContent
				template.UpdateOutputFilename(spec.Data.Service.Name, template.OutputFileNaming.Style)
				template.UpdateOutputFilePath(codeOutputDirectory)
				codeCapsule.SetMode("", "")
				codeGenerator.Set(codeCapsule, template)
				err = codeGenerator.Run()
				utils.CheckErrorForExit(err)
			}

			if template := loadedTemplates["sub_service"]; template != nil {
				template.FileContent += sharedTemplateContent
				for _, subService := range spec.Data.SubServices {
					template.UpdateOutputFilename(subService.Name, template.OutputFileNaming.Style)
					template.UpdateOutputFilePath(codeOutputDirectory)
					codeCapsule.SetMode(template.ID, subService.ID)
					codeGenerator.Set(codeCapsule, template)
					err = codeGenerator.Run()
					utils.CheckErrorForExit(err)
				}
			}

			if template := loadedTemplates["types"]; template != nil {
				template.FileContent += sharedTemplateContent
				template.UpdateOutputFilename("types", template.OutputFileNaming.Style)
				template.UpdateOutputFilePath(codeOutputDirectory)
				codeCapsule.SetMode("", "")
				codeGenerator.Set(codeCapsule, template)
				err = codeGenerator.Run()
				utils.CheckErrorForExit(err)
			}
		}

		fmt.Println("\nEverything looks fine.")
	},
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by entrance of the application.
// It only needs to happen once to the rootCMD.
func Execute() {
	if err := RootCMD.Execute(); err != nil {
		fmt.Println(err)
		os.Exit(-1)
	}
}
<|endoftext|>


<commit_before>package router

import (
	"net/http"
	"os"
	"path/filepath"

	"github.com/WeCanHearYou/wchy/context"
	"github.com/WeCanHearYou/wchy/handler"

	"github.com/WeCanHearYou/wchy/env"
	"github.com/gin-gonic/gin"
)

// GetMainEngine returns main HTTP engine
func GetMainEngine(ctx *context.WchyContext) *gin.Engine {
	router := gin.New()

	if env.IsDevelopment() {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	router.Use(MultiTenant(ctx))
	router.LoadHTMLGlob(filepath.Join(os.Getenv("GOPATH"), "src/github.com/WeCanHearYou/wchy/views/*"))

	router.GET("/", handler.Index(ctx))

	api := router.Group("/api")
	{
		api.GET("/status", handler.Status(ctx))
		api.GET("/tenants/:domain", handler.TenantByDomain(ctx))
	}

	router.StaticFS("/assets", http.Dir("node_modules"))

	return router
}
<commit_msg>views path on heroku<commit_after>package router

import (
	"net/http"
	"os"
	"path/filepath"

	"github.com/WeCanHearYou/wchy/context"
	"github.com/WeCanHearYou/wchy/handler"

	"github.com/WeCanHearYou/wchy/env"
	"github.com/gin-gonic/gin"
)

// GetMainEngine returns main HTTP engine
func GetMainEngine(ctx *context.WchyContext) *gin.Engine {
	router := gin.New()

	if env.IsDevelopment() {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	router.Use(MultiTenant(ctx))
	if env.IsTest() {
		router.LoadHTMLGlob(filepath.Join(os.Getenv("GOPATH"), "src/github.com/WeCanHearYou/wchy/views/*"))
	} else {
		router.LoadHTMLGlob("views/*")
	}

	router.GET("/", handler.Index(ctx))

	api := router.Group("/api")
	{
		api.GET("/status", handler.Status(ctx))
		api.GET("/tenants/:domain", handler.TenantByDomain(ctx))
	}

	router.StaticFS("/assets", http.Dir("node_modules"))

	return router
}
<|endoftext|>


<commit_before>package router

import (
	"net/http"
	"os"
	"path"

	"github.com/go-ggz/ggz/assets"
	"github.com/go-ggz/ggz/config"
	"github.com/go-ggz/ggz/models"
	"github.com/go-ggz/ggz/modules/minio"
	"github.com/go-ggz/ggz/router/middleware/header"
	"github.com/go-ggz/ggz/router/middleware/logger"
	"github.com/go-ggz/ggz/router/middleware/prometheus"
	"github.com/go-ggz/ggz/web"

	"github.com/gin-contrib/gzip"
	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

// GlobalInit is for global configuration reload-able.
func GlobalInit() {
	if err := models.NewEngine(); err != nil {
		logrus.Fatalf("Failed to initialize ORM engine: %v", err)
	}

	if config.QRCode.Enable && config.Storage.Driver == "disk" {
		storage := path.Join(config.Storage.Path, config.QRCode.Bucket)
		if err := os.MkdirAll(storage, os.ModePerm); err != nil {
			logrus.Fatalf("Failed to create storage folder: %v", err)
		}
	}

	if config.QRCode.Enable && config.Storage.Driver == "s3" {
		minio.NewEngine(
			config.Minio.EndPoint,
			config.Minio.AccessID,
			config.Minio.SecretKey,
			config.Minio.SSL,
		)

		if err := minio.S3.MakeBucket(config.Minio.Bucket, config.Minio.Region); err != nil {
			logrus.Fatalf("Failed to create s3 bucket: %v", err)
		}
	}
}

// Load initializes the routing of the application.
func Load(middleware ...gin.HandlerFunc) http.Handler {
	if config.Debug {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	e := gin.New()

	e.Use(gin.Recovery())
	e.Use(logger.SetLogger())
	e.Use(gzip.Gzip(gzip.DefaultCompression))
	e.Use(header.Options)
	e.Use(header.Secure)
	e.Use(middleware...)

	if config.Server.Pprof {
		pprof.Register(
			e,
			&pprof.Options{
				RoutePrefix: path.Join(config.Server.Root, "debug", "pprof"),
			},
		)
	}

	// 404 not found
	e.NoRoute(web.NotFound)

	// default route /
	root := e.Group(config.Server.Root)
	{
		if config.Storage.Driver == "disk" {
			root.StaticFS(
				"/storage",
				gin.Dir(
					config.Storage.Path,
					false,
				),
			)
		}

		root.StaticFS(
			"/assets",
			assets.Load(),
		)

		root.GET("", web.Index)
		root.GET("/favicon.ico", web.Favicon)
		root.GET("/metrics", prometheus.Handler())
		root.POST("/s", web.CreateShortenURL)
		root.GET("/g/:slug", web.FetchShortenedURL)
	}

	return e
}

// LoadRedirct initializes the routing of the shorten URL application.
func LoadRedirct(middleware ...gin.HandlerFunc) http.Handler {
	if config.Debug {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	e := gin.New()

	e.Use(gin.Recovery())
	e.Use(logger.SetLogger())
	e.Use(gzip.Gzip(gzip.DefaultCompression))
	e.Use(header.Options)
	e.Use(header.Secure)
	e.Use(middleware...)

	if config.Server.Pprof {
		pprof.Register(
			e,
			&pprof.Options{
				RoutePrefix: path.Join(config.Server.Root, "debug", "pprof"),
			},
		)
	}

	// 404 not found
	e.NoRoute(web.NotFound)

	// default route /
	root := e.Group(config.Server.Root)
	{
		root.GET("", web.Index)
		root.GET("/:slug", web.ShortenedURL)
	}

	return e
}
<commit_msg>remove shorten router<commit_after>package router

import (
	"net/http"
	"os"
	"path"

	"github.com/go-ggz/ggz/assets"
	"github.com/go-ggz/ggz/config"
	"github.com/go-ggz/ggz/models"
	"github.com/go-ggz/ggz/modules/minio"
	"github.com/go-ggz/ggz/router/middleware/header"
	"github.com/go-ggz/ggz/router/middleware/logger"
	"github.com/go-ggz/ggz/router/middleware/prometheus"
	"github.com/go-ggz/ggz/web"

	"github.com/gin-contrib/gzip"
	"github.com/gin-contrib/pprof"
	"github.com/gin-gonic/gin"
	"github.com/sirupsen/logrus"
)

// GlobalInit is for global configuration reload-able.
func GlobalInit() {
	if err := models.NewEngine(); err != nil {
		logrus.Fatalf("Failed to initialize ORM engine: %v", err)
	}

	if config.QRCode.Enable && config.Storage.Driver == "disk" {
		storage := path.Join(config.Storage.Path, config.QRCode.Bucket)
		if err := os.MkdirAll(storage, os.ModePerm); err != nil {
			logrus.Fatalf("Failed to create storage folder: %v", err)
		}
	}

	if config.QRCode.Enable && config.Storage.Driver == "s3" {
		minio.NewEngine(
			config.Minio.EndPoint,
			config.Minio.AccessID,
			config.Minio.SecretKey,
			config.Minio.SSL,
		)

		if err := minio.S3.MakeBucket(config.Minio.Bucket, config.Minio.Region); err != nil {
			logrus.Fatalf("Failed to create s3 bucket: %v", err)
		}
	}
}

// Load initializes the routing of the application.
func Load(middleware ...gin.HandlerFunc) http.Handler {
	if config.Debug {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	e := gin.New()

	e.Use(gin.Recovery())
	e.Use(logger.SetLogger())
	e.Use(gzip.Gzip(gzip.DefaultCompression))
	e.Use(header.Options)
	e.Use(header.Secure)
	e.Use(middleware...)

	if config.Server.Pprof {
		pprof.Register(
			e,
			&pprof.Options{
				RoutePrefix: path.Join(config.Server.Root, "debug", "pprof"),
			},
		)
	}

	// 404 not found
	e.NoRoute(web.NotFound)

	// default route /
	root := e.Group(config.Server.Root)
	{
		if config.Storage.Driver == "disk" {
			root.StaticFS(
				"/storage",
				gin.Dir(
					config.Storage.Path,
					false,
				),
			)
		}

		root.StaticFS(
			"/assets",
			assets.Load(),
		)

		root.GET("", web.Index)
		root.GET("/favicon.ico", web.Favicon)
		root.GET("/metrics", prometheus.Handler())
	}

	return e
}

// LoadRedirct initializes the routing of the shorten URL application.
func LoadRedirct(middleware ...gin.HandlerFunc) http.Handler {
	if config.Debug {
		gin.SetMode(gin.DebugMode)
	} else {
		gin.SetMode(gin.ReleaseMode)
	}

	e := gin.New()

	e.Use(gin.Recovery())
	e.Use(logger.SetLogger())
	e.Use(gzip.Gzip(gzip.DefaultCompression))
	e.Use(header.Options)
	e.Use(header.Secure)
	e.Use(middleware...)

	if config.Server.Pprof {
		pprof.Register(
			e,
			&pprof.Options{
				RoutePrefix: path.Join(config.Server.Root, "debug", "pprof"),
			},
		)
	}

	// 404 not found
	e.NoRoute(web.NotFound)

	// default route /
	root := e.Group(config.Server.Root)
	{
		root.GET("", web.Index)
		root.GET("/:slug", web.ShortenedURL)
	}

	return e
}
<|endoftext|>