<commit_before>package influxdb

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal"
	"github.com/influxdata/telegraf/plugins/serializers/influx"
)

const (
	defaultRequestTimeout          = time.Second * 5
	defaultDatabase                = "telegraf"
	errStringDatabaseNotFound      = "database not found"
	errStringHintedHandoffNotEmpty = "hinted handoff queue not empty"
	errStringPartialWrite          = "partial write"
	errStringPointsBeyondRP        = "points beyond retention policy"
	errStringUnableToParse         = "unable to parse"
)

var (
	// Escape an identifier in InfluxQL.
	escapeIdentifier = strings.NewReplacer(
		"\n", `\n`,
		`\`, `\\`,
		`"`, `\"`,
	)
)

// APIError is a general error reported by the InfluxDB server
type APIError struct {
	StatusCode  int
	Title       string
	Description string
}

func (e APIError) Error() string {
	if e.Description != "" {
		return fmt.Sprintf("%s: %s", e.Title, e.Description)
	}
	return e.Title
}

type DatabaseNotFoundError struct {
	APIError
	Database string
}

// QueryResponse is the response body from the /query endpoint
type QueryResponse struct {
	Results []QueryResult `json:"results"`
}

type QueryResult struct {
	Err string `json:"error,omitempty"`
}

func (r QueryResponse) Error() string {
	if len(r.Results) > 0 {
		return r.Results[0].Err
	}
	return ""
}

// WriteResponse is the response body from the /write endpoint
type WriteResponse struct {
	Err string `json:"error,omitempty"`
}

func (r WriteResponse) Error() string {
	return r.Err
}

type HTTPConfig struct {
	URL                       *url.URL
	UserAgent                 string
	Timeout                   time.Duration
	Username                  string
	Password                  string
	TLSConfig                 *tls.Config
	Proxy                     *url.URL
	Headers                   map[string]string
	ContentEncoding           string
	Database                  string
	DatabaseTag               string
	ExcludeDatabaseTag        bool
	RetentionPolicy           string
	RetentionPolicyTag        string
	ExcludeRetentionPolicyTag bool
	Consistency               string
	SkipDatabaseCreation      bool

	InfluxUintSupport bool `toml:"influx_uint_support"`
	Serializer        *influx.Serializer
	Log               telegraf.Logger
}

type httpClient struct {
	client *http.Client
	config HTTPConfig
	// Tracks that the 'create database` statement was executed for the
	// database.  An attempt to create the database is made each time a new
	// database is encountered in the database_tag and after a "database not
	// found" error occurs.
	createDatabaseExecuted map[string]bool

	log telegraf.Logger
}

func NewHTTPClient(config HTTPConfig) (*httpClient, error) {
	if config.URL == nil {
		return nil, ErrMissingURL
	}

	if config.Database == "" {
		config.Database = defaultDatabase
	}

	if config.Timeout == 0 {
		config.Timeout = defaultRequestTimeout
	}

	userAgent := config.UserAgent
	if userAgent == "" {
		userAgent = internal.ProductToken()
	}

	if config.Headers == nil {
		config.Headers = make(map[string]string)
	}
	config.Headers["User-Agent"] = userAgent
	for k, v := range config.Headers {
		config.Headers[k] = v
	}

	var proxy func(*http.Request) (*url.URL, error)
	if config.Proxy != nil {
		proxy = http.ProxyURL(config.Proxy)
	} else {
		proxy = http.ProxyFromEnvironment
	}

	if config.Serializer == nil {
		config.Serializer = influx.NewSerializer()
	}

	var transport *http.Transport
	switch config.URL.Scheme {
	case "http", "https":
		transport = &http.Transport{
			Proxy:           proxy,
			TLSClientConfig: config.TLSConfig,
		}
	case "unix":
		transport = &http.Transport{
			Dial: func(_, _ string) (net.Conn, error) {
				return net.DialTimeout(
					config.URL.Scheme,
					config.URL.Path,
					defaultRequestTimeout,
				)
			},
		}
	default:
		return nil, fmt.Errorf("unsupported scheme %q", config.URL.Scheme)
	}

	client := &httpClient{
		client: &http.Client{
			Timeout:   config.Timeout,
			Transport: transport,
		},
		createDatabaseExecuted: make(map[string]bool),
		config:                 config,
		log:                    config.Log,
	}
	return client, nil
}

// URL returns the origin URL that this client connects too.
func (c *httpClient) URL() string {
	return c.config.URL.String()
}

// Database returns the default database that this client connects too.
func (c *httpClient) Database() string {
	return c.config.Database
}

// CreateDatabase attempts to create a new database in the InfluxDB server.
// Note that some names are not allowed by the server, notably those with
// non-printable characters or slashes.
func (c *httpClient) CreateDatabase(ctx context.Context, database string) error {
	query := fmt.Sprintf(`CREATE DATABASE "%s"`, escapeIdentifier.Replace(database))

	req, err := c.makeQueryRequest(query)
	if err != nil {
		return err
	}

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		internal.OnClientError(c.client, err)
		return err
	}
	defer resp.Body.Close()

	queryResp := &QueryResponse{}
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(queryResp)

	if err != nil {
		if resp.StatusCode == 200 {
			return nil
		}

		return &APIError{
			StatusCode: resp.StatusCode,
			Title:      resp.Status,
		}
	}

	// Even with a 200 status code there can be an error in the response body.
	// If there is also no error string then the operation was successful.
	if resp.StatusCode == http.StatusOK && queryResp.Error() == "" {
		c.createDatabaseExecuted[database] = true
		return nil
	}

	// Don't attempt to recreate the database after a 403 Forbidden error.
	// This behavior exists only to maintain backwards compatibility.
	if resp.StatusCode == http.StatusForbidden {
		c.createDatabaseExecuted[database] = true
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: queryResp.Error(),
	}
}

type dbrp struct {
	Database        string
	RetentionPolicy string
}

