<commit_before>// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package seelog

import (
	"errors"
	"fmt"
	"os"
	"sync"
)

func reportInternalError(err error) {
	fmt.Fprintln(os.Stderr, "Seelog error: "+err.Error())
}

// LoggerInterface represents structs capable of logging Seelog messages
type LoggerInterface interface {

	// Tracef formats message according to format specifier
	// and writes to log with level = Trace.
	Tracef(format string, params ...interface{})

	// Debugf formats message according to format specifier
	// and writes to log with level = Debug.
	Debugf(format string, params ...interface{})

	// Infof formats message according to format specifier
	// and writes to log with level = Info.
	Infof(format string, params ...interface{})

	// Warnf formats message according to format specifier
	// and writes to log with level = Warn.
	Warnf(format string, params ...interface{}) error

	// Errorf formats message according to format specifier
	// and writes to log with level = Error.
	Errorf(format string, params ...interface{}) error

	// Criticalf formats message according to format specifier
	// and writes to log with level = Critical.
	Criticalf(format string, params ...interface{}) error

	// Trace formats message using the default formats for its operands
	// and writes to log with level = Trace
	Trace(v ...interface{})

	// Debug formats message using the default formats for its operands
	// and writes to log with level = Debug
	Debug(v ...interface{})

	// Info formats message using the default formats for its operands
	// and writes to log with level = Info
	Info(v ...interface{})

	// Warn formats message using the default formats for its operands
	// and writes to log with level = Warn
	Warn(v ...interface{}) error

	// Error formats message using the default formats for its operands
	// and writes to log with level = Error
	Error(v ...interface{}) error

	// Critical formats message using the default formats for its operands
	// and writes to log with level = Critical
	Critical(v ...interface{}) error

	traceWithCallDepth(callDepth int, message fmt.Stringer)
	debugWithCallDepth(callDepth int, message fmt.Stringer)
	infoWithCallDepth(callDepth int, message fmt.Stringer)
	warnWithCallDepth(callDepth int, message fmt.Stringer)
	errorWithCallDepth(callDepth int, message fmt.Stringer)
	criticalWithCallDepth(callDepth int, message fmt.Stringer)

	// Close flushes all the messages in the logger and closes it. It cannot be used after this operation.
	Close()

	// Flush flushes all the messages in the logger.
	Flush()

	// Closed returns true if the logger was previously closed.
	Closed() bool

	// SetAdditionalStackDepth sets the additional number of frames to skip by runtime.Caller
	// when getting function information needed to print seelog format identifiers such as %Func or %File.
	//
	// This func may be used when you wrap seelog funcs and want to print caller info of you own
	// wrappers instead of seelog func callers. In this case you should set depth = 1. If you then
	// wrap your wrapper, you should set depth = 2, etc.
	//
	// NOTE: Incorrect depth value may lead to errors in runtime.Caller evaluation or incorrect
	// function/file names in log files. Do not use it if you are not going to wrap seelog funcs.
	// You may reset the value to default using a SetAdditionalStackDepth(0) call.
	SetAdditionalStackDepth(depth int) error
}

// innerLoggerInterface is an internal logging interface
type innerLoggerInterface interface {
	innerLog(level LogLevel, context LogContextInterface, message fmt.Stringer)
	Flush()
}

// [file path][func name][level] -> [allowed]
type allowedContextCache map[string]map[string]map[LogLevel]bool

// commonLogger contains all common data needed for logging and contains methods used to log messages.
type commonLogger struct {
	config        *logConfig          // Config used for logging
	contextCache  allowedContextCache // Caches whether log is enabled for specific "full path-func name-level" sets
	closed        bool                // 'true' when all writers are closed, all data is flushed, logger is unusable.
	m             sync.Mutex          // Mutex for main operations
	unusedLevels  []bool
	innerLogger   innerLoggerInterface
	addStackDepth int // Additional stack depth needed for correct seelog caller context detection
}

func newCommonLogger(config *logConfig, internalLogger innerLoggerInterface) *commonLogger {
	cLogger := new(commonLogger)

	cLogger.config = config
	cLogger.contextCache = make(allowedContextCache)
	cLogger.unusedLevels = make([]bool, Off)
	cLogger.fillUnusedLevels()
	cLogger.innerLogger = internalLogger

	return cLogger
}

func (cLogger *commonLogger) SetAdditionalStackDepth(depth int) error {
	if depth < 0 {
		return fmt.Errorf("negative depth: %d", depth)
	}
	cLogger.m.Lock()
	cLogger.addStackDepth = depth
	cLogger.m.Unlock()
	return nil
}

func (cLogger *commonLogger) Tracef(format string, params ...interface{}) {
	cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
}

func (cLogger *commonLogger) Debugf(format string, params ...interface{}) {
	cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
}

func (cLogger *commonLogger) Infof(format string, params ...interface{}) {
	cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
}

func (cLogger *commonLogger) Warnf(format string, params ...interface{}) error {
	message := newLogFormattedMessage(format, params)
	cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Errorf(format string, params ...interface{}) error {
	message := newLogFormattedMessage(format, params)
	cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Criticalf(format string, params ...interface{}) error {
	message := newLogFormattedMessage(format, params)
	cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Trace(v ...interface{}) {
	cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
}

func (cLogger *commonLogger) Debug(v ...interface{}) {
	cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
}

func (cLogger *commonLogger) Info(v ...interface{}) {
	cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
}

func (cLogger *commonLogger) Warn(v ...interface{}) error {
	message := newLogMessage(v)
	cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Error(v ...interface{}) error {
	message := newLogMessage(v)
	cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Critical(v ...interface{}) error {
	message := newLogMessage(v)
	cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) traceWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(TraceLvl, message, callDepth)
}

func (cLogger *commonLogger) debugWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(DebugLvl, message, callDepth)
}

func (cLogger *commonLogger) infoWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(InfoLvl, message, callDepth)
}

func (cLogger *commonLogger) warnWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(WarnLvl, message, callDepth)
}

func (cLogger *commonLogger) errorWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(ErrorLvl, message, callDepth)
}

func (cLogger *commonLogger) criticalWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(CriticalLvl, message, callDepth)
	cLogger.innerLogger.Flush()
}

func (cLogger *commonLogger) Closed() bool {
	return cLogger.closed
}

func (cLogger *commonLogger) fillUnusedLevels() {
	for i := 0; i < len(cLogger.unusedLevels); i++ {
		cLogger.unusedLevels[i] = true
	}

	cLogger.fillUnusedLevelsByContraint(cLogger.config.Constraints)

	for _, exception := range cLogger.config.Exceptions {
		cLogger.fillUnusedLevelsByContraint(exception)
	}
}

func (cLogger *commonLogger) fillUnusedLevelsByContraint(constraint logLevelConstraints) {
	for i := 0; i < len(cLogger.unusedLevels); i++ {
		if constraint.IsAllowed(LogLevel(i)) {
			cLogger.unusedLevels[i] = false
		}
	}
}

// stackCallDepth is used to indicate the call depth of 'log' func.
// This depth level is used in the runtime.Caller(...) call. See
// common_context.go -> specifyContext, extractCallerInfo for details.
func (cLogger *commonLogger) log(level LogLevel, message fmt.Stringer, stackCallDepth int) {
	cLogger.m.Lock()
	defer cLogger.m.Unlock()

	if cLogger.Closed() {
		return
	}
	if cLogger.unusedLevels[level] {
		return
	}
	context, _ := specifyContext(stackCallDepth + cLogger.addStackDepth)
	// Context errors are not reported because there are situations
	// in which context errors are normal Seelog usage cases. For
	// example in executables with stripped symbols.
	// Error contexts are returned instead. See common_context.go.
	/*if err != nil {
		reportInternalError(err)
		return
	}*/
	cLogger.innerLogger.innerLog(level, context, message)
}

func (cLogger *commonLogger) processLogMsg(level LogLevel, message fmt.Stringer, context LogContextInterface) {
	defer func() {
		if err := recover(); err != nil {
			reportInternalError(fmt.Errorf("recovered from panic during message processing: %s", err))
		}
	}()
	if cLogger.config.IsAllowed(level, context) {
		cLogger.config.RootDispatcher.Dispatch(message.String(), level, context, reportInternalError)
	}
}

func (cLogger *commonLogger) isAllowed(level LogLevel, context LogContextInterface) bool {
	funcMap, ok := cLogger.contextCache[context.FullPath()]
	if !ok {
		funcMap = make(map[string]map[LogLevel]bool, 0)
		cLogger.contextCache[context.FullPath()] = funcMap
	}

	levelMap, ok := funcMap[context.Func()]
	if !ok {
		levelMap = make(map[LogLevel]bool, 0)
		funcMap[context.Func()] = levelMap
	}

	isAllowValue, ok := levelMap[level]
	if !ok {
		isAllowValue = cLogger.config.IsAllowed(level, context)
		levelMap[level] = isAllowValue
	}

	return isAllowValue
}

type logMessage struct {
	params []interface{}
}

type logFormattedMessage struct {
	format string
	params []interface{}
}

func newLogMessage(params []interface{}) fmt.Stringer {
	message := new(logMessage)

	message.params = params

	return message
}

func newLogFormattedMessage(format string, params []interface{}) *logFormattedMessage {
	message := new(logFormattedMessage)

	message.params = params
	message.format = format

	return message
}

func (message *logMessage) String() string {
	return fmt.Sprint(message.params...)
}

func (message *logFormattedMessage) String() string {
	return fmt.Sprintf(message.format, message.params...)
}
<commit_msg>Refine reportInternalError message formatting<commit_after>// Copyright (c) 2012 - Cloud Instruments Co., Ltd.
//
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are met:
//
// 1. Redistributions of source code must retain the above copyright notice, this
//    list of conditions and the following disclaimer.
// 2. Redistributions in binary form must reproduce the above copyright notice,
//    this list of conditions and the following disclaimer in the documentation
//    and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
// ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
// ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
// (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
// LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
// ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

package seelog

import (
	"errors"
	"fmt"
	"os"
	"sync"
)

func reportInternalError(err error) {
}

