<commit_before>package chrootuser

import (
	"os/user"
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

var (
	// ErrNoSuchUser indicates that the user provided by the caller does not
	// exist in /etc/passws
	ErrNoSuchUser = errors.New("user does not exist in /etc/passwd")
)

// GetUser will return the uid, gid of the user specified in the userspec
// it will use the /etc/passwd and /etc/group files inside of the rootdir
// to return this information.
// userspec format [user | user:group | uid | uid:gid | user:gid | uid:group ]
func GetUser(rootdir, userspec string) (uint32, uint32, string, error) {
	var gid64 uint64
	var gerr error = user.UnknownGroupError("error looking up group")

	spec := strings.SplitN(userspec, ":", 2)
	userspec = spec[0]
	groupspec := ""
	if userspec == "" {
		return 0, 0, "/", nil
	}
	if len(spec) > 1 {
		groupspec = spec[1]
	}

	uid64, uerr := strconv.ParseUint(userspec, 10, 32)
	if uerr == nil && groupspec == "" {
		// We parsed the user name as a number, and there's no group
		// component, so try to look up the primary GID of the user who
		// has this UID.
		var name string
		name, gid64, gerr = lookupGroupForUIDInContainer(rootdir, uid64)
		if gerr == nil {
			userspec = name
		} else {
			// Leave userspec alone, but swallow the error and just
			// use GID 0.
			gid64 = 0
			gerr = nil
		}
	}
	if uerr != nil {
		// The user ID couldn't be parsed as a number, so try to look
		// up the user's UID and primary GID.
		uid64, gid64, uerr = lookupUserInContainer(rootdir, userspec)
		gerr = uerr
	}

	if groupspec != "" {
		// We have a group name or number, so parse it.
		gid64, gerr = strconv.ParseUint(groupspec, 10, 32)
		if gerr != nil {
			// The group couldn't be parsed as a number, so look up
			// the group's GID.
			gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
		}
	}

	homedir, err := lookupHomedirInContainer(rootdir, uid64)
	if err != nil {
		homedir = "/"
	}

	if uerr == nil && gerr == nil {
		return uint32(uid64), uint32(gid64), homedir, nil
	}

	err = errors.Wrapf(uerr, "error determining run uid")
	if uerr == nil {
		err = errors.Wrapf(gerr, "error determining run gid")
	}

	return 0, 0, homedir, err
}

// GetGroup returns the gid by looking it up in the /etc/group file
// groupspec format [ group | gid ]
func GetGroup(rootdir, groupspec string) (uint32, error) {
	gid64, gerr := strconv.ParseUint(groupspec, 10, 32)
	if gerr != nil {
		// The group couldn't be parsed as a number, so look up
		// the group's GID.
		gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
	}
	if gerr != nil {
		return 0, errors.Wrapf(gerr, "error looking up group for gid %q", groupspec)
	}
	return uint32(gid64), nil
}

// GetAdditionalGroupsForUser returns a list of gids that userid is associated with
func GetAdditionalGroupsForUser(rootdir string, userid uint64) ([]uint32, error) {
	gids, err := lookupAdditionalGroupsForUIDInContainer(rootdir, userid)
	if err != nil {
		return nil, errors.Wrapf(err, "error looking up supplemental groups for uid %d", userid)
	}
	return gids, nil
}

// LookupUIDInContainer returns username and gid associated with a UID in a container
// it will use the /etc/passwd files inside of the rootdir
// to return this information.
func LookupUIDInContainer(rootdir string, uid uint64) (user string, gid uint64, err error) {
	return lookupUIDInContainer(rootdir, uid)
}
<commit_msg>set HOME env to /root on chroot-isolation by default<commit_after>package chrootuser

import (
	"os/user"
	"strconv"
	"strings"

	"github.com/pkg/errors"
)

var (
	// ErrNoSuchUser indicates that the user provided by the caller does not
	// exist in /etc/passws
	ErrNoSuchUser = errors.New("user does not exist in /etc/passwd")
)

// GetUser will return the uid, gid of the user specified in the userspec
// it will use the /etc/passwd and /etc/group files inside of the rootdir
// to return this information.
// userspec format [user | user:group | uid | uid:gid | user:gid | uid:group ]
func GetUser(rootdir, userspec string) (uint32, uint32, string, error) {
	var gid64 uint64
	var gerr error = user.UnknownGroupError("error looking up group")

	spec := strings.SplitN(userspec, ":", 2)
	userspec = spec[0]
	groupspec := ""

	if userspec == "" {
		userspec = "0"
	}

	if len(spec) > 1 {
		groupspec = spec[1]
	}

	uid64, uerr := strconv.ParseUint(userspec, 10, 32)
	if uerr == nil && groupspec == "" {
		// We parsed the user name as a number, and there's no group
		// component, so try to look up the primary GID of the user who
		// has this UID.
		var name string
		name, gid64, gerr = lookupGroupForUIDInContainer(rootdir, uid64)
		if gerr == nil {
			userspec = name
		} else {
			// Leave userspec alone, but swallow the error and just
			// use GID 0.
			gid64 = 0
			gerr = nil
		}
	}
	if uerr != nil {
		// The user ID couldn't be parsed as a number, so try to look
		// up the user's UID and primary GID.
		uid64, gid64, uerr = lookupUserInContainer(rootdir, userspec)
		gerr = uerr
	}

	if groupspec != "" {
		// We have a group name or number, so parse it.
		gid64, gerr = strconv.ParseUint(groupspec, 10, 32)
		if gerr != nil {
			// The group couldn't be parsed as a number, so look up
			// the group's GID.
			gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
		}
	}

	homedir, err := lookupHomedirInContainer(rootdir, uid64)
	if err != nil {
		homedir = "/"
	}

	if uerr == nil && gerr == nil {
		return uint32(uid64), uint32(gid64), homedir, nil
	}

	err = errors.Wrapf(uerr, "error determining run uid")
	if uerr == nil {
		err = errors.Wrapf(gerr, "error determining run gid")
	}

	return 0, 0, homedir, err
}

// GetGroup returns the gid by looking it up in the /etc/group file
// groupspec format [ group | gid ]
func GetGroup(rootdir, groupspec string) (uint32, error) {
	gid64, gerr := strconv.ParseUint(groupspec, 10, 32)
	if gerr != nil {
		// The group couldn't be parsed as a number, so look up
		// the group's GID.
		gid64, gerr = lookupGroupInContainer(rootdir, groupspec)
	}
	if gerr != nil {
		return 0, errors.Wrapf(gerr, "error looking up group for gid %q", groupspec)
	}
	return uint32(gid64), nil
}

// GetAdditionalGroupsForUser returns a list of gids that userid is associated with
func GetAdditionalGroupsForUser(rootdir string, userid uint64) ([]uint32, error) {
	gids, err := lookupAdditionalGroupsForUIDInContainer(rootdir, userid)
	if err != nil {
		return nil, errors.Wrapf(err, "error looking up supplemental groups for uid %d", userid)
	}
	return gids, nil
}

// LookupUIDInContainer returns username and gid associated with a UID in a container
// it will use the /etc/passwd files inside of the rootdir
// to return this information.
func LookupUIDInContainer(rootdir string, uid uint64) (user string, gid uint64, err error) {
	return lookupUIDInContainer(rootdir, uid)
}
<|endoftext|>


<commit_before><commit_msg>Actually apply the environment variables<commit_after><|endoftext|>


<commit_before>package ingester

import (
	"encoding/binary"
	"fmt"
	"hash/fnv"
	"sync"
	"time"

	"github.com/cortexproject/cortex/pkg/util"
	"github.com/go-kit/kit/log/level"
	"github.com/grafana/loki/pkg/logproto"
	"github.com/grafana/loki/pkg/logql"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
)

const bufferSizeForTailResponse = 5

type tailer struct {
	id       uint32
	orgID    string
	matchers []*labels.Matcher
	filter   logql.Filter
	expr     logql.Expr

	sendChan chan *logproto.Stream

	// Signaling channel used to notify once the tailer gets closed
	// and the loop and senders should stop
	closeChan chan struct{}
	closeOnce sync.Once

	blockedAt      *time.Time
	blockedMtx     sync.RWMutex
	droppedStreams []*logproto.DroppedStream

	conn logproto.Querier_TailServer
}

func newTailer(orgID, query string, conn logproto.Querier_TailServer) (*tailer, error) {
	expr, err := logql.ParseLogSelector(query)
	if err != nil {
		return nil, err
	}
	filter, err := expr.Filter()
	if err != nil {
		return nil, err
	}
	matchers := expr.Matchers()

	return &tailer{
		orgID:          orgID,
		matchers:       matchers,
		filter:         filter,
		sendChan:       make(chan *logproto.Stream, bufferSizeForTailResponse),
		conn:           conn,
		droppedStreams: []*logproto.DroppedStream{},
		id:             generateUniqueID(orgID, query),
		closeChan:      make(chan struct{}),
		expr:           expr,
	}, nil
}

func (t *tailer) loop() {
	var stream *logproto.Stream
	var err error
	var ok bool

	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			err := t.conn.Context().Err()
			if err != nil {
				t.close()
				return
			}
		case <-t.closeChan:
			return
		case stream, ok = <-t.sendChan:
			if !ok {
				return
			} else if stream == nil {
				continue
			}

			// while sending new stream pop lined up dropped streams metadata for sending to querier
			tailResponse := logproto.TailResponse{Stream: stream, DroppedStreams: t.popDroppedStreams()}
			err = t.conn.Send(&tailResponse)
			if err != nil {
				level.Error(util.Logger).Log("Error writing to tail client", fmt.Sprintf("%v", err))
				t.close()
				return
			}
		}
	}
}

func (t *tailer) send(stream logproto.Stream) {
	if t.isClosed() {
		return
	}

	// if we are already dropping streams due to blocked connection, drop new streams directly to save some effort
	if blockedSince := t.blockedSince(); blockedSince != nil {
		if blockedSince.Before(time.Now().Add(-time.Second * 15)) {
			t.close()
			return
		}
		t.dropStream(stream)
		return
	}

	t.filterEntriesInStream(&stream)

	if len(stream.Entries) == 0 {
		return
	}

	select {
	case t.sendChan <- &stream:
	default:
		t.dropStream(stream)
	}
}

func (t *tailer) filterEntriesInStream(stream *logproto.Stream) {
	var filteredEntries []logproto.Entry
	for _, e := range stream.Entries {
		if t.filter == nil || t.filter([]byte(e.Line)) {
			filteredEntries = append(filteredEntries, e)
		}
	}
	stream.Entries = filteredEntries
}

// Returns true if tailer is interested in the passed labelset
func (t *tailer) isWatchingLabels(metric model.Metric) bool {
	for _, matcher := range t.matchers {
		if !matcher.Matches(string(metric[model.LabelName(matcher.Name)])) {
			return false
		}
	}

	return true
}

func (t *tailer) isClosed() bool {
	select {
	case <-t.closeChan:
		return true
	default:
		return false
	}
}

