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

// Package prometheus provides bindings to the Prometheus HTTP API:
// http://prometheus.io/docs/querying/api/
package prometheus

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/common/model"
	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

const (
	statusAPIError = 422
	apiPrefix      = "/api/v1"

	epQuery       = "/query"
	epQueryRange  = "/query_range"
	epLabelValues = "/label/:name/values"
	epSeries      = "/series"
)

// ErrorType models the different API error types.
type ErrorType string

// Possible values for ErrorType.
const (
	ErrBadData     ErrorType = "bad_data"
	ErrTimeout               = "timeout"
	ErrCanceled              = "canceled"
	ErrExec                  = "execution"
	ErrBadResponse           = "bad_response"
)

// Error is an error returned by the API.
type Error struct {
	Type ErrorType
	Msg  string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}

// CancelableTransport is like net.Transport but provides
// per-request cancelation functionality.
type CancelableTransport interface {
	http.RoundTripper
	CancelRequest(req *http.Request)
}

// DefaultTransport is used if no Transport is set in Config.
var DefaultTransport CancelableTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
}

// Config defines configuration parameters for a new client.
type Config struct {
	// The address of the Prometheus to connect to.
	Address string

	// Transport is used by the Client to drive HTTP requests. If not
	// provided, DefaultTransport will be used.
	Transport CancelableTransport
}

func (cfg *Config) transport() CancelableTransport {
	if cfg.Transport == nil {
		return DefaultTransport
	}
	return cfg.Transport
}

// Client is the interface for an API client.
type Client interface {
	url(ep string, args map[string]string) *url.URL
	do(context.Context, *http.Request) (*http.Response, []byte, error)
}

// New returns a new Client.
//
// It is safe to use the returned Client from multiple goroutines.
func New(cfg Config) (Client, error) {
	u, err := url.Parse(cfg.Address)
	if err != nil {
		return nil, err
	}
	u.Path = strings.TrimRight(u.Path, "/") + apiPrefix

	return &httpClient{
		endpoint:  u,
		transport: cfg.transport(),
	}, nil
}

type httpClient struct {
	endpoint  *url.URL
	transport CancelableTransport
}

func (c *httpClient) url(ep string, args map[string]string) *url.URL {
	p := path.Join(c.endpoint.Path, ep)

	for arg, val := range args {
		arg = ":" + arg
		p = strings.Replace(p, arg, val, -1)
	}

	u := *c.endpoint
	u.Path = p

	return &u
}

func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)

	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, nil, err
	}

	var body []byte
	done := make(chan struct{})
	go func() {
		body, err = ioutil.ReadAll(resp.Body)
		close(done)
	}()

	select {
	case <-ctx.Done():
		err = resp.Body.Close()
		<-done
		if err == nil {
			err = ctx.Err()
		}
	case <-done:
	}

	return resp, body, err
}

// apiClient wraps a regular client and processes successful API responses.
// Successful also includes responses that errored at the API level.
type apiClient struct {
	Client
}

type apiResponse struct {
	Status    string          `json:"status"`
	Data      json.RawMessage `json:"data"`
	ErrorType ErrorType       `json:"errorType"`
	Error     string          `json:"error"`
}

func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, body, err := c.Client.do(ctx, req)
	if err != nil {
		return resp, body, err
	}

	code := resp.StatusCode

	if code/100 != 2 && code != statusAPIError {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  fmt.Sprintf("bad response code %d", resp.StatusCode),
		}
	}

	var result apiResponse

	if err = json.Unmarshal(body, &result); err != nil {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  err.Error(),
		}
	}

	if (code == statusAPIError) != (result.Status == "error") {
		err = &Error{
			Type: ErrBadResponse,
			Msg:  "inconsistent body for response code",
		}
	}

	if code == statusAPIError && result.Status == "error" {
		err = &Error{
			Type: result.ErrorType,
			Msg:  result.Error,
		}
	}

	return resp, []byte(result.Data), err
}

// Range represents a sliced time range.
type Range struct {
	// The boundaries of the time range.
	Start, End time.Time
	// The maximum time between two slices within the boundaries.
	Step time.Duration
}

// queryResult contains result data for a query.
type queryResult struct {
	Type   model.ValueType `json:"resultType"`
	Result interface{}     `json:"result"`

	// The decoded value.
	v model.Value
}

func (qr *queryResult) UnmarshalJSON(b []byte) error {
	v := struct {
		Type   model.ValueType `json:"resultType"`
		Result json.RawMessage `json:"result"`
	}{}

	err := json.Unmarshal(b, &v)
	if err != nil {
		return err
	}

	switch v.Type {
	case model.ValScalar:
		var sv model.Scalar
		err = json.Unmarshal(v.Result, &sv)
		qr.v = &sv

	case model.ValVector:
		var vv model.Vector
		err = json.Unmarshal(v.Result, &vv)
		qr.v = vv

	case model.ValMatrix:
		var mv model.Matrix
		err = json.Unmarshal(v.Result, &mv)
		qr.v = mv

	default:
		err = fmt.Errorf("unexpected value type %q", v.Type)
	}
	return err
}

// QueryAPI provides bindings the Prometheus's query API.
type QueryAPI interface {
	// Query performs a query for the given time.
	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
	// QueryRange performs a query for the given range.
	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
	// QueryLabelValues performs a query for the values of the given label.
	QueryLabelValues(ctx context.Context, label string) ([]string, error)
}

// NewQueryAPI returns a new QueryAPI for the client.
//
// It is safe to use the returned QueryAPI from multiple goroutines.
func NewQueryAPI(c Client) QueryAPI {
	return &httpQueryAPI{client: apiClient{c}}
}

type httpQueryAPI struct {
	client Client
}

func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
	u := h.client.url(epQuery, nil)
	q := u.Query()

	q.Set("query", query)
	q.Set("time", ts.Format(time.RFC3339Nano))

	u.RawQuery = q.Encode()

	req, _ := http.NewRequest("GET", u.String(), nil)

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
	u := h.client.url(epQueryRange, nil)
	q := u.Query()

	var (
		start = r.Start.Format(time.RFC3339Nano)
		end   = r.End.Format(time.RFC3339Nano)
		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
	)

	q.Set("query", query)
	q.Set("start", start)
	q.Set("end", end)
	q.Set("step", step)

	u.RawQuery = q.Encode()

	req, _ := http.NewRequest("GET", u.String(), nil)

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) QueryLabelValues(ctx context.Context, label string) ([]string, error) {
	u := h.client.url(epLabelValues, map[string]string{"name": label})
	req, _ := http.NewRequest(http.MethodGet, u.String(), nil)
	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}
	var values []string
	err = json.Unmarshal(body, &values)
	return values, err
}
<commit_msg>api: stops ignoring errors creating requests<commit_after>// Copyright 2015 The Prometheus Authors
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package prometheus provides bindings to the Prometheus HTTP API:
// http://prometheus.io/docs/querying/api/
package prometheus

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net"
	"net/http"
	"net/url"
	"path"
	"strconv"
	"strings"
	"time"

	"github.com/prometheus/common/model"
	"golang.org/x/net/context"
	"golang.org/x/net/context/ctxhttp"
)

const (
	statusAPIError = 422
	apiPrefix      = "/api/v1"

	epQuery       = "/query"
	epQueryRange  = "/query_range"
	epLabelValues = "/label/:name/values"
	epSeries      = "/series"
)

// ErrorType models the different API error types.
type ErrorType string

// Possible values for ErrorType.
const (
	ErrBadData     ErrorType = "bad_data"
	ErrTimeout               = "timeout"
	ErrCanceled              = "canceled"
	ErrExec                  = "execution"
	ErrBadResponse           = "bad_response"
)

// Error is an error returned by the API.
type Error struct {
	Type ErrorType
	Msg  string
}

func (e *Error) Error() string {
	return fmt.Sprintf("%s: %s", e.Type, e.Msg)
}

// CancelableTransport is like net.Transport but provides
// per-request cancelation functionality.
type CancelableTransport interface {
	http.RoundTripper
	CancelRequest(req *http.Request)
}

// DefaultTransport is used if no Transport is set in Config.
var DefaultTransport CancelableTransport = &http.Transport{
	Proxy: http.ProxyFromEnvironment,
	Dial: (&net.Dialer{
		Timeout:   30 * time.Second,
		KeepAlive: 30 * time.Second,
	}).Dial,
	TLSHandshakeTimeout: 10 * time.Second,
}

// Config defines configuration parameters for a new client.
type Config struct {
	// The address of the Prometheus to connect to.
	Address string

	// Transport is used by the Client to drive HTTP requests. If not
	// provided, DefaultTransport will be used.
	Transport CancelableTransport
}

func (cfg *Config) transport() CancelableTransport {
	if cfg.Transport == nil {
		return DefaultTransport
	}
	return cfg.Transport
}

// Client is the interface for an API client.
type Client interface {
	url(ep string, args map[string]string) *url.URL
	do(context.Context, *http.Request) (*http.Response, []byte, error)
}

// New returns a new Client.
//
// It is safe to use the returned Client from multiple goroutines.
func New(cfg Config) (Client, error) {
	u, err := url.Parse(cfg.Address)
	if err != nil {
		return nil, err
	}
	u.Path = strings.TrimRight(u.Path, "/") + apiPrefix

	return &httpClient{
		endpoint:  u,
		transport: cfg.transport(),
	}, nil
}

type httpClient struct {
	endpoint  *url.URL
	transport CancelableTransport
}

func (c *httpClient) url(ep string, args map[string]string) *url.URL {
	p := path.Join(c.endpoint.Path, ep)

	for arg, val := range args {
		arg = ":" + arg
		p = strings.Replace(p, arg, val, -1)
	}

	u := *c.endpoint
	u.Path = p

	return &u
}

func (c *httpClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, err := ctxhttp.Do(ctx, &http.Client{Transport: c.transport}, req)

	defer func() {
		if resp != nil {
			resp.Body.Close()
		}
	}()

	if err != nil {
		return nil, nil, err
	}

	var body []byte
	done := make(chan struct{})
	go func() {
		body, err = ioutil.ReadAll(resp.Body)
		close(done)
	}()

	select {
	case <-ctx.Done():
		err = resp.Body.Close()
		<-done
		if err == nil {
			err = ctx.Err()
		}
	case <-done:
	}

	return resp, body, err
}

// apiClient wraps a regular client and processes successful API responses.
// Successful also includes responses that errored at the API level.
type apiClient struct {
	Client
}

type apiResponse struct {
	Status    string          `json:"status"`
	Data      json.RawMessage `json:"data"`
	ErrorType ErrorType       `json:"errorType"`
	Error     string          `json:"error"`
}

func (c apiClient) do(ctx context.Context, req *http.Request) (*http.Response, []byte, error) {
	resp, body, err := c.Client.do(ctx, req)
	if err != nil {
		return resp, body, err
	}

	code := resp.StatusCode

	if code/100 != 2 && code != statusAPIError {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  fmt.Sprintf("bad response code %d", resp.StatusCode),
		}
	}

	var result apiResponse

	if err = json.Unmarshal(body, &result); err != nil {
		return resp, body, &Error{
			Type: ErrBadResponse,
			Msg:  err.Error(),
		}
	}

	if (code == statusAPIError) != (result.Status == "error") {
		err = &Error{
			Type: ErrBadResponse,
			Msg:  "inconsistent body for response code",
		}
	}

	if code == statusAPIError && result.Status == "error" {
		err = &Error{
			Type: result.ErrorType,
			Msg:  result.Error,
		}
	}

	return resp, []byte(result.Data), err
}

// Range represents a sliced time range.
type Range struct {
	// The boundaries of the time range.
	Start, End time.Time
	// The maximum time between two slices within the boundaries.
	Step time.Duration
}

// queryResult contains result data for a query.
type queryResult struct {
	Type   model.ValueType `json:"resultType"`
	Result interface{}     `json:"result"`

	// The decoded value.
	v model.Value
}