// LoggerInterface represents structs capable of logging Seelog messages
type LoggerInterface interface {

	// Tracef formats message according to format specifier
	// and writes to log with level = Trace.
	Tracef(format string, params ...interface{})

	// Debugf formats message according to format specifier
	// and writes to log with level = Debug.
	Debugf(format string, params ...interface{})

	// Infof formats message according to format specifier
	// and writes to log with level = Info.
	Infof(format string, params ...interface{})

	// Warnf formats message according to format specifier
	// and writes to log with level = Warn.
	Warnf(format string, params ...interface{}) error

	// Errorf formats message according to format specifier
	// and writes to log with level = Error.
	Errorf(format string, params ...interface{}) error

	// Criticalf formats message according to format specifier
	// and writes to log with level = Critical.
	Criticalf(format string, params ...interface{}) error

	// Trace formats message using the default formats for its operands
	// and writes to log with level = Trace
	Trace(v ...interface{})

	// Debug formats message using the default formats for its operands
	// and writes to log with level = Debug
	Debug(v ...interface{})

	// Info formats message using the default formats for its operands
	// and writes to log with level = Info
	Info(v ...interface{})

	// Warn formats message using the default formats for its operands
	// and writes to log with level = Warn
	Warn(v ...interface{}) error

	// Error formats message using the default formats for its operands
	// and writes to log with level = Error
	Error(v ...interface{}) error

	// Critical formats message using the default formats for its operands
	// and writes to log with level = Critical
	Critical(v ...interface{}) error

	traceWithCallDepth(callDepth int, message fmt.Stringer)
	debugWithCallDepth(callDepth int, message fmt.Stringer)
	infoWithCallDepth(callDepth int, message fmt.Stringer)
	warnWithCallDepth(callDepth int, message fmt.Stringer)
	errorWithCallDepth(callDepth int, message fmt.Stringer)
	criticalWithCallDepth(callDepth int, message fmt.Stringer)

	// Close flushes all the messages in the logger and closes it. It cannot be used after this operation.
	Close()

	// Flush flushes all the messages in the logger.
	Flush()

	// Closed returns true if the logger was previously closed.
	Closed() bool

	// SetAdditionalStackDepth sets the additional number of frames to skip by runtime.Caller
	// when getting function information needed to print seelog format identifiers such as %Func or %File.
	//
	// This func may be used when you wrap seelog funcs and want to print caller info of you own
	// wrappers instead of seelog func callers. In this case you should set depth = 1. If you then
	// wrap your wrapper, you should set depth = 2, etc.
	//
	// NOTE: Incorrect depth value may lead to errors in runtime.Caller evaluation or incorrect
	// function/file names in log files. Do not use it if you are not going to wrap seelog funcs.
	// You may reset the value to default using a SetAdditionalStackDepth(0) call.
	SetAdditionalStackDepth(depth int) error
}

// innerLoggerInterface is an internal logging interface
type innerLoggerInterface interface {
	innerLog(level LogLevel, context LogContextInterface, message fmt.Stringer)
	Flush()
}

// [file path][func name][level] -> [allowed]
type allowedContextCache map[string]map[string]map[LogLevel]bool

// commonLogger contains all common data needed for logging and contains methods used to log messages.
type commonLogger struct {
	config        *logConfig          // Config used for logging
	contextCache  allowedContextCache // Caches whether log is enabled for specific "full path-func name-level" sets
	closed        bool                // 'true' when all writers are closed, all data is flushed, logger is unusable.
	m             sync.Mutex          // Mutex for main operations
	unusedLevels  []bool
	innerLogger   innerLoggerInterface
	addStackDepth int // Additional stack depth needed for correct seelog caller context detection
}

func newCommonLogger(config *logConfig, internalLogger innerLoggerInterface) *commonLogger {
	cLogger := new(commonLogger)

	cLogger.config = config
	cLogger.contextCache = make(allowedContextCache)
	cLogger.unusedLevels = make([]bool, Off)
	cLogger.fillUnusedLevels()
	cLogger.innerLogger = internalLogger

	return cLogger
}

func (cLogger *commonLogger) SetAdditionalStackDepth(depth int) error {
	if depth < 0 {
		return fmt.Errorf("negative depth: %d", depth)
	}
	cLogger.m.Lock()
	cLogger.addStackDepth = depth
	cLogger.m.Unlock()
	return nil
}

func (cLogger *commonLogger) Tracef(format string, params ...interface{}) {
	cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
}

func (cLogger *commonLogger) Debugf(format string, params ...interface{}) {
	cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
}

func (cLogger *commonLogger) Infof(format string, params ...interface{}) {
	cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogFormattedMessage(format, params))
}

func (cLogger *commonLogger) Warnf(format string, params ...interface{}) error {
	message := newLogFormattedMessage(format, params)
	cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Errorf(format string, params ...interface{}) error {
	message := newLogFormattedMessage(format, params)
	cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Criticalf(format string, params ...interface{}) error {
	message := newLogFormattedMessage(format, params)
	cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Trace(v ...interface{}) {
	cLogger.traceWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
}

func (cLogger *commonLogger) Debug(v ...interface{}) {
	cLogger.debugWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
}

func (cLogger *commonLogger) Info(v ...interface{}) {
	cLogger.infoWithCallDepth(loggerFuncCallDepth, newLogMessage(v))
}

func (cLogger *commonLogger) Warn(v ...interface{}) error {
	message := newLogMessage(v)
	cLogger.warnWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Error(v ...interface{}) error {
	message := newLogMessage(v)
	cLogger.errorWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) Critical(v ...interface{}) error {
	message := newLogMessage(v)
	cLogger.criticalWithCallDepth(loggerFuncCallDepth, message)
	return errors.New(message.String())
}

func (cLogger *commonLogger) traceWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(TraceLvl, message, callDepth)
}

func (cLogger *commonLogger) debugWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(DebugLvl, message, callDepth)
}

func (cLogger *commonLogger) infoWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(InfoLvl, message, callDepth)
}

func (cLogger *commonLogger) warnWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(WarnLvl, message, callDepth)
}

func (cLogger *commonLogger) errorWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(ErrorLvl, message, callDepth)
}

func (cLogger *commonLogger) criticalWithCallDepth(callDepth int, message fmt.Stringer) {
	cLogger.log(CriticalLvl, message, callDepth)
	cLogger.innerLogger.Flush()
}

func (cLogger *commonLogger) Closed() bool {
	return cLogger.closed
}

func (cLogger *commonLogger) fillUnusedLevels() {
	for i := 0; i < len(cLogger.unusedLevels); i++ {
		cLogger.unusedLevels[i] = true
	}

	cLogger.fillUnusedLevelsByContraint(cLogger.config.Constraints)

	for _, exception := range cLogger.config.Exceptions {
		cLogger.fillUnusedLevelsByContraint(exception)
	}
}

func (cLogger *commonLogger) fillUnusedLevelsByContraint(constraint logLevelConstraints) {
	for i := 0; i < len(cLogger.unusedLevels); i++ {
		if constraint.IsAllowed(LogLevel(i)) {
			cLogger.unusedLevels[i] = false
		}
	}
}

// stackCallDepth is used to indicate the call depth of 'log' func.
// This depth level is used in the runtime.Caller(...) call. See
// common_context.go -> specifyContext, extractCallerInfo for details.
func (cLogger *commonLogger) log(level LogLevel, message fmt.Stringer, stackCallDepth int) {
	cLogger.m.Lock()
	defer cLogger.m.Unlock()

	if cLogger.Closed() {
		return
	}
	if cLogger.unusedLevels[level] {
		return
	}
	context, _ := specifyContext(stackCallDepth + cLogger.addStackDepth)
	// Context errors are not reported because there are situations
	// in which context errors are normal Seelog usage cases. For
	// example in executables with stripped symbols.
	// Error contexts are returned instead. See common_context.go.
	/*if err != nil {
		reportInternalError(err)
		return
	}*/
	cLogger.innerLogger.innerLog(level, context, message)
}

func (cLogger *commonLogger) processLogMsg(level LogLevel, message fmt.Stringer, context LogContextInterface) {
	defer func() {
		if err := recover(); err != nil {
			reportInternalError(fmt.Errorf("recovered from panic during message processing: %s", err))
		}
	}()
	if cLogger.config.IsAllowed(level, context) {
		cLogger.config.RootDispatcher.Dispatch(message.String(), level, context, reportInternalError)
	}
}

func (cLogger *commonLogger) isAllowed(level LogLevel, context LogContextInterface) bool {
	funcMap, ok := cLogger.contextCache[context.FullPath()]
	if !ok {
		funcMap = make(map[string]map[LogLevel]bool, 0)
		cLogger.contextCache[context.FullPath()] = funcMap
	}

	levelMap, ok := funcMap[context.Func()]
	if !ok {
		levelMap = make(map[LogLevel]bool, 0)
		funcMap[context.Func()] = levelMap
	}

	isAllowValue, ok := levelMap[level]
	if !ok {
		isAllowValue = cLogger.config.IsAllowed(level, context)
		levelMap[level] = isAllowValue
	}

	return isAllowValue
}

type logMessage struct {
	params []interface{}
}

type logFormattedMessage struct {
	format string
	params []interface{}
}

func newLogMessage(params []interface{}) fmt.Stringer {
	message := new(logMessage)

	message.params = params

	return message
}

func newLogFormattedMessage(format string, params []interface{}) *logFormattedMessage {
	message := new(logFormattedMessage)

	message.params = params
	message.format = format

	return message
}

func (message *logMessage) String() string {
	return fmt.Sprint(message.params...)
}

func (message *logFormattedMessage) String() string {
	return fmt.Sprintf(message.format, message.params...)
}
<|endoftext|>


<commit_before>package xlog

import (
	"log"
	"os"
	"fmt"
	"io"
)

// DefaultDateFormat is the date format to use when none has been specified.
const DefaultDateFormat = "2006-01-02 15:04:05.000"

// DefaultMessageFormat is the message format to use when none has been specified.
const DefaultMessageFormat = "{date|" + DefaultDateFormat + "} [{level}] {message}"

// Level describes a logging level.
type Level int

const (
	Debug Level = 1 << iota
	Info        = 1 << iota
	Notice      = 1 << iota
	Warning     = 1 << iota
	Error       = 1 << iota
	Critical    = 1 << iota
	Alert       = 1 << iota
	Emergency   = 1 << iota
)

// Levels maps Level to a string representation.
var Levels = map[Level]string{
	Debug: "DEBUG",
	Info: "INFO",
	Notice: "NOTICE",
	Warning: "WARNING",
	Error: "ERROR",
	Critical: "CRITICAL",
	Alert: "ALERT",
	Emergency: "EMERGENCY",
}

// FileAliases maps file aliases to real file pointers.
var FileAliases = map[string]*os.File{
	"stdout": os.Stdout,
	"stdin": os.Stdin,
	"stderr": os.Stderr,
}

var (
	// FileFlags defines the file open options.
	FileFlags int = os.O_RDWR|os.O_CREATE | os.O_APPEND

	// FileMode defines the mode files are opened in.
	FileMode os.FileMode = 0666

	// PanicOnFileErrors defines whether the logger should panic when opening a file
	// fails. When set to false, any file open errors are ignored, and the file won't be
	// appended.
	PanicOnFileErrors = true

	// LoggerCapacity defines the initial capacity for each type of logger.
	LoggerCapacity = 2
)

