<commit_before>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol"
	"golang.org/x/net/context"
)

const (
	// error param keys
	errorParamTlf        = "tlf"
	errorParamMode       = "mode"
	errorParamFeature    = "feature"
	errorParamUsername   = "username"
	errorParamExternal   = "external"
	errorParamRekeySelf  = "rekeyself"
	errorParamUsageBytes = "usageBytes"
	errorParamLimitBytes = "limitBytes"

	// error operation modes
	errorModeRead  = "read"
	errorModeWrite = "write"

	// features that aren't ready yet
	errorFeatureFileLimit = "2gbFileLimit"
	errorFeatureDirLimit  = "512kbDirLimit"
)

const connectionStatusConnected keybase1.FSStatusCode = keybase1.FSStatusCode_START
const connectionStatusDisconnected keybase1.FSStatusCode = keybase1.FSStatusCode_ERROR

// noErrorNames are lookup names that should not result in an error
// notification.  These should all be reserved or illegal Keybase
// usernames that will never be associated with a real account.
var noErrorNames = map[string]bool{
	"objects":        true, // git shells
	"gemfile":        true, // rvm
	"Gemfile":        true, // rvm
	"devfs":          true, // lsof?  KBFS-823
	"_mtn":           true, // emacs on Linux
	"_MTN":           true, // emacs on Linux
	"docker-machine": true, // docker shell stuff
	"HEAD":           true, // git shell
	"Keybase.app":    true, // some OSX mount thing
	"DCIM":           true, // looking for digital pic folder
	"Thumbs.db":      true, // Windows mounts
}

// ReporterKBPKI implements the Notify function of the Reporter
// interface in addition to embedding ReporterSimple for error
// tracking.  Notify will make RPCs to the keybase daemon.
type ReporterKBPKI struct {
	*ReporterSimple
	config       Config
	log          logger.Logger
	notifyBuffer chan *keybase1.FSNotification
	canceler     func()
}

// NewReporterKBPKI creates a new ReporterKBPKI.
func NewReporterKBPKI(config Config, maxErrors, bufSize int) *ReporterKBPKI {
	r := &ReporterKBPKI{
		ReporterSimple: NewReporterSimple(config.Clock(), maxErrors),
		config:         config,
		log:            config.MakeLogger(""),
		notifyBuffer:   make(chan *keybase1.FSNotification, bufSize),
	}
	var ctx context.Context
	ctx, r.canceler = context.WithCancel(context.Background())
	go r.send(ctx)
	return r
}

// ReportErr implements the Reporter interface for ReporterKBPKI.
func (r *ReporterKBPKI) ReportErr(ctx context.Context,
	tlfName CanonicalTlfName, public bool, mode ErrorModeType, err error) {
	r.ReporterSimple.ReportErr(ctx, tlfName, public, mode, err)

	// Fire off error popups
	var n *keybase1.FSNotification
	params := make(map[string]string)
	var code keybase1.FSErrorType = -1
	switch e := err.(type) {
	case ReadAccessError:
		code = keybase1.FSErrorType_ACCESS_DENIED
	case WriteAccessError:
		code = keybase1.FSErrorType_ACCESS_DENIED
	case NoSuchUserError:
		if !noErrorNames[e.Input] {
			code = keybase1.FSErrorType_USER_NOT_FOUND
			params[errorParamUsername] = e.Input
			if strings.ContainsAny(e.Input, "@:") {
				params[errorParamExternal] = "true"
			} else {
				params[errorParamExternal] = "false"
			}
		}
	case UnverifiableTlfUpdateError:
		code = keybase1.FSErrorType_REVOKED_DATA_DETECTED
	case NoCurrentSessionError:
		code = keybase1.FSErrorType_NOT_LOGGED_IN
	case NeedSelfRekeyError:
		code = keybase1.FSErrorType_REKEY_NEEDED
		params[errorParamRekeySelf] = "true"
	case NeedOtherRekeyError:
		code = keybase1.FSErrorType_REKEY_NEEDED
		params[errorParamRekeySelf] = "false"
	case NoSuchFolderListError:
		if !noErrorNames[e.Name] {
			code = keybase1.FSErrorType_BAD_FOLDER
			params[errorParamTlf] = fmt.Sprintf("/keybase/%s", e.Name)
		}
	case FileTooBigError:
		code = keybase1.FSErrorType_NOT_IMPLEMENTED
		params[errorParamFeature] = errorFeatureFileLimit
	case DirTooBigError:
		code = keybase1.FSErrorType_NOT_IMPLEMENTED
		params[errorParamFeature] = errorFeatureDirLimit
	case NewMetadataVersionError:
		code = keybase1.FSErrorType_OLD_VERSION
		err = OutdatedVersionError{}
	case NewDataVersionError:
		code = keybase1.FSErrorType_OLD_VERSION
		err = OutdatedVersionError{}
	case OverQuotaWarning:
		code = keybase1.FSErrorType_OVER_QUOTA
		params[errorParamUsageBytes] = strconv.FormatInt(e.UsageBytes, 10)
		params[errorParamLimitBytes] = strconv.FormatInt(e.LimitBytes, 10)
	}

	if code < 0 && err == context.DeadlineExceeded {
		code = keybase1.FSErrorType_TIMEOUT
	}

	if code >= 0 {
		n = errorNotification(err, code, tlfName, public, mode, params)
		r.Notify(ctx, n)
	}
}

// Notify implements the Reporter interface for ReporterKBPKI.
//
// TODO: might be useful to get the debug tags out of ctx and store
//       them in the notifyBuffer as well so that send() can put
//       them back in its context.
func (r *ReporterKBPKI) Notify(ctx context.Context, notification *keybase1.FSNotification) {
	select {
	case r.notifyBuffer <- notification:
	default:
		r.log.CDebugf(ctx, "ReporterKBPKI: notify buffer full, dropping %+v",
			notification)
	}
}

// Shutdown implements the Reporter interface for ReporterKBPKI.
func (r *ReporterKBPKI) Shutdown() {
	r.canceler()
	close(r.notifyBuffer)
}

// send takes notifications out of notifyBuffer and sends them to
// the keybase daemon.
func (r *ReporterKBPKI) send(ctx context.Context) {
	for notification := range r.notifyBuffer {
		if err := r.config.KeybaseDaemon().Notify(ctx, notification); err != nil {
			r.log.CDebugf(ctx, "ReporterDaemon: error sending notification: %s",
				err)
		}
	}
}

// writeNotification creates FSNotifications from paths for file
// write events.
func writeNotification(file path, finish bool) *keybase1.FSNotification {
	n := baseNotification(file, finish)
	if file.Tlf.IsPublic() {
		n.NotificationType = keybase1.FSNotificationType_SIGNING
	} else {
		n.NotificationType = keybase1.FSNotificationType_ENCRYPTING
	}
	return n
}

// readNotification creates FSNotifications from paths for file
// read events.
func readNotification(file path, finish bool) *keybase1.FSNotification {
	n := baseNotification(file, finish)
	if file.Tlf.IsPublic() {
		n.NotificationType = keybase1.FSNotificationType_VERIFYING
	} else {
		n.NotificationType = keybase1.FSNotificationType_DECRYPTING
	}
	return n
}

// rekeyNotification creates FSNotifications from TlfHandles for rekey
// events.
func rekeyNotification(ctx context.Context, config Config, handle *TlfHandle, finish bool) *keybase1.FSNotification {
	code := keybase1.FSStatusCode_START
	if finish {
		code = keybase1.FSStatusCode_FINISH
	}

	return &keybase1.FSNotification{
		PublicTopLevelFolder: handle.IsPublic(),
		Filename:             string(handle.GetCanonicalName()),
		StatusCode:           code,
		NotificationType:     keybase1.FSNotificationType_REKEYING,
	}
}

// connectionNotification creates FSNotifications based on whether
// or not KBFS is online.
func connectionNotification(status keybase1.FSStatusCode) *keybase1.FSNotification {
	// TODO finish placeholder
	return &keybase1.FSNotification{
		NotificationType: keybase1.FSNotificationType_CONNECTION,
		StatusCode:       status,
	}
}

// baseNotification creates a basic FSNotification without a
// NotificationType from a path.
func baseNotification(file path, finish bool) *keybase1.FSNotification {
	code := keybase1.FSStatusCode_START
	if finish {
		code = keybase1.FSStatusCode_FINISH
	}

	return &keybase1.FSNotification{
		PublicTopLevelFolder: file.Tlf.IsPublic(),
		Filename:             file.String(),
		StatusCode:           code,
	}
}

// genericErrorNotification creates FSNotifications for generic
// errors, and makes it look like a read error.
func errorNotification(err error, errType keybase1.FSErrorType,
	tlfName CanonicalTlfName, public bool, mode ErrorModeType,
	params map[string]string) *keybase1.FSNotification {
	if tlfName != "" {
		params[errorParamTlf] = string(tlfName)
	}
	var nType keybase1.FSNotificationType
	switch mode {
	case ReadMode:
		params[errorParamMode] = errorModeRead
		if public {
			nType = keybase1.FSNotificationType_VERIFYING
		} else {
			nType = keybase1.FSNotificationType_DECRYPTING
		}
	case WriteMode:
		params[errorParamMode] = errorModeWrite
		if public {
			nType = keybase1.FSNotificationType_SIGNING
		} else {
			nType = keybase1.FSNotificationType_ENCRYPTING
		}
	default:
		panic(fmt.Sprintf("Unknown mode: %v", mode))
	}
	return &keybase1.FSNotification{
		Filename:             params[errorParamTlf],
		StatusCode:           keybase1.FSStatusCode_ERROR,
		Status:               err.Error(),
		ErrorType:            errType,
		Params:               params,
		NotificationType:     nType,
		PublicTopLevelFolder: public,
	}
}

func mdReadSuccessNotification(tlfName CanonicalTlfName,
	public bool) *keybase1.FSNotification {
	params := make(map[string]string)
	if tlfName != "" {
		params[errorParamTlf] = string(tlfName)
	}
	return &keybase1.FSNotification{
		Filename:             params[errorParamTlf],
		StatusCode:           keybase1.FSStatusCode_START,
		NotificationType:     keybase1.FSNotificationType_MD_READ_SUCCESS,
		PublicTopLevelFolder: public,
	}
}
<commit_msg>reporter_kbpki: blacklist more error names we've seen recently (#155)<commit_after>// Copyright 2016 Keybase Inc. All rights reserved.
// Use of this source code is governed by a BSD
// license that can be found in the LICENSE file.

package libkbfs

import (
	"fmt"
	"strconv"
	"strings"

	"github.com/keybase/client/go/logger"
	keybase1 "github.com/keybase/client/go/protocol"
	"golang.org/x/net/context"
)

const (
	// error param keys
	errorParamTlf        = "tlf"
	errorParamMode       = "mode"
	errorParamFeature    = "feature"
	errorParamUsername   = "username"
	errorParamExternal   = "external"
	errorParamRekeySelf  = "rekeyself"
	errorParamUsageBytes = "usageBytes"
	errorParamLimitBytes = "limitBytes"

	// error operation modes
	errorModeRead  = "read"
	errorModeWrite = "write"

	// features that aren't ready yet
	errorFeatureFileLimit = "2gbFileLimit"
	errorFeatureDirLimit  = "512kbDirLimit"
)

const connectionStatusConnected keybase1.FSStatusCode = keybase1.FSStatusCode_START
const connectionStatusDisconnected keybase1.FSStatusCode = keybase1.FSStatusCode_ERROR