func (qr *queryResult) UnmarshalJSON(b []byte) error {
	v := struct {
		Type   model.ValueType `json:"resultType"`
		Result json.RawMessage `json:"result"`
	}{}

	err := json.Unmarshal(b, &v)
	if err != nil {
		return err
	}

	switch v.Type {
	case model.ValScalar:
		var sv model.Scalar
		err = json.Unmarshal(v.Result, &sv)
		qr.v = &sv

	case model.ValVector:
		var vv model.Vector
		err = json.Unmarshal(v.Result, &vv)
		qr.v = vv

	case model.ValMatrix:
		var mv model.Matrix
		err = json.Unmarshal(v.Result, &mv)
		qr.v = mv

	default:
		err = fmt.Errorf("unexpected value type %q", v.Type)
	}
	return err
}

// QueryAPI provides bindings the Prometheus's query API.
type QueryAPI interface {
	// Query performs a query for the given time.
	Query(ctx context.Context, query string, ts time.Time) (model.Value, error)
	// QueryRange performs a query for the given range.
	QueryRange(ctx context.Context, query string, r Range) (model.Value, error)
	// QueryLabelValues performs a query for the values of the given label.
	QueryLabelValues(ctx context.Context, label string) ([]string, error)
}

// NewQueryAPI returns a new QueryAPI for the client.
//
// It is safe to use the returned QueryAPI from multiple goroutines.
func NewQueryAPI(c Client) QueryAPI {
	return &httpQueryAPI{client: apiClient{c}}
}

type httpQueryAPI struct {
	client Client
}

func (h *httpQueryAPI) Query(ctx context.Context, query string, ts time.Time) (model.Value, error) {
	u := h.client.url(epQuery, nil)
	q := u.Query()

	q.Set("query", query)
	q.Set("time", ts.Format(time.RFC3339Nano))

	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) QueryRange(ctx context.Context, query string, r Range) (model.Value, error) {
	u := h.client.url(epQueryRange, nil)
	q := u.Query()

	var (
		start = r.Start.Format(time.RFC3339Nano)
		end   = r.End.Format(time.RFC3339Nano)
		step  = strconv.FormatFloat(r.Step.Seconds(), 'f', 3, 64)
	)

	q.Set("query", query)
	q.Set("start", start)
	q.Set("end", end)
	q.Set("step", step)

	u.RawQuery = q.Encode()

	req, err := http.NewRequest("GET", u.String(), nil)
	if err != nil {
		return nil, err
	}

	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}

	var qres queryResult
	err = json.Unmarshal(body, &qres)

	return model.Value(qres.v), err
}

func (h *httpQueryAPI) QueryLabelValues(ctx context.Context, label string) ([]string, error) {
	u := h.client.url(epLabelValues, map[string]string{"name": label})
	req, err := http.NewRequest(http.MethodGet, u.String(), nil)
	if err != nil {
		return nil, err
	}
	_, body, err := h.client.do(ctx, req)
	if err != nil {
		return nil, err
	}
	var values []string
	err = json.Unmarshal(body, &values)
	return values, err
}
<|endoftext|>


<commit_before>package adaptlog

import "errors"

// LogLevel type
type LogLevel uint8

// Logging levels.
const (
	// Panic
	Panic LogLevel = iota
	// Fatal
	Fatal
	// Error
	Error
	// Warn
	Warn
	// Info
	Info
	// Debug
	Debug
)

// LevelLogger interface
type LevelLogger interface {
	ExtendedLogger
	Error(...interface{})
	Errorf(string, ...interface{})
	Errorln(...interface{})

	Warn(...interface{})
	Warnf(string, ...interface{})
	Warnln(...interface{})

	Info(...interface{})
	Infof(string, ...interface{})
	Infoln(...interface{})

	Debug(...interface{})
	Debugf(string, ...interface{})
	Debugln(...interface{})
}

var leveledLogger LevelLogger
var initialized bool

func init() {
	initialized = false
}

// InitializeLeveledLogger initializes a leveled logger. Only once needed
func InitializeLeveledLogger(logger LevelLogger) {
	leveledLogger = logger
	initialized = true
}

// NewLeveledLogger creates a new leveled logger
func NewLeveledLogger() (*LeveledLogger, error) {

	if !initialized {
		return nil, errors.New("text string")
	}

	return &LeveledLogger{
		logger: leveledLogger,
	}, nil
}

// LeveledLogger for logging with level support
type LeveledLogger struct {
	logger LevelLogger
}

// Panic logs level
func (l *LeveledLogger) Panic(args ...interface{}) {
	l.logger.Panic(args)
}

// Panicf logs level with message
func (l *LeveledLogger) Panicf(msg string, args ...interface{}) {
	l.logger.Panicf(msg, args)
}

// Panicln logs level with new line
func (l *LeveledLogger) Panicln(args ...interface{}) {
	l.logger.Panicln(args)
}

// Fatal logs level
func (l *LeveledLogger) Fatal(args ...interface{}) {
	l.logger.Fatal(args)
}

// Fatalf logs level with message
func (l *LeveledLogger) Fatalf(msg string, args ...interface{}) {
	l.logger.Fatalf(msg, args)
}

// Fatalln logs level with new line
func (l *LeveledLogger) Fatalln(args ...interface{}) {
	l.logger.Fatalln(args)
}

// Error logs level
func (l *LeveledLogger) Error(args ...interface{}) {
	l.logger.Error(args)
}

// Errorf logs level with message
func (l *LeveledLogger) Errorf(msg string, args ...interface{}) {
	l.logger.Errorf(msg, args)
}

// Errorln logs level with new line
func (l *LeveledLogger) Errorln(args ...interface{}) {
	l.logger.Errorln(args)
}

// Warn logs level
func (l *LeveledLogger) Warn(args ...interface{}) {
	l.logger.Warn(args)
}

// Warnf logs level with message
func (l *LeveledLogger) Warnf(msg string, args ...interface{}) {
	l.logger.Warnf(msg, args)
}

// Warnln logs level with new line
func (l *LeveledLogger) Warnln(args ...interface{}) {
	l.logger.Warnln(args)
}

// Info logs level
func (l *LeveledLogger) Info(args ...interface{}) {
	l.logger.Info(args)
}

// Infof logs level with message
func (l *LeveledLogger) Infof(msg string, args ...interface{}) {
	l.logger.Infof(msg, args)
}

// Infoln logs level with new line
func (l *LeveledLogger) Infoln(args ...interface{}) {
	l.logger.Infoln(args)
}

// Debug logs level
func (l *LeveledLogger) Debug(args ...interface{}) {
	l.logger.Debug(args)
}

// Debugf logs level with message
func (l *LeveledLogger) Debugf(msg string, args ...interface{}) {
	l.logger.Debugf(msg, args)
}

// Debugln logs level with new line
func (l *LeveledLogger) Debugln(args ...interface{}) {
	l.logger.Debugln(args)
}
<commit_msg>cleaned up documentation<commit_after>package adaptlog

import "errors"

// LvlLogger interface
type LvlLogger interface {
	ExtendedLogger
	Error(...interface{})
	Errorf(string, ...interface{})
	Errorln(...interface{})

	Warn(...interface{})
	Warnf(string, ...interface{})
	Warnln(...interface{})

	Info(...interface{})
	Infof(string, ...interface{})
	Infoln(...interface{})

	Debug(...interface{})
	Debugf(string, ...interface{})
	Debugln(...interface{})
}

var levelLogger *LvlLogger

// ConfigLevelLogger configures a leveled logger
func ConfigLevelLogger(logger *LvlLogger) {
	levelLogger = logger
}

// NewLevelLogger creates a new level logger
func NewLevelLogger() (*LevelLogger, error) {

	if levelLogger == nil {
		return nil, errors.New("text string")
	}

	return &LevelLogger{*levelLogger}, nil
}

// LevelLogger for logging with level support
type LevelLogger struct {
	logger LvlLogger
}

// Panic level logging
func (l *LevelLogger) Panic(args ...interface{}) {
	l.logger.Panic(args)
}

// Panicf level logging with message
func (l *LevelLogger) Panicf(msg string, args ...interface{}) {
	l.logger.Panicf(msg, args)
}

// Panicln level logging with new line
func (l *LevelLogger) Panicln(args ...interface{}) {
	l.logger.Panicln(args)
}

// Fatal level logging
func (l *LevelLogger) Fatal(args ...interface{}) {
	l.logger.Fatal(args)
}

// Fatalf level logging with message
func (l *LevelLogger) Fatalf(msg string, args ...interface{}) {
	l.logger.Fatalf(msg, args)
}

// Fatalln level logging with new line
func (l *LevelLogger) Fatalln(args ...interface{}) {
	l.logger.Fatalln(args)
}

// Error level logging
func (l *LevelLogger) Error(args ...interface{}) {
	l.logger.Error(args)
}

// Errorf level logging with message
func (l *LevelLogger) Errorf(msg string, args ...interface{}) {
	l.logger.Errorf(msg, args)
}

// Errorln level logging with new line
func (l *LevelLogger) Errorln(args ...interface{}) {
	l.logger.Errorln(args)
}

// Warn level logging
func (l *LevelLogger) Warn(args ...interface{}) {
	l.logger.Warn(args)
}

// Warnf level logging with message
func (l *LevelLogger) Warnf(msg string, args ...interface{}) {
	l.logger.Warnf(msg, args)
}

// Warnln level logging with new line
func (l *LevelLogger) Warnln(args ...interface{}) {
	l.logger.Warnln(args)
}

// Info level logging
func (l *LevelLogger) Info(args ...interface{}) {
	l.logger.Info(args)
}

// Infof level logging with message
func (l *LevelLogger) Infof(msg string, args ...interface{}) {
	l.logger.Infof(msg, args)
}

// Infoln level logging with new line
func (l *LevelLogger) Infoln(args ...interface{}) {
	l.logger.Infoln(args)
}

// Debug level logging
func (l *LevelLogger) Debug(args ...interface{}) {
	l.logger.Debug(args)
}

// Debugf level logging with message
func (l *LevelLogger) Debugf(msg string, args ...interface{}) {
	l.logger.Debugf(msg, args)
}

// Debugln level logging with new line
func (l *LevelLogger) Debugln(args ...interface{}) {
	l.logger.Debugln(args)
}
<|endoftext|>


<commit_before>package slack

import (
	"github.com/lestrrat/go-slack/objects"
	"golang.org/x/oauth2"
)

const (
	ParseFull = "full"
	ParseNone = "none"
)

type ControlSequence interface {
	String() string
}

type ChannelLink struct {
	ID      string
	Channel string
}

type UserLink struct {
	ID       string
	Username string
}

type ExternalLink struct {
	URL  string
	Text string
}

// DefaultSlackAPIEndpoint contains the prefix used for Slack REST API
const (
	DefaultAPIEndpoint         = "https://slack.com/api/"
	DefaultOAuth2AuthEndpoint  = "https://slack.com/oauth/authorize"
	DefaultOAuth2TokenEndpoint = "https://slack.com/api/oauth.access"
)

// Oauth2Endpoint contains the Slack OAuth2 endpoint configuration
var OAuth2Endpoint = oauth2.Endpoint{
	AuthURL:  DefaultOAuth2AuthEndpoint,
	TokenURL: DefaultOAuth2TokenEndpoint,
}

type Client struct {
	auth     *AuthService
	channels *ChannelsService
	chat     *ChatService
	rtm      *RTMService
	users    *UsersService
	debug    bool
	slackURL string
	token    string
}

// SlackResponse is the general response part given by all
// slack API response.
type SlackResponse struct {
	OK        bool          `json:"ok"`
	ReplyTo   int           `json:"reply_to,omitempty"`
	Error     ErrorResponse `json:"error,omitempty"`
	Timestamp string        `json:"ts"`
}

// ErrorResponse wraps errors returned by Slack. It's usually a string,
// but it could be a structure.
// https://api.slack.com/rtm#handling_responses
type ErrorResponse struct {
	Code    int    `json:"code"`
	Message string `json:"msg"`
}

// AuthService handles all `auth.*` API endpoints
type AuthService struct {
	client *httpClient
	token  string
}

// AuthTestResponse is the data structure response from auth.test
type AuthTestResponse struct {
	URL    string `json:"url"`
	Team   string `json:"team"`
	User   string `json:"user"`
	TeamID string `json:"team_id"`
	UserID string `json:"user_id"`
}

// ChannelsService handles all `channel.*` API endpoints
type ChannelsService struct {
	client *httpClient
	token  string
}