func (t *tailer) close() {
	t.closeOnce.Do(func() {
		// Signal the close channel
		close(t.closeChan)

		// We intentionally do not close sendChan in order to avoid a panic on
		// send to a just-closed channel. It's OK not to close a channel, since
		// it will be eventually garbage collected as soon as no goroutine
		// references it anymore, whether it has been closed or not.
	})
}

func (t *tailer) blockedSince() *time.Time {
	t.blockedMtx.RLock()
	defer t.blockedMtx.RUnlock()

	return t.blockedAt
}

func (t *tailer) dropStream(stream logproto.Stream) {
	if len(stream.Entries) == 0 {
		return
	}

	t.blockedMtx.Lock()
	defer t.blockedMtx.Unlock()

	if t.blockedAt == nil {
		blockedAt := time.Now()
		t.blockedAt = &blockedAt
	}
	droppedStream := logproto.DroppedStream{
		From:   stream.Entries[0].Timestamp,
		To:     stream.Entries[len(stream.Entries)-1].Timestamp,
		Labels: stream.Labels,
	}
	t.droppedStreams = append(t.droppedStreams, &droppedStream)
}

func (t *tailer) popDroppedStreams() []*logproto.DroppedStream {
	t.blockedMtx.Lock()
	defer t.blockedMtx.Unlock()

	if t.blockedAt == nil {
		return nil
	}

	droppedStreams := t.droppedStreams
	t.droppedStreams = []*logproto.DroppedStream{}
	t.blockedAt = nil

	return droppedStreams
}

func (t *tailer) getID() uint32 {
	return t.id
}

// An id is useful in managing tailer instances
func generateUniqueID(orgID, query string) uint32 {
	uniqueID := fnv.New32()
	_, _ = uniqueID.Write([]byte(orgID))
	_, _ = uniqueID.Write([]byte(query))

	timeNow := make([]byte, 8)
	binary.LittleEndian.PutUint64(timeNow, uint64(time.Now().UnixNano()))
	_, _ = uniqueID.Write(timeNow)

	return uniqueID.Sum32()
}
<commit_msg>Skip ingester tailer filtering if no filter is set<commit_after>package ingester

import (
	"encoding/binary"
	"fmt"
	"hash/fnv"
	"sync"
	"time"

	"github.com/cortexproject/cortex/pkg/util"
	"github.com/go-kit/kit/log/level"
	"github.com/grafana/loki/pkg/logproto"
	"github.com/grafana/loki/pkg/logql"
	"github.com/prometheus/common/model"
	"github.com/prometheus/prometheus/pkg/labels"
)

const bufferSizeForTailResponse = 5

type tailer struct {
	id       uint32
	orgID    string
	matchers []*labels.Matcher
	filter   logql.Filter
	expr     logql.Expr

	sendChan chan *logproto.Stream

	// Signaling channel used to notify once the tailer gets closed
	// and the loop and senders should stop
	closeChan chan struct{}
	closeOnce sync.Once

	blockedAt      *time.Time
	blockedMtx     sync.RWMutex
	droppedStreams []*logproto.DroppedStream

	conn logproto.Querier_TailServer
}

func newTailer(orgID, query string, conn logproto.Querier_TailServer) (*tailer, error) {
	expr, err := logql.ParseLogSelector(query)
	if err != nil {
		return nil, err
	}
	filter, err := expr.Filter()
	if err != nil {
		return nil, err
	}
	matchers := expr.Matchers()

	return &tailer{
		orgID:          orgID,
		matchers:       matchers,
		filter:         filter,
		sendChan:       make(chan *logproto.Stream, bufferSizeForTailResponse),
		conn:           conn,
		droppedStreams: []*logproto.DroppedStream{},
		id:             generateUniqueID(orgID, query),
		closeChan:      make(chan struct{}),
		expr:           expr,
	}, nil
}

func (t *tailer) loop() {
	var stream *logproto.Stream
	var err error
	var ok bool

	ticker := time.NewTicker(3 * time.Second)
	defer ticker.Stop()

	for {
		select {
		case <-ticker.C:
			err := t.conn.Context().Err()
			if err != nil {
				t.close()
				return
			}
		case <-t.closeChan:
			return
		case stream, ok = <-t.sendChan:
			if !ok {
				return
			} else if stream == nil {
				continue
			}

			// while sending new stream pop lined up dropped streams metadata for sending to querier
			tailResponse := logproto.TailResponse{Stream: stream, DroppedStreams: t.popDroppedStreams()}
			err = t.conn.Send(&tailResponse)
			if err != nil {
				level.Error(util.Logger).Log("Error writing to tail client", fmt.Sprintf("%v", err))
				t.close()
				return
			}
		}
	}
}

func (t *tailer) send(stream logproto.Stream) {
	if t.isClosed() {
		return
	}

	// if we are already dropping streams due to blocked connection, drop new streams directly to save some effort
	if blockedSince := t.blockedSince(); blockedSince != nil {
		if blockedSince.Before(time.Now().Add(-time.Second * 15)) {
			t.close()
			return
		}
		t.dropStream(stream)
		return
	}

	t.filterEntriesInStream(&stream)

	if len(stream.Entries) == 0 {
		return
	}

	select {
	case t.sendChan <- &stream:
	default:
		t.dropStream(stream)
	}
}

func (t *tailer) filterEntriesInStream(stream *logproto.Stream) {
	// Optimization: skip filtering entirely, if no filter is set
	if t.filter == nil {
		return
	}

	var filteredEntries []logproto.Entry
	for _, e := range stream.Entries {
		if t.filter([]byte(e.Line)) {
			filteredEntries = append(filteredEntries, e)
		}
	}
	stream.Entries = filteredEntries
}

// Returns true if tailer is interested in the passed labelset
func (t *tailer) isWatchingLabels(metric model.Metric) bool {
	for _, matcher := range t.matchers {
		if !matcher.Matches(string(metric[model.LabelName(matcher.Name)])) {
			return false
		}
	}

	return true
}

func (t *tailer) isClosed() bool {
	select {
	case <-t.closeChan:
		return true
	default:
		return false
	}
}

func (t *tailer) close() {
	t.closeOnce.Do(func() {
		// Signal the close channel
		close(t.closeChan)

		// We intentionally do not close sendChan in order to avoid a panic on
		// send to a just-closed channel. It's OK not to close a channel, since
		// it will be eventually garbage collected as soon as no goroutine
		// references it anymore, whether it has been closed or not.
	})
}

func (t *tailer) blockedSince() *time.Time {
	t.blockedMtx.RLock()
	defer t.blockedMtx.RUnlock()

	return t.blockedAt
}

func (t *tailer) dropStream(stream logproto.Stream) {
	if len(stream.Entries) == 0 {
		return
	}

	t.blockedMtx.Lock()
	defer t.blockedMtx.Unlock()

	if t.blockedAt == nil {
		blockedAt := time.Now()
		t.blockedAt = &blockedAt
	}
	droppedStream := logproto.DroppedStream{
		From:   stream.Entries[0].Timestamp,
		To:     stream.Entries[len(stream.Entries)-1].Timestamp,
		Labels: stream.Labels,
	}
	t.droppedStreams = append(t.droppedStreams, &droppedStream)
}

func (t *tailer) popDroppedStreams() []*logproto.DroppedStream {
	t.blockedMtx.Lock()
	defer t.blockedMtx.Unlock()

	if t.blockedAt == nil {
		return nil
	}

	droppedStreams := t.droppedStreams
	t.droppedStreams = []*logproto.DroppedStream{}
	t.blockedAt = nil

	return droppedStreams
}

func (t *tailer) getID() uint32 {
	return t.id
}

// An id is useful in managing tailer instances
func generateUniqueID(orgID, query string) uint32 {
	uniqueID := fnv.New32()
	_, _ = uniqueID.Write([]byte(orgID))
	_, _ = uniqueID.Write([]byte(query))

	timeNow := make([]byte, 8)
	binary.LittleEndian.PutUint64(timeNow, uint64(time.Now().UnixNano()))
	_, _ = uniqueID.Write(timeNow)

	return uniqueID.Sum32()
}
<|endoftext|>


<commit_before>package mqtt

import (
	"encoding/hex"
	"errors"
	"github.com/PandoCloud/pando-cloud/pkg/rpcs"
	"github.com/PandoCloud/pando-cloud/pkg/server"
	"net"
	"time"
)

const (
	SendChanLen      = 16
	defaultKeepAlive = 30
)

type ResponseType struct {
	SendTime    uint8
	PublishType uint8
	DataType    string
}

type Connection struct {
	Mgr             *Manager
	DeviceId        uint64
	Conn            net.Conn
	SendChan        chan Message
	MessageId       uint16
	MessageWaitChan map[uint16]chan error
	KeepAlive       uint16
	LastHbTime      int64
	Token           []byte
}

func NewConnection(conn net.Conn, mgr *Manager) *Connection {
	sendchan := make(chan Message, SendChanLen)
	c := &Connection{
		Conn:            conn,
		SendChan:        sendchan,
		Mgr:             mgr,
		KeepAlive:       defaultKeepAlive,
		MessageWaitChan: make(map[uint16]chan error),
	}

	go c.SendMsgToClient()
	go c.RcvMsgFromClient()

	return c
}

func (c *Connection) Submit(msg Message) {
	if c.Conn != nil {
		c.SendChan <- msg
	}
}

// Publish will publish a message , and return a chan to wait for completion.
func (c *Connection) Publish(msg Message, timeout time.Duration) error {
	server.Log.Debugf("publishing message : %v, timeout %v", msg, timeout)

	message := msg.(*Publish)
	message.MessageId = c.MessageId
	c.MessageId++
	c.Submit(message)

	ch := make(chan error)

	// we don't wait for confirm.
	if timeout == 0 {
		return nil
	}

	c.MessageWaitChan[message.MessageId] = ch
	// wait for timeout and
	go func() {
		timer := time.NewTimer(timeout)
		<-timer.C
		waitCh, exist := c.MessageWaitChan[message.MessageId]
		if exist {
			waitCh <- errors.New("timeout pushlishing message.")
			delete(c.MessageWaitChan, message.MessageId)
			close(waitCh)
		}
	}()

	err := <-ch
	return err
}

func (c *Connection) confirmPublish(messageid uint16) {
	waitCh, exist := c.MessageWaitChan[messageid]
	if exist {
		waitCh <- nil
		delete(c.MessageWaitChan, messageid)
		close(waitCh)
	}
}

func (c *Connection) ValidateToken(token []byte) error {

	err := c.Mgr.Provider.ValidateDeviceToken(c.DeviceId, token)
	if err != nil {
		return err
	}

	c.Token = token

	return nil
}

func (c *Connection) Close() {
	deviceid := c.DeviceId
	server.Log.Infof("closing connection of device %v", deviceid)
	if c.Conn != nil {
		c.Conn.Close()
	}
	if c.SendChan != nil {
		close(c.SendChan)
		c.SendChan = nil
	}
	c.Mgr.DelConn(deviceid)
	c.Mgr.Provider.OnDeviceOffline(deviceid)
}