// noErrorNames are lookup names that should not result in an error
// notification.  These should all be reserved or illegal Keybase
// usernames that will never be associated with a real account.
var noErrorNames = map[string]bool{
	"objects":        true, // git shells
	"gemfile":        true, // rvm
	"Gemfile":        true, // rvm
	"devfs":          true, // lsof?  KBFS-823
	"_mtn":           true, // emacs on Linux
	"_MTN":           true, // emacs on Linux
	"docker-machine": true, // docker shell stuff
	"HEAD":           true, // git shell
	"Keybase.app":    true, // some OSX mount thing
	"DCIM":           true, // looking for digital pic folder
	"Thumbs.db":      true, // Windows mounts
	"config":         true, // Windows, possibly 7-Zip?
	"m4root":         true, // OS X, iMovie?
	"BDMV":           true, // OS X, iMovie?
	"node_modules":   true, // Some npm shell configuration
}

// ReporterKBPKI implements the Notify function of the Reporter
// interface in addition to embedding ReporterSimple for error
// tracking.  Notify will make RPCs to the keybase daemon.
type ReporterKBPKI struct {
	*ReporterSimple
	config       Config
	log          logger.Logger
	notifyBuffer chan *keybase1.FSNotification
	canceler     func()
}

// NewReporterKBPKI creates a new ReporterKBPKI.
func NewReporterKBPKI(config Config, maxErrors, bufSize int) *ReporterKBPKI {
	r := &ReporterKBPKI{
		ReporterSimple: NewReporterSimple(config.Clock(), maxErrors),
		config:         config,
		log:            config.MakeLogger(""),
		notifyBuffer:   make(chan *keybase1.FSNotification, bufSize),
	}
	var ctx context.Context
	ctx, r.canceler = context.WithCancel(context.Background())
	go r.send(ctx)
	return r
}

// ReportErr implements the Reporter interface for ReporterKBPKI.
func (r *ReporterKBPKI) ReportErr(ctx context.Context,
	tlfName CanonicalTlfName, public bool, mode ErrorModeType, err error) {
	r.ReporterSimple.ReportErr(ctx, tlfName, public, mode, err)

	// Fire off error popups
	var n *keybase1.FSNotification
	params := make(map[string]string)
	var code keybase1.FSErrorType = -1
	switch e := err.(type) {
	case ReadAccessError:
		code = keybase1.FSErrorType_ACCESS_DENIED
	case WriteAccessError:
		code = keybase1.FSErrorType_ACCESS_DENIED
	case NoSuchUserError:
		if !noErrorNames[e.Input] {
			code = keybase1.FSErrorType_USER_NOT_FOUND
			params[errorParamUsername] = e.Input
			if strings.ContainsAny(e.Input, "@:") {
				params[errorParamExternal] = "true"
			} else {
				params[errorParamExternal] = "false"
			}
		}
	case UnverifiableTlfUpdateError:
		code = keybase1.FSErrorType_REVOKED_DATA_DETECTED
	case NoCurrentSessionError:
		code = keybase1.FSErrorType_NOT_LOGGED_IN
	case NeedSelfRekeyError:
		code = keybase1.FSErrorType_REKEY_NEEDED
		params[errorParamRekeySelf] = "true"
	case NeedOtherRekeyError:
		code = keybase1.FSErrorType_REKEY_NEEDED
		params[errorParamRekeySelf] = "false"
	case NoSuchFolderListError:
		if !noErrorNames[e.Name] {
			code = keybase1.FSErrorType_BAD_FOLDER
			params[errorParamTlf] = fmt.Sprintf("/keybase/%s", e.Name)
		}
	case FileTooBigError:
		code = keybase1.FSErrorType_NOT_IMPLEMENTED
		params[errorParamFeature] = errorFeatureFileLimit
	case DirTooBigError:
		code = keybase1.FSErrorType_NOT_IMPLEMENTED
		params[errorParamFeature] = errorFeatureDirLimit
	case NewMetadataVersionError:
		code = keybase1.FSErrorType_OLD_VERSION
		err = OutdatedVersionError{}
	case NewDataVersionError:
		code = keybase1.FSErrorType_OLD_VERSION
		err = OutdatedVersionError{}
	case OverQuotaWarning:
		code = keybase1.FSErrorType_OVER_QUOTA
		params[errorParamUsageBytes] = strconv.FormatInt(e.UsageBytes, 10)
		params[errorParamLimitBytes] = strconv.FormatInt(e.LimitBytes, 10)
	}

	if code < 0 && err == context.DeadlineExceeded {
		code = keybase1.FSErrorType_TIMEOUT
	}

	if code >= 0 {
		n = errorNotification(err, code, tlfName, public, mode, params)
		r.Notify(ctx, n)
	}
}

// Notify implements the Reporter interface for ReporterKBPKI.
//
// TODO: might be useful to get the debug tags out of ctx and store
//       them in the notifyBuffer as well so that send() can put
//       them back in its context.
func (r *ReporterKBPKI) Notify(ctx context.Context, notification *keybase1.FSNotification) {
	select {
	case r.notifyBuffer <- notification:
	default:
		r.log.CDebugf(ctx, "ReporterKBPKI: notify buffer full, dropping %+v",
			notification)
	}
}

// Shutdown implements the Reporter interface for ReporterKBPKI.
func (r *ReporterKBPKI) Shutdown() {
	r.canceler()
	close(r.notifyBuffer)
}

// send takes notifications out of notifyBuffer and sends them to
// the keybase daemon.
func (r *ReporterKBPKI) send(ctx context.Context) {
	for notification := range r.notifyBuffer {
		if err := r.config.KeybaseDaemon().Notify(ctx, notification); err != nil {
			r.log.CDebugf(ctx, "ReporterDaemon: error sending notification: %s",
				err)
		}
	}
}

// writeNotification creates FSNotifications from paths for file
// write events.
func writeNotification(file path, finish bool) *keybase1.FSNotification {
	n := baseNotification(file, finish)
	if file.Tlf.IsPublic() {
		n.NotificationType = keybase1.FSNotificationType_SIGNING
	} else {
		n.NotificationType = keybase1.FSNotificationType_ENCRYPTING
	}
	return n
}

// readNotification creates FSNotifications from paths for file
// read events.
func readNotification(file path, finish bool) *keybase1.FSNotification {
	n := baseNotification(file, finish)
	if file.Tlf.IsPublic() {
		n.NotificationType = keybase1.FSNotificationType_VERIFYING
	} else {
		n.NotificationType = keybase1.FSNotificationType_DECRYPTING
	}
	return n
}

// rekeyNotification creates FSNotifications from TlfHandles for rekey
// events.
func rekeyNotification(ctx context.Context, config Config, handle *TlfHandle, finish bool) *keybase1.FSNotification {
	code := keybase1.FSStatusCode_START
	if finish {
		code = keybase1.FSStatusCode_FINISH
	}

	return &keybase1.FSNotification{
		PublicTopLevelFolder: handle.IsPublic(),
		Filename:             string(handle.GetCanonicalName()),
		StatusCode:           code,
		NotificationType:     keybase1.FSNotificationType_REKEYING,
	}
}

// connectionNotification creates FSNotifications based on whether
// or not KBFS is online.
func connectionNotification(status keybase1.FSStatusCode) *keybase1.FSNotification {
	// TODO finish placeholder
	return &keybase1.FSNotification{
		NotificationType: keybase1.FSNotificationType_CONNECTION,
		StatusCode:       status,
	}
}

// baseNotification creates a basic FSNotification without a
// NotificationType from a path.
func baseNotification(file path, finish bool) *keybase1.FSNotification {
	code := keybase1.FSStatusCode_START
	if finish {
		code = keybase1.FSStatusCode_FINISH
	}

	return &keybase1.FSNotification{
		PublicTopLevelFolder: file.Tlf.IsPublic(),
		Filename:             file.String(),
		StatusCode:           code,
	}
}

// genericErrorNotification creates FSNotifications for generic
// errors, and makes it look like a read error.
func errorNotification(err error, errType keybase1.FSErrorType,
	tlfName CanonicalTlfName, public bool, mode ErrorModeType,
	params map[string]string) *keybase1.FSNotification {
	if tlfName != "" {
		params[errorParamTlf] = string(tlfName)
	}
	var nType keybase1.FSNotificationType
	switch mode {
	case ReadMode:
		params[errorParamMode] = errorModeRead
		if public {
			nType = keybase1.FSNotificationType_VERIFYING
		} else {
			nType = keybase1.FSNotificationType_DECRYPTING
		}
	case WriteMode:
		params[errorParamMode] = errorModeWrite
		if public {
			nType = keybase1.FSNotificationType_SIGNING
		} else {
			nType = keybase1.FSNotificationType_ENCRYPTING
		}
	default:
		panic(fmt.Sprintf("Unknown mode: %v", mode))
	}
	return &keybase1.FSNotification{
		Filename:             params[errorParamTlf],
		StatusCode:           keybase1.FSStatusCode_ERROR,
		Status:               err.Error(),
		ErrorType:            errType,
		Params:               params,
		NotificationType:     nType,
		PublicTopLevelFolder: public,
	}
}

func mdReadSuccessNotification(tlfName CanonicalTlfName,
	public bool) *keybase1.FSNotification {
	params := make(map[string]string)
	if tlfName != "" {
		params[errorParamTlf] = string(tlfName)
	}
	return &keybase1.FSNotification{
		Filename:             params[errorParamTlf],
		StatusCode:           keybase1.FSStatusCode_START,
		NotificationType:     keybase1.FSNotificationType_MD_READ_SUCCESS,
		PublicTopLevelFolder: public,
	}
}
<|endoftext|>


<commit_before>package libp2p

import (
	"github.com/libp2p/go-libp2p/p2p/host/autonat"
	circuitv1 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv1/relay"
	circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
	"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"
	"github.com/libp2p/go-libp2p/p2p/protocol/identify"
	"github.com/libp2p/go-libp2p/p2p/protocol/ping"

	rcmgr "github.com/libp2p/go-libp2p-resource-manager"
)

