<commit_before>//the icws_golib package wraps CIC ICWS fuctionality in a GO library.
//To get started, call icws_golib.NewIcws() to instansiate a new Icws
//struct.  Then call icws.Login(...) and continue on from there.
package icws_golib

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

type Icws struct {
	CurrentToken, CurrentCookie, CurrentSession, CurrentServer, UserId, HttpScheme string
	Port                                                                           int
}

//Version information for the server
type ServerVersion struct {
	//The product's two-digit release year. For the release "CIC 2015 R1" this value will be "15"
	MajorVersion string
	//The product's release number. For the release "CIC 2015 R1" this value will be "1".
	MinorVersion string
	//The patch number of the release. The value "0" indicates the release without any patches. For the release "CIC 2015 R1" this value will be "0", and for the release "CIC 2015 R1 Patch2" this value will be "2".
	Su string
	//The product line identifier.
	ProductId string
	//The codebase identifier.
	CodeBaseId string
	//The build number.
	Build string
	//The display string for the release. This does not include patch information.
	ProductReleaseDisplayString string
	//The display string for the release including patch information. This string is recommended for use on application "About" screens.
	ProductPatchDisplayString string
}

//Definition of a server feature
type ServerFeature struct {
	//Id of the features
	FeatureId string
	//version of the feature
	Version int
}

//Creates a new ICWS struct
func NewIcws() (icws *Icws) {
	icws = &Icws{}
	icws.HttpScheme = "https"
	icws.Port = 8019
	return
}