func (c *Connection) RcvMsgFromClient() {
	conn := c.Conn
	host := conn.RemoteAddr().String()
	server.Log.Infof("recieve new connection from %s", host)
	for {
		msg, err := DecodeOneMessage(conn)
		if err != nil {

			server.Log.Errorf("read error: %s", err)
			c.Close()
			return
		}

		server.Log.Infof("%s, come msg===\n%v\n=====", host, msg)
		c.LastHbTime = time.Now().Unix()
		switch msg := msg.(type) {
		case *Connect:
			ret := RetCodeAccepted
			if msg.ProtocolVersion == 3 && msg.ProtocolName != "MQIsdp" {
				ret = RetCodeUnacceptableProtocolVersion
			} else if msg.ProtocolVersion == 4 && msg.ProtocolName != "MQTT" {
				ret = RetCodeUnacceptableProtocolVersion
			} else if msg.ProtocolVersion > 4 {
				ret = RetCodeUnacceptableProtocolVersion
			}

			if len(msg.ClientId) < 1 || len(msg.ClientId) > 23 {
				server.Log.Warn("invalid clientid length: %d", len(msg.ClientId))
				ret = RetCodeIdentifierRejected
				c.Close()
				return
			}

			deviceid, err := ClientIdToDeviceId(msg.ClientId)
			if err != nil {
				server.Log.Warn("invalid Identify: %d", ret)
				c.Close()
				return
			}
			c.DeviceId = deviceid

			token, err := hex.DecodeString(msg.Password)
			if err != nil {
				server.Log.Warn("token format error : %v", err)
				ret = RetCodeNotAuthorized
				c.Close()
				return
			}
			err = c.ValidateToken(token)
			if err != nil {
				server.Log.Warn("validate token error : %v", err)
				ret = RetCodeNotAuthorized
				c.Close()
				return
			}

			if ret != RetCodeAccepted {
				server.Log.Warn("invalid CON: %d", ret)
				c.Close()
				return
			}

			args := rpcs.ArgsGetOnline{
				Id:                c.DeviceId,
				ClientIP:          host,
				AccessRPCHost:     server.GetRPCHost(),
				HeartbeatInterval: uint32(c.KeepAlive),
			}
			err = c.Mgr.Provider.OnDeviceOnline(args)
			if err != nil {
				server.Log.Warn("device online error : %v", err)
				c.Close()
				return
			}

			c.Mgr.AddConn(c.DeviceId, c)
			connack := &ConnAck{
				ReturnCode: ret,
			}

			c.Submit(connack)
			c.KeepAlive = msg.KeepAliveTimer
			server.Log.Infof("%s, connected to server now", host)

		case *Publish:
			server.Log.Infof("%s, publish topic: %s", host, msg.TopicName)

			c.Mgr.PublishMessage2Server(c.DeviceId, msg)
			if msg.QosLevel.IsAtLeastOnce() {
				server.Log.Infof("publish ack send now")
				publishack := &PubAck{MessageId: msg.MessageId}
				c.Submit(publishack)
			} else if msg.QosLevel.IsExactlyOnce() {
				server.Log.Infof("publish Rec send now")
				publishRec := &PubRec{MessageId: msg.MessageId}
				c.Submit(publishRec)
			}

			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}

		case *PubAck:
			server.Log.Infof("%s, comes publish ack", host)
			c.confirmPublish(msg.MessageId)
			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}

		case *PubRec:
			server.Log.Infof("%s, comes publish rec", host)
			publishRel := &PubRel{MessageId: msg.MessageId}
			c.Submit(publishRel)

		case *PubRel:
			server.Log.Infof("%s, comes publish rel", host)
			publishCom := &PubComp{MessageId: msg.MessageId}
			c.Submit(publishCom)

		case *PubComp:
			server.Log.Infof("%s, comes publish comp", host)
			c.confirmPublish(msg.MessageId)
			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}

		case *PingReq:
			server.Log.Infof("%s, ping req comes", host)
			pingrsp := &PingResp{}
			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}
			c.Submit(pingrsp)

		case *Subscribe:
			server.Log.Infof("%s, subscribe topic: %v", host, msg.Topics)

		case *Unsubscribe:
			server.Log.Infof("%s, unsubscribe topic: %v", host, msg.Topics)

		case *Disconnect:
			server.Log.Infof("%s, disconnect now, exit...", host)
			c.Close()
			return

		default:
			server.Log.Errorf("unknown msg type %T", msg)
			c.Close()
			return
		}
	}

}

func (c *Connection) SendMsgToClient() {
	host := c.Conn.RemoteAddr()
	for {
		msg, ok := <-c.SendChan
		if !ok {
			server.Log.Errorf("%s is end now", host)
			return
		}

		server.Log.Debugf("send msg to %s=======\n%v\n=========", host, msg)
		err := msg.Encode(c.Conn)
		if err != nil {
			server.Log.Errorf("send msg err: %s=====\n%v\n=====", err, msg)
			continue
		}
	}
}
<commit_msg>fix Issue #26<commit_after>package mqtt

import (
	"encoding/hex"
	"errors"
	"github.com/PandoCloud/pando-cloud/pkg/rpcs"
	"github.com/PandoCloud/pando-cloud/pkg/server"
	"net"
	"time"
)

const (
	SendChanLen      = 16
	defaultKeepAlive = 30
)

type ResponseType struct {
	SendTime    uint8
	PublishType uint8
	DataType    string
}

type Connection struct {
	Mgr             *Manager
	DeviceId        uint64
	Conn            net.Conn
	SendChan        chan Message
	MessageId       uint16
	MessageWaitChan map[uint16]chan error
	KeepAlive       uint16
	LastHbTime      int64
	Token           []byte
}

func NewConnection(conn net.Conn, mgr *Manager) *Connection {
	sendchan := make(chan Message, SendChanLen)
	c := &Connection{
		Conn:            conn,
		SendChan:        sendchan,
		Mgr:             mgr,
		KeepAlive:       defaultKeepAlive,
		MessageWaitChan: make(map[uint16]chan error),
	}

	go c.SendMsgToClient()
	go c.RcvMsgFromClient()

	return c
}

func (c *Connection) Submit(msg Message) {
	if c.Conn != nil {
		c.SendChan <- msg
	}
}

// Publish will publish a message , and return a chan to wait for completion.
func (c *Connection) Publish(msg Message, timeout time.Duration) error {
	server.Log.Debugf("publishing message : %v, timeout %v", msg, timeout)

	message := msg.(*Publish)
	message.MessageId = c.MessageId
	c.MessageId++
	c.Submit(message)

	ch := make(chan error)

	// we don't wait for confirm.
	if timeout == 0 {
		return nil
	}

	c.MessageWaitChan[message.MessageId] = ch
	// wait for timeout and
	go func() {
		timer := time.NewTimer(timeout)
		<-timer.C
		waitCh, exist := c.MessageWaitChan[message.MessageId]
		if exist {
			waitCh <- errors.New("timeout pushlishing message.")
			delete(c.MessageWaitChan, message.MessageId)
			close(waitCh)
		}
	}()

	err := <-ch
	return err
}

func (c *Connection) confirmPublish(messageid uint16) {
	waitCh, exist := c.MessageWaitChan[messageid]
	if exist {
		waitCh <- nil
		delete(c.MessageWaitChan, messageid)
		close(waitCh)
	}
}

func (c *Connection) ValidateToken(token []byte) error {

	err := c.Mgr.Provider.ValidateDeviceToken(c.DeviceId, token)
	if err != nil {
		return err
	}

	c.Token = token

	return nil
}

func (c *Connection) Close() {
	deviceid := c.DeviceId
	server.Log.Infof("closing connection of device %v", deviceid)
	if c.Conn != nil {
		c.Conn.Close()
	}
	if c.SendChan != nil {
		close(c.SendChan)
		c.SendChan = nil
	}
	c.Mgr.Provider.OnDeviceOffline(deviceid)
}

func (c *Connection) RcvMsgFromClient() {
	conn := c.Conn
	host := conn.RemoteAddr().String()
	server.Log.Infof("recieve new connection from %s", host)
	for {
		msg, err := DecodeOneMessage(conn)
		if err != nil {

			server.Log.Errorf("read error: %s", err)
			c.Close()
			return
		}

		server.Log.Infof("%s, come msg===\n%v\n=====", host, msg)
		c.LastHbTime = time.Now().Unix()
		switch msg := msg.(type) {
		case *Connect:
			ret := RetCodeAccepted
			if msg.ProtocolVersion == 3 && msg.ProtocolName != "MQIsdp" {
				ret = RetCodeUnacceptableProtocolVersion
			} else if msg.ProtocolVersion == 4 && msg.ProtocolName != "MQTT" {
				ret = RetCodeUnacceptableProtocolVersion
			} else if msg.ProtocolVersion > 4 {
				ret = RetCodeUnacceptableProtocolVersion
			}

			if len(msg.ClientId) < 1 || len(msg.ClientId) > 23 {
				server.Log.Warn("invalid clientid length: %d", len(msg.ClientId))
				ret = RetCodeIdentifierRejected
				c.Close()
				return
			}

			deviceid, err := ClientIdToDeviceId(msg.ClientId)
			if err != nil {
				server.Log.Warn("invalid Identify: %d", ret)
				c.Close()
				return
			}
			c.DeviceId = deviceid

			token, err := hex.DecodeString(msg.Password)
			if err != nil {
				server.Log.Warn("token format error : %v", err)
				ret = RetCodeNotAuthorized
				c.Close()
				return
			}
			err = c.ValidateToken(token)
			if err != nil {
				server.Log.Warn("validate token error : %v", err)
				ret = RetCodeNotAuthorized
				c.Close()
				return
			}

			if ret != RetCodeAccepted {
				server.Log.Warn("invalid CON: %d", ret)
				c.Close()
				return
			}

			args := rpcs.ArgsGetOnline{
				Id:                c.DeviceId,
				ClientIP:          host,
				AccessRPCHost:     server.GetRPCHost(),
				HeartbeatInterval: uint32(c.KeepAlive),
			}
			err = c.Mgr.Provider.OnDeviceOnline(args)
			if err != nil {
				server.Log.Warn("device online error : %v", err)
				c.Close()
				return
			}

			c.Mgr.AddConn(c.DeviceId, c)
			connack := &ConnAck{
				ReturnCode: ret,
			}

			c.Submit(connack)
			c.KeepAlive = msg.KeepAliveTimer
			server.Log.Infof("%s, connected to server now", host)

		case *Publish:
			server.Log.Infof("%s, publish topic: %s", host, msg.TopicName)

			c.Mgr.PublishMessage2Server(c.DeviceId, msg)
			if msg.QosLevel.IsAtLeastOnce() {
				server.Log.Infof("publish ack send now")
				publishack := &PubAck{MessageId: msg.MessageId}
				c.Submit(publishack)
			} else if msg.QosLevel.IsExactlyOnce() {
				server.Log.Infof("publish Rec send now")
				publishRec := &PubRec{MessageId: msg.MessageId}
				c.Submit(publishRec)
			}

			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}

		case *PubAck:
			server.Log.Infof("%s, comes publish ack", host)
			c.confirmPublish(msg.MessageId)
			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}

		case *PubRec:
			server.Log.Infof("%s, comes publish rec", host)
			publishRel := &PubRel{MessageId: msg.MessageId}
			c.Submit(publishRel)

		case *PubRel:
			server.Log.Infof("%s, comes publish rel", host)
			publishCom := &PubComp{MessageId: msg.MessageId}
			c.Submit(publishCom)

		case *PubComp:
			server.Log.Infof("%s, comes publish comp", host)
			c.confirmPublish(msg.MessageId)
			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}

		case *PingReq:
			server.Log.Infof("%s, ping req comes", host)
			pingrsp := &PingResp{}
			err := c.Mgr.Provider.OnDeviceHeartBeat(c.DeviceId)
			if err != nil {
				server.Log.Warnf("%s, heartbeat set error %s, close now...", host, err)
				c.Close()
				return
			}
			c.Submit(pingrsp)

		case *Subscribe:
			server.Log.Infof("%s, subscribe topic: %v", host, msg.Topics)

		case *Unsubscribe:
			server.Log.Infof("%s, unsubscribe topic: %v", host, msg.Topics)

		case *Disconnect:
			server.Log.Infof("%s, disconnect now, exit...", host)
			c.Close()
			return

		default:
			server.Log.Errorf("unknown msg type %T", msg)
			c.Close()
			return
		}
	}

}