// SetDefaultServiceLimits sets the default limits for bundled libp2p services
//
// More specifically this sets the following limits:
// - identify:
//   128 streams in, 128 streams out, 256 streams total, 4MB min, 64MB max svc memory
//   16/16/32 streams per peer
// - ping:
//   128 streams in, 128 sreams out, 256 streasms total, 4MB min, 64MB max svc memory
//   2/3/4 streams per peer
// - autonat
//   128 streams in, 128 streams out, 128 streams total, 4MB min, 64MB max svc memory
//   2/2/2 streams per peer
// - holepunch
//   128 streams in, 128 streams out, 128 streams total, 4MB min, 64MB max svc memory
//   2/2/2 streams per peer
// - relay v1 and v2 (separate services)
//   1024 streams in, 1024 streams out, 1024 streams total, 4MB min, 64MB max svc memory
//   64/64/64 streams per peer
func SetDefaultServiceLimits(limiter *rcmgr.BasicLimiter) {
	peerSvcLimit := func(numStreamsIn, numStreamsOut, numStreamsTotal int) rcmgr.Limit {
		return &rcmgr.StaticLimit{
			// memory: 256kb for window buffers plus some change for message buffers per stream
			Memory: int64(numStreamsTotal * (256<<10 + 16384)),
			BaseLimit: rcmgr.BaseLimit{
				StreamsInbound:  numStreamsIn,
				StreamsOutbound: numStreamsOut,
				Streams:         numStreamsTotal,
			},
		}
	}

	if limiter.ServiceLimits == nil {
		limiter.ServiceLimits = make(map[string]rcmgr.Limit)
	}
	if limiter.ServicePeerLimits == nil {
		limiter.ServicePeerLimits = make(map[string]rcmgr.Limit)
	}
	// identify
	if _, ok := limiter.ServiceLimits[identify.ServiceName]; !ok {
		limiter.ServiceLimits[identify.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 256)     // max 256 streams -- symmetric
		limiter.ServicePeerLimits[identify.ServiceName] = peerSvcLimit(16, 16, 32)
	}
	// ping
	if _, ok := limiter.ServiceLimits[ping.ServiceName]; !ok {
		limiter.ServiceLimits[ping.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 128)     // max 128 streams - asymmetric
		limiter.ServicePeerLimits[ping.ServiceName] = peerSvcLimit(2, 3, 4)
	}
	// autonat
	if _, ok := limiter.ServiceLimits[autonat.ServiceName]; !ok {
		limiter.ServiceLimits[autonat.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 128)     // max 128 streams - asymmetric
		limiter.ServicePeerLimits[autonat.ServiceName] = peerSvcLimit(2, 2, 2)
	}
	// holepunch
	if _, ok := limiter.ServiceLimits[holepunch.ServiceName]; !ok {
		limiter.ServiceLimits[holepunch.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 256)     // max 256 streams - symmetric
		limiter.ServicePeerLimits[autonat.ServiceName] = peerSvcLimit(2, 2, 2)
	}
	// relay/v1
	if _, ok := limiter.ServiceLimits[circuitv1.ServiceName]; !ok {
		limiter.ServiceLimits[circuitv1.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(1024, 1024, 1024)  // max 1024 streams - asymmetric
		limiter.ServicePeerLimits[circuitv1.ServiceName] = peerSvcLimit(64, 64, 64)
	}
	// relay/v2
	if _, ok := limiter.ServiceLimits[circuitv2.ServiceName]; !ok {
		limiter.ServiceLimits[circuitv2.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(1024, 1024, 1024)  // max 1024 streams - asymmetric
		limiter.ServicePeerLimits[circuitv2.ServiceName] = peerSvcLimit(64, 64, 64)
	}
}
<commit_msg>decouple service from peer service limit setting for defaults<commit_after>package libp2p

import (
	"github.com/libp2p/go-libp2p/p2p/host/autonat"
	circuitv1 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv1/relay"
	circuitv2 "github.com/libp2p/go-libp2p/p2p/protocol/circuitv2/relay"
	"github.com/libp2p/go-libp2p/p2p/protocol/holepunch"
	"github.com/libp2p/go-libp2p/p2p/protocol/identify"
	"github.com/libp2p/go-libp2p/p2p/protocol/ping"

	rcmgr "github.com/libp2p/go-libp2p-resource-manager"
)

// SetDefaultServiceLimits sets the default limits for bundled libp2p services
//
// More specifically this sets the following limits:
// - identify:
//   128 streams in, 128 streams out, 256 streams total, 4MB min, 64MB max svc memory
//   16/16/32 streams per peer
// - ping:
//   128 streams in, 128 sreams out, 256 streasms total, 4MB min, 64MB max svc memory
//   2/3/4 streams per peer
// - autonat
//   128 streams in, 128 streams out, 128 streams total, 4MB min, 64MB max svc memory
//   2/2/2 streams per peer
// - holepunch
//   128 streams in, 128 streams out, 128 streams total, 4MB min, 64MB max svc memory
//   2/2/2 streams per peer
// - relay v1 and v2 (separate services)
//   1024 streams in, 1024 streams out, 1024 streams total, 4MB min, 64MB max svc memory
//   64/64/64 streams per peer
func SetDefaultServiceLimits(limiter *rcmgr.BasicLimiter) {
	peerSvcLimit := func(numStreamsIn, numStreamsOut, numStreamsTotal int) rcmgr.Limit {
		return &rcmgr.StaticLimit{
			// memory: 256kb for window buffers plus some change for message buffers per stream
			Memory: int64(numStreamsTotal * (256<<10 + 16384)),
			BaseLimit: rcmgr.BaseLimit{
				StreamsInbound:  numStreamsIn,
				StreamsOutbound: numStreamsOut,
				Streams:         numStreamsTotal,
			},
		}
	}

	if limiter.ServiceLimits == nil {
		limiter.ServiceLimits = make(map[string]rcmgr.Limit)
	}
	if limiter.ServicePeerLimits == nil {
		limiter.ServicePeerLimits = make(map[string]rcmgr.Limit)
	}
	// identify
	if _, ok := limiter.ServiceLimits[identify.ServiceName]; !ok {
		limiter.ServiceLimits[identify.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 256)     // max 256 streams -- symmetric
	}
	if _, ok := limiter.ServicePeerLimits[identify.ServiceName]; !ok {
		limiter.ServicePeerLimits[identify.ServiceName] = peerSvcLimit(16, 16, 32)
	}
	// ping
	if _, ok := limiter.ServiceLimits[ping.ServiceName]; !ok {
		limiter.ServiceLimits[ping.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 128)     // max 128 streams - asymmetric
	}
	if _, ok := limiter.ServicePeerLimits[ping.ServiceName]; !ok {
		limiter.ServicePeerLimits[ping.ServiceName] = peerSvcLimit(2, 3, 4)
	}
	// autonat
	if _, ok := limiter.ServiceLimits[autonat.ServiceName]; !ok {
		limiter.ServiceLimits[autonat.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 128)     // max 128 streams - asymmetric
	}
	if _, ok := limiter.ServicePeerLimits[autonat.ServiceName]; !ok {
		limiter.ServicePeerLimits[autonat.ServiceName] = peerSvcLimit(2, 2, 2)
	}
	// holepunch
	if _, ok := limiter.ServiceLimits[holepunch.ServiceName]; !ok {
		limiter.ServiceLimits[holepunch.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(128, 128, 256)     // max 256 streams - symmetric
	}
	if _, ok := limiter.ServicePeerLimits[holepunch.ServiceName]; !ok {
		limiter.ServicePeerLimits[autonat.ServiceName] = peerSvcLimit(2, 2, 2)
	}
	// relay/v1
	if _, ok := limiter.ServiceLimits[circuitv1.ServiceName]; !ok {
		limiter.ServiceLimits[circuitv1.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(1024, 1024, 1024)  // max 1024 streams - asymmetric
	}
	if _, ok := limiter.ServicePeerLimits[circuitv1.ServiceName]; !ok {
		limiter.ServicePeerLimits[circuitv1.ServiceName] = peerSvcLimit(64, 64, 64)
	}
	// relay/v2
	if _, ok := limiter.ServiceLimits[circuitv2.ServiceName]; !ok {
		limiter.ServiceLimits[circuitv2.ServiceName] = limiter.DefaultServiceLimits.
			WithMemoryLimit(1, 4<<20, 64<<20). // max 64MB service memory
			WithStreamLimit(1024, 1024, 1024)  // max 1024 streams - asymmetric
	}
	if _, ok := limiter.ServicePeerLimits[circuitv2.ServiceName]; !ok {
		limiter.ServicePeerLimits[circuitv2.ServiceName] = peerSvcLimit(64, 64, 64)
	}
}
<|endoftext|>


<commit_before>// Package iris v3.0.0-beta.1
//
// Note: When 'Station', we mean the Iris type.
package iris

import (
	"os"

	"sync"

	"time"

	"strconv"

	"github.com/fatih/color"
	"github.com/kataras/iris/config"
	"github.com/kataras/iris/logger"
	"github.com/kataras/iris/mail"
	"github.com/kataras/iris/render/rest"
	"github.com/kataras/iris/render/template"
	"github.com/kataras/iris/server"
	"github.com/kataras/iris/sessions"
	//  memory loads the memory session provider
	_ "github.com/kataras/iris/sessions/providers/memory"
	// _ redis loads the redis session provider
	_ "github.com/kataras/iris/sessions/providers/redis"
	"github.com/kataras/iris/utils"
	"github.com/kataras/iris/websocket"
	"github.com/klauspost/compress/gzip"
)

const (
	// Version of the iris
	Version = "v3.0.0-beta.1"
	banner  = `                        _____      _
                       |_   _|    (_)
                         | |  ____ _  ___
                         | | | __|| |/ __|
                        _| |_| |  | |\__ \
                       |_____|_|  |_||___/ ` + Version + `
                                                 				 `
)

/* for conversion */

var (
	// HTMLEngine conversion for config.HTMLEngine
	HTMLEngine = config.HTMLEngine
	// PongoEngine conversion for config.PongoEngine
	PongoEngine = config.PongoEngine
	// MarkdownEngine conversion for config.MarkdownEngine
	MarkdownEngine = config.MarkdownEngine
	// JadeEngine conversion for config.JadeEngine
	JadeEngine = config.JadeEngine
	// AmberEngine conversion for config.AmberEngine
	AmberEngine = config.AmberEngine

	// DefaultEngine conversion for config.DefaultEngine
	DefaultEngine = config.DefaultEngine
	// NoEngine conversion for config.NoEngine
	NoEngine = config.NoEngine
	//

	NoLayout = config.NoLayout
)

/* */

var stationsRunning = 0

type (

	// Iris is the container of all, server, router, cache and the sync.Pool
	Iris struct {
		*router
		config          *config.Iris
		server          *server.Server
		plugins         *PluginContainer
		rest            *rest.Render
		templates       *template.Template
		sessionManager  *sessions.Manager
		websocketServer websocket.Server
		mailService     mail.Service
		logger          *logger.Logger
		gzipWriterPool  sync.Pool // this pool is used everywhere needed in the iris for example inside party-> StaticSimple
	}
)

// New creates and returns a new iris station.
//
// Receives an optional config.Iris as parameter
// If empty then config.Default() is used instead
func New(cfg ...config.Iris) *Iris {

	c := config.Default().Merge(cfg)

	// create the Iris
	s := &Iris{config: &c, plugins: &PluginContainer{}}
	// create & set the router
	s.router = newRouter(s)

	// set the Logger
	s.logger = logger.New()

	// set the gzip writer pool
	s.gzipWriterPool = sync.Pool{New: func() interface{} { return &gzip.Writer{} }}
	return s
}

// newContextPool returns a new context pool, internal method used in tree and router
func (s *Iris) newContextPool() sync.Pool {
	return sync.Pool{New: func() interface{} {
		return &Context{station: s}
	}}
}

func (s *Iris) initTemplates() {
	if s.templates == nil { // because if .Templates() called before server's listen, s.templates != nil when PreListen
		//  init the templates

		// set the custom iris-direct-integration functions, layout and no-layout  if  HTMLEngine is used
		if s.config.Render.Template.Engine == config.HTMLEngine {
			funcs := map[string]interface{}{
				"url": func(routeName string, args ...interface{}) (string, error) {
					r := s.RouteByName(routeName)
					// check if not found
					if r.GetPath() == "" {
						return "", ErrRenderRouteNotFound.Format(routeName)
					}

					if result, ok := r.parse(args...); ok {
						return result, nil
					}
					return "", nil
				},
			}
			for k, v := range funcs {
				// we don't want to override the user's LayoutFuncs, user should be able to override anything.
				if s.config.Render.Template.HTMLTemplate.LayoutFuncs[k] == nil {
					s.config.Render.Template.HTMLTemplate.LayoutFuncs[k] = v
				}

				if s.config.Render.Template.HTMLTemplate.Funcs[k] == nil {
					s.config.Render.Template.HTMLTemplate.Funcs[k] = v
				}

			}

		}

		s.templates = template.New(s.config.Render.Template)

	}

}

func (s *Iris) initWebsocketServer() {
	if s.websocketServer == nil {
		// enable websocket if config.Websocket.Endpoint != ""
		if s.config.Websocket.Endpoint != "" {
			s.websocketServer = websocket.New(s, s.config.Websocket)
		}
	}
}

func (s *Iris) initMailService() {
	if s.mailService == nil {
		// enable mail sender  service if configs are valid
		if s.config.Mail.Host != "" && s.config.Mail.Username != "" && s.config.Mail.Password != "" {
			s.mailService = mail.New(s.config.Mail)
		}
	}
}

func (s *Iris) printBanner() {
	c := color.New(color.FgHiBlue).Add(color.Bold)
	printTicker := utils.NewTicker()
	i := 0
	printTicker.OnTick(func() {
		c.Printf("%c", banner[i])
		i++
		if i == len(banner) {
			printTicker.Stop()

			c.Add(color.FgGreen)
			stationsRunning++
			c.Println()
			if stationsRunning > 1 {
				c.Println("Server[" + strconv.Itoa(stationsRunning) + "]")
			}
			c.Printf("%s: Running at %s\n", time.Now().Format(config.TimeFormat), s.server.Config.ListeningAddr)
			c.DisableColor()
		}
	})

	printTicker.Start(time.Duration(1) * time.Millisecond)

}

// PreListen call router's optimize, sets the server's handler and notice the plugins
// capital because we need it sometimes, for example inside the graceful
// receives the config.Server
// returns the station's Server (*server.Server)
// it's a non-blocking func
func (s *Iris) PreListen(opt config.Server) *server.Server {
	// set the logger's state
	s.logger.SetEnable(!s.config.DisableLog)
	// router preparation, runs only once even if called more than one time.
	if !s.router.optimized {
		s.router.optimize()

		s.server = server.New(opt)
		s.server.SetHandler(s.router.ServeRequest)

		if s.config.MaxRequestBodySize > 0 {
			s.server.MaxRequestBodySize = int(s.config.MaxRequestBodySize)
		}
	}

	s.plugins.DoPreListen(s)

	return s.server
}

// PostListen sets the rest render, template engine, sessions and notice the plugins
// capital because we need it sometimes, for example inside the graceful
// it's a non-blocking func
func (s *Iris) PostListen() {
	//if not error opening the server, then:

	//set the  rest (for Data, Text, JSON, JSONP, XML)
	s.rest = rest.New(s.config.Render.Rest)
	// set the templates
	s.initTemplates()
	// set the session manager if we have a provider
	if s.config.Sessions.Provider != "" {
		s.sessionManager = sessions.New(s.config.Sessions)
	}

	// set the websocket
	s.initWebsocketServer()
	if !s.config.DisableBanner {
		s.printBanner()
	}

	s.plugins.DoPostListen(s)
}

// listen is internal method, open the server with specific options passed by the Listen and ListenTLS
// it's a blocking func
func (s *Iris) listen(opt config.Server) (err error) {
	s.PreListen(opt)

	if err = s.server.OpenServer(); err == nil {
		s.PostListen()

		ch := make(chan os.Signal)
		<-ch
		s.Close()
	}
	return
}

// ListenWithErr starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It returns an error you are responsible how to handle this
// if you need a func to panic on error use the Listen
// ex: log.Fatal(iris.ListenWithErr(":8080"))
func (s *Iris) ListenWithErr(addr string) error {
	opt := config.Server{ListeningAddr: addr}
	return s.listen(opt)
}

// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It panics on error if you need a func to return an error use the ListenWithErr
// ex: iris.Listen(":8080")
func (s *Iris) Listen(addr string) {
	if err := s.ListenWithErr(addr); err != nil {
		panic(err)
	}
}

// ListenTLSWithErr Starts a https server with certificates,
// if you use this method the requests of the form of 'http://' will fail
// only https:// connections are allowed
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It returns an error you are responsible how to handle this
// if you need a func to panic on error use the ListenTLS
// ex: log.Fatal(iris.ListenTLSWithErr(":8080","yourfile.cert","yourfile.key"))
func (s *Iris) ListenTLSWithErr(addr string, certFile, keyFile string) error {
	opt := config.Server{ListeningAddr: addr, CertFile: certFile, KeyFile: keyFile}
	return s.listen(opt)
}

// ListenTLS Starts a https server with certificates,
// if you use this method the requests of the form of 'http://' will fail
// only https:// connections are allowed
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It panics on error if you need a func to return an error use the ListenTLSWithErr
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
func (s *Iris) ListenTLS(addr string, certFile, keyFile string) {
	if err := s.ListenTLSWithErr(addr, certFile, keyFile); err != nil {
		panic(err)
	}
}

// CloseWithErr is used to close the tcp listener from the server, returns an error
func (s *Iris) CloseWithErr() error {
	s.plugins.DoPreClose(s)
	return s.server.CloseServer()
}

//Close terminates the server and panic if error occurs
func (s *Iris) Close() {
	if err := s.CloseWithErr(); err != nil {
		panic(err)
	}
}

// Server returns the server
func (s *Iris) Server() *server.Server {
	return s.server
}

// Plugins returns the plugin container
func (s *Iris) Plugins() *PluginContainer {
	return s.plugins
}

// Config returns the configs
func (s *Iris) Config() *config.Iris {
	return s.config
}

// Logger returns the logger
func (s *Iris) Logger() *logger.Logger {
	return s.logger
}

// Rest returns the rest render
func (s *Iris) Rest() *rest.Render {
	return s.rest
}

// Templates returns the template render
func (s *Iris) Templates() *template.Template {
	s.initTemplates() // for any case the user called .Templates() before server's listen
	return s.templates
}

// Websocket returns the websocket server
func (s *Iris) Websocket() websocket.Server {
	s.initWebsocketServer() // for any case the user called .Websocket() before server's listen
	return s.websocketServer
}

// Mail returns the mail sender service
func (s *Iris) Mail() mail.Service {
	s.initMailService()
	return s.mailService
}
<commit_msg>Fix banner sometimes error because conc<commit_after>// Package iris v3.0.0-beta.1
//
// Note: When 'Station', we mean the Iris type.
package iris

import (
	"os"

	"sync"

	"time"

	"strconv"

	"github.com/fatih/color"
	"github.com/kataras/iris/config"
	"github.com/kataras/iris/logger"
	"github.com/kataras/iris/mail"
	"github.com/kataras/iris/render/rest"
	"github.com/kataras/iris/render/template"
	"github.com/kataras/iris/server"
	"github.com/kataras/iris/sessions"
	//  memory loads the memory session provider
	_ "github.com/kataras/iris/sessions/providers/memory"
	// _ redis loads the redis session provider
	_ "github.com/kataras/iris/sessions/providers/redis"
	"github.com/kataras/iris/utils"
	"github.com/kataras/iris/websocket"
	"github.com/klauspost/compress/gzip"
)

const (
	// Version of the iris
	Version = "v3.0.0-beta.1"
	banner  = `                        _____      _
                       |_   _|    (_)
                         | |  ____ _  ___
                         | | | __|| |/ __|
                        _| |_| |  | |\__ \
                       |_____|_|  |_||___/ ` + Version + `
                                                 				 `
)

/* for conversion */

var (
	// HTMLEngine conversion for config.HTMLEngine
	HTMLEngine = config.HTMLEngine
	// PongoEngine conversion for config.PongoEngine
	PongoEngine = config.PongoEngine
	// MarkdownEngine conversion for config.MarkdownEngine
	MarkdownEngine = config.MarkdownEngine
	// JadeEngine conversion for config.JadeEngine
	JadeEngine = config.JadeEngine
	// AmberEngine conversion for config.AmberEngine
	AmberEngine = config.AmberEngine

	// DefaultEngine conversion for config.DefaultEngine
	DefaultEngine = config.DefaultEngine
	// NoEngine conversion for config.NoEngine
	NoEngine = config.NoEngine
	//

	NoLayout = config.NoLayout
)

/* */

var stationsRunning = 0

type (

	// Iris is the container of all, server, router, cache and the sync.Pool
	Iris struct {
		*router
		config          *config.Iris
		server          *server.Server
		plugins         *PluginContainer
		rest            *rest.Render
		templates       *template.Template
		sessionManager  *sessions.Manager
		websocketServer websocket.Server
		mailService     mail.Service
		logger          *logger.Logger
		gzipWriterPool  sync.Pool // this pool is used everywhere needed in the iris for example inside party-> StaticSimple
	}
)

// New creates and returns a new iris station.
//
// Receives an optional config.Iris as parameter
// If empty then config.Default() is used instead
func New(cfg ...config.Iris) *Iris {

	c := config.Default().Merge(cfg)

	// create the Iris
	s := &Iris{config: &c, plugins: &PluginContainer{}}
	// create & set the router
	s.router = newRouter(s)

	// set the Logger
	s.logger = logger.New()

	// set the gzip writer pool
	s.gzipWriterPool = sync.Pool{New: func() interface{} { return &gzip.Writer{} }}
	return s
}

// newContextPool returns a new context pool, internal method used in tree and router
func (s *Iris) newContextPool() sync.Pool {
	return sync.Pool{New: func() interface{} {
		return &Context{station: s}
	}}
}

func (s *Iris) initTemplates() {
	if s.templates == nil { // because if .Templates() called before server's listen, s.templates != nil when PreListen
		//  init the templates

		// set the custom iris-direct-integration functions, layout and no-layout  if  HTMLEngine is used
		if s.config.Render.Template.Engine == config.HTMLEngine {
			funcs := map[string]interface{}{
				"url": func(routeName string, args ...interface{}) (string, error) {
					r := s.RouteByName(routeName)
					// check if not found
					if r.GetPath() == "" {
						return "", ErrRenderRouteNotFound.Format(routeName)
					}

					if result, ok := r.parse(args...); ok {
						return result, nil
					}
					return "", nil
				},
			}
			for k, v := range funcs {
				// we don't want to override the user's LayoutFuncs, user should be able to override anything.
				if s.config.Render.Template.HTMLTemplate.LayoutFuncs[k] == nil {
					s.config.Render.Template.HTMLTemplate.LayoutFuncs[k] = v
				}

				if s.config.Render.Template.HTMLTemplate.Funcs[k] == nil {
					s.config.Render.Template.HTMLTemplate.Funcs[k] = v
				}

			}

		}

		s.templates = template.New(s.config.Render.Template)

	}

}

func (s *Iris) initWebsocketServer() {
	if s.websocketServer == nil {
		// enable websocket if config.Websocket.Endpoint != ""
		if s.config.Websocket.Endpoint != "" {
			s.websocketServer = websocket.New(s, s.config.Websocket)
		}
	}
}

func (s *Iris) initMailService() {
	if s.mailService == nil {
		// enable mail sender  service if configs are valid
		if s.config.Mail.Host != "" && s.config.Mail.Username != "" && s.config.Mail.Password != "" {
			s.mailService = mail.New(s.config.Mail)
		}
	}
}

func (s *Iris) printBanner() {
	c := color.New(color.FgHiBlue).Add(color.Bold)
	printTicker := utils.NewTicker()
	i := 0
	printTicker.OnTick(func() {
		if len(banner) <= i {
			printTicker.Stop()

			c.Add(color.FgGreen)
			stationsRunning++
			c.Println()
			if stationsRunning > 1 {
				c.Println("Server[" + strconv.Itoa(stationsRunning) + "]")
			}
			c.Printf("%s: Running at %s\n", time.Now().Format(config.TimeFormat), s.server.Config.ListeningAddr)
			c.DisableColor()
			return
		}
		c.Printf("%c", banner[i])
		i++

	})

	printTicker.Start(time.Duration(1) * time.Millisecond)

}

// PreListen call router's optimize, sets the server's handler and notice the plugins
// capital because we need it sometimes, for example inside the graceful
// receives the config.Server
// returns the station's Server (*server.Server)
// it's a non-blocking func
func (s *Iris) PreListen(opt config.Server) *server.Server {
	// set the logger's state
	s.logger.SetEnable(!s.config.DisableLog)
	// router preparation, runs only once even if called more than one time.
	if !s.router.optimized {
		s.router.optimize()

		s.server = server.New(opt)
		s.server.SetHandler(s.router.ServeRequest)

		if s.config.MaxRequestBodySize > 0 {
			s.server.MaxRequestBodySize = int(s.config.MaxRequestBodySize)
		}
	}

	s.plugins.DoPreListen(s)

	return s.server
}

// PostListen sets the rest render, template engine, sessions and notice the plugins
// capital because we need it sometimes, for example inside the graceful
// it's a non-blocking func
func (s *Iris) PostListen() {
	//if not error opening the server, then:

	//set the  rest (for Data, Text, JSON, JSONP, XML)
	s.rest = rest.New(s.config.Render.Rest)
	// set the templates
	s.initTemplates()
	// set the session manager if we have a provider
	if s.config.Sessions.Provider != "" {
		s.sessionManager = sessions.New(s.config.Sessions)
	}

	// set the websocket
	s.initWebsocketServer()
	if !s.config.DisableBanner {
		s.printBanner()
	}

	s.plugins.DoPostListen(s)
}

// listen is internal method, open the server with specific options passed by the Listen and ListenTLS
// it's a blocking func
func (s *Iris) listen(opt config.Server) (err error) {
	s.PreListen(opt)

	if err = s.server.OpenServer(); err == nil {
		s.PostListen()

		ch := make(chan os.Signal)
		<-ch
		s.Close()
	}
	return
}

// ListenWithErr starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It returns an error you are responsible how to handle this
// if you need a func to panic on error use the Listen
// ex: log.Fatal(iris.ListenWithErr(":8080"))
func (s *Iris) ListenWithErr(addr string) error {
	opt := config.Server{ListeningAddr: addr}
	return s.listen(opt)
}

// Listen starts the standalone http server
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It panics on error if you need a func to return an error use the ListenWithErr
// ex: iris.Listen(":8080")
func (s *Iris) Listen(addr string) {
	if err := s.ListenWithErr(addr); err != nil {
		panic(err)
	}
}

// ListenTLSWithErr Starts a https server with certificates,
// if you use this method the requests of the form of 'http://' will fail
// only https:// connections are allowed
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It returns an error you are responsible how to handle this
// if you need a func to panic on error use the ListenTLS
// ex: log.Fatal(iris.ListenTLSWithErr(":8080","yourfile.cert","yourfile.key"))
func (s *Iris) ListenTLSWithErr(addr string, certFile, keyFile string) error {
	opt := config.Server{ListeningAddr: addr, CertFile: certFile, KeyFile: keyFile}
	return s.listen(opt)
}

// ListenTLS Starts a https server with certificates,
// if you use this method the requests of the form of 'http://' will fail
// only https:// connections are allowed
// which listens to the addr parameter which as the form of
// host:port or just port
//
// It panics on error if you need a func to return an error use the ListenTLSWithErr
// ex: iris.ListenTLS(":8080","yourfile.cert","yourfile.key")
func (s *Iris) ListenTLS(addr string, certFile, keyFile string) {
	if err := s.ListenTLSWithErr(addr, certFile, keyFile); err != nil {
		panic(err)
	}
}

// CloseWithErr is used to close the tcp listener from the server, returns an error
func (s *Iris) CloseWithErr() error {
	s.plugins.DoPreClose(s)
	return s.server.CloseServer()
}

//Close terminates the server and panic if error occurs
func (s *Iris) Close() {
	if err := s.CloseWithErr(); err != nil {
		panic(err)
	}
}

// Server returns the server
func (s *Iris) Server() *server.Server {
	return s.server
}

// Plugins returns the plugin container
func (s *Iris) Plugins() *PluginContainer {
	return s.plugins
}

// Config returns the configs
func (s *Iris) Config() *config.Iris {
	return s.config
}

// Logger returns the logger
func (s *Iris) Logger() *logger.Logger {
	return s.logger
}

// Rest returns the rest render
func (s *Iris) Rest() *rest.Render {
	return s.rest
}

// Templates returns the template render
func (s *Iris) Templates() *template.Template {
	s.initTemplates() // for any case the user called .Templates() before server's listen
	return s.templates
}

// Websocket returns the websocket server
func (s *Iris) Websocket() websocket.Server {
	s.initWebsocketServer() // for any case the user called .Websocket() before server's listen
	return s.websocketServer
}

// Mail returns the mail sender service
func (s *Iris) Mail() mail.Service {
	s.initMailService()
	return s.mailService
}
<|endoftext|>


<commit_before>package zabbix

import (
	"fmt"
	"github.com/AlekSi/reflector"
)

type (
	ItemType  int
	ValueType int
	DataType  int
	DeltaType int
)

const (
	ZabbixAgent       ItemType = 0
	SNMPv1Agent       ItemType = 1
	ZabbixTrapper     ItemType = 2
	SimpleCheck       ItemType = 3
	SNMPv2Agent       ItemType = 4
	ZabbixInternal    ItemType = 5
	SNMPv3Agent       ItemType = 6
	ZabbixAgentActive ItemType = 7
	ZabbixAggregate   ItemType = 8
	WebItem           ItemType = 9
	ExternalCheck     ItemType = 10
	DatabaseMonitor   ItemType = 11
	IPMIAgent         ItemType = 12
	SSHAgent          ItemType = 13
	TELNETAgent       ItemType = 14
	Calculated        ItemType = 15
	JMXAgent          ItemType = 16

	Float     ValueType = 0
	Character ValueType = 1
	Log       ValueType = 2
	Unsigned  ValueType = 3
	Text      ValueType = 4

	Decimal     DataType = 0
	Octal       DataType = 1
	Hexadecimal DataType = 2
	Boolean     DataType = 3

	AsIs  DeltaType = 0
	Speed DeltaType = 1
	Delta DeltaType = 2
)

// https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/definitions
type Item struct {
	ItemId      string    `json:"itemid,omitempty"`
	Delay       int       `json:"delay"`
	HostId      string    `json:"hostid"`
	InterfaceId string    `json:"interfaceid,omitempty"`
	Key         string    `json:"key_"`
	Name        string    `json:"name"`
	Type        ItemType  `json:"type"`
	ValueType   ValueType `json:"value_type"`
	DataType    DataType  `json:"data_type"`
	Delta       DeltaType `json:"delta"`
	Description string    `json:"description"`
	Error       string    `json:"error"`
	History     int       `json:"history,omitempty"`
	Trends      int       `json:"trends,omitempty"`

	// Fields below used only when creating applications
	ApplicationIds []string `json:"applications,omitempty"`
}

type Items []Item

// Converts slice to map by key. Panics if there are duplicate keys.
func (items Items) ByKey() (res map[string]Item) {
	res = make(map[string]Item, len(items))
	for _, i := range items {
		_, present := res[i.Key]
		if present {
			panic(fmt.Errorf("Duplicate key %s", i.Key))
		}
		res[i.Key] = i
	}
	return
}

// Wrapper for item.get https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/get
func (api *API) ItemsGet(params Params) (res Items, err error) {
	if _, present := params["output"]; !present {
		params["output"] = "extend"
	}
	response, err := api.CallWithError("item.get", params)
	if err != nil {
		return
	}

	reflector.MapsToStructs2(response.Result.([]interface{}), &res, reflector.Strconv, "json")
	return
}

// Gets items by application Id.
func (api *API) ItemsGetByApplicationId(id string) (res Items, err error) {
	return api.ItemsGet(Params{"applicationids": id})
}

// Wrapper for item.create: https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/create
func (api *API) ItemsCreate(items Items) (err error) {
	response, err := api.CallWithError("item.create", items)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	itemids := result["itemids"].([]interface{})
	for i, id := range itemids {
		items[i].ItemId = id.(string)
	}
	return
}

// Wrapper for item.delete: https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/delete
// Cleans ItemId in all items elements if call succeed.
func (api *API) ItemsDelete(items Items) (err error) {
	ids := make([]string, len(items))
	for i, item := range items {
		ids[i] = item.ItemId
	}

	err = api.ItemsDeleteByIds(ids)
	if err == nil {
		for i := range items {
			items[i].ItemId = ""
		}
	}
	return
}

// Wrapper for item.delete: https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/delete
func (api *API) ItemsDeleteByIds(ids []string) (err error) {
	response, err := api.CallWithError("item.delete", ids)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	itemids := result["itemids"].(map[string]interface{})
	if len(ids) != len(itemids) {
		err = &ExpectedMore{len(ids), len(itemids)}
	}
	return
}
<commit_msg>Fix item.delete for Zabbix 2.2.2.<commit_after>package zabbix

import (
	"fmt"
	"github.com/AlekSi/reflector"
)

type (
	ItemType  int
	ValueType int
	DataType  int
	DeltaType int
)

const (
	ZabbixAgent       ItemType = 0
	SNMPv1Agent       ItemType = 1
	ZabbixTrapper     ItemType = 2
	SimpleCheck       ItemType = 3
	SNMPv2Agent       ItemType = 4
	ZabbixInternal    ItemType = 5
	SNMPv3Agent       ItemType = 6
	ZabbixAgentActive ItemType = 7
	ZabbixAggregate   ItemType = 8
	WebItem           ItemType = 9
	ExternalCheck     ItemType = 10
	DatabaseMonitor   ItemType = 11
	IPMIAgent         ItemType = 12
	SSHAgent          ItemType = 13
	TELNETAgent       ItemType = 14
	Calculated        ItemType = 15
	JMXAgent          ItemType = 16

	Float     ValueType = 0
	Character ValueType = 1
	Log       ValueType = 2
	Unsigned  ValueType = 3
	Text      ValueType = 4

	Decimal     DataType = 0
	Octal       DataType = 1
	Hexadecimal DataType = 2
	Boolean     DataType = 3

	AsIs  DeltaType = 0
	Speed DeltaType = 1
	Delta DeltaType = 2
)

// https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/definitions
type Item struct {
	ItemId      string    `json:"itemid,omitempty"`
	Delay       int       `json:"delay"`
	HostId      string    `json:"hostid"`
	InterfaceId string    `json:"interfaceid,omitempty"`
	Key         string    `json:"key_"`
	Name        string    `json:"name"`
	Type        ItemType  `json:"type"`
	ValueType   ValueType `json:"value_type"`
	DataType    DataType  `json:"data_type"`
	Delta       DeltaType `json:"delta"`
	Description string    `json:"description"`
	Error       string    `json:"error"`
	History     int       `json:"history,omitempty"`
	Trends      int       `json:"trends,omitempty"`

	// Fields below used only when creating applications
	ApplicationIds []string `json:"applications,omitempty"`
}

type Items []Item

// Converts slice to map by key. Panics if there are duplicate keys.
func (items Items) ByKey() (res map[string]Item) {
	res = make(map[string]Item, len(items))
	for _, i := range items {
		_, present := res[i.Key]
		if present {
			panic(fmt.Errorf("Duplicate key %s", i.Key))
		}
		res[i.Key] = i
	}
	return
}

// Wrapper for item.get https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/get
func (api *API) ItemsGet(params Params) (res Items, err error) {
	if _, present := params["output"]; !present {
		params["output"] = "extend"
	}
	response, err := api.CallWithError("item.get", params)
	if err != nil {
		return
	}

	reflector.MapsToStructs2(response.Result.([]interface{}), &res, reflector.Strconv, "json")
	return
}

// Gets items by application Id.
func (api *API) ItemsGetByApplicationId(id string) (res Items, err error) {
	return api.ItemsGet(Params{"applicationids": id})
}

// Wrapper for item.create: https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/create
func (api *API) ItemsCreate(items Items) (err error) {
	response, err := api.CallWithError("item.create", items)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	itemids := result["itemids"].([]interface{})
	for i, id := range itemids {
		items[i].ItemId = id.(string)
	}
	return
}

// Wrapper for item.delete: https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/delete
// Cleans ItemId in all items elements if call succeed.
func (api *API) ItemsDelete(items Items) (err error) {
	ids := make([]string, len(items))
	for i, item := range items {
		ids[i] = item.ItemId
	}

	err = api.ItemsDeleteByIds(ids)
	if err == nil {
		for i := range items {
			items[i].ItemId = ""
		}
	}
	return
}

// Wrapper for item.delete: https://www.zabbix.com/documentation/2.0/manual/appendix/api/item/delete
func (api *API) ItemsDeleteByIds(ids []string) (err error) {
	response, err := api.CallWithError("item.delete", ids)
	if err != nil {
		return
	}

	result := response.Result.(map[string]interface{})
	itemids1, ok := result["itemids"].([]interface{})
	l := len(itemids1)
	if !ok {
		// some versions actually return map there
		itemids2 := result["itemids"].(map[string]interface{})
		l = len(itemids2)
	}
	if len(ids) != l {
		err = &ExpectedMore{len(ids), l}
	}
	return
}
<|endoftext|>


<commit_before>package adapter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/gliderlabs/logspout/router"
)

const (
	logglyAddr          = "https://logs-01.loggly.com"
	logglyEventEndpoint = "/bulk"
	logglyTagsHeader    = "X-LOGGLY-TAG"
)

// Adapter satisfies the router.LogAdapter interface by providing Stream which
// passes all messages to loggly.
type Adapter struct {
	token      string
	client     *http.Client
	tags       string
	log        *log.Logger
	queue      chan logglyMessage
	m          sync.Mutex
	bufferSize int
}

// New returns an Adapter that receives messages from logspout. Additionally,
// it launches a goroutine to buffer and flush messages to loggly.
func New(logglyToken, tags string, bufferSize int) *Adapter {

	adapter := &Adapter{
		client:     http.DefaultClient,
		bufferSize: bufferSize,
		log:        log.New(os.Stdout, "logspout-loggly", log.LstdFlags),
		queue:      make(chan logglyMessage),
		token:      logglyToken,
		tags:       tags,
	}

	go adapter.readQueue()

	return adapter
}

// Stream satisfies the router.LogAdapter interface and passes all messages to
// Loggly
func (l *Adapter) Stream(logstream chan *router.Message) {
	for m := range logstream {
		l.queue <- logglyMessage{
			Message:           m.Data,
			ContainerName:     m.Container.Name,
			ContainerID:       m.Container.ID,
			ContainerImage:    m.Container.Config.Image,
			ContainerHostname: m.Container.Config.Hostname,
		}
	}
}

func (l *Adapter) readQueue() {
	buffer := l.newBuffer()

	for msg := range l.queue {
		if len(buffer) == cap(buffer) {
			l.flushBuffer(buffer)
			buffer = l.newBuffer()
		}

		buffer = append(buffer, msg)
	}
}

func (l *Adapter) newBuffer() []logglyMessage {
	return make([]logglyMessage, 0, l.bufferSize)
}

func (l *Adapter) flushBuffer(buffer []logglyMessage) {
	var data bytes.Buffer

	for _, msg := range buffer {
		j, _ := json.Marshal(msg)
		data.Write(j)
		data.WriteString("\n")
	}

	req, _ := http.NewRequest(
		"POST",
		fmt.Sprintf("%s%s/%s", logglyAddr, logglyEventEndpoint, l.token),
		&data,
	)

	go l.sendRequestToLoggly(req)
}

func (l *Adapter) sendRequestToLoggly(req *http.Request) {
	l.m.Lock()
	defer l.m.Unlock()
	resp, err := l.client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		l.log.Println(
			fmt.Errorf(
				"error from client: %s",
				err.Error(),
			),
		)
		return
	}

	if resp.StatusCode != http.StatusOK {
		l.log.Println(
			fmt.Errorf(
				"received a non 200 status code when sending message to loggly: %s",
				err.Error(),
			),
		)
	}
}
<commit_msg>periodic flush the message buffer<commit_after>package adapter

import (
	"bytes"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
	"sync"
	"time"

	"github.com/gliderlabs/logspout/router"
)

const (
	logglyAddr          = "https://logs-01.loggly.com"
	logglyEventEndpoint = "/bulk"
	logglyTagsHeader    = "X-LOGGLY-TAG"
)

// Adapter satisfies the router.LogAdapter interface by providing Stream which
// passes all messages to loggly.
type Adapter struct {
	token      string
	client     *http.Client
	tags       string
	log        *log.Logger
	queue      chan logglyMessage
	m          sync.Mutex
	bufferSize int
}

// New returns an Adapter that receives messages from logspout. Additionally,
// it launches a goroutine to buffer and flush messages to loggly.
func New(logglyToken, tags string, bufferSize int) *Adapter {

	adapter := &Adapter{
		client:     http.DefaultClient,
		bufferSize: bufferSize,
		log:        log.New(os.Stdout, "logspout-loggly", log.LstdFlags),
		queue:      make(chan logglyMessage),
		token:      logglyToken,
		tags:       tags,
	}

	go adapter.readQueue()

	return adapter
}

// Stream satisfies the router.LogAdapter interface and passes all messages to
// Loggly
func (l *Adapter) Stream(logstream chan *router.Message) {
	for m := range logstream {
		l.queue <- logglyMessage{
			Message:           m.Data,
			ContainerName:     m.Container.Name,
			ContainerID:       m.Container.ID,
			ContainerImage:    m.Container.Config.Image,
			ContainerHostname: m.Container.Config.Hostname,
		}
	}
}

func (l *Adapter) readQueue() {
	buffer := l.newBuffer()

	periodicFlush := time.NewTicker(time.Second * 10)

	for {
		select {
		case msg := <-l.queue:
			if len(buffer) == cap(buffer) {
				l.flushBuffer(buffer)
				buffer = l.newBuffer()
			}

			buffer = append(buffer, msg)

		case <-periodicFlush.C:
			if len(buffer) > 0 {
				l.flushBuffer(buffer)
				buffer = l.newBuffer()
			}
		}
	}
}

func (l *Adapter) newBuffer() []logglyMessage {
	return make([]logglyMessage, 0, l.bufferSize)
}

func (l *Adapter) flushBuffer(buffer []logglyMessage) {
	var data bytes.Buffer

	for _, msg := range buffer {
		j, _ := json.Marshal(msg)
		data.Write(j)
		data.WriteString("\n")
	}

	req, _ := http.NewRequest(
		"POST",
		fmt.Sprintf("%s%s/%s", logglyAddr, logglyEventEndpoint, l.token),
		&data,
	)

	go l.sendRequestToLoggly(req)
}

func (l *Adapter) sendRequestToLoggly(req *http.Request) {
	l.m.Lock()
	defer l.m.Unlock()
	resp, err := l.client.Do(req)
	defer resp.Body.Close()

	if err != nil {
		l.log.Println(
			fmt.Errorf(
				"error from client: %s",
				err.Error(),
			),
		)
		return
	}

	if resp.StatusCode != http.StatusOK {
		l.log.Println(
			fmt.Errorf(
				"received a non 200 status code when sending message to loggly: %s",
				err.Error(),
			),
		)
	}
}
<|endoftext|>


<commit_before>package framework

import (
	"fmt"
	"sort"
	"strings"

	"github.com/hashicorp/vault/logical"
)

// Helper which returns a generic regex string for creating endpoint patterns
// that are identified by the given name in the backends
func GenericNameRegex(name string) string {
	return fmt.Sprintf("(?P<%s>\\w[\\w-.]+\\w)", name)
}

// Helper which returns a regex string for optionally accepting the a field
// from the API URL
func OptionalParamRegex(name string) string {
	return fmt.Sprintf("(/(?P<%s>.+))?", name)
}

// PathAppend is a helper for appending lists of paths into a single
// list.
func PathAppend(paths ...[]*Path) []*Path {
	result := make([]*Path, 0, 10)
	for _, ps := range paths {
		result = append(result, ps...)
	}

	return result
}

// Path is a single path that the backend responds to.
type Path struct {
	// Pattern is the pattern of the URL that matches this path.
	//
	// This should be a valid regular expression. Named captures will be
	// exposed as fields that should map to a schema in Fields. If a named
	// capture is not a field in the Fields map, then it will be ignored.
	Pattern string

	// Fields is the mapping of data fields to a schema describing that
	// field. Named captures in the Pattern also map to fields. If a named
	// capture name matches a PUT body name, the named capture takes
	// priority.
	//
	// Note that only named capture fields are available in every operation,
	// whereas all fields are avaiable in the Write operation.
	Fields map[string]*FieldSchema

	// Callbacks are the set of callbacks that are called for a given
	// operation. If a callback for a specific operation is not present,
	// then logical.ErrUnsupportedOperation is automatically generated.
	//
	// The help operation is the only operation that the Path will
	// automatically handle if the Help field is set. If both the Help
	// field is set and there is a callback registered here, then the
	// callback will be called.
	Callbacks map[logical.Operation]OperationFunc

	// ExistenceCheck, if implemented, is used to query whether a given
	// resource exists or not. This is used for ACL purposes: if an Update
	// action is specified, and the existence check returns false, the action
	// is not allowed since the resource must first be created. The reverse is
	// also true. If not specified, the Update action is forced and the user
	// must have UpdateCapability on the path.
	ExistenceCheck func(*logical.Request, *FieldData) (bool, error)

	// Help is text describing how to use this path. This will be used
	// to auto-generate the help operation. The Path will automatically
	// generate a parameter listing and URL structure based on the
	// regular expression, so the help text should just contain a description
	// of what happens.
	//
	// HelpSynopsis is a one-sentence description of the path. This will
	// be automatically line-wrapped at 80 characters.
	//
	// HelpDescription is a long-form description of the path. This will
	// be automatically line-wrapped at 80 characters.
	HelpSynopsis    string
	HelpDescription string
}

func (p *Path) helpCallback(
	req *logical.Request, data *FieldData) (*logical.Response, error) {
	var tplData pathTemplateData
	tplData.Request = req.Path
	tplData.RoutePattern = p.Pattern
	tplData.Synopsis = strings.TrimSpace(p.HelpSynopsis)
	if tplData.Synopsis == "" {
		tplData.Synopsis = "<no synopsis>"
	}
	tplData.Description = strings.TrimSpace(p.HelpDescription)
	if tplData.Description == "" {
		tplData.Description = "<no description>"
	}

	// Alphabetize the fields
	fieldKeys := make([]string, 0, len(p.Fields))
	for k, _ := range p.Fields {
		fieldKeys = append(fieldKeys, k)
	}
	sort.Strings(fieldKeys)

	// Build the field help
	tplData.Fields = make([]pathTemplateFieldData, len(fieldKeys))
	for i, k := range fieldKeys {
		schema := p.Fields[k]
		description := strings.TrimSpace(schema.Description)
		if description == "" {
			description = "<no description>"
		}

		tplData.Fields[i] = pathTemplateFieldData{
			Key:         k,
			Type:        schema.Type.String(),
			Description: description,
		}
	}

	help, err := executeTemplate(pathHelpTemplate, &tplData)
	if err != nil {
		return nil, fmt.Errorf("error executing template: %s", err)
	}

	return logical.HelpResponse(help, nil), nil
}

type pathTemplateData struct {
	Request      string
	RoutePattern string
	Synopsis     string
	Description  string
	Fields       []pathTemplateFieldData
}

type pathTemplateFieldData struct {
	Key         string
	Type        string
	Description string
	URL         bool
}

const pathHelpTemplate = `
Request:        {{.Request}}
Matching Route: {{.RoutePattern}}

{{.Synopsis}}

## PARAMETERS
{{range .Fields}}
{{indent 4 .Key}} ({{.Type}})
{{indent 8 .Description}}
{{end}}
## DESCRIPTION

{{.Description}}
`
<commit_msg>Only show params if there are fields<commit_after>package framework

import (
	"fmt"
	"sort"
	"strings"

	"github.com/hashicorp/vault/logical"
)

// Helper which returns a generic regex string for creating endpoint patterns
// that are identified by the given name in the backends
func GenericNameRegex(name string) string {
	return fmt.Sprintf("(?P<%s>\\w[\\w-.]+\\w)", name)
}

// Helper which returns a regex string for optionally accepting the a field
// from the API URL
func OptionalParamRegex(name string) string {
	return fmt.Sprintf("(/(?P<%s>.+))?", name)
}

// PathAppend is a helper for appending lists of paths into a single
// list.
func PathAppend(paths ...[]*Path) []*Path {
	result := make([]*Path, 0, 10)
	for _, ps := range paths {
		result = append(result, ps...)
	}

	return result
}

// Path is a single path that the backend responds to.
type Path struct {
	// Pattern is the pattern of the URL that matches this path.
	//
	// This should be a valid regular expression. Named captures will be
	// exposed as fields that should map to a schema in Fields. If a named
	// capture is not a field in the Fields map, then it will be ignored.
	Pattern string

	// Fields is the mapping of data fields to a schema describing that
	// field. Named captures in the Pattern also map to fields. If a named
	// capture name matches a PUT body name, the named capture takes
	// priority.
	//
	// Note that only named capture fields are available in every operation,
	// whereas all fields are avaiable in the Write operation.
	Fields map[string]*FieldSchema

	// Callbacks are the set of callbacks that are called for a given
	// operation. If a callback for a specific operation is not present,
	// then logical.ErrUnsupportedOperation is automatically generated.
	//
	// The help operation is the only operation that the Path will
	// automatically handle if the Help field is set. If both the Help
	// field is set and there is a callback registered here, then the
	// callback will be called.
	Callbacks map[logical.Operation]OperationFunc

	// ExistenceCheck, if implemented, is used to query whether a given
	// resource exists or not. This is used for ACL purposes: if an Update
	// action is specified, and the existence check returns false, the action
	// is not allowed since the resource must first be created. The reverse is
	// also true. If not specified, the Update action is forced and the user
	// must have UpdateCapability on the path.
	ExistenceCheck func(*logical.Request, *FieldData) (bool, error)

	// Help is text describing how to use this path. This will be used
	// to auto-generate the help operation. The Path will automatically
	// generate a parameter listing and URL structure based on the
	// regular expression, so the help text should just contain a description
	// of what happens.
	//
	// HelpSynopsis is a one-sentence description of the path. This will
	// be automatically line-wrapped at 80 characters.
	//
	// HelpDescription is a long-form description of the path. This will
	// be automatically line-wrapped at 80 characters.
	HelpSynopsis    string
	HelpDescription string
}

func (p *Path) helpCallback(
	req *logical.Request, data *FieldData) (*logical.Response, error) {
	var tplData pathTemplateData
	tplData.Request = req.Path
	tplData.RoutePattern = p.Pattern
	tplData.Synopsis = strings.TrimSpace(p.HelpSynopsis)
	if tplData.Synopsis == "" {
		tplData.Synopsis = "<no synopsis>"
	}
	tplData.Description = strings.TrimSpace(p.HelpDescription)
	if tplData.Description == "" {
		tplData.Description = "<no description>"
	}

	// Alphabetize the fields
	fieldKeys := make([]string, 0, len(p.Fields))
	for k, _ := range p.Fields {
		fieldKeys = append(fieldKeys, k)
	}
	sort.Strings(fieldKeys)

	// Build the field help
	tplData.Fields = make([]pathTemplateFieldData, len(fieldKeys))
	for i, k := range fieldKeys {
		schema := p.Fields[k]
		description := strings.TrimSpace(schema.Description)
		if description == "" {
			description = "<no description>"
		}

		tplData.Fields[i] = pathTemplateFieldData{
			Key:         k,
			Type:        schema.Type.String(),
			Description: description,
		}
	}

	help, err := executeTemplate(pathHelpTemplate, &tplData)
	if err != nil {
		return nil, fmt.Errorf("error executing template: %s", err)
	}

	return logical.HelpResponse(help, nil), nil
}

type pathTemplateData struct {
	Request      string
	RoutePattern string
	Synopsis     string
	Description  string
	Fields       []pathTemplateFieldData
}

type pathTemplateFieldData struct {
	Key         string
	Type        string
	Description string
	URL         bool
}

const pathHelpTemplate = `
Request:        {{.Request}}
Matching Route: {{.RoutePattern}}

{{.Synopsis}}

{{ if .Fields -}}
## PARAMETERS
{{range .Fields}}
{{indent 4 .Key}} ({{.Type}})
{{indent 8 .Description}}
{{end}}{{end}}
## DESCRIPTION

{{.Description}}
`
<|endoftext|>


<commit_before>package device

import (
	"fmt"

	"github.com/pkg/errors"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/device/nictype"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/validate"
)

// newByType returns a new unitialised device based of the type indicated by the project and device config.
func newByType(state *state.State, projectName string, conf deviceConfig.Device) (device, error) {
	if conf["type"] == "" {
		return nil, fmt.Errorf("Missing device type in config")
	}

	// NIC type is required to lookup network devices.
	nicType, err := nictype.NICType(state, projectName, conf)
	if err != nil {
		return nil, err
	}

	// Lookup device type implementation.
	var dev device
	switch conf["type"] {
	case "nic":
		switch nicType {
		case "physical":
			dev = &nicPhysical{}
		case "ipvlan":
			dev = &nicIPVLAN{}
		case "p2p":
			dev = &nicP2P{}
		case "bridged":
			dev = &nicBridged{}
		case "routed":
			dev = &nicRouted{}
		case "macvlan":
			dev = &nicMACVLAN{}
		case "sriov":
			dev = &nicSRIOV{}
		case "ovn":
			dev = &nicOVN{}
		}
	case "infiniband":
		switch nicType {
		case "physical":
			dev = &infinibandPhysical{}
		case "sriov":
			dev = &infinibandSRIOV{}
		}
	case "gpu":
		switch conf["gputype"] {
		case "mig":
			dev = &gpuMIG{}
		case "mdev":
			dev = &gpuMdev{}
		case "sriov":
			dev = &gpuSRIOV{}
		default:
			dev = &gpuPhysical{}
		}
	case "proxy":
		dev = &proxy{}
	case "usb":
		dev = &usb{}
	case "unix-char", "unix-block":
		dev = &unixCommon{}
	case "unix-hotplug":
		dev = &unixHotplug{}
	case "disk":
		dev = &disk{}
	case "none":
		dev = &none{}
	case "tpm":
		dev = &tpm{}
	case "pci":
		dev = &pci{}
	}

	// Check a valid device type has been found.
	if dev == nil {
		return nil, ErrUnsupportedDevType
	}

	return dev, nil
}

// load instantiates a device and initialises its internal state. It does not validate the config supplied.
func load(inst instance.Instance, state *state.State, projectName string, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) (device, error) {
	// Warning: When validating a profile, inst is expected to be provided as nil.
	dev, err := newByType(state, projectName, conf)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed loading device %q", name)
	}

	// Setup the device's internal variables.
	dev.init(inst, state, name, conf, volatileGet, volatileSet)

	return dev, nil
}

// New instantiates a new device struct, validates the supplied config and sets it into the device.
// If the device type is valid, but the other config validation fails then an instantiated device
// is still returned with the validation error. If an unknown device is requested or the device is
// not compatible with the instance type then an ErrUnsupportedDevType error is returned.
// Note: The supplied config may be modified during validation to enrich. If this is not desired, supply a copy.
func New(inst instance.Instance, state *state.State, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) (Device, error) {
	err := validate.IsDeviceName(name)
	if err != nil {
		return nil, err
	}

	dev, err := load(inst, state, inst.Project(), name, conf, volatileGet, volatileSet)
	if err != nil {
		return nil, err
	}

	err = dev.validateConfig(inst)

	// We still return the instantiated device here, as in some scenarios the caller
	// may still want to use the device (such as when stopping or removing) even if
	// the config validation has failed.
	return dev, err
}

// Validate checks a device's config is valid. This only requires an instance.ConfigReader rather than an full
// blown instance to allow profile devices to be validated too.
// Note: The supplied config may be modified during validation to enrich. If this is not desired, supply a copy.
func Validate(instConfig instance.ConfigReader, state *state.State, name string, conf deviceConfig.Device) error {
	err := validate.IsDeviceName(name)
	if err != nil {
		return err
	}

	dev, err := load(nil, state, instConfig.Project(), name, conf, nil, nil)
	if err != nil {
		return err
	}

	return dev.validateConfig(instConfig)
}

// LoadByType loads a device by type based on its project and config.
// It does not validate config beyond the type fields.
func LoadByType(state *state.State, projectName string, conf deviceConfig.Device) (Type, error) {
	dev, err := newByType(state, projectName, conf)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed loading device type")
	}

	return dev, nil
}
<commit_msg>lxd/device/device/load: Update New to return device even if name validation fails<commit_after>package device