func (i *Icws) loginWithData(applicationName, server, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s://%s:%d"
	server = fmt.Sprintf(urlFormat, i.HttpScheme, server, i.Port)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into CIC through a reverse proxy
func (i *Icws) ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s/%s"
	server := fmt.Sprintf(urlFormat, i.HttpScheme, proxyUrl, serverName)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, i.HttpScheme, proxyUrl, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into a CIC server through a reverse proxy where the url to reach the server would be proxyUrl/serverName
func (i *Icws) ProxyLogin(applicationName, proxyUrl, serverName, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) ProxyLoginMarketPlaceApp(applicationName, proxyUrl, serverName, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server.  Server should be a server name e.g. MyServer.domain.com
func (i *Icws) Login(applicationName, server, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) LoginMarketPlaceApp(applicationName, server, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//gets server version details.  Does not need to be connected to the server
func (i *Icws) GetVersion() (version ServerVersion, err error) {

	body, err := i.httpNoConnectionGet("/connection/version")

	err = json.Unmarshal(body, &version)

	return
}

//Gets a list of features and their version from the server
func (i *Icws) GetFeatures() (features []ServerFeature, err error) {

	body, err := i.httpNoConnectionGet("/connection/features")

	if err != nil {
		return nil, err
	}

	var featureMap map[string][]ServerFeature
	err = json.Unmarshal(body, &featureMap)

	features = featureMap["featureInfoList"]
	return

}

func (i *Icws) httpDelete(url string) (err error) {

	req, err := i.httpRequest("DELETE", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, _ := ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpGet(url string) (body []byte, err error) {

	req, err := i.httpRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpNoConnectionGet(url string) (body []byte, err error) {

	req, err := i.httpNoConnectionRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpPost(url string, attrs map[string]string) (body []byte, err error) {
	body, _, err, _ = i.httpPostAdvanced(url, attrs)
	return
}

func (i *Icws) httpPostAdvanced(url string, attrs map[string]string) (body []byte, statusCode int, err error, cookie string) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("POST", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()

	statusCode = response.StatusCode

	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	if response.Header["Set-Cookie"] != nil {
		cookie = response.Header["Set-Cookie"][0]
	}
	return
}

func (i *Icws) httpPut(url string, attrs map[string]string) (body []byte, err error) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("PUT", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpClient() (client *http.Client) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

func (i *Icws) httpNoConnectionRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws"+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	return
}

func (i *Icws) httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws/"+i.CurrentSession+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	if len(i.CurrentCookie) > 0 {
		request.Header.Add("Cookie", i.CurrentCookie)
	} else {
		return
	}

	if len(i.CurrentToken) > 0 {
		request.Header.Add("ININ-ICWS-CSRF-Token", i.CurrentToken)
	} else {
		return
	}

	// request.Header.Add("User-Agent", fmt.Sprintf("cic cli (%s-%s)", runtime.GOOS, runtime.GOARCH))
	return
}

func createErrorMessage(statusCode int, body []byte) string {

	var errorDescription string

	switch statusCode {
	case 400:
		errorDescription = "Bad Request (400)"
	case 401:
		errorDescription = "Unauthorized (401)"
	case 403:
		errorDescription = "Forbidden (403)"
	case 404:
		errorDescription = "Not Found (404)"
	case 410:
		errorDescription = "Gone (410)"
	case 500:
		errorDescription = "Internal Server Error (500)"
	}

	var message map[string]interface{}
	json.Unmarshal(body, &message)

	if message["errorId"] != nil {
		errorDescription += ": " + message["errorId"].(string)
	}

	if message["message"] != nil {
		errorDescription += " " + message["message"].(string)
	}
	return errorDescription
}
<commit_msg>bug fixes<commit_after>//the icws_golib package wraps CIC ICWS fuctionality in a GO library.
//To get started, call icws_golib.NewIcws() to instansiate a new Icws
//struct.  Then call icws.Login(...) and continue on from there.
package icws_golib

import (
	"bytes"
	"crypto/tls"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"net/http"
)

type Icws struct {
	CurrentToken, CurrentCookie, CurrentSession, CurrentServer, UserId, HttpScheme string
	Port                                                                           int
}

//Version information for the server
type ServerVersion struct {
	//The product's two-digit release year. For the release "CIC 2015 R1" this value will be "15"
	MajorVersion string
	//The product's release number. For the release "CIC 2015 R1" this value will be "1".
	MinorVersion string
	//The patch number of the release. The value "0" indicates the release without any patches. For the release "CIC 2015 R1" this value will be "0", and for the release "CIC 2015 R1 Patch2" this value will be "2".
	Su string
	//The product line identifier.
	ProductId string
	//The codebase identifier.
	CodeBaseId string
	//The build number.
	Build string
	//The display string for the release. This does not include patch information.
	ProductReleaseDisplayString string
	//The display string for the release including patch information. This string is recommended for use on application "About" screens.
	ProductPatchDisplayString string
}

//Definition of a server feature
type ServerFeature struct {
	//Id of the features
	FeatureId string
	//version of the feature
	Version int
}

//Creates a new ICWS struct
func NewIcws() (icws *Icws) {
	icws = &Icws{}
	icws.HttpScheme = "https"
	icws.Port = 8019
	return
}

func (i *Icws) loginWithData(applicationName, server, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s://%s:%d"
	server = fmt.Sprintf(urlFormat, i.HttpScheme, server, i.Port)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, i.HttpScheme, nextServer, i.Port)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into CIC through a reverse proxy
func (i *Icws) ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password string, loginData map[string]string) (err error) {

	urlFormat := "%s/%s"
	server := fmt.Sprintf(urlFormat, serverName)

	log.Printf("Logging into %s with user %s", server, username)

	i.CurrentSession = ""
	i.CurrentServer = server
	body, statusCode, err, cookie := i.httpPostAdvanced("connection", loginData)

	nextServerIndex := 0
	for statusCode == 503 {

		var returnData map[string]interface{}
		json.Unmarshal(body, &returnData)

		nextServer := returnData["alternateHostList"].([]interface{})[0]

		server = fmt.Sprintf(urlFormat, nextServer)

		log.Printf("Redirected to server %s", server)

		i.CurrentServer = server
		body, statusCode, err, cookie = i.httpPostAdvanced("connection", loginData)

		nextServerIndex++
	}

	if err == nil {

		var returnData map[string]string
		json.Unmarshal(body, &returnData)
		i.CurrentToken = returnData["csrfToken"]
		i.CurrentSession = returnData["sessionId"]
		i.CurrentServer = server
		i.CurrentCookie = cookie
		i.UserId = username

	} else {
		log.Printf("ERROR: %s\n", err.Error())
	}
	return
}

//logs into a CIC server through a reverse proxy where the url to reach the server would be proxyUrl/serverName
func (i *Icws) ProxyLogin(applicationName, proxyUrl, serverName, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) ProxyLoginMarketPlaceApp(applicationName, proxyUrl, serverName, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.ProxyLoginWithData(applicationName, proxyUrl, serverName, username, password, loginData)
}

//Logs into a CIC server.  Server should be a server name e.g. MyServer.domain.com
func (i *Icws) Login(applicationName, server, username, password string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//Logs into a CIC server for a MarketPlace application using the app's custom license.  Server should be a url e.g. https://MyServer:8019
func (i *Icws) LoginMarketPlaceApp(applicationName, server, username, password, marketplaceLicense, marketplaceAppKey string) (err error) {

	var loginData = map[string]string{
		"__type":          "urn:inin.com:connection:icAuthConnectionRequestSettings",
		"applicationName": applicationName,
		"userID":          username,
		"password":        password,
		"marketPlaceApplicationLicenseName": marketplaceLicense,
		"marketPlaceApplicationCode" : marketplaceAppKey,
	}

	return i.loginWithData(applicationName, server, username, password, loginData)
}

//gets server version details.  Does not need to be connected to the server
func (i *Icws) GetVersion() (version ServerVersion, err error) {

	body, err := i.httpNoConnectionGet("/connection/version")

	err = json.Unmarshal(body, &version)

	return
}

//Gets a list of features and their version from the server
func (i *Icws) GetFeatures() (features []ServerFeature, err error) {

	body, err := i.httpNoConnectionGet("/connection/features")

	if err != nil {
		return nil, err
	}

	var featureMap map[string][]ServerFeature
	err = json.Unmarshal(body, &featureMap)

	features = featureMap["featureInfoList"]
	return

}

func (i *Icws) httpDelete(url string) (err error) {

	req, err := i.httpRequest("DELETE", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, _ := ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpGet(url string) (body []byte, err error) {

	req, err := i.httpRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpNoConnectionGet(url string) (body []byte, err error) {

	req, err := i.httpNoConnectionRequest("GET", url, nil)

	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return
	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)
	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpPost(url string, attrs map[string]string) (body []byte, err error) {
	body, _, err, _ = i.httpPostAdvanced(url, attrs)
	return
}

func (i *Icws) httpPostAdvanced(url string, attrs map[string]string) (body []byte, statusCode int, err error, cookie string) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("POST", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()

	statusCode = response.StatusCode

	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	if response.Header["Set-Cookie"] != nil {
		cookie = response.Header["Set-Cookie"][0]
	}
	return
}

func (i *Icws) httpPut(url string, attrs map[string]string) (body []byte, err error) {

	rbody, _ := json.Marshal(attrs)
	req, err := i.httpRequest("PUT", url, bytes.NewReader(rbody))
	if err != nil {
		return
	}

	response, err := i.httpClient().Do(req)
	if err != nil {
		return

	}
	defer response.Body.Close()
	if response.StatusCode == 401 {
		err = errors.New("authorization expired")
		return
	}
	body, err = ioutil.ReadAll(response.Body)

	if response.StatusCode/100 != 2 {
		err = errors.New(createErrorMessage(response.StatusCode, body))

		return
	}

	return
}

func (i *Icws) httpClient() (client *http.Client) {
	tr := &http.Transport{
		TLSClientConfig: &tls.Config{InsecureSkipVerify: true},
	}
	client = &http.Client{Transport: tr}
	return
}

func (i *Icws) httpNoConnectionRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws"+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	return
}

func (i *Icws) httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
	request, err = http.NewRequest(method, i.CurrentServer+"/icws/"+i.CurrentSession+url, body)
	if err != nil {
		return
	}

	request.Header.Add("Content-Type", "application/json")
	request.Header.Add("Accept-Language", "en-us")

	if len(i.CurrentCookie) > 0 {
		request.Header.Add("Cookie", i.CurrentCookie)
	} else {
		return
	}

	if len(i.CurrentToken) > 0 {
		request.Header.Add("ININ-ICWS-CSRF-Token", i.CurrentToken)
	} else {
		return
	}

	// request.Header.Add("User-Agent", fmt.Sprintf("cic cli (%s-%s)", runtime.GOOS, runtime.GOARCH))
	return
}

func createErrorMessage(statusCode int, body []byte) string {

	var errorDescription string

	switch statusCode {
	case 400:
		errorDescription = "Bad Request (400)"
	case 401:
		errorDescription = "Unauthorized (401)"
	case 403:
		errorDescription = "Forbidden (403)"
	case 404:
		errorDescription = "Not Found (404)"
	case 410:
		errorDescription = "Gone (410)"
	case 500:
		errorDescription = "Internal Server Error (500)"
	}

	var message map[string]interface{}
	json.Unmarshal(body, &message)

	if message["errorId"] != nil {
		errorDescription += ": " + message["errorId"].(string)
	}

	if message["message"] != nil {
		errorDescription += " " + message["message"].(string)
	}
	return errorDescription
}
<|endoftext|>


<commit_before>package clicommand

import (
	"fmt"
	"time"

	"github.com/buildkite/agent/api"
	"github.com/buildkite/agent/cliconfig"
	"github.com/buildkite/agent/retry"
	"github.com/urfave/cli"
)

var MetaDataKeysHelpDescription = `Usage:

   buildkite-agent meta-data keys [arguments...]

Description:

   Lists all meta-data keys that have been previously set, delimited by a newline.

Example:

   $ buildkite-agent meta-data keys`

type MetaDataKeysConfig struct {
	Job       string `cli:"job" validate:"required"`
	Delimiter string `cli:"delimiter"`

	// Global flags
	Debug   bool   `cli:"debug"`
	NoColor bool   `cli:"no-color"`
	Profile string `cli:"profile"`

	// API config
	DebugHTTP        bool   `cli:"debug-http"`
	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
	Endpoint         string `cli:"endpoint" validate:"required"`
	NoHTTP2          bool   `cli:"no-http2"`
}

var MetaDataKeysCommand = cli.Command{
	Name:        "keys",
	Usage:       "Lists all meta-data keys that have been previously set",
	Description: MetaDataKeysHelpDescription,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:   "job",
			Value:  "",
			Usage:  "Which job should the meta-data be checked for",
			EnvVar: "BUILDKITE_JOB_ID",
		},
		cli.StringFlag{
			Name:   "delimiter",
			Value:  "\n",
			Usage:  "The delimiter to use between meta-data keys",
			EnvVar: "BUILDKITE_META_DATA_KEYS_DELIMITER",
		},

		// API Flags
		AgentAccessTokenFlag,
		EndpointFlag,
		NoHTTP2Flag,
		DebugHTTPFlag,

		// Global flags
		NoColorFlag,
		DebugFlag,
		ProfileFlag,
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := MetaDataKeysConfig{}

		l := CreateLogger(&cfg)

		// Load the configuration
		if err := cliconfig.Load(c, l, &cfg); err != nil {
			l.Fatal("%s", err)
		}

		// Setup any global configuration options
		done := HandleGlobalFlags(l, cfg)
		defer done()

		// Create the API client
		client := api.NewClient(l, loadAPIClientConfig(cfg, `AgentAccessToken`))

		// Find the meta data keys
		var err error
		var keys []string
		var resp *api.Response
		err = retry.Do(func(s *retry.Stats) error {
			keys, resp, err = client.MetaDataKeys(cfg.Job)
			if resp != nil && (resp.StatusCode == 401 || resp.StatusCode == 404) {
				s.Break()
			}
			if err != nil {
				l.Warn("%s (%s)", err, s)
			}

			return err
		}, &retry.Config{Maximum: 10, Interval: 5 * time.Second})
		if err != nil {
			l.Fatal("Failed to find meta-data keys: %s", err)
		}

		for _, key := range keys {
			fmt.Printf("%s%s", cfg.Delimiter, key)
		}
	},
}
<commit_msg>Print delimiter after key<commit_after>package clicommand

import (
	"fmt"
	"time"

	"github.com/buildkite/agent/api"
	"github.com/buildkite/agent/cliconfig"
	"github.com/buildkite/agent/retry"
	"github.com/urfave/cli"
)

var MetaDataKeysHelpDescription = `Usage:

   buildkite-agent meta-data keys [arguments...]

Description:

   Lists all meta-data keys that have been previously set, delimited by a newline.

Example:

   $ buildkite-agent meta-data keys`

type MetaDataKeysConfig struct {
	Job       string `cli:"job" validate:"required"`
	Delimiter string `cli:"delimiter"`

	// Global flags
	Debug   bool   `cli:"debug"`
	NoColor bool   `cli:"no-color"`
	Profile string `cli:"profile"`

	// API config
	DebugHTTP        bool   `cli:"debug-http"`
	AgentAccessToken string `cli:"agent-access-token" validate:"required"`
	Endpoint         string `cli:"endpoint" validate:"required"`
	NoHTTP2          bool   `cli:"no-http2"`
}

var MetaDataKeysCommand = cli.Command{
	Name:        "keys",
	Usage:       "Lists all meta-data keys that have been previously set",
	Description: MetaDataKeysHelpDescription,
	Flags: []cli.Flag{
		cli.StringFlag{
			Name:   "job",
			Value:  "",
			Usage:  "Which job should the meta-data be checked for",
			EnvVar: "BUILDKITE_JOB_ID",
		},
		cli.StringFlag{
			Name:   "delimiter",
			Value:  "\n",
			Usage:  "The delimiter to use between meta-data keys",
			EnvVar: "BUILDKITE_META_DATA_KEYS_DELIMITER",
		},

		// API Flags
		AgentAccessTokenFlag,
		EndpointFlag,
		NoHTTP2Flag,
		DebugHTTPFlag,

		// Global flags
		NoColorFlag,
		DebugFlag,
		ProfileFlag,
	},
	Action: func(c *cli.Context) {
		// The configuration will be loaded into this struct
		cfg := MetaDataKeysConfig{}

		l := CreateLogger(&cfg)

		// Load the configuration
		if err := cliconfig.Load(c, l, &cfg); err != nil {
			l.Fatal("%s", err)
		}

		// Setup any global configuration options
		done := HandleGlobalFlags(l, cfg)
		defer done()

		// Create the API client
		client := api.NewClient(l, loadAPIClientConfig(cfg, `AgentAccessToken`))

		// Find the meta data keys
		var err error
		var keys []string
		var resp *api.Response
		err = retry.Do(func(s *retry.Stats) error {
			keys, resp, err = client.MetaDataKeys(cfg.Job)
			if resp != nil && (resp.StatusCode == 401 || resp.StatusCode == 404) {
				s.Break()
			}
			if err != nil {
				l.Warn("%s (%s)", err, s)
			}

			return err
		}, &retry.Config{Maximum: 10, Interval: 5 * time.Second})
		if err != nil {
			l.Fatal("Failed to find meta-data keys: %s", err)
		}

		last := len(keys) - 1

		for idx, key := range keys {
			fmt.Printf("%s", key)
			if idx != last {
				fmt.Printf("%s", cfg.Delimiter)
			}
		}
	},
}
<|endoftext|>


<commit_before>package kv_kvs_impl

import (
	"encoding/json"
	"github.com/dgraph-io/badger"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/kvs/kv_kvs"
	"go.uber.org/zap"
)

func New(ctl app_control.Control, db *badger.DB, tx *badger.Txn) kv_kvs.Kvs {
	return &badgerWrapper{
		ctl: ctl,
		db:  db,
		tx:  tx,
	}
}

type badgerWrapper struct {
	ctl app_control.Control
	db  *badger.DB
	tx  *badger.Txn
}

func (z *badgerWrapper) NextSequence(name string) (uint64, error) {
	l := z.ctl.Log().With(zap.String("name", name))
	seq, err := z.db.GetSequence([]byte(name), 100)
	if err != nil {
		l.Debug("Unable to get seq", zap.Error(err))
		return 0, err
	}
	defer seq.Release()
	s, err := seq.Next()
	if err != nil {
		l.Debug("Unable to generate seq", zap.Error(err))
		return 0, err
	}
	return s, nil
}

func (z *badgerWrapper) PutString(key string, value string) error {
	l := z.ctl.Log()
	err := z.tx.Set([]byte(key), []byte(value))
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) PutBytes(key string, value []byte) error {
	l := z.ctl.Log()
	err := z.tx.Set([]byte(key), value)
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) PutJson(key string, j json.RawMessage) error {
	l := z.ctl.Log()
	err := z.tx.Set([]byte(key), j)
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) PutJsonModel(key string, v interface{}) error {
	l := z.ctl.Log()
	b, err := json.Marshal(v)
	if err != nil {
		l.Debug("Unable to marshal value", zap.Error(err))
		return err
	}
	err = z.tx.Set([]byte(key), b)
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) GetString(key string) (value string, err error) {
	r, err := z.tx.Get([]byte(key))
	if err != nil {
		return "", err
	}
	return r.String(), nil
}

func (z *badgerWrapper) GetBytes(key string) (value []byte, err error) {
	r, err := z.tx.Get([]byte(key))
	if err != nil {
		return nil, err
	}
	return r.ValueCopy(nil)
}

func (z *badgerWrapper) GetJson(key string) (j json.RawMessage, err error) {
	r, err := z.tx.Get([]byte(key))
	if err != nil {
		return nil, err
	}
	return r.ValueCopy(nil)
}

func (z *badgerWrapper) GetJsonModel(key string, v interface{}) (err error) {
	r, err := z.GetJson(key)
	if err = json.Unmarshal(r, v); err != nil {
		return err
	}
	return nil
}

func (z *badgerWrapper) Delete(key string) error {
	return z.tx.Delete([]byte(key))
}

func (z *badgerWrapper) ForEach(f func(key string, value []byte) error) error {
	opts := badger.DefaultIteratorOptions
	opts.PrefetchSize = 100
	it := z.tx.NewIterator(opts)
	defer it.Close()
	for it.Rewind(); it.Valid(); it.Next() {
		i := it.Item()
		v, err := i.ValueCopy(nil)
		if err != nil {
			return err
		}
		if err := f(string(i.Key()), v); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>fix kvs logic & test<commit_after>package kv_kvs_impl

import (
	"encoding/json"
	"github.com/dgraph-io/badger"
	"github.com/watermint/toolbox/infra/control/app_control"
	"github.com/watermint/toolbox/infra/kvs/kv_kvs"
	"go.uber.org/zap"
)

func New(ctl app_control.Control, db *badger.DB, tx *badger.Txn) kv_kvs.Kvs {
	return &badgerWrapper{
		ctl: ctl,
		db:  db,
		tx:  tx,
	}
}

type badgerWrapper struct {
	ctl app_control.Control
	db  *badger.DB
	tx  *badger.Txn
}

func (z *badgerWrapper) NextSequence(name string) (uint64, error) {
	l := z.ctl.Log().With(zap.String("name", name))
	seq, err := z.db.GetSequence([]byte(name), 100)
	if err != nil {
		l.Debug("Unable to get seq", zap.Error(err))
		return 0, err
	}
	defer seq.Release()
	s, err := seq.Next()
	if err != nil {
		l.Debug("Unable to generate seq", zap.Error(err))
		return 0, err
	}
	return s, nil
}

func (z *badgerWrapper) PutString(key string, value string) error {
	l := z.ctl.Log()
	err := z.tx.Set([]byte(key), []byte(value))
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) PutBytes(key string, value []byte) error {
	l := z.ctl.Log()
	err := z.tx.Set([]byte(key), value)
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) PutJson(key string, j json.RawMessage) error {
	l := z.ctl.Log()
	err := z.tx.Set([]byte(key), j)
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) PutJsonModel(key string, v interface{}) error {
	l := z.ctl.Log()
	b, err := json.Marshal(v)
	if err != nil {
		l.Debug("Unable to marshal value", zap.Error(err))
		return err
	}
	err = z.tx.Set([]byte(key), b)
	if err != nil {
		l.Debug("Unable to put key/value", zap.String("key", key))
		return err
	}
	return nil
}

func (z *badgerWrapper) GetString(key string) (value string, err error) {
	r, err := z.tx.Get([]byte(key))
	if err != nil {
		return "", err
	}
	v, err := r.ValueCopy(nil)
	if err != nil {
		return "", err
	}
	return string(v), nil
}

func (z *badgerWrapper) GetBytes(key string) (value []byte, err error) {
	r, err := z.tx.Get([]byte(key))
	if err != nil {
		return nil, err
	}
	return r.ValueCopy(nil)
}

func (z *badgerWrapper) GetJson(key string) (j json.RawMessage, err error) {
	r, err := z.tx.Get([]byte(key))
	if err != nil {
		return nil, err
	}
	return r.ValueCopy(nil)
}

func (z *badgerWrapper) GetJsonModel(key string, v interface{}) (err error) {
	r, err := z.GetJson(key)
	if err = json.Unmarshal(r, v); err != nil {
		return err
	}
	return nil
}

func (z *badgerWrapper) Delete(key string) error {
	return z.tx.Delete([]byte(key))
}

func (z *badgerWrapper) ForEach(f func(key string, value []byte) error) error {
	opts := badger.DefaultIteratorOptions
	opts.PrefetchSize = 100
	it := z.tx.NewIterator(opts)
	defer it.Close()
	for it.Rewind(); it.Valid(); it.Next() {
		i := it.Item()
		v, err := i.ValueCopy(nil)
		if err != nil {
			return err
		}
		if err := f(string(i.Key()), v); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
)

const (
	helpString = `
usage: overalls -project=[path] -covermode[mode] OPTIONS

overalls recursively traverses your projects directory structure
running 'go test -covermode=count -coverprofile=profile.coverprofile'
in each directory with go test files, concatenates them into one
coverprofile in your root directory named 'overalls.coverprofile'

OPTIONS
  -project
	Your project path relative to the '$GOPATH/src' directory
	example: -project=github.com/bluesuncorp/overalls

  -covermode
    Mode to run when testing files.
    default:count

OPTIONAL

  -ignore
    A comma separated list of directory names to ignore, relative to project path.
    example: -ignore=[.git,.hiddentdir...]
    default: '.git,vendor'

  -debug
    A flag indicating whether to print debug messages.
    example: -debug
    default:false
`
)

const (
	defaultIgnores = ".git,vendor"
	outFilename    = "overalls.coverprofile"
	pkgFilename    = "profile.coverprofile"
)

var (
	modeRegex   = regexp.MustCompile("mode: [a-z]+\n")
	gopath      = filepath.Clean(os.Getenv("GOPATH"))
	srcPath     = gopath + "/src/"
	projectPath string
	ignoreFlag  string
	projectFlag string
	coverFlag   string
	helpFlag    bool
	debugFlag   bool
	emptyStruct struct{}
	ignores     = map[string]struct{}{}
)

func help() {
	fmt.Printf(helpString)
}

func init() {
	flag.StringVar(&projectFlag, "project", "", "-project [path]: relative to the '$GOPATH/src' directory")
	flag.StringVar(&coverFlag, "covermode", "count", "Mode to run when testing files")
	flag.StringVar(&ignoreFlag, "ignore", defaultIgnores, "-ignore [dir1,dir2...]: comma separated list of directory names to ignore")
	flag.BoolVar(&debugFlag, "debug", false, "-debug [true|false]")
	flag.BoolVar(&helpFlag, "help", false, "-help")
}

func parseFlags() {
	flag.Parse()

	if helpFlag {
		help()
		os.Exit(0)
	}

	if debugFlag {
		fmt.Println("GOPATH:", gopath)
	}

	if len(gopath) == 0 || gopath == "." {
		fmt.Printf("\n**invalid GOPATH '%s'\n", gopath)
		os.Exit(1)
	}

	fmt.Println("|", projectFlag)
	projectFlag = filepath.Clean(projectFlag)

	if debugFlag {
		fmt.Println("Project Path:", projectFlag)
	}

	if len(projectFlag) == 0 || projectFlag == "." {
		fmt.Printf("\n**invalid project path '%s'\n", projectFlag)
		help()
		os.Exit(1)
	}

	switch coverFlag {
	case "set", "count", "atomic":
	default:
		fmt.Printf("\n**invalid covermode '%s'\n", coverFlag)
		os.Exit(1)
	}

	arr := strings.Split(ignoreFlag, ",")
	for _, v := range arr {
		ignores[v] = emptyStruct
	}
}

func main() {
	logger := log.New(os.Stdout, "", log.LstdFlags)
	runMain(logger)
}

func runMain(logger *log.Logger) {
	parseFlags()

	var err error
	var wd string

	projectPath = srcPath + projectFlag + "/"

	if err = os.Chdir(projectPath); err != nil {
		logger.Printf("\n**invalid project path '%s'\n%s\n", projectFlag, err)
		help()
		os.Exit(1)
	}

	if debugFlag {
		wd, err = os.Getwd()
		if err != nil {
			fmt.Println(err)
		}

		logger.Printf("Working DIR:", wd)
	}

	testFiles(logger)
}

func processDIR(logger *log.Logger, wg *sync.WaitGroup, fullPath, relPath string, out chan<- []byte) {

	defer wg.Done()

	// 1 for "test", 4 for coermode, coverprofile, outputdir, relpath
	args := make([]string, 1, 1+len(flag.Args())+4)
	args[0] = "test"
	args = append(args, flag.Args()...)
	args = append(args, "-covermode="+coverFlag, "-coverprofile="+pkgFilename, "-outputdir="+fullPath+"/", relPath)
	fmt.Printf("Test args: %+v\n", args)

	cmd := exec.Command("go", args...)
	if debugFlag {
		logger.Println("Processing:", strings.Join(cmd.Args, " "))
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		logger.Fatal("Unable to get process stdout")
	}
	go func() {
		bs := bufio.NewScanner(stdout)
		defer stdout.Close()
		for bs.Scan() {
			logger.Print(bs.Text())
		}
	}()

	if err := cmd.Run(); err != nil {
		logger.Fatal("ERROR:", err)
		os.Exit(1)
	}

	b, err := ioutil.ReadFile(relPath + "/profile.coverprofile")
	if err != nil {
		logger.Println("ERROR:", err)
		os.Exit(1)
	}

	out <- b
}

func testFiles(logger *log.Logger) {
	out := make(chan []byte)
	wg := &sync.WaitGroup{}

	walker := func(path string, info os.FileInfo, err error) error {

		if !info.IsDir() {
			return nil
		}

		rel := strings.Replace(path, projectPath, "", 1)

		if _, ignore := ignores[rel]; ignore {
			return filepath.SkipDir
		}

		rel = "./" + rel

		if files, err := filepath.Glob(rel + "/*_test.go"); len(files) == 0 || err != nil {

			if err != nil {
				logger.Printf("Error checking for test files")
				os.Exit(1)
			}

			if debugFlag {
				logger.Printf("No Go Test files in DIR:", rel, "skipping")
			}

			return nil
		}

		wg.Add(1)
		go processDIR(logger, wg, path, rel, out)

		return nil
	}

	if err := filepath.Walk(projectPath, walker); err != nil {
		logger.Fatalf("\n**could not walk project path '%s'\n%s\n", projectPath, err)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	buff := bytes.NewBufferString("")

	for cover := range out {
		buff.Write(cover)
	}

	final := buff.String()
	final = modeRegex.ReplaceAllString(final, "")
	final = "mode: " + coverFlag + "\n" + final

	if err := ioutil.WriteFile(outFilename, []byte(final), 0644); err != nil {
		logger.Fatal("ERROR Writing \""+outFilename+"\"", err)
	}
}
<commit_msg>Minor cleanup<commit_after>package main

import (
	"bufio"
	"bytes"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"strings"
	"sync"
)

const (
	helpString = `
usage: overalls -project=[path] -covermode[mode] OPTIONS

overalls recursively traverses your projects directory structure
running 'go test -covermode=count -coverprofile=profile.coverprofile'
in each directory with go test files, concatenates them into one
coverprofile in your root directory named 'overalls.coverprofile'

OPTIONS
  -project
	Your project path relative to the '$GOPATH/src' directory
	example: -project=github.com/bluesuncorp/overalls

  -covermode
    Mode to run when testing files.
    default:count

OPTIONAL

  -ignore
    A comma separated list of directory names to ignore, relative to project path.
    example: -ignore=[.git,.hiddentdir...]
    default: '.git,vendor'

  -debug
    A flag indicating whether to print debug messages.
    example: -debug
    default:false
`
)

const (
	defaultIgnores = ".git,vendor"
	outFilename    = "overalls.coverprofile"
	pkgFilename    = "profile.coverprofile"
)

var (
	modeRegex   = regexp.MustCompile("mode: [a-z]+\n")
	gopath      = filepath.Clean(os.Getenv("GOPATH"))
	srcPath     = gopath + "/src/"
	projectPath string
	ignoreFlag  string
	projectFlag string
	coverFlag   string
	helpFlag    bool
	debugFlag   bool
	emptyStruct struct{}
	ignores     = map[string]struct{}{}
)

func help() {
	fmt.Printf(helpString)
}

func init() {
	flag.StringVar(&projectFlag, "project", "", "-project [path]: relative to the '$GOPATH/src' directory")
	flag.StringVar(&coverFlag, "covermode", "count", "Mode to run when testing files")
	flag.StringVar(&ignoreFlag, "ignore", defaultIgnores, "-ignore [dir1,dir2...]: comma separated list of directory names to ignore")
	flag.BoolVar(&debugFlag, "debug", false, "-debug [true|false]")
	flag.BoolVar(&helpFlag, "help", false, "-help")
}

func parseFlags() {
	flag.Parse()

	if helpFlag {
		help()
		os.Exit(0)
	}

	if debugFlag {
		fmt.Println("GOPATH:", gopath)
	}

	if len(gopath) == 0 || gopath == "." {
		fmt.Printf("\n**invalid GOPATH '%s'\n", gopath)
		os.Exit(1)
	}

	fmt.Println("|", projectFlag)
	projectFlag = filepath.Clean(projectFlag)

	if debugFlag {
		fmt.Println("Project Path:", projectFlag)
	}

	if len(projectFlag) == 0 || projectFlag == "." {
		fmt.Printf("\n**invalid project path '%s'\n", projectFlag)
		help()
		os.Exit(1)
	}

	switch coverFlag {
	case "set", "count", "atomic":
	default:
		fmt.Printf("\n**invalid covermode '%s'\n", coverFlag)
		os.Exit(1)
	}

	arr := strings.Split(ignoreFlag, ",")
	for _, v := range arr {
		ignores[v] = emptyStruct
	}
}

func main() {
	logger := log.New(os.Stdout, "", log.LstdFlags)
	runMain(logger)
}

func runMain(logger *log.Logger) {
	parseFlags()

	var err error
	var wd string

	projectPath = srcPath + projectFlag + "/"

	if err = os.Chdir(projectPath); err != nil {
		logger.Printf("\n**invalid project path '%s'\n%s\n", projectFlag, err)
		help()
		os.Exit(1)
	}

	if debugFlag {
		wd, err = os.Getwd()
		if err != nil {
			fmt.Println(err)
		}

		logger.Println("Working DIR:", wd)
	}

	testFiles(logger)
}

func scanOutput(r io.ReadCloser, fn func(...interface{})) {
	defer r.Close()
	bs := bufio.NewScanner(r)
	for bs.Scan() {
		fn(bs.Text())
	}
	if err := bs.Err(); err != nil {
		fn(fmt.Sprintf("Scan error: %v", err.Error()))
	}
}

func processDIR(logger *log.Logger, wg *sync.WaitGroup, fullPath, relPath string, out chan<- []byte) {
	defer wg.Done()

	// 1 for "test", 4 for coermode, coverprofile, outputdir, relpath
	args := make([]string, 1, 1+len(flag.Args())+4)
	args[0] = "test"
	args = append(args, flag.Args()...)
	args = append(args, "-covermode="+coverFlag, "-coverprofile="+pkgFilename, "-outputdir="+fullPath+"/", relPath)
	fmt.Printf("Test args: %+v\n", args)

	cmd := exec.Command("go", args...)
	if debugFlag {
		logger.Println("Processing:", strings.Join(cmd.Args, " "))
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		logger.Fatal("Unable to get process stdout")
	}
	go scanOutput(stdout, logger.Print)

	stderr, err := cmd.StderrPipe()
	if err != nil {
		logger.Fatal("Unable to get process stderr")
	}
	go scanOutput(stderr, logger.Print)

	if err := cmd.Run(); err != nil {
		logger.Fatal("ERROR:", err)
	}

	b, err := ioutil.ReadFile(relPath + "/profile.coverprofile")
	if err != nil {
		logger.Fatal("ERROR:", err)
	}

	out <- b
}

func testFiles(logger *log.Logger) {
	out := make(chan []byte)
	wg := &sync.WaitGroup{}

	walker := func(path string, info os.FileInfo, err error) error {

		if !info.IsDir() {
			return nil
		}

		rel := strings.Replace(path, projectPath, "", 1)

		if _, ignore := ignores[rel]; ignore {
			return filepath.SkipDir
		}

		rel = "./" + rel

		if files, err := filepath.Glob(rel + "/*_test.go"); len(files) == 0 || err != nil {

			if err != nil {
				logger.Printf("Error checking for test files")
				os.Exit(1)
			}

			if debugFlag {
				logger.Printf("No Go Test files in DIR:", rel, "skipping")
			}

			return nil
		}

		wg.Add(1)
		go processDIR(logger, wg, path, rel, out)

		return nil
	}

	if err := filepath.Walk(projectPath, walker); err != nil {
		logger.Fatalf("\n**could not walk project path '%s'\n%s\n", projectPath, err)
	}

	go func() {
		wg.Wait()
		close(out)
	}()

	buff := bytes.NewBufferString("")

	for cover := range out {
		buff.Write(cover)
	}

	final := buff.String()
	final = modeRegex.ReplaceAllString(final, "")
	final = "mode: " + coverFlag + "\n" + final

	if err := ioutil.WriteFile(outFilename, []byte(final), 0644); err != nil {
		logger.Fatal("ERROR Writing \""+outFilename+"\"", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

func main() {
	db, err := sql.Open("mysql", "root@tcp(localhost:3306)/peter")
	if err != nil {
		panic(err.Error())
	}
	defer db.Close()

	rows, err := db.Query("SELECT name, is_active FROM user")
	if err != nil {
		panic(err.Error())
	}

	for rows.Next() {
		var name string
		var active bool
		if err := rows.Scan(&name, &active); err != nil {
			panic(err.Error())
		}
		fmt.Println("-----------------------------------")
		fmt.Println("Name: ", name)
		fmt.Println("Active: ", active)
	}
}
<commit_msg>Address parameter flag<commit_after>package main

import (
	"database/sql"
	"flag"
	"fmt"
	_ "github.com/go-sql-driver/mysql"
)

var MySQLAddress = flag.String("address", "root@tcp(localhost:3306)/peter", "Database connection address")

func main() {
	flag.Parse()
	db, err := sql.Open("mysql", *MySQLAddress)
	if err != nil {
		fmt.Println("Unable to connect to that address, baby")
		fmt.Println("-----------------------------------")
		panic(err.Error())
	}
	defer db.Close()

	rows, err := db.Query(`SELECT
								product.id,
								product.name,
								product.category_id,
								category.name
							FROM
							    product,
							    category
							WHERE
							    product.category_id=category.id;`)

	if err != nil {
		panic(err.Error())
	}

	for rows.Next() {
		var id, name, category_id, category_name string
		if err := rows.Scan(&id, &name, &category_id, &category_name); err != nil {
			panic(err.Error())
		}
		fmt.Println("-----------------------------------")
		fmt.Println("Name: ", name)
		fmt.Println("Category: ", category_name)
	}
}
<|endoftext|>


<commit_before>// An IMAP server
package imapsrv

import (
	"bufio"
	"fmt"
	"auth"
	"log"
	"net"
)

// DefaultListener is the listener that is used if no listener is specified
const DefaultListener = "0.0.0.0:143"

// config is an IMAP server configuration
type config struct {
	maxClients uint
	listeners  []listener
	mailstore  Mailstore

	authBackend auth.AuthStore
}

type option func(*Server) error

// listener represents a listener as used by the server
type listener struct {
	addr string
}

// Server is an IMAP Server
type Server struct {
	// Server configuration
	config *config
	// Number of active clients
	activeClients uint
}

// client is an IMAP Client as seen by an IMAP server
type client struct {
	conn   net.Conn
	bufin  *bufio.Reader
	bufout *bufio.Writer
	id     string
	config *config
}

// defaultConfig returns the default server configuration
func defaultConfig() *config {
	return &config{
		listeners:  make([]listener, 0, 4),
		maxClients: 8,
	}
}

// Add a mailstore to the config
// StoreOption add a mailstore to the config
func StoreOption(m Mailstore) option {
	return func(s *Server) error {
		s.config.mailstore = m
		return nil
	}
}

// AuthStoreOption adds an authenticaton backend
func AuthStoreOption(a auth.AuthStore) option {
	return func(s *Server) error {
		s.config.authBackend = a
		return nil
	}
}

// ListenOption adds an interface to listen to
func ListenOption(Addr string) option {
	return func(s *Server) error {
		l := listener{
			addr: Addr,
		}
		s.config.listeners = append(s.config.listeners, l)
		return nil
	}
}

// MaxClientsOption sets the MaxClients config
func MaxClientsOption(max uint) option {
	return func(s *Server) error {
		s.config.maxClients = max
		return nil
	}
}

// NewServer creates a new server with the given options
func NewServer(options ...option) *Server {
	// set the default config
	s := &Server{}
	s.config = defaultConfig()

	// override the config with the functional options
	for _, option := range options {
		err := option(s)
		if err != nil {
			panic(err)
		}
	}

	return s
}

// Start an IMAP server
func (s *Server) Start() error {

	listeners := make([]net.Listener, 0, 4)

	// Use a default listener if none exist
	if len(s.config.listeners) == 0 {
		s.config.listeners = append(s.config.listeners,
			listener{addr: DefaultListener})
	}

	// Start listening for IMAP connections
	for _, iface := range s.config.listeners {
		listener, err := net.Listen("tcp", iface.addr)
		if err != nil {
			log.Printf("IMAP cannot listen on %s, %v", iface.addr, err)
			return err
		}

		listeners = append(listeners, listener)
	}

	// Start the server on each port
	n := len(listeners)
	for i := 0; i < n; i += 1 {
		listener := listeners[i]

		// Start each listener in a separate go routine
		// except for the last one
		if i < n-1 {
			go s.runListener(listener, i)
		} else {
			s.runListener(listener, i)
		}
	}

	return nil
}

// runListener runs the given listener on a separate goroutine
func (s *Server) runListener(listener net.Listener, id int) {

	log.Printf("IMAP server %d listening on %s", id, listener.Addr().String())

	clientNumber := 1

	for {
		// Accept a connection from a new client
		conn, err := listener.Accept()
		if err != nil {
			log.Print("IMAP accept error, ", err)
			continue
		}

		// Handle the client
		client := &client{
			conn:   conn,
			bufin:  bufio.NewReader(conn),
			bufout: bufio.NewWriter(conn),
			id:     fmt.Sprint(id, "/", clientNumber),
			config: s.config,
		}

		go client.handle(s)

		clientNumber += 1
	}

}

// handle requests from an IMAP client
func (c *client) handle(s *Server) {

	// Close the client on exit from this function
	defer c.close()

	// Handle parser panics gracefully
	defer func() {
		if e := recover(); e != nil {
			err := e.(parseError)
			c.logError(err)
			fatalResponse(c.bufout, err)
		}
	}()

	// Create a parser
	parser := createParser(c.bufin)

	// Write the welcome message
	err := ok("*", "IMAP4rev1 Service Ready").write(c.bufout)

	if err != nil {
		c.logError(err)
		return
	}

	//  Create a session
	sess := createSession(c.id, c.config, s)

	for {
		// Get the next IMAP command
		command := parser.next()

		// Execute the IMAP command
		response := command.execute(sess)

		// Write back the response
		err = response.write(c.bufout)

		if err != nil {
			c.logError(err)
			return
		}

		// Should the connection be closed?
		if response.closeConnection {
			return
		}
	}
}

// close closes an IMAP client
func (c *client) close() {
	c.conn.Close()
}

// logError sends a log message to the default Logger
func (c *client) logError(err error) {
	log.Printf("IMAP client %s, %v", c.id, err)
}
<commit_msg>Added missing file from previous commit.<commit_after>// An IMAP server
package imapsrv

import (
	"bufio"
	"fmt"
	"github.com/alienscience/imapsrv/auth"
	"log"
	"net"
)

// DefaultListener is the listener that is used if no listener is specified
const DefaultListener = "0.0.0.0:143"

// config is an IMAP server configuration
type config struct {
	maxClients uint
	listeners  []listener
	mailstore  Mailstore

	authBackend auth.AuthStore
}

type option func(*Server) error

// listener represents a listener as used by the server
type listener struct {
	addr string
}

// Server is an IMAP Server
type Server struct {
	// Server configuration
	config *config
	// Number of active clients
	activeClients uint
}

// client is an IMAP Client as seen by an IMAP server
type client struct {
	conn   net.Conn
	bufin  *bufio.Reader
	bufout *bufio.Writer
	id     string
	config *config
}

// defaultConfig returns the default server configuration
func defaultConfig() *config {
	return &config{
		listeners:  make([]listener, 0, 4),
		maxClients: 8,
	}
}

// Add a mailstore to the config
// StoreOption add a mailstore to the config
func StoreOption(m Mailstore) option {
	return func(s *Server) error {
		s.config.mailstore = m
		return nil
	}
}

// AuthStoreOption adds an authenticaton backend
func AuthStoreOption(a auth.AuthStore) option {
	return func(s *Server) error {
		s.config.authBackend = a
		return nil
	}
}

// ListenOption adds an interface to listen to
func ListenOption(Addr string) option {
	return func(s *Server) error {
		l := listener{
			addr: Addr,
		}
		s.config.listeners = append(s.config.listeners, l)
		return nil
	}
}

// MaxClientsOption sets the MaxClients config
func MaxClientsOption(max uint) option {
	return func(s *Server) error {
		s.config.maxClients = max
		return nil
	}
}

// NewServer creates a new server with the given options
func NewServer(options ...option) *Server {
	// set the default config
	s := &Server{}
	s.config = defaultConfig()

	// override the config with the functional options
	for _, option := range options {
		err := option(s)
		if err != nil {
			panic(err)
		}
	}

	return s
}

// Start an IMAP server
func (s *Server) Start() error {

	listeners := make([]net.Listener, 0, 4)

	// Use a default listener if none exist
	if len(s.config.listeners) == 0 {
		s.config.listeners = append(s.config.listeners,
			listener{addr: DefaultListener})
	}

	// Start listening for IMAP connections
	for _, iface := range s.config.listeners {
		listener, err := net.Listen("tcp", iface.addr)
		if err != nil {
			log.Printf("IMAP cannot listen on %s, %v", iface.addr, err)
			return err
		}

		listeners = append(listeners, listener)
	}

	// Start the server on each port
	n := len(listeners)
	for i := 0; i < n; i += 1 {
		listener := listeners[i]

		// Start each listener in a separate go routine
		// except for the last one
		if i < n-1 {
			go s.runListener(listener, i)
		} else {
			s.runListener(listener, i)
		}
	}

	return nil
}

// runListener runs the given listener on a separate goroutine
func (s *Server) runListener(listener net.Listener, id int) {

	log.Printf("IMAP server %d listening on %s", id, listener.Addr().String())

	clientNumber := 1

	for {
		// Accept a connection from a new client
		conn, err := listener.Accept()
		if err != nil {
			log.Print("IMAP accept error, ", err)
			continue
		}

		// Handle the client
		client := &client{
			conn:   conn,
			bufin:  bufio.NewReader(conn),
			bufout: bufio.NewWriter(conn),
			id:     fmt.Sprint(id, "/", clientNumber),
			config: s.config,
		}

		go client.handle(s)

		clientNumber += 1
	}

}

// handle requests from an IMAP client
func (c *client) handle(s *Server) {

	// Close the client on exit from this function
	defer c.close()

	// Handle parser panics gracefully
	defer func() {
		if e := recover(); e != nil {
			err := e.(parseError)
			c.logError(err)
			fatalResponse(c.bufout, err)
		}
	}()

	// Create a parser
	parser := createParser(c.bufin)

	// Write the welcome message
	err := ok("*", "IMAP4rev1 Service Ready").write(c.bufout)

	if err != nil {
		c.logError(err)
		return
	}

	//  Create a session
	sess := createSession(c.id, c.config, s)

	for {
		// Get the next IMAP command
		command := parser.next()

		// Execute the IMAP command
		response := command.execute(sess)

		// Write back the response
		err = response.write(c.bufout)

		if err != nil {
			c.logError(err)
			return
		}

		// Should the connection be closed?
		if response.closeConnection {
			return
		}
	}
}

// close closes an IMAP client
func (c *client) close() {
	c.conn.Close()
}

// logError sends a log message to the default Logger
func (c *client) logError(err error) {
	log.Printf("IMAP client %s, %v", c.id, err)
}
<|endoftext|>


<commit_before>package main

import (
	"os/exec"
	"strings"
	"encoding/gob"
	"fmt"
	"bufio"
	"io"
	"log"
	"net"
	"os"

	"github.com/flynn/go-discover/discover"
	"github.com/flynn/lorne/types"
	"github.com/titanous/go-dockerclient"
	"github.com/flynn/sampi/types"
	sc "github.com/flynn/sampi/client"
	lc "github.com/flynn/lorne/client"
)

// WARNING: assumes one host at the moment (firstHost will always be the same)

var sd *discover.Client
var sched *sc.Client
var host *lc.Client
var hostid string

func init() {
	var err error
	sd, err = discover.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	sched, err = sc.New()
	if err != nil {
		log.Fatal(err)
	}

	hostid = findHost()
	host, err = lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	root := "/var/lib/demo/apps"
	hostname := shell("curl -s icanhazip.com")

	set, _ := sd.Services("shelf")
	addrs := set.OnlineAddrs()
	if len(addrs) < 1 {
		panic("Shelf is not discoverable")
	}
	shelfHost := addrs[0]

	app := os.Args[2]
	os.MkdirAll(root+"/"+app, 0755)

	fmt.Printf("-----> Building %s on %s ...\n", app, hostname)

	scheduleAndAttach(app + ".build", docker.Config{
		Image:        "flynn/slugbuilder",
		Cmd:          []string{"http://" + shelfHost + "/" + app + ".tgz"},
		Tty:          false,
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		OpenStdin:    true,
		StdinOnce:    true,
	})

	fmt.Printf("-----> Deploying %s ...\n", app)

	jobid := app + ".web"

	stopIfExists(jobid)
	scheduleWithTcpPort(jobid, docker.Config{
		Image:        "flynn/slugrunner",
		Cmd:          []string{"start web"},
		Tty:          false,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		OpenStdin:    false,
		StdinOnce:    false,
		Env: []string{
			"SLUG_URL=http://" + shelfHost + "/" + app + ".tgz",
		},
	})

	fmt.Printf("=====> Application deployed!\n")
	//fmt.Printf("       %s\n", getUrl(jobid))
	fmt.Println("")

}

func shell(cmdline string) string {
        out, err := exec.Command("bash", "-c", cmdline).Output()
        if err != nil {
                panic(err)
        }
        return strings.Trim(string(out), " \n")
}

func stopIfExists(jobid string) {
	_, err := host.GetJob(jobid)
	if err != nil {
		return
	}
	if err := host.StopJob(jobid); err != nil {
		log.Fatal(err)
	}
}

func scheduleWithTcpPort(jobid string, config docker.Config) {

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config, TCPPorts: 1}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}
}

/*func getUrl(jobid string) string {
	if job, err := host.GetJob(jobid); err != nil {
		log.Fatal(err)
	}
}*/

func findHost() string {
	state, err := sched.State()
	if err != nil {
		log.Fatal(err)
	}

	var firstHost string
	for k := range state {
		firstHost = k
		break
	}
	if firstHost == "" {
		log.Fatal("no hosts")
	}
	return firstHost
}

func scheduleAndAttach(jobid string, config docker.Config) {

	services, err := sd.Services("flynn-lorne-attach." + hostid)
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.Dial("tcp", services.OnlineAddrs()[0])
	if err != nil {
		log.Fatal(err)
	}
	err = gob.NewEncoder(conn).Encode(&lorne.AttachReq{
		JobID: jobid,
		Flags: lorne.AttachFlagStdout | lorne.AttachFlagStderr | lorne.AttachFlagStdin | lorne.AttachFlagStream,
	})
	if err != nil {
		log.Fatal(err)
	}
	attachState := make([]byte, 1)
	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}
	switch attachState[0] {
	case lorne.AttachError:
		log.Fatal("attach error")
	}

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}

	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}

	go func() {
		io.Copy(conn, os.Stdin)
		conn.(*net.TCPConn).CloseWrite()
	}()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text()[8:])
	}
	conn.Close()
}
<commit_msg>don't error<commit_after>package main

import (
	"os/exec"
	"strings"
	"encoding/gob"
	"fmt"
	"bufio"
	"io"
	"log"
	"net"
	"os"

	"github.com/flynn/go-discover/discover"
	"github.com/flynn/lorne/types"
	"github.com/titanous/go-dockerclient"
	"github.com/flynn/sampi/types"
	sc "github.com/flynn/sampi/client"
	lc "github.com/flynn/lorne/client"
)

// WARNING: assumes one host at the moment (firstHost will always be the same)

var sd *discover.Client
var sched *sc.Client
var host *lc.Client
var hostid string

func init() {
	var err error
	sd, err = discover.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	sched, err = sc.New()
	if err != nil {
		log.Fatal(err)
	}

	hostid = findHost()
	host, err = lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	root := "/var/lib/demo/apps"
	hostname := shell("curl -s icanhazip.com")

	set, _ := sd.Services("shelf")
	addrs := set.OnlineAddrs()
	if len(addrs) < 1 {
		panic("Shelf is not discoverable")
	}
	shelfHost := addrs[0]

	app := os.Args[2]
	os.MkdirAll(root+"/"+app, 0755)

	fmt.Printf("-----> Building %s on %s ...\n", app, hostname)

	scheduleAndAttach(app + ".build", docker.Config{
		Image:        "flynn/slugbuilder",
		Cmd:          []string{"http://" + shelfHost + "/" + app + ".tgz"},
		Tty:          false,
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		OpenStdin:    true,
		StdinOnce:    true,
	})

	fmt.Printf("-----> Deploying %s ...\n", app)

	jobid := app + ".web"

	stopIfExists(jobid)
	scheduleWithTcpPort(jobid, docker.Config{
		Image:        "flynn/slugrunner",
		Cmd:          []string{"start web"},
		Tty:          false,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		OpenStdin:    false,
		StdinOnce:    false,
		Env: []string{
			"SLUG_URL=http://" + shelfHost + "/" + app + ".tgz",
		},
	})

	fmt.Printf("=====> Application deployed!\n")
	//fmt.Printf("       %s\n", getUrl(jobid))
	fmt.Println("")

}

func shell(cmdline string) string {
        out, err := exec.Command("bash", "-c", cmdline).Output()
        if err != nil {
                panic(err)
        }
        return strings.Trim(string(out), " \n")
}

func stopIfExists(jobid string) {
	_, err := host.GetJob(jobid)
	if err != nil {
		return
	}
	if err := host.StopJob(jobid); err != nil {
		return
	}
}

func scheduleWithTcpPort(jobid string, config docker.Config) {

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config, TCPPorts: 1}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}
}

/*func getUrl(jobid string) string {
	if job, err := host.GetJob(jobid); err != nil {
		log.Fatal(err)
	}
}*/

func findHost() string {
	state, err := sched.State()
	if err != nil {
		log.Fatal(err)
	}

	var firstHost string
	for k := range state {
		firstHost = k
		break
	}
	if firstHost == "" {
		log.Fatal("no hosts")
	}
	return firstHost
}

func scheduleAndAttach(jobid string, config docker.Config) {

	services, err := sd.Services("flynn-lorne-attach." + hostid)
	if err != nil {
		log.Fatal(err)
	}
	conn, err := net.Dial("tcp", services.OnlineAddrs()[0])
	if err != nil {
		log.Fatal(err)
	}
	err = gob.NewEncoder(conn).Encode(&lorne.AttachReq{
		JobID: jobid,
		Flags: lorne.AttachFlagStdout | lorne.AttachFlagStderr | lorne.AttachFlagStdin | lorne.AttachFlagStream,
	})
	if err != nil {
		log.Fatal(err)
	}
	attachState := make([]byte, 1)
	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}
	switch attachState[0] {
	case lorne.AttachError:
		log.Fatal("attach error")
	}

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}

	if _, err := conn.Read(attachState); err != nil {
		log.Fatal(err)
	}

	go func() {
		io.Copy(conn, os.Stdin)
		conn.(*net.TCPConn).CloseWrite()
	}()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text()[8:])
	}
	conn.Close()
}
<|endoftext|>