func (c *Connection) SendMsgToClient() {
	host := c.Conn.RemoteAddr()
	for {
		msg, ok := <-c.SendChan
		if !ok {
			server.Log.Errorf("%s is end now", host)
			return
		}

		server.Log.Debugf("send msg to %s=======\n%v\n=========", host, msg)
		err := msg.Encode(c.Conn)
		if err != nil {
			server.Log.Errorf("send msg err: %s=====\n%v\n=====", err, msg)
			continue
		}
	}
}
<|endoftext|>


<commit_before>// Package plugins provides structures and helper functions to manage Docker
// plugins.
//
// Docker discovers plugins by looking for them in the plugin directory whenever
// a user or container tries to use one by name. UNIX domain socket files must
// be located under /run/docker/plugins, whereas spec files can be located
// either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled
// by the Registry interface, which lets you list all plugins or get a plugin by
// its name if it exists.
//
// The plugins need to implement an HTTP server and bind this to the UNIX socket
// or the address specified in the spec files.
// A handshake is send at /Plugin.Activate, and plugins are expected to return
// a Manifest with a list of of Docker subsystems which this plugin implements.
//
// In order to use a plugins, you can use the ``Get`` with the name of the
// plugin and the subsystem it implements.
//
//	plugin, err := plugins.Get("example", "VolumeDriver")
//	if err != nil {
//		return fmt.Errorf("Error looking up volume plugin example: %v", err)
//	}
package plugins

import (
	"errors"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go-connections/tlsconfig"
)

var (
	// ErrNotImplements is returned if the plugin does not implement the requested driver.
	ErrNotImplements = errors.New("Plugin does not implement the requested driver")
)

type plugins struct {
	sync.Mutex
	plugins map[string]*Plugin
}

var (
	storage          = plugins{plugins: make(map[string]*Plugin)}
	extpointHandlers = make(map[string]func(string, *Client))
)

// Manifest lists what a plugin implements.
type Manifest struct {
	// List of subsystem the plugin implements.
	Implements []string
}

// Plugin is the definition of a docker plugin.
type Plugin struct {
	// Name of the plugin
	Name string `json:"-"`
	// Address of the plugin
	Addr string
	// TLS configuration of the plugin
	TLSConfig tlsconfig.Options
	// Client attached to the plugin
	Client *Client `json:"-"`
	// Manifest of the plugin (see above)
	Manifest *Manifest `json:"-"`

	activatErr   error
	activateOnce sync.Once
}

func newLocalPlugin(name, addr string) *Plugin {
	return &Plugin{
		Name:      name,
		Addr:      addr,
		TLSConfig: tlsconfig.Options{InsecureSkipVerify: true},
	}
}

func (p *Plugin) activate() error {
	p.activateOnce.Do(func() {
		p.activatErr = p.activateWithLock()
	})
	return p.activatErr
}

func (p *Plugin) activateWithLock() error {
	c, err := NewClient(p.Addr, p.TLSConfig)
	if err != nil {
		return err
	}
	p.Client = c

	m := new(Manifest)
	if err = p.Client.Call("Plugin.Activate", nil, m); err != nil {
		return err
	}

	p.Manifest = m

	for _, iface := range m.Implements {
		handler, handled := extpointHandlers[iface]
		if !handled {
			continue
		}
		handler(p.Name, p.Client)
	}
	return nil
}

func (p *Plugin) implements(kind string) bool {
	for _, driver := range p.Manifest.Implements {
		if driver == kind {
			return true
		}
	}
	return false
}

func load(name string) (*Plugin, error) {
	return loadWithRetry(name, true)
}

func loadWithRetry(name string, retry bool) (*Plugin, error) {
	registry := newLocalRegistry()
	start := time.Now()

	var retries int
	for {
		pl, err := registry.Plugin(name)
		if err != nil {
			if !retry {
				return nil, err
			}

			timeOff := backoff(retries)
			if abort(start, timeOff) {
				return nil, err
			}
			retries++
			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
			time.Sleep(timeOff)
			continue
		}

		storage.Lock()
		storage.plugins[name] = pl
		storage.Unlock()

		err = pl.activate()

		if err != nil {
			storage.Lock()
			delete(storage.plugins, name)
			storage.Unlock()
		}

		return pl, err
	}
}

func get(name string) (*Plugin, error) {
	storage.Lock()
	pl, ok := storage.plugins[name]
	storage.Unlock()
	if ok {
		return pl, pl.activate()
	}
	return load(name)
}

// Get returns the plugin given the specified name and requested implementation.
func Get(name, imp string) (*Plugin, error) {
	pl, err := get(name)
	if err != nil {
		return nil, err
	}
	if pl.implements(imp) {
		logrus.Debugf("%s implements: %s", name, imp)
		return pl, nil
	}
	return nil, ErrNotImplements
}

// Handle adds the specified function to the extpointHandlers.
func Handle(iface string, fn func(string, *Client)) {
	extpointHandlers[iface] = fn
}

// GetAll returns all the plugins for the specified implementation
func GetAll(imp string) ([]*Plugin, error) {
	pluginNames, err := Scan()
	if err != nil {
		return nil, err
	}

	type plLoad struct {
		pl  *Plugin
		err error
	}

	chPl := make(chan plLoad, len(pluginNames))
	for _, name := range pluginNames {
		go func(name string) {
			pl, err := loadWithRetry(name, false)
			chPl <- plLoad{pl, err}
		}(name)
	}

	var out []*Plugin
	for i := 0; i < len(pluginNames); i++ {
		pl := <-chPl
		if pl.err != nil {
			logrus.Error(err)
			continue
		}
		if pl.pl.implements(imp) {
			out = append(out, pl.pl)
		}
	}
	return out, nil
}
<commit_msg>Fixes plugin file descriptor leak on plugin discovery<commit_after>// Package plugins provides structures and helper functions to manage Docker
// plugins.
//
// Docker discovers plugins by looking for them in the plugin directory whenever
// a user or container tries to use one by name. UNIX domain socket files must
// be located under /run/docker/plugins, whereas spec files can be located
// either under /etc/docker/plugins or /usr/lib/docker/plugins. This is handled
// by the Registry interface, which lets you list all plugins or get a plugin by
// its name if it exists.
//
// The plugins need to implement an HTTP server and bind this to the UNIX socket
// or the address specified in the spec files.
// A handshake is send at /Plugin.Activate, and plugins are expected to return
// a Manifest with a list of of Docker subsystems which this plugin implements.
//
// In order to use a plugins, you can use the ``Get`` with the name of the
// plugin and the subsystem it implements.
//
//	plugin, err := plugins.Get("example", "VolumeDriver")
//	if err != nil {
//		return fmt.Errorf("Error looking up volume plugin example: %v", err)
//	}
package plugins

import (
	"errors"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/docker/go-connections/tlsconfig"
)

var (
	// ErrNotImplements is returned if the plugin does not implement the requested driver.
	ErrNotImplements = errors.New("Plugin does not implement the requested driver")
)

type plugins struct {
	sync.Mutex
	plugins map[string]*Plugin
}

var (
	storage          = plugins{plugins: make(map[string]*Plugin)}
	extpointHandlers = make(map[string]func(string, *Client))
)

// Manifest lists what a plugin implements.
type Manifest struct {
	// List of subsystem the plugin implements.
	Implements []string
}

// Plugin is the definition of a docker plugin.
type Plugin struct {
	// Name of the plugin
	Name string `json:"-"`
	// Address of the plugin
	Addr string
	// TLS configuration of the plugin
	TLSConfig tlsconfig.Options
	// Client attached to the plugin
	Client *Client `json:"-"`
	// Manifest of the plugin (see above)
	Manifest *Manifest `json:"-"`

	activatErr   error
	activateOnce sync.Once
}

func newLocalPlugin(name, addr string) *Plugin {
	return &Plugin{
		Name:      name,
		Addr:      addr,
		TLSConfig: tlsconfig.Options{InsecureSkipVerify: true},
	}
}

func (p *Plugin) activate() error {
	p.activateOnce.Do(func() {
		p.activatErr = p.activateWithLock()
	})
	return p.activatErr
}

func (p *Plugin) activateWithLock() error {
	c, err := NewClient(p.Addr, p.TLSConfig)
	if err != nil {
		return err
	}
	p.Client = c

	m := new(Manifest)
	if err = p.Client.Call("Plugin.Activate", nil, m); err != nil {
		return err
	}

	p.Manifest = m

	for _, iface := range m.Implements {
		handler, handled := extpointHandlers[iface]
		if !handled {
			continue
		}
		handler(p.Name, p.Client)
	}
	return nil
}

func (p *Plugin) implements(kind string) bool {
	for _, driver := range p.Manifest.Implements {
		if driver == kind {
			return true
		}
	}
	return false
}

func load(name string) (*Plugin, error) {
	return loadWithRetry(name, true)
}

func loadWithRetry(name string, retry bool) (*Plugin, error) {
	registry := newLocalRegistry()
	start := time.Now()

	var retries int
	for {
		pl, err := registry.Plugin(name)
		if err != nil {
			if !retry {
				return nil, err
			}

			timeOff := backoff(retries)
			if abort(start, timeOff) {
				return nil, err
			}
			retries++
			logrus.Warnf("Unable to locate plugin: %s, retrying in %v", name, timeOff)
			time.Sleep(timeOff)
			continue
		}

		storage.Lock()
		storage.plugins[name] = pl
		storage.Unlock()

		err = pl.activate()

		if err != nil {
			storage.Lock()
			delete(storage.plugins, name)
			storage.Unlock()
		}

		return pl, err
	}
}

func get(name string) (*Plugin, error) {
	storage.Lock()
	pl, ok := storage.plugins[name]
	storage.Unlock()
	if ok {
		return pl, pl.activate()
	}
	return load(name)
}