import (
	"fmt"

	"github.com/pkg/errors"

	deviceConfig "github.com/lxc/lxd/lxd/device/config"
	"github.com/lxc/lxd/lxd/device/nictype"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/state"
	"github.com/lxc/lxd/shared/validate"
)

// newByType returns a new unitialised device based of the type indicated by the project and device config.
func newByType(state *state.State, projectName string, conf deviceConfig.Device) (device, error) {
	if conf["type"] == "" {
		return nil, fmt.Errorf("Missing device type in config")
	}

	// NIC type is required to lookup network devices.
	nicType, err := nictype.NICType(state, projectName, conf)
	if err != nil {
		return nil, err
	}

	// Lookup device type implementation.
	var dev device
	switch conf["type"] {
	case "nic":
		switch nicType {
		case "physical":
			dev = &nicPhysical{}
		case "ipvlan":
			dev = &nicIPVLAN{}
		case "p2p":
			dev = &nicP2P{}
		case "bridged":
			dev = &nicBridged{}
		case "routed":
			dev = &nicRouted{}
		case "macvlan":
			dev = &nicMACVLAN{}
		case "sriov":
			dev = &nicSRIOV{}
		case "ovn":
			dev = &nicOVN{}
		}
	case "infiniband":
		switch nicType {
		case "physical":
			dev = &infinibandPhysical{}
		case "sriov":
			dev = &infinibandSRIOV{}
		}
	case "gpu":
		switch conf["gputype"] {
		case "mig":
			dev = &gpuMIG{}
		case "mdev":
			dev = &gpuMdev{}
		case "sriov":
			dev = &gpuSRIOV{}
		default:
			dev = &gpuPhysical{}
		}
	case "proxy":
		dev = &proxy{}
	case "usb":
		dev = &usb{}
	case "unix-char", "unix-block":
		dev = &unixCommon{}
	case "unix-hotplug":
		dev = &unixHotplug{}
	case "disk":
		dev = &disk{}
	case "none":
		dev = &none{}
	case "tpm":
		dev = &tpm{}
	case "pci":
		dev = &pci{}
	}

	// Check a valid device type has been found.
	if dev == nil {
		return nil, ErrUnsupportedDevType
	}

	return dev, nil
}