<commit_before>package hclsyntax

import (
	"fmt"
	"strings"

	"github.com/hashicorp/hcl2/hcl"
)

// AsHCLBlock returns the block data expressed as a *hcl.Block.
func (b *Block) AsHCLBlock() *hcl.Block {
	if b == nil {
		return nil
	}

	lastHeaderRange := b.TypeRange
	if len(b.LabelRanges) > 0 {
		lastHeaderRange = b.LabelRanges[len(b.LabelRanges)-1]
	}

	return &hcl.Block{
		Type:   b.Type,
		Labels: b.Labels,
		Body:   b.Body,

		DefRange:    hcl.RangeBetween(b.TypeRange, lastHeaderRange),
		TypeRange:   b.TypeRange,
		LabelRanges: b.LabelRanges,
	}
}

// Body is the implementation of hcl.Body for the HCL native syntax.
type Body struct {
	Attributes Attributes
	Blocks     Blocks

	// These are used with PartialContent to produce a "remaining items"
	// body to return. They are nil on all bodies fresh out of the parser.
	hiddenAttrs  map[string]struct{}
	hiddenBlocks map[string]struct{}

	SrcRange hcl.Range
	EndRange hcl.Range // Final token of the body, for reporting missing items
}

// Assert that *Body implements hcl.Body
var assertBodyImplBody hcl.Body = &Body{}