// Get returns the plugin given the specified name and requested implementation.
func Get(name, imp string) (*Plugin, error) {
	pl, err := get(name)
	if err != nil {
		return nil, err
	}
	if pl.implements(imp) {
		logrus.Debugf("%s implements: %s", name, imp)
		return pl, nil
	}
	return nil, ErrNotImplements
}

// Handle adds the specified function to the extpointHandlers.
func Handle(iface string, fn func(string, *Client)) {
	extpointHandlers[iface] = fn
}

// GetAll returns all the plugins for the specified implementation
func GetAll(imp string) ([]*Plugin, error) {
	pluginNames, err := Scan()
	if err != nil {
		return nil, err
	}

	type plLoad struct {
		pl  *Plugin
		err error
	}

	chPl := make(chan *plLoad, len(pluginNames))
	var wg sync.WaitGroup
	for _, name := range pluginNames {
		if pl, ok := storage.plugins[name]; ok {
			chPl <- &plLoad{pl, nil}
			continue
		}

		wg.Add(1)
		go func(name string) {
			defer wg.Done()
			pl, err := loadWithRetry(name, false)
			chPl <- &plLoad{pl, err}
		}(name)
	}

	wg.Wait()
	close(chPl)

	var out []*Plugin
	for pl := range chPl {
		if pl.err != nil {
			logrus.Error(err)
			continue
		}
		if pl.pl.implements(imp) {
			out = append(out, pl.pl)
		}
	}
	return out, nil
}
<|endoftext|>


<commit_before>// Copyright 2018 The gVisor 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 rand implements a cryptographically secure pseudorandom number
// generator.
package rand

import (
	"crypto/rand"
	"io"

	"golang.org/x/sys/unix"
	"gvisor.dev/gvisor/pkg/sync"
)

// reader implements an io.Reader that returns pseudorandom bytes.
type reader struct {
	once         sync.Once
	useGetrandom bool
}

// Read implements io.Reader.Read.
func (r *reader) Read(p []byte) (int, error) {
	r.once.Do(func() {
		_, err := unix.Getrandom(p, 0)
		if err != unix.ENOSYS {
			r.useGetrandom = true
		}
	})

	if r.useGetrandom {
		return unix.Getrandom(p, 0)
	}
	return rand.Read(p)
}

// Reader is the default reader.
var Reader io.Reader = &reader{}

// Read reads from the default reader.
func Read(b []byte) (int, error) {
	return io.ReadFull(Reader, b)
}

// Init can be called to make sure /dev/urandom is pre-opened on kernels that
// do not support getrandom(2).
func Init() error {
	p := make([]byte, 1)
	_, err := Read(p)
	return err
}
<commit_msg>Wrap rand.Reader in a bufio.Reader.<commit_after>// Copyright 2018 The gVisor 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 rand implements a cryptographically secure pseudorandom number
// generator.
package rand

import (
	"crypto/rand"
	"io"

	"golang.org/x/sys/unix"
	"gvisor.dev/gvisor/pkg/sync"
)

// reader implements an io.Reader that returns pseudorandom bytes.
type reader struct {
	once         sync.Once
	useGetrandom bool
}

// Read implements io.Reader.Read.
func (r *reader) Read(p []byte) (int, error) {
	r.once.Do(func() {
		_, err := unix.Getrandom(p, 0)
		if err != unix.ENOSYS {
			r.useGetrandom = true
		}
	})

	if r.useGetrandom {
		return unix.Getrandom(p, 0)
	}
	return rand.Read(p)
}

// mu protects the global Reader below.
var mu sync.Mutex

// Reader is the default reader.
var Reader io.Reader = &reader{}

// Read reads from the default reader.
func Read(b []byte) (int, error) {
	mu.Lock()
	n, err := io.ReadFull(Reader, b)
	mu.Unlock()
	return n, err
}

// Init can be called to make sure /dev/urandom is pre-opened on kernels that
// do not support getrandom(2).
func Init() error {
	p := make([]byte, 1)
	_, err := Read(p)
	return err
}
<|endoftext|>


<commit_before>package goriak

// WithPw sets the amount of primary nodes required to report back during writes.
// Used with Set(), SetRaw(), SetJSON(), and Delete().
func (c Command) WithPw(pw uint32) Command {
	c.riakPW = pw
	return c
}

// WithDw sets the amount of nodes required to report sucessfully writes to backend storage.
// Used with Set(), SetRaw(), SetJSON(), and Delete()
func (c Command) WithDw(dw uint32) Command {
	c.riakDW = dw
	return c
}

// WithW sets the amount of nodes required to report back during writes.
// Used with Set(), SetRaw(), SetJSON(), and Delete().
func (c Command) WithW(w uint32) Command {
	c.riakW = w
	return c
}

// WithRr sets the amount fo nodes required to report successfull deletes from backend storage.
func (c Command) WithRr(rw uint32) Command {
	c.riakRW = rw
	return c
}

// WithPr sets the amount of primary nodes required to report back during reads.
// Used with Get(), GetRaw(), and GetJSON().
func (c Command) WithPr(pr uint32) Command {
	c.riakPR = pr
	return c
}

// WithR sets the amount of nodes required to report back during reads.
// Used with Get(), GetRaw(), and GetJSON().
func (c Command) WithR(r uint32) Command {
	c.riakR = r
	return c
}
<commit_msg>Improved comments<commit_after>package goriak

// WithPw sets the amount of primary nodes required to report back during writes.
// Used with Set(), SetRaw(), SetJSON(), and Delete().
func (c Command) WithPw(pw uint32) Command {
	c.riakPW = pw
	return c
}

// WithDw sets the amount of nodes required to report sucessfully writes to backend storage.
// Used with Set(), SetRaw(), SetJSON(), and Delete()
func (c Command) WithDw(dw uint32) Command {
	c.riakDW = dw
	return c
}

// WithW sets the amount of nodes required to report back during writes.
// Used with Set(), SetRaw(), SetJSON(), and Delete().
func (c Command) WithW(w uint32) Command {
	c.riakW = w
	return c
}

// WithRr sets the amount fo nodes required to report successfull deletes from backend storage.
// Used with Delete().
func (c Command) WithRr(rw uint32) Command {
	c.riakRW = rw
	return c
}

// WithPr sets the amount of primary nodes required to report back during reads.
// Used with Get(), GetRaw(), GetJSON(), and Delete().
func (c Command) WithPr(pr uint32) Command {
	c.riakPR = pr
	return c
}

// WithR sets the amount of nodes required to report back during reads.
// Used with Get(), GetRaw(), GetJSON(), and Delete().
func (c Command) WithR(r uint32) Command {
	c.riakR = r
	return c
}
<|endoftext|>


<commit_before>// Copyright 2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rand

import (
	"context"
	"log"
	"sync"
	"syscall"
	"time"

	"github.com/u-root/u-root/pkg/cmdline"
	"golang.org/x/sys/unix"
)

var defaultContextReader = &getrandomReader{}

var backupReader = &urandomReader{}

type getrandomReader struct {
	once   sync.Once
	backup bool
}

// ReadContext implements a cancelable read from /dev/urandom.
func (r *getrandomReader) ReadContext(ctx context.Context, b []byte) (int, error) {
	r.once.Do(func() {
		if cmdline.ContainsFlag("uroot.nohwrng") {
			r.backup = true
			return
		}
		if _, err := unix.Getrandom(b, unix.GRND_NONBLOCK); err == syscall.ENOSYS {
			r.backup = true
		}
	})
	if r.backup {
		return backupReader.ReadContext(ctx, b)
	}

	for {
		// getrandom(2) with GRND_NONBLOCK uses the urandom number
		// source, but only returns numbers if the crng has been
		// initialized.
		//
		// This is preferrable to /dev/urandom, as /dev/urandom will
		// make up fake random numbers until the crng has been
		// initialized.
		n, err := unix.Getrandom(b, unix.GRND_NONBLOCK)
		if err == nil {
			return n, err
		}
		select {
		case <-ctx.Done():
			return 0, ctx.Err()

		default:
			if err != nil && err != syscall.EAGAIN && err != syscall.EINTR {
				return n, err
			}
		}
	}
}

// ReadContextWithSlowLogs logs a helpful message if it takes a significant
// amount of time (>2s) to produce random data.
func (r *getrandomReader) ReadContextWithSlowLogs(ctx context.Context, b []byte) (int, error) {
	d := 2 * time.Second
	t := time.AfterFunc(d, func() {
		log.Printf("getrandom is taking a long time (>%v). "+
			"If running on hardware, consider enabling Linux's CONFIG_RANDOM_TRUST_CPU=y. "+
			"If running in a VM/emulator, try setting up virtio-rng.", d)
	})
	defer t.Stop()
	return r.ReadContext(ctx, b)
}
<commit_msg>rand: use env var instead of kcmdline<commit_after>// Copyright 2019 the u-root Authors. All rights reserved
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rand

import (
	"context"
	"log"
	"os"
	"sync"
	"syscall"
	"time"

	"golang.org/x/sys/unix"
)

var defaultContextReader = &getrandomReader{}

var backupReader = &urandomReader{}

type getrandomReader struct {
	once   sync.Once
	backup bool
}

// ReadContext implements a cancelable read from /dev/urandom.
func (r *getrandomReader) ReadContext(ctx context.Context, b []byte) (int, error) {
	r.once.Do(func() {
		if os.Getenv("UROOT_NOHWRNG") != "" {
			r.backup = true
			return
		}
		if _, err := unix.Getrandom(b, unix.GRND_NONBLOCK); err == syscall.ENOSYS {
			r.backup = true
		}
	})
	if r.backup {
		return backupReader.ReadContext(ctx, b)
	}

	for {
		// getrandom(2) with GRND_NONBLOCK uses the urandom number
		// source, but only returns numbers if the crng has been
		// initialized.
		//
		// This is preferrable to /dev/urandom, as /dev/urandom will
		// make up fake random numbers until the crng has been
		// initialized.
		n, err := unix.Getrandom(b, unix.GRND_NONBLOCK)
		if err == nil {
			return n, err
		}
		select {
		case <-ctx.Done():
			return 0, ctx.Err()

		default:
			if err != nil && err != syscall.EAGAIN && err != syscall.EINTR {
				return n, err
			}
		}
	}
}

// ReadContextWithSlowLogs logs a helpful message if it takes a significant
// amount of time (>2s) to produce random data.
func (r *getrandomReader) ReadContextWithSlowLogs(ctx context.Context, b []byte) (int, error) {
	d := 2 * time.Second
	t := time.AfterFunc(d, func() {
		log.Printf("getrandom is taking a long time (>%v). "+
			"If running on hardware, consider enabling Linux's CONFIG_RANDOM_TRUST_CPU=y. "+
			"If running in a VM/emulator, try setting up virtio-rng.", d)
	})
	defer t.Stop()
	return r.ReadContext(ctx, b)
}
<|endoftext|>