// Write sends the metrics to InfluxDB
func (c *httpClient) Write(ctx context.Context, metrics []telegraf.Metric) error {
	// If these options are not used, we can skip in plugin batching and send
	// the full batch in a single request.
	if c.config.DatabaseTag == "" && c.config.RetentionPolicyTag == "" {
		return c.writeBatch(ctx, c.config.Database, c.config.RetentionPolicy, metrics)
	}

	batches := make(map[dbrp][]telegraf.Metric)
	for _, metric := range metrics {
		db, ok := metric.GetTag(c.config.DatabaseTag)
		if !ok {
			db = c.config.Database
		}

		rp, ok := metric.GetTag(c.config.RetentionPolicyTag)
		if !ok {
			rp = c.config.RetentionPolicy
		}

		dbrp := dbrp{
			Database:        db,
			RetentionPolicy: rp,
		}

		if c.config.ExcludeDatabaseTag || c.config.ExcludeRetentionPolicyTag {
			// Avoid modifying the metric in case we need to retry the request.
			metric = metric.Copy()
			metric.Accept()
			if c.config.ExcludeDatabaseTag {
				metric.RemoveTag(c.config.DatabaseTag)
			}
			if c.config.ExcludeRetentionPolicyTag {
				metric.RemoveTag(c.config.RetentionPolicyTag)
			}
		}

		batches[dbrp] = append(batches[dbrp], metric)
	}

	for dbrp, batch := range batches {
		if !c.config.SkipDatabaseCreation && !c.createDatabaseExecuted[dbrp.Database] {
			err := c.CreateDatabase(ctx, dbrp.Database)
			if err != nil {
				c.log.Warnf("When writing to [%s]: database %q creation failed: %v",
					c.config.URL, dbrp.Database, err)
			}
		}

		err := c.writeBatch(ctx, dbrp.Database, dbrp.RetentionPolicy, batch)
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *httpClient) writeBatch(ctx context.Context, db, rp string, metrics []telegraf.Metric) error {
	loc, err := makeWriteURL(c.config.URL, db, rp, c.config.Consistency)
	if err != nil {
		return err
	}

	reader, err := c.requestBodyReader(metrics)
	if err != nil {
		return err
	}
	defer reader.Close()

	req, err := c.makeWriteRequest(loc, reader)
	if err != nil {
		return err
	}

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		internal.OnClientError(c.client, err)
		return err
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNoContent {
		return nil
	}

	writeResp := &WriteResponse{}
	dec := json.NewDecoder(resp.Body)

	var desc string
	err = dec.Decode(writeResp)
	if err == nil {
		desc = writeResp.Err
	}

	if strings.Contains(desc, errStringDatabaseNotFound) {
		return &DatabaseNotFoundError{
			APIError: APIError{
				StatusCode:  resp.StatusCode,
				Title:       resp.Status,
				Description: desc,
			},
			Database: db,
		}
	}

	// This "error" is an informational message about the state of the
	// InfluxDB cluster.
	if strings.Contains(desc, errStringHintedHandoffNotEmpty) {
		return nil
	}

	// Points beyond retention policy is returned when points are immediately
	// discarded for being older than the retention policy.  Usually this not
	// a cause for concern and we don't want to retry.
	if strings.Contains(desc, errStringPointsBeyondRP) {
		c.log.Warnf("When writing to [%s]: received error %v",
			c.URL(), desc)
		return nil
	}

	// Other partial write errors, such as "field type conflict", are not
	// correctable at this point and so the point is dropped instead of
	// retrying.
	if strings.Contains(desc, errStringPartialWrite) {
		c.log.Errorf("When writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	// This error indicates a bug in either Telegraf line protocol
	// serialization, retries would not be successful.
	if strings.Contains(desc, errStringUnableToParse) {
		c.log.Errorf("When writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: desc,
	}
}

func (c *httpClient) makeQueryRequest(query string) (*http.Request, error) {
	queryURL, err := makeQueryURL(c.config.URL)
	if err != nil {
		return nil, err
	}

	params := url.Values{}
	params.Set("q", query)
	form := strings.NewReader(params.Encode())

	req, err := http.NewRequest("POST", queryURL, form)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.addHeaders(req)

	return req, nil
}

func (c *httpClient) makeWriteRequest(url string, body io.Reader) (*http.Request, error) {
	var err error

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

	req.Header.Set("Content-Type", "text/plain; charset=utf-8")
	c.addHeaders(req)

	if c.config.ContentEncoding == "gzip" {
		req.Header.Set("Content-Encoding", "gzip")
	}

	return req, nil
}

// requestBodyReader warp io.Reader from influx.NewReader to io.ReadCloser, which is usefully to fast close the write
// side of the connection in case of error
func (c *httpClient) requestBodyReader(metrics []telegraf.Metric) (io.ReadCloser, error) {
	reader := influx.NewReader(metrics, c.config.Serializer)

	if c.config.ContentEncoding == "gzip" {
		rc, err := internal.CompressWithGzip(reader)
		if err != nil {
			return nil, err
		}

		return rc, nil
	}

	return ioutil.NopCloser(reader), nil
}

func (c *httpClient) addHeaders(req *http.Request) {
	if c.config.Username != "" || c.config.Password != "" {
		req.SetBasicAuth(c.config.Username, c.config.Password)
	}

	for header, value := range c.config.Headers {
		req.Header.Set(header, value)
	}
}

func makeWriteURL(loc *url.URL, db, rp, consistency string) (string, error) {
	params := url.Values{}
	params.Set("db", db)

	if rp != "" {
		params.Set("rp", rp)
	}

	if consistency != "one" && consistency != "" {
		params.Set("consistency", consistency)
	}

	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/write"
	case "http", "https":
		u.Path = path.Join(u.Path, "write")
	default:
		return "", fmt.Errorf("unsupported scheme: %q", loc.Scheme)
	}
	u.RawQuery = params.Encode()
	return u.String(), nil
}

func makeQueryURL(loc *url.URL) (string, error) {
	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/query"
	case "http", "https":
		u.Path = path.Join(u.Path, "query")
	default:
		return "", fmt.Errorf("unsupported scheme: %q", loc.Scheme)
	}
	return u.String(), nil
}

func (c *httpClient) Close() {
	c.client.CloseIdleConnections()
}
<commit_msg>Add more verbose errors to influxdb output (#6061)<commit_after>package influxdb

import (
	"context"
	"crypto/tls"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
	"strings"
	"time"

	"github.com/influxdata/telegraf"
	"github.com/influxdata/telegraf/internal"
	"github.com/influxdata/telegraf/plugins/serializers/influx"
)

const (
	defaultRequestTimeout          = time.Second * 5
	defaultDatabase                = "telegraf"
	errStringDatabaseNotFound      = "database not found"
	errStringHintedHandoffNotEmpty = "hinted handoff queue not empty"
	errStringPartialWrite          = "partial write"
	errStringPointsBeyondRP        = "points beyond retention policy"
	errStringUnableToParse         = "unable to parse"
)

var (
	// Escape an identifier in InfluxQL.
	escapeIdentifier = strings.NewReplacer(
		"\n", `\n`,
		`\`, `\\`,
		`"`, `\"`,
	)
)

// APIError is a general error reported by the InfluxDB server
type APIError struct {
	StatusCode  int
	Title       string
	Description string
}

func (e APIError) Error() string {
	if e.Description != "" {
		return fmt.Sprintf("%s: %s", e.Title, e.Description)
	}
	return e.Title
}

type DatabaseNotFoundError struct {
	APIError
	Database string
}

// QueryResponse is the response body from the /query endpoint
type QueryResponse struct {
	Results []QueryResult `json:"results"`
}

type QueryResult struct {
	Err string `json:"error,omitempty"`
}

func (r QueryResponse) Error() string {
	if len(r.Results) > 0 {
		return r.Results[0].Err
	}
	return ""
}

// WriteResponse is the response body from the /write endpoint
type WriteResponse struct {
	Err string `json:"error,omitempty"`
}

func (r WriteResponse) Error() string {
	return r.Err
}

type HTTPConfig struct {
	URL                       *url.URL
	UserAgent                 string
	Timeout                   time.Duration
	Username                  string
	Password                  string
	TLSConfig                 *tls.Config
	Proxy                     *url.URL
	Headers                   map[string]string
	ContentEncoding           string
	Database                  string
	DatabaseTag               string
	ExcludeDatabaseTag        bool
	RetentionPolicy           string
	RetentionPolicyTag        string
	ExcludeRetentionPolicyTag bool
	Consistency               string
	SkipDatabaseCreation      bool

	InfluxUintSupport bool `toml:"influx_uint_support"`
	Serializer        *influx.Serializer
	Log               telegraf.Logger
}

type httpClient struct {
	client *http.Client
	config HTTPConfig
	// Tracks that the 'create database` statement was executed for the
	// database.  An attempt to create the database is made each time a new
	// database is encountered in the database_tag and after a "database not
	// found" error occurs.
	createDatabaseExecuted map[string]bool

	log telegraf.Logger
}

func NewHTTPClient(config HTTPConfig) (*httpClient, error) {
	if config.URL == nil {
		return nil, ErrMissingURL
	}

	if config.Database == "" {
		config.Database = defaultDatabase
	}

	if config.Timeout == 0 {
		config.Timeout = defaultRequestTimeout
	}

	userAgent := config.UserAgent
	if userAgent == "" {
		userAgent = internal.ProductToken()
	}

	if config.Headers == nil {
		config.Headers = make(map[string]string)
	}
	config.Headers["User-Agent"] = userAgent
	for k, v := range config.Headers {
		config.Headers[k] = v
	}

	var proxy func(*http.Request) (*url.URL, error)
	if config.Proxy != nil {
		proxy = http.ProxyURL(config.Proxy)
	} else {
		proxy = http.ProxyFromEnvironment
	}

	if config.Serializer == nil {
		config.Serializer = influx.NewSerializer()
	}

	var transport *http.Transport
	switch config.URL.Scheme {
	case "http", "https":
		transport = &http.Transport{
			Proxy:           proxy,
			TLSClientConfig: config.TLSConfig,
		}
	case "unix":
		transport = &http.Transport{
			Dial: func(_, _ string) (net.Conn, error) {
				return net.DialTimeout(
					config.URL.Scheme,
					config.URL.Path,
					defaultRequestTimeout,
				)
			},
		}
	default:
		return nil, fmt.Errorf("unsupported scheme %q", config.URL.Scheme)
	}

	client := &httpClient{
		client: &http.Client{
			Timeout:   config.Timeout,
			Transport: transport,
		},
		createDatabaseExecuted: make(map[string]bool),
		config:                 config,
		log:                    config.Log,
	}
	return client, nil
}

// URL returns the origin URL that this client connects too.
func (c *httpClient) URL() string {
	return c.config.URL.String()
}

// Database returns the default database that this client connects too.
func (c *httpClient) Database() string {
	return c.config.Database
}

// CreateDatabase attempts to create a new database in the InfluxDB server.
// Note that some names are not allowed by the server, notably those with
// non-printable characters or slashes.
func (c *httpClient) CreateDatabase(ctx context.Context, database string) error {
	query := fmt.Sprintf(`CREATE DATABASE "%s"`, escapeIdentifier.Replace(database))

	req, err := c.makeQueryRequest(query)
	if err != nil {
		return err
	}

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		internal.OnClientError(c.client, err)
		return err
	}
	defer resp.Body.Close()

	queryResp := &QueryResponse{}
	dec := json.NewDecoder(resp.Body)
	err = dec.Decode(queryResp)

	if err != nil {
		if resp.StatusCode == 200 {
			return nil
		}

		return &APIError{
			StatusCode: resp.StatusCode,
			Title:      resp.Status,
		}
	}

	// Even with a 200 status code there can be an error in the response body.
	// If there is also no error string then the operation was successful.
	if resp.StatusCode == http.StatusOK && queryResp.Error() == "" {
		c.createDatabaseExecuted[database] = true
		return nil
	}

	// Don't attempt to recreate the database after a 403 Forbidden error.
	// This behavior exists only to maintain backwards compatibility.
	if resp.StatusCode == http.StatusForbidden {
		c.createDatabaseExecuted[database] = true
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: queryResp.Error(),
	}
}

type dbrp struct {
	Database        string
	RetentionPolicy string
}

// Write sends the metrics to InfluxDB
func (c *httpClient) Write(ctx context.Context, metrics []telegraf.Metric) error {
	// If these options are not used, we can skip in plugin batching and send
	// the full batch in a single request.
	if c.config.DatabaseTag == "" && c.config.RetentionPolicyTag == "" {
		return c.writeBatch(ctx, c.config.Database, c.config.RetentionPolicy, metrics)
	}

	batches := make(map[dbrp][]telegraf.Metric)
	for _, metric := range metrics {
		db, ok := metric.GetTag(c.config.DatabaseTag)
		if !ok {
			db = c.config.Database
		}

		rp, ok := metric.GetTag(c.config.RetentionPolicyTag)
		if !ok {
			rp = c.config.RetentionPolicy
		}

		dbrp := dbrp{
			Database:        db,
			RetentionPolicy: rp,
		}

		if c.config.ExcludeDatabaseTag || c.config.ExcludeRetentionPolicyTag {
			// Avoid modifying the metric in case we need to retry the request.
			metric = metric.Copy()
			metric.Accept()
			if c.config.ExcludeDatabaseTag {
				metric.RemoveTag(c.config.DatabaseTag)
			}
			if c.config.ExcludeRetentionPolicyTag {
				metric.RemoveTag(c.config.RetentionPolicyTag)
			}
		}

		batches[dbrp] = append(batches[dbrp], metric)
	}

	for dbrp, batch := range batches {
		if !c.config.SkipDatabaseCreation && !c.createDatabaseExecuted[dbrp.Database] {
			err := c.CreateDatabase(ctx, dbrp.Database)
			if err != nil {
				c.log.Warnf("When writing to [%s]: database %q creation failed: %v",
					c.config.URL, dbrp.Database, err)
			}
		}

		err := c.writeBatch(ctx, dbrp.Database, dbrp.RetentionPolicy, batch)
		if err != nil {
			return err
		}
	}
	return nil
}

func (c *httpClient) writeBatch(ctx context.Context, db, rp string, metrics []telegraf.Metric) error {
	loc, err := makeWriteURL(c.config.URL, db, rp, c.config.Consistency)
	if err != nil {
		return fmt.Errorf("failed making write url: %s", err.Error())
	}

	reader, err := c.requestBodyReader(metrics)
	if err != nil {
		return err
	}
	defer reader.Close()

	req, err := c.makeWriteRequest(loc, reader)
	if err != nil {
		return fmt.Errorf("failed making write req: %s", err.Error())
	}

	resp, err := c.client.Do(req.WithContext(ctx))
	if err != nil {
		internal.OnClientError(c.client, err)
		return fmt.Errorf("failed doing req: %s", err.Error())
	}
	defer resp.Body.Close()

	if resp.StatusCode == http.StatusNoContent {
		return nil
	}

	writeResp := &WriteResponse{}
	dec := json.NewDecoder(resp.Body)

	var desc string
	err = dec.Decode(writeResp)
	if err == nil {
		desc = writeResp.Err
	}

	if strings.Contains(desc, errStringDatabaseNotFound) {
		return &DatabaseNotFoundError{
			APIError: APIError{
				StatusCode:  resp.StatusCode,
				Title:       resp.Status,
				Description: desc,
			},
			Database: db,
		}
	}

	// This "error" is an informational message about the state of the
	// InfluxDB cluster.
	if strings.Contains(desc, errStringHintedHandoffNotEmpty) {
		return nil
	}

	// Points beyond retention policy is returned when points are immediately
	// discarded for being older than the retention policy.  Usually this not
	// a cause for concern and we don't want to retry.
	if strings.Contains(desc, errStringPointsBeyondRP) {
		c.log.Warnf("When writing to [%s]: received error %v",
			c.URL(), desc)
		return nil
	}

	// Other partial write errors, such as "field type conflict", are not
	// correctable at this point and so the point is dropped instead of
	// retrying.
	if strings.Contains(desc, errStringPartialWrite) {
		c.log.Errorf("When writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	// This error indicates a bug in either Telegraf line protocol
	// serialization, retries would not be successful.
	if strings.Contains(desc, errStringUnableToParse) {
		c.log.Errorf("When writing to [%s]: received error %v; discarding points",
			c.URL(), desc)
		return nil
	}

	return &APIError{
		StatusCode:  resp.StatusCode,
		Title:       resp.Status,
		Description: desc,
	}
}

func (c *httpClient) makeQueryRequest(query string) (*http.Request, error) {
	queryURL, err := makeQueryURL(c.config.URL)
	if err != nil {
		return nil, err
	}

	params := url.Values{}
	params.Set("q", query)
	form := strings.NewReader(params.Encode())

	req, err := http.NewRequest("POST", queryURL, form)
	if err != nil {
		return nil, err
	}

	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	c.addHeaders(req)

	return req, nil
}

func (c *httpClient) makeWriteRequest(url string, body io.Reader) (*http.Request, error) {
	var err error

	req, err := http.NewRequest("POST", url, body)
	if err != nil {
		return nil, fmt.Errorf("failed creating new request: %s", err.Error())
	}

	req.Header.Set("Content-Type", "text/plain; charset=utf-8")
	c.addHeaders(req)

	if c.config.ContentEncoding == "gzip" {
		req.Header.Set("Content-Encoding", "gzip")
	}

	return req, nil
}

// requestBodyReader warp io.Reader from influx.NewReader to io.ReadCloser, which is usefully to fast close the write
// side of the connection in case of error
func (c *httpClient) requestBodyReader(metrics []telegraf.Metric) (io.ReadCloser, error) {
	reader := influx.NewReader(metrics, c.config.Serializer)

	if c.config.ContentEncoding == "gzip" {
		rc, err := internal.CompressWithGzip(reader)
		if err != nil {
			return nil, err
		}

		return rc, nil
	}

	return ioutil.NopCloser(reader), nil
}

func (c *httpClient) addHeaders(req *http.Request) {
	if c.config.Username != "" || c.config.Password != "" {
		req.SetBasicAuth(c.config.Username, c.config.Password)
	}

	for header, value := range c.config.Headers {
		req.Header.Set(header, value)
	}
}

func makeWriteURL(loc *url.URL, db, rp, consistency string) (string, error) {
	params := url.Values{}
	params.Set("db", db)

	if rp != "" {
		params.Set("rp", rp)
	}

	if consistency != "one" && consistency != "" {
		params.Set("consistency", consistency)
	}

	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/write"
	case "http", "https":
		u.Path = path.Join(u.Path, "write")
	default:
		return "", fmt.Errorf("unsupported scheme: %q", loc.Scheme)
	}
	u.RawQuery = params.Encode()
	return u.String(), nil
}

func makeQueryURL(loc *url.URL) (string, error) {
	u := *loc
	switch u.Scheme {
	case "unix":
		u.Scheme = "http"
		u.Host = "127.0.0.1"
		u.Path = "/query"
	case "http", "https":
		u.Path = path.Join(u.Path, "query")
	default:
		return "", fmt.Errorf("unsupported scheme: %q", loc.Scheme)
	}
	return u.String(), nil
}

func (c *httpClient) Close() {
	c.client.CloseIdleConnections()
}
<|endoftext|>


<commit_before>// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2015 The Go Authors.  All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Package grpc outputs gRPC service descriptions in Go code.
// It runs as a plugin for the Go protocol buffer compiler plugin.
// It is linked in to protoc-gen-go.
package grpc

import (
	"fmt"
	"path"
	"strconv"
	"strings"

	pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/johanbrandhorst/protobuf/protoc-gen-gopherjs/generator"
)

// generatedCodeVersion indicates a version of the generated code.
// It is incremented whenever an incompatibility between the generated code and
// the grpc package is introduced; the generated code references
// a constant, grpc.SupportPackageIsVersionN (where N is generatedCodeVersion).
const generatedCodeVersion = 1

// Paths for packages used by code generated in this file,
// relative to the import_prefix of the generator.Generator.
const (
	contextPkgPath = "context"
	grpcPkgPath    = "github.com/johanbrandhorst/protobuf/grpcweb"
)

func init() {
	generator.RegisterPlugin(new(grpc))
}

// grpc is an implementation of the Go protocol buffer compiler's
// plugin architecture.  It generates bindings for gRPC support.
type grpc struct {
	gen *generator.Generator
}

// Name returns the name of this plugin, "grpc".
func (g *grpc) Name() string {
	return "grpc"
}

// The names for packages imported in the generated code.
// They may vary from the final path component of the import path
// if the name is used by other packages.
var (
	contextPkg string
	grpcPkg    string
)

// Init initializes the plugin.
func (g *grpc) Init(gen *generator.Generator) {
	g.gen = gen
	contextPkg = generator.RegisterUniquePackageName("context", nil)
	grpcPkg = generator.RegisterUniquePackageName("grpcweb", nil)
}

// Given a type name defined in a .proto, return its object.
// Also record that we're using it, to guarantee the associated import.
func (g *grpc) objectNamed(name string) generator.Object {
	g.gen.RecordTypeUse(name)
	return g.gen.ObjectNamed(name)
}

// Given a type name defined in a .proto, return its name as we will print it.
func (g *grpc) typeName(str string) string {
	return g.gen.TypeName(g.objectNamed(str))
}

// P forwards to g.gen.P.
func (g *grpc) P(args ...interface{}) { g.gen.P(args...) }

// Generate generates code for the services in the given file.
func (g *grpc) Generate(file *generator.FileDescriptor) {
	if len(file.FileDescriptorProto.Service) == 0 {
		return
	}

	g.P("// Reference imports to suppress errors if they are not otherwise used.")
	g.P("var _ ", contextPkg, ".Context")
	g.P("var _ ", grpcPkg, ".Client")
	g.P()

	// Assert version compatibility.
	g.P("// This is a compile-time assertion to ensure that this generated file")
	g.P("// is compatible with the grpcweb package it is being compiled against.")
	g.P("const _ = ", grpcPkg, ".GrpcWebPackageIsVersion", generatedCodeVersion)
	g.P()

	for i, service := range file.FileDescriptorProto.Service {
		g.generateService(file, service, i)
	}
}

// GenerateImports generates the import declaration for this file.
func (g *grpc) GenerateImports(file *generator.FileDescriptor) {
	if len(file.FileDescriptorProto.Service) == 0 {
		return
	}
	g.P("import (")
	g.P(contextPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, contextPkgPath)))
	g.P()
	g.P(grpcPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, grpcPkgPath)))
	g.P(")")
	g.P()
}