func (b *Body) walkChildNodes(w internalWalkFunc) {
	w(b.Attributes)
	w(b.Blocks)
}

func (b *Body) Range() hcl.Range {
	return b.SrcRange
}

func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
	content, remainHCL, diags := b.PartialContent(schema)

	// No we'll see if anything actually remains, to produce errors about
	// extraneous items.
	remain := remainHCL.(*Body)

	for name, attr := range b.Attributes {
		if _, hidden := remain.hiddenAttrs[name]; !hidden {
			var suggestions []string
			for _, attrS := range schema.Attributes {
				if _, defined := content.Attributes[attrS.Name]; defined {
					continue
				}
				suggestions = append(suggestions, attrS.Name)
			}
			suggestion := nameSuggestion(name, suggestions)
			if suggestion != "" {
				suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
			} else {
				// Is there a block of the same name?
				for _, blockS := range schema.Blocks {
					if blockS.Type == name {
						suggestion = fmt.Sprintf(" Did you mean to define a block of type %q?", name)
						break
					}
				}
			}

			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Unsupported argument",
				Detail:   fmt.Sprintf("An argument named %q is not expected here.%s", name, suggestion),
				Subject:  &attr.NameRange,
			})
		}
	}

	for _, block := range b.Blocks {
		blockTy := block.Type
		if _, hidden := remain.hiddenBlocks[blockTy]; !hidden {
			var suggestions []string
			for _, blockS := range schema.Blocks {
				suggestions = append(suggestions, blockS.Type)
			}
			suggestion := nameSuggestion(blockTy, suggestions)
			if suggestion != "" {
				suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
			} else {
				// Is there an attribute of the same name?
				for _, attrS := range schema.Attributes {
					if attrS.Name == blockTy {
						suggestion = fmt.Sprintf(" Did you mean to define argument %q? If so, use the equals sign to assign it a value.", blockTy)
						break
					}
				}
			}

			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Unsupported block type",
				Detail:   fmt.Sprintf("Blocks of type %q are not expected here.%s", blockTy, suggestion),
				Subject:  &block.TypeRange,
			})
		}
	}

	return content, diags
}