<commit_before>package registry

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/Sirupsen/logrus/formatters/logstash"
	"github.com/bugsnag/bugsnag-go"
	"github.com/docker/distribution/configuration"
	ctxu "github.com/docker/distribution/context"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/handlers"
	"github.com/docker/distribution/registry/listener"
	"github.com/docker/distribution/uuid"
	"github.com/docker/distribution/version"
	gorhandlers "github.com/gorilla/handlers"
	"github.com/spf13/cobra"
	"github.com/yvasiyarov/gorelic"
	"golang.org/x/net/context"
)

// Cmd is a cobra command for running the registry.
var Cmd = &cobra.Command{
	Use:   "registry <config>",
	Short: "registry stores and distributes Docker images",
	Long:  "registry stores and distributes Docker images.",
	Run: func(cmd *cobra.Command, args []string) {
		if showVersion {
			version.PrintVersion()
			return
		}

		config, err := resolveConfiguration(args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "configuration error: %v\n", err)
			cmd.Usage()
			os.Exit(1)
		}

		if config.HTTP.Debug.Addr != "" {
			go func(addr string) {
				log.Infof("debug server listening %v", addr)
				if err := http.ListenAndServe(addr, nil); err != nil {
					log.Fatalf("error listening on debug interface: %v", err)
				}
			}(config.HTTP.Debug.Addr)
		}

		registry, err := NewRegistry(context.Background(), config)
		if err != nil {
			log.Fatalln(err)
		}

		if err = registry.ListenAndServe(); err != nil {
			log.Fatalln(err)
		}
	},
}

var showVersion bool

func init() {
	Cmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit")
}

// A Registry represents a complete instance of the registry.
// TODO(aaronl): It might make sense for Registry to become an interface.
type Registry struct {
	config *configuration.Configuration
	app    *handlers.App
	server *http.Server
}

// NewRegistry creates a new registry from a context and configuration struct.
func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Registry, error) {
	// Note this
	ctx = ctxu.WithValue(ctx, "version", version.Version)

	var err error
	ctx, err = configureLogging(ctx, config)
	if err != nil {
		return nil, fmt.Errorf("error configuring logger: %v", err)
	}

	// inject a logger into the uuid library. warns us if there is a problem
	// with uuid generation under low entropy.
	uuid.Loggerf = ctxu.GetLogger(ctx).Warnf

	app := handlers.NewApp(ctx, config)
	// TODO(aaronl): The global scope of the health checks means NewRegistry
	// can only be called once per process.
	app.RegisterHealthChecks()
	handler := configureReporting(app)
	handler = alive("/", handler)
	handler = health.Handler(handler)
	handler = panicHandler(handler)
	handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)

	server := &http.Server{
		Handler: handler,
	}

	return &Registry{
		app:    app,
		config: config,
		server: server,
	}, nil
}

// ListenAndServe runs the registry's HTTP server.
func (registry *Registry) ListenAndServe() error {
	config := registry.config

	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		return err
	}

	if config.HTTP.TLS.Certificate != "" {
		tlsConf := &tls.Config{
			ClientAuth:               tls.NoClientCert,
			NextProtos:               []string{"http/1.1"},
			Certificates:             make([]tls.Certificate, 1),
			MinVersion:               tls.VersionTLS10,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
		}

		tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
		if err != nil {
			return err
		}

		if len(config.HTTP.TLS.ClientCAs) != 0 {
			pool := x509.NewCertPool()

			for _, ca := range config.HTTP.TLS.ClientCAs {
				caPem, err := ioutil.ReadFile(ca)
				if err != nil {
					return err
				}

				if ok := pool.AppendCertsFromPEM(caPem); !ok {
					return fmt.Errorf("Could not add CA to pool")
				}
			}

			for _, subj := range pool.Subjects() {
				ctxu.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
			}

			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConf.ClientCAs = pool
		}

		ln = tls.NewListener(ln, tlsConf)
		ctxu.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
	} else {
		ctxu.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
	}

	return registry.server.Serve(ln)
}

func configureReporting(app *handlers.App) http.Handler {
	var handler http.Handler = app

	if app.Config.Reporting.Bugsnag.APIKey != "" {
		bugsnagConfig := bugsnag.Configuration{
			APIKey: app.Config.Reporting.Bugsnag.APIKey,
			// TODO(brianbland): provide the registry version here
			// AppVersion: "2.0",
		}
		if app.Config.Reporting.Bugsnag.ReleaseStage != "" {
			bugsnagConfig.ReleaseStage = app.Config.Reporting.Bugsnag.ReleaseStage
		}
		if app.Config.Reporting.Bugsnag.Endpoint != "" {
			bugsnagConfig.Endpoint = app.Config.Reporting.Bugsnag.Endpoint
		}
		bugsnag.Configure(bugsnagConfig)

		handler = bugsnag.Handler(handler)
	}

	if app.Config.Reporting.NewRelic.LicenseKey != "" {
		agent := gorelic.NewAgent()
		agent.NewrelicLicense = app.Config.Reporting.NewRelic.LicenseKey
		if app.Config.Reporting.NewRelic.Name != "" {
			agent.NewrelicName = app.Config.Reporting.NewRelic.Name
		}
		agent.CollectHTTPStat = true
		agent.Verbose = app.Config.Reporting.NewRelic.Verbose
		agent.Run()

		handler = agent.WrapHTTPHandler(handler)
	}

	return handler
}

// configureLogging prepares the context with a logger using the
// configuration.
func configureLogging(ctx ctxu.Context, config *configuration.Configuration) (context.Context, error) {
	if config.Log.Level == "" && config.Log.Formatter == "" {
		// If no config for logging is set, fallback to deprecated "Loglevel".
		log.SetLevel(logLevel(config.Loglevel))
		ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, "version"))
		return ctx, nil
	}

	log.SetLevel(logLevel(config.Log.Level))

	formatter := config.Log.Formatter
	if formatter == "" {
		formatter = "text" // default formatter
	}

	switch formatter {
	case "json":
		log.SetFormatter(&log.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "text":
		log.SetFormatter(&log.TextFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "logstash":
		log.SetFormatter(&logstash.LogstashFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	default:
		// just let the library use default on empty string.
		if config.Log.Formatter != "" {
			return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
		}
	}

	if config.Log.Formatter != "" {
		log.Debugf("using %q logging formatter", config.Log.Formatter)
	}

	// log the application version with messages
	ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, "version"))

	if len(config.Log.Fields) > 0 {
		// build up the static fields, if present.
		var fields []interface{}
		for k := range config.Log.Fields {
			fields = append(fields, k)
		}

		ctx = ctxu.WithValues(ctx, config.Log.Fields)
		ctx = ctxu.WithLogger(ctx, ctxu.GetLogger(ctx, fields...))
	}

	return ctx, nil
}

func logLevel(level configuration.Loglevel) log.Level {
	l, err := log.ParseLevel(string(level))
	if err != nil {
		l = log.InfoLevel
		log.Warnf("error parsing level %q: %v, using %q	", level, err, l)
	}

	return l
}

// panicHandler add a HTTP handler to web app. The handler recover the happening
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
// defined in config.yml.
func panicHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Panic(fmt.Sprintf("%v", err))
			}
		}()
		handler.ServeHTTP(w, r)
	})
}

// alive simply wraps the handler with a route that always returns an http 200
// response when the path is matched. If the path is not matched, the request
// is passed to the provided handler. There is no guarantee of anything but
// that the server is up. Wrap with other handlers (such as health.Handler)
// for greater affect.
func alive(path string, handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == path {
			w.Header().Set("Cache-Control", "no-cache")
			w.WriteHeader(http.StatusOK)
			return
		}

		handler.ServeHTTP(w, r)
	})
}

func resolveConfiguration(args []string) (*configuration.Configuration, error) {
	var configurationPath string

	if len(args) > 0 {
		configurationPath = args[0]
	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
	}

	if configurationPath == "" {
		return nil, fmt.Errorf("configuration path unspecified")
	}

	fp, err := os.Open(configurationPath)
	if err != nil {
		return nil, err
	}

	defer fp.Close()

	config, err := configuration.Parse(fp)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
	}

	return config, nil
}
<commit_msg>Only use the distribution/context package in registry.go<commit_after>package registry

import (
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/Sirupsen/logrus/formatters/logstash"
	"github.com/bugsnag/bugsnag-go"
	"github.com/docker/distribution/configuration"
	"github.com/docker/distribution/context"
	"github.com/docker/distribution/health"
	"github.com/docker/distribution/registry/handlers"
	"github.com/docker/distribution/registry/listener"
	"github.com/docker/distribution/uuid"
	"github.com/docker/distribution/version"
	gorhandlers "github.com/gorilla/handlers"
	"github.com/spf13/cobra"
	"github.com/yvasiyarov/gorelic"
)

// Cmd is a cobra command for running the registry.
var Cmd = &cobra.Command{
	Use:   "registry <config>",
	Short: "registry stores and distributes Docker images",
	Long:  "registry stores and distributes Docker images.",
	Run: func(cmd *cobra.Command, args []string) {
		if showVersion {
			version.PrintVersion()
			return
		}

		config, err := resolveConfiguration(args)
		if err != nil {
			fmt.Fprintf(os.Stderr, "configuration error: %v\n", err)
			cmd.Usage()
			os.Exit(1)
		}

		if config.HTTP.Debug.Addr != "" {
			go func(addr string) {
				log.Infof("debug server listening %v", addr)
				if err := http.ListenAndServe(addr, nil); err != nil {
					log.Fatalf("error listening on debug interface: %v", err)
				}
			}(config.HTTP.Debug.Addr)
		}

		registry, err := NewRegistry(context.Background(), config)
		if err != nil {
			log.Fatalln(err)
		}

		if err = registry.ListenAndServe(); err != nil {
			log.Fatalln(err)
		}
	},
}

var showVersion bool

func init() {
	Cmd.PersistentFlags().BoolVarP(&showVersion, "version", "v", false, "show the version and exit")
}

// A Registry represents a complete instance of the registry.
// TODO(aaronl): It might make sense for Registry to become an interface.
type Registry struct {
	config *configuration.Configuration
	app    *handlers.App
	server *http.Server
}

// NewRegistry creates a new registry from a context and configuration struct.
func NewRegistry(ctx context.Context, config *configuration.Configuration) (*Registry, error) {
	// Note this
	ctx = context.WithValue(ctx, "version", version.Version)

	var err error
	ctx, err = configureLogging(ctx, config)
	if err != nil {
		return nil, fmt.Errorf("error configuring logger: %v", err)
	}

	// inject a logger into the uuid library. warns us if there is a problem
	// with uuid generation under low entropy.
	uuid.Loggerf = context.GetLogger(ctx).Warnf

	app := handlers.NewApp(ctx, config)
	// TODO(aaronl): The global scope of the health checks means NewRegistry
	// can only be called once per process.
	app.RegisterHealthChecks()
	handler := configureReporting(app)
	handler = alive("/", handler)
	handler = health.Handler(handler)
	handler = panicHandler(handler)
	handler = gorhandlers.CombinedLoggingHandler(os.Stdout, handler)

	server := &http.Server{
		Handler: handler,
	}

	return &Registry{
		app:    app,
		config: config,
		server: server,
	}, nil
}