type ChannelsHistoryResponse struct {
	HasMore  bool                `json:"has_more"`
	Latest   string              `json:"latest"`
	Messages objects.MessageList `json:"messages"`
}

// ChatService handles all `chat.*` API endpoints
type ChatService struct {
	client *httpClient
	token  string
}

type ChatResponse struct {
	Channel   string      `json:"channel"`
	Timestamp string      `json:"ts"`
	Message   interface{} `json:"message"` // TODO
}

// RTMService handles all `rtm.*` API endpoints
type RTMService struct {
	client *httpClient
	token  string
}

type RTMResponse struct {
	URL      string               `json:"url"`
	Self     *objects.UserDetails `json:"self"`
	Team     *objects.Team        `json:"team"`
	Users    []*objects.User      `json:"users"`
	Channels []*objects.Channel   `json:"channels"`
	Groups   []*objects.Group     `json:"groups"`
	Bots     []*objects.Bot       `json:"bots"`
	IMs      []*objects.IM        `json:"ims"`
}

// UsersService handles all `users.*` API endpoints
type UsersService struct {
	client *httpClient
	token  string
}
<commit_msg>Include Data and Surface in the interface definition<commit_after>package slack

import (
	"github.com/lestrrat/go-slack/objects"
	"golang.org/x/oauth2"
)

const (
	ParseFull = "full"
	ParseNone = "none"
)

type ControlSequence interface {
	Data() string
	Surface() string
	String() string
}

type ChannelLink struct {
	ID      string
	Channel string
}

type UserLink struct {
	ID       string
	Username string
}

type ExternalLink struct {
	URL  string
	Text string
}

// DefaultSlackAPIEndpoint contains the prefix used for Slack REST API
const (
	DefaultAPIEndpoint         = "https://slack.com/api/"
	DefaultOAuth2AuthEndpoint  = "https://slack.com/oauth/authorize"
	DefaultOAuth2TokenEndpoint = "https://slack.com/api/oauth.access"
)

// Oauth2Endpoint contains the Slack OAuth2 endpoint configuration
var OAuth2Endpoint = oauth2.Endpoint{
	AuthURL:  DefaultOAuth2AuthEndpoint,
	TokenURL: DefaultOAuth2TokenEndpoint,
}

type Client struct {
	auth     *AuthService
	channels *ChannelsService
	chat     *ChatService
	rtm      *RTMService
	users    *UsersService
	debug    bool
	slackURL string
	token    string
}

// SlackResponse is the general response part given by all
// slack API response.
type SlackResponse struct {
	OK        bool          `json:"ok"`
	ReplyTo   int           `json:"reply_to,omitempty"`
	Error     ErrorResponse `json:"error,omitempty"`
	Timestamp string        `json:"ts"`
}

// ErrorResponse wraps errors returned by Slack. It's usually a string,
// but it could be a structure.
// https://api.slack.com/rtm#handling_responses
type ErrorResponse struct {
	Code    int    `json:"code"`
	Message string `json:"msg"`
}

// AuthService handles all `auth.*` API endpoints
type AuthService struct {
	client *httpClient
	token  string
}

// AuthTestResponse is the data structure response from auth.test
type AuthTestResponse struct {
	URL    string `json:"url"`
	Team   string `json:"team"`
	User   string `json:"user"`
	TeamID string `json:"team_id"`
	UserID string `json:"user_id"`
}

// ChannelsService handles all `channel.*` API endpoints
type ChannelsService struct {
	client *httpClient
	token  string
}

type ChannelsHistoryResponse struct {
	HasMore  bool                `json:"has_more"`
	Latest   string              `json:"latest"`
	Messages objects.MessageList `json:"messages"`
}

// ChatService handles all `chat.*` API endpoints
type ChatService struct {
	client *httpClient
	token  string
}

type ChatResponse struct {
	Channel   string      `json:"channel"`
	Timestamp string      `json:"ts"`
	Message   interface{} `json:"message"` // TODO
}

// RTMService handles all `rtm.*` API endpoints
type RTMService struct {
	client *httpClient
	token  string
}

type RTMResponse struct {
	URL      string               `json:"url"`
	Self     *objects.UserDetails `json:"self"`
	Team     *objects.Team        `json:"team"`
	Users    []*objects.User      `json:"users"`
	Channels []*objects.Channel   `json:"channels"`
	Groups   []*objects.Group     `json:"groups"`
	Bots     []*objects.Bot       `json:"bots"`
	IMs      []*objects.IM        `json:"ims"`
}

// UsersService handles all `users.*` API endpoints
type UsersService struct {
	client *httpClient
	token  string
}
<|endoftext|>


<commit_before>package main

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/events"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rbac"
	"github.com/lxc/lxd/lxd/request"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
)

var eventTypes = []string{api.EventTypeLogging, api.EventTypeOperation, api.EventTypeLifecycle}
var privilegedEventTypes = []string{api.EventTypeLogging}

var eventsCmd = APIEndpoint{
	Path: "events",

	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: allowAuthenticated},
}

type eventsServe struct {
	req *http.Request
	d   *Daemon
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.d, r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error {
	// Detect project mode.
	projectName := queryParam(r, "project")
	allProjects := shared.IsTrue(queryParam(r, "all-projects"))

	if allProjects && projectName != "" {
		return api.StatusErrorf(http.StatusBadRequest, "Cannot specify a project when requesting all projects")
	} else if !allProjects && projectName == "" {
		projectName = project.Default
	}

	if !allProjects && projectName != project.Default {
		_, err := d.db.GetProject(context.Background(), projectName)
		if err != nil {
			return err
		}
	}

	types := strings.Split(r.FormValue("type"), ",")
	if len(types) == 1 && types[0] == "" {
		types = []string{}
		for _, entry := range eventTypes {
			if !rbac.UserIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
				continue
			}

			types = append(types, entry)
		}
	}

	// Validate event types.
	for _, entry := range types {
		if !shared.StringInSlice(entry, eventTypes) {
			_ = response.BadRequest(fmt.Errorf("'%s' isn't a supported event type", entry)).Render(w)
			return nil
		}
	}

	if shared.StringInSlice(api.EventTypeLogging, types) && !rbac.UserIsAdmin(r) {
		_ = response.Forbidden(nil).Render(w)
		return nil
	}

	// Upgrade the connection to websocket
	conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		return err
	}

	defer func() { _ = conn.Close() }() // Ensure listener below ends when this function ends.

	d.events.SetLocalLocation(d.State().ServerName)

	var excludeLocations []string
	// Get the current local serverName and store it for the events.
	// We do that now to avoid issues with changes to the name and to limit
	// the number of DB access to just one per connection.
	err = d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		if isClusterNotification(r) {
			ctx := r.Context()

			// Try and match cluster member certificate fingerprint to member name.
			fingerprint, found := ctx.Value(request.CtxUsername).(string)
			if found {
				cert, err := cluster.GetCertificateByFingerprintPrefix(context.Background(), tx.Tx(), fingerprint)
				if err != nil {
					return fmt.Errorf("Failed matching client certificate to cluster member: %w", err)
				}

				// Add the cluster member client's name to the excluded locations so that we can avoid
				// looping the event back to them when they send us an event via recvFunc.
				excludeLocations = append(excludeLocations, cert.Name)
			}
		}

		return nil
	})
	if err != nil {
		return err
	}

	var recvFunc events.EventHandler
	var excludeSources []events.EventSource
	if isClusterNotification(r) {
		// If client is another cluster member, it will already be pulling events from other cluster
		// members so no need to also deliver forwarded events that this member receives.
		excludeSources = append(excludeSources, events.EventSourcePull)

		recvFunc = func(event api.Event) {
			// Inject event received via push from event listener client so its forwarded to
			// other event hub members (if operating in event hub mode).
			d.events.Inject(event, events.EventSourcePush)
		}
	}

	listenerConnection := events.NewWebsocketListenerConnection(conn)

	listener, err := d.events.AddListener(projectName, allProjects, listenerConnection, types, excludeSources, recvFunc, excludeLocations)
	if err != nil {
		return err
	}

	listener.Wait(r.Context())

	return nil
}

// swagger:operation GET /1.0/events server events_get
//
// Get the event stream
//
// Connects to the event API using websocket.
//
// ---
// produces:
//   - application/json
// parameters:
//   - in: query
//     name: project
//     description: Project name
//     type: string
//     example: default
//   - in: query
//     name: type
//     description: Event type(s), comma separated (valid types are logging, operation or lifecycle)
//     type: string
//     example: logging,lifecycle
//   - in: query
//     name: all-projects
//     description: Retrieve instances from all projects
//     type: boolean
// responses:
//   "200":
//     description: Websocket message (JSON)
//     schema:
//       $ref: "#/definitions/Event"
//   "403":
//     $ref: "#/responses/Forbidden"
//   "500":
//     $ref: "#/responses/InternalServerError"
func eventsGet(d *Daemon, r *http.Request) response.Response {
	return &eventsServe{req: r, d: d}
}
<commit_msg>lxd/events: Improve error handling in eventsSocket<commit_after>package main

import (
	"context"
	"fmt"
	"net/http"
	"strings"

	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/db/cluster"
	"github.com/lxc/lxd/lxd/events"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/rbac"
	"github.com/lxc/lxd/lxd/request"
	"github.com/lxc/lxd/lxd/response"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/logger"
)

var eventTypes = []string{api.EventTypeLogging, api.EventTypeOperation, api.EventTypeLifecycle}
var privilegedEventTypes = []string{api.EventTypeLogging}

var eventsCmd = APIEndpoint{
	Path: "events",

	Get: APIEndpointAction{Handler: eventsGet, AccessHandler: allowAuthenticated},
}

type eventsServe struct {
	req *http.Request
	d   *Daemon
}

func (r *eventsServe) Render(w http.ResponseWriter) error {
	return eventsSocket(r.d, r.req, w)
}

func (r *eventsServe) String() string {
	return "event handler"
}

func eventsSocket(d *Daemon, r *http.Request, w http.ResponseWriter) error {
	// Detect project mode.
	projectName := queryParam(r, "project")
	allProjects := shared.IsTrue(queryParam(r, "all-projects"))

	if allProjects && projectName != "" {
		return api.StatusErrorf(http.StatusBadRequest, "Cannot specify a project when requesting all projects")
	} else if !allProjects && projectName == "" {
		projectName = project.Default
	}

	if !allProjects && projectName != project.Default {
		_, err := d.db.GetProject(context.Background(), projectName)
		if err != nil {
			return err
		}
	}

	types := strings.Split(r.FormValue("type"), ",")
	if len(types) == 1 && types[0] == "" {
		types = []string{}
		for _, entry := range eventTypes {
			if !rbac.UserIsAdmin(r) && shared.StringInSlice(entry, privilegedEventTypes) {
				continue
			}

			types = append(types, entry)
		}
	}

	// Validate event types.
	for _, entry := range types {
		if !shared.StringInSlice(entry, eventTypes) {
			return api.StatusErrorf(http.StatusBadRequest, "%q isn't a supported event type", entry)
		}
	}

	if shared.StringInSlice(api.EventTypeLogging, types) && !rbac.UserIsAdmin(r) {
		return api.StatusErrorf(http.StatusForbidden, "Forbidden")
	}

	l := logger.AddContext(logger.Log, logger.Ctx{"remote": r.RemoteAddr})

	// Upgrade the connection to websocket
	conn, err := shared.WebsocketUpgrader.Upgrade(w, r, nil)
	if err != nil {
		l.Warn("Failed upgrading event connection", logger.Ctx{"err": err})
		return nil
	}

	defer func() { _ = conn.Close() }() // Ensure listener below ends when this function ends.

	d.events.SetLocalLocation(d.State().ServerName)

	var excludeLocations []string
	// Get the current local serverName and store it for the events.
	// We do that now to avoid issues with changes to the name and to limit
	// the number of DB access to just one per connection.
	err = d.db.Cluster.Transaction(context.TODO(), func(ctx context.Context, tx *db.ClusterTx) error {
		if isClusterNotification(r) {
			ctx := r.Context()

			// Try and match cluster member certificate fingerprint to member name.
			fingerprint, found := ctx.Value(request.CtxUsername).(string)
			if found {
				cert, err := cluster.GetCertificateByFingerprintPrefix(context.Background(), tx.Tx(), fingerprint)
				if err != nil {
					return fmt.Errorf("Failed matching client certificate to cluster member: %w", err)
				}

				// Add the cluster member client's name to the excluded locations so that we can avoid
				// looping the event back to them when they send us an event via recvFunc.
				excludeLocations = append(excludeLocations, cert.Name)
			}
		}

		return nil
	})
	if err != nil {
		l.Warn("Failed setting up event connection", logger.Ctx{"err": err})
		return nil
	}

	var recvFunc events.EventHandler
	var excludeSources []events.EventSource
	if isClusterNotification(r) {
		// If client is another cluster member, it will already be pulling events from other cluster
		// members so no need to also deliver forwarded events that this member receives.
		excludeSources = append(excludeSources, events.EventSourcePull)

		recvFunc = func(event api.Event) {
			// Inject event received via push from event listener client so its forwarded to
			// other event hub members (if operating in event hub mode).
			d.events.Inject(event, events.EventSourcePush)
		}
	}

	listenerConnection := events.NewWebsocketListenerConnection(conn)

	listener, err := d.events.AddListener(projectName, allProjects, listenerConnection, types, excludeSources, recvFunc, excludeLocations)
	if err != nil {
		l.Warn("Failed to add event listener", logger.Ctx{"err": err})
		return nil
	}

	listener.Wait(r.Context())

	return nil
}