// reservedClientName records whether a client name is reserved on the client side.
var reservedClientName = map[string]bool{
// TODO: do we need any in gRPC?
}

func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }

// generateService generates all the code for the named service.
func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
	path := fmt.Sprintf("6,%d", index) // 6 means service.

	origServName := service.GetName()
	fullServName := origServName
	if pkg := file.GetPackage(); pkg != "" {
		fullServName = pkg + "." + fullServName
	}
	servName := generator.CamelCase(origServName)

	g.P()
	g.P("// Client API for ", servName, " service")
	g.P()

	// Client interface.
	g.gen.PrintComments(path)
	g.P("type ", servName, "Client interface {")
	for i, method := range service.Method {
		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
		g.P(g.generateClientSignature(servName, method))
	}
	g.P("}")
	g.P()

	// Client structure.
	g.P("type ", unexport(servName), "Client struct {")
	g.P("client *", grpcPkg, ".Client")
	g.P("}")
	g.P()

	// NewClient factory.
	g.P("// New", servName, "Client creates a new gRPC-Web client.")
	g.P("func New", servName, "Client (hostname string, opts ...grpcweb.DialOption) ", servName, "Client {")
	g.P("return &", unexport(servName), "Client{")
	g.P("client: ", grpcPkg, `.NewClient(hostname, "`, fullServName, `", opts...),`)
	g.P("}")
	g.P("}")
	g.P()

	var methodIndex, streamIndex int
	serviceDescVar := "_" + servName + "_serviceDesc"
	// Client method implementations.
	for _, method := range service.Method {
		var descExpr string
		if method.GetClientStreaming() {
			g.gen.Fail("Client streaming is not supported by gRPC-Web yet")
		}
		if !method.GetServerStreaming() && !method.GetClientStreaming() {
			// Unary RPC method
			descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex)
			methodIndex++
		} else {
			// Streaming RPC method
			descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex)
			streamIndex++
		}
		g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr)
	}
}

// generateClientSignature returns the client-side signature for a method.
func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
	origMethName := method.GetName()
	methName := generator.CamelCase(origMethName)
	if reservedClientName[methName] {
		methName += "_"
	}
	reqArg := ", in *" + g.typeName(method.GetInputType())
	if method.GetClientStreaming() {
		reqArg = ""
	}
	respName := "*" + g.typeName(method.GetOutputType())
	if method.GetServerStreaming() || method.GetClientStreaming() {
		respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
	}
	return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName)
}