// ListenAndServe runs the registry's HTTP server.
func (registry *Registry) ListenAndServe() error {
	config := registry.config

	ln, err := listener.NewListener(config.HTTP.Net, config.HTTP.Addr)
	if err != nil {
		return err
	}

	if config.HTTP.TLS.Certificate != "" {
		tlsConf := &tls.Config{
			ClientAuth:               tls.NoClientCert,
			NextProtos:               []string{"http/1.1"},
			Certificates:             make([]tls.Certificate, 1),
			MinVersion:               tls.VersionTLS10,
			PreferServerCipherSuites: true,
			CipherSuites: []uint16{
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
				tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA,
				tls.TLS_RSA_WITH_AES_128_CBC_SHA,
				tls.TLS_RSA_WITH_AES_256_CBC_SHA,
			},
		}

		tlsConf.Certificates[0], err = tls.LoadX509KeyPair(config.HTTP.TLS.Certificate, config.HTTP.TLS.Key)
		if err != nil {
			return err
		}

		if len(config.HTTP.TLS.ClientCAs) != 0 {
			pool := x509.NewCertPool()

			for _, ca := range config.HTTP.TLS.ClientCAs {
				caPem, err := ioutil.ReadFile(ca)
				if err != nil {
					return err
				}

				if ok := pool.AppendCertsFromPEM(caPem); !ok {
					return fmt.Errorf("Could not add CA to pool")
				}
			}

			for _, subj := range pool.Subjects() {
				context.GetLogger(registry.app).Debugf("CA Subject: %s", string(subj))
			}

			tlsConf.ClientAuth = tls.RequireAndVerifyClientCert
			tlsConf.ClientCAs = pool
		}

		ln = tls.NewListener(ln, tlsConf)
		context.GetLogger(registry.app).Infof("listening on %v, tls", ln.Addr())
	} else {
		context.GetLogger(registry.app).Infof("listening on %v", ln.Addr())
	}

	return registry.server.Serve(ln)
}

func configureReporting(app *handlers.App) http.Handler {
	var handler http.Handler = app

	if app.Config.Reporting.Bugsnag.APIKey != "" {
		bugsnagConfig := bugsnag.Configuration{
			APIKey: app.Config.Reporting.Bugsnag.APIKey,
			// TODO(brianbland): provide the registry version here
			// AppVersion: "2.0",
		}
		if app.Config.Reporting.Bugsnag.ReleaseStage != "" {
			bugsnagConfig.ReleaseStage = app.Config.Reporting.Bugsnag.ReleaseStage
		}
		if app.Config.Reporting.Bugsnag.Endpoint != "" {
			bugsnagConfig.Endpoint = app.Config.Reporting.Bugsnag.Endpoint
		}
		bugsnag.Configure(bugsnagConfig)

		handler = bugsnag.Handler(handler)
	}

	if app.Config.Reporting.NewRelic.LicenseKey != "" {
		agent := gorelic.NewAgent()
		agent.NewrelicLicense = app.Config.Reporting.NewRelic.LicenseKey
		if app.Config.Reporting.NewRelic.Name != "" {
			agent.NewrelicName = app.Config.Reporting.NewRelic.Name
		}
		agent.CollectHTTPStat = true
		agent.Verbose = app.Config.Reporting.NewRelic.Verbose
		agent.Run()

		handler = agent.WrapHTTPHandler(handler)
	}

	return handler
}

// configureLogging prepares the context with a logger using the
// configuration.
func configureLogging(ctx context.Context, config *configuration.Configuration) (context.Context, error) {
	if config.Log.Level == "" && config.Log.Formatter == "" {
		// If no config for logging is set, fallback to deprecated "Loglevel".
		log.SetLevel(logLevel(config.Loglevel))
		ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))
		return ctx, nil
	}

	log.SetLevel(logLevel(config.Log.Level))

	formatter := config.Log.Formatter
	if formatter == "" {
		formatter = "text" // default formatter
	}

	switch formatter {
	case "json":
		log.SetFormatter(&log.JSONFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "text":
		log.SetFormatter(&log.TextFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	case "logstash":
		log.SetFormatter(&logstash.LogstashFormatter{
			TimestampFormat: time.RFC3339Nano,
		})
	default:
		// just let the library use default on empty string.
		if config.Log.Formatter != "" {
			return ctx, fmt.Errorf("unsupported logging formatter: %q", config.Log.Formatter)
		}
	}

	if config.Log.Formatter != "" {
		log.Debugf("using %q logging formatter", config.Log.Formatter)
	}

	// log the application version with messages
	ctx = context.WithLogger(ctx, context.GetLogger(ctx, "version"))

	if len(config.Log.Fields) > 0 {
		// build up the static fields, if present.
		var fields []interface{}
		for k := range config.Log.Fields {
			fields = append(fields, k)
		}

		ctx = context.WithValues(ctx, config.Log.Fields)
		ctx = context.WithLogger(ctx, context.GetLogger(ctx, fields...))
	}

	return ctx, nil
}

func logLevel(level configuration.Loglevel) log.Level {
	l, err := log.ParseLevel(string(level))
	if err != nil {
		l = log.InfoLevel
		log.Warnf("error parsing level %q: %v, using %q	", level, err, l)
	}

	return l
}

// panicHandler add a HTTP handler to web app. The handler recover the happening
// panic. logrus.Panic transmits panic message to pre-config log hooks, which is
// defined in config.yml.
func panicHandler(handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		defer func() {
			if err := recover(); err != nil {
				log.Panic(fmt.Sprintf("%v", err))
			}
		}()
		handler.ServeHTTP(w, r)
	})
}

// alive simply wraps the handler with a route that always returns an http 200
// response when the path is matched. If the path is not matched, the request
// is passed to the provided handler. There is no guarantee of anything but
// that the server is up. Wrap with other handlers (such as health.Handler)
// for greater affect.
func alive(path string, handler http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		if r.URL.Path == path {
			w.Header().Set("Cache-Control", "no-cache")
			w.WriteHeader(http.StatusOK)
			return
		}

		handler.ServeHTTP(w, r)
	})
}

func resolveConfiguration(args []string) (*configuration.Configuration, error) {
	var configurationPath string

	if len(args) > 0 {
		configurationPath = args[0]
	} else if os.Getenv("REGISTRY_CONFIGURATION_PATH") != "" {
		configurationPath = os.Getenv("REGISTRY_CONFIGURATION_PATH")
	}

	if configurationPath == "" {
		return nil, fmt.Errorf("configuration path unspecified")
	}

	fp, err := os.Open(configurationPath)
	if err != nil {
		return nil, err
	}

	defer fp.Close()

	config, err := configuration.Parse(fp)
	if err != nil {
		return nil, fmt.Errorf("error parsing %s: %v", configurationPath, err)
	}

	return config, nil
}
<|endoftext|>


<commit_before>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package receiver