// swagger:operation GET /1.0/events server events_get
//
// Get the event stream
//
// Connects to the event API using websocket.
//
// ---
// produces:
//   - application/json
// parameters:
//   - in: query
//     name: project
//     description: Project name
//     type: string
//     example: default
//   - in: query
//     name: type
//     description: Event type(s), comma separated (valid types are logging, operation or lifecycle)
//     type: string
//     example: logging,lifecycle
//   - in: query
//     name: all-projects
//     description: Retrieve instances from all projects
//     type: boolean
// responses:
//   "200":
//     description: Websocket message (JSON)
//     schema:
//       $ref: "#/definitions/Event"
//   "403":
//     $ref: "#/responses/Forbidden"
//   "500":
//     $ref: "#/responses/InternalServerError"
func eventsGet(d *Daemon, r *http.Request) response.Response {
	return &eventsServe{req: r, d: d}
}
<|endoftext|>


<commit_before>package inject

import (
	"context"
	"net/http"
	"testing"

	"github.com/skygeario/skygear-server/pkg/core/config"
	"github.com/skygeario/skygear-server/pkg/server/skyerr"
	. "github.com/smartystreets/goconvey/convey"
)

type contextKey string

var (
	configurationKey = contextKey("configuration")
)

type istore interface {
	get() string
}

type store struct {
	name string
}

func (s store) get() string {
	return s.name
}

type dmap struct{}

// Provide provides dependency instance by name
// nolint: golint
func (s dmap) Provide(
	dependencyName string,
	ctx context.Context,
	requestID string,
	tConfig config.TenantConfiguration,
) interface{} {
	switch dependencyName {
	case "str":
		return "string"
	case "int":
		return 1
	case "store":
		return store{tConfig.AppName}
	case "istore":
		return &store{tConfig.AppName}
	default:
		return nil
	}
}

func TestInjectDependency(t *testing.T) {
	conf := config.TenantConfiguration{
		AppName: "TestApp",
	}

	req, _ := http.NewRequest("POST", "", nil)
	req = req.WithContext(context.WithValue(req.Context(), configurationKey, conf))

	Convey("Test injectDependency", t, func() {
		Convey("should inject simple type", func() {
			type targetStruct struct {
				Str string `dependency:"str"`
				Int int    `dependency:"int"`
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Str, ShouldEqual, "string")
			So(target.Int, ShouldEqual, 1)
		})

		Convey("should inject interface", func() {
			type targetStruct struct {
				Store istore `dependency:"istore"`
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Store, ShouldImplement, (*istore)(nil))
			So(target.Store.get(), ShouldEqual, "TestApp")
		})

		Convey("should inject struct", func() {
			type targetStruct struct {
				Store store `dependency:"store"`
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Store, ShouldHaveSameTypeAs, store{})
			So(target.Store.get(), ShouldEqual, "TestApp")
		})

		Convey("should not inject to with field without tag", func() {
			type targetStruct struct {
				Str string `dependency:"str"`
				str string
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Str, ShouldEqual, "string")
			So(target.str, ShouldBeEmpty)
		})

		Convey("should panic if field type is wrong", func() {
			type targetStruct struct {
				Str int `dependency:"str"`
			}

			target := targetStruct{}
			So(func() {
				DefaultRequestInject(&target, dmap{}, req)
			}, ShouldPanic)
		})

		Convey("should return error dependency name is wrong", func() {
			type targetStruct struct {
				Str int `dependency:"i_am_your_father"`
			}

			target := targetStruct{}
			err := DefaultRequestInject(&target, dmap{}, req)
			errResponse := err.(skyerr.Error)
			So(errResponse.Code(), ShouldEqual, skyerr.InvalidArgument)
		})
	})
}
<commit_msg>Fix DI test<commit_after>package inject

import (
	"context"
	"net/http"
	"testing"

	"github.com/skygeario/skygear-server/pkg/core/config"
	"github.com/skygeario/skygear-server/pkg/server/skyerr"
	. "github.com/smartystreets/goconvey/convey"
)

type contextKey string

var (
	configurationKey = contextKey("configuration")
)

type istore interface {
	get() string
}

type store struct {
	name string
}

func (s store) get() string {
	return s.name
}

type dmap struct{}

// Provide provides dependency instance by name
// nolint: golint
func (s dmap) Provide(
	dependencyName string,
	ctx context.Context,
	requestID string,
	tConfig config.TenantConfiguration,
) interface{} {
	switch dependencyName {
	case "str":
		return "string"
	case "int":
		return 1
	case "store":
		return store{tConfig.AppName}
	case "istore":
		return &store{tConfig.AppName}
	default:
		return nil
	}
}

func TestInjectDependency(t *testing.T) {
	conf := config.TenantConfiguration{
		AppName: "TestApp",
	}

	req, _ := http.NewRequest("POST", "", nil)
	config.SetTenantConfig(req, conf)

	Convey("Test injectDependency", t, func() {
		Convey("should inject simple type", func() {
			type targetStruct struct {
				Str string `dependency:"str"`
				Int int    `dependency:"int"`
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Str, ShouldEqual, "string")
			So(target.Int, ShouldEqual, 1)
		})

		Convey("should inject interface", func() {
			type targetStruct struct {
				Store istore `dependency:"istore"`
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Store, ShouldImplement, (*istore)(nil))
			So(target.Store.get(), ShouldEqual, "TestApp")
		})

		Convey("should inject struct", func() {
			type targetStruct struct {
				Store store `dependency:"store"`
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Store, ShouldHaveSameTypeAs, store{})
			So(target.Store.get(), ShouldEqual, "TestApp")
		})

		Convey("should not inject to with field without tag", func() {
			type targetStruct struct {
				Str string `dependency:"str"`
				str string
			}

			target := targetStruct{}
			DefaultRequestInject(&target, dmap{}, req)
			So(target.Str, ShouldEqual, "string")
			So(target.str, ShouldBeEmpty)
		})

		Convey("should panic if field type is wrong", func() {
			type targetStruct struct {
				Str int `dependency:"str"`
			}

			target := targetStruct{}
			So(func() {
				DefaultRequestInject(&target, dmap{}, req)
			}, ShouldPanic)
		})

		Convey("should return error dependency name is wrong", func() {
			type targetStruct struct {
				Str int `dependency:"i_am_your_father"`
			}

			target := targetStruct{}
			err := DefaultRequestInject(&target, dmap{}, req)
			errResponse := err.(skyerr.Error)
			So(errResponse.Code(), ShouldEqual, skyerr.InvalidArgument)
		})
	})
}
<|endoftext|>


<commit_before>package expr

import (
	"fmt"
	"math"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/consolidation"
	"github.com/grafana/metrictank/schema"
	"github.com/grafana/metrictank/util"
	"github.com/grafana/metrictank/util/align"
)

// Normalize normalizes series to the same common LCM interval - if they don't already have the same interval
// any adjusted series gets created in a series drawn out of the pool and is added to the dataMap so it can be reclaimed
func Normalize(dataMap DataMap, in []models.Series) []models.Series {
	if len(in) < 2 {
		return in
	}
	var intervals []uint32
	for _, s := range in {
		if s.Interval == 0 {
			panic("illegal interval 0")
		}
		intervals = append(intervals, s.Interval)
	}
	lcm := util.Lcm(intervals)
	for i, s := range in {
		if s.Interval != lcm {
			in[i] = NormalizeTo(dataMap, s, lcm)
		}
	}
	return in
}

func NormalizeTwo(dataMap DataMap, a, b models.Series) (models.Series, models.Series) {
	if a.Interval == b.Interval {
		return a, b
	}
	intervals := []uint32{a.Interval, b.Interval}
	lcm := util.Lcm(intervals)

	if a.Interval != lcm {
		a = NormalizeTo(dataMap, a, lcm)
	}
	if b.Interval != lcm {
		b = NormalizeTo(dataMap, b, lcm)
	}
	return a, b
}

// NormalizeTo normalizes the given series to the desired interval
// the following MUST be true when calling this:
// * interval > in.Interval
// * interval % in.Interval == 0
func NormalizeTo(dataMap DataMap, in models.Series, interval uint32) models.Series {

	if len(in.Datapoints) == 0 {
		panic(fmt.Sprintf("series %q cannot be normalized from interval %d to %d because it is empty", in.Target, in.Interval, interval))
	}

	// we need to copy the datapoints first because the consolidater will reuse the input slice
	// also, the input may not be pre-canonical. so add nulls in front and at the back to make it pre-canonical.
	// this may make points in front and at the back less accurate when consolidated (e.g. summing when some of the points are null results in a lower value)
	// but this is what graphite does....
	datapoints := pointSlicePool.Get().([]schema.Point)

	// example of how this works:
	// if in.Interval is 5, and interval is 15, then for example, to generate point 15, you want inputs 5, 10 and 15.
	// or more generally (you can follow any example vertically):
	//  5 10 15 20 25 30 35 40 45 50 <-- if any of these timestamps are your first point in `in`
	//  5  5  5 20 20 20 35 35 35 50 <-- then these are the corresponding timestamps of the first values we want as input for the consolidator
	// 15 15 15 30 30 30 45 45 45 60 <-- which, when fed through alignForwardIfNotAligned(), result in these numbers
	//  5  5  5 20 20 20 35 35 35 50 <-- subtract (aggnum-1)* in.interval or equivalent -interval + in.Interval = -15 + 5 = -10. these are our desired numbers!

	// now, for the final value, it's important to be aware of cases like this:
	// until=47, interval=10, in.Interval = 5
	// a canonical 10s series would have as last point 40. whereas our input series will have 45, which will consolidate into a point with timestamp 50, which is incorrect
	// (it breaches `to`, and may have more points than other series it needs to be combined with)
	// thus, we also need to potentially trim points from the back until the last point has the same Ts as a canonical series would

	canonicalStart := align.ForwardIfNotAligned(in.Datapoints[0].Ts, interval) - interval + in.Interval
	for ts := canonicalStart; ts < in.Datapoints[0].Ts; ts += in.Interval {
		datapoints = append(datapoints, schema.Point{Val: math.NaN(), Ts: ts})
	}

	datapoints = append(datapoints, in.Datapoints...)

	canonicalTs := (datapoints[len(datapoints)-1].Ts / interval) * interval
	numDrop := int((datapoints[len(datapoints)-1].Ts - canonicalTs) / in.Interval)
	datapoints = datapoints[0 : len(datapoints)-numDrop]

	// series may have been created by a function that didn't know which consolidation function to default to.
	// in the future maybe we can do more clever things here. e.g. perSecond maybe consolidate by max.
	if in.Consolidator == 0 {
		in.Consolidator = consolidation.Avg
	}
	in.Datapoints = consolidation.Consolidate(datapoints, interval/in.Interval, in.Consolidator)
	in.Interval = interval
	dataMap.Add(Req{}, in)
	return in
}
<commit_msg>refactor: isolate out "make pre-canonical" logic<commit_after>package expr