// load instantiates a device and initialises its internal state. It does not validate the config supplied.
func load(inst instance.Instance, state *state.State, projectName string, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) (device, error) {
	// Warning: When validating a profile, inst is expected to be provided as nil.
	dev, err := newByType(state, projectName, conf)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed loading device %q", name)
	}

	// Setup the device's internal variables.
	dev.init(inst, state, name, conf, volatileGet, volatileSet)

	return dev, nil
}

// New instantiates a new device struct, validates the supplied config and sets it into the device.
// If the device type is valid, but the other config validation fails then an instantiated device
// is still returned with the validation error. If an unknown device is requested or the device is
// not compatible with the instance type then an ErrUnsupportedDevType error is returned.
// Note: The supplied config may be modified during validation to enrich. If this is not desired, supply a copy.
func New(inst instance.Instance, state *state.State, name string, conf deviceConfig.Device, volatileGet VolatileGetter, volatileSet VolatileSetter) (Device, error) {
	dev, err := load(inst, state, inst.Project(), name, conf, volatileGet, volatileSet)
	if err != nil {
		return nil, err
	}

	// We still return the instantiated device here, as in some scenarios the caller
	// may still want to use the device (such as when stopping or removing) even if
	// the config validation has failed.

	err = validate.IsDeviceName(name)
	if err != nil {
		return dev, err
	}

	err = dev.validateConfig(inst)
	if err != nil {
		return dev, err
	}

	return dev, nil
}