func (b *Body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
	attrs := make(hcl.Attributes)
	var blocks hcl.Blocks
	var diags hcl.Diagnostics
	hiddenAttrs := make(map[string]struct{})
	hiddenBlocks := make(map[string]struct{})

	if b.hiddenAttrs != nil {
		for k, v := range b.hiddenAttrs {
			hiddenAttrs[k] = v
		}
	}
	if b.hiddenBlocks != nil {
		for k, v := range b.hiddenBlocks {
			hiddenBlocks[k] = v
		}
	}

	for _, attrS := range schema.Attributes {
		name := attrS.Name
		attr, exists := b.Attributes[name]
		_, hidden := hiddenAttrs[name]
		if hidden || !exists {
			if attrS.Required {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Missing required argument",
					Detail:   fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name),
					Subject:  b.MissingItemRange().Ptr(),
				})
			}
			continue
		}

		hiddenAttrs[name] = struct{}{}
		attrs[name] = attr.AsHCLAttribute()
	}

	blocksWanted := make(map[string]hcl.BlockHeaderSchema)
	for _, blockS := range schema.Blocks {
		blocksWanted[blockS.Type] = blockS
	}

	for _, block := range b.Blocks {
		if _, hidden := hiddenBlocks[block.Type]; hidden {
			continue
		}
		blockS, wanted := blocksWanted[block.Type]
		if !wanted {
			continue
		}

		if len(block.Labels) > len(blockS.LabelNames) {
			name := block.Type
			if len(blockS.LabelNames) == 0 {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  fmt.Sprintf("Extraneous label for %s", name),
					Detail: fmt.Sprintf(
						"No labels are expected for %s blocks.", name,
					),
					Subject: block.LabelRanges[0].Ptr(),
					Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
				})
			} else {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  fmt.Sprintf("Extraneous label for %s", name),
					Detail: fmt.Sprintf(
						"Only %d labels (%s) are expected for %s blocks.",
						len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "), name,
					),
					Subject: block.LabelRanges[len(blockS.LabelNames)].Ptr(),
					Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
				})
			}
			continue
		}

		if len(block.Labels) < len(blockS.LabelNames) {
			name := block.Type
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  fmt.Sprintf("Missing %s for %s", blockS.LabelNames[len(block.Labels)], name),
				Detail: fmt.Sprintf(
					"All %s blocks must have %d labels (%s).",
					name, len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "),
				),
				Subject: &block.OpenBraceRange,
				Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
			})
			continue
		}

		blocks = append(blocks, block.AsHCLBlock())
	}

	// We hide blocks only after we've processed all of them, since otherwise
	// we can't process more than one of the same type.
	for _, blockS := range schema.Blocks {
		hiddenBlocks[blockS.Type] = struct{}{}
	}

	remain := &Body{
		Attributes: b.Attributes,
		Blocks:     b.Blocks,

		hiddenAttrs:  hiddenAttrs,
		hiddenBlocks: hiddenBlocks,

		SrcRange: b.SrcRange,
		EndRange: b.EndRange,
	}

	return &hcl.BodyContent{
		Attributes: attrs,
		Blocks:     blocks,

		MissingItemRange: b.MissingItemRange(),
	}, remain, diags
}