import (
	"fmt"
	"math"

	"github.com/grafana/metrictank/api/models"
	"github.com/grafana/metrictank/consolidation"
	"github.com/grafana/metrictank/schema"
	"github.com/grafana/metrictank/util"
	"github.com/grafana/metrictank/util/align"
)

// Normalize normalizes series to the same common LCM interval - if they don't already have the same interval
// any adjusted series gets created in a series drawn out of the pool and is added to the dataMap so it can be reclaimed
func Normalize(dataMap DataMap, in []models.Series) []models.Series {
	if len(in) < 2 {
		return in
	}
	var intervals []uint32
	for _, s := range in {
		if s.Interval == 0 {
			panic("illegal interval 0")
		}
		intervals = append(intervals, s.Interval)
	}
	lcm := util.Lcm(intervals)
	for i, s := range in {
		if s.Interval != lcm {
			in[i] = NormalizeTo(dataMap, s, lcm)
		}
	}
	return in
}

func NormalizeTwo(dataMap DataMap, a, b models.Series) (models.Series, models.Series) {
	if a.Interval == b.Interval {
		return a, b
	}
	intervals := []uint32{a.Interval, b.Interval}
	lcm := util.Lcm(intervals)

	if a.Interval != lcm {
		a = NormalizeTo(dataMap, a, lcm)
	}
	if b.Interval != lcm {
		b = NormalizeTo(dataMap, b, lcm)
	}
	return a, b
}

// NormalizeTo normalizes the given series to the desired interval
// will pad front and strip from back as needed, to assure the output is canonical for the given interval
// the following MUST be true when calling this:
// * interval > in.Interval
// * interval % in.Interval == 0
func NormalizeTo(dataMap DataMap, in models.Series, interval uint32) models.Series {

	if len(in.Datapoints) == 0 {
		panic(fmt.Sprintf("series %q cannot be normalized from interval %d to %d because it is empty", in.Target, in.Interval, interval))
	}

	// we need to copy the datapoints first because the consolidater will reuse the input slice
	// also, for the consolidator's output to be canonical, the input must be pre-canonical.
	// so add nulls in front and at the back to make it pre-canonical.
	// this may make points in front and at the back less accurate when consolidated (e.g. summing when some of the points are null results in a lower value)
	// but this is what graphite does....
	datapoints := pointSlicePool.Get().([]schema.Point)
	datapoints = makePreCanonicalCopy(in, interval, datapoints)

	// series may have been created by a function that didn't know which consolidation function to default to.
	// in the future maybe we can do more clever things here. e.g. perSecond maybe consolidate by max.
	if in.Consolidator == 0 {
		in.Consolidator = consolidation.Avg
	}
	in.Datapoints = consolidation.Consolidate(datapoints, interval/in.Interval, in.Consolidator)
	in.Interval = interval
	dataMap.Add(Req{}, in)
	return in
}

// makePreCanonicalCopy returns a copy of in's datapoints slice, but adjusted to be pre-canonical with respect to interval.
// for this, it reuses the 'datapoints' slice.
func makePreCanonicalCopy(in models.Series, interval uint32, datapoints []schema.Point) []schema.Point {
	// to achieve this we need to assure our input starts and ends with the right timestamp.

	// we need to figure out what is the ts of the first point to feed into the consolidator
	// example of how this works:
	// if in.Interval is 5, and interval is 15, then for example, to generate point 15, because we postmark and we want a full input going into this point,
	// you want inputs 5, 10 and 15.
	// or more generally (you can follow any example vertically):
	//  5 10 15 20 25 30 35 40 45 50 <-- if any of these timestamps are your first point in `in`
	//  5  5  5 20 20 20 35 35 35 50 <-- then these are the corresponding timestamps of the first values we want as input for the consolidator
	// 15 15 15 30 30 30 45 45 45 60 <-- which, when fed through alignForwardIfNotAligned(), result in these numbers
	//  5  5  5 20 20 20 35 35 35 50 <-- subtract (aggnum-1)* in.interval or equivalent -interval + in.Interval = -15 + 5 = -10. this is our initial timestamp.

	canonicalStart := align.ForwardIfNotAligned(in.Datapoints[0].Ts, interval) - interval + in.Interval
	for ts := canonicalStart; ts < in.Datapoints[0].Ts; ts += in.Interval {
		datapoints = append(datapoints, schema.Point{Val: math.NaN(), Ts: ts})
	}

	datapoints = append(datapoints, in.Datapoints...)

	// for the desired last input ts, it's important to be aware of cases like this:
	// until=47, interval=10, in.Interval = 5
	// a canonical 10s series would have as last point 40. whereas our input series will have 45, which will consolidate into a point with timestamp 50, which is incorrect
	// (it breaches `to`, and may have more points than other series it needs to be combined with)
	// thus, we also need to potentially trim points from the back until the last point has the same Ts as a canonical series would

	canonicalTs := (datapoints[len(datapoints)-1].Ts / interval) * interval
	numDrop := int((datapoints[len(datapoints)-1].Ts - canonicalTs) / in.Interval)
	return datapoints[0 : len(datapoints)-numDrop]
}
<|endoftext|>


<commit_before>// **********************************************************************
//    Copyright (c) 2017 Henry Seurer
//
//   Permission is hereby granted, free of charge, to any person
//    obtaining a copy of this software and associated documentation
//    files (the "Software"), to deal in the Software without
//    restriction, including without limitation the rights to use,
//    copy, modify, merge, publish, distribute, sublicense, and/or sell
//    copies of the Software, and to permit persons to whom the
//    Software is furnished to do so, subject to the following
//    conditions:
//
//   The above copyright notice and this permission notice shall be
//   included in all copies or substantial portions of the Software.
//
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//    OTHER DEALINGS IN THE SOFTWARE.
//
// **********************************************************************

package wiringpi

/*
#include <time.h>

unsigned long long as_nanoseconds(struct timespec* ts) {
    return ts->tv_sec * (unsigned long long)1000000000L + ts->tv_nsec;
}

unsigned long long monotonic_time() {
    struct timespec last_t;
    clock_gettime(CLOCK_MONOTONIC, &last_t);
    return as_nanoseconds(&last_t);
}
*/
import "C"
import "unsafe"

import (
	"fmt"
	"os"
)

var (
	board2pin = []int{
		-1,
		-1,
		-1,
		8,
		-1,
		9,
		-1,
		7,
		15,
		-1,
		16,
		0,
		1,
		2,
		-1,
		-1,
		4,
		-1,
		5,
		12,
		-1,
		13,
		6,
		14,
		10,
		-1,
		11,
	}
	gpio2pin = []int{
		8,
		9,
		-1,
		-1,
		7,
		-1,
		-1,
		11,
		10,
		13,
		12,
		14,
		-1,
		-1,
		15,
		16,
		-1,
		0,
		1,
		-1,
		-1,
		2,
		3,
		4,
		5,
		6,
		-1,
		-1,
		17,
		18,
		19,
		20,
	}

	gpioModes = []string{"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"}
)

//noinspection GoUnusedConst
const (
	PIN_GPIO_0  = 0
	PIN_GPIO_1  = 1
	PIN_GPIO_2  = 2
	PIN_GPIO_3  = 3
	PIN_GPIO_4  = 4
	PIN_GPIO_5  = 5
	PIN_GPIO_6  = 6
	PIN_GPIO_7  = 7
	PIN_SDA     = 8
	PIN_SCL     = 9
	PIN_CE0     = 10
	PIN_CE1     = 11
	PIN_MOSI    = 12
	PIN_MOSO    = 13
	PIN_SCLK    = 14
	PIN_TXD     = 15
	PIN_RXD     = 16
	PIN_GPIO_8  = 17
	PIN_GPIO_9  = 18
	PIN_GPIO_10 = 19
	PIN_GPIO_11 = 20

	MODE_IN   = 0
	MODE_OUT  = 1
	MODE_ALT5 = 2
	MODE_ALT4 = 3
	MODE_ALT0 = 4
	MODE_ALT1 = 5
	MODE_ALT2 = 6
	MODE_ALT3 = 7
)

//use RPi.GPIO's BOARD numbering
//noinspection GoUnusedExportedFunction
func BoardToPin(pin int) int {
	if pin < 1 || pin >= len(board2pin) {
		panic(fmt.Sprintf("Invalid board pin number: %d", pin))
	}
	return board2pin[pin]
}

//noinspection GoUnusedExportedFunction
func GpioToPin(pin int) int {
	if pin < 0 || pin >= len(gpio2pin) {
		panic(fmt.Sprintf("Invalid bcm gpio number: %d", pin))
	}
	return gpio2pin[pin]
}

//noinspection GoUnusedExportedFunction
func PinToGpio(pin int) int {
	return internalPinToGpio(pin)
}

// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real
// underlying Broadcom GPIO pin numbers. See the pins page for a table which maps the wiringPi pin number to the
// Broadcom GPIO pin number to the physical location on the edge connector.
//
// This function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func Setup() int {
	return internalSetup()
}

//This is identical to above, however it allows the calling programs to use the Broadcom GPIO pin numbers
// directly with no re-mapping.
//
// As above, this function needs to be called with root privileges, and note that some pins are different
// from revision 1 to revision 2 boards.
//
//noinspection GoUnusedExportedFunction
func SetupGpio() int {
	return internalSetupGpio()
}

// Identical to above, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
//
// As above, this function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func SetupPhys() int {
	return internalSetupPhys()
}

// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be
// aware of the differences between Rev 1 and Rev 2 boards.
//
// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface
// before you run your program. You can do this in a separate shell-script, or by using the system() function
// from inside your program to call the gpio program.
//
//Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)
//
//noinspection GoUnusedExportedFunction
func SetupSys() int {
	return internalSetupSys()
}

// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. Note that only wiringPi pin 1
// (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) supports CLOCK output modes.
//
// This function has no effect when in Sys mode. If you need to change the pin mode, then you can do it with the
// gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PinMode(pin int, mode int) {
	internalPinMode(pin, mode)
}

// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. Unlike the
// Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
//
// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. If you need to activate a
// pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PullUpDnControl(pin int, pud int) {
	internalPullUpDnControl(pin, pud)
}

//Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
//
//WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
//
//noinspection GoUnusedExportedFunction
func DigitalWrite(pin int, mode int) {
	internalDigitalWrite(pin, mode)
}

// Writes the value to the PWM register for the given pin. The Raspberry Pi has one on-board PWM pin, pin 1
// (BMC_GPIO 18, Phys 12) and the range is 0-1024. Other PWM devices may have other PWM ranges.
//
// This function is not able to control the Pi’s on-board PWM when in Sys mode.
//
//noinspection GoUnusedExportedFunction
func PwmWrite(pin int, value int) {
	internalPwmWrite(pin, value)
}

//noinspection GoUnusedExportedFunction
func DigitalRead(pin int) int {
	return internalDigitalRead(pin)
}

//noinspection GoUnusedExportedFunction
func DigitalReadStr(pin int) string {
	if internalDigitalRead(pin) == LOW {
		return "LOW"
	}
	return "HIGH"
}

func GetMode(pin int) int {
	return internalGetMode(pin)
}

//noinspection GoUnusedExportedFunction
func GetModeStr(pin int) string {
	var mode = internalGetMode(pin)

	if mode > len(gpioModes) {
		return "INVALID"
	}

	return gpioModes[GetMode(pin)]
}

//noinspection GoUnusedExportedFunction
func Delay(ms int) {
	internalDelay(ms)
}

//noinspection GoUnusedExportedFunction
func DelayMicroseconds(microSec int) {
	internalDelayMicroseconds(microSec)
}

//noinspection GoUnusedExportedFunction
func WiringISR(pin int, mode int) chan int {
	return internalWiringISR(pin, mode)
}

//noinspection GoUnusedExportedFunction
func IsRaspberryPi() bool {
	_, err := os.Stat("/opt/vc/include/bcm_host.h")
	return !os.IsNotExist(err)
}

//noinspection GoUnusedExportedFunction
func SetupI2C(devId int) int {
	return internalSetupI2C(devId)
}

//noinspection GoUnusedExportedFunction
func I2cRead(fd int) int {
	return internalI2CRead(fd)
}