func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
	methName := generator.CamelCase(method.GetName())
	outType := g.typeName(method.GetOutputType())
	streamType := unexport(servName) + methName + "Client"

	g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{")
	switch {
	case !method.GetServerStreaming() && !method.GetClientStreaming():
		g.P("req, err := in.Serialize()")
		g.P("if err != nil { return nil, err }")
		g.P()
		g.P(`resp, err := c.client.RPCCall(ctx, "`, method.GetName(), `", req, opts...)`)
		g.P("if err != nil { return nil, err }")
		g.P()
		g.P("return new(", outType, ").Deserialize(resp)")
		g.P("}")
		g.P()
		return
	case method.GetServerStreaming():
		g.P("req, err := in.Serialize()")
		g.P("if err != nil { return nil, err }")
		g.P()
		g.P(`srv, err := c.client.Stream(ctx, "`, method.GetName(), `", req, opts...)`)
		g.P("if err != nil { return nil, err }")
		g.P()
		g.P("return &", streamType, "{")
		g.P("stream: srv,")
		g.P("}, nil")
		g.P("}")
		g.P()
	case method.GetClientStreaming():
		g.gen.Fail("Client streaming is not yet supported by gRPC Web")
	}

	genRecv := method.GetServerStreaming()

	// Stream auxiliary types and methods.
	g.P("type ", servName, "_", methName, "Client interface {")
	if genRecv {
		g.P("Recv() (*", outType, ", error)")
	}
	g.P("}")
	g.P()

	g.P("type ", streamType, " struct {")
	g.P("stream *", grpcPkg, ".StreamClient")
	g.P("}")
	g.P()

	if genRecv {
		g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {")
		g.P("resp, err := x.stream.Recv()")
		g.P("if err != nil { return nil, err }")
		g.P()
		g.P("return new(", outType, ").Deserialize(resp)")
		g.P("}")
		g.P()
	}
}
<commit_msg>Fix comment, indentation<commit_after>// Go support for Protocol Buffers - Google's data interchange format
//
// Copyright 2015 The Go Authors.  All rights reserved.
// https://github.com/golang/protobuf
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//     * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

// Package grpc outputs gRPC service descriptions in Go code.
// It runs as a plugin for the Go protocol buffer compiler plugin.
// It is linked in to protoc-gen-go.
package grpc

import (
	"fmt"
	"path"
	"strconv"
	"strings"

	pb "github.com/golang/protobuf/protoc-gen-go/descriptor"
	"github.com/johanbrandhorst/protobuf/protoc-gen-gopherjs/generator"
)

// generatedCodeVersion indicates a version of the generated code.
// It is incremented whenever an incompatibility between the generated code and
// the grpcweb package is introduced; the generated code references
// a constant, grpcweb.GrpcWebPackageIsVersionN (where N is generatedCodeVersion).
const generatedCodeVersion = 1

// Paths for packages used by code generated in this file,
// relative to the import_prefix of the generator.Generator.
const (
	contextPkgPath = "context"
	grpcPkgPath    = "github.com/johanbrandhorst/protobuf/grpcweb"
)

func init() {
	generator.RegisterPlugin(new(grpc))
}

// grpc is an implementation of the Go protocol buffer compiler's
// plugin architecture.  It generates bindings for gRPC support.
type grpc struct {
	gen *generator.Generator
}

// Name returns the name of this plugin, "grpc".
func (g *grpc) Name() string {
	return "grpc"
}

// The names for packages imported in the generated code.
// They may vary from the final path component of the import path
// if the name is used by other packages.
var (
	contextPkg string
	grpcPkg    string
)

// Init initializes the plugin.
func (g *grpc) Init(gen *generator.Generator) {
	g.gen = gen
	contextPkg = generator.RegisterUniquePackageName("context", nil)
	grpcPkg = generator.RegisterUniquePackageName("grpcweb", nil)
}

// Given a type name defined in a .proto, return its object.
// Also record that we're using it, to guarantee the associated import.
func (g *grpc) objectNamed(name string) generator.Object {
	g.gen.RecordTypeUse(name)
	return g.gen.ObjectNamed(name)
}

// Given a type name defined in a .proto, return its name as we will print it.
func (g *grpc) typeName(str string) string {
	return g.gen.TypeName(g.objectNamed(str))
}

// P forwards to g.gen.P.
func (g *grpc) P(args ...interface{}) { g.gen.P(args...) }

// In forwards to g.gen.In.
func (g *grpc) In() { g.gen.In() }

// Out forwards to g.gen.Out.
func (g *grpc) Out() { g.gen.Out() }

// Generate generates code for the services in the given file.
func (g *grpc) Generate(file *generator.FileDescriptor) {
	if len(file.FileDescriptorProto.Service) == 0 {
		return
	}

	g.P("// Reference imports to suppress errors if they are not otherwise used.")
	g.P("var _ ", contextPkg, ".Context")
	g.P("var _ ", grpcPkg, ".Client")
	g.P()

	// Assert version compatibility.
	g.P("// This is a compile-time assertion to ensure that this generated file")
	g.P("// is compatible with the grpcweb package it is being compiled against.")
	g.P("const _ = ", grpcPkg, ".GrpcWebPackageIsVersion", generatedCodeVersion)
	g.P()

	for i, service := range file.FileDescriptorProto.Service {
		g.generateService(file, service, i)
	}
}

// GenerateImports generates the import declaration for this file.
func (g *grpc) GenerateImports(file *generator.FileDescriptor) {
	if len(file.FileDescriptorProto.Service) == 0 {
		return
	}
	g.P("import (")
	g.P(contextPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, contextPkgPath)))
	g.In()
	g.P(grpcPkg, " ", strconv.Quote(path.Join(g.gen.ImportPrefix, grpcPkgPath)))
	g.Out()
	g.P(")")
	g.P()
}

// reservedClientName records whether a client name is reserved on the client side.
var reservedClientName = map[string]bool{
// TODO: do we need any in gRPC?
}

func unexport(s string) string { return strings.ToLower(s[:1]) + s[1:] }

// generateService generates all the code for the named service.
func (g *grpc) generateService(file *generator.FileDescriptor, service *pb.ServiceDescriptorProto, index int) {
	path := fmt.Sprintf("6,%d", index) // 6 means service.

	origServName := service.GetName()
	fullServName := origServName
	if pkg := file.GetPackage(); pkg != "" {
		fullServName = pkg + "." + fullServName
	}
	servName := generator.CamelCase(origServName)

	g.P()
	g.P("// Client API for ", servName, " service")
	g.P()

	// Client interface.
	g.gen.PrintComments(path)
	g.P("type ", servName, "Client interface {")
	g.In()
	for i, method := range service.Method {
		g.gen.PrintComments(fmt.Sprintf("%s,2,%d", path, i)) // 2 means method in a service.
		g.P(g.generateClientSignature(servName, method))
	}
	g.Out()
	g.P("}")
	g.P()

	// Client structure.
	g.P("type ", unexport(servName), "Client struct {")
	g.In()
	g.P("client *", grpcPkg, ".Client")
	g.Out()
	g.P("}")
	g.P()

	// NewClient factory.
	g.P("// New", servName, "Client creates a new gRPC-Web client.")
	g.P("func New", servName, "Client (hostname string, opts ...grpcweb.DialOption) ", servName, "Client {")
	g.In()
	g.P("return &", unexport(servName), "Client{")
	g.In()
	g.P("client: ", grpcPkg, `.NewClient(hostname, "`, fullServName, `", opts...),`)
	g.Out()
	g.P("}")
	g.Out()
	g.P("}")
	g.P()

	var methodIndex, streamIndex int
	serviceDescVar := "_" + servName + "_serviceDesc"
	// Client method implementations.
	for _, method := range service.Method {
		var descExpr string
		if method.GetClientStreaming() {
			g.gen.Fail("Client streaming is not supported by gRPC-Web yet")
		}
		if !method.GetServerStreaming() && !method.GetClientStreaming() {
			// Unary RPC method
			descExpr = fmt.Sprintf("&%s.Methods[%d]", serviceDescVar, methodIndex)
			methodIndex++
		} else {
			// Streaming RPC method
			descExpr = fmt.Sprintf("&%s.Streams[%d]", serviceDescVar, streamIndex)
			streamIndex++
		}
		g.generateClientMethod(servName, fullServName, serviceDescVar, method, descExpr)
	}
}

// generateClientSignature returns the client-side signature for a method.
func (g *grpc) generateClientSignature(servName string, method *pb.MethodDescriptorProto) string {
	origMethName := method.GetName()
	methName := generator.CamelCase(origMethName)
	if reservedClientName[methName] {
		methName += "_"
	}
	reqArg := ", in *" + g.typeName(method.GetInputType())
	if method.GetClientStreaming() {
		reqArg = ""
	}
	respName := "*" + g.typeName(method.GetOutputType())
	if method.GetServerStreaming() || method.GetClientStreaming() {
		respName = servName + "_" + generator.CamelCase(origMethName) + "Client"
	}
	return fmt.Sprintf("%s(ctx %s.Context%s, opts ...%s.CallOption) (%s, error)", methName, contextPkg, reqArg, grpcPkg, respName)
}

func (g *grpc) generateClientMethod(servName, fullServName, serviceDescVar string, method *pb.MethodDescriptorProto, descExpr string) {
	methName := generator.CamelCase(method.GetName())
	outType := g.typeName(method.GetOutputType())
	streamType := unexport(servName) + methName + "Client"

	g.P("func (c *", unexport(servName), "Client) ", g.generateClientSignature(servName, method), "{")
	g.In()
	switch {
	case !method.GetServerStreaming() && !method.GetClientStreaming():
		g.P("req := in.Serialize()")
		g.P()
		g.P(`resp, err := c.client.RPCCall(ctx, "`, method.GetName(), `", req, opts...)`)
		g.P("if err != nil {")
		g.In()
		g.P("return nil, err")
		g.Out()
		g.P("}")
		g.P()
		g.P("return new(", outType, ").Deserialize(resp)")
		g.Out()
		g.P("}")
		g.P()
		return
	case method.GetServerStreaming():
		g.P("req := in.Serialize()")
		g.P()
		g.P(`srv, err := c.client.Stream(ctx, "`, method.GetName(), `", req, opts...)`)
		g.P("if err != nil {")
		g.In()
		g.P("return nil, err")
		g.Out()
		g.P("}")
		g.P()
		g.P("return &", streamType, "{")
		g.In()
		g.P("stream: srv,")
		g.Out()
		g.P("}, nil")
		g.Out()
		g.P("}")
		g.P()
	case method.GetClientStreaming():
		g.gen.Fail("Client streaming is not yet supported by gRPC-Web")
	}

	genRecv := method.GetServerStreaming()

	// Stream auxiliary types and methods.
	g.P("type ", servName, "_", methName, "Client interface {")
	if genRecv {
		g.In()
		g.P("Recv() (*", outType, ", error)")
		g.Out()
	}
	g.P("}")
	g.P()

	g.P("type ", streamType, " struct {")
	g.In()
	g.P("stream *", grpcPkg, ".StreamClient")
	g.Out()
	g.P("}")
	g.P()

	if genRecv {
		g.P("func (x *", streamType, ") Recv() (*", outType, ", error) {")
		g.In()
		g.P("resp, err := x.stream.Recv()")
		g.P("if err != nil {")
		g.In()
		g.P("return nil, err")
		g.Out()
		g.P("}")
		g.P()
		g.P("return new(", outType, ").Deserialize(resp)")
		g.Out()
		g.P("}")
		g.P()
	}
}
<|endoftext|>


<commit_before>package cluster

import (
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/megamsys/libgo/cmd"
	"github.com/megamsys/opennebula-go/api"
	"github.com/megamsys/opennebula-go/compute"
	"github.com/megamsys/opennebula-go/virtualmachine"
	"net"
	"net/url"
	"strconv"
	"strings"
)

// CreateVM creates a vm in the specified node.
// It returns the vm, or an error, in case of failures.
const (
	START   = "start"
	STOP    = "stop"
	RESTART = "restart"
)

var ErrConnRefused = errors.New("connection refused")