// Loggable is an interface that provides methods for logging messages to
// various levels.
type Loggable interface {
	Log(level Level, v ...interface{})
	Logf(level Level, format string, v ...interface{})
	Debug(v ...interface{})
	Debugf(format string, v ...interface{})
	Info(v ...interface{})
	Infof(format string, v ...interface{})
	Warning(v ...interface{})
	Warningf(format string, v ...interface{})
	Error(v ...interface{})
	Errorf(format string, v ...interface{})
	Critical(v ...interface{})
	Criticalf(format string, v ...interface{})
	Alert(v ...interface{})
	Alertf(format string, v ...interface{})
	Emergency(v ...interface{})
	Emergencyf(format string, v ...interface{})
}

// Logger is a light weight logger designed to write to multiple files at different
// log levels.
type Logger struct {
	// Enabled defines whether logging is enabled.
	Enabled  bool

	// Formatter is used to format the log messages.
	Formatter Formatter

	// Loggers holds the appended file loggers.
	Loggers LoggerMap

	// FatalOn represents levels that causes the application to exit.
	FatalOn Level

	// PanicOn represents levels that causes the application to panic.
	PanicOn Level

	// pointers contains any files that have been opened for logging.
	pointers []*os.File

	// closed defines whether the logger has been closed.
	closed bool
}

// NewLogger returns a *Logger instance that's been initialized with default values.
func NewLogger() *Logger {
	//fmt.Println(v & Info);

	return &Logger{
		Enabled: true,
		Formatter: NewDefaultFormatter(DefaultMessageFormat),
		Loggers: NewDefaultLoggerMap(),
		FatalOn: 0,
		PanicOn: 0,
		pointers: make([]*os.File, 0),
		closed: false,
	}
}

// NewFormattedLogger returns a *Logger instance using the provided formatter.
func NewFormattedLogger(formatter Formatter) *Logger {
	return &Logger{
		Enabled: true,
		Formatter: formatter,
		Loggers: NewDefaultLoggerMap(),
		FatalOn: 0,
		PanicOn: 0,
		pointers: make([]*os.File, 0),
		closed: false,
	}
}

// Append adds a file that will be written to at the given level or greater.
// The file argument may be either the full path to a system file, or one of the
// aliases "stdout", "stdin", or "stderr".
func (l *Logger) Append(file string, level Level) {
	if w, ok := FileAliases[file]; ok {
		l.Loggers.Append(newLogger(w), level)
	} else {
		w := l.open(file)
		if w != nil {
			l.Loggers.Append(newLogger(w), level)
			l.pointers = append(l.pointers, w)
		}
	}
}

// AppendWriter adds a writer that will be written to at the given level or greater.
func (l *Logger) AppendWriter(w io.Writer, level Level) {
	l.Loggers.Append(newLogger(w), level)
}

// Close disables logging and frees up resources used by the logger.
// Note this method only closes files opened by the logger. It's the user's
// responsibility to close files that were passed to the logger via the
// AppendWriter method.
func (l *Logger) Close() {
	if !l.closed {
		for _, pointer := range l.pointers {
			pointer.Close()
		}

		l.Enabled = false
		l.Loggers = nil
		l.pointers = nil
	}
}

// Writable returns true when logging is enabled, and the logger hasn't been closed.
func (l *Logger) Writable() bool {
	return l.Enabled && !l.closed
}

// Log writes the message to each logger appended at the given level or higher.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Log(level Level, v ...interface{}) {
	if l.Writable() {
		message := l.Formatter.Format(level, v...)
		for _, logger := range l.Loggers.FindByLevel(level) {
			logger.Print(message)
		}

		if l.FatalOn&level > 0 {
			os.Exit(1)
		} else if l.PanicOn&level > 0 {
			panic(message)
		}
	}
}

// Log writes the message to each logger appended at the given level or higher.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Logf(level Level, format string, v ...interface{}) {
	l.Log(level, fmt.Sprintf(format, v...))
}

// Debug prints to each log file at the Debug level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Debug(v ...interface{}) {
	l.Log(Debug, v...)
}

// Debugf prints to each log file at the Debug level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Debugf(format string, v ...interface{}) {
	l.Logf(Debug, format, v...)
}

// Info prints to each log file at the Info level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Info(v ...interface{}) {
	l.Log(Info, v...)
}

// Infof prints to each log file at the Info level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Infof(format string, v ...interface{}) {
	l.Logf(Info, format, v...)
}

// Notice prints to each log file at the Notice level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Notice(v ...interface{}) {
	l.Log(Notice, v...)
}

// Noticef prints to each log file at the Notice level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Noticef(format string, v ...interface{}) {
	l.Logf(Notice, format, v...)
}

// Warning prints to each log file at the Warning level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Warning(v ...interface{}) {
	l.Log(Warning, v...)
}

// Warningf prints to each log file at the Warning level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Warningf(format string, v ...interface{}) {
	l.Logf(Warning, format, v...)
}

// Error prints to each log file at the Error level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Error(v ...interface{}) {
	l.Log(Error, v...)
}

// Errorf prints to each log file at the Error level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Errorf(format string, v ...interface{}) {
	l.Logf(Error, format, v...)
}

// Critical prints to each log file at the Critical level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Critical(v ...interface{}) {
	l.Log(Critical, v...)
}

// Criticalf prints to each log file at the Critical level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Criticalf(format string, v ...interface{}) {
	l.Logf(Critical, format, v...)
}

// Alert prints to each log file at the Alert level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Alert(v ...interface{}) {
	l.Log(Alert, v...)
}

// Alertf prints to each log file at the Alert level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Alertf(format string, v ...interface{}) {
	l.Logf(Alert, format, v...)
}

// Emergency prints to each log file at the Emergency level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Emergency(v ...interface{}) {
	l.Log(Emergency, v...)
}

// Emergencyf prints to each log file at the Emergency level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Emergencyf(format string, v ...interface{}) {
	l.Logf(Emergency, format, v...)
}

// open returns a file that logs can be written to.
func (l *Logger) open(name string) *os.File {
	w, err := os.OpenFile(name, FileFlags, FileMode)
	if err != nil {
		if PanicOnFileErrors {
			panic(err)
		} else {
			w = nil
		}
	}

	return w
}

// newLogger returns a *log.Logger instance configured with the default options.
func newLogger(w io.Writer) *log.Logger {
	return log.New(w, "", 0)
}
<commit_msg>Setting the formatter name in NewLogger()<commit_after>package xlog

import (
	"log"
	"os"
	"fmt"
	"io"
)

// DefaultDateFormat is the date format to use when none has been specified.
const DefaultDateFormat = "2006-01-02 15:04:05.000"

// DefaultMessageFormat is the message format to use when none has been specified.
const DefaultMessageFormat = "{date|" + DefaultDateFormat + "} [{level}] {message}"

// Level describes a logging level.
type Level int

const (
	Debug Level = 1 << iota
	Info        = 1 << iota
	Notice      = 1 << iota
	Warning     = 1 << iota
	Error       = 1 << iota
	Critical    = 1 << iota
	Alert       = 1 << iota
	Emergency   = 1 << iota
)

// Levels maps Level to a string representation.
var Levels = map[Level]string{
	Debug: "DEBUG",
	Info: "INFO",
	Notice: "NOTICE",
	Warning: "WARNING",
	Error: "ERROR",
	Critical: "CRITICAL",
	Alert: "ALERT",
	Emergency: "EMERGENCY",
}

// FileAliases maps file aliases to real file pointers.
var FileAliases = map[string]*os.File{
	"stdout": os.Stdout,
	"stdin": os.Stdin,
	"stderr": os.Stderr,
}

var (
	// FileFlags defines the file open options.
	FileFlags int = os.O_RDWR|os.O_CREATE | os.O_APPEND

	// FileMode defines the mode files are opened in.
	FileMode os.FileMode = 0666

	// PanicOnFileErrors defines whether the logger should panic when opening a file
	// fails. When set to false, any file open errors are ignored, and the file won't be
	// appended.
	PanicOnFileErrors = true

	// LoggerCapacity defines the initial capacity for each type of logger.
	LoggerCapacity = 2
)

// Loggable is an interface that provides methods for logging messages to
// various levels.
type Loggable interface {
	Log(level Level, v ...interface{})
	Logf(level Level, format string, v ...interface{})
	Debug(v ...interface{})
	Debugf(format string, v ...interface{})
	Info(v ...interface{})
	Infof(format string, v ...interface{})
	Warning(v ...interface{})
	Warningf(format string, v ...interface{})
	Error(v ...interface{})
	Errorf(format string, v ...interface{})
	Critical(v ...interface{})
	Criticalf(format string, v ...interface{})
	Alert(v ...interface{})
	Alertf(format string, v ...interface{})
	Emergency(v ...interface{})
	Emergencyf(format string, v ...interface{})
}

// Logger is a light weight logger designed to write to multiple files at different
// log levels.
type Logger struct {
	// Enabled defines whether logging is enabled.
	Enabled  bool

	// Formatter is used to format the log messages.
	Formatter Formatter

	// Loggers holds the appended file loggers.
	Loggers LoggerMap

	// FatalOn represents levels that causes the application to exit.
	FatalOn Level

	// PanicOn represents levels that causes the application to panic.
	PanicOn Level

	// pointers contains any files that have been opened for logging.
	pointers []*os.File

	// closed defines whether the logger has been closed.
	closed bool
}

// NewLogger returns a *Logger instance that's been initialized with default values.
func NewLogger() *Logger {
	//fmt.Println(v & Info);

	return &Logger{
		Enabled: true,
		Formatter: NewDefaultFormatter(DefaultMessageFormat, ""),
		Loggers: NewDefaultLoggerMap(),
		FatalOn: 0,
		PanicOn: 0,
		pointers: make([]*os.File, 0),
		closed: false,
	}
}

// NewFormattedLogger returns a *Logger instance using the provided formatter.
func NewFormattedLogger(formatter Formatter) *Logger {
	return &Logger{
		Enabled: true,
		Formatter: formatter,
		Loggers: NewDefaultLoggerMap(),
		FatalOn: 0,
		PanicOn: 0,
		pointers: make([]*os.File, 0),
		closed: false,
	}
}

// Append adds a file that will be written to at the given level or greater.
// The file argument may be either the full path to a system file, or one of the
// aliases "stdout", "stdin", or "stderr".
func (l *Logger) Append(file string, level Level) {
	if w, ok := FileAliases[file]; ok {
		l.Loggers.Append(newLogger(w), level)
	} else {
		w := l.open(file)
		if w != nil {
			l.Loggers.Append(newLogger(w), level)
			l.pointers = append(l.pointers, w)
		}
	}
}

// AppendWriter adds a writer that will be written to at the given level or greater.
func (l *Logger) AppendWriter(w io.Writer, level Level) {
	l.Loggers.Append(newLogger(w), level)
}

// Close disables logging and frees up resources used by the logger.
// Note this method only closes files opened by the logger. It's the user's
// responsibility to close files that were passed to the logger via the
// AppendWriter method.
func (l *Logger) Close() {
	if !l.closed {
		for _, pointer := range l.pointers {
			pointer.Close()
		}

		l.Enabled = false
		l.Loggers = nil
		l.pointers = nil
	}
}