// Validate checks a device's config is valid. This only requires an instance.ConfigReader rather than an full
// blown instance to allow profile devices to be validated too.
// Note: The supplied config may be modified during validation to enrich. If this is not desired, supply a copy.
func Validate(instConfig instance.ConfigReader, state *state.State, name string, conf deviceConfig.Device) error {
	err := validate.IsDeviceName(name)
	if err != nil {
		return err
	}

	dev, err := load(nil, state, instConfig.Project(), name, conf, nil, nil)
	if err != nil {
		return err
	}

	return dev.validateConfig(instConfig)
}

// LoadByType loads a device by type based on its project and config.
// It does not validate config beyond the type fields.
func LoadByType(state *state.State, projectName string, conf deviceConfig.Device) (Type, error) {
	dev, err := newByType(state, projectName, conf)
	if err != nil {
		return nil, errors.Wrapf(err, "Failed loading device type")
	}

	return dev, nil
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	fping "github.com/tatsushid/go-fastping"
)

type PingPlugin struct {
	Hosts    []string
	Labels   []string
	Tempfile string
}

func (pp PingPlugin) FetchMetrics() (map[string]interface{}, error) {
	stat := make(map[string]interface{})

	pinger := fping.NewPinger()
	pinger.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
		rttMilliSec := float64(rtt.Nanoseconds()) / 1000.0 / 1000.0
		stat[escapeHostName(addr.String())] = rttMilliSec
	}

	for _, host := range pp.Hosts {
		pinger.AddIP(host)
	}

	err := pinger.Run()
	if err != nil {
		return nil, err
	}

	return stat, nil
}