func (c *Cluster) CreateVM(opts compute.VirtualMachine, t string) (string, string, string, error) {
	var (
		addr    string
		machine string
		vmid    string
		err     error
	)
	maxTries := 5
	for ; maxTries > 0; maxTries-- {

		nodlist, err := c.Nodes()

		for _, v := range nodlist {
			if v.Metadata[api.ONEZONE] == opts.Region {
				addr = v.Address
				opts.Vnets, opts.ClusterId = c.getVnets(v, opts.Vnets)
				if v.Metadata[api.VCPU_PERCENTAGE] != "" {
					opts.Cpu = cpuThrottle(v.Metadata[api.VCPU_PERCENTAGE], opts.Cpu)
				} else {
					opts.Cpu = cpuThrottle(t, opts.Cpu)
				}
			}
		}

		if addr == "" {
			return addr, machine, vmid, fmt.Errorf("%s", cmd.Colorfy("Unavailable nodes (hint: start or beat it).\n", "red", "", ""))
		}

		if err == nil {
			machine, vmid, err = c.createVMInNode(opts, addr)
			if err == nil {
				c.handleNodeSuccess(addr)
				break
			}
			log.Errorf("  > Trying... %s", addr)
		}
		shouldIncrementFailures := false
		isCreateMachineErr := false
		baseErr := err
		if nodeErr, ok := baseErr.(OneNodeError); ok {
			isCreateMachineErr = nodeErr.cmd == "createVM"
			baseErr = nodeErr.BaseError()
		}
		if urlErr, ok := baseErr.(*url.Error); ok {
			baseErr = urlErr.Err
		}
		_, isNetErr := baseErr.(*net.OpError)
		if isNetErr || isCreateMachineErr || baseErr == ErrConnRefused {
			shouldIncrementFailures = true
		}
		c.handleNodeError(addr, err, shouldIncrementFailures)
		return addr, machine, vmid, err
	}
	if err != nil {
		return addr, machine, vmid, fmt.Errorf("CreateVM: maximum number of tries exceeded, last error: %s", err.Error())
	}
	return addr, machine, vmid, err
}

//create a vm in a node.
func (c *Cluster) createVMInNode(opts compute.VirtualMachine, nodeAddress string) (string, string, error) {
	var vmid string
	node, err := c.getNodeByAddr(nodeAddress)
	if err != nil {
		return "", "", err
	}
	opts.TemplateName = node.template
	opts.T = node.Client

	res, err := opts.Create()
	b, err := json.Marshal(res)

	if err != nil {
		return "", "", err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	if len(spstr[1]) < 5 {
   vmid = spstr[1]
	} else {
		return "", "", wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

	return opts.Name, vmid, nil
}

func (c *Cluster) GetIpPort(opts virtualmachine.Vnc, region string) (string, string, error) {

	addr, err := c.getRegion(region)
	if err != nil {
		return "", "", err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return "", "", err
	}
	//opts.TemplateName = node.template
	opts.T = node.Client

	res, err := opts.GetVm()
	vnchost := res.GetHostIp()
	vncport := res.GetPort()

	if err != nil {
		return "", "", wrapErrorWithCmd(node, err, "createVM")
	}

	return vnchost, vncport, nil
}

// DestroyVM kills a vm, returning an error in case of failure.
func (c *Cluster) DestroyVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	_, err = opts.Delete()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) VM(opts compute.VirtualMachine, action string) error {
	switch action {
	case START:
		return c.StartVM(opts)
	case STOP:
		return c.StopVM(opts)
	case RESTART:
		return c.RestartVM(opts)
	default:
		return nil
	}
}
func (c *Cluster) StartVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	_, err = opts.Resume()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) RestartVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	_, err = opts.Reboot()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) StopVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	_, err = opts.Poweroff()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) getNodeByAddr(addr string) (node, error) {
	return c.getNode(func(s Storage) (Node, error) {
		return s.RetrieveNode(addr)
	})
}