// Writable returns true when logging is enabled, and the logger hasn't been closed.
func (l *Logger) Writable() bool {
	return l.Enabled && !l.closed
}

// Log writes the message to each logger appended at the given level or higher.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Log(level Level, v ...interface{}) {
	if l.Writable() {
		message := l.Formatter.Format(level, v...)
		for _, logger := range l.Loggers.FindByLevel(level) {
			logger.Print(message)
		}

		if l.FatalOn&level > 0 {
			os.Exit(1)
		} else if l.PanicOn&level > 0 {
			panic(message)
		}
	}
}

// Log writes the message to each logger appended at the given level or higher.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Logf(level Level, format string, v ...interface{}) {
	l.Log(level, fmt.Sprintf(format, v...))
}

// Debug prints to each log file at the Debug level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Debug(v ...interface{}) {
	l.Log(Debug, v...)
}

// Debugf prints to each log file at the Debug level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Debugf(format string, v ...interface{}) {
	l.Logf(Debug, format, v...)
}

// Info prints to each log file at the Info level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Info(v ...interface{}) {
	l.Log(Info, v...)
}

// Infof prints to each log file at the Info level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Infof(format string, v ...interface{}) {
	l.Logf(Info, format, v...)
}

// Notice prints to each log file at the Notice level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Notice(v ...interface{}) {
	l.Log(Notice, v...)
}

// Noticef prints to each log file at the Notice level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Noticef(format string, v ...interface{}) {
	l.Logf(Notice, format, v...)
}

// Warning prints to each log file at the Warning level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Warning(v ...interface{}) {
	l.Log(Warning, v...)
}

// Warningf prints to each log file at the Warning level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Warningf(format string, v ...interface{}) {
	l.Logf(Warning, format, v...)
}

// Error prints to each log file at the Error level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Error(v ...interface{}) {
	l.Log(Error, v...)
}

// Errorf prints to each log file at the Error level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Errorf(format string, v ...interface{}) {
	l.Logf(Error, format, v...)
}

// Critical prints to each log file at the Critical level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Critical(v ...interface{}) {
	l.Log(Critical, v...)
}

// Criticalf prints to each log file at the Critical level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Criticalf(format string, v ...interface{}) {
	l.Logf(Critical, format, v...)
}

// Alert prints to each log file at the Alert level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Alert(v ...interface{}) {
	l.Log(Alert, v...)
}

// Alertf prints to each log file at the Alert level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Alertf(format string, v ...interface{}) {
	l.Logf(Alert, format, v...)
}

// Emergency prints to each log file at the Emergency level.
// Arguments are handled in the manner of fmt.Print.
func (l *Logger) Emergency(v ...interface{}) {
	l.Log(Emergency, v...)
}

// Emergencyf prints to each log file at the Emergency level.
// Arguments are handled in the manner of fmt.Printf.
func (l *Logger) Emergencyf(format string, v ...interface{}) {
	l.Logf(Emergency, format, v...)
}

// open returns a file that logs can be written to.
func (l *Logger) open(name string) *os.File {
	w, err := os.OpenFile(name, FileFlags, FileMode)
	if err != nil {
		if PanicOnFileErrors {
			panic(err)
		} else {
			w = nil
		}
	}

	return w
}

// newLogger returns a *log.Logger instance configured with the default options.
func newLogger(w io.Writer) *log.Logger {
	return log.New(w, "", 0)
}
<|endoftext|>


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

/*
	Package oauth1a implements the OAuth 1.0a specification.
*/
package oauth1a

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

// Container for client-specific configuration related to the OAuth process.
// This struct is intended to be serialized and stored for future use.
type ClientConfig struct {
	ConsumerSecret string
	ConsumerKey    string
	CallbackURL    string
}

// Represents an API which offers OAuth access.
type Service struct {
	RequestURL   string
	AuthorizeURL string
	AccessURL    string
	*ClientConfig
	Signer
}

// Signs an HTTP request with the needed OAuth parameters.
func (s *Service) Sign(request *http.Request, userConfig *UserConfig) error {
	return s.Signer.Sign(request, s.ClientConfig, userConfig)
}

// Interface for any OAuth signing implementations.
type Signer interface {
	Sign(request *http.Request, config *ClientConfig, user *UserConfig) error
}

// A Signer which implements the HMAC-SHA1 signing algorithm.
type HmacSha1Signer struct{}

// Sort a set of request parameters alphabetically, and encode according to the
// OAuth 1.0a specification.
func (HmacSha1Signer) encodeParameters(params map[string]string) string {
	keys := make([]string, len(params))
	encodedParts := make([]string, len(params))
	i := 0
	for key, _ := range params {
		keys[i] = key
		i += 1
	}
	sort.Strings(keys)
	for i, key := range keys {
		value := params[key]
		encoded := Rfc3986Escape(key) + "=" + Rfc3986Escape(value)
		encodedParts[i] = encoded
	}
	return url.QueryEscape(strings.Join(encodedParts, "&"))
}

// Generate a unique nonce value.  Should not be called more than once per
// nanosecond
// TODO: Come up with a better generation method.
func (HmacSha1Signer) GenerateNonce() string {
	ns := time.Now()
	token := fmt.Sprintf("OAuth Client Lib %v", ns)
	h := sha1.New()
	h.Write([]byte(token))
	return fmt.Sprintf("%x", h.Sum(nil))
}

// Returns a map of all of the oauth_* (including signature) parameters for the
// given request, and the signature base string used to generate the signature.
func (s *HmacSha1Signer) GetOAuthParams(request *http.Request, clientConfig *ClientConfig, userConfig *UserConfig, nonce string, timestamp string) (map[string]string, string) {
	request.ParseForm()
	oauthParams := map[string]string{
		"oauth_consumer_key":     clientConfig.ConsumerKey,
		"oauth_nonce":            nonce,
		"oauth_signature_method": "HMAC-SHA1",
		"oauth_timestamp":        timestamp,
		"oauth_version":          "1.0",
	}
	tokenKey, tokenSecret := userConfig.GetToken()
	if tokenKey != "" {
		oauthParams["oauth_token"] = tokenKey
	}
	signingParams := map[string]string{}
	for key, value := range oauthParams {
		signingParams[key] = value
	}
	for key, value := range request.URL.Query() {
		//TODO: Support multiple parameters with the same name.
		signingParams[key] = value[0]
	}
	for key, value := range request.Form {
		//TODO: Support multiple parameters with the same name.
		signingParams[key] = value[0]
	}
	signingUrl := fmt.Sprintf("%v://%v%v", request.URL.Scheme, request.URL.Host, request.URL.Path)
	signatureParts := []string{
		request.Method,
		url.QueryEscape(signingUrl),
		s.encodeParameters(signingParams)}
	signatureBase := strings.Join(signatureParts, "&")
	oauthParams["oauth_signature"] = s.GetSignature(clientConfig.ConsumerSecret, tokenSecret, signatureBase)
	return oauthParams, signatureBase
}

// Calculates the HMAC-SHA1 signature of a base string, given a consumer and
// token secret.
func (s *HmacSha1Signer) GetSignature(consumerSecret string, tokenSecret string, signatureBase string) string {
	signingKey := consumerSecret + "&" + tokenSecret
	signer := hmac.New(sha1.New, []byte(signingKey))
	signer.Write([]byte(signatureBase))
	oauthSignature := base64.StdEncoding.EncodeToString(signer.Sum(nil))
	return oauthSignature
}

// Given an unsigned request, add the appropriate OAuth Authorization header
// using the HMAC-SHA1 algorithm.
func (s *HmacSha1Signer) Sign(request *http.Request, clientConfig *ClientConfig, userConfig *UserConfig) error {
	nonce := s.GenerateNonce()
	timestamp := fmt.Sprintf("%v", time.Now().Unix())
	oauthParams, _ := s.GetOAuthParams(request, clientConfig, userConfig, nonce, timestamp)
	headerParts := make([]string, len(oauthParams))
	var i = 0
	for key, value := range oauthParams {
		headerParts[i] = Rfc3986Escape(key) + "=\"" + Rfc3986Escape(value) + "\""
		i += 1
	}
	oauthHeader := "OAuth " + strings.Join(headerParts, ", ")
	request.Header["Authorization"] = []string{oauthHeader}
	return nil
}

// Characters which should not be escaped according to RFC 3986.
const UNESCAPE_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~"

// Escapes a string more in line with Rfc3986 than http.URLEscape.
// URLEscape was converting spaces to "+" instead of "%20", which was messing up
// the signing of requests.
func Rfc3986Escape(input string) string {
	var output bytes.Buffer
	// Convert string to bytes because iterating over a unicode string
	// in go parses runes, not bytes.
	for _, c := range []byte(input) {
		if strings.IndexAny(string(c), UNESCAPE_CHARS) == -1 {
			encoded := fmt.Sprintf("%%%X", c)
			output.Write([]uint8(encoded))
		} else {
			output.WriteByte(uint8(c))
		}
	}
	return string(output.Bytes())
}
<commit_msg>Make timestamps more consistent<commit_after>// Copyright 2011 Arne Roomann-Kurrik.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/*
	Package oauth1a implements the OAuth 1.0a specification.
*/
package oauth1a

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"fmt"
	"net/http"
	"net/url"
	"sort"
	"strings"
	"time"
)

// Container for client-specific configuration related to the OAuth process.
// This struct is intended to be serialized and stored for future use.
type ClientConfig struct {
	ConsumerSecret string
	ConsumerKey    string
	CallbackURL    string
}

// Represents an API which offers OAuth access.
type Service struct {
	RequestURL   string
	AuthorizeURL string
	AccessURL    string
	*ClientConfig
	Signer
}

// Signs an HTTP request with the needed OAuth parameters.
func (s *Service) Sign(request *http.Request, userConfig *UserConfig) error {
	return s.Signer.Sign(request, s.ClientConfig, userConfig)
}

// Interface for any OAuth signing implementations.
type Signer interface {
	Sign(request *http.Request, config *ClientConfig, user *UserConfig) error
}

// A Signer which implements the HMAC-SHA1 signing algorithm.
type HmacSha1Signer struct{}

// Sort a set of request parameters alphabetically, and encode according to the
// OAuth 1.0a specification.
func (HmacSha1Signer) encodeParameters(params map[string]string) string {
	keys := make([]string, len(params))
	encodedParts := make([]string, len(params))
	i := 0
	for key, _ := range params {
		keys[i] = key
		i += 1
	}
	sort.Strings(keys)
	for i, key := range keys {
		value := params[key]
		encoded := Rfc3986Escape(key) + "=" + Rfc3986Escape(value)
		encodedParts[i] = encoded
	}
	return url.QueryEscape(strings.Join(encodedParts, "&"))
}

// Generate a unique nonce value.  Should not be called more than once per
// nanosecond
// TODO: Come up with a better generation method.
func (HmacSha1Signer) GenerateNonce() string {
	ns := time.Now()
	token := fmt.Sprintf("OAuth Client Lib %v", ns)
	h := sha1.New()
	h.Write([]byte(token))
	return fmt.Sprintf("%x", h.Sum(nil))
}