func (pp PingPlugin) GraphDefinition() map[string](mp.Graphs) {
	metrics := []mp.Metrics{}
	for i := 0; i < len(pp.Hosts); i++ {
		metrics = append(metrics, mp.Metrics{
			Name:    escapeHostName(pp.Hosts[i]),
			Label:   pp.Labels[i],
			Diff:    false,
			Stacked: false,
		})
	}

	return map[string](mp.Graphs){
		"ping.rtt": mp.Graphs{
			Label:   "Ping Round Trip Times",
			Unit:    "float",
			Metrics: metrics,
		},
	}
}

func escapeHostName(host string) string {
	return strings.Replace(host, ".", "_", -1)
}

func validate(ipAddr string) bool {
	r := regexp.MustCompile("^\\d+\\.\\d+\\.\\d+\\.\\d+$")
	return r.MatchString(ipAddr)
}

func parseHostsString(optHost string, strict ...string) ([]string, []string, error) {
	hosts := strings.Split(optHost, ",")
	ips, labels := make([]string, len(hosts)), make([]string, len(hosts))

	for i := 0; i < len(hosts); i++ {
		v := strings.SplitN(hosts[i], ":", 2)
		if !validate(v[0]) {
			ip, err := net.ResolveIPAddr("ip4", v[0])
			if err != nil {
				if strict[0] != "" {
					return nil, nil, err
				}
				continue
			}
			ips[i] = ip.String()
		} else {
			ips[i] = v[0]
		}

		if len(v) == 2 {
			labels[i] = v[1]
		} else {
			labels[i] = v[0]
		}
	}

	return ips, labels, nil
}