//noinspection GoUnusedExportedFunction
func MonotonicTime() uint64 {
	return uint64(C.monotonic_time())
}

//noinspection GoUnusedExportedFunction
func ConvertMonotonicTimeToUSec(time uint64) uint64 {
	return time / 1000
}
<commit_msg>Trying to check the godebug.<commit_after>// **********************************************************************
//    Copyright (c) 2017 Henry Seurer
//
//   Permission is hereby granted, free of charge, to any person
//    obtaining a copy of this software and associated documentation
//    files (the "Software"), to deal in the Software without
//    restriction, including without limitation the rights to use,
//    copy, modify, merge, publish, distribute, sublicense, and/or sell
//    copies of the Software, and to permit persons to whom the
//    Software is furnished to do so, subject to the following
//    conditions:
//
//   The above copyright notice and this permission notice shall be
//   included in all copies or substantial portions of the Software.
//
//    THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
//    EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
//    OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
//    NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
//    HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
//    WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
//    FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
//    OTHER DEALINGS IN THE SOFTWARE.
//
// **********************************************************************

package wiringpi

/*
#include <time.h>

unsigned long long as_nanoseconds(struct timespec* ts) {
    return ts->tv_sec * (unsigned long long)1000000000L + ts->tv_nsec;
}

unsigned long long monotonic_time() {
    struct timespec last_t;
    clock_gettime(CLOCK_MONOTONIC, &last_t);
    return as_nanoseconds(&last_t);
}
*/
import "C"

import (
	"fmt"
	"os"
)

var (
	board2pin = []int{
		-1,
		-1,
		-1,
		8,
		-1,
		9,
		-1,
		7,
		15,
		-1,
		16,
		0,
		1,
		2,
		-1,
		-1,
		4,
		-1,
		5,
		12,
		-1,
		13,
		6,
		14,
		10,
		-1,
		11,
	}
	gpio2pin = []int{
		8,
		9,
		-1,
		-1,
		7,
		-1,
		-1,
		11,
		10,
		13,
		12,
		14,
		-1,
		-1,
		15,
		16,
		-1,
		0,
		1,
		-1,
		-1,
		2,
		3,
		4,
		5,
		6,
		-1,
		-1,
		17,
		18,
		19,
		20,
	}

	gpioModes = []string{"IN", "OUT", "ALT5", "ALT4", "ALT0", "ALT1", "ALT2", "ALT3"}
)

//noinspection GoUnusedConst
const (
	PIN_GPIO_0  = 0
	PIN_GPIO_1  = 1
	PIN_GPIO_2  = 2
	PIN_GPIO_3  = 3
	PIN_GPIO_4  = 4
	PIN_GPIO_5  = 5
	PIN_GPIO_6  = 6
	PIN_GPIO_7  = 7
	PIN_SDA     = 8
	PIN_SCL     = 9
	PIN_CE0     = 10
	PIN_CE1     = 11
	PIN_MOSI    = 12
	PIN_MOSO    = 13
	PIN_SCLK    = 14
	PIN_TXD     = 15
	PIN_RXD     = 16
	PIN_GPIO_8  = 17
	PIN_GPIO_9  = 18
	PIN_GPIO_10 = 19
	PIN_GPIO_11 = 20

	MODE_IN   = 0
	MODE_OUT  = 1
	MODE_ALT5 = 2
	MODE_ALT4 = 3
	MODE_ALT0 = 4
	MODE_ALT1 = 5
	MODE_ALT2 = 6
	MODE_ALT3 = 7
)

//use RPi.GPIO's BOARD numbering
//noinspection GoUnusedExportedFunction
func BoardToPin(pin int) int {
	if pin < 1 || pin >= len(board2pin) {
		panic(fmt.Sprintf("Invalid board pin number: %d", pin))
	}
	return board2pin[pin]
}

//noinspection GoUnusedExportedFunction
func GpioToPin(pin int) int {
	if pin < 0 || pin >= len(gpio2pin) {
		panic(fmt.Sprintf("Invalid bcm gpio number: %d", pin))
	}
	return gpio2pin[pin]
}

//noinspection GoUnusedExportedFunction
func PinToGpio(pin int) int {
	return internalPinToGpio(pin)
}

// This initialises wiringPi and assumes that the calling program is going to be using the wiringPi pin numbering scheme.
// This is a simplified numbering scheme which provides a mapping from virtual pin numbers 0 through 16 to the real
// underlying Broadcom GPIO pin numbers. See the pins page for a table which maps the wiringPi pin number to the
// Broadcom GPIO pin number to the physical location on the edge connector.
//
// This function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func Setup() int {
	return internalSetup()
}

//This is identical to above, however it allows the calling programs to use the Broadcom GPIO pin numbers
// directly with no re-mapping.
//
// As above, this function needs to be called with root privileges, and note that some pins are different
// from revision 1 to revision 2 boards.
//
//noinspection GoUnusedExportedFunction
func SetupGpio() int {
	return internalSetupGpio()
}

// Identical to above, however it allows the calling programs to use the physical pin numbers on the P1 connector only.
//
// As above, this function needs to be called with root privileges.
//
//noinspection GoUnusedExportedFunction
func SetupPhys() int {
	return internalSetupPhys()
}

// This initialises wiringPi but uses the /sys/class/gpio interface rather than accessing the hardware directly.
// This can be called as a non-root user provided the GPIO pins have been exported before-hand using the gpio program.
// Pin numbering in this mode is the native Broadcom GPIO numbers – the same as wiringPiSetupGpio() above, so be
// aware of the differences between Rev 1 and Rev 2 boards.
//
// Note: In this mode you can only use the pins which have been exported via the /sys/class/gpio interface
// before you run your program. You can do this in a separate shell-script, or by using the system() function
// from inside your program to call the gpio program.
//
//Also note that some functions have no effect when using this mode as they’re not currently possible to action unless called with root privileges. (although you can use system() to call gpio to set/change modes if needed)
//
//noinspection GoUnusedExportedFunction
func SetupSys() int {
	return internalSetupSys()
}

// This sets the mode of a pin to either INPUT, OUTPUT, PWM_OUTPUT or GPIO_CLOCK. Note that only wiringPi pin 1
// (BCM_GPIO 18) supports PWM output and only wiringPi pin 7 (BCM_GPIO 4) supports CLOCK output modes.
//
// This function has no effect when in Sys mode. If you need to change the pin mode, then you can do it with the
// gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PinMode(pin int, mode int) {
	internalPinMode(pin, mode)
}

// This sets the pull-up or pull-down resistor mode on the given pin, which should be set as an input. Unlike the
// Arduino, the BCM2835 has both pull-up an down internal resistors. The parameter pud should be; PUD_OFF, (no pull up/down), PUD_DOWN (pull to ground) or PUD_UP (pull to 3.3v) The internal pull up/down resistors have a value of approximately 50KΩ on the Raspberry Pi.
//
// This function has no effect on the Raspberry Pi’s GPIO pins when in Sys mode. If you need to activate a
// pull-up/pull-down, then you can do it with the gpio program in a script before you start your program.
//
//noinspection GoUnusedExportedFunction
func PullUpDnControl(pin int, pud int) {
	internalPullUpDnControl(pin, pud)
}

//Writes the value HIGH or LOW (1 or 0) to the given pin which must have been previously set as an output.
//
//WiringPi treats any non-zero number as HIGH, however 0 is the only representation of LOW.
//
//noinspection GoUnusedExportedFunction
func DigitalWrite(pin int, mode int) {
	internalDigitalWrite(pin, mode)
}

// Writes the value to the PWM register for the given pin. The Raspberry Pi has one on-board PWM pin, pin 1
// (BMC_GPIO 18, Phys 12) and the range is 0-1024. Other PWM devices may have other PWM ranges.
//
// This function is not able to control the Pi’s on-board PWM when in Sys mode.
//
//noinspection GoUnusedExportedFunction
func PwmWrite(pin int, value int) {
	internalPwmWrite(pin, value)
}

//noinspection GoUnusedExportedFunction
func DigitalRead(pin int) int {
	return internalDigitalRead(pin)
}

//noinspection GoUnusedExportedFunction
func DigitalReadStr(pin int) string {
	if internalDigitalRead(pin) == LOW {
		return "LOW"
	}
	return "HIGH"
}

func GetMode(pin int) int {
	return internalGetMode(pin)
}

//noinspection GoUnusedExportedFunction
func GetModeStr(pin int) string {
	var mode = internalGetMode(pin)

	if mode > len(gpioModes) {
		return "INVALID"
	}

	return gpioModes[GetMode(pin)]
}

//noinspection GoUnusedExportedFunction
func Delay(ms int) {
	internalDelay(ms)
}

//noinspection GoUnusedExportedFunction
func DelayMicroseconds(microSec int) {
	internalDelayMicroseconds(microSec)
}

//noinspection GoUnusedExportedFunction
func WiringISR(pin int, mode int) chan int {
	return internalWiringISR(pin, mode)
}

//noinspection GoUnusedExportedFunction
func IsRaspberryPi() bool {
	_, err := os.Stat("/opt/vc/include/bcm_host.h")
	return !os.IsNotExist(err)
}

//noinspection GoUnusedExportedFunction
func SetupI2C(devId int) int {
	return internalSetupI2C(devId)
}

//noinspection GoUnusedExportedFunction
func I2cRead(fd int) int {
	return internalI2CRead(fd)
}

//noinspection GoUnusedExportedFunction
func MonotonicTime() uint64 {
	return uint64(C.monotonic_time())
}

//noinspection GoUnusedExportedFunction
func ConvertMonotonicTimeToUSec(time uint64) uint64 {
	return time / 1000
}
<|endoftext|>


<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ca

import (
	"encoding/json"
	"fmt"
	google_protobuf "google/protobuf"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"testing"
	"time"

	"github.com/spf13/viper"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"

	"github.com/hyperledger/fabric/consensus/helper"
	"github.com/hyperledger/fabric/core"
	"github.com/hyperledger/fabric/core/chaincode"
	"github.com/hyperledger/fabric/core/crypto"
	"github.com/hyperledger/fabric/core/ledger"
	"github.com/hyperledger/fabric/core/ledger/genesis"
	"github.com/hyperledger/fabric/core/peer"
	"github.com/hyperledger/fabric/core/rest"
	pb "github.com/hyperledger/fabric/protos"
)

var (
	tca    *TCA
	eca    *ECA
	server *grpc.Server
)

type ValidityPeriod struct {
	Name  string
	Value string
}

func TestMain(m *testing.M) {
	setupTestConfig()
	os.Exit(m.Run())
}