func (b *Body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
	attrs := make(hcl.Attributes)
	var diags hcl.Diagnostics

	if len(b.Blocks) > 0 {
		example := b.Blocks[0]
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  fmt.Sprintf("Unexpected %s block", example.Type),
			Detail:   "Blocks are not allowed here.",
			Context:  &example.TypeRange,
		})
		// we will continue processing anyway, and return the attributes
		// we are able to find so that certain analyses can still be done
		// in the face of errors.
	}

	if b.Attributes == nil {
		return attrs, diags
	}

	for name, attr := range b.Attributes {
		if _, hidden := b.hiddenAttrs[name]; hidden {
			continue
		}
		attrs[name] = attr.AsHCLAttribute()
	}

	return attrs, diags
}

func (b *Body) MissingItemRange() hcl.Range {
	return b.EndRange
}

// Attributes is the collection of attribute definitions within a body.
type Attributes map[string]*Attribute

func (a Attributes) walkChildNodes(w internalWalkFunc) {
	for _, attr := range a {
		w(attr)
	}
}

// Range returns the range of some arbitrary point within the set of
// attributes, or an invalid range if there are no attributes.
//
// This is provided only to complete the Node interface, but has no practical
// use.
func (a Attributes) Range() hcl.Range {
	// An attributes doesn't really have a useful range to report, since
	// it's just a grouping construct. So we'll arbitrarily take the
	// range of one of the attributes, or produce an invalid range if we have
	// none. In practice, there's little reason to ask for the range of
	// an Attributes.
	for _, attr := range a {
		return attr.Range()
	}
	return hcl.Range{
		Filename: "<unknown>",
	}
}