import (
	"encoding/json"
	"fmt"
	"io"
	stdlog "log"
	"regexp"
	"strconv"
	"strings"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/circonus-labs/circonus-agent/internal/tags"
	cgm "github.com/circonus-labs/circonus-gometrics"
	"github.com/pkg/errors"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

func init() {
	histogramRx = regexp.MustCompile(`^H\[(?P<bucket>[^\]]+)\]=(?P<count>[0-9]+)$`)
	histogramRxNames = histogramRx.SubexpNames()
}

func initCGM() error {
	metricsmu.Lock()
	defer metricsmu.Unlock()

	if metrics != nil {
		return nil
	}

	cmc := &cgm.Config{
		Debug: viper.GetBool(config.KeyDebugCGM),
		Log:   stdlog.New(log.With().Str("pkg", "receiver").Logger(), "", 0),
	}
	// put cgm into manual mode (no interval, no api key, invalid submission url)
	cmc.Interval = "0"                            // disable automatic flush
	cmc.CheckManager.Check.SubmissionURL = "none" // disable check management (create/update)

	hm, err := cgm.NewCirconusMetrics(cmc)
	if err != nil {
		return errors.Wrap(err, "receiver cgm")
	}

	metrics = hm
	return nil
}

// Flush returns current metrics
func Flush() *cgm.Metrics {
	initCGM()
	metricsmu.Lock()
	defer metricsmu.Unlock()

	return metrics.FlushMetrics()
}

// Parse handles incoming PUT/POST requests
func Parse(id string, data io.ReadCloser) error {
	initCGM()
	metricsmu.Lock()
	defer metricsmu.Unlock()

	var tmp tags.JSONMetrics // cgm.Metrics
	if err := json.NewDecoder(data).Decode(&tmp); err != nil {
		if serr, ok := err.(*json.SyntaxError); ok {
			return errors.Wrapf(serr, "id:%s - offset %d", id, serr.Offset)
		}
		return errors.Wrapf(err, "parsing json for %s", id)
	}

	for name, metric := range tmp {
		metricName := strings.Join([]string{id, name}, config.MetricNameSeparator)
		if len(metric.Tags) > 0 {
			st, err := tags.PrepStreamTags(strings.Join(metric.Tags, tags.Separator))
			if err != nil {
				log.Warn().Err(err).Str("pkg", "receiver").Str("metric", metricName).Strs("tags", metric.Tags).Msg("ignoring tags")
			}
			metricName += st
		}
		switch metric.Type {
		case "i":
			if v := parseInt32(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "I":
			if v := parseUint32(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "l":
			if v := parseInt64(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "L":
			if v := parseUint64(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "n":
			v, isHist := parseFloat(metricName, metric)
			if v != nil {
				metrics.AddGauge(metricName, *v)
			} else if isHist {
				samples := parseHistogram(metricName, metric)
				if samples != nil && len(*samples) > 0 {
					for _, sample := range *samples {
						if sample.bucket {
							metrics.RecordCountForValue(metricName, sample.value, sample.count)
						} else {
							metrics.RecordValue(metricName, sample.value)
						}
					}
				}
			}
		case "s":
			metrics.SetText(metricName, fmt.Sprintf("%v", metric.Value))
		default:
			log.Warn().Str("metric", metricName).Str("type", metric.Type).Str("pkg", "receiver").Msg("unsupported metric type")
		}
	}

	return nil
}

func parseInt32(metricName string, metric tags.JSONMetric) *int32 {
	switch t := metric.Value.(type) {
	case float64:
		v := int32(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseInt(metric.Value.(string), 10, 32)
		if err == nil {
			v2 := int32(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseUint32(metricName string, metric tags.JSONMetric) *uint32 {
	switch t := metric.Value.(type) {
	case float64:
		v := uint32(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseUint(metric.Value.(string), 10, 32)
		if err == nil {
			v2 := uint32(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseInt64(metricName string, metric tags.JSONMetric) *int64 {
	switch t := metric.Value.(type) {
	case float64:
		v := int64(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseInt(metric.Value.(string), 10, 64)
		if err == nil {
			v2 := int64(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseUint64(metricName string, metric tags.JSONMetric) *uint64 {
	switch t := metric.Value.(type) {
	case float64:
		v := uint64(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseUint(metric.Value.(string), 10, 64)
		if err == nil {
			v2 := uint64(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseFloat(metricName string, metric tags.JSONMetric) (*float64, bool) {
	switch t := metric.Value.(type) {
	case float64:
		v := metric.Value.(float64)
		return &v, false
	case []interface{}: // treat as histogram
		return nil, true
	case string:
		v, err := strconv.ParseFloat(metric.Value.(string), 64)
		if err == nil {
			v2 := float64(v)
			return &v2, false
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil, false
}

type histSample struct {
	bucket bool
	count  int64
	value  float64
}

func parseHistogram(metricName string, metric tags.JSONMetric) *[]histSample {
	switch t := metric.Value.(type) {
	case []interface{}:
		ret := make([]histSample, 0, len(metric.Value.([]interface{})))
		for idx, v := range metric.Value.([]interface{}) {
			switch t2 := v.(type) {
			case float64:
				ret = append(ret, histSample{bucket: false, value: v.(float64)})
			case string:
				sv := v.(string)
				if !strings.Contains(sv, "H[") {
					v2, err := strconv.ParseFloat(sv, 64)
					if err != nil {
						log.Error().
							Str("pkg", "receiver").
							Str("metric", metricName).
							Interface("value", v).
							Int("position", idx).
							Err(err).
							Msg("parsing histogram sample")
						continue
					}
					ret = append(ret, histSample{bucket: false, value: float64(v2)})
					continue
				}

				//
				// it's an encoded histogram sample H[value]=count
				//
				bucket := ""
				count := ""
				matches := histogramRx.FindAllStringSubmatch(sv, -1)
				for _, match := range matches {
					for idx, val := range match {
						switch histogramRxNames[idx] {
						case "bucket":
							bucket = val
						case "count":
							count = val
						}
					}
				}
				if bucket == "" || count == "" {
					log.Error().
						Str("pkg", "receiver").
						Str("metric", metricName).
						Str("sample", sv).
						Int("position", idx).
						Msg("invalid encoded histogram sample")
					continue
				}
				b, err := strconv.ParseFloat(bucket, 64)
				if err != nil {
					log.Error().
						Str("pkg", "receiver").
						Str("metric", metricName).
						Str("sample", sv).
						Int("position", idx).
						Err(err).
						Msg("encoded histogram sample, value parse")
					continue
				}
				c, err := strconv.ParseInt(count, 10, 64)
				if err != nil {
					log.Error().
						Str("pkg", "receiver").
						Str("metric", metricName).
						Str("sample", sv).
						Int("position", idx).
						Err(err).
						Msg("encoded histogram sample, count parse")
					continue
				}
				ret = append(ret, histSample{bucket: true, value: b, count: c})
			default:
				log.Error().
					Str("pkg", "receiver").
					Str("metric", metricName).
					Interface("value", v).
					Int("position", idx).
					Str("type", fmt.Sprintf("%T", t2)).
					Msg("invalid value type for histogram sample")
			}
		}
		if len(ret) == 0 {
			return nil
		}
		return &ret
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for histogram")
	}
	return nil
}
<commit_msg>upd: lock after parsing incoming json<commit_after>// Copyright © 2017 Circonus, Inc. <support@circonus.com>
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//

package receiver

import (
	"encoding/json"
	"fmt"
	"io"
	stdlog "log"
	"regexp"
	"strconv"
	"strings"

	"github.com/circonus-labs/circonus-agent/internal/config"
	"github.com/circonus-labs/circonus-agent/internal/tags"
	cgm "github.com/circonus-labs/circonus-gometrics"
	"github.com/pkg/errors"
	"github.com/rs/zerolog/log"
	"github.com/spf13/viper"
)

func init() {
	histogramRx = regexp.MustCompile(`^H\[(?P<bucket>[^\]]+)\]=(?P<count>[0-9]+)$`)
	histogramRxNames = histogramRx.SubexpNames()
}

func initCGM() error {
	metricsmu.Lock()
	defer metricsmu.Unlock()

	if metrics != nil {
		return nil
	}

	cmc := &cgm.Config{
		Debug: viper.GetBool(config.KeyDebugCGM),
		Log:   stdlog.New(log.With().Str("pkg", "receiver").Logger(), "", 0),
	}
	// put cgm into manual mode (no interval, no api key, invalid submission url)
	cmc.Interval = "0"                            // disable automatic flush
	cmc.CheckManager.Check.SubmissionURL = "none" // disable check management (create/update)

	hm, err := cgm.NewCirconusMetrics(cmc)
	if err != nil {
		return errors.Wrap(err, "receiver cgm")
	}

	metrics = hm
	return nil
}

// Flush returns current metrics
func Flush() *cgm.Metrics {
	initCGM()
	metricsmu.Lock()
	defer metricsmu.Unlock()

	return metrics.FlushMetrics()
}

// Parse handles incoming PUT/POST requests
func Parse(id string, data io.ReadCloser) error {
	var tmp tags.JSONMetrics // cgm.Metrics
	if err := json.NewDecoder(data).Decode(&tmp); err != nil {
		if serr, ok := err.(*json.SyntaxError); ok {
			return errors.Wrapf(serr, "id:%s - offset %d", id, serr.Offset)
		}
		return errors.Wrapf(err, "parsing json for %s", id)
	}

	initCGM()
	metricsmu.Lock()
	defer metricsmu.Unlock()

	for name, metric := range tmp {
		metricName := strings.Join([]string{id, name}, config.MetricNameSeparator)
		if len(metric.Tags) > 0 {
			st, err := tags.PrepStreamTags(strings.Join(metric.Tags, tags.Separator))
			if err != nil {
				log.Warn().Err(err).Str("pkg", "receiver").Str("metric", metricName).Strs("tags", metric.Tags).Msg("ignoring tags")
			}
			metricName += st
		}
		switch metric.Type {
		case "i":
			if v := parseInt32(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "I":
			if v := parseUint32(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "l":
			if v := parseInt64(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "L":
			if v := parseUint64(metricName, metric); v != nil {
				metrics.AddGauge(metricName, *v)
			}
		case "n":
			v, isHist := parseFloat(metricName, metric)
			if v != nil {
				metrics.AddGauge(metricName, *v)
			} else if isHist {
				samples := parseHistogram(metricName, metric)
				if samples != nil && len(*samples) > 0 {
					for _, sample := range *samples {
						if sample.bucket {
							metrics.RecordCountForValue(metricName, sample.value, sample.count)
						} else {
							metrics.RecordValue(metricName, sample.value)
						}
					}
				}
			}
		case "s":
			metrics.SetText(metricName, fmt.Sprintf("%v", metric.Value))
		default:
			log.Warn().Str("metric", metricName).Str("type", metric.Type).Str("pkg", "receiver").Msg("unsupported metric type")
		}
	}

	return nil
}

func parseInt32(metricName string, metric tags.JSONMetric) *int32 {
	switch t := metric.Value.(type) {
	case float64:
		v := int32(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseInt(metric.Value.(string), 10, 32)
		if err == nil {
			v2 := int32(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseUint32(metricName string, metric tags.JSONMetric) *uint32 {
	switch t := metric.Value.(type) {
	case float64:
		v := uint32(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseUint(metric.Value.(string), 10, 32)
		if err == nil {
			v2 := uint32(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseInt64(metricName string, metric tags.JSONMetric) *int64 {
	switch t := metric.Value.(type) {
	case float64:
		v := int64(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseInt(metric.Value.(string), 10, 64)
		if err == nil {
			v2 := int64(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseUint64(metricName string, metric tags.JSONMetric) *uint64 {
	switch t := metric.Value.(type) {
	case float64:
		v := uint64(metric.Value.(float64))
		return &v
	case string:
		v, err := strconv.ParseUint(metric.Value.(string), 10, 64)
		if err == nil {
			v2 := uint64(v)
			return &v2
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil
}

func parseFloat(metricName string, metric tags.JSONMetric) (*float64, bool) {
	switch t := metric.Value.(type) {
	case float64:
		v := metric.Value.(float64)
		return &v, false
	case []interface{}: // treat as histogram
		return nil, true
	case string:
		v, err := strconv.ParseFloat(metric.Value.(string), 64)
		if err == nil {
			v2 := float64(v)
			return &v2, false
		}
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Err(err).
			Msg("parsing")
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for metric type")
	}
	return nil, false
}

type histSample struct {
	bucket bool
	count  int64
	value  float64
}

func parseHistogram(metricName string, metric tags.JSONMetric) *[]histSample {
	switch t := metric.Value.(type) {
	case []interface{}:
		ret := make([]histSample, 0, len(metric.Value.([]interface{})))
		for idx, v := range metric.Value.([]interface{}) {
			switch t2 := v.(type) {
			case float64:
				ret = append(ret, histSample{bucket: false, value: v.(float64)})
			case string:
				sv := v.(string)
				if !strings.Contains(sv, "H[") {
					v2, err := strconv.ParseFloat(sv, 64)
					if err != nil {
						log.Error().
							Str("pkg", "receiver").
							Str("metric", metricName).
							Interface("value", v).
							Int("position", idx).
							Err(err).
							Msg("parsing histogram sample")
						continue
					}
					ret = append(ret, histSample{bucket: false, value: float64(v2)})
					continue
				}

				//
				// it's an encoded histogram sample H[value]=count
				//
				bucket := ""
				count := ""
				matches := histogramRx.FindAllStringSubmatch(sv, -1)
				for _, match := range matches {
					for idx, val := range match {
						switch histogramRxNames[idx] {
						case "bucket":
							bucket = val
						case "count":
							count = val
						}
					}
				}
				if bucket == "" || count == "" {
					log.Error().
						Str("pkg", "receiver").
						Str("metric", metricName).
						Str("sample", sv).
						Int("position", idx).
						Msg("invalid encoded histogram sample")
					continue
				}
				b, err := strconv.ParseFloat(bucket, 64)
				if err != nil {
					log.Error().
						Str("pkg", "receiver").
						Str("metric", metricName).
						Str("sample", sv).
						Int("position", idx).
						Err(err).
						Msg("encoded histogram sample, value parse")
					continue
				}
				c, err := strconv.ParseInt(count, 10, 64)
				if err != nil {
					log.Error().
						Str("pkg", "receiver").
						Str("metric", metricName).
						Str("sample", sv).
						Int("position", idx).
						Err(err).
						Msg("encoded histogram sample, count parse")
					continue
				}
				ret = append(ret, histSample{bucket: true, value: b, count: c})
			default:
				log.Error().
					Str("pkg", "receiver").
					Str("metric", metricName).
					Interface("value", v).
					Int("position", idx).
					Str("type", fmt.Sprintf("%T", t2)).
					Msg("invalid value type for histogram sample")
			}
		}
		if len(ret) == 0 {
			return nil
		}
		return &ret
	default:
		log.Error().
			Str("pkg", "receiver").
			Str("metric", metricName).
			Interface("value", metric).
			Str("type", fmt.Sprintf("%T", t)).
			Msg("invalid value type for histogram")
	}
	return nil
}
<|endoftext|>