func main() {
	optHost := flag.String("host", "127.0.0.1:localhost", "IP Address[:Metric label],IP[:Label],...")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	hosts, labels, err := parseHostsString(*optHost, os.Getenv("MACKEREL_AGENT_PLUGIN_META"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err.Error())
		os.Exit(1)
	}

	var pp PingPlugin
	pp.Hosts = hosts
	pp.Labels = labels

	helper := mp.NewMackerelPlugin(pp)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-ping-%s", escapeHostName(strings.Join(hosts[:], "-")))
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>Returns the average of RTT.<commit_after>package main

import (
	"flag"
	"fmt"
	"net"
	"os"
	"regexp"
	"strings"
	"time"

	mp "github.com/mackerelio/go-mackerel-plugin-helper"
	fping "github.com/tatsushid/go-fastping"
)

type PingPlugin struct {
	Hosts       []string
	Labels      []string
	Tempfile    string
	Count       int
	WaitTime    int
	AcceptCount int
}

func (pp PingPlugin) FetchMetrics() (map[string]interface{}, error) {
	stat := make(map[string]interface{})
	total := make(map[string]float64)
	count := make(map[string]int)

	pinger := fping.NewPinger()
	pinger.OnRecv = func(addr *net.IPAddr, rtt time.Duration) {
		rttMilliSec := float64(rtt.Nanoseconds()) / 1000.0 / 1000.0
		total[escapeHostName(addr.String())] += rttMilliSec
		count[escapeHostName(addr.String())] += 1
	}

	for _, host := range pp.Hosts {
		pinger.AddIP(host)
	}

	pinger.MaxRTT = time.Millisecond * time.Duration(pp.WaitTime)

	for i := 0; i < pp.Count; i++ {
		err := pinger.Run()
		if err != nil {
			return nil, err
		}
	}
	for k, v := range total {
		if count[k] >= (pp.Count - pp.AcceptCount) {
			stat[k] = v / float64(count[k])
		}
	}

	return stat, nil
}

func (pp PingPlugin) GraphDefinition() map[string](mp.Graphs) {
	metrics := []mp.Metrics{}
	for i := 0; i < len(pp.Hosts); i++ {
		metrics = append(metrics, mp.Metrics{
			Name:    escapeHostName(pp.Hosts[i]),
			Label:   pp.Labels[i],
			Diff:    false,
			Stacked: false,
		})
	}

	return map[string](mp.Graphs){
		"ping.rtt": mp.Graphs{
			Label:   "Ping Round Trip Times",
			Unit:    "float",
			Metrics: metrics,
		},
	}
}

func escapeHostName(host string) string {
	return strings.Replace(host, ".", "_", -1)
}

func validate(ipAddr string) bool {
	r := regexp.MustCompile("^\\d+\\.\\d+\\.\\d+\\.\\d+$")
	return r.MatchString(ipAddr)
}

func parseHostsString(optHost string, strict ...string) ([]string, []string, error) {
	hosts := strings.Split(optHost, ",")
	ips, labels := make([]string, len(hosts)), make([]string, len(hosts))

	for i := 0; i < len(hosts); i++ {
		v := strings.SplitN(hosts[i], ":", 2)
		if !validate(v[0]) {
			ip, err := net.ResolveIPAddr("ip4", v[0])
			if err != nil {
				if strict[0] != "" {
					return nil, nil, err
				}
				continue
			}
			ips[i] = ip.String()
		} else {
			ips[i] = v[0]
		}

		if len(v) == 2 {
			labels[i] = v[1]
		} else {
			labels[i] = v[0]
		}
	}

	return ips, labels, nil
}

func main() {
	optHost := flag.String("host", "127.0.0.1:localhost", "IP Address[:Metric label],IP[:Label],...")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	optCount := flag.Int("count", 1, "Sending (and receiving) count ping packets.")
	optWaitTime := flag.Int("waittime", 1000, "Wait time, Max RTT(ms)")
	optAcceptCount := flag.Int("acceptmiss", 0, "Accept out of wait time count ping packets.")
	flag.Parse()

	hosts, labels, err := parseHostsString(*optHost, os.Getenv("MACKEREL_AGENT_PLUGIN_META"))
	if err != nil {
		fmt.Fprintf(os.Stderr, "%v\n", err.Error())
		os.Exit(1)
	}

	var pp PingPlugin
	pp.Hosts = hosts
	pp.Labels = labels
	pp.Count = *optCount
	pp.WaitTime = *optWaitTime
	pp.AcceptCount = *optAcceptCount

	helper := mp.NewMackerelPlugin(pp)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-ping-%s", escapeHostName(strings.Join(hosts[:], "-")))
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<|endoftext|>


<commit_before><commit_msg>update test for swarmapi<commit_after><|endoftext|>


<commit_before>package log

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

var Stderr io.Writer = os.Stderr
var Discard io.Writer = ioutil.Discard

var topics map[string]struct{} = make(map[string]struct{})

func Println(topic string, v ...interface{}) bool {
	if IsOn(topic) {
		fmt.Println(v)
		return true
	}
	return false
}

func SetOutput(w io.Writer) { log.SetOutput(w) }

func On(topic string) {
	for _, t := range strings.Split(topic, " ") {
		topics[t] = struct{}{}
	}
}

func Off(topic string) {
	delete(topics, topic)
}

func IsOn(topic string) bool {
	_, ok := topics[topic]
	return ok
}
<commit_msg>lot of different fixes<commit_after>package log

import (
	"io"
	"io/ioutil"
	"log"
	"os"
	"strings"
)

var Stderr io.Writer = os.Stderr
var Discard io.Writer = ioutil.Discard

var topics map[string]struct{} = make(map[string]struct{})

func Println(topic string, v ...interface{}) bool {
	if IsOn(topic) {
		log.Println(v...)
		return true
	}
	return false
}

func SetOutput(w io.Writer) { log.SetOutput(w) }

func On(topic string) {
	for _, t := range strings.Split(topic, " ") {
		topics[t] = struct{}{}
	}
}

func Off(topic string) {
	delete(topics, topic)
}

func IsOn(topic string) bool {
	_, ok := topics[topic]
	return ok
}
<|endoftext|>


<commit_before>// All this package really does is add a little structure and a little color
// to the standard log package

package logger

import (
	"log"
	"os"

	"github.com/fatih/color"
)

type Logger struct{}

func New() *Logger {
	return &Logger{}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()
var red = color.New(color.FgRed).SprintFunc()

func (l *Logger) Info(v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Warn(v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Fatal(v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	os.Exit(1)
}
func (l *Logger) Panic(v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	panic(logger)
}

func (l *Logger) Infof(format string, v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Warnf(format string, v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	os.Exit(1)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	panic(logger)
}
<commit_msg>Added ability to specify an out that satisfies io.Writer<commit_after>// All this package really does is add a little structure and a little color
// to the standard log package

package logger

import (
	"io"
	"log"
	"os"

	"github.com/fatih/color"
)

type Logger struct {
	out io.Writer
}

func New(out io.Writer) *Logger {
	return &Logger{out: out}
}

var cyan = color.New(color.FgCyan).SprintFunc()
var yellow = color.New(color.FgYellow).SprintFunc()
var red = color.New(color.FgRed).SprintFunc()

func (l *Logger) Info(v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Warn(v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Println(v...)
}
func (l *Logger) Fatal(v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	os.Exit(1)
}
func (l *Logger) Panic(v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Println(v...)
	panic(logger)
}

func (l *Logger) Infof(format string, v ...interface{}) {
	prefix := cyan("==> Info: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Warnf(format string, v ...interface{}) {
	prefix := yellow("==> Warn: ")
	logger := log.New(os.Stdout, prefix, 0)
	logger.Printf(format, v...)
}
func (l *Logger) Fatalf(format string, v ...interface{}) {
	prefix := red("==> Fatal: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	os.Exit(1)
}
func (l *Logger) Panicf(format string, v ...interface{}) {
	prefix := red("==> Panic: ")
	logger := log.New(os.Stdout, prefix+" -- ", log.Llongfile)
	logger.Printf(format, v...)
	panic(logger)
}
<|endoftext|>