// Attribute represents a single attribute definition within a body.
type Attribute struct {
	Name string
	Expr Expression

	SrcRange    hcl.Range
	NameRange   hcl.Range
	EqualsRange hcl.Range
}

func (a *Attribute) walkChildNodes(w internalWalkFunc) {
	w(a.Expr)
}

func (a *Attribute) Range() hcl.Range {
	return a.SrcRange
}

// AsHCLAttribute returns the block data expressed as a *hcl.Attribute.
func (a *Attribute) AsHCLAttribute() *hcl.Attribute {
	if a == nil {
		return nil
	}
	return &hcl.Attribute{
		Name: a.Name,
		Expr: a.Expr,

		Range:     a.SrcRange,
		NameRange: a.NameRange,
	}
}

// Blocks is the list of nested blocks within a body.
type Blocks []*Block

func (bs Blocks) walkChildNodes(w internalWalkFunc) {
	for _, block := range bs {
		w(block)
	}
}

// Range returns the range of some arbitrary point within the list of
// blocks, or an invalid range if there are no blocks.
//
// This is provided only to complete the Node interface, but has no practical
// use.
func (bs Blocks) Range() hcl.Range {
	if len(bs) > 0 {
		return bs[0].Range()
	}
	return hcl.Range{
		Filename: "<unknown>",
	}
}

// Block represents a nested block structure
type Block struct {
	Type   string
	Labels []string
	Body   *Body

	TypeRange       hcl.Range
	LabelRanges     []hcl.Range
	OpenBraceRange  hcl.Range
	CloseBraceRange hcl.Range
}

func (b *Block) walkChildNodes(w internalWalkFunc) {
	w(b.Body)
}

func (b *Block) Range() hcl.Range {
	return hcl.RangeBetween(b.TypeRange, b.CloseBraceRange)
}

func (b *Block) DefRange() hcl.Range {
	return hcl.RangeBetween(b.TypeRange, b.OpenBraceRange)
}
<commit_msg>hclsyntax: Fix error diagnostic for blocks inside JustAttributes<commit_after>package hclsyntax

import (
	"fmt"
	"strings"

	"github.com/hashicorp/hcl2/hcl"
)

// AsHCLBlock returns the block data expressed as a *hcl.Block.
func (b *Block) AsHCLBlock() *hcl.Block {
	if b == nil {
		return nil
	}

	lastHeaderRange := b.TypeRange
	if len(b.LabelRanges) > 0 {
		lastHeaderRange = b.LabelRanges[len(b.LabelRanges)-1]
	}

	return &hcl.Block{
		Type:   b.Type,
		Labels: b.Labels,
		Body:   b.Body,

		DefRange:    hcl.RangeBetween(b.TypeRange, lastHeaderRange),
		TypeRange:   b.TypeRange,
		LabelRanges: b.LabelRanges,
	}
}

// Body is the implementation of hcl.Body for the HCL native syntax.
type Body struct {
	Attributes Attributes
	Blocks     Blocks

	// These are used with PartialContent to produce a "remaining items"
	// body to return. They are nil on all bodies fresh out of the parser.
	hiddenAttrs  map[string]struct{}
	hiddenBlocks map[string]struct{}

	SrcRange hcl.Range
	EndRange hcl.Range // Final token of the body, for reporting missing items
}

// Assert that *Body implements hcl.Body
var assertBodyImplBody hcl.Body = &Body{}

func (b *Body) walkChildNodes(w internalWalkFunc) {
	w(b.Attributes)
	w(b.Blocks)
}

func (b *Body) Range() hcl.Range {
	return b.SrcRange
}

func (b *Body) Content(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Diagnostics) {
	content, remainHCL, diags := b.PartialContent(schema)

	// No we'll see if anything actually remains, to produce errors about
	// extraneous items.
	remain := remainHCL.(*Body)

	for name, attr := range b.Attributes {
		if _, hidden := remain.hiddenAttrs[name]; !hidden {
			var suggestions []string
			for _, attrS := range schema.Attributes {
				if _, defined := content.Attributes[attrS.Name]; defined {
					continue
				}
				suggestions = append(suggestions, attrS.Name)
			}
			suggestion := nameSuggestion(name, suggestions)
			if suggestion != "" {
				suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
			} else {
				// Is there a block of the same name?
				for _, blockS := range schema.Blocks {
					if blockS.Type == name {
						suggestion = fmt.Sprintf(" Did you mean to define a block of type %q?", name)
						break
					}
				}
			}

			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Unsupported argument",
				Detail:   fmt.Sprintf("An argument named %q is not expected here.%s", name, suggestion),
				Subject:  &attr.NameRange,
			})
		}
	}

	for _, block := range b.Blocks {
		blockTy := block.Type
		if _, hidden := remain.hiddenBlocks[blockTy]; !hidden {
			var suggestions []string
			for _, blockS := range schema.Blocks {
				suggestions = append(suggestions, blockS.Type)
			}
			suggestion := nameSuggestion(blockTy, suggestions)
			if suggestion != "" {
				suggestion = fmt.Sprintf(" Did you mean %q?", suggestion)
			} else {
				// Is there an attribute of the same name?
				for _, attrS := range schema.Attributes {
					if attrS.Name == blockTy {
						suggestion = fmt.Sprintf(" Did you mean to define argument %q? If so, use the equals sign to assign it a value.", blockTy)
						break
					}
				}
			}

			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  "Unsupported block type",
				Detail:   fmt.Sprintf("Blocks of type %q are not expected here.%s", blockTy, suggestion),
				Subject:  &block.TypeRange,
			})
		}
	}

	return content, diags
}

func (b *Body) PartialContent(schema *hcl.BodySchema) (*hcl.BodyContent, hcl.Body, hcl.Diagnostics) {
	attrs := make(hcl.Attributes)
	var blocks hcl.Blocks
	var diags hcl.Diagnostics
	hiddenAttrs := make(map[string]struct{})
	hiddenBlocks := make(map[string]struct{})

	if b.hiddenAttrs != nil {
		for k, v := range b.hiddenAttrs {
			hiddenAttrs[k] = v
		}
	}
	if b.hiddenBlocks != nil {
		for k, v := range b.hiddenBlocks {
			hiddenBlocks[k] = v
		}
	}

	for _, attrS := range schema.Attributes {
		name := attrS.Name
		attr, exists := b.Attributes[name]
		_, hidden := hiddenAttrs[name]
		if hidden || !exists {
			if attrS.Required {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  "Missing required argument",
					Detail:   fmt.Sprintf("The argument %q is required, but no definition was found.", attrS.Name),
					Subject:  b.MissingItemRange().Ptr(),
				})
			}
			continue
		}

		hiddenAttrs[name] = struct{}{}
		attrs[name] = attr.AsHCLAttribute()
	}

	blocksWanted := make(map[string]hcl.BlockHeaderSchema)
	for _, blockS := range schema.Blocks {
		blocksWanted[blockS.Type] = blockS
	}

	for _, block := range b.Blocks {
		if _, hidden := hiddenBlocks[block.Type]; hidden {
			continue
		}
		blockS, wanted := blocksWanted[block.Type]
		if !wanted {
			continue
		}

		if len(block.Labels) > len(blockS.LabelNames) {
			name := block.Type
			if len(blockS.LabelNames) == 0 {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  fmt.Sprintf("Extraneous label for %s", name),
					Detail: fmt.Sprintf(
						"No labels are expected for %s blocks.", name,
					),
					Subject: block.LabelRanges[0].Ptr(),
					Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
				})
			} else {
				diags = append(diags, &hcl.Diagnostic{
					Severity: hcl.DiagError,
					Summary:  fmt.Sprintf("Extraneous label for %s", name),
					Detail: fmt.Sprintf(
						"Only %d labels (%s) are expected for %s blocks.",
						len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "), name,
					),
					Subject: block.LabelRanges[len(blockS.LabelNames)].Ptr(),
					Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
				})
			}
			continue
		}

		if len(block.Labels) < len(blockS.LabelNames) {
			name := block.Type
			diags = append(diags, &hcl.Diagnostic{
				Severity: hcl.DiagError,
				Summary:  fmt.Sprintf("Missing %s for %s", blockS.LabelNames[len(block.Labels)], name),
				Detail: fmt.Sprintf(
					"All %s blocks must have %d labels (%s).",
					name, len(blockS.LabelNames), strings.Join(blockS.LabelNames, ", "),
				),
				Subject: &block.OpenBraceRange,
				Context: hcl.RangeBetween(block.TypeRange, block.OpenBraceRange).Ptr(),
			})
			continue
		}

		blocks = append(blocks, block.AsHCLBlock())
	}

	// We hide blocks only after we've processed all of them, since otherwise
	// we can't process more than one of the same type.
	for _, blockS := range schema.Blocks {
		hiddenBlocks[blockS.Type] = struct{}{}
	}

	remain := &Body{
		Attributes: b.Attributes,
		Blocks:     b.Blocks,

		hiddenAttrs:  hiddenAttrs,
		hiddenBlocks: hiddenBlocks,

		SrcRange: b.SrcRange,
		EndRange: b.EndRange,
	}

	return &hcl.BodyContent{
		Attributes: attrs,
		Blocks:     blocks,

		MissingItemRange: b.MissingItemRange(),
	}, remain, diags
}

func (b *Body) JustAttributes() (hcl.Attributes, hcl.Diagnostics) {
	attrs := make(hcl.Attributes)
	var diags hcl.Diagnostics

	if len(b.Blocks) > 0 {
		example := b.Blocks[0]
		diags = append(diags, &hcl.Diagnostic{
			Severity: hcl.DiagError,
			Summary:  fmt.Sprintf("Unexpected %q block", example.Type),
			Detail:   "Blocks are not allowed here.",
			Subject:  &example.TypeRange,
		})
		// we will continue processing anyway, and return the attributes
		// we are able to find so that certain analyses can still be done
		// in the face of errors.
	}

	if b.Attributes == nil {
		return attrs, diags
	}

	for name, attr := range b.Attributes {
		if _, hidden := b.hiddenAttrs[name]; hidden {
			continue
		}
		attrs[name] = attr.AsHCLAttribute()
	}

	return attrs, diags
}