func (c *Cluster) SnapVMDisk(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	_, err = opts.DiskSnap()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func cpuThrottle(vcpu, cpu string) string {
	ThrottleFactor, _ := strconv.Atoi(vcpu)
	cpuThrottleFactor := float64(ThrottleFactor)
	ICpu, _ := strconv.Atoi(cpu)
	throttle := float64(ICpu)
	realCPU := throttle / cpuThrottleFactor
	cpu = strconv.FormatFloat(realCPU, 'f', 6, 64) //ugly, compute has the info.
	return cpu
}

func (c *Cluster) getRegion(region string) (string, error) {
	var (
		addr string
	)
	nodlist, err := c.Nodes()
	if err != nil {
		addr = ""
	}
	for _, v := range nodlist {
		if v.Metadata[api.ONEZONE] == region {
			addr = v.Address
		}
	}

	if addr == "" {
		return addr, fmt.Errorf("%s", cmd.Colorfy("Unavailable nodes (hint: start or beat it).\n", "red", "", ""))
	}

	return addr, nil
}
<commit_msg>track all opennebula action errors<commit_after>package cluster

import (
	"encoding/json"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/megamsys/libgo/cmd"
	"github.com/megamsys/opennebula-go/api"
	"github.com/megamsys/opennebula-go/compute"
	"github.com/megamsys/opennebula-go/virtualmachine"
	"net"
	"net/url"
	"strconv"
	"strings"
)

// CreateVM creates a vm in the specified node.
// It returns the vm, or an error, in case of failures.
const (
	START   = "start"
	STOP    = "stop"
	RESTART = "restart"
)

var ErrConnRefused = errors.New("connection refused")

func (c *Cluster) CreateVM(opts compute.VirtualMachine, t string) (string, string, string, error) {
	var (
		addr    string
		machine string
		vmid    string
		err     error
	)
	maxTries := 5
	for ; maxTries > 0; maxTries-- {

		nodlist, err := c.Nodes()

		for _, v := range nodlist {
			if v.Metadata[api.ONEZONE] == opts.Region {
				addr = v.Address
				opts.Vnets, opts.ClusterId = c.getVnets(v, opts.Vnets)
				if v.Metadata[api.VCPU_PERCENTAGE] != "" {
					opts.Cpu = cpuThrottle(v.Metadata[api.VCPU_PERCENTAGE], opts.Cpu)
				} else {
					opts.Cpu = cpuThrottle(t, opts.Cpu)
				}
			}
		}

		if addr == "" {
			return addr, machine, vmid, fmt.Errorf("%s", cmd.Colorfy("Unavailable nodes (hint: start or beat it).\n", "red", "", ""))
		}

		if err == nil {
			machine, vmid, err = c.createVMInNode(opts, addr)
			if err == nil {
				c.handleNodeSuccess(addr)
				break
			}
			log.Errorf("  > Trying... %s", addr)
		}
		shouldIncrementFailures := false
		isCreateMachineErr := false
		baseErr := err
		if nodeErr, ok := baseErr.(OneNodeError); ok {
			isCreateMachineErr = nodeErr.cmd == "createVM"
			baseErr = nodeErr.BaseError()
		}
		if urlErr, ok := baseErr.(*url.Error); ok {
			baseErr = urlErr.Err
		}
		_, isNetErr := baseErr.(*net.OpError)
		if isNetErr || isCreateMachineErr || baseErr == ErrConnRefused {
			shouldIncrementFailures = true
		}
		c.handleNodeError(addr, err, shouldIncrementFailures)
		return addr, machine, vmid, err
	}
	if err != nil {
		return addr, machine, vmid, fmt.Errorf("CreateVM: maximum number of tries exceeded, last error: %s", err.Error())
	}
	return addr, machine, vmid, err
}

//create a vm in a node.
func (c *Cluster) createVMInNode(opts compute.VirtualMachine, nodeAddress string) (string, string, error) {
	var vmid string
	node, err := c.getNodeByAddr(nodeAddress)
	if err != nil {
		return "", "", err
	}
	opts.TemplateName = node.template
	opts.T = node.Client

	res, err := opts.Create()
	b, err := json.Marshal(res)

	if err != nil {
		return "", "", err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return "", "", Err
	}

	if !isSuccess {
   return "", "", wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

   vmid = spstr[1]
	return opts.Name, vmid, nil
}

func (c *Cluster) GetIpPort(opts virtualmachine.Vnc, region string) (string, string, error) {

	addr, err := c.getRegion(region)
	if err != nil {
		return "", "", err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return "", "", err
	}
	//opts.TemplateName = node.template
	opts.T = node.Client

	res, err := opts.GetVm()
	b, err := json.Marshal(res)

	if err != nil {
		return "", "", err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return "", "", Err
	}

	if !isSuccess {
   	return "", "", wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}
	vnchost := res.GetHostIp()
	vncport := res.GetPort()

	if err != nil {
		return "", "", wrapErrorWithCmd(node, err, "createVM")
	}

	return vnchost, vncport, nil
}

// DestroyVM kills a vm, returning an error in case of failure.
func (c *Cluster) DestroyVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	res, err := opts.Delete()
	if err != nil {
		return wrapError(node, err)
	}

	b, err := json.Marshal(res)

	if err != nil {
		return err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return Err
	}

	if !isSuccess {
   return wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

	return nil
}

func (c *Cluster) VM(opts compute.VirtualMachine, action string) error {
	switch action {
	case START:
		return c.StartVM(opts)
	case STOP:
		return c.StopVM(opts)
	case RESTART:
		return c.RestartVM(opts)
	default:
		return nil
	}
}
func (c *Cluster) StartVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	res, err := opts.Resume()

	if err != nil {
		return wrapError(node, err)
	}

	b, err := json.Marshal(res)

	if err != nil {
		return err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return Err
	}

	if !isSuccess {
   return wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

	return nil
}

func (c *Cluster) RestartVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	res, err := opts.Reboot()
	if err != nil {
		return wrapError(node, err)
	}

	b, err := json.Marshal(res)

	if err != nil {
		return err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return Err
	}

	if !isSuccess {
   return wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

	return nil
}

func (c *Cluster) StopVM(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	res, err := opts.Poweroff()
	b, err := json.Marshal(res)

	if err != nil {
		return err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return Err
	}

	if !isSuccess {
   return wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) getNodeByAddr(addr string) (node, error) {
	return c.getNode(func(s Storage) (Node, error) {
		return s.RetrieveNode(addr)
	})
}

func (c *Cluster) SnapVMDisk(opts compute.VirtualMachine) error {

	addr, err := c.getRegion(opts.Region)
	if err != nil {
		return err
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.T = node.Client

	res, err := opts.DiskSnap()
	if err != nil {
		return wrapError(node, err)
	}

	b, err := json.Marshal(res)

	if err != nil {
		return err
	}
	str := string(b)
	spstr := strings.Split(str, ",")

	isSuccess, Err := strconv.ParseBool(spstr[0])
	if Err != nil {
		return  Err
	}

	if !isSuccess {
   return wrapErrorWithCmd(node, errors.New(spstr[1]), "createVM")
	}

	return nil
}

func cpuThrottle(vcpu, cpu string) string {
	ThrottleFactor, _ := strconv.Atoi(vcpu)
	cpuThrottleFactor := float64(ThrottleFactor)
	ICpu, _ := strconv.Atoi(cpu)
	throttle := float64(ICpu)
	realCPU := throttle / cpuThrottleFactor
	cpu = strconv.FormatFloat(realCPU, 'f', 6, 64) //ugly, compute has the info.
	return cpu
}

func (c *Cluster) getRegion(region string) (string, error) {
	var (
		addr string
	)
	nodlist, err := c.Nodes()
	if err != nil {
		addr = ""
	}
	for _, v := range nodlist {
		if v.Metadata[api.ONEZONE] == region {
			addr = v.Address
		}
	}

	if addr == "" {
		return addr, fmt.Errorf("%s", cmd.Colorfy("Unavailable nodes (hint: start or beat it).\n", "red", "", ""))
	}

	return addr, nil
}
<|endoftext|>


<commit_before>package cluster

import (
	"errors"
	"fmt"
	"net"
	"net/url"

	log "github.com/Sirupsen/logrus"
	"github.com/megamsys/opennebula-go/api"
	"github.com/megamsys/opennebula-go/compute"
)

// CreateVM creates a vm in the specified node.
// It returns the vm, or an error, in case of failures.
func (c *Cluster) CreateVM(opts compute.VirtualMachine) (string, string, error) {
	var (
		addr    string
		machine string
		err     error
	)
	maxTries := 5
	for ; maxTries > 0; maxTries-- {

		if nodlist, err := c.Nodes(); err != nil {
			return addr, machine, errors.New("CreateVM needs a non empty node addr")
		} else {
			addr = nodlist[0].Address
		}

		if err == nil {
			machine, err = c.createVMInNode(opts, addr)
			if err == nil {
				c.handleNodeSuccess(addr)
				break
			}
			log.Errorf("Error trying to create machine in node %q: %s. Trying again in another node...", addr, err.Error())
		}
		shouldIncrementFailures := false
		isCreateMachineErr := false
		baseErr := err
		if nodeErr, ok := baseErr.(OneNodeError); ok {
			isCreateMachineErr = nodeErr.cmd == "createVM"
			baseErr = nodeErr.BaseError()
		}
		if urlErr, ok := baseErr.(*url.Error); ok {
			baseErr = urlErr.Err
		}
		_, isNetErr := baseErr.(*net.OpError)
		if isNetErr || isCreateMachineErr || baseErr == api.ErrConnRefused {
			shouldIncrementFailures = true
		}
		c.handleNodeError(addr, err, shouldIncrementFailures)
		return addr, machine, err
	}
	if err != nil {
		return addr, machine, fmt.Errorf("CreateVM: maximum number of tries exceeded, last error: %s", err.Error())
	}
	return addr, machine, err
}

//create a vm in a node.
func (c *Cluster) createVMInNode(opts compute.VirtualMachine, nodeAddress string) (string, error) {
	node, err := c.getNodeByAddr(nodeAddress)
	if err != nil {
		return "", err
	}
	opts.Client = node.Client

	_, err = opts.Create()

	if err != nil {
		return "", wrapErrorWithCmd(node, err, "createVM")
	}

	return opts.Name, nil
}

// DestroyVM kills a vm, returning an error in case of failure.
func (c *Cluster) DestroyVM(opts compute.VirtualMachine) error {
	var (
		addr string
	)
	if nodlist, err := c.Nodes(); err != nil {
		return errors.New("DeleteVM needs a non empty node addr")
	} else {
		addr = nodlist[0].Address
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	_, err = opts.Delete()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) getNodeByAddr(addr string) (node, error) {
	return c.getNode(func(s Storage) (Node, error) {
		return s.RetrieveNode(addr)
	})
}
<commit_msg>updates for the issue https://github.com/megamsys/megamd/issues/109<commit_after>package cluster

import (
	"errors"
	"fmt"
	"net"
	"net/url"

	log "github.com/Sirupsen/logrus"
	"github.com/megamsys/opennebula-go/api"
	"github.com/megamsys/opennebula-go/compute"
)

// CreateVM creates a vm in the specified node.
// It returns the vm, or an error, in case of failures.
func (c *Cluster) CreateVM(opts compute.VirtualMachine) (string, string, error) {
	var (
		addr    string
		machine string
		err     error
	)
	maxTries := 5
	for ; maxTries > 0; maxTries-- {

		if nodlist, err := c.Nodes(); err != nil {
			return addr, machine, errors.New("CreateVM needs a non empty node addr")
		} else {
			addr = nodlist[0].Address
		}

		if err == nil {
			machine, err = c.createVMInNode(opts, addr)
			if err == nil {
				c.handleNodeSuccess(addr)
				break
			}
			log.Errorf("Error trying to create machine in node %q: %s. Trying again in another node...", addr, err.Error())
		}
		shouldIncrementFailures := false
		isCreateMachineErr := false
		baseErr := err
		if nodeErr, ok := baseErr.(OneNodeError); ok {
			isCreateMachineErr = nodeErr.cmd == "createVM"
			baseErr = nodeErr.BaseError()
		}
		if urlErr, ok := baseErr.(*url.Error); ok {
			baseErr = urlErr.Err
		}
		_, isNetErr := baseErr.(*net.OpError)
		if isNetErr || isCreateMachineErr || baseErr == api.ErrConnRefused {
			shouldIncrementFailures = true
		}
		c.handleNodeError(addr, err, shouldIncrementFailures)
		return addr, machine, err
	}
	if err != nil {
		return addr, machine, fmt.Errorf("CreateVM: maximum number of tries exceeded, last error: %s", err.Error())
	}
	return addr, machine, err
}

//create a vm in a node.
func (c *Cluster) createVMInNode(opts compute.VirtualMachine, nodeAddress string) (string, error) {
	node, err := c.getNodeByAddr(nodeAddress)
	if err != nil {
		return "", err
	}
	opts.TemplateName = node.template
	opts.Client = node.Client

	_, err = opts.Create()

	if err != nil {
		return "", wrapErrorWithCmd(node, err, "createVM")
	}

	return opts.Name, nil
}

// DestroyVM kills a vm, returning an error in case of failure.
func (c *Cluster) DestroyVM(opts compute.VirtualMachine) error {
	var (
		addr string
	)
	if nodlist, err := c.Nodes(); err != nil {
		return errors.New("DeleteVM needs a non empty node addr")
	} else {
		addr = nodlist[0].Address
	}

	node, err := c.getNodeByAddr(addr)
	if err != nil {
		return err
	}
	opts.Client = node.Client

	_, err = opts.Delete()
	if err != nil {
		return wrapError(node, err)
	}
	return nil
}

func (c *Cluster) getNodeByAddr(addr string) (node, error) {
	return c.getNode(func(s Storage) (Node, error) {
		return s.RetrieveNode(addr)
	})
}
<|endoftext|>


<commit_before>// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package shell

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/mitchellh/iochan"
	"github.com/mitchellh/mapstructure"
	"github.com/mitchellh/packer/packer"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"
)

const DefaultRemotePath = "/tmp/script.sh"

type config struct {
	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The local path of the shell script to upload and execute.
	Path string

	// An array of multiple scripts to run.
	Scripts []string

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	RemotePath string `mapstructure:"remote_path"`

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes.
	ExecuteCommand string `mapstructure:"execute_command"`
}

type Provisioner struct {
	config config
}

type ExecuteCommandTemplate struct {
	Path string
}

func (p *Provisioner) Prepare(raws ...interface{}) error {
	for _, raw := range raws {
		if err := mapstructure.Decode(raw, &p.config); err != nil {
			return err
		}
	}

	if p.config.ExecuteCommand == "" {
		p.config.ExecuteCommand = "sh {{.Path}}"
	}

	if p.config.Inline != nil && len(p.config.Inline) == 0 {
		p.config.Inline = nil
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = DefaultRemotePath
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	errs := make([]error, 0)

	if p.config.Path != "" && len(p.config.Scripts) > 0 {
		errs = append(errs, errors.New("Only one of path or scripts can be specified."))
	}

	if p.config.Path != "" {
		p.config.Scripts = []string{p.config.Path}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = append(errs, errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = append(errs, errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = append(errs, fmt.Errorf("Bad script '%s': %s", path, err))
		}
	}

	if len(errs) > 0 {
		return &packer.MultiError{errs}
	}

	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) {
	scripts := make([]string, len(p.config.Scripts))

	// If we have an inline script, then turn that into a temporary
	// shell script and use that.
	if p.config.Inline != nil {
		tf, err := ioutil.TempFile("", "packer-shell")
		if err != nil {
			ui.Error(fmt.Sprintf("Error preparing shell script: %s", err))
			return
		}
		defer os.Remove(tf.Name())

		// Set the path to the temporary file
		scripts = append(scripts, tf.Name())

		// Write our contents to it
		writer := bufio.NewWriter(tf)
		for _, command := range p.config.Inline {
			if _, err := writer.WriteString(command + "\n"); err != nil {
				ui.Error(fmt.Sprintf("Error preparing shell script: %s", err))
				return
			}
		}

		if err := writer.Flush(); err != nil {
			ui.Error(fmt.Sprintf("Error preparing shell script: %s", err))
			return
		}

		tf.Close()
	}

	for _, path := range scripts {
		ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))

		log.Printf("Opening %s for reading", path)
		f, err := os.Open(path)
		if err != nil {
			ui.Error(fmt.Sprintf("Error opening shell script: %s", err))
			return
		}

		log.Printf("Uploading %s => %s", path, p.config.RemotePath)
		err = comm.Upload(p.config.RemotePath, f)
		if err != nil {
			ui.Error(fmt.Sprintf("Error uploading shell script: %s", err))
			return
		}

		// Compile the command
		var command bytes.Buffer
		t := template.Must(template.New("command").Parse(p.config.ExecuteCommand))
		t.Execute(&command, &ExecuteCommandTemplate{p.config.RemotePath})

		// Setup the remote command
		stdout_r, stdout_w := io.Pipe()
		stderr_r, stderr_w := io.Pipe()

		var cmd packer.RemoteCmd
		cmd.Command = command.String()
		cmd.Stdout = stdout_w
		cmd.Stderr = stderr_w

		log.Printf("Executing command: %s", cmd.Command)
		err = comm.Start(&cmd)
		if err != nil {
			ui.Error(fmt.Sprintf("Failed executing command: %s", err))
			return
		}

		exitChan := make(chan int, 1)
		stdoutChan := iochan.DelimReader(stdout_r, '\n')
		stderrChan := iochan.DelimReader(stderr_r, '\n')

		go func() {
			defer stdout_w.Close()
			defer stderr_w.Close()

			cmd.Wait()
			exitChan <- cmd.ExitStatus
		}()

	OutputLoop:
		for {
			select {
			case output := <-stderrChan:
				ui.Message(strings.TrimSpace(output))
			case output := <-stdoutChan:
				ui.Message(strings.TrimSpace(output))
			case exitStatus := <-exitChan:
				log.Printf("shell provisioner exited with status %d", exitStatus)
				break OutputLoop
			}
		}

		// Make sure we finish off stdout/stderr because we may have gotten
		// a message from the exit channel first.
		for output := range stdoutChan {
			ui.Message(output)
		}

		for output := range stderrChan {
			ui.Message(output)
		}
	}
}
<commit_msg>provisioner/shell: copy the scripts [GH-29]<commit_after>// This package implements a provisioner for Packer that executes
// shell scripts within the remote machine.
package shell

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"github.com/mitchellh/iochan"
	"github.com/mitchellh/mapstructure"
	"github.com/mitchellh/packer/packer"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"text/template"
)

const DefaultRemotePath = "/tmp/script.sh"

type config struct {
	// An inline script to execute. Multiple strings are all executed
	// in the context of a single shell.
	Inline []string

	// The local path of the shell script to upload and execute.
	Path string

	// An array of multiple scripts to run.
	Scripts []string

	// The remote path where the local shell script will be uploaded to.
	// This should be set to a writable file that is in a pre-existing directory.
	RemotePath string `mapstructure:"remote_path"`

	// The command used to execute the script. The '{{ .Path }}' variable
	// should be used to specify where the script goes.
	ExecuteCommand string `mapstructure:"execute_command"`
}

type Provisioner struct {
	config config
}

type ExecuteCommandTemplate struct {
	Path string
}

func (p *Provisioner) Prepare(raws ...interface{}) error {
	for _, raw := range raws {
		if err := mapstructure.Decode(raw, &p.config); err != nil {
			return err
		}
	}

	if p.config.ExecuteCommand == "" {
		p.config.ExecuteCommand = "sh {{.Path}}"
	}

	if p.config.Inline != nil && len(p.config.Inline) == 0 {
		p.config.Inline = nil
	}

	if p.config.RemotePath == "" {
		p.config.RemotePath = DefaultRemotePath
	}

	if p.config.Scripts == nil {
		p.config.Scripts = make([]string, 0)
	}

	errs := make([]error, 0)

	if p.config.Path != "" && len(p.config.Scripts) > 0 {
		errs = append(errs, errors.New("Only one of path or scripts can be specified."))
	}

	if p.config.Path != "" {
		p.config.Scripts = []string{p.config.Path}
	}

	if len(p.config.Scripts) == 0 && p.config.Inline == nil {
		errs = append(errs, errors.New("Either a script file or inline script must be specified."))
	} else if len(p.config.Scripts) > 0 && p.config.Inline != nil {
		errs = append(errs, errors.New("Only a script file or an inline script can be specified, not both."))
	}

	for _, path := range p.config.Scripts {
		if _, err := os.Stat(path); err != nil {
			errs = append(errs, fmt.Errorf("Bad script '%s': %s", path, err))
		}
	}

	if len(errs) > 0 {
		return &packer.MultiError{errs}
	}

	return nil
}

func (p *Provisioner) Provision(ui packer.Ui, comm packer.Communicator) {
	scripts := make([]string, len(p.config.Scripts))
	copy(scripts, p.config.Scripts)

	// If we have an inline script, then turn that into a temporary
	// shell script and use that.
	if p.config.Inline != nil {
		tf, err := ioutil.TempFile("", "packer-shell")
		if err != nil {
			ui.Error(fmt.Sprintf("Error preparing shell script: %s", err))
			return
		}
		defer os.Remove(tf.Name())

		// Set the path to the temporary file
		scripts = append(scripts, tf.Name())

		// Write our contents to it
		writer := bufio.NewWriter(tf)
		for _, command := range p.config.Inline {
			if _, err := writer.WriteString(command + "\n"); err != nil {
				ui.Error(fmt.Sprintf("Error preparing shell script: %s", err))
				return
			}
		}

		if err := writer.Flush(); err != nil {
			ui.Error(fmt.Sprintf("Error preparing shell script: %s", err))
			return
		}

		tf.Close()
	}

	for _, path := range scripts {
		ui.Say(fmt.Sprintf("Provisioning with shell script: %s", path))

		log.Printf("Opening %s for reading", path)
		f, err := os.Open(path)
		if err != nil {
			ui.Error(fmt.Sprintf("Error opening shell script: %s", err))
			return
		}

		log.Printf("Uploading %s => %s", path, p.config.RemotePath)
		err = comm.Upload(p.config.RemotePath, f)
		if err != nil {
			ui.Error(fmt.Sprintf("Error uploading shell script: %s", err))
			return
		}

		// Compile the command
		var command bytes.Buffer
		t := template.Must(template.New("command").Parse(p.config.ExecuteCommand))
		t.Execute(&command, &ExecuteCommandTemplate{p.config.RemotePath})

		// Setup the remote command
		stdout_r, stdout_w := io.Pipe()
		stderr_r, stderr_w := io.Pipe()

		var cmd packer.RemoteCmd
		cmd.Command = command.String()
		cmd.Stdout = stdout_w
		cmd.Stderr = stderr_w

		log.Printf("Executing command: %s", cmd.Command)
		err = comm.Start(&cmd)
		if err != nil {
			ui.Error(fmt.Sprintf("Failed executing command: %s", err))
			return
		}

		exitChan := make(chan int, 1)
		stdoutChan := iochan.DelimReader(stdout_r, '\n')
		stderrChan := iochan.DelimReader(stderr_r, '\n')

		go func() {
			defer stdout_w.Close()
			defer stderr_w.Close()

			cmd.Wait()
			exitChan <- cmd.ExitStatus
		}()

	OutputLoop:
		for {
			select {
			case output := <-stderrChan:
				ui.Message(strings.TrimSpace(output))
			case output := <-stdoutChan:
				ui.Message(strings.TrimSpace(output))
			case exitStatus := <-exitChan:
				log.Printf("shell provisioner exited with status %d", exitStatus)
				break OutputLoop
			}
		}

		// Make sure we finish off stdout/stderr because we may have gotten
		// a message from the exit channel first.
		for output := range stdoutChan {
			ui.Message(output)
		}

		for output := range stderrChan {
			ui.Message(output)
		}
	}
}
<|endoftext|>


<commit_before>package lints

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

/*******************************************************************************************************
BRs: 7.1.2.3
extKeyUsage (required)
Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. id-kp-emailProtection [RFC5280] MAY be present. Other values SHOULD NOT be present.
*******************************************************************************************************/

import (
	"github.com/zmap/zcrypto/x509"
	"github.com/zmap/zlint/util"
)

type subExtKeyUsageLegalUsage struct{}

func (l *subExtKeyUsageLegalUsage) Initialize() error {
	return nil
}

func (l *subExtKeyUsageLegalUsage) CheckApplies(c *x509.Certificate) bool {
	return c.ExtKeyUsage != nil
}

func (l *subExtKeyUsageLegalUsage) Execute(c *x509.Certificate) *LintResult {
	// Add actual lint here
	for _, kp := range c.ExtKeyUsage {
		if kp == x509.ExtKeyUsageServerAuth ||
			kp == x509.ExtKeyUsageClientAuth ||
			kp == x509.ExtKeyUsageEmailProtection {
			// If we find any of these three, considered passing, continue
			continue
		} else {
			// A bad usage was found, report and leave
			return &LintResult{Status: Warn}
		}
	}
	// If no bad usage was found, pass
	return &LintResult{Status: Pass}
}

func init() {
	RegisterLint(&Lint{
		Name:          "w_sub_cert_eku_extra_values",
		Description:   "Subscriber Certificate: extKeyUsage either the value id-kp-serverAuth or id-kp-clientAuth or both values MUST be present.",
		Citation:      "BRs: 7.1.2.3",
		Source:        CABFBaselineRequirements,
		EffectiveDate: util.CABEffectiveDate,
		Lint:          &subExtKeyUsageLegalUsage{},
	})
}
<commit_msg>Fix lint description (#218)<commit_after>package lints

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

/*******************************************************************************************************
BRs: 7.1.2.3
extKeyUsage (required)
Either the value id-kp-serverAuth [RFC5280] or id-kp-clientAuth [RFC5280] or both values MUST be present. id-kp-emailProtection [RFC5280] MAY be present. Other values SHOULD NOT be present.
*******************************************************************************************************/

import (
	"github.com/zmap/zcrypto/x509"
	"github.com/zmap/zlint/util"
)

type subExtKeyUsageLegalUsage struct{}

func (l *subExtKeyUsageLegalUsage) Initialize() error {
	return nil
}

func (l *subExtKeyUsageLegalUsage) CheckApplies(c *x509.Certificate) bool {
	return c.ExtKeyUsage != nil
}

func (l *subExtKeyUsageLegalUsage) Execute(c *x509.Certificate) *LintResult {
	// Add actual lint here
	for _, kp := range c.ExtKeyUsage {
		if kp == x509.ExtKeyUsageServerAuth ||
			kp == x509.ExtKeyUsageClientAuth ||
			kp == x509.ExtKeyUsageEmailProtection {
			// If we find any of these three, considered passing, continue
			continue
		} else {
			// A bad usage was found, report and leave
			return &LintResult{Status: Warn}
		}
	}
	// If no bad usage was found, pass
	return &LintResult{Status: Pass}
}

func init() {
	RegisterLint(&Lint{
		Name:          "w_sub_cert_eku_extra_values",
		Description:   "Subscriber Certificate: extKeyUsage values other than id-kp-serverAuth, id-kp-clientAuth, and id-kp-emailProtection SHOULD NOT be present.",
		Citation:      "BRs: 7.1.2.3",
		Source:        CABFBaselineRequirements,
		EffectiveDate: util.CABEffectiveDate,
		Lint:          &subExtKeyUsageLegalUsage{},
	})
}
<|endoftext|>


<commit_before>package command

import (
	"encoding/json"
	"fmt"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/closemeeting"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/createmeeting"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/putproduct"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/removeproduct"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/setbuyer"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/setplace"
)

func ExampleCommandHandler_CreateMeeting() {
	// Send a json Request in this form
	var request createmeeting.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "meeting": {
	//     "id": "",
	//     "place": "",
	//     "creator": "",
	//     "buyer": "",
	//     "date": "0001-01-01T00:00:00Z",
	//     "closeDate": "0001-01-01T00:00:00Z",
	//     "closed": false,
	//     "orders": null,
	//     "offer": null
	//   }
	// }
}

func ExampleCommandHandler_CloseMeeting() {
	// Send a json Request in this form
	var request closemeeting.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_PutProduct() {
	// Send a json Request in this form
	var request putproduct.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "meetingID": "",
	//   "product": {
	//     "name": "",
	//     "price": 0
	//   },
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_RemoveProduct() {
	// Send a json Request in this form
	var request removeproduct.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "productName": "",
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_SetBuyer() {
	// Send a json Request in this form
	var request setbuyer.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "buyer": "",
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_SetPlace() {
	// Send a json Request in this form
	var request setplace.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "place": "",
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}
<commit_msg>fix (sehr skurile) tests<commit_after>package command

import (
	"encoding/json"
	"fmt"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/closemeeting"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/createmeeting"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/putproduct"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/removeproduct"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/setbuyer"
	"github.com/WeisswurstSystems/WWM-BB/meeting/usecase/setplace"
)

func ExampleCommandHandler_CreateMeeting() {
	// Send a json Request in this form
	var request createmeeting.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "meeting": {
	//     "id": "",
	//     "place": "",
	//     "creator": "",
	//     "buyer": "",
	//     "date": "0001-01-01T00:00:00Z",
	//     "closeDate": "0001-01-01T00:00:00Z",
	//     "closed": false,
	//     "orders": null,
	//     "offer": null
	//   },
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_CloseMeeting() {
	// Send a json Request in this form
	var request closemeeting.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_PutProduct() {
	// Send a json Request in this form
	var request putproduct.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "meetingID": "",
	//   "product": {
	//     "name": "",
	//     "price": 0
	//   },
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_RemoveProduct() {
	// Send a json Request in this form
	var request removeproduct.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "productName": "",
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_SetBuyer() {
	// Send a json Request in this form
	var request setbuyer.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "buyer": "",
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}

func ExampleCommandHandler_SetPlace() {
	// Send a json Request in this form
	var request setplace.Request
	data, _ := json.MarshalIndent(request, "", "  ")
	fmt.Printf("%s", data)

	// Output:
	// {
	//   "place": "",
	//   "meetingID": "",
	//   "login": {
	//     "mail": "",
	//     "password": ""
	//   }
	// }
}
<|endoftext|>


<commit_before>package hostdb

import (
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// TestInsertHost tests the insertHost method.
func TestInsertHost(t *testing.T) {
	// no dependencies necessary
	hdb := newHostDB(nil, nil, nil, nil, nil, nil)

	// invalid host should not be added
	hdb.insertHost(modules.HostSettings{NetAddress: "foo"})
	if _, exists := hdb.allHosts["foo"]; exists {
		t.Error("host with invalid NetAddress was inserted")
	}

	// valid host should be added
	hdb.insertHost(modules.HostSettings{NetAddress: "foo:1234"})
	if _, exists := hdb.allHosts["foo:1234"]; !exists {
		t.Error("host with valid NetAddress was not inserted")
	}
	// host should be scanned
	select {
	case <-hdb.scanPool:
	case <-time.After(10 * time.Millisecond):
		t.Error("host was not scanned")
	}

	// duplicate host should not be added
	hdb.insertHost(modules.HostSettings{NetAddress: "foo:1234"})
	if len(hdb.allHosts) != 1 {
		t.Error("duplicate host was added:", hdb.allHosts)
	}
	// no scan should occur
	select {
	case <-hdb.scanPool:
		t.Error("duplicate host was added to scan pool")
	case <-time.After(10 * time.Millisecond):
	}
}

// TestActiveHosts tests the ActiveHosts method.
func TestActiveHosts(t *testing.T) {
	// no dependencies necessary
	hdb := newHostDB(nil, nil, nil, nil, nil, nil)

	// empty
	if hosts := hdb.ActiveHosts(); len(hosts) != 0 {
		t.Errorf("wrong number of hosts: expected %v, got %v", 0, len(hosts))
	}

	// with one host
	h1 := new(hostEntry)
	h1.NetAddress = "foo"
	hdb.activeHosts = map[modules.NetAddress]*hostNode{
		h1.NetAddress: &hostNode{hostEntry: h1},
	}
	if hosts := hdb.ActiveHosts(); len(hosts) != 1 {
		t.Errorf("wrong number of hosts: expected %v, got %v", 1, len(hosts))
	}

	// with multiple hosts
	h2 := new(hostEntry)
	h2.NetAddress = "bar"
	hdb.activeHosts = map[modules.NetAddress]*hostNode{
		h1.NetAddress: &hostNode{hostEntry: h1},
		h2.NetAddress: &hostNode{hostEntry: h2},
	}
	if hosts := hdb.ActiveHosts(); len(hosts) != 2 {
		t.Errorf("wrong number of hosts: expected %v, got %v", 2, len(hosts))
	}
}

// TestAveragePrice tests the AveragePrice method, which also depends on the
// randomHosts function.
func TestAveragePrice(t *testing.T) {
	// no dependencies necessary
	hdb := newHostDB(nil, nil, nil, nil, nil, nil)

	// empty
	if avg := hdb.AveragePrice(); !avg.IsZero() {
		t.Error("average of empty hostdb should be zero:", avg)
	}

	// with one host
	h1 := new(hostEntry)
	h1.NetAddress = "foo"
	h1.Price = types.NewCurrency64(100)
	h1.weight = baseWeight
	hdb.insertNode(h1)
	if len(hdb.activeHosts) != 1 {
		t.Error("host was not added:", hdb.activeHosts)
	}
	if avg := hdb.AveragePrice(); avg.Cmp(h1.Price) != 0 {
		t.Error("average of one host should be that host's price:", avg)
	}

	// with two hosts
	h2 := new(hostEntry)
	h2.NetAddress = "bar"
	h2.Price = types.NewCurrency64(300)
	h2.weight = baseWeight
	hdb.insertNode(h2)
	if len(hdb.activeHosts) != 2 {
		t.Error("host was not added:", hdb.activeHosts)
	}
	if avg := hdb.AveragePrice(); avg.Cmp(types.NewCurrency64(200)) != 0 {
		t.Error("average of two hosts should be their sum/2:", avg)
	}
}
<commit_msg>inserted hosts no longer wind up in allHosts<commit_after>package hostdb

import (
	"testing"
	"time"

	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"
)

// TestInsertHost tests the insertHost method.
func TestInsertHost(t *testing.T) {
	// no dependencies necessary
	hdb := newHostDB(nil, nil, nil, nil, nil, nil)

	// invalid host should not be scanned
	hdb.insertHost(modules.HostSettings{NetAddress: "foo"})
	select {
	case <-hdb.scanPool:
		t.Error("invalid host was added to scan pool")
	case <-time.After(10 * time.Millisecond):
	}

	// valid host should be scanned
	hdb.insertHost(modules.HostSettings{NetAddress: "foo:1234"})
	select {
	case <-hdb.scanPool:
	case <-time.After(10 * time.Millisecond):
		t.Error("host was not scanned")
	}

	// duplicate host should not be scanned
	hdb.allHosts["bar:1234"] = nil
	hdb.insertHost(modules.HostSettings{NetAddress: "bar:1234"})
	select {
	case <-hdb.scanPool:
		t.Error("duplicate host was added to scan pool")
	case <-time.After(10 * time.Millisecond):
	}
}

// TestActiveHosts tests the ActiveHosts method.
func TestActiveHosts(t *testing.T) {
	// no dependencies necessary
	hdb := newHostDB(nil, nil, nil, nil, nil, nil)

	// empty
	if hosts := hdb.ActiveHosts(); len(hosts) != 0 {
		t.Errorf("wrong number of hosts: expected %v, got %v", 0, len(hosts))
	}

	// with one host
	h1 := new(hostEntry)
	h1.NetAddress = "foo"
	hdb.activeHosts = map[modules.NetAddress]*hostNode{
		h1.NetAddress: &hostNode{hostEntry: h1},
	}
	if hosts := hdb.ActiveHosts(); len(hosts) != 1 {
		t.Errorf("wrong number of hosts: expected %v, got %v", 1, len(hosts))
	}

	// with multiple hosts
	h2 := new(hostEntry)
	h2.NetAddress = "bar"
	hdb.activeHosts = map[modules.NetAddress]*hostNode{
		h1.NetAddress: &hostNode{hostEntry: h1},
		h2.NetAddress: &hostNode{hostEntry: h2},
	}
	if hosts := hdb.ActiveHosts(); len(hosts) != 2 {
		t.Errorf("wrong number of hosts: expected %v, got %v", 2, len(hosts))
	}
}

// TestAveragePrice tests the AveragePrice method, which also depends on the
// randomHosts function.
func TestAveragePrice(t *testing.T) {
	// no dependencies necessary
	hdb := newHostDB(nil, nil, nil, nil, nil, nil)

	// empty
	if avg := hdb.AveragePrice(); !avg.IsZero() {
		t.Error("average of empty hostdb should be zero:", avg)
	}

	// with one host
	h1 := new(hostEntry)
	h1.NetAddress = "foo"
	h1.Price = types.NewCurrency64(100)
	h1.weight = baseWeight
	hdb.insertNode(h1)
	if len(hdb.activeHosts) != 1 {
		t.Error("host was not added:", hdb.activeHosts)
	}
	if avg := hdb.AveragePrice(); avg.Cmp(h1.Price) != 0 {
		t.Error("average of one host should be that host's price:", avg)
	}

	// with two hosts
	h2 := new(hostEntry)
	h2.NetAddress = "bar"
	h2.Price = types.NewCurrency64(300)
	h2.weight = baseWeight
	hdb.insertNode(h2)
	if len(hdb.activeHosts) != 2 {
		t.Error("host was not added:", hdb.activeHosts)
	}
	if avg := hdb.AveragePrice(); avg.Cmp(types.NewCurrency64(200)) != 0 {
		t.Error("average of two hosts should be their sum/2:", avg)
	}
}
<|endoftext|>


<commit_before>package version

import (
	"fmt"

	pb "github.com/pachyderm/pachyderm/src/client/version/versionpb"
)

const (
	// MajorVersion is the current major version for pachyderm.
	MajorVersion = 1
	// MinorVersion is the current minor version for pachyderm.
	MinorVersion = 4
	// MicroVersion is the patch number for pachyderm.
	MicroVersion = 6
)

var (
	// AdditionalVersion is the string provided at release time
	// The value is passed to the linker at build time
	// DO NOT set the value of this variable here
	AdditionalVersion string
	// Version is the current version for pachyderm.
	Version = &pb.Version{
		Major:      MajorVersion,
		Minor:      MinorVersion,
		Micro:      MicroVersion,
		Additional: AdditionalVersion,
	}
)

// PrettyPrintVersion returns a version string optionally tagged with metadata.
// For example: "1.2.3", or "1.2.3-rc1" if version.Additional is "rc1".
func PrettyPrintVersion(version *pb.Version) string {
	result := fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Micro)
	if version.Additional != "" {
		result += fmt.Sprintf("-%s", version.Additional)
	}
	return result
}
<commit_msg>Bump to 1.4.7<commit_after>package version

import (
	"fmt"

	pb "github.com/pachyderm/pachyderm/src/client/version/versionpb"
)

const (
	// MajorVersion is the current major version for pachyderm.
	MajorVersion = 1
	// MinorVersion is the current minor version for pachyderm.
	MinorVersion = 4
	// MicroVersion is the patch number for pachyderm.
	MicroVersion = 7
)

var (
	// AdditionalVersion is the string provided at release time
	// The value is passed to the linker at build time
	// DO NOT set the value of this variable here
	AdditionalVersion string
	// Version is the current version for pachyderm.
	Version = &pb.Version{
		Major:      MajorVersion,
		Minor:      MinorVersion,
		Micro:      MicroVersion,
		Additional: AdditionalVersion,
	}
)

// PrettyPrintVersion returns a version string optionally tagged with metadata.
// For example: "1.2.3", or "1.2.3-rc1" if version.Additional is "rc1".
func PrettyPrintVersion(version *pb.Version) string {
	result := fmt.Sprintf("%d.%d.%d", version.Major, version.Minor, version.Micro)
	if version.Additional != "" {
		result += fmt.Sprintf("-%s", version.Additional)
	}
	return result
}
<|endoftext|>