// Generate a timestamp.
func (HmacSha1Signer) GenerateTimestamp() int64 {
	return time.Now().Unix()
}

// Returns a map of all of the oauth_* (including signature) parameters for the
// given request, and the signature base string used to generate the signature.
func (s *HmacSha1Signer) GetOAuthParams(request *http.Request, clientConfig *ClientConfig, userConfig *UserConfig, nonce string, timestamp string) (map[string]string, string) {
	request.ParseForm()
	oauthParams := map[string]string{
		"oauth_consumer_key":     clientConfig.ConsumerKey,
		"oauth_nonce":            nonce,
		"oauth_signature_method": "HMAC-SHA1",
		"oauth_timestamp":        timestamp,
		"oauth_version":          "1.0",
	}
	tokenKey, tokenSecret := userConfig.GetToken()
	if tokenKey != "" {
		oauthParams["oauth_token"] = tokenKey
	}
	signingParams := map[string]string{}
	for key, value := range oauthParams {
		signingParams[key] = value
	}
	for key, value := range request.URL.Query() {
		//TODO: Support multiple parameters with the same name.
		signingParams[key] = value[0]
	}
	for key, value := range request.Form {
		//TODO: Support multiple parameters with the same name.
		signingParams[key] = value[0]
	}
	signingUrl := fmt.Sprintf("%v://%v%v", request.URL.Scheme, request.URL.Host, request.URL.Path)
	signatureParts := []string{
		request.Method,
		url.QueryEscape(signingUrl),
		s.encodeParameters(signingParams)}
	signatureBase := strings.Join(signatureParts, "&")
	oauthParams["oauth_signature"] = s.GetSignature(clientConfig.ConsumerSecret, tokenSecret, signatureBase)
	return oauthParams, signatureBase
}

// Calculates the HMAC-SHA1 signature of a base string, given a consumer and
// token secret.
func (s *HmacSha1Signer) GetSignature(consumerSecret string, tokenSecret string, signatureBase string) string {
	signingKey := consumerSecret + "&" + tokenSecret
	signer := hmac.New(sha1.New, []byte(signingKey))
	signer.Write([]byte(signatureBase))
	oauthSignature := base64.StdEncoding.EncodeToString(signer.Sum(nil))
	return oauthSignature
}

// Given an unsigned request, add the appropriate OAuth Authorization header
// using the HMAC-SHA1 algorithm.
func (s *HmacSha1Signer) Sign(request *http.Request, clientConfig *ClientConfig, userConfig *UserConfig) error {
	nonce := s.GenerateNonce()
	timestamp := fmt.Sprintf("%v", s.GenerateTimestamp())
	oauthParams, _ := s.GetOAuthParams(request, clientConfig, userConfig, nonce, timestamp)
	headerParts := make([]string, len(oauthParams))
	var i = 0
	for key, value := range oauthParams {
		headerParts[i] = Rfc3986Escape(key) + "=\"" + Rfc3986Escape(value) + "\""
		i += 1
	}
	oauthHeader := "OAuth " + strings.Join(headerParts, ", ")
	request.Header["Authorization"] = []string{oauthHeader}
	return nil
}

// Characters which should not be escaped according to RFC 3986.
const UNESCAPE_CHARS = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz-._~"

// Escapes a string more in line with Rfc3986 than http.URLEscape.
// URLEscape was converting spaces to "+" instead of "%20", which was messing up
// the signing of requests.
func Rfc3986Escape(input string) string {
	var output bytes.Buffer
	// Convert string to bytes because iterating over a unicode string
	// in go parses runes, not bytes.
	for _, c := range []byte(input) {
		if strings.IndexAny(string(c), UNESCAPE_CHARS) == -1 {
			encoded := fmt.Sprintf("%%%X", c)
			output.Write([]uint8(encoded))
		} else {
			output.WriteByte(uint8(c))
		}
	}
	return string(output.Bytes())
}
<|endoftext|>


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

package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/matrix-org/dendrite/appservice"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/embed"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
	"github.com/matrix-org/dendrite/currentstateserver"
	"github.com/matrix-org/dendrite/eduserver"
	"github.com/matrix-org/dendrite/eduserver/cache"
	"github.com/matrix-org/dendrite/federationsender"
	"github.com/matrix-org/dendrite/federationsender/api"
	"github.com/matrix-org/dendrite/internal"
	"github.com/matrix-org/dendrite/internal/config"
	"github.com/matrix-org/dendrite/internal/httputil"
	"github.com/matrix-org/dendrite/internal/setup"
	"github.com/matrix-org/dendrite/roomserver"
	"github.com/matrix-org/dendrite/userapi"
	"github.com/matrix-org/gomatrixserverlib"
	"github.com/yggdrasil-network/yggdrasil-go/src/crypto"

	"github.com/sirupsen/logrus"
)

var (
	instanceName = flag.String("name", "dendrite-p2p-ygg", "the name of this P2P demo instance")
	instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
	instancePeer = flag.String("peer", "", "an internet Yggdrasil peer to connect to")
)

// nolint:gocyclo
func main() {
	flag.Parse()
	internal.SetupPprof()

	ygg, err := yggconn.Setup(*instanceName, ".")
	if err != nil {
		panic(err)
	}
	ygg.SetMulticastEnabled(true)
	if instancePeer != nil && *instancePeer != "" {
		if err := ygg.SetStaticPeer(*instancePeer); err != nil {
			logrus.WithError(err).Error("Failed to set static peer")
		}
	}

	cfg := &config.Dendrite{}
	cfg.SetDefaults()
	cfg.Matrix.ServerName = gomatrixserverlib.ServerName(ygg.DerivedServerName())
	cfg.Matrix.PrivateKey = ygg.SigningPrivateKey()
	cfg.Matrix.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
	cfg.Kafka.UseNaffka = true
	cfg.Kafka.Topics.OutputRoomEvent = "roomserverOutput"
	cfg.Kafka.Topics.OutputClientData = "clientapiOutput"
	cfg.Kafka.Topics.OutputTypingEvent = "typingServerOutput"
	cfg.Database.Account = config.DataSource(fmt.Sprintf("file:%s-account.db", *instanceName))
	cfg.Database.Device = config.DataSource(fmt.Sprintf("file:%s-device.db", *instanceName))
	cfg.Database.MediaAPI = config.DataSource(fmt.Sprintf("file:%s-mediaapi.db", *instanceName))
	cfg.Database.SyncAPI = config.DataSource(fmt.Sprintf("file:%s-syncapi.db", *instanceName))
	cfg.Database.RoomServer = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", *instanceName))
	cfg.Database.ServerKey = config.DataSource(fmt.Sprintf("file:%s-serverkey.db", *instanceName))
	cfg.Database.FederationSender = config.DataSource(fmt.Sprintf("file:%s-federationsender.db", *instanceName))
	cfg.Database.AppService = config.DataSource(fmt.Sprintf("file:%s-appservice.db", *instanceName))
	cfg.Database.CurrentState = config.DataSource(fmt.Sprintf("file:%s-currentstate.db", *instanceName))
	cfg.Database.Naffka = config.DataSource(fmt.Sprintf("file:%s-naffka.db", *instanceName))
	if err = cfg.Derive(); err != nil {
		panic(err)
	}

	base := setup.NewBaseDendrite(cfg, "Monolith", false)
	defer base.Close() // nolint: errcheck

	accountDB := base.CreateAccountsDB()
	deviceDB := base.CreateDeviceDB()
	federation := ygg.CreateFederationClient(base)

	serverKeyAPI := &signing.YggdrasilKeys{}
	keyRing := serverKeyAPI.KeyRing()

	userAPI := userapi.NewInternalAPI(accountDB, deviceDB, cfg.Matrix.ServerName, nil)

	rsComponent := roomserver.NewInternalAPI(
		base, keyRing, federation,
	)
	rsAPI := rsComponent

	eduInputAPI := eduserver.NewInternalAPI(
		base, cache.New(), userAPI,
	)

	asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)

	fsAPI := federationsender.NewInternalAPI(
		base, federation, rsAPI, keyRing,
	)

	rsComponent.SetFederationSenderAPI(fsAPI)

	embed.Embed(base.BaseMux, *instancePort, "Yggdrasil Demo")

	stateAPI := currentstateserver.NewInternalAPI(base.Cfg, base.KafkaConsumer)

	monolith := setup.Monolith{
		Config:        base.Cfg,
		AccountDB:     accountDB,
		DeviceDB:      deviceDB,
		Client:        ygg.CreateClient(base),
		FedClient:     federation,
		KeyRing:       keyRing,
		KafkaConsumer: base.KafkaConsumer,
		KafkaProducer: base.KafkaProducer,

		AppserviceAPI:       asAPI,
		EDUInternalAPI:      eduInputAPI,
		FederationSenderAPI: fsAPI,
		RoomserverAPI:       rsAPI,
		UserAPI:             userAPI,
		StateAPI:            stateAPI,
		//ServerKeyAPI:        serverKeyAPI,
		ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
			ygg, fsAPI, federation,
		),
	}
	monolith.AddAllPublicRoutes(base.PublicAPIMux)

	httputil.SetupHTTPAPI(
		base.BaseMux,
		base.PublicAPIMux,
		base.InternalAPIMux,
		cfg,
		base.UseHTTPAPIs,
	)

	ygg.NotifySessionNew(func(boxPubKey crypto.BoxPubKey) {
		req := &api.PerformServersAliveRequest{
			Servers: []gomatrixserverlib.ServerName{
				gomatrixserverlib.ServerName(boxPubKey.String()),
			},
		}
		res := &api.PerformServersAliveResponse{}
		if err := fsAPI.PerformServersAlive(context.TODO(), req, res); err != nil {
			logrus.WithError(err).Warn("Failed to notify server alive due to new session")
		}
	})

	ygg.NotifyLinkNew(func(boxPubKey crypto.BoxPubKey, linkType, remote string) {
		req := &api.PerformServersAliveRequest{
			Servers: []gomatrixserverlib.ServerName{
				gomatrixserverlib.ServerName(boxPubKey.String()),
			},
		}
		res := &api.PerformServersAliveResponse{}
		if err := fsAPI.PerformServersAlive(context.TODO(), req, res); err != nil {
			logrus.WithError(err).Warn("Failed to notify server alive due to new link")
		}
	})

	// Build both ends of a HTTP multiplex.
	httpServer := &http.Server{
		Addr:         ":0",
		TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 45 * time.Second,
		IdleTimeout:  60 * time.Second,
		BaseContext: func(_ net.Listener) context.Context {
			return context.Background()
		},
		Handler: base.BaseMux,
	}

	go func() {
		logrus.Info("Listening on ", ygg.DerivedServerName())
		logrus.Fatal(httpServer.Serve(ygg))
	}()
	go func() {
		httpBindAddr := fmt.Sprintf(":%d", *instancePort)
		logrus.Info("Listening on ", httpBindAddr)
		logrus.Fatal(http.ListenAndServe(httpBindAddr, base.BaseMux))
	}()

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