func (b *Body) MissingItemRange() hcl.Range {
	return b.EndRange
}

// Attributes is the collection of attribute definitions within a body.
type Attributes map[string]*Attribute

func (a Attributes) walkChildNodes(w internalWalkFunc) {
	for _, attr := range a {
		w(attr)
	}
}

// Range returns the range of some arbitrary point within the set of
// attributes, or an invalid range if there are no attributes.
//
// This is provided only to complete the Node interface, but has no practical
// use.
func (a Attributes) Range() hcl.Range {
	// An attributes doesn't really have a useful range to report, since
	// it's just a grouping construct. So we'll arbitrarily take the
	// range of one of the attributes, or produce an invalid range if we have
	// none. In practice, there's little reason to ask for the range of
	// an Attributes.
	for _, attr := range a {
		return attr.Range()
	}
	return hcl.Range{
		Filename: "<unknown>",
	}
}

// Attribute represents a single attribute definition within a body.
type Attribute struct {
	Name string
	Expr Expression

	SrcRange    hcl.Range
	NameRange   hcl.Range
	EqualsRange hcl.Range
}

func (a *Attribute) walkChildNodes(w internalWalkFunc) {
	w(a.Expr)
}

func (a *Attribute) Range() hcl.Range {
	return a.SrcRange
}

// AsHCLAttribute returns the block data expressed as a *hcl.Attribute.
func (a *Attribute) AsHCLAttribute() *hcl.Attribute {
	if a == nil {
		return nil
	}
	return &hcl.Attribute{
		Name: a.Name,
		Expr: a.Expr,

		Range:     a.SrcRange,
		NameRange: a.NameRange,
	}
}

// Blocks is the list of nested blocks within a body.
type Blocks []*Block

func (bs Blocks) walkChildNodes(w internalWalkFunc) {
	for _, block := range bs {
		w(block)
	}
}

// Range returns the range of some arbitrary point within the list of
// blocks, or an invalid range if there are no blocks.
//
// This is provided only to complete the Node interface, but has no practical
// use.
func (bs Blocks) Range() hcl.Range {
	if len(bs) > 0 {
		return bs[0].Range()
	}
	return hcl.Range{
		Filename: "<unknown>",
	}
}

// Block represents a nested block structure
type Block struct {
	Type   string
	Labels []string
	Body   *Body

	TypeRange       hcl.Range
	LabelRanges     []hcl.Range
	OpenBraceRange  hcl.Range
	CloseBraceRange hcl.Range
}

func (b *Block) walkChildNodes(w internalWalkFunc) {
	w(b.Body)
}

func (b *Block) Range() hcl.Range {
	return hcl.RangeBetween(b.TypeRange, b.CloseBraceRange)
}

func (b *Block) DefRange() hcl.Range {
	return hcl.RangeBetween(b.TypeRange, b.OpenBraceRange)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/flynn/go-discoverd"
	"github.com/flynn/go-dockerclient"
	lc "github.com/flynn/lorne/client"
	"github.com/flynn/lorne/types"
	sc "github.com/flynn/sampi/client"
	"github.com/flynn/sampi/types"
)

// WARNING: assumes one host at the moment

var sd *discoverd.Client
var sched *sc.Client
var host *lc.Client
var hostid string

func init() {
	var err error
	sd, err = discoverd.NewClient()
	if err != nil {
		log.Fatal(err)
	}

	sched, err = sc.New()
	if err != nil {
		log.Fatal(err)
	}

	hostid = findHost()
	host, err = lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	root := "/var/lib/demo/apps"

	services, _ := sd.Services("shelf")
	if len(services) < 1 {
		panic("Shelf is not discoverable")
	}
	shelfHost := services[0].Addr

	app := os.Args[2]
	os.MkdirAll(root+"/"+app, 0755)

	fmt.Printf("-----> Building %s...\n", app)

	scheduleAndAttach(sc.RandomJobID(app+"-build."), docker.Config{
		Image:        "flynn/slugbuilder",
		Cmd:          []string{"http://" + shelfHost + "/" + app + ".tgz"},
		Tty:          false,
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		OpenStdin:    true,
		StdinOnce:    true,
	})

	fmt.Printf("-----> Deploying %s ...\n", app)

	jobid := sc.RandomJobID(app + "-web.")

	stopIfExists(jobid)
	scheduleWithTcpPort(jobid, docker.Config{
		Image:        "flynn/slugrunner",
		Cmd:          []string{"start", "web"},
		Tty:          false,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		OpenStdin:    false,
		StdinOnce:    false,
		Env: []string{
			"SLUG_URL=http://" + shelfHost + "/" + app + ".tgz",
			"SD_NAME=" + app,
		},
	})

	time.Sleep(1 * time.Second)
	fmt.Printf("=====> Application deployed:\n")
	fmt.Printf("       http://10.0.2.15:%s\n", getPort(jobid))
	fmt.Println("")

}

func shell(cmdline string) string {
	out, err := exec.Command("bash", "-c", cmdline).Output()
	if err != nil {
		panic(err)
	}
	return strings.Trim(string(out), " \n")
}

func stopIfExists(jobid string) {
	_, err := host.GetJob(jobid)
	if err != nil {
		return
	}
	if err := host.StopJob(jobid); err != nil {
		return
	}
}

func scheduleWithTcpPort(jobid string, config docker.Config) {
	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config, TCPPorts: 1}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}
}

func getPort(jobid string) string {
	job, err := host.GetJob(jobid)
	if err != nil {
		log.Fatal(err)
	}
	for portspec := range job.Job.Config.ExposedPorts {
		return strings.Split(portspec, "/")[0]
	}
	return ""
}

func findHost() string {
	state, err := sched.State()
	if err != nil {
		log.Fatal(err)
	}

	var firstHost string
	for k := range state {
		firstHost = k
		break
	}
	if firstHost == "" {
		log.Fatal("no hosts")
	}
	return firstHost
}

func scheduleAndAttach(jobid string, config docker.Config) {
	client, err := lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
	conn, attachWait, err := client.Attach(&lorne.AttachReq{
		JobID: jobid,
		Flags: lorne.AttachFlagStdout | lorne.AttachFlagStderr | lorne.AttachFlagStdin | lorne.AttachFlagStream,
	}, true)
	if err != nil {
		log.Fatal(err)
	}

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}

	if err := attachWait(); err != nil {
		log.Fatal(err)
	}

	go func() {
		io.Copy(conn, os.Stdin)
		conn.CloseWrite()
	}()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text()[8:])
	}
	conn.Close()
}
<commit_msg>receiver: Update discoverd usage<commit_after>package main

import (
	"bufio"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/flynn/go-discoverd"
	"github.com/flynn/go-dockerclient"
	lc "github.com/flynn/lorne/client"
	"github.com/flynn/lorne/types"
	sc "github.com/flynn/sampi/client"
	"github.com/flynn/sampi/types"
)

// WARNING: assumes one host at the moment

var sched *sc.Client
var host *lc.Client
var hostid string

func init() {
	var err error
	sched, err = sc.New()
	if err != nil {
		log.Fatal(err)
	}

	hostid = findHost()
	host, err = lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
}

func main() {
	root := "/var/lib/demo/apps"

	services, _ := discoverd.Services("shelf", discoverd.DefaultTimeout)
	if len(services) < 1 {
		panic("Shelf is not discoverable")
	}
	shelfHost := services[0].Addr

	app := os.Args[2]
	os.MkdirAll(root+"/"+app, 0755)

	fmt.Printf("-----> Building %s...\n", app)

	scheduleAndAttach(sc.RandomJobID(app+"-build."), docker.Config{
		Image:        "flynn/slugbuilder",
		Cmd:          []string{"http://" + shelfHost + "/" + app + ".tgz"},
		Tty:          false,
		AttachStdin:  true,
		AttachStdout: true,
		AttachStderr: true,
		OpenStdin:    true,
		StdinOnce:    true,
	})

	fmt.Printf("-----> Deploying %s ...\n", app)

	jobid := sc.RandomJobID(app + "-web.")

	stopIfExists(jobid)
	scheduleWithTcpPort(jobid, docker.Config{
		Image:        "flynn/slugrunner",
		Cmd:          []string{"start", "web"},
		Tty:          false,
		AttachStdin:  false,
		AttachStdout: false,
		AttachStderr: false,
		OpenStdin:    false,
		StdinOnce:    false,
		Env: []string{
			"SLUG_URL=http://" + shelfHost + "/" + app + ".tgz",
			"SD_NAME=" + app,
		},
	})

	time.Sleep(1 * time.Second)
	fmt.Printf("=====> Application deployed:\n")
	fmt.Printf("       http://10.0.2.15:%s\n", getPort(jobid))
	fmt.Println("")

}

func shell(cmdline string) string {
	out, err := exec.Command("bash", "-c", cmdline).Output()
	if err != nil {
		panic(err)
	}
	return strings.Trim(string(out), " \n")
}

func stopIfExists(jobid string) {
	_, err := host.GetJob(jobid)
	if err != nil {
		return
	}
	if err := host.StopJob(jobid); err != nil {
		return
	}
}

func scheduleWithTcpPort(jobid string, config docker.Config) {
	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config, TCPPorts: 1}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}
}

func getPort(jobid string) string {
	job, err := host.GetJob(jobid)
	if err != nil {
		log.Fatal(err)
	}
	for portspec := range job.Job.Config.ExposedPorts {
		return strings.Split(portspec, "/")[0]
	}
	return ""
}

func findHost() string {
	state, err := sched.State()
	if err != nil {
		log.Fatal(err)
	}

	var firstHost string
	for k := range state {
		firstHost = k
		break
	}
	if firstHost == "" {
		log.Fatal("no hosts")
	}
	return firstHost
}

func scheduleAndAttach(jobid string, config docker.Config) {
	client, err := lc.New(hostid)
	if err != nil {
		log.Fatal(err)
	}
	conn, attachWait, err := client.Attach(&lorne.AttachReq{
		JobID: jobid,
		Flags: lorne.AttachFlagStdout | lorne.AttachFlagStderr | lorne.AttachFlagStdin | lorne.AttachFlagStream,
	}, true)
	if err != nil {
		log.Fatal(err)
	}

	schedReq := &sampi.ScheduleReq{
		Incremental: true,
		HostJobs:    map[string][]*sampi.Job{hostid: {{ID: jobid, Config: &config}}},
	}
	if _, err := sched.Schedule(schedReq); err != nil {
		log.Fatal(err)
	}

	if err := attachWait(); err != nil {
		log.Fatal(err)
	}

	go func() {
		io.Copy(conn, os.Stdin)
		conn.CloseWrite()
	}()
	scanner := bufio.NewScanner(conn)
	for scanner.Scan() {
		fmt.Fprintln(os.Stdout, scanner.Text()[8:])
	}
	conn.Close()
}
<|endoftext|>