func setupTestConfig() {
	viper.AutomaticEnv()
	viper.SetConfigName("ca_test") // name of config file (without extension)
	viper.AddConfigPath("./")      // path to look for the config file in
	viper.AddConfigPath("./..")    // path to look for the config file in
	err := viper.ReadInConfig()    // Find and read the config file
	if err != nil {                // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
}

func TestValidityPeriod(t *testing.T) {
	t.Skip()
	var updateInterval int64
	updateInterval = 37

	// 1. Start TCA and Openchain...
	go startServices(t)

	// ... and wait just let the services finish the startup
	time.Sleep(time.Second * 240)

	// 2. Obtain the validity period by querying and directly from the ledger
	validityPeriodA := queryValidityPeriod(t)
	validityPeriodFromLedgerA := getValidityPeriodFromLedger(t)

	// 3. Wait for the validity period to be updated...
	time.Sleep(time.Second * 40)

	// ... and read the values again
	validityPeriodB := queryValidityPeriod(t)
	validityPeriodFromLedgerB := getValidityPeriodFromLedger(t)

	// 5. Stop TCA and Openchain
	stopServices(t)

	// 6. Compare the values
	if validityPeriodA != validityPeriodFromLedgerA {
		t.Logf("Validity period read from ledger must be equals tothe one obtained by querying the Openchain. Expected: %d, Actual: %d", validityPeriodA, validityPeriodFromLedgerA)
		t.Fail()
	}

	if validityPeriodB != validityPeriodFromLedgerB {
		t.Logf("Validity period read from ledger must be equals tothe one obtained by querying the Openchain. Expected: %d, Actual: %d", validityPeriodB, validityPeriodFromLedgerB)
		t.Fail()
	}

	if validityPeriodB-validityPeriodA != updateInterval {
		t.Logf("Validity period difference must be equal to the update interval. Expected: %d, Actual: %d", updateInterval, validityPeriodB-validityPeriodA)
		t.Fail()
	}

	// 7. since the validity period is used as time in the validators convert both validity periods to Unix time and compare them
	vpA := time.Unix(validityPeriodFromLedgerA, 0)
	vpB := time.Unix(validityPeriodFromLedgerB, 0)

	nextVP := vpA.Add(time.Second * 37)
	if !vpB.Equal(nextVP) {
		t.Logf("Validity period difference must be equal to the update interval. Error converting validity period to Unix time.")
		t.Fail()
	}

	// 8. cleanup tca and openchain folders
	if err := os.RemoveAll(viper.GetString("peer.fileSystemPath")); err != nil {
		t.Logf("Failed removing [%s] [%s]\n", viper.GetString("peer.fileSystemPath"), err)
	}
	if err := os.RemoveAll(".ca"); err != nil {
		t.Logf("Failed removing [%s] [%s]\n", ".ca", err)
	}
}

func startServices(t *testing.T) {
	go startTCA()
	err := startOpenchain(t)
	if err != nil {
		t.Logf("Error starting Openchain: %s", err)
		t.Fail()
	}
}

func stopServices(t *testing.T) {
	stopOpenchain(t)
	stopTCA()
}

func startTCA() {
	LogInit(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr, os.Stdout)

	eca = NewECA()
	defer eca.Close()

	tca = NewTCA(eca)
	defer tca.Close()

	sockp, err := net.Listen("tcp", viper.GetString("server.port"))
	if err != nil {
		panic("Cannot open port: " + err.Error())
	}

	server = grpc.NewServer()

	eca.Start(server)
	tca.Start(server)

	server.Serve(sockp)
}

func stopTCA() {
	eca.Close()
	tca.Close()
	server.Stop()
}

func queryValidityPeriod(t *testing.T) int64 {
	hash := viper.GetString("pki.validity-period.chaincodeHash")
	args := []string{"system.validity.period"}

	validityPeriod, err := queryTransaction(hash, args, t)
	if err != nil {
		t.Logf("Failed querying validity period: %s", err)
		t.Fail()
	}

	var vp ValidityPeriod
	json.Unmarshal(validityPeriod, &vp)

	value, err := strconv.ParseInt(vp.Value, 10, 64)
	if err != nil {
		t.Logf("Failed parsing validity period: %s", err)
		t.Fail()
	}

	return value
}

func getValidityPeriodFromLedger(t *testing.T) int64 {
	cid := viper.GetString("pki.validity-period.chaincodeHash")

	ledger, err := ledger.GetLedger()
	if err != nil {
		t.Logf("Failed getting access to the ledger: %s", err)
		t.Fail()
	}

	vpBytes, err := ledger.GetState(cid, "system.validity.period", true)
	if err != nil {
		t.Logf("Failed reading validity period from the ledger: %s", err)
		t.Fail()
	}

	i, err := strconv.ParseInt(string(vpBytes[:]), 10, 64)
	if err != nil {
		t.Logf("Failed to parse validity period: %s", err)
		t.Fail()
	}

	return i
}

func queryTransaction(hash string, args []string, t *testing.T) ([]byte, error) {

	chaincodeInvocationSpec := createChaincodeInvocationForQuery(args, hash, "system_chaincode_invoker")

	fmt.Printf("Going to query\n")

	response, err := queryChaincode(chaincodeInvocationSpec, t)

	if err != nil {
		return nil, fmt.Errorf("Error querying <%s>: %s", "validity period", err)
	}

	t.Logf("Successfully invoked validity period update: %s", string(response.Msg))

	return response.Msg, nil
}

func queryChaincode(chaincodeInvSpec *pb.ChaincodeInvocationSpec, t *testing.T) (*pb.Response, error) {

	devopsClient, err := getDevopsClient(viper.GetString("pki.validity-period.devops-address"))

	if err != nil {
		return nil, fmt.Errorf("Error retrieving devops client: %s", err)
	}

	resp, err := devopsClient.Query(context.Background(), chaincodeInvSpec)

	if err != nil {
		return nil, fmt.Errorf("Error invoking validity period update system chaincode: %s", err)
	}

	t.Logf("Successfully invoked validity period update: %v(%s)", chaincodeInvSpec, string(resp.Msg))

	return resp, nil
}

func createChaincodeInvocationForQuery(arguments []string, chaincodeHash string, token string) *pb.ChaincodeInvocationSpec {
	spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG,
		ChaincodeID: &pb.ChaincodeID{Name: chaincodeHash},
		CtorMsg: &pb.ChaincodeInput{Function: "query",
			Args: arguments,
		},
	}

	spec.SecureContext = string(token)

	invocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}

	return invocationSpec
}

func getSecHelper() (crypto.Peer, error) {
	var secHelper crypto.Peer
	var err error
	if viper.GetBool("security.enabled") {
		enrollID := viper.GetString("security.enrollID")
		enrollSecret := viper.GetString("security.enrollSecret")
		if viper.GetBool("peer.validator.enabled") {
			if err = crypto.RegisterValidator(enrollID, nil, enrollID, enrollSecret); nil != err {
				return nil, err
			}
			secHelper, err = crypto.InitValidator(enrollID, nil)
			if nil != err {
				return nil, err
			}
		} else {
			if err = crypto.RegisterPeer(enrollID, nil, enrollID, enrollSecret); nil != err {
				return nil, err
			}
			secHelper, err = crypto.InitPeer(enrollID, nil)
			if nil != err {
				return nil, err
			}
		}
	}
	return secHelper, err
}

func startOpenchain(t *testing.T) error {

	peerEndpoint, err := peer.GetPeerEndpoint()
	if err != nil {
		return fmt.Errorf("Failed to get Peer Endpoint: %s", err)
	}

	listenAddr := viper.GetString("peer.listenaddress")

	if "" == listenAddr {
		t.Log("Listen address not specified, using peer endpoint address")
		listenAddr = peerEndpoint.Address
	}

	lis, err := net.Listen("tcp", listenAddr)
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	t.Logf("Security enabled status: %t", viper.GetBool("security.enabled"))

	var opts []grpc.ServerOption
	if viper.GetBool("peer.tls.enabled") {
		creds, err := credentials.NewServerTLSFromFile(viper.GetString("peer.tls.cert.file"), viper.GetString("peer.tls.key.file"))
		if err != nil {
			grpclog.Fatalf("Failed to generate credentials %v", err)
		}
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}

	grpcServer := grpc.NewServer(opts...)

	// Register the Peer server
	var peerServer *peer.PeerImpl

	secHelper, err := getSecHelper()
	if err != nil {
		return err
	}

	secHelperFunc := func() crypto.Peer {
		return secHelper
	}

	if viper.GetBool("peer.validator.enabled") {
		t.Logf("Running as validating peer - installing consensus %s", viper.GetString("peer.validator.consensus"))
		peerServer, _ = peer.NewPeerWithHandler(secHelperFunc, helper.NewConsensusHandler)
	} else {
		t.Log("Running as non-validating peer")
		peerServer, _ = peer.NewPeerWithHandler(secHelperFunc, peer.NewPeerHandler)
	}
	pb.RegisterPeerServer(grpcServer, peerServer)

	// Register the Admin server
	pb.RegisterAdminServer(grpcServer, core.NewAdminServer())

	// Register ChaincodeSupport server...
	// TODO : not the "DefaultChain" ... we have to revisit when we do multichain

	registerChaincodeSupport(chaincode.DefaultChain, grpcServer, secHelper)

	// Register Devops server
	serverDevops := core.NewDevopsServer(peerServer)
	pb.RegisterDevopsServer(grpcServer, serverDevops)

	// Register the ServerOpenchain server
	serverOpenchain, err := rest.NewOpenchainServer()
	if err != nil {
		return fmt.Errorf("Error creating OpenchainServer: %s", err)
	}

	pb.RegisterOpenchainServer(grpcServer, serverOpenchain)

	// Create and register the REST service
	go rest.StartOpenchainRESTServer(serverOpenchain, serverDevops)

	rootNode, err := core.GetRootNode()
	if err != nil {
		grpclog.Fatalf("Failed to get peer.discovery.rootnode valey: %s", err)
	}

	t.Logf("Starting peer with id=%s, network id=%s, address=%s, discovery.rootnode=%s, validator=%v",
		peerEndpoint.ID, viper.GetString("peer.networkId"),
		peerEndpoint.Address, rootNode, viper.GetBool("peer.validator.enabled"))

	// Start the grpc server. Done in a goroutine so we can deploy the
	// genesis block if needed.
	serve := make(chan bool)
	go func() {
		grpcServer.Serve(lis)
		serve <- true
	}()

	// Deploy the geneis block if needed.
	if viper.GetBool("peer.validator.enabled") {
		makeGeneisError := genesis.MakeGenesis()
		if makeGeneisError != nil {
			return makeGeneisError
		}
	}

	// Block until grpc server exits
	<-serve

	return nil
}

func stopOpenchain(t *testing.T) {
	clientConn, err := peer.NewPeerClientConnection()
	if err != nil {
		t.Log(fmt.Errorf("Error trying to connect to local peer: %v", err))
		t.Fail()
	}

	t.Log("Stopping peer...")
	serverClient := pb.NewAdminClient(clientConn)

	status, err := serverClient.StopServer(context.Background(), &google_protobuf.Empty{})
	t.Logf("Current status: %s", status)

}

func registerChaincodeSupport(chainname chaincode.ChainName, grpcServer *grpc.Server, secHelper crypto.Peer) {
	//get user mode
	userRunsCC := false
	if viper.GetString("chaincode.mode") == chaincode.DevModeUserRunsChaincode {
		userRunsCC = true
	}

	//get chaincode startup timeout
	tOut, err := strconv.Atoi(viper.GetString("chaincode.startuptimeout"))
	if err != nil { //what went wrong ?
		fmt.Printf("could not retrive timeout var...setting to 5secs\n")
		tOut = 5000
	}
	ccStartupTimeout := time.Duration(tOut) * time.Millisecond

	//(chainname ChainName, getPeerEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration, secHelper crypto.Peer)
	pb.RegisterChaincodeSupportServer(grpcServer, chaincode.NewChaincodeSupport(chainname, peer.GetPeerEndpoint, userRunsCC, ccStartupTimeout, secHelper))
}
<commit_msg>Fix 1671 Use the err returned from StopServer()<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

		 http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package ca

import (
	"encoding/json"
	"fmt"
	google_protobuf "google/protobuf"
	"io/ioutil"
	"net"
	"os"
	"strconv"
	"testing"
	"time"

	"github.com/spf13/viper"
	"golang.org/x/net/context"
	"google.golang.org/grpc"
	"google.golang.org/grpc/credentials"
	"google.golang.org/grpc/grpclog"

	"github.com/hyperledger/fabric/consensus/helper"
	"github.com/hyperledger/fabric/core"
	"github.com/hyperledger/fabric/core/chaincode"
	"github.com/hyperledger/fabric/core/crypto"
	"github.com/hyperledger/fabric/core/ledger"
	"github.com/hyperledger/fabric/core/ledger/genesis"
	"github.com/hyperledger/fabric/core/peer"
	"github.com/hyperledger/fabric/core/rest"
	pb "github.com/hyperledger/fabric/protos"
)

var (
	tca    *TCA
	eca    *ECA
	server *grpc.Server
)

type ValidityPeriod struct {
	Name  string
	Value string
}

func TestMain(m *testing.M) {
	setupTestConfig()
	os.Exit(m.Run())
}

func setupTestConfig() {
	viper.AutomaticEnv()
	viper.SetConfigName("ca_test") // name of config file (without extension)
	viper.AddConfigPath("./")      // path to look for the config file in
	viper.AddConfigPath("./..")    // path to look for the config file in
	err := viper.ReadInConfig()    // Find and read the config file
	if err != nil {                // Handle errors reading the config file
		panic(fmt.Errorf("Fatal error config file: %s \n", err))
	}
}