package main

import (
	"context"
	"crypto/tls"
	"flag"
	"fmt"
	"net"
	"net/http"
	"time"

	"github.com/matrix-org/dendrite/appservice"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/embed"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/signing"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggconn"
	"github.com/matrix-org/dendrite/cmd/dendrite-demo-yggdrasil/yggrooms"
	"github.com/matrix-org/dendrite/currentstateserver"
	"github.com/matrix-org/dendrite/eduserver"
	"github.com/matrix-org/dendrite/eduserver/cache"
	"github.com/matrix-org/dendrite/federationsender"
	"github.com/matrix-org/dendrite/federationsender/api"
	"github.com/matrix-org/dendrite/internal"
	"github.com/matrix-org/dendrite/internal/config"
	"github.com/matrix-org/dendrite/internal/httputil"
	"github.com/matrix-org/dendrite/internal/setup"
	"github.com/matrix-org/dendrite/roomserver"
	"github.com/matrix-org/dendrite/userapi"
	"github.com/matrix-org/gomatrixserverlib"
	"github.com/yggdrasil-network/yggdrasil-go/src/crypto"

	"github.com/sirupsen/logrus"
)

var (
	instanceName = flag.String("name", "dendrite-p2p-ygg", "the name of this P2P demo instance")
	instancePort = flag.Int("port", 8008, "the port that the client API will listen on")
	instancePeer = flag.String("peer", "", "an internet Yggdrasil peer to connect to")
)

// nolint:gocyclo
func main() {
	flag.Parse()
	internal.SetupPprof()

	ygg, err := yggconn.Setup(*instanceName, ".")
	if err != nil {
		panic(err)
	}
	ygg.SetMulticastEnabled(true)
	if instancePeer != nil && *instancePeer != "" {
		if err = ygg.SetStaticPeer(*instancePeer); err != nil {
			logrus.WithError(err).Error("Failed to set static peer")
		}
	}

	cfg := &config.Dendrite{}
	cfg.SetDefaults()
	cfg.Matrix.ServerName = gomatrixserverlib.ServerName(ygg.DerivedServerName())
	cfg.Matrix.PrivateKey = ygg.SigningPrivateKey()
	cfg.Matrix.KeyID = gomatrixserverlib.KeyID(signing.KeyID)
	cfg.Kafka.UseNaffka = true
	cfg.Kafka.Topics.OutputRoomEvent = "roomserverOutput"
	cfg.Kafka.Topics.OutputClientData = "clientapiOutput"
	cfg.Kafka.Topics.OutputTypingEvent = "typingServerOutput"
	cfg.Database.Account = config.DataSource(fmt.Sprintf("file:%s-account.db", *instanceName))
	cfg.Database.Device = config.DataSource(fmt.Sprintf("file:%s-device.db", *instanceName))
	cfg.Database.MediaAPI = config.DataSource(fmt.Sprintf("file:%s-mediaapi.db", *instanceName))
	cfg.Database.SyncAPI = config.DataSource(fmt.Sprintf("file:%s-syncapi.db", *instanceName))
	cfg.Database.RoomServer = config.DataSource(fmt.Sprintf("file:%s-roomserver.db", *instanceName))
	cfg.Database.ServerKey = config.DataSource(fmt.Sprintf("file:%s-serverkey.db", *instanceName))
	cfg.Database.FederationSender = config.DataSource(fmt.Sprintf("file:%s-federationsender.db", *instanceName))
	cfg.Database.AppService = config.DataSource(fmt.Sprintf("file:%s-appservice.db", *instanceName))
	cfg.Database.CurrentState = config.DataSource(fmt.Sprintf("file:%s-currentstate.db", *instanceName))
	cfg.Database.Naffka = config.DataSource(fmt.Sprintf("file:%s-naffka.db", *instanceName))
	if err = cfg.Derive(); err != nil {
		panic(err)
	}

	base := setup.NewBaseDendrite(cfg, "Monolith", false)
	defer base.Close() // nolint: errcheck

	accountDB := base.CreateAccountsDB()
	deviceDB := base.CreateDeviceDB()
	federation := ygg.CreateFederationClient(base)

	serverKeyAPI := &signing.YggdrasilKeys{}
	keyRing := serverKeyAPI.KeyRing()

	userAPI := userapi.NewInternalAPI(accountDB, deviceDB, cfg.Matrix.ServerName, nil)

	rsComponent := roomserver.NewInternalAPI(
		base, keyRing, federation,
	)
	rsAPI := rsComponent

	eduInputAPI := eduserver.NewInternalAPI(
		base, cache.New(), userAPI,
	)

	asAPI := appservice.NewInternalAPI(base, userAPI, rsAPI)

	fsAPI := federationsender.NewInternalAPI(
		base, federation, rsAPI, keyRing,
	)

	rsComponent.SetFederationSenderAPI(fsAPI)

	embed.Embed(base.BaseMux, *instancePort, "Yggdrasil Demo")

	stateAPI := currentstateserver.NewInternalAPI(base.Cfg, base.KafkaConsumer)

	monolith := setup.Monolith{
		Config:        base.Cfg,
		AccountDB:     accountDB,
		DeviceDB:      deviceDB,
		Client:        ygg.CreateClient(base),
		FedClient:     federation,
		KeyRing:       keyRing,
		KafkaConsumer: base.KafkaConsumer,
		KafkaProducer: base.KafkaProducer,

		AppserviceAPI:       asAPI,
		EDUInternalAPI:      eduInputAPI,
		FederationSenderAPI: fsAPI,
		RoomserverAPI:       rsAPI,
		UserAPI:             userAPI,
		StateAPI:            stateAPI,
		//ServerKeyAPI:        serverKeyAPI,
		ExtPublicRoomsProvider: yggrooms.NewYggdrasilRoomProvider(
			ygg, fsAPI, federation,
		),
	}
	monolith.AddAllPublicRoutes(base.PublicAPIMux)

	httputil.SetupHTTPAPI(
		base.BaseMux,
		base.PublicAPIMux,
		base.InternalAPIMux,
		cfg,
		base.UseHTTPAPIs,
	)

	ygg.NotifySessionNew(func(boxPubKey crypto.BoxPubKey) {
		req := &api.PerformServersAliveRequest{
			Servers: []gomatrixserverlib.ServerName{
				gomatrixserverlib.ServerName(boxPubKey.String()),
			},
		}
		res := &api.PerformServersAliveResponse{}
		if err := fsAPI.PerformServersAlive(context.TODO(), req, res); err != nil {
			logrus.WithError(err).Warn("Failed to notify server alive due to new session")
		}
	})

	ygg.NotifyLinkNew(func(boxPubKey crypto.BoxPubKey, linkType, remote string) {
		req := &api.PerformServersAliveRequest{
			Servers: []gomatrixserverlib.ServerName{
				gomatrixserverlib.ServerName(boxPubKey.String()),
			},
		}
		res := &api.PerformServersAliveResponse{}
		if err := fsAPI.PerformServersAlive(context.TODO(), req, res); err != nil {
			logrus.WithError(err).Warn("Failed to notify server alive due to new link")
		}
	})

	// Build both ends of a HTTP multiplex.
	httpServer := &http.Server{
		Addr:         ":0",
		TLSNextProto: map[string]func(*http.Server, *tls.Conn, http.Handler){},
		ReadTimeout:  15 * time.Second,
		WriteTimeout: 45 * time.Second,
		IdleTimeout:  60 * time.Second,
		BaseContext: func(_ net.Listener) context.Context {
			return context.Background()
		},
		Handler: base.BaseMux,
	}

	go func() {
		logrus.Info("Listening on ", ygg.DerivedServerName())
		logrus.Fatal(httpServer.Serve(ygg))
	}()
	go func() {
		httpBindAddr := fmt.Sprintf(":%d", *instancePort)
		logrus.Info("Listening on ", httpBindAddr)
		logrus.Fatal(http.ListenAndServe(httpBindAddr, base.BaseMux))
	}()

	select {}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/localpath"
)

func TestNotFound(t *testing.T) {
	createTestProfile(t)
	err := Set("nonexistent", "10")
	if err == nil {
		t.Fatalf("Set did not return error for unknown property")
	}
}

func TestSetNotAllowed(t *testing.T) {
	createTestProfile(t)
	err := Set("vm-driver", "123456")
	if err == nil || err.Error() != "[driver \"123456\" is not supported]" {
		t.Fatalf("Set did not return error for unallowed value")
	}
}

func TestSetOK(t *testing.T) {
	createTestProfile(t)
	err := Set("vm-driver", "virtualbox")
	defer Unset("vm-driver")
	if err != nil {
		t.Fatalf("Set returned error for valid property value")
	}
	val, err := Get("vm-driver")
	if err != nil {
		t.Fatalf("Get returned error for valid property")
	}
	if val != "virtualbox" {
		t.Fatalf("Get returned %s, expected \"virtualbox\"", val)
	}
}