func TestValidityPeriod(t *testing.T) {
	t.Skip()
	var updateInterval int64
	updateInterval = 37

	// 1. Start TCA and Openchain...
	go startServices(t)

	// ... and wait just let the services finish the startup
	time.Sleep(time.Second * 240)

	// 2. Obtain the validity period by querying and directly from the ledger
	validityPeriodA := queryValidityPeriod(t)
	validityPeriodFromLedgerA := getValidityPeriodFromLedger(t)

	// 3. Wait for the validity period to be updated...
	time.Sleep(time.Second * 40)

	// ... and read the values again
	validityPeriodB := queryValidityPeriod(t)
	validityPeriodFromLedgerB := getValidityPeriodFromLedger(t)

	// 5. Stop TCA and Openchain
	stopServices(t)

	// 6. Compare the values
	if validityPeriodA != validityPeriodFromLedgerA {
		t.Logf("Validity period read from ledger must be equals tothe one obtained by querying the Openchain. Expected: %d, Actual: %d", validityPeriodA, validityPeriodFromLedgerA)
		t.Fail()
	}

	if validityPeriodB != validityPeriodFromLedgerB {
		t.Logf("Validity period read from ledger must be equals tothe one obtained by querying the Openchain. Expected: %d, Actual: %d", validityPeriodB, validityPeriodFromLedgerB)
		t.Fail()
	}

	if validityPeriodB-validityPeriodA != updateInterval {
		t.Logf("Validity period difference must be equal to the update interval. Expected: %d, Actual: %d", updateInterval, validityPeriodB-validityPeriodA)
		t.Fail()
	}

	// 7. since the validity period is used as time in the validators convert both validity periods to Unix time and compare them
	vpA := time.Unix(validityPeriodFromLedgerA, 0)
	vpB := time.Unix(validityPeriodFromLedgerB, 0)

	nextVP := vpA.Add(time.Second * 37)
	if !vpB.Equal(nextVP) {
		t.Logf("Validity period difference must be equal to the update interval. Error converting validity period to Unix time.")
		t.Fail()
	}

	// 8. cleanup tca and openchain folders
	if err := os.RemoveAll(viper.GetString("peer.fileSystemPath")); err != nil {
		t.Logf("Failed removing [%s] [%s]\n", viper.GetString("peer.fileSystemPath"), err)
	}
	if err := os.RemoveAll(".ca"); err != nil {
		t.Logf("Failed removing [%s] [%s]\n", ".ca", err)
	}
}

func startServices(t *testing.T) {
	go startTCA()
	err := startOpenchain(t)
	if err != nil {
		t.Logf("Error starting Openchain: %s", err)
		t.Fail()
	}
}

func stopServices(t *testing.T) {
	stopOpenchain(t)
	stopTCA()
}

func startTCA() {
	LogInit(ioutil.Discard, os.Stdout, os.Stdout, os.Stderr, os.Stdout)

	eca = NewECA()
	defer eca.Close()

	tca = NewTCA(eca)
	defer tca.Close()

	sockp, err := net.Listen("tcp", viper.GetString("server.port"))
	if err != nil {
		panic("Cannot open port: " + err.Error())
	}

	server = grpc.NewServer()

	eca.Start(server)
	tca.Start(server)

	server.Serve(sockp)
}

func stopTCA() {
	eca.Close()
	tca.Close()
	server.Stop()
}

func queryValidityPeriod(t *testing.T) int64 {
	hash := viper.GetString("pki.validity-period.chaincodeHash")
	args := []string{"system.validity.period"}

	validityPeriod, err := queryTransaction(hash, args, t)
	if err != nil {
		t.Logf("Failed querying validity period: %s", err)
		t.Fail()
	}

	var vp ValidityPeriod
	json.Unmarshal(validityPeriod, &vp)

	value, err := strconv.ParseInt(vp.Value, 10, 64)
	if err != nil {
		t.Logf("Failed parsing validity period: %s", err)
		t.Fail()
	}

	return value
}

func getValidityPeriodFromLedger(t *testing.T) int64 {
	cid := viper.GetString("pki.validity-period.chaincodeHash")

	ledger, err := ledger.GetLedger()
	if err != nil {
		t.Logf("Failed getting access to the ledger: %s", err)
		t.Fail()
	}

	vpBytes, err := ledger.GetState(cid, "system.validity.period", true)
	if err != nil {
		t.Logf("Failed reading validity period from the ledger: %s", err)
		t.Fail()
	}

	i, err := strconv.ParseInt(string(vpBytes[:]), 10, 64)
	if err != nil {
		t.Logf("Failed to parse validity period: %s", err)
		t.Fail()
	}

	return i
}

func queryTransaction(hash string, args []string, t *testing.T) ([]byte, error) {

	chaincodeInvocationSpec := createChaincodeInvocationForQuery(args, hash, "system_chaincode_invoker")

	fmt.Printf("Going to query\n")

	response, err := queryChaincode(chaincodeInvocationSpec, t)

	if err != nil {
		return nil, fmt.Errorf("Error querying <%s>: %s", "validity period", err)
	}

	t.Logf("Successfully invoked validity period update: %s", string(response.Msg))

	return response.Msg, nil
}

func queryChaincode(chaincodeInvSpec *pb.ChaincodeInvocationSpec, t *testing.T) (*pb.Response, error) {

	devopsClient, err := getDevopsClient(viper.GetString("pki.validity-period.devops-address"))

	if err != nil {
		return nil, fmt.Errorf("Error retrieving devops client: %s", err)
	}

	resp, err := devopsClient.Query(context.Background(), chaincodeInvSpec)

	if err != nil {
		return nil, fmt.Errorf("Error invoking validity period update system chaincode: %s", err)
	}

	t.Logf("Successfully invoked validity period update: %v(%s)", chaincodeInvSpec, string(resp.Msg))

	return resp, nil
}

func createChaincodeInvocationForQuery(arguments []string, chaincodeHash string, token string) *pb.ChaincodeInvocationSpec {
	spec := &pb.ChaincodeSpec{Type: pb.ChaincodeSpec_GOLANG,
		ChaincodeID: &pb.ChaincodeID{Name: chaincodeHash},
		CtorMsg: &pb.ChaincodeInput{Function: "query",
			Args: arguments,
		},
	}

	spec.SecureContext = string(token)

	invocationSpec := &pb.ChaincodeInvocationSpec{ChaincodeSpec: spec}

	return invocationSpec
}

func getSecHelper() (crypto.Peer, error) {
	var secHelper crypto.Peer
	var err error
	if viper.GetBool("security.enabled") {
		enrollID := viper.GetString("security.enrollID")
		enrollSecret := viper.GetString("security.enrollSecret")
		if viper.GetBool("peer.validator.enabled") {
			if err = crypto.RegisterValidator(enrollID, nil, enrollID, enrollSecret); nil != err {
				return nil, err
			}
			secHelper, err = crypto.InitValidator(enrollID, nil)
			if nil != err {
				return nil, err
			}
		} else {
			if err = crypto.RegisterPeer(enrollID, nil, enrollID, enrollSecret); nil != err {
				return nil, err
			}
			secHelper, err = crypto.InitPeer(enrollID, nil)
			if nil != err {
				return nil, err
			}
		}
	}
	return secHelper, err
}

func startOpenchain(t *testing.T) error {

	peerEndpoint, err := peer.GetPeerEndpoint()
	if err != nil {
		return fmt.Errorf("Failed to get Peer Endpoint: %s", err)
	}

	listenAddr := viper.GetString("peer.listenaddress")

	if "" == listenAddr {
		t.Log("Listen address not specified, using peer endpoint address")
		listenAddr = peerEndpoint.Address
	}

	lis, err := net.Listen("tcp", listenAddr)
	if err != nil {
		grpclog.Fatalf("failed to listen: %v", err)
	}

	t.Logf("Security enabled status: %t", viper.GetBool("security.enabled"))

	var opts []grpc.ServerOption
	if viper.GetBool("peer.tls.enabled") {
		creds, err := credentials.NewServerTLSFromFile(viper.GetString("peer.tls.cert.file"), viper.GetString("peer.tls.key.file"))
		if err != nil {
			grpclog.Fatalf("Failed to generate credentials %v", err)
		}
		opts = []grpc.ServerOption{grpc.Creds(creds)}
	}

	grpcServer := grpc.NewServer(opts...)

	// Register the Peer server
	var peerServer *peer.PeerImpl

	secHelper, err := getSecHelper()
	if err != nil {
		return err
	}

	secHelperFunc := func() crypto.Peer {
		return secHelper
	}

	if viper.GetBool("peer.validator.enabled") {
		t.Logf("Running as validating peer - installing consensus %s", viper.GetString("peer.validator.consensus"))
		peerServer, _ = peer.NewPeerWithHandler(secHelperFunc, helper.NewConsensusHandler)
	} else {
		t.Log("Running as non-validating peer")
		peerServer, _ = peer.NewPeerWithHandler(secHelperFunc, peer.NewPeerHandler)
	}
	pb.RegisterPeerServer(grpcServer, peerServer)

	// Register the Admin server
	pb.RegisterAdminServer(grpcServer, core.NewAdminServer())

	// Register ChaincodeSupport server...
	// TODO : not the "DefaultChain" ... we have to revisit when we do multichain

	registerChaincodeSupport(chaincode.DefaultChain, grpcServer, secHelper)

	// Register Devops server
	serverDevops := core.NewDevopsServer(peerServer)
	pb.RegisterDevopsServer(grpcServer, serverDevops)

	// Register the ServerOpenchain server
	serverOpenchain, err := rest.NewOpenchainServer()
	if err != nil {
		return fmt.Errorf("Error creating OpenchainServer: %s", err)
	}

	pb.RegisterOpenchainServer(grpcServer, serverOpenchain)

	// Create and register the REST service
	go rest.StartOpenchainRESTServer(serverOpenchain, serverDevops)

	rootNode, err := core.GetRootNode()
	if err != nil {
		grpclog.Fatalf("Failed to get peer.discovery.rootnode valey: %s", err)
	}

	t.Logf("Starting peer with id=%s, network id=%s, address=%s, discovery.rootnode=%s, validator=%v",
		peerEndpoint.ID, viper.GetString("peer.networkId"),
		peerEndpoint.Address, rootNode, viper.GetBool("peer.validator.enabled"))

	// Start the grpc server. Done in a goroutine so we can deploy the
	// genesis block if needed.
	serve := make(chan bool)
	go func() {
		grpcServer.Serve(lis)
		serve <- true
	}()

	// Deploy the geneis block if needed.
	if viper.GetBool("peer.validator.enabled") {
		makeGeneisError := genesis.MakeGenesis()
		if makeGeneisError != nil {
			return makeGeneisError
		}
	}

	// Block until grpc server exits
	<-serve

	return nil
}

func stopOpenchain(t *testing.T) {
	clientConn, err := peer.NewPeerClientConnection()
	if err != nil {
		t.Log(fmt.Errorf("Error trying to connect to local peer: %v", err))
		t.Fail()
	}

	t.Log("Stopping peer...")
	serverClient := pb.NewAdminClient(clientConn)

	status, err := serverClient.StopServer(context.Background(), &google_protobuf.Empty{})
	if err != nil {
		t.Logf("Failed to stop: %v", err)
		t.Fail()
	}
	t.Logf("Current status: %s", status)
}

func registerChaincodeSupport(chainname chaincode.ChainName, grpcServer *grpc.Server, secHelper crypto.Peer) {
	//get user mode
	userRunsCC := false
	if viper.GetString("chaincode.mode") == chaincode.DevModeUserRunsChaincode {
		userRunsCC = true
	}

	//get chaincode startup timeout
	tOut, err := strconv.Atoi(viper.GetString("chaincode.startuptimeout"))
	if err != nil { //what went wrong ?
		fmt.Printf("could not retrive timeout var...setting to 5secs\n")
		tOut = 5000
	}
	ccStartupTimeout := time.Duration(tOut) * time.Millisecond

	//(chainname ChainName, getPeerEndpoint func() (*pb.PeerEndpoint, error), userrunsCC bool, ccstartuptimeout time.Duration, secHelper crypto.Peer)
	pb.RegisterChaincodeSupportServer(grpcServer, chaincode.NewChaincodeSupport(chainname, peer.GetPeerEndpoint, userRunsCC, ccStartupTimeout, secHelper))
}
<|endoftext|>