func createTestProfile(t *testing.T) string {
	t.Helper()
	td, err := ioutil.TempDir("", "profile")
	if err != nil {
		t.Fatalf("tempdir: %v", err)
	}

	err = os.Setenv(localpath.MinikubeHome, td)
	if err != nil {
		t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome)
	}

	// Not necessary, but it is a handy random alphanumeric
	name := filepath.Base(td)
	if err := os.MkdirAll(config.ProfileFolderPath(name), 0777); err != nil {
		t.Fatalf("error creating temporary directory")
	}
	if err := config.DefaultLoader.WriteConfigToFile(name, &config.MachineConfig{}); err != nil {
		t.Fatalf("error creating temporary profile config: %v", err)
	}
	return name
}
<commit_msg>fix lint<commit_after>/*
Copyright 2016 The Kubernetes Authors All rights reserved.

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

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

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

package config

import (
	"io/ioutil"
	"os"
	"path/filepath"
	"testing"

	"k8s.io/minikube/pkg/minikube/config"
	"k8s.io/minikube/pkg/minikube/localpath"
)

func TestNotFound(t *testing.T) {
	createTestProfile(t)
	err := Set("nonexistent", "10")
	if err == nil {
		t.Fatalf("Set did not return error for unknown property")
	}
}

func TestSetNotAllowed(t *testing.T) {
	createTestProfile(t)
	err := Set("vm-driver", "123456")
	if err == nil || err.Error() != "[driver \"123456\" is not supported]" {
		t.Fatalf("Set did not return error for unallowed value")
	}
}

func TestSetOK(t *testing.T) {
	createTestProfile(t)
	err := Set("vm-driver", "virtualbox")
	defer func() {
		err = Unset("vm-driver")
		if err != nil {
			t.Errorf("failed to unset vm-driver")
		}
	}()
	if err != nil {
		t.Fatalf("Set returned error for valid property value")
	}
	val, err := Get("vm-driver")
	if err != nil {
		t.Fatalf("Get returned error for valid property")
	}
	if val != "virtualbox" {
		t.Fatalf("Get returned %s, expected \"virtualbox\"", val)
	}
}

func createTestProfile(t *testing.T) {
	t.Helper()
	td, err := ioutil.TempDir("", "profile")
	if err != nil {
		t.Fatalf("tempdir: %v", err)
	}

	err = os.Setenv(localpath.MinikubeHome, td)
	if err != nil {
		t.Errorf("error setting up test environment. could not set %s", localpath.MinikubeHome)
	}

	// Not necessary, but it is a handy random alphanumeric
	name := filepath.Base(td)
	if err := os.MkdirAll(config.ProfileFolderPath(name), 0777); err != nil {
		t.Fatalf("error creating temporary directory")
	}
	if err := config.DefaultLoader.WriteConfigToFile(name, &config.MachineConfig{}); err != nil {
		t.Fatalf("error creating temporary profile config: %v", err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"path/filepath"
	"testing"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/pkg/test"
	"github.com/cerana/cerana/provider"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/systemd"
	"github.com/cerana/cerana/providers/zfs"
	"github.com/spf13/pflag"
	"github.com/stretchr/testify/suite"
)

type StatsPusher struct {
	suite.Suite
	config      *config
	configData  *ConfigData
	configFile  *os.File
	coordinator *test.Coordinator
	tracker     *acomm.Tracker
	systemd     *systemd.MockSystemd
	zfs         *zfs.MockZFS
	clusterConf *clusterconf.MockClusterConf
}

func TestStatsPusher(t *testing.T) {
	suite.Run(t, new(StatsPusher))
}

func (s *StatsPusher) SetupSuite() {
	noError := s.Require().NoError

	logrus.SetLevel(logrus.FatalLevel)

	// Setup mock coordinator
	var err error
	s.coordinator, err = test.NewCoordinator("")
	noError(err)

	coordinatorURL := s.coordinator.NewProviderViper().GetString("coordinator_url")
	s.configData = &ConfigData{
		CoordinatorURL: coordinatorURL,
		HeartbeatURL:   coordinatorURL,
		LogLevel:       "fatal",
		RequestTimeout: 5,
		DatasetTTL:     4,
		BundleTTL:      3,
		NodeTTL:        2,
	}

	s.config, _, _, s.configFile, err = newTestConfig(false, true, s.configData)
	noError(err, "failed to create config")
	noError(s.config.loadConfig(), "failed to load config")

	// Setup mock coordinator and providers
	s.coordinator, err = test.NewCoordinator("")
	noError(err)

	s.tracker, err = acomm.NewTracker(filepath.Join(s.coordinator.SocketDir, "tracker.sock"), nil, nil, 5*time.Second)
	noError(err)

	s.setupSystemd()
	s.setupZFS()
	s.setupClusterConf()

	noError(s.coordinator.Start())
}

func (s *StatsPusher) setupClusterConf() {
	s.clusterConf = clusterconf.NewMockClusterConf()
	s.coordinator.RegisterProvider(s.clusterConf)
}

func (s *StatsPusher) setupZFS() {
	v := s.coordinator.NewProviderViper()
	flagset := pflag.NewFlagSet("zfs", pflag.PanicOnError)
	config := provider.NewConfig(flagset, v)
	s.Require().NoError(flagset.Parse([]string{}))
	s.Require().NoError(config.LoadConfig())
	s.zfs = zfs.NewMockZFS(config, s.tracker)
	s.coordinator.RegisterProvider(s.zfs)
}

func (s *StatsPusher) setupSystemd() {
	s.systemd = systemd.NewMockSystemd()
	s.coordinator.RegisterProvider(s.systemd)
}

func (s *StatsPusher) TearDownSuite() {
	s.coordinator.Stop()
	s.Require().NoError(s.coordinator.Cleanup())
	_ = os.Remove(s.configFile.Name())
}
<commit_msg>Fix up statspusher test suite setup/teardown<commit_after>package main

import (
	"os"
	"testing"

	"github.com/Sirupsen/logrus"
	"github.com/cerana/cerana/acomm"
	"github.com/cerana/cerana/pkg/test"
	"github.com/cerana/cerana/provider"
	"github.com/cerana/cerana/providers/clusterconf"
	"github.com/cerana/cerana/providers/systemd"
	"github.com/cerana/cerana/providers/zfs"
	"github.com/spf13/pflag"
	"github.com/stretchr/testify/suite"
)

type StatsPusher struct {
	suite.Suite
	config      *config
	configData  *ConfigData
	configFile  *os.File
	statsPusher *statsPusher
	tracker     *acomm.Tracker
	coordinator *test.Coordinator
	systemd     *systemd.MockSystemd
	zfs         *zfs.MockZFS
	clusterConf *clusterconf.MockClusterConf
}

func TestStatsPusher(t *testing.T) {
	suite.Run(t, new(StatsPusher))
}

func (s *StatsPusher) SetupSuite() {
	noError := s.Require().NoError

	logrus.SetLevel(logrus.FatalLevel)

	// Setup mock coordinator
	var err error
	s.coordinator, err = test.NewCoordinator("")
	noError(err)

	coordinatorURL := s.coordinator.NewProviderViper().GetString("coordinator_url")
	s.configData = &ConfigData{
		CoordinatorURL: coordinatorURL,
		HeartbeatURL:   coordinatorURL,
		LogLevel:       "fatal",
		RequestTimeout: 5,
		DatasetTTL:     4,
		BundleTTL:      3,
		NodeTTL:        2,
	}

	s.config, _, _, s.configFile, err = newTestConfig(false, true, s.configData)
	noError(err, "failed to create config")
	noError(s.config.loadConfig(), "failed to load config")

	s.statsPusher, err = newStatsPusher(s.config)
	noError(err)
	noError(s.statsPusher.tracker.Start())

	// Setup mock providers
	noError(err)

	s.setupSystemd()
	s.setupZFS()
	s.setupClusterConf()

	noError(s.coordinator.Start())
}

func (s *StatsPusher) setupClusterConf() {
	s.clusterConf = clusterconf.NewMockClusterConf()
	s.coordinator.RegisterProvider(s.clusterConf)
}

func (s *StatsPusher) setupZFS() {
	v := s.coordinator.NewProviderViper()
	flagset := pflag.NewFlagSet("zfs", pflag.PanicOnError)
	config := provider.NewConfig(flagset, v)
	s.Require().NoError(flagset.Parse([]string{}))
	s.Require().NoError(config.LoadConfig())
	s.zfs = zfs.NewMockZFS(config, s.coordinator.ProviderTracker())
	s.coordinator.RegisterProvider(s.zfs)
}

func (s *StatsPusher) setupSystemd() {
	s.systemd = systemd.NewMockSystemd()
	s.coordinator.RegisterProvider(s.systemd)
}

func (s *StatsPusher) TearDownSuite() {
	s.coordinator.Stop()
	s.Require().NoError(s.coordinator.Cleanup())
	_ = os.Remove(s.configFile.Name())
	s.statsPusher.tracker.Stop()
}
<|endoftext|>


<commit_before>// Package list outputs a list of Lambda function information.
package list

import (
	"fmt"

	"github.com/apex/log"
	"github.com/spf13/cobra"

	"github.com/apex/apex/cmd/apex/root"
	"github.com/apex/apex/colors"
)

// tfvars output format.
var tfvars bool

// example output.
const example = `  List all functions
  $ apex list

  Output list as Terraform variables (.tfvars)
  $ apex list --tfvars`

// Command config.
var Command = &cobra.Command{
	Use:     "list",
	Short:   "List functions",
	Example: example,
	RunE:    run,
}

// Initialize.
func init() {
	root.Register(Command)

	f := Command.Flags()
	f.BoolVar(&tfvars, "tfvars", false, "Output as Terraform variables")
}

// Run command.
func run(c *cobra.Command, args []string) error {
	if err := root.Project.LoadFunctions(); err != nil {
		return err
	}

	if tfvars {
		outputTFvars()
	} else {
		outputList()
	}

	return nil
}

// outputTFvars format.
func outputTFvars() {
	for _, fn := range root.Project.Functions {
		config, err := fn.GetConfig()
		if err != nil {
			log.Debugf("can't fetch function config: %s", err.Error())
			continue
		}

		fmt.Printf("apex_function_%s=%q\n", fn.Name, *config.Configuration.FunctionArn)
	}
}

// outputList format.
func outputList() {
	fmt.Println()
	for _, fn := range root.Project.Functions {
		fmt.Printf("  \033[%dm%s\033[0m\n", colors.Blue, fn.Name)
		if fn.Description != "" {
			fmt.Printf("    description: %v\n", fn.Description)
		}
		fmt.Printf("    runtime: %v\n", fn.Runtime)
		fmt.Printf("    memory: %vmb\n", fn.Memory)
		fmt.Printf("    timeout: %vs\n", fn.Timeout)
		fmt.Printf("    role: %v\n", fn.Role)
		fmt.Printf("    handler: %v\n", fn.Handler)

		config, err := fn.GetConfigCurrent()
		if err != nil {
			fmt.Println()
			continue // ignore
		}

		fmt.Printf("    current version: %s\n", *config.Configuration.Version)

		fmt.Println()
	}
}
<commit_msg>change list command desc to match others<commit_after>// Package list outputs a list of Lambda function information.
package list

import (
	"fmt"

	"github.com/apex/log"
	"github.com/spf13/cobra"

	"github.com/apex/apex/cmd/apex/root"
	"github.com/apex/apex/colors"
)

// tfvars output format.
var tfvars bool

// example output.
const example = `  List all functions
  $ apex list

  Output list as Terraform variables (.tfvars)
  $ apex list --tfvars`

// Command config.
var Command = &cobra.Command{
	Use:     "list",
	Short:   "Output functions list",
	Example: example,
	RunE:    run,
}

// Initialize.
func init() {
	root.Register(Command)

	f := Command.Flags()
	f.BoolVar(&tfvars, "tfvars", false, "Output as Terraform variables")
}

// Run command.
func run(c *cobra.Command, args []string) error {
	if err := root.Project.LoadFunctions(); err != nil {
		return err
	}

	if tfvars {
		outputTFvars()
	} else {
		outputList()
	}

	return nil
}

// outputTFvars format.
func outputTFvars() {
	for _, fn := range root.Project.Functions {
		config, err := fn.GetConfig()
		if err != nil {
			log.Debugf("can't fetch function config: %s", err.Error())
			continue
		}

		fmt.Printf("apex_function_%s=%q\n", fn.Name, *config.Configuration.FunctionArn)
	}
}

// outputList format.
func outputList() {
	fmt.Println()
	for _, fn := range root.Project.Functions {
		fmt.Printf("  \033[%dm%s\033[0m\n", colors.Blue, fn.Name)
		if fn.Description != "" {
			fmt.Printf("    description: %v\n", fn.Description)
		}
		fmt.Printf("    runtime: %v\n", fn.Runtime)
		fmt.Printf("    memory: %vmb\n", fn.Memory)
		fmt.Printf("    timeout: %vs\n", fn.Timeout)
		fmt.Printf("    role: %v\n", fn.Role)
		fmt.Printf("    handler: %v\n", fn.Handler)

		config, err := fn.GetConfigCurrent()
		if err != nil {
			fmt.Println()
			continue // ignore
		}

		fmt.Printf("    current version: %s\n", *config.Configuration.Version)

		fmt.Println()
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/lib"
)

// AccountNameFlag is used for all --account flags, including the global one.
type AccountNameFlag string

// Set runs lib.Client.ParseAccountName using the global.Client to make sure we get just the 'pure' account name; no cluster / endpoint details
func (name *AccountNameFlag) Set(value string) error {
	*name = AccountNameFlag(global.Client.ParseAccountName(value, global.Config.GetIgnoreErr("account")))
	return nil
}

// String returns the AccountNameFlag as a string.
func (name *AccountNameFlag) String() string {
	return string(*name)
}

// GroupNameFlag is used for all --account flags, including the global one.
type GroupNameFlag lib.GroupName

// Set runs lib.Client.ParseGroupName using the global.Client to make sure we have a valid group name
func (name *GroupNameFlag) Set(value string) error {
	gp := global.Client.ParseGroupName(value, global.Config.GetGroup())
	*name = GroupNameFlag(*gp)
	fmt.Printf("Setting GroupNameFlag to %s: parsed as %#v\r\n", value, gp)
	return nil
}

// String returns the GroupNameFlag as a string.
func (name *GroupNameFlag) String() string {
	return lib.GroupName(*name).String()
}
<commit_msg>Remove debug code from flags.go<commit_after>package main

import (
	"fmt"
	"github.com/BytemarkHosting/bytemark-client/lib"
)

// AccountNameFlag is used for all --account flags, including the global one.
type AccountNameFlag string

// Set runs lib.Client.ParseAccountName using the global.Client to make sure we get just the 'pure' account name; no cluster / endpoint details
func (name *AccountNameFlag) Set(value string) error {
	*name = AccountNameFlag(global.Client.ParseAccountName(value, global.Config.GetIgnoreErr("account")))
	return nil
}

// String returns the AccountNameFlag as a string.
func (name *AccountNameFlag) String() string {
	return string(*name)
}

// GroupNameFlag is used for all --account flags, including the global one.
type GroupNameFlag lib.GroupName

// Set runs lib.Client.ParseGroupName using the global.Client to make sure we have a valid group name
func (name *GroupNameFlag) Set(value string) error {
	gp := global.Client.ParseGroupName(value, global.Config.GetGroup())
	*name = GroupNameFlag(*gp)
	return nil
}

// String returns the GroupNameFlag as a string.
func (name *GroupNameFlag) String() string {
	return lib.GroupName(*name).String()
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"time"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/args"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	brainMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

func init() {
	commands = append(commands, cli.Command{
		Name:        "reset",
		Usage:       "restart a server as though the reset button had been pushed",
		UsageText:   "bytemark reset <server>",
		Description: "For cloud servers, this does not cause the qemu process to be restarted. This means that the server will remain on the same head and will not notice hardware changes.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to reset",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to reset %v...\r\n", vmName)
			err = c.Client().ResetVirtualMachine(vmName)
			if err != nil {
				return err
			}

			log.Errorf("%v reset successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "restart",
		Usage:       "power off a server and start it again",
		UsageText:   "bytemark restart <server> [--rescue || --appliance <appliance>]",
		Description: "This command will power down a server and then start it back up again.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to restart",
				Value: new(app.VirtualMachineNameFlag),
			},
			cli.BoolFlag{
				Name:  "rescue",
				Usage: "boots the server using the rescue appliance",
			},
			cli.StringFlag{
				Name:  "appliance",
				Usage: "the appliance to boot into when the server starts",
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			appliance := c.String("appliance")

			if appliance != "" && c.Bool("rescue") {
				return fmt.Errorf("--appliance and --rescue have both been set when only one is allowed")
			}

			if c.Bool("rescue") {
				appliance = "rescue"
			}

			fmt.Fprintf(c.App().Writer, "Shutting down %v...", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}
			err = waitForShutdown(c, vmName)
			if err != nil {
				return
			}

			c.Log("Done!\n\nStarting %s back up.", vmName)
			if appliance != "" {
				err = brainMethods.StartVirtualMachineWithAppliance(c.Client(), vmName, appliance)
				c.Log("Server has now started. Please use bytemark console or visit the panel to connect.\n\n")
			} else {
				err = c.Client().StartVirtualMachine(vmName)
			}

			return
		}),
	}, cli.Command{
		Name:        "shutdown",
		Usage:       "cleanly shut down a server",
		UsageText:   "bytemark shutdown <server>",
		Description: "This command sends the ACPI shutdown signal to the server, causing a clean shut down. This is like pressing the power button on a computer you have physical access to.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to shutdown",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			fmt.Fprintf(c.App().Writer, "Shutting down %v...", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}

			err = waitForShutdown(c, vmName)
			if err != nil {
				return
			}

			c.Log("Done!", vmName)
			return
		}),
	}, cli.Command{
		Name:        "start",
		Usage:       "start a stopped server",
		UsageText:   "bytemark start <server>",
		Description: "This command will start a server that is not currently running.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to start",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to start %s...\r\n", vmName)
			err = c.Client().StartVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s started successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "stop",
		Usage:       "stop a server, as though pulling the power cable out",
		UsageText:   "bytemark stop <server>",
		Description: "This command will instantly power down a server. Note that this may cause data loss, particularly on servers with unjournaled file systems (e.g. ext2)",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to stop",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to stop %s...\r\n", vmName)
			err = c.Client().StopVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s stopped successfully.\r\n", vmName)
			return
		}),
	})
}
func waitForShutdown(c *app.Context, name lib.VirtualMachineName) (err error) {
	vm := brain.VirtualMachine{PowerOn: true}

	for vm.PowerOn {
		time.Sleep(5 * time.Second)
		fmt.Fprint(c.App().Writer, ".")

		vm, err = c.Client().GetVirtualMachine(name)
		if err != nil {
			return
		}
	}
	return
}
<commit_msg>update reboot complete message<commit_after>package main

import (
	"fmt"
	"time"

	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/args"
	"github.com/BytemarkHosting/bytemark-client/cmd/bytemark/app/with"
	"github.com/BytemarkHosting/bytemark-client/lib"
	"github.com/BytemarkHosting/bytemark-client/lib/brain"
	brainMethods "github.com/BytemarkHosting/bytemark-client/lib/requests/brain"
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
)

func init() {
	commands = append(commands, cli.Command{
		Name:        "reset",
		Usage:       "restart a server as though the reset button had been pushed",
		UsageText:   "bytemark reset <server>",
		Description: "For cloud servers, this does not cause the qemu process to be restarted. This means that the server will remain on the same head and will not notice hardware changes.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to reset",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to reset %v...\r\n", vmName)
			err = c.Client().ResetVirtualMachine(vmName)
			if err != nil {
				return err
			}

			log.Errorf("%v reset successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "restart",
		Usage:       "power off a server and start it again",
		UsageText:   "bytemark restart <server> [--rescue || --appliance <appliance>]",
		Description: "This command will power down a server and then start it back up again.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to restart",
				Value: new(app.VirtualMachineNameFlag),
			},
			cli.BoolFlag{
				Name:  "rescue",
				Usage: "boots the server using the rescue appliance",
			},
			cli.StringFlag{
				Name:  "appliance",
				Usage: "the appliance to boot into when the server starts",
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			appliance := c.String("appliance")

			if appliance != "" && c.Bool("rescue") {
				return fmt.Errorf("--appliance and --rescue have both been set when only one is allowed")
			}

			if c.Bool("rescue") {
				appliance = "rescue"
			}

			fmt.Fprintf(c.App().Writer, "Shutting down %v...", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}
			err = waitForShutdown(c, vmName)
			if err != nil {
				return
			}

			c.Log("Done!\n\nStarting %s back up.", vmName)
			if appliance != "" {
				err = brainMethods.StartVirtualMachineWithAppliance(c.Client(), vmName, appliance)
				c.Log("Server has now started. Use bytemark console %v` or visit https://%v to connect.", c.String("server"), c.Config().PanelURL())
			} else {
				err = c.Client().StartVirtualMachine(vmName)
			}

			return
		}),
	}, cli.Command{
		Name:        "shutdown",
		Usage:       "cleanly shut down a server",
		UsageText:   "bytemark shutdown <server>",
		Description: "This command sends the ACPI shutdown signal to the server, causing a clean shut down. This is like pressing the power button on a computer you have physical access to.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to shutdown",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			fmt.Fprintf(c.App().Writer, "Shutting down %v...", vmName)
			err = c.Client().ShutdownVirtualMachine(vmName, true)
			if err != nil {
				return
			}

			err = waitForShutdown(c, vmName)
			if err != nil {
				return
			}

			c.Log("Done!", vmName)
			return
		}),
	}, cli.Command{
		Name:        "start",
		Usage:       "start a stopped server",
		UsageText:   "bytemark start <server>",
		Description: "This command will start a server that is not currently running.",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to start",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to start %s...\r\n", vmName)
			err = c.Client().StartVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s started successfully.\r\n", vmName)
			return
		}),
	}, cli.Command{
		Name:        "stop",
		Usage:       "stop a server, as though pulling the power cable out",
		UsageText:   "bytemark stop <server>",
		Description: "This command will instantly power down a server. Note that this may cause data loss, particularly on servers with unjournaled file systems (e.g. ext2)",
		Flags: []cli.Flag{
			cli.GenericFlag{
				Name:  "server",
				Usage: "the server to stop",
				Value: new(app.VirtualMachineNameFlag),
			},
		},
		Action: app.Action(args.Optional("server"), with.RequiredFlags("server"), with.Auth, func(c *app.Context) (err error) {
			vmName := c.VirtualMachineName("server")
			log.Logf("Attempting to stop %s...\r\n", vmName)
			err = c.Client().StopVirtualMachine(vmName)
			if err != nil {
				return
			}

			log.Logf("%s stopped successfully.\r\n", vmName)
			return
		}),
	})
}
func waitForShutdown(c *app.Context, name lib.VirtualMachineName) (err error) {
	vm := brain.VirtualMachine{PowerOn: true}

	for vm.PowerOn {
		time.Sleep(5 * time.Second)
		fmt.Fprint(c.App().Writer, ".")

		vm, err = c.Client().GetVirtualMachine(name)
		if err != nil {
			return
		}
	}
	return
}
<|endoftext|>