<commit_before>package tesTaskEngineWorker

import (
	"fmt"
	proto "github.com/golang/protobuf/proto"
	"os"
	"path"
	"path/filepath"
	"strings"
	pbe "tes/ga4gh"
)

// FileMapper is responsible for mapping paths into a working directory on the
// worker's host file system.
//
// Every job needs it's own directory to work in. When a file is downloaded for
// a job, it needs to be stored in the job's working directory. Similar for job
// outputs, uploads, stdin/out/err, etc. FileMapper helps the worker engine
// manage all these paths.
type FileMapper struct {
	Volumes []Volume
	Inputs  []*pbe.TaskParameter
	Outputs []*pbe.TaskParameter
	dir     string
}

// Volume represents a volume mounted into a docker container.
// This includes a HostPath, the path on the host file system,
// and a ContainerPath, the path on the container file system,
// and the mode ("rw" = read-only, "ro" = read-write).
type Volume struct {
	// The path in tes worker.
	HostPath string
	// The path in Docker.
	ContainerPath string
	Mode          string
}

// NewFileMapper returns a new FileMapper configured to map files for a job.
//
// The following example will return a FileMapper that maps into the
// "/path/to/workdir/123/" directory on the host file system.
//     NewJobFileMapper("123", "/path/to/workdir")
func NewJobFileMapper(jobID string, baseDir string) *FileMapper {
	dir := path.Join(baseDir, jobID)
	// TODO error handling
	dir, _ = filepath.Abs(dir)
	return &FileMapper{dir: dir}
}

// AddVolume adds a mapped volume to the mapper. A corresponding Volume record
// is added to mapper.Volumes.
//
// Currently, volumes are hard-coded to "rw" (read-write).
//
// If the volume paths are invalid or can't be mapped, an error is returned.
func (mapper *FileMapper) AddVolume(source string, mountPoint string) error {
	if source != "" {
		return fmt.Errorf("Could not create a volume: 'source' is not supported for %s", source)
	}
	if mountPoint == "" {
		return fmt.Errorf("Could not create a volume: 'mountPoint' is required for %s", mountPoint)
	}

	hostPath, err := mapper.HostPath(mountPoint)
	if err != nil {
		return err
	}

	v := Volume{
		HostPath:      hostPath,
		ContainerPath: mountPoint,
		// TODO should be read only?
		Mode: "rw",
	}

  // Ensure that the volume directory exists on the host
	perr := ensurePath(hostPath)
	if perr != nil { return perr }

	mapper.Volumes = append(mapper.Volumes, v)
	return nil
}

// HostPath returns a mapped path.
//
// The path is concatenated to the mapper's base dir.
// e.g. If the mapper is configured with a base dir of "/tmp/mapped_files", then
// mapper.HostPath("/home/ubuntu/myfile") will return "/tmp/mapped_files/home/ubuntu/myfile".
//
// The mapped path is required to be a subpath of the mapper's base directory.
// e.g. mapper.HostPath("../../foo") should fail with an error.
func (mapper *FileMapper) HostPath(src string) (string, error) {
	p := path.Join(mapper.dir, src)
	p = path.Clean(p)
	if !mapper.IsSubpath(p, mapper.dir) {
		return "", fmt.Errorf("Invalid path: %s is not a valid subpath of %s", p, mapper.dir)
	}
	return p, nil
}

// OpenHostFile opens a file on the host file system at a mapped path.
// "src" is an unmapped path. This function will handle mapping the path.
//
// This function calls os.Open
//
// If the path can't be mapped or the file can't be opened, an error is returned.
func (mapper *FileMapper) OpenHostFile(src string) (*os.File, error) {
	p, perr := mapper.HostPath(src)
	if perr != nil {
		return nil, perr
	}
	f, oerr := os.Open(p)
	if oerr != nil {
		return nil, oerr
	}
	return f, nil
}

// CreateHostFile creates a file on the host file system at a mapped path.
// "src" is an unmapped path. This function will handle mapping the path.
//
// This function calls os.Create
//
// If the path can't be mapped or the file can't be created, an error is returned.
func (mapper *FileMapper) CreateHostFile(src string) (*os.File, error) {
	p, perr := mapper.HostPath(src)
	if perr != nil {
		return nil, perr
	}
	err := ensurePath(p)
	if err != nil {
		return nil, err
	}
	f, oerr := os.Create(p)
	if oerr != nil {
		return nil, oerr
	}
	return f, nil
}

// AppInput adds an input to the mapped files for the given TaskParameter.
// A copy of the TaskParameter will be added to mapper.Inputs, with the
// "Path" field updated to the mapped host path.
//
// If the path can't be mapped, or the path is not in an existing volume,
// an error is returned.
func (mapper *FileMapper) AddInput(input *pbe.TaskParameter) error {
	p, err := mapper.HostPath(input.Path)
	if err != nil {
		return err
	}
	// Require that the path be in a defined volume
	if !mapper.IsInVolume(p) {
		return fmt.Errorf("Input path is required to be in a volume: %s", input.Path)
	}

	perr := ensurePath(p)
	if perr != nil { return perr }

	// Create a TaskParameter for the input with a path mapped to the host
	hostIn := proto.Clone(input).(*pbe.TaskParameter)
	hostIn.Path = p
	mapper.Inputs = append(mapper.Inputs, hostIn)
	return nil
}

// AddOutput adds an output to the mapped files for the given TaskParameter.
// A copy of the TaskParameter will be added to mapper.Outputs, with the
// "Path" field updated to the mapped host path.
//
// If the Create flag is set on the TaskParameter, the file will be created
// on the host file system.
//
// If the path can't be mapped, or the path is not in an existing volume,
// an error is returned.
func (mapper *FileMapper) AddOutput(output *pbe.TaskParameter) error {
	p, err := mapper.HostPath(output.Path)
	if err != nil {
		return err
	}
	// Require that the path be in a defined volume
	if !mapper.IsInVolume(p) {
		return fmt.Errorf("Output path is required to be in a volume: %s", output.Path)
	}
	// Create the file if needed, as per the TES spec
	if output.Create {
		err := ensureFile(p, output.Class)
		if err != nil {
			return err
		}
	}
	// Create a TaskParameter for the out with a path mapped to the host
	hostOut := proto.Clone(output).(*pbe.TaskParameter)
	hostOut.Path = p
	mapper.Outputs = append(mapper.Outputs, hostOut)
	return nil
}

// IsSubpath returns true if the given path "p" is a subpath of "base".
func (mapper *FileMapper) IsSubpath(p string, base string) bool {
	return strings.HasPrefix(p, base)
}

// InInVolume checks whether a given path is in a mapped volume.
func (mapper *FileMapper) IsInVolume(p string) bool {
	for _, vol := range mapper.Volumes {
		if mapper.IsSubpath(p, vol.HostPath) {
			return true
		}
	}
	return false
}
<commit_msg>Call the right function dummy<commit_after>package tesTaskEngineWorker

import (
	"fmt"
	proto "github.com/golang/protobuf/proto"
	"os"
	"path"
	"path/filepath"
	"strings"
	pbe "tes/ga4gh"
)

// FileMapper is responsible for mapping paths into a working directory on the
// worker's host file system.
//
// Every job needs it's own directory to work in. When a file is downloaded for
// a job, it needs to be stored in the job's working directory. Similar for job
// outputs, uploads, stdin/out/err, etc. FileMapper helps the worker engine
// manage all these paths.
type FileMapper struct {
	Volumes []Volume
	Inputs  []*pbe.TaskParameter
	Outputs []*pbe.TaskParameter
	dir     string
}

// Volume represents a volume mounted into a docker container.
// This includes a HostPath, the path on the host file system,
// and a ContainerPath, the path on the container file system,
// and the mode ("rw" = read-only, "ro" = read-write).
type Volume struct {
	// The path in tes worker.
	HostPath string
	// The path in Docker.
	ContainerPath string
	Mode          string
}

// NewFileMapper returns a new FileMapper configured to map files for a job.
//
// The following example will return a FileMapper that maps into the
// "/path/to/workdir/123/" directory on the host file system.
//     NewJobFileMapper("123", "/path/to/workdir")
func NewJobFileMapper(jobID string, baseDir string) *FileMapper {
	dir := path.Join(baseDir, jobID)
	// TODO error handling
	dir, _ = filepath.Abs(dir)
	return &FileMapper{dir: dir}
}

// AddVolume adds a mapped volume to the mapper. A corresponding Volume record
// is added to mapper.Volumes.
//
// Currently, volumes are hard-coded to "rw" (read-write).
//
// If the volume paths are invalid or can't be mapped, an error is returned.
func (mapper *FileMapper) AddVolume(source string, mountPoint string) error {
	if source != "" {
		return fmt.Errorf("Could not create a volume: 'source' is not supported for %s", source)
	}
	if mountPoint == "" {
		return fmt.Errorf("Could not create a volume: 'mountPoint' is required for %s", mountPoint)
	}

	hostPath, err := mapper.HostPath(mountPoint)
	if err != nil {
		return err
	}

	v := Volume{
		HostPath:      hostPath,
		ContainerPath: mountPoint,
		// TODO should be read only?
		Mode: "rw",
	}

  // Ensure that the volume directory exists on the host
	perr := ensureDir(hostPath)
	if perr != nil { return perr }

	mapper.Volumes = append(mapper.Volumes, v)
	return nil
}

// HostPath returns a mapped path.
//
// The path is concatenated to the mapper's base dir.
// e.g. If the mapper is configured with a base dir of "/tmp/mapped_files", then
// mapper.HostPath("/home/ubuntu/myfile") will return "/tmp/mapped_files/home/ubuntu/myfile".
//
// The mapped path is required to be a subpath of the mapper's base directory.
// e.g. mapper.HostPath("../../foo") should fail with an error.
func (mapper *FileMapper) HostPath(src string) (string, error) {
	p := path.Join(mapper.dir, src)
	p = path.Clean(p)
	if !mapper.IsSubpath(p, mapper.dir) {
		return "", fmt.Errorf("Invalid path: %s is not a valid subpath of %s", p, mapper.dir)
	}
	return p, nil
}

// OpenHostFile opens a file on the host file system at a mapped path.
// "src" is an unmapped path. This function will handle mapping the path.
//
// This function calls os.Open
//
// If the path can't be mapped or the file can't be opened, an error is returned.
func (mapper *FileMapper) OpenHostFile(src string) (*os.File, error) {
	p, perr := mapper.HostPath(src)
	if perr != nil {
		return nil, perr
	}
	f, oerr := os.Open(p)
	if oerr != nil {
		return nil, oerr
	}
	return f, nil
}

// CreateHostFile creates a file on the host file system at a mapped path.
// "src" is an unmapped path. This function will handle mapping the path.
//
// This function calls os.Create
//
// If the path can't be mapped or the file can't be created, an error is returned.
func (mapper *FileMapper) CreateHostFile(src string) (*os.File, error) {
	p, perr := mapper.HostPath(src)
	if perr != nil {
		return nil, perr
	}
	err := ensurePath(p)
	if err != nil {
		return nil, err
	}
	f, oerr := os.Create(p)
	if oerr != nil {
		return nil, oerr
	}
	return f, nil
}

// AppInput adds an input to the mapped files for the given TaskParameter.
// A copy of the TaskParameter will be added to mapper.Inputs, with the
// "Path" field updated to the mapped host path.
//
// If the path can't be mapped, or the path is not in an existing volume,
// an error is returned.
func (mapper *FileMapper) AddInput(input *pbe.TaskParameter) error {
	p, err := mapper.HostPath(input.Path)
	if err != nil {
		return err
	}
	// Require that the path be in a defined volume
	if !mapper.IsInVolume(p) {
		return fmt.Errorf("Input path is required to be in a volume: %s", input.Path)
	}

	perr := ensurePath(p)
	if perr != nil { return perr }

	// Create a TaskParameter for the input with a path mapped to the host
	hostIn := proto.Clone(input).(*pbe.TaskParameter)
	hostIn.Path = p
	mapper.Inputs = append(mapper.Inputs, hostIn)
	return nil
}

// AddOutput adds an output to the mapped files for the given TaskParameter.
// A copy of the TaskParameter will be added to mapper.Outputs, with the
// "Path" field updated to the mapped host path.
//
// If the Create flag is set on the TaskParameter, the file will be created
// on the host file system.
//
// If the path can't be mapped, or the path is not in an existing volume,
// an error is returned.
func (mapper *FileMapper) AddOutput(output *pbe.TaskParameter) error {
	p, err := mapper.HostPath(output.Path)
	if err != nil {
		return err
	}
	// Require that the path be in a defined volume
	if !mapper.IsInVolume(p) {
		return fmt.Errorf("Output path is required to be in a volume: %s", output.Path)
	}
	// Create the file if needed, as per the TES spec
	if output.Create {
		err := ensureFile(p, output.Class)
		if err != nil {
			return err
		}
	}
	// Create a TaskParameter for the out with a path mapped to the host
	hostOut := proto.Clone(output).(*pbe.TaskParameter)
	hostOut.Path = p
	mapper.Outputs = append(mapper.Outputs, hostOut)
	return nil
}

// IsSubpath returns true if the given path "p" is a subpath of "base".
func (mapper *FileMapper) IsSubpath(p string, base string) bool {
	return strings.HasPrefix(p, base)
}

// InInVolume checks whether a given path is in a mapped volume.
func (mapper *FileMapper) IsInVolume(p string) bool {
	for _, vol := range mapper.Volumes {
		if mapper.IsSubpath(p, vol.HostPath) {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package agent

import (
	"context"
	"crypto/sha1"
	"encoding/binary"
	"hash"
	"reflect"
	"sort"
	"sync"
	"sync/atomic"
	"unsafe"
)

//
// slotQueueMgr keeps track of hot container slotQueues where each slotQueue
// provides for multiple consumers/producers. slotQueue also stores
// a few basic stats in slotStats.
//

type Slot interface {
	exec(ctx context.Context, call *call) error
	Close(ctx context.Context) error
	Error() error
}

// slotQueueMgr manages hot container slotQueues
type slotQueueMgr struct {
	hMu sync.Mutex // protects hot
	hot map[string]*slotQueue
}

// request and container states
type slotQueueStats struct {
	requestStates   [RequestStateMax]uint64
	containerStates [ContainerStateMax]uint64
}

type slotToken struct {
	slot    Slot
	trigger chan struct{}
	id      uint64
	isBusy  uint32
}

// LIFO queue that exposes input/output channels along
// with runner/waiter tracking for agent
type slotQueue struct {
	key       string
	cond      *sync.Cond
	slots     []*slotToken
	nextId    uint64
	signaller chan chan error
	statsLock sync.Mutex // protects stats below
	stats     slotQueueStats
}

func NewSlotQueueMgr() *slotQueueMgr {
	obj := &slotQueueMgr{
		hot: make(map[string]*slotQueue),
	}
	return obj
}

func NewSlotQueue(key string) *slotQueue {
	obj := &slotQueue{
		key:       key,
		cond:      sync.NewCond(new(sync.Mutex)),
		slots:     make([]*slotToken, 0),
		signaller: make(chan chan error, 1),
	}

	return obj
}

func (a *slotQueue) acquireSlot(s *slotToken) bool {
	// let's get the lock
	if !atomic.CompareAndSwapUint32(&s.isBusy, 0, 1) {
		return false
	}

	a.cond.L.Lock()
	// common case: acquired slots are usually at the end
	for i := len(a.slots) - 1; i >= 0; i-- {
		if a.slots[i].id == s.id {
			a.slots = append(a.slots[:i], a.slots[i+1:]...)
			break
		}
	}
	a.cond.L.Unlock()

	// now we have the lock, push the trigger
	close(s.trigger)
	return true
}

func (a *slotQueue) startDequeuer(ctx context.Context) chan *slotToken {

	isWaiting := false
	output := make(chan *slotToken)

	go func() {
		<-ctx.Done()
		a.cond.L.Lock()
		if isWaiting {
			a.cond.Broadcast()
		}
		a.cond.L.Unlock()
	}()

	go func() {
		for {
			a.cond.L.Lock()

			isWaiting = true
			for len(a.slots) <= 0 && (ctx.Err() == nil) {
				a.cond.Wait()
			}
			isWaiting = false

			if ctx.Err() != nil {
				a.cond.L.Unlock()
				return
			}

			item := a.slots[len(a.slots)-1]
			a.cond.L.Unlock()

			select {
			case output <- item: // good case (dequeued)
			case <-item.trigger: // ejected (eject handles cleanup)
			case <-ctx.Done(): // time out or cancel from caller
			}
		}
	}()

	return output
}

func (a *slotQueue) queueSlot(slot Slot) *slotToken {

	token := &slotToken{slot, make(chan struct{}), 0, 0}

	a.cond.L.Lock()
	token.id = a.nextId
	a.slots = append(a.slots, token)
	a.nextId += 1
	a.cond.L.Unlock()

	a.cond.Broadcast()
	return token
}

// isIdle() returns true is there's no activity for this slot queue. This
// means no one is waiting, running or starting.
func (a *slotQueue) isIdle() bool {
	var isIdle bool

	a.statsLock.Lock()

	isIdle = a.stats.requestStates[RequestStateWait] == 0 &&
		a.stats.requestStates[RequestStateExec] == 0 &&
		a.stats.containerStates[ContainerStateWait] == 0 &&
		a.stats.containerStates[ContainerStateStart] == 0 &&
		a.stats.containerStates[ContainerStateIdle] == 0 &&
		a.stats.containerStates[ContainerStateBusy] == 0

	a.statsLock.Unlock()

	return isIdle
}

func (a *slotQueue) getStats() slotQueueStats {
	var out slotQueueStats
	a.statsLock.Lock()
	out = a.stats
	a.statsLock.Unlock()
	return out
}

func isNewContainerNeeded(cur *slotQueueStats) bool {

	idleWorkers := cur.containerStates[ContainerStateIdle]
	starters := cur.containerStates[ContainerStateStart]
	startWaiters := cur.containerStates[ContainerStateWait]

	queuedRequests := cur.requestStates[RequestStateWait]

	// we expect idle containers to immediately pick up
	// any waiters. We assume non-idle containers busy.
	effectiveWaiters := uint64(0)
	if idleWorkers < queuedRequests {
		effectiveWaiters = queuedRequests - idleWorkers
	}

	if effectiveWaiters == 0 {
		return false
	}

	// we expect resource waiters to eventually transition
	// into starters.
	effectiveStarters := starters + startWaiters

	// if containers are starting, do not start more than effective waiters
	if effectiveStarters > 0 && effectiveStarters >= effectiveWaiters {
		return false
	}

	return true
}

func (a *slotQueue) enterRequestState(reqType RequestStateType) {
	if reqType > RequestStateNone && reqType < RequestStateMax {
		a.statsLock.Lock()
		a.stats.requestStates[reqType] += 1
		a.statsLock.Unlock()
	}
}

func (a *slotQueue) exitRequestState(reqType RequestStateType) {
	if reqType > RequestStateNone && reqType < RequestStateMax {
		a.statsLock.Lock()
		a.stats.requestStates[reqType] -= 1
		a.statsLock.Unlock()
	}
}

func (a *slotQueue) enterContainerState(conType ContainerStateType) {
	if conType > ContainerStateNone && conType < ContainerStateMax {
		a.statsLock.Lock()
		a.stats.containerStates[conType] += 1
		a.statsLock.Unlock()
	}
}

func (a *slotQueue) exitContainerState(conType ContainerStateType) {
	if conType > ContainerStateNone && conType < ContainerStateMax {
		a.statsLock.Lock()
		a.stats.containerStates[conType] -= 1
		a.statsLock.Unlock()
	}
}

// getSlot must ensure that if it receives a slot, it will be returned, otherwise
// a container will be locked up forever waiting for slot to free.
func (a *slotQueueMgr) getSlotQueue(key string) (*slotQueue, bool) {

	a.hMu.Lock()
	slots, ok := a.hot[key]
	if !ok {
		slots = NewSlotQueue(key)
		a.hot[key] = slots
	}
	a.hMu.Unlock()

	return slots, !ok
}

// currently unused. But at some point, we need to age/delete old
// slotQueues.
func (a *slotQueueMgr) deleteSlotQueue(slots *slotQueue) bool {
	isDeleted := false

	a.hMu.Lock()
	if slots.isIdle() {
		delete(a.hot, slots.key)
		isDeleted = true
	}
	a.hMu.Unlock()

	return isDeleted
}

// TODO this should be at least SHA-256 or more
var shapool = &sync.Pool{New: func() interface{} { return sha1.New() }}

// TODO do better; once we have app+route versions this function
// can be simply app+route names & version
func getSlotQueueKey(call *call) string {
	// return a sha1 hash of a (hopefully) unique string of all the config
	// values, to make map lookups quicker [than the giant unique string]

	hash := shapool.Get().(hash.Hash)
	hash.Reset()
	defer shapool.Put(hash)

	hash.Write(unsafeBytes(call.AppID))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.SyslogURL))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.Path))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.Image))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.Format))
	hash.Write(unsafeBytes("\x00"))

	// these are all static in size we only need to delimit the whole block of them
	var byt [8]byte
	binary.LittleEndian.PutUint32(byt[:4], uint32(call.Timeout))
	hash.Write(byt[:4])

	binary.LittleEndian.PutUint32(byt[:4], uint32(call.IdleTimeout))
	hash.Write(byt[:4])

	binary.LittleEndian.PutUint32(byt[:4], uint32(call.TmpFsSize))
	hash.Write(byt[:4])

	binary.LittleEndian.PutUint64(byt[:], call.Memory)
	hash.Write(byt[:])

	binary.LittleEndian.PutUint64(byt[:], uint64(call.CPUs))
	hash.Write(byt[:])
	hash.Write(unsafeBytes("\x00"))

	// we have to sort these before printing, yay.
	// TODO if we had a max size for config const we could avoid this!
	keys := make([]string, 0, len(call.Config))
	for k := range call.Config {
		i := sort.SearchStrings(keys, k)
		keys = append(keys, "")
		copy(keys[i+1:], keys[i:])
		keys[i] = k
	}

	for _, k := range keys {
		hash.Write(unsafeBytes(k))
		hash.Write(unsafeBytes("\x00"))
		hash.Write(unsafeBytes(call.Config[k]))
		hash.Write(unsafeBytes("\x00"))
	}

	// we need to additionally delimit config and annotations to eliminate overlap bug
	hash.Write(unsafeBytes("\x00"))

	keys = keys[:0] // clear keys
	for k := range call.Annotations {
		i := sort.SearchStrings(keys, k)
		keys = append(keys, "")
		copy(keys[i+1:], keys[i:])
		keys[i] = k
	}

	for _, k := range keys {
		hash.Write(unsafeBytes(k))
		hash.Write(unsafeBytes("\x00"))
		v, _ := call.Annotations.Get(k)
		hash.Write(v)
		hash.Write(unsafeBytes("\x00"))
	}

	var buf [sha1.Size]byte
	hash.Sum(buf[:0])
	return string(buf[:])
}

// WARN: this is read only
func unsafeBytes(a string) []byte {
	strHeader := (*reflect.StringHeader)(unsafe.Pointer(&a))

	var b []byte
	byteHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	byteHeader.Data = strHeader.Data

	// need to take the length of `a` here to ensure it's alive until after we update b's Data
	// field since the garbage collector can collect a variable once it is no longer used
	// not when it goes out of scope, for more details see https://github.com/golang/go/issues/9046
	l := len(a)
	byteHeader.Len = l
	byteHeader.Cap = l
	return b
}
<commit_msg>Use sha256 for slot token (#1155)<commit_after>package agent

import (
	"context"
	"crypto/sha256"
	"encoding/binary"
	"hash"
	"reflect"
	"sort"
	"sync"
	"sync/atomic"
	"unsafe"
)

//
// slotQueueMgr keeps track of hot container slotQueues where each slotQueue
// provides for multiple consumers/producers. slotQueue also stores
// a few basic stats in slotStats.
//

type Slot interface {
	exec(ctx context.Context, call *call) error
	Close(ctx context.Context) error
	Error() error
}

// slotQueueMgr manages hot container slotQueues
type slotQueueMgr struct {
	hMu sync.Mutex // protects hot
	hot map[string]*slotQueue
}

// request and container states
type slotQueueStats struct {
	requestStates   [RequestStateMax]uint64
	containerStates [ContainerStateMax]uint64
}

type slotToken struct {
	slot    Slot
	trigger chan struct{}
	id      uint64
	isBusy  uint32
}

// LIFO queue that exposes input/output channels along
// with runner/waiter tracking for agent
type slotQueue struct {
	key       string
	cond      *sync.Cond
	slots     []*slotToken
	nextId    uint64
	signaller chan chan error
	statsLock sync.Mutex // protects stats below
	stats     slotQueueStats
}

func NewSlotQueueMgr() *slotQueueMgr {
	obj := &slotQueueMgr{
		hot: make(map[string]*slotQueue),
	}
	return obj
}

func NewSlotQueue(key string) *slotQueue {
	obj := &slotQueue{
		key:       key,
		cond:      sync.NewCond(new(sync.Mutex)),
		slots:     make([]*slotToken, 0),
		signaller: make(chan chan error, 1),
	}

	return obj
}

func (a *slotQueue) acquireSlot(s *slotToken) bool {
	// let's get the lock
	if !atomic.CompareAndSwapUint32(&s.isBusy, 0, 1) {
		return false
	}

	a.cond.L.Lock()
	// common case: acquired slots are usually at the end
	for i := len(a.slots) - 1; i >= 0; i-- {
		if a.slots[i].id == s.id {
			a.slots = append(a.slots[:i], a.slots[i+1:]...)
			break
		}
	}
	a.cond.L.Unlock()

	// now we have the lock, push the trigger
	close(s.trigger)
	return true
}

func (a *slotQueue) startDequeuer(ctx context.Context) chan *slotToken {

	isWaiting := false
	output := make(chan *slotToken)

	go func() {
		<-ctx.Done()
		a.cond.L.Lock()
		if isWaiting {
			a.cond.Broadcast()
		}
		a.cond.L.Unlock()
	}()

	go func() {
		for {
			a.cond.L.Lock()

			isWaiting = true
			for len(a.slots) <= 0 && (ctx.Err() == nil) {
				a.cond.Wait()
			}
			isWaiting = false

			if ctx.Err() != nil {
				a.cond.L.Unlock()
				return
			}

			item := a.slots[len(a.slots)-1]
			a.cond.L.Unlock()

			select {
			case output <- item: // good case (dequeued)
			case <-item.trigger: // ejected (eject handles cleanup)
			case <-ctx.Done(): // time out or cancel from caller
			}
		}
	}()

	return output
}

func (a *slotQueue) queueSlot(slot Slot) *slotToken {

	token := &slotToken{slot, make(chan struct{}), 0, 0}

	a.cond.L.Lock()
	token.id = a.nextId
	a.slots = append(a.slots, token)
	a.nextId += 1
	a.cond.L.Unlock()

	a.cond.Broadcast()
	return token
}

// isIdle() returns true is there's no activity for this slot queue. This
// means no one is waiting, running or starting.
func (a *slotQueue) isIdle() bool {
	var isIdle bool

	a.statsLock.Lock()

	isIdle = a.stats.requestStates[RequestStateWait] == 0 &&
		a.stats.requestStates[RequestStateExec] == 0 &&
		a.stats.containerStates[ContainerStateWait] == 0 &&
		a.stats.containerStates[ContainerStateStart] == 0 &&
		a.stats.containerStates[ContainerStateIdle] == 0 &&
		a.stats.containerStates[ContainerStateBusy] == 0

	a.statsLock.Unlock()

	return isIdle
}

func (a *slotQueue) getStats() slotQueueStats {
	var out slotQueueStats
	a.statsLock.Lock()
	out = a.stats
	a.statsLock.Unlock()
	return out
}

func isNewContainerNeeded(cur *slotQueueStats) bool {

	idleWorkers := cur.containerStates[ContainerStateIdle]
	starters := cur.containerStates[ContainerStateStart]
	startWaiters := cur.containerStates[ContainerStateWait]

	queuedRequests := cur.requestStates[RequestStateWait]

	// we expect idle containers to immediately pick up
	// any waiters. We assume non-idle containers busy.
	effectiveWaiters := uint64(0)
	if idleWorkers < queuedRequests {
		effectiveWaiters = queuedRequests - idleWorkers
	}

	if effectiveWaiters == 0 {
		return false
	}

	// we expect resource waiters to eventually transition
	// into starters.
	effectiveStarters := starters + startWaiters

	// if containers are starting, do not start more than effective waiters
	if effectiveStarters > 0 && effectiveStarters >= effectiveWaiters {
		return false
	}

	return true
}

func (a *slotQueue) enterRequestState(reqType RequestStateType) {
	if reqType > RequestStateNone && reqType < RequestStateMax {
		a.statsLock.Lock()
		a.stats.requestStates[reqType] += 1
		a.statsLock.Unlock()
	}
}

func (a *slotQueue) exitRequestState(reqType RequestStateType) {
	if reqType > RequestStateNone && reqType < RequestStateMax {
		a.statsLock.Lock()
		a.stats.requestStates[reqType] -= 1
		a.statsLock.Unlock()
	}
}

func (a *slotQueue) enterContainerState(conType ContainerStateType) {
	if conType > ContainerStateNone && conType < ContainerStateMax {
		a.statsLock.Lock()
		a.stats.containerStates[conType] += 1
		a.statsLock.Unlock()
	}
}

func (a *slotQueue) exitContainerState(conType ContainerStateType) {
	if conType > ContainerStateNone && conType < ContainerStateMax {
		a.statsLock.Lock()
		a.stats.containerStates[conType] -= 1
		a.statsLock.Unlock()
	}
}

// getSlot must ensure that if it receives a slot, it will be returned, otherwise
// a container will be locked up forever waiting for slot to free.
func (a *slotQueueMgr) getSlotQueue(key string) (*slotQueue, bool) {

	a.hMu.Lock()
	slots, ok := a.hot[key]
	if !ok {
		slots = NewSlotQueue(key)
		a.hot[key] = slots
	}
	a.hMu.Unlock()

	return slots, !ok
}

// currently unused. But at some point, we need to age/delete old
// slotQueues.
func (a *slotQueueMgr) deleteSlotQueue(slots *slotQueue) bool {
	isDeleted := false

	a.hMu.Lock()
	if slots.isIdle() {
		delete(a.hot, slots.key)
		isDeleted = true
	}
	a.hMu.Unlock()

	return isDeleted
}

var shapool = &sync.Pool{New: func() interface{} { return sha256.New() }}

// TODO do better; once we have app+route versions this function
// can be simply app+route names & version
func getSlotQueueKey(call *call) string {
	// return a sha256 hash of a (hopefully) unique string of all the config
	// values, to make map lookups quicker [than the giant unique string]

	hash := shapool.Get().(hash.Hash)
	hash.Reset()
	defer shapool.Put(hash)

	hash.Write(unsafeBytes(call.AppID))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.SyslogURL))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.Path))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.Image))
	hash.Write(unsafeBytes("\x00"))
	hash.Write(unsafeBytes(call.Format))
	hash.Write(unsafeBytes("\x00"))

	// these are all static in size we only need to delimit the whole block of them
	var byt [8]byte
	binary.LittleEndian.PutUint32(byt[:4], uint32(call.Timeout))
	hash.Write(byt[:4])

	binary.LittleEndian.PutUint32(byt[:4], uint32(call.IdleTimeout))
	hash.Write(byt[:4])

	binary.LittleEndian.PutUint32(byt[:4], uint32(call.TmpFsSize))
	hash.Write(byt[:4])

	binary.LittleEndian.PutUint64(byt[:], call.Memory)
	hash.Write(byt[:])

	binary.LittleEndian.PutUint64(byt[:], uint64(call.CPUs))
	hash.Write(byt[:])
	hash.Write(unsafeBytes("\x00"))

	// we have to sort these before printing, yay.
	// TODO if we had a max size for config const we could avoid this!
	keys := make([]string, 0, len(call.Config))
	for k := range call.Config {
		i := sort.SearchStrings(keys, k)
		keys = append(keys, "")
		copy(keys[i+1:], keys[i:])
		keys[i] = k
	}

	for _, k := range keys {
		hash.Write(unsafeBytes(k))
		hash.Write(unsafeBytes("\x00"))
		hash.Write(unsafeBytes(call.Config[k]))
		hash.Write(unsafeBytes("\x00"))
	}

	// we need to additionally delimit config and annotations to eliminate overlap bug
	hash.Write(unsafeBytes("\x00"))

	keys = keys[:0] // clear keys
	for k := range call.Annotations {
		i := sort.SearchStrings(keys, k)
		keys = append(keys, "")
		copy(keys[i+1:], keys[i:])
		keys[i] = k
	}

	for _, k := range keys {
		hash.Write(unsafeBytes(k))
		hash.Write(unsafeBytes("\x00"))
		v, _ := call.Annotations.Get(k)
		hash.Write(v)
		hash.Write(unsafeBytes("\x00"))
	}

	var buf [sha256.Size]byte
	hash.Sum(buf[:0])
	return string(buf[:])
}

// WARN: this is read only
func unsafeBytes(a string) []byte {
	strHeader := (*reflect.StringHeader)(unsafe.Pointer(&a))

	var b []byte
	byteHeader := (*reflect.SliceHeader)(unsafe.Pointer(&b))
	byteHeader.Data = strHeader.Data

	// need to take the length of `a` here to ensure it's alive until after we update b's Data
	// field since the garbage collector can collect a variable once it is no longer used
	// not when it goes out of scope, for more details see https://github.com/golang/go/issues/9046
	l := len(a)
	byteHeader.Len = l
	byteHeader.Cap = l
	return b
}
<|endoftext|>


<commit_before>package is

import (
	"bytes"
	"fmt"
	"reflect"
)

func objectTypeName(o interface{}) string {
	return fmt.Sprintf("%T", o)
}

func objectTypeNames(o []interface{}) string {
	if o == nil {
		return objectTypeName(o)
	}
	if len(o) == 1 {
		return objectTypeName(o[0])
	}
	var b bytes.Buffer
	b.WriteString(objectTypeName(o[0]))
	for _, e := range o[1:] {
		b.WriteString(",")
		b.WriteString(objectTypeName(e))
	}
	return b.String()
}

func isNil(o interface{}) bool {
	if o == nil {
		return true
	}
	value := reflect.ValueOf(o)
	kind := value.Kind()
	if kind >= reflect.Chan &&
		kind <= reflect.Slice &&
		value.IsNil() {
		return true
	}
	return false
}

func isZero(o interface{}) bool {
	if o == nil {
		return true
	}
	v := reflect.ValueOf(o)
	switch v.Kind() {
	case reflect.Ptr:
		return reflect.DeepEqual(o,
			reflect.New(v.Type().Elem()).Interface())
	case reflect.Slice, reflect.Array, reflect.Map, reflect.Chan:
		if v.Len() == 0 {
			return true
		}
		return false
	default:
		return reflect.DeepEqual(o,
			reflect.Zero(v.Type()).Interface())
	}
}

func isEqual(a interface{}, b interface{}) bool {
	if isNil(a) || isNil(b) {
		if isNil(a) && !isNil(b) {
			return false
		}
		if !isNil(a) && isNil(b) {
			return false
		}
		return a == b
	}
	if reflect.DeepEqual(a, b) {
		return true
	}
	aValue := reflect.ValueOf(a)
	bValue := reflect.ValueOf(b)

	// Convert types and compare
	if bValue.Type().ConvertibleTo(aValue.Type()) {
		return reflect.DeepEqual(a, bValue.Convert(aValue.Type()).Interface())
	}

	return false
}

// fail is a function variable that is called by test functions when they
// fail. It is overridden in test code for this package.
var fail = failDefault

// failDefault is the default failure function.
func failDefault(is *Is, format string, args ...interface{}) {
	is.TB.Helper()

	failFmt := ""
	if len(is.failFormat) != 0 {
		failFmt = fmt.Sprintf("%s - %s", format, is.failFormat)
	}
	args = append(args, is.failArgs...)
	if is.strict {
		is.TB.Fatalf(failFmt, args...)
	} else {
		is.TB.Errorf(failFmt, args...)
	}
}
<commit_msg>Fix bad failformat<commit_after>package is

import (
	"bytes"
	"fmt"
	"reflect"
)

func objectTypeName(o interface{}) string {
	return fmt.Sprintf("%T", o)
}

func objectTypeNames(o []interface{}) string {
	if o == nil {
		return objectTypeName(o)
	}
	if len(o) == 1 {
		return objectTypeName(o[0])
	}
	var b bytes.Buffer
	b.WriteString(objectTypeName(o[0]))
	for _, e := range o[1:] {
		b.WriteString(",")
		b.WriteString(objectTypeName(e))
	}
	return b.String()
}

func isNil(o interface{}) bool {
	if o == nil {
		return true
	}
	value := reflect.ValueOf(o)
	kind := value.Kind()
	if kind >= reflect.Chan &&
		kind <= reflect.Slice &&
		value.IsNil() {
		return true
	}
	return false
}

func isZero(o interface{}) bool {
	if o == nil {
		return true
	}
	v := reflect.ValueOf(o)
	switch v.Kind() {
	case reflect.Ptr:
		return reflect.DeepEqual(o,
			reflect.New(v.Type().Elem()).Interface())
	case reflect.Slice, reflect.Array, reflect.Map, reflect.Chan:
		if v.Len() == 0 {
			return true
		}
		return false
	default:
		return reflect.DeepEqual(o,
			reflect.Zero(v.Type()).Interface())
	}
}

func isEqual(a interface{}, b interface{}) bool {
	if isNil(a) || isNil(b) {
		if isNil(a) && !isNil(b) {
			return false
		}
		if !isNil(a) && isNil(b) {
			return false
		}
		return a == b
	}
	if reflect.DeepEqual(a, b) {
		return true
	}
	aValue := reflect.ValueOf(a)
	bValue := reflect.ValueOf(b)

	// Convert types and compare
	if bValue.Type().ConvertibleTo(aValue.Type()) {
		return reflect.DeepEqual(a, bValue.Convert(aValue.Type()).Interface())
	}

	return false
}

// fail is a function variable that is called by test functions when they
// fail. It is overridden in test code for this package.
var fail = failDefault

// failDefault is the default failure function.
func failDefault(is *Is, format string, args ...interface{}) {
	is.TB.Helper()

	failFmt := format
	if len(is.failFormat) != 0 {
		failFmt = fmt.Sprintf("%s - %s", format, is.failFormat)
		args = append(args, is.failArgs...)
	}
	if is.strict {
		is.TB.Fatalf(failFmt, args...)
	} else {
		is.TB.Errorf(failFmt, args...)
	}
}
<|endoftext|>


<commit_before>package gomapper

import (
	"reflect"
)

// Options is a configuration container to pass to the
// new instance methods for GoMap
type Options struct {
	Maps         []Mapping
	IgnoreFields []string
	IgnoreNil    bool
}

// GoMap holds all configuration for any mappings
// registered at the startup
type GoMap struct {
	maps         []Mapping
	ignoreFields []string
}

// Mapping between two different structs
type Mapping struct {
	Source      interface{}
	Destination interface{}
	FieldLinks  map[string]string
}

// New returns a new gomapme Confguration struct
func New(options Options) *GoMap {
	return &GoMap{
		maps:         options.Maps,
		ignoreFields: options.IgnoreFields,
	}
}

// NewDefault returns a plain GoMap func with default configuration
func NewDefault() *GoMap {
	gomap := GoMap{
		maps:         make([]Mapping, 0),
		ignoreFields: make([]string, 0),
	}
	return &gomap
}

// Map transforms the input struct to the output struct. Always pass the
// destination by reference and the source by value
func (g *GoMap) Map(s interface{}, d interface{}) {
	dstPtrVal := reflect.ValueOf(d)
	dstPtrType := dstPtrVal.Type()
	dstType := dstPtrType.Elem()
	dstVal := reflect.Indirect(dstPtrVal)
	srcVal := reflect.ValueOf(s)

	checkIgnore := len(g.ignoreFields) > 0

	// loop the desintation VM fields
	for i := 0; i < dstType.NumField(); i++ {

		ft := dstType.Field(i)
		sv := srcVal.FieldByName(ft.Name)

		if checkIgnore && ignoreMatch(g.ignoreFields, ft.Name) {
			continue
		}

		if sv.IsValid() {
			fv := dstVal.FieldByName(ft.Name)
			fv.Set(sv)
		}
	}
}

func ignoreMatch(ignores []string, field string) bool {
	for i := range ignores {
		if ignores[i] == field {
			return true
		}
	}
	return false
}

// Add applys a new mapping between two structs to the
// global configuration
func (g *GoMap) Add(m Mapping) {
	g.maps = append(g.maps, m)
}
<commit_msg>Attach the ignore check to the GoMap receiver<commit_after>package gomapper

import (
	"reflect"
)

// Options is a configuration container to pass to the
// new instance methods for GoMap
type Options struct {
	Overrides    []Mapping
	IgnoreFields []string
	IgnoreNil    bool
}

// GoMap holds all configuration for any mappings
// registered at the startup
type GoMap struct {
	overrides    []Mapping
	ignoreFields []string
}

// Mapping between two different structs
type Mapping struct {
	Source      interface{}
	Destination interface{}
	FieldLinks  map[string]string
}

// New returns a new gomapme Confguration struct
func New(options Options) *GoMap {
	return &GoMap{
		overrides:    options.Overrides,
		ignoreFields: options.IgnoreFields,
	}
}

// NewDefault returns a plain GoMap func with default configuration
func NewDefault() *GoMap {
	gomap := GoMap{
		overrides:    make([]Mapping, 0),
		ignoreFields: make([]string, 0),
	}
	return &gomap
}

// Map transforms the input struct to the output struct. Always pass the
// destination by reference and the source by value
func (g *GoMap) Map(s interface{}, d interface{}) {
	dstPtrVal := reflect.ValueOf(d)
	dstPtrType := dstPtrVal.Type()
	dstType := dstPtrType.Elem()
	dstVal := reflect.Indirect(dstPtrVal)
	srcVal := reflect.ValueOf(s)

	checkIgnore := len(g.ignoreFields) > 0

	// loop the desintation VM fields
	for i := 0; i < dstType.NumField(); i++ {

		ft := dstType.Field(i)
		if checkIgnore && g.ignoreField(ft.Name) {
			continue
		}

		//try find a mapping override match

		sv := srcVal.FieldByName(ft.Name)

		if sv.IsValid() {
			fv := dstVal.FieldByName(ft.Name)
			//add logic here to cast
			fv.Set(sv)
		}
	}
}

func (g *GoMap) ignoreField(field string) bool {
	for i := range g.ignoreFields {
		if g.ignoreFields[i] == field {
			return true
		}
	}
	return false
}

// Add applys a new mapping between two structs to the
// global configuration
func (g *GoMap) Add(m Mapping) {
	g.overrides = append(g.overrides, m)
}
<|endoftext|>


<commit_before>package adminifier

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/cooper/quiki/authenticator"
	"github.com/cooper/quiki/webserver"
	"github.com/cooper/quiki/wiki"
	"github.com/cooper/quiki/wikifier"
	"github.com/pkg/errors"
)

var javascriptTemplates string

var frameHandlers = map[string]func(*wikiRequest){
	"dashboard":     handleDashboardFrame,
	"pages":         handlePagesFrame,
	"categories":    handleCategoriesFrame,
	"images":        handleImagesFrame,
	"models":        handleModelsFrame,
	"settings":      handleSettingsFrame,
	"edit-page":     handleEditPageFrame,
	"edit-model":    handleEditModelFrame,
	"switch-branch": handleSwitchBranchFrame,
}

var wikiFuncHandlers = map[string]func(*wikiRequest){
	"switch-branch/": handleSwitchBranch,
	"create-branch":  handleCreateBranch,
	"write-page":     handleWritePage,
}

// wikiTemplate members are available to all wiki templates
type wikiTemplate struct {
	User              *authenticator.User // user
	ServerPanelAccess bool                // whether user can access main panel
	Shortcode         string              // wiki shortcode
	WikiTitle         string              // wiki title
	Branch            string              // selected branch
	Static            string              // static root
	AdminRoot         string              // adminifier root
	Root              string              // wiki root
}

type wikiRequest struct {
	shortcode string
	wikiRoot  string
	wi        *webserver.WikiInfo
	w         http.ResponseWriter
	r         *http.Request
	tmplName  string
	dot       interface{}
	err       error
}

type editorOpts struct {
	model, config bool
	info          wikifier.PageInfo
}

// TODO: verify session on ALL wiki handlers

func setupWikiHandlers(shortcode string, wi *webserver.WikiInfo) {

	// each of these URLs generates wiki.tpl
	for _, which := range []string{
		"dashboard", "pages", "categories",
		"images", "models", "settings", "help",
		"edit-page", "edit-model", "switch-branch",
	} {
		mux.HandleFunc(host+root+shortcode+"/"+which, func(w http.ResponseWriter, r *http.Request) {
			handleWiki(shortcode, wi, w, r)
		})
	}

	// frames to load via ajax
	frameRoot := root + shortcode + "/frame/"
	mux.HandleFunc(host+frameRoot, func(w http.ResponseWriter, r *http.Request) {

		// check logged in
		if !sessMgr.GetBool(r.Context(), "loggedIn") {
			http.Redirect(w, r, root+"login", http.StatusTemporaryRedirect)
			return
		}

		frameName := strings.TrimPrefix(r.URL.Path, frameRoot)
		tmplName := "frame-" + frameName + ".tpl"

		// call func to create template params
		var dot interface{} = nil
		if handler, exist := frameHandlers[frameName]; exist {

			// create wiki request
			wr := &wikiRequest{
				shortcode: shortcode,
				wikiRoot:  root + shortcode,
				w:         w,
				r:         r,
			}
			dot = wr

			// possibly switch wikis
			switchUserWiki(wr, wi)
			if wr.err != nil {
				panic(wr.err)
			}

			// call handler
			handler(wr)

			// handler returned an error
			if wr.err != nil {
				panic(wr.err)
			}

			// handler was successful
			if wr.dot != nil {
				dot = wr.dot
			}
			if wr.tmplName != "" {
				tmplName = wr.tmplName
			}
		}

		// frame template does not exist
		if exist := tmpl.Lookup(tmplName); exist == nil {
			http.NotFound(w, r)
			return
		}

		// execute frame template with dot
		err := tmpl.ExecuteTemplate(w, tmplName, dot)

		// error occurred in template execution
		if err != nil {
			panic(err)
		}
	})

	// functions
	funcRoot := root + shortcode + "/func/"
	for funcName, thisHandler := range wikiFuncHandlers {
		handler := thisHandler
		mux.HandleFunc(host+funcRoot+funcName, func(w http.ResponseWriter, r *http.Request) {

			// check logged in
			//
			// TODO: everything in func/ will be JSON,
			// so return a "not logged in" error to present login popup
			// rather than redirecting
			//
			if !sessMgr.GetBool(r.Context(), "loggedIn") {
				http.Redirect(w, r, root+"login", http.StatusTemporaryRedirect)
				return
			}

			// create wiki request
			wr := &wikiRequest{
				shortcode: shortcode,
				wikiRoot:  root + shortcode,
				w:         w,
				r:         r,
			}

			// possibly switch wikis
			switchUserWiki(wr, wi)
			if wr.err != nil {
				panic(wr.err)
			}

			// call handler
			handler(wr)

			// handler returned an error
			if wr.err != nil {
				panic(wr.err)
			}
		})
	}
}

func handleWiki(shortcode string, wi *webserver.WikiInfo, w http.ResponseWriter, r *http.Request) {

	// check logged in
	if !sessMgr.GetBool(r.Context(), "loggedIn") {
		http.Redirect(w, r, root+"login", http.StatusTemporaryRedirect)
		return
	}

	// load javascript templates
	if javascriptTemplates == "" {
		files, _ := filepath.Glob(dirAdminifier + "/template/js-tmpl/*.tpl")
		for _, fileName := range files {
			data, _ := ioutil.ReadFile(fileName)
			javascriptTemplates += string(data)
		}
	}

	err := tmpl.ExecuteTemplate(w, "wiki.tpl", struct {
		JSTemplates template.HTML
		wikiTemplate
	}{
		template.HTML(javascriptTemplates),
		getGenericTemplate(&wikiRequest{
			shortcode: shortcode,
			wi:        wi,
			w:         w,
			r:         r,
		}),
	})
	if err != nil {
		panic(err)
	}
}

func handleDashboardFrame(wr *wikiRequest) {
}

var pageSorters map[string]wiki.SortFunc = map[string]wiki.SortFunc{
	"t": wiki.SortTitle,
	"a": wiki.SortAuthor,
	"c": wiki.SortCreated,
	"m": wiki.SortModified,
}

func handlePagesFrame(wr *wikiRequest) {

	// find sort
	descending := true
	sortFunc := wiki.SortModified
	s := wr.r.URL.Query().Get("sort")
	if len(s) != 0 {
		sortFunc = pageSorters[string(s[0])]
		descending = len(s) > 1 && s[1] == '-'
	}

	// sort
	pages := wr.wi.PagesSorted(descending, sortFunc, wiki.SortTitle)

	handleFileFrames(wr, pages)
}

func handleImagesFrame(wr *wikiRequest) {
	handleFileFrames(wr, wr.wi.Images(), "d")
}

func handleModelsFrame(wr *wikiRequest) {
	handleFileFrames(wr, wr.wi.Models())
}

func handleCategoriesFrame(wr *wikiRequest) {
	handleFileFrames(wr, wr.wi.Categories())
}

func handleFileFrames(wr *wikiRequest, results interface{}, extras ...string) {

	// json stuffs
	res, err := json.Marshal(map[string]interface{}{
		"sort_types": append([]string{"t", "a", "c", "m"}, extras...),
		"results":    results,
	})
	if err != nil {
		wr.err = err
		return
	}

	// determine sort
	// consider: should we validate sort here also
	s := wr.r.URL.Query().Get("sort")
	if s == "" {
		s = "m-"
	}

	wr.dot = struct {
		JSON  template.HTML
		Order string
		wikiTemplate
	}{
		JSON:         template.HTML("<!--JSON\n" + string(res) + "\n-->"),
		Order:        s,
		wikiTemplate: getGenericTemplate(wr),
	}
}

func handleSettingsFrame(wr *wikiRequest) {
	// serve editor for the config file
	handleEditor(wr, wr.wi.ConfigFile, "wiki.conf", "Configuration file", editorOpts{config: true})
}

func handleEditPageFrame(wr *wikiRequest) {
	q := wr.r.URL.Query()

	// no page filename provided
	name := q.Get("page")
	if name == "" {
		wr.err = errors.New("no page filename provided")
		return
	}

	// find the page. if File is empty, it doesn't exist
	info := wr.wi.PageInfo(name)
	if info.File == "" {
		wr.err = errors.New("page does not exist")
		return
	}

	// serve editor
	handleEditor(wr, info.Path, info.File, info.Title, editorOpts{info: info})
}

func handleEditModelFrame(wr *wikiRequest) {
	q := wr.r.URL.Query()

	// no page filename provided
	name := q.Get("page")
	if name == "" {
		wr.err = errors.New("no model filename provided")
		return
	}

	// find the model. if File is empty, it doesn't exist
	info := wr.wi.ModelInfo(name)
	if info.File == "" {
		wr.err = errors.New("model does not exist")
		return
	}

	// serve editor
	handleEditor(wr, info.Path, info.File, info.File, editorOpts{model: true})
}

func handleEditor(wr *wikiRequest, path, file, title string, o editorOpts) {
	wr.tmplName = "frame-editor.tpl"

	// call DisplayFile to get the content
	var fileRes wiki.DisplayFile
	switch r := wr.wi.DisplayFile(path).(type) {
	case wiki.DisplayFile:
		fileRes = r
	case wiki.DisplayError:
		wr.err = errors.New(r.DetailedError)
		return
	default:
		wr.err = errors.New("unknown error occurred in DisplayFile")
		return
	}

	// json stuff
	jsonData, err := json.Marshal(struct {
		Model  bool              `json:"model"`
		Config bool              `json:"config"`
		Info   wikifier.PageInfo `json:"info,omitempty"`
		wiki.DisplayFile
	}{
		Model:       o.model,
		Config:      o.config,
		Info:        o.info,
		DisplayFile: fileRes,
	})
	if err != nil {
		wr.err = err
		return
	}

	// template stuff
	wr.dot = struct {
		Found   bool
		JSON    template.HTML
		Model   bool   // true if editing a model
		Config  bool   // true if editing config
		Title   string // page title or filename
		File    string // filename
		Content string // file content
		wikiTemplate
	}{
		Found:        true,
		JSON:         template.HTML("<!--JSON\n" + string(jsonData) + "\n-->"),
		Model:        o.model,
		Config:       o.config,
		Title:        title,
		File:         file,
		Content:      fileRes.Content,
		wikiTemplate: getGenericTemplate(wr),
	}
}

func handleSwitchBranchFrame(wr *wikiRequest) {
	branches, err := wr.wi.BranchNames()
	if err != nil {
		wr.err = err
		return
	}
	wr.dot = struct {
		Branches []string
		wikiTemplate
	}{
		Branches:     branches,
		wikiTemplate: getGenericTemplate(wr),
	}
}

func handleSwitchBranch(wr *wikiRequest) {
	branchName := strings.TrimPrefix(wr.r.URL.Path, wr.wikiRoot+"/func/switch-branch/")
	if branchName == "" {
		wr.err = errors.New("no branch selected")
		return
	}

	// bad branch name
	if !wiki.ValidBranchName(branchName) {
		wr.err = errors.New("invalid branch name: " + branchName)
		return
	}

	// fetch the branch
	_, wr.err = wr.wi.Branch(branchName)
	if wr.err != nil {
		return
	}

	// set branch
	sessMgr.Put(wr.r.Context(), "branch", branchName)

	// TODO: when this request is submitted by JS, the UI can just reload
	// the current frame so the user stays on the same page, just in new branch

	// redirect back to dashboard
	http.Redirect(wr.w, wr.r, wr.wikiRoot+"/dashboard", http.StatusTemporaryRedirect)
}

func handleCreateBranch(wr *wikiRequest) {

	// TODO: need a different version of parsePost that returns JSON errors
	if !parsePost(wr.w, wr.r, "branch") {
		return
	}

	// bad branch name
	branchName := wr.r.Form.Get("branch")
	if !wiki.ValidBranchName(branchName) {
		wr.err = errors.New("invalid branch name: " + branchName)
		return
	}

	// create or switch branches
	_, err := wr.wi.NewBranch(branchName)
	if err != nil {
		wr.err = err
		return
	}
	sessMgr.Put(wr.r.Context(), "branch", branchName)

	// redirect back to dashboard
	http.Redirect(wr.w, wr.r, wr.wikiRoot+"/dashboard", http.StatusTemporaryRedirect)
}

func handleWritePage(wr *wikiRequest) {
	if !parsePost(wr.w, wr.r, "page", "content") {
		return
	}

	// TODO: double check the path is OK
	pageName, content, message := wr.r.Form.Get("page"), wr.r.Form.Get("content"), wr.r.Form.Get("message")

	// write the file & commit
	if err := wr.wi.WriteFile(filepath.Join("pages", pageName), []byte(content), true, getCommitOpts(wr, message)); err != nil {
		wr.err = err
		return
	}
}

// possibly switch wiki branches
func switchUserWiki(wr *wikiRequest, wi *webserver.WikiInfo) {
	userWiki := wi
	branchName := sessMgr.GetString(wr.r.Context(), "branch")
	if branchName != "" {
		branchWiki, err := wi.Branch(branchName)
		if err != nil {
			wr.err = err
			return
		}
		userWiki = wi.Copy(branchWiki)
	}
	wr.wi = userWiki
}

func getGenericTemplate(wr *wikiRequest) wikiTemplate {
	return wikiTemplate{
		User:              sessMgr.Get(wr.r.Context(), "user").(*authenticator.User),
		ServerPanelAccess: true, // TODO
		Branch:            sessMgr.GetString(wr.r.Context(), "branch"),
		Shortcode:         wr.shortcode,
		WikiTitle:         wr.wi.Title,
		AdminRoot:         strings.TrimRight(root, "/"),
		Static:            root + "static",
		Root:              root + wr.shortcode,
	}
}

func getCommitOpts(wr *wikiRequest, comment string) wiki.CommitOpts {
	user := sessMgr.Get(wr.r.Context(), "user").(*authenticator.User)
	return wiki.CommitOpts{
		Comment: comment,
		Name:    user.DisplayName,
		Email:   user.Email,
	}
}
<commit_msg>sorting in adminifier UI :smile:<commit_after>package adminifier

import (
	"encoding/json"
	"html/template"
	"io/ioutil"
	"net/http"
	"path/filepath"
	"strings"

	"github.com/cooper/quiki/authenticator"
	"github.com/cooper/quiki/webserver"
	"github.com/cooper/quiki/wiki"
	"github.com/cooper/quiki/wikifier"
	"github.com/pkg/errors"
)

var javascriptTemplates string

var frameHandlers = map[string]func(*wikiRequest){
	"dashboard":     handleDashboardFrame,
	"pages":         handlePagesFrame,
	"categories":    handleCategoriesFrame,
	"images":        handleImagesFrame,
	"models":        handleModelsFrame,
	"settings":      handleSettingsFrame,
	"edit-page":     handleEditPageFrame,
	"edit-model":    handleEditModelFrame,
	"switch-branch": handleSwitchBranchFrame,
}

var wikiFuncHandlers = map[string]func(*wikiRequest){
	"switch-branch/": handleSwitchBranch,
	"create-branch":  handleCreateBranch,
	"write-page":     handleWritePage,
}

// wikiTemplate members are available to all wiki templates
type wikiTemplate struct {
	User              *authenticator.User // user
	ServerPanelAccess bool                // whether user can access main panel
	Shortcode         string              // wiki shortcode
	WikiTitle         string              // wiki title
	Branch            string              // selected branch
	Static            string              // static root
	AdminRoot         string              // adminifier root
	Root              string              // wiki root
}

type wikiRequest struct {
	shortcode string
	wikiRoot  string
	wi        *webserver.WikiInfo
	w         http.ResponseWriter
	r         *http.Request
	tmplName  string
	dot       interface{}
	err       error
}

type editorOpts struct {
	model, config bool
	info          wikifier.PageInfo
}

// TODO: verify session on ALL wiki handlers

func setupWikiHandlers(shortcode string, wi *webserver.WikiInfo) {

	// each of these URLs generates wiki.tpl
	for _, which := range []string{
		"dashboard", "pages", "categories",
		"images", "models", "settings", "help",
		"edit-page", "edit-model", "switch-branch",
	} {
		mux.HandleFunc(host+root+shortcode+"/"+which, func(w http.ResponseWriter, r *http.Request) {
			handleWiki(shortcode, wi, w, r)
		})
	}

	// frames to load via ajax
	frameRoot := root + shortcode + "/frame/"
	mux.HandleFunc(host+frameRoot, func(w http.ResponseWriter, r *http.Request) {

		// check logged in
		if !sessMgr.GetBool(r.Context(), "loggedIn") {
			http.Redirect(w, r, root+"login", http.StatusTemporaryRedirect)
			return
		}

		frameName := strings.TrimPrefix(r.URL.Path, frameRoot)
		tmplName := "frame-" + frameName + ".tpl"

		// call func to create template params
		var dot interface{} = nil
		if handler, exist := frameHandlers[frameName]; exist {

			// create wiki request
			wr := &wikiRequest{
				shortcode: shortcode,
				wikiRoot:  root + shortcode,
				w:         w,
				r:         r,
			}
			dot = wr

			// possibly switch wikis
			switchUserWiki(wr, wi)
			if wr.err != nil {
				panic(wr.err)
			}

			// call handler
			handler(wr)

			// handler returned an error
			if wr.err != nil {
				panic(wr.err)
			}

			// handler was successful
			if wr.dot != nil {
				dot = wr.dot
			}
			if wr.tmplName != "" {
				tmplName = wr.tmplName
			}
		}

		// frame template does not exist
		if exist := tmpl.Lookup(tmplName); exist == nil {
			http.NotFound(w, r)
			return
		}

		// execute frame template with dot
		err := tmpl.ExecuteTemplate(w, tmplName, dot)

		// error occurred in template execution
		if err != nil {
			panic(err)
		}
	})

	// functions
	funcRoot := root + shortcode + "/func/"
	for funcName, thisHandler := range wikiFuncHandlers {
		handler := thisHandler
		mux.HandleFunc(host+funcRoot+funcName, func(w http.ResponseWriter, r *http.Request) {

			// check logged in
			//
			// TODO: everything in func/ will be JSON,
			// so return a "not logged in" error to present login popup
			// rather than redirecting
			//
			if !sessMgr.GetBool(r.Context(), "loggedIn") {
				http.Redirect(w, r, root+"login", http.StatusTemporaryRedirect)
				return
			}

			// create wiki request
			wr := &wikiRequest{
				shortcode: shortcode,
				wikiRoot:  root + shortcode,
				w:         w,
				r:         r,
			}

			// possibly switch wikis
			switchUserWiki(wr, wi)
			if wr.err != nil {
				panic(wr.err)
			}

			// call handler
			handler(wr)

			// handler returned an error
			if wr.err != nil {
				panic(wr.err)
			}
		})
	}
}

func handleWiki(shortcode string, wi *webserver.WikiInfo, w http.ResponseWriter, r *http.Request) {

	// check logged in
	if !sessMgr.GetBool(r.Context(), "loggedIn") {
		http.Redirect(w, r, root+"login", http.StatusTemporaryRedirect)
		return
	}

	// load javascript templates
	if javascriptTemplates == "" {
		files, _ := filepath.Glob(dirAdminifier + "/template/js-tmpl/*.tpl")
		for _, fileName := range files {
			data, _ := ioutil.ReadFile(fileName)
			javascriptTemplates += string(data)
		}
	}

	err := tmpl.ExecuteTemplate(w, "wiki.tpl", struct {
		JSTemplates template.HTML
		wikiTemplate
	}{
		template.HTML(javascriptTemplates),
		getGenericTemplate(&wikiRequest{
			shortcode: shortcode,
			wi:        wi,
			w:         w,
			r:         r,
		}),
	})
	if err != nil {
		panic(err)
	}
}

func handleDashboardFrame(wr *wikiRequest) {
}

var sorters map[string]wiki.SortFunc = map[string]wiki.SortFunc{
	"t": wiki.SortTitle,
	"a": wiki.SortAuthor,
	"c": wiki.SortCreated,
	"m": wiki.SortModified,
}

// find sort from query
func getSortFunc(wr *wikiRequest) (bool, wiki.SortFunc) {
	descending := true
	sortFunc := wiki.SortModified
	s := wr.r.URL.Query().Get("sort")
	if len(s) != 0 {
		sortFunc = sorters[string(s[0])]
		descending = len(s) > 1 && s[1] == '-'
	}

	return descending, sortFunc
}

func handlePagesFrame(wr *wikiRequest) {
	descending, sortFunc := getSortFunc(wr)
	pages := wr.wi.PagesSorted(descending, sortFunc, wiki.SortTitle)
	handleFileFrames(wr, pages)
}

func handleImagesFrame(wr *wikiRequest) {
	descending, sortFunc := getSortFunc(wr)
	images := wr.wi.ImagesSorted(descending, sortFunc, wiki.SortTitle)
	handleFileFrames(wr, images, "d")
}

func handleModelsFrame(wr *wikiRequest) {
	descending, sortFunc := getSortFunc(wr)
	models := wr.wi.ModelsSorted(descending, sortFunc, wiki.SortTitle)
	handleFileFrames(wr, models)
}

func handleCategoriesFrame(wr *wikiRequest) {
	descending, sortFunc := getSortFunc(wr)
	cats := wr.wi.CategoriesSorted(descending, sortFunc, wiki.SortTitle)
	handleFileFrames(wr, cats)
}

func handleFileFrames(wr *wikiRequest, results interface{}, extras ...string) {

	// json stuffs
	res, err := json.Marshal(map[string]interface{}{
		"sort_types": append([]string{"t", "a", "c", "m"}, extras...),
		"results":    results,
	})
	if err != nil {
		wr.err = err
		return
	}

	// determine sort
	// consider: should we validate sort here also
	s := wr.r.URL.Query().Get("sort")
	if s == "" {
		s = "m-"
	}

	wr.dot = struct {
		JSON  template.HTML
		Order string
		wikiTemplate
	}{
		JSON:         template.HTML("<!--JSON\n" + string(res) + "\n-->"),
		Order:        s,
		wikiTemplate: getGenericTemplate(wr),
	}
}

func handleSettingsFrame(wr *wikiRequest) {
	// serve editor for the config file
	handleEditor(wr, wr.wi.ConfigFile, "wiki.conf", "Configuration file", editorOpts{config: true})
}

func handleEditPageFrame(wr *wikiRequest) {
	q := wr.r.URL.Query()

	// no page filename provided
	name := q.Get("page")
	if name == "" {
		wr.err = errors.New("no page filename provided")
		return
	}

	// find the page. if File is empty, it doesn't exist
	info := wr.wi.PageInfo(name)
	if info.File == "" {
		wr.err = errors.New("page does not exist")
		return
	}

	// serve editor
	handleEditor(wr, info.Path, info.File, info.Title, editorOpts{info: info})
}

func handleEditModelFrame(wr *wikiRequest) {
	q := wr.r.URL.Query()

	// no page filename provided
	name := q.Get("page")
	if name == "" {
		wr.err = errors.New("no model filename provided")
		return
	}

	// find the model. if File is empty, it doesn't exist
	info := wr.wi.ModelInfo(name)
	if info.File == "" {
		wr.err = errors.New("model does not exist")
		return
	}

	// serve editor
	handleEditor(wr, info.Path, info.File, info.File, editorOpts{model: true})
}

func handleEditor(wr *wikiRequest, path, file, title string, o editorOpts) {
	wr.tmplName = "frame-editor.tpl"

	// call DisplayFile to get the content
	var fileRes wiki.DisplayFile
	switch r := wr.wi.DisplayFile(path).(type) {
	case wiki.DisplayFile:
		fileRes = r
	case wiki.DisplayError:
		wr.err = errors.New(r.DetailedError)
		return
	default:
		wr.err = errors.New("unknown error occurred in DisplayFile")
		return
	}

	// json stuff
	jsonData, err := json.Marshal(struct {
		Model  bool              `json:"model"`
		Config bool              `json:"config"`
		Info   wikifier.PageInfo `json:"info,omitempty"`
		wiki.DisplayFile
	}{
		Model:       o.model,
		Config:      o.config,
		Info:        o.info,
		DisplayFile: fileRes,
	})
	if err != nil {
		wr.err = err
		return
	}

	// template stuff
	wr.dot = struct {
		Found   bool
		JSON    template.HTML
		Model   bool   // true if editing a model
		Config  bool   // true if editing config
		Title   string // page title or filename
		File    string // filename
		Content string // file content
		wikiTemplate
	}{
		Found:        true,
		JSON:         template.HTML("<!--JSON\n" + string(jsonData) + "\n-->"),
		Model:        o.model,
		Config:       o.config,
		Title:        title,
		File:         file,
		Content:      fileRes.Content,
		wikiTemplate: getGenericTemplate(wr),
	}
}

func handleSwitchBranchFrame(wr *wikiRequest) {
	branches, err := wr.wi.BranchNames()
	if err != nil {
		wr.err = err
		return
	}
	wr.dot = struct {
		Branches []string
		wikiTemplate
	}{
		Branches:     branches,
		wikiTemplate: getGenericTemplate(wr),
	}
}

func handleSwitchBranch(wr *wikiRequest) {
	branchName := strings.TrimPrefix(wr.r.URL.Path, wr.wikiRoot+"/func/switch-branch/")
	if branchName == "" {
		wr.err = errors.New("no branch selected")
		return
	}

	// bad branch name
	if !wiki.ValidBranchName(branchName) {
		wr.err = errors.New("invalid branch name: " + branchName)
		return
	}

	// fetch the branch
	_, wr.err = wr.wi.Branch(branchName)
	if wr.err != nil {
		return
	}

	// set branch
	sessMgr.Put(wr.r.Context(), "branch", branchName)

	// TODO: when this request is submitted by JS, the UI can just reload
	// the current frame so the user stays on the same page, just in new branch

	// redirect back to dashboard
	http.Redirect(wr.w, wr.r, wr.wikiRoot+"/dashboard", http.StatusTemporaryRedirect)
}

func handleCreateBranch(wr *wikiRequest) {

	// TODO: need a different version of parsePost that returns JSON errors
	if !parsePost(wr.w, wr.r, "branch") {
		return
	}

	// bad branch name
	branchName := wr.r.Form.Get("branch")
	if !wiki.ValidBranchName(branchName) {
		wr.err = errors.New("invalid branch name: " + branchName)
		return
	}

	// create or switch branches
	_, err := wr.wi.NewBranch(branchName)
	if err != nil {
		wr.err = err
		return
	}
	sessMgr.Put(wr.r.Context(), "branch", branchName)

	// redirect back to dashboard
	http.Redirect(wr.w, wr.r, wr.wikiRoot+"/dashboard", http.StatusTemporaryRedirect)
}

func handleWritePage(wr *wikiRequest) {
	if !parsePost(wr.w, wr.r, "page", "content") {
		return
	}

	// TODO: double check the path is OK
	pageName, content, message := wr.r.Form.Get("page"), wr.r.Form.Get("content"), wr.r.Form.Get("message")

	// write the file & commit
	if err := wr.wi.WriteFile(filepath.Join("pages", pageName), []byte(content), true, getCommitOpts(wr, message)); err != nil {
		wr.err = err
		return
	}
}

// possibly switch wiki branches
func switchUserWiki(wr *wikiRequest, wi *webserver.WikiInfo) {
	userWiki := wi
	branchName := sessMgr.GetString(wr.r.Context(), "branch")
	if branchName != "" {
		branchWiki, err := wi.Branch(branchName)
		if err != nil {
			wr.err = err
			return
		}
		userWiki = wi.Copy(branchWiki)
	}
	wr.wi = userWiki
}

func getGenericTemplate(wr *wikiRequest) wikiTemplate {
	return wikiTemplate{
		User:              sessMgr.Get(wr.r.Context(), "user").(*authenticator.User),
		ServerPanelAccess: true, // TODO
		Branch:            sessMgr.GetString(wr.r.Context(), "branch"),
		Shortcode:         wr.shortcode,
		WikiTitle:         wr.wi.Title,
		AdminRoot:         strings.TrimRight(root, "/"),
		Static:            root + "static",
		Root:              root + wr.shortcode,
	}
}

func getCommitOpts(wr *wikiRequest, comment string) wiki.CommitOpts {
	user := sessMgr.Get(wr.r.Context(), "user").(*authenticator.User)
	return wiki.CommitOpts{
		Comment: comment,
		Name:    user.DisplayName,
		Email:   user.Email,
	}
}
<|endoftext|>


<commit_before>package qmp

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
)

// Status returns the current VM status.
func (m *Monitor) Status() (string, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Status string `json:"status"`
		} `json:"return"`
	}

	// Query the status.
	err := m.run("query-status", "", &resp)
	if err != nil {
		return "", err
	}

	return resp.Return.Status, nil
}

// Console fetches the File for a particular console.
func (m *Monitor) Console(target string) (*os.File, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			Label    string `json:"label"`
			Filename string `json:"filename"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-chardev", "", &resp)
	if err != nil {
		return nil, err
	}

	// Look for the requested console.
	for _, v := range resp.Return {
		if v.Label == target {
			ptyPath := strings.TrimPrefix(v.Filename, "pty:")

			if !shared.PathExists(ptyPath) {
				continue
			}

			// Open the PTS device
			console, err := os.OpenFile(ptyPath, os.O_RDWR, 0600)
			if err != nil {
				return nil, err
			}

			return console, nil
		}
	}

	return nil, ErrMonitorBadConsole
}

// SendFile adds a new file descriptor to the QMP fd table associated to name.
func (m *Monitor) SendFile(name string, file *os.File) error {
	// Check if disconnected
	if m.disconnected {
		return ErrMonitorDisconnect
	}

	// Query the status.
	_, err := m.qmp.RunWithFile([]byte(fmt.Sprintf("{'execute': 'getfd', 'arguments': {'fdname': '%s'}}", name)), file)
	if err != nil {
		// Confirm the daemon didn't die.
		errPing := m.ping()
		if errPing != nil {
			return errPing
		}

		return err
	}

	return nil
}

// Migrate starts a migration stream.
func (m *Monitor) Migrate(uri string) error {
	// Query the status.
	err := m.run("migrate", fmt.Sprintf("{'uri': '%s'}", uri), nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Prepare the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", "", &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// MigrateIncoming starts the receiver of a migration stream.
func (m *Monitor) MigrateIncoming(uri string) error {
	// Query the status.
	err := m.run("migrate-incoming", fmt.Sprintf("{'uri': '%s'}", uri), nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Preapre the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", "", &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// Powerdown tells the VM to gracefully shutdown.
func (m *Monitor) Powerdown() error {
	return m.run("system_powerdown", "", nil)
}

// Start tells QEMU to start the emulation.
func (m *Monitor) Start() error {
	return m.run("cont", "", nil)
}

// Pause tells QEMU to temporarily stop the emulation.
func (m *Monitor) Pause() error {
	return m.run("stop", "", nil)
}

// Quit tells QEMU to exit immediately.
func (m *Monitor) Quit() error {
	return m.run("quit", "", nil)
}

// GetCPUs fetches the vCPU information for pinning.
func (m *Monitor) GetCPUs() ([]int, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			CPU int `json:"cpu-index"`
			PID int `json:"thread-id"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-cpus-fast", "", &resp)
	if err != nil {
		return nil, err
	}

	// Make a slice of PIDs.
	pids := []int{}
	for _, cpu := range resp.Return {
		pids = append(pids, cpu.PID)
	}

	return pids, nil
}

// GetMemorySizeBytes returns the current size of the base memory in bytes.
func (m *Monitor) GetMemorySizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			BaseMemory int64 `json:"base-memory"`
		} `json:"return"`
	}

	err := m.run("query-memory-size-summary", "", &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.BaseMemory, nil
}

// GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size).
func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Actual int64 `json:"actual"`
		} `json:"return"`
	}

	err := m.run("query-balloon", "", &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.Actual, nil
}

// SetMemoryBalloonSizeBytes sets the size of the memory in bytes (which will resize the balloon as needed).
func (m *Monitor) SetMemoryBalloonSizeBytes(sizeBytes int64) error {
	return m.run("balloon", fmt.Sprintf("{'value': %d}", sizeBytes), nil)
}

// AddNIC adds a NIC device.
func (m *Monitor) AddNIC(netDev map[string]interface{}, device map[string]string) error {
	revert := revert.New()
	defer revert.Fail()

	if netDev != nil {
		args, err := json.Marshal(netDev)
		if err != nil {
			return err
		}

		err = m.run("netdev_add", string(args), nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC netdev")
		}

		revert.Add(func() {
			netDevDel := map[string]interface{}{
				"id": netDev["id"],
			}

			args, err := json.Marshal(netDevDel)
			if err != nil {
				return
			}

			err = m.run("netdev_del", string(args), nil)
			if err != nil {
				return
			}
		})
	}

	if device != nil {
		args, err := json.Marshal(device)
		if err != nil {
			return err
		}

		err = m.run("device_add", string(args), nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC device")
		}
	}

	revert.Success()
	return nil
}

// Reset VM.
func (m *Monitor) Reset() error {
	err := m.run("system_reset", "", nil)
	if err != nil {
		return errors.Wrapf(err, "Failed resetting")
	}

	return nil
}
<commit_msg>lxd/instance/drivers/qmp/commands: Adds RemoveNIC function<commit_after>package qmp

import (
	"encoding/json"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/shared"
)

// Status returns the current VM status.
func (m *Monitor) Status() (string, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Status string `json:"status"`
		} `json:"return"`
	}

	// Query the status.
	err := m.run("query-status", "", &resp)
	if err != nil {
		return "", err
	}

	return resp.Return.Status, nil
}

// Console fetches the File for a particular console.
func (m *Monitor) Console(target string) (*os.File, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			Label    string `json:"label"`
			Filename string `json:"filename"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-chardev", "", &resp)
	if err != nil {
		return nil, err
	}

	// Look for the requested console.
	for _, v := range resp.Return {
		if v.Label == target {
			ptyPath := strings.TrimPrefix(v.Filename, "pty:")

			if !shared.PathExists(ptyPath) {
				continue
			}

			// Open the PTS device
			console, err := os.OpenFile(ptyPath, os.O_RDWR, 0600)
			if err != nil {
				return nil, err
			}

			return console, nil
		}
	}

	return nil, ErrMonitorBadConsole
}

// SendFile adds a new file descriptor to the QMP fd table associated to name.
func (m *Monitor) SendFile(name string, file *os.File) error {
	// Check if disconnected
	if m.disconnected {
		return ErrMonitorDisconnect
	}

	// Query the status.
	_, err := m.qmp.RunWithFile([]byte(fmt.Sprintf("{'execute': 'getfd', 'arguments': {'fdname': '%s'}}", name)), file)
	if err != nil {
		// Confirm the daemon didn't die.
		errPing := m.ping()
		if errPing != nil {
			return errPing
		}

		return err
	}

	return nil
}

// Migrate starts a migration stream.
func (m *Monitor) Migrate(uri string) error {
	// Query the status.
	err := m.run("migrate", fmt.Sprintf("{'uri': '%s'}", uri), nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Prepare the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", "", &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// MigrateIncoming starts the receiver of a migration stream.
func (m *Monitor) MigrateIncoming(uri string) error {
	// Query the status.
	err := m.run("migrate-incoming", fmt.Sprintf("{'uri': '%s'}", uri), nil)
	if err != nil {
		return err
	}

	// Wait until it completes or fails.
	for {
		time.Sleep(1 * time.Second)

		// Preapre the response.
		var resp struct {
			Return struct {
				Status string `json:"status"`
			} `json:"return"`
		}

		err := m.run("query-migrate", "", &resp)
		if err != nil {
			return err
		}

		if resp.Return.Status == "failed" {
			return fmt.Errorf("Migration call failed")
		}

		if resp.Return.Status == "completed" {
			break
		}
	}

	return nil
}

// Powerdown tells the VM to gracefully shutdown.
func (m *Monitor) Powerdown() error {
	return m.run("system_powerdown", "", nil)
}

// Start tells QEMU to start the emulation.
func (m *Monitor) Start() error {
	return m.run("cont", "", nil)
}

// Pause tells QEMU to temporarily stop the emulation.
func (m *Monitor) Pause() error {
	return m.run("stop", "", nil)
}

// Quit tells QEMU to exit immediately.
func (m *Monitor) Quit() error {
	return m.run("quit", "", nil)
}

// GetCPUs fetches the vCPU information for pinning.
func (m *Monitor) GetCPUs() ([]int, error) {
	// Prepare the response.
	var resp struct {
		Return []struct {
			CPU int `json:"cpu-index"`
			PID int `json:"thread-id"`
		} `json:"return"`
	}

	// Query the consoles.
	err := m.run("query-cpus-fast", "", &resp)
	if err != nil {
		return nil, err
	}

	// Make a slice of PIDs.
	pids := []int{}
	for _, cpu := range resp.Return {
		pids = append(pids, cpu.PID)
	}

	return pids, nil
}

// GetMemorySizeBytes returns the current size of the base memory in bytes.
func (m *Monitor) GetMemorySizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			BaseMemory int64 `json:"base-memory"`
		} `json:"return"`
	}

	err := m.run("query-memory-size-summary", "", &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.BaseMemory, nil
}

// GetMemoryBalloonSizeBytes returns effective size of the memory in bytes (considering the current balloon size).
func (m *Monitor) GetMemoryBalloonSizeBytes() (int64, error) {
	// Prepare the response.
	var resp struct {
		Return struct {
			Actual int64 `json:"actual"`
		} `json:"return"`
	}

	err := m.run("query-balloon", "", &resp)
	if err != nil {
		return -1, err
	}

	return resp.Return.Actual, nil
}

// SetMemoryBalloonSizeBytes sets the size of the memory in bytes (which will resize the balloon as needed).
func (m *Monitor) SetMemoryBalloonSizeBytes(sizeBytes int64) error {
	return m.run("balloon", fmt.Sprintf("{'value': %d}", sizeBytes), nil)
}

// AddNIC adds a NIC device.
func (m *Monitor) AddNIC(netDev map[string]interface{}, device map[string]string) error {
	revert := revert.New()
	defer revert.Fail()

	if netDev != nil {
		args, err := json.Marshal(netDev)
		if err != nil {
			return err
		}

		err = m.run("netdev_add", string(args), nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC netdev")
		}

		revert.Add(func() {
			netDevDel := map[string]interface{}{
				"id": netDev["id"],
			}

			args, err := json.Marshal(netDevDel)
			if err != nil {
				return
			}

			err = m.run("netdev_del", string(args), nil)
			if err != nil {
				return
			}
		})
	}

	if device != nil {
		args, err := json.Marshal(device)
		if err != nil {
			return err
		}

		err = m.run("device_add", string(args), nil)
		if err != nil {
			return errors.Wrapf(err, "Failed adding NIC device")
		}
	}

	revert.Success()
	return nil
}

// RemoveNIC removes a NIC device.
func (m *Monitor) RemoveNIC(netDevID string, deviceID string) error {
	if deviceID != "" {
		deviceID := map[string]string{
			"id": deviceID,
		}

		args, err := json.Marshal(deviceID)
		if err != nil {
			return err
		}

		err = m.run("device_del", string(args), nil)
		if err != nil {
			// If the device has already been removed then all good.
			if err != nil && !strings.Contains(err.Error(), "not found") {
				return errors.Wrapf(err, "Failed removing NIC device")
			}
		}
	}

	if netDevID != "" {
		netDevID := map[string]string{
			"id": netDevID,
		}

		args, err := json.Marshal(netDevID)
		if err != nil {
			return err
		}

		err = m.run("netdev_del", string(args), nil)

		// Not all NICs need a netdev, so if its missing, its not a problem.
		if err != nil && !strings.Contains(err.Error(), "not found") {
			return errors.Wrapf(err, "Failed removing NIC netdev")
		}
	}

	return nil
}

// Reset VM.
func (m *Monitor) Reset() error {
	err := m.run("system_reset", "", nil)
	if err != nil {
		return errors.Wrapf(err, "Failed resetting")
	}

	return nil
}
<|endoftext|>


<commit_before>package libkbfs

import (
	"context"
	"errors"

	"github.com/keybase/kbfs/kbfsblock"
	kbgitkbfs "github.com/keybase/kbfs/protocol/kbgitkbfs"
	"github.com/keybase/kbfs/tlf"
)

type diskBlockCacheServiceConfig interface {
	diskBlockCacheGetter
}

// DiskBlockCacheService delegates requests for blocks to this KBFS
// instance's disk cache.
type DiskBlockCacheService struct {
	config diskBlockCacheServiceConfig
}

var _ kbgitkbfs.DiskBlockCacheInterface = (*DiskBlockCacheService)(nil)

// NewDiskBlockCacheService creates a new DiskBlockCacheService.
func NewDiskBlockCacheService(config diskBlockCacheServiceConfig) *DiskBlockCacheService {
	return &DiskBlockCacheService{
		config: config,
	}
}

// GetBlock implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) GetBlock(ctx context.Context,
	arg kbgitkbfs.GetBlockArg) (kbgitkbfs.GetBlockRes, error) {
	dbc := cache.config.DiskBlockCache()
	if dbc == nil {
		return kbgitkbfs.GetBlockRes{},
			DiskBlockCacheError{"Disk cache is nil"}
	}
	tlfID, err := tlf.ParseID(arg.TlfID.String())
	if err != nil {
		return kbgitkbfs.GetBlockRes{}, newDiskBlockCacheError(err)
	}
	blockID, err := kbfsblock.IDFromString(arg.BlockID)
	if err != nil {
		return kbgitkbfs.GetBlockRes{}, newDiskBlockCacheError(err)
	}
	buf, serverHalf, prefetchStatus, err := dbc.Get(ctx, tlfID, blockID)
	if err != nil {
		return kbgitkbfs.GetBlockRes{}, newDiskBlockCacheError(err)
	}

	return kbgitkbfs.GetBlockRes{
		buf, serverHalf.String(), kbgitkbfs.PrefetchStatus(prefetchStatus),
	}, nil
}

// PutBlock implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) PutBlock(ctx context.Context,
	arg kbgitkbfs.PutBlockArg) error {
	return errors.New("not implemented")
}

// DeleteBlocks implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) DeleteBlocks(ctx context.Context,
	blockIDs []string) (kbgitkbfs.DeleteBlocksRes, error) {
	return kbgitkbfs.DeleteBlocksRes{}, errors.New("not implemented")
}

// UpdateBlockMetadata implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) UpdateBlockMetadata(ctx context.Context,
	arg kbgitkbfs.UpdateBlockMetadataArg) error {
	return errors.New("not implemented")
}
<commit_msg>disk_block_cache_service: Implemented PutBlock.<commit_after>package libkbfs

import (
	"context"
	"errors"

	"github.com/keybase/kbfs/kbfsblock"
	"github.com/keybase/kbfs/kbfscrypto"
	kbgitkbfs "github.com/keybase/kbfs/protocol/kbgitkbfs"
	"github.com/keybase/kbfs/tlf"
)

type diskBlockCacheServiceConfig interface {
	diskBlockCacheGetter
}

// DiskBlockCacheService delegates requests for blocks to this KBFS
// instance's disk cache.
type DiskBlockCacheService struct {
	config diskBlockCacheServiceConfig
}

var _ kbgitkbfs.DiskBlockCacheInterface = (*DiskBlockCacheService)(nil)

// NewDiskBlockCacheService creates a new DiskBlockCacheService.
func NewDiskBlockCacheService(config diskBlockCacheServiceConfig) *DiskBlockCacheService {
	return &DiskBlockCacheService{
		config: config,
	}
}

// GetBlock implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) GetBlock(ctx context.Context,
	arg kbgitkbfs.GetBlockArg) (kbgitkbfs.GetBlockRes, error) {
	dbc := cache.config.DiskBlockCache()
	if dbc == nil {
		return kbgitkbfs.GetBlockRes{},
			DiskBlockCacheError{"Disk cache is nil"}
	}
	tlfID, err := tlf.ParseID(arg.TlfID.String())
	if err != nil {
		return kbgitkbfs.GetBlockRes{}, newDiskBlockCacheError(err)
	}
	blockID, err := kbfsblock.IDFromString(arg.BlockID)
	if err != nil {
		return kbgitkbfs.GetBlockRes{}, newDiskBlockCacheError(err)
	}
	buf, serverHalf, prefetchStatus, err := dbc.Get(ctx, tlfID, blockID)
	if err != nil {
		return kbgitkbfs.GetBlockRes{}, newDiskBlockCacheError(err)
	}

	return kbgitkbfs.GetBlockRes{
		buf, serverHalf.String(), kbgitkbfs.PrefetchStatus(prefetchStatus),
	}, nil
}

// PutBlock implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) PutBlock(ctx context.Context,
	arg kbgitkbfs.PutBlockArg) error {
	dbc := cache.config.DiskBlockCache()
	if dbc == nil {
		return DiskBlockCacheError{"Disk cache is nil"}
	}
	tlfID, err := tlf.ParseID(arg.TlfID.String())
	if err != nil {
		return newDiskBlockCacheError(err)
	}
	blockID, err := kbfsblock.IDFromString(arg.BlockID)
	if err != nil {
		return newDiskBlockCacheError(err)
	}
	serverHalf, err := kbfscrypto.ParseBlockCryptKeyServerHalf(arg.ServerHalf)
	if err != nil {
		return newDiskBlockCacheError(err)
	}
	err = dbc.Put(ctx, tlfID, blockID, arg.Buf, serverHalf)
	if err != nil {
		return newDiskBlockCacheError(err)
	}
	return nil
}

// DeleteBlocks implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) DeleteBlocks(ctx context.Context,
	blockIDs []string) (kbgitkbfs.DeleteBlocksRes, error) {
	return kbgitkbfs.DeleteBlocksRes{}, errors.New("not implemented")
}

// UpdateBlockMetadata implements the DiskBlockCacheInterface interface for
// DiskBlockCacheService.
func (cache *DiskBlockCacheService) UpdateBlockMetadata(ctx context.Context,
	arg kbgitkbfs.UpdateBlockMetadataArg) error {
	return errors.New("not implemented")
}
<|endoftext|>


<commit_before>package search

import (
	"github.com/dertseha/everoute/travel"
	"github.com/dertseha/everoute/util"
)

type RouteFinderBuilder interface {
	AddWaypoint(criterion SearchCriterion) RouteFinderBuilder
	ForDestination(criterion SearchCriterion) RouteFinderBuilder

	Build() RouteFinder
}

type routeFinderBuilder struct {
	capability  travel.TravelCapability
	rule        travel.TravelRule
	startPaths  []travel.Path
	waypoints   []SearchCriterion
	destination SearchCriterion
	collector   RouteSearchResultCollector

	searchDone func()

	populationLimit  int
	generationLimit  int
	uncontestedLimit int

	mutationPercentage int

	rand util.Randomizer
}

func NewRouteFinder(capability travel.TravelCapability, rule travel.TravelRule,
	startPaths []travel.Path, collector RouteSearchResultCollector, searchDone func()) RouteFinderBuilder {
	builder := &routeFinderBuilder{
		capability: capability,
		rule:       rule,
		startPaths: startPaths,
		waypoints:  make([]SearchCriterion, 0),
		collector:  collector,

		searchDone: searchDone,

		populationLimit:    50,
		generationLimit:    40000,
		mutationPercentage: 20,

		rand: util.DefaultRandomizer()}

	return builder
}

func (builder *routeFinderBuilder) AddWaypoint(criterion SearchCriterion) RouteFinderBuilder {
	builder.waypoints = append(builder.waypoints, criterion)

	return builder
}

func (builder *routeFinderBuilder) ForDestination(criterion SearchCriterion) RouteFinderBuilder {
	builder.destination = criterion

	return builder
}

func (builder *routeFinderBuilder) Build() RouteFinder {
	finder := &routeFinder{
		startPaths:    builder.startPaths,
		waypointCount: len(builder.waypoints),
		collector:     builder.collector,

		searchDone: builder.searchDone,

		populationLimit:    builder.populationLimit,
		generationLimit:    builder.generationLimit,
		uncontestedLimit:   builder.populationLimit * 20,
		mutationPercentage: builder.mutationPercentage,

		executor: util.SingleThreadExecutor(builder.populationLimit * 4),
		rand:     builder.rand,

		splicer:    newChromosomeSplicer(builder.rand),
		population: emptyRouteList(builder.rule)}

	finder.incubator = newRouteIncubator(builder.capability, builder.rule, builder.waypoints, builder.destination, builder.rand, finder)

	finder.IncubatorEmpty()

	return finder
}
<commit_msg>Changed route finder parameter - uncontestedLimit is now based on generationLimit<commit_after>package search

import (
	"github.com/dertseha/everoute/travel"
	"github.com/dertseha/everoute/util"
)

type RouteFinderBuilder interface {
	AddWaypoint(criterion SearchCriterion) RouteFinderBuilder
	ForDestination(criterion SearchCriterion) RouteFinderBuilder

	Build() RouteFinder
}

type routeFinderBuilder struct {
	capability  travel.TravelCapability
	rule        travel.TravelRule
	startPaths  []travel.Path
	waypoints   []SearchCriterion
	destination SearchCriterion
	collector   RouteSearchResultCollector

	searchDone func()

	populationLimit  int
	generationLimit  int
	uncontestedLimit int

	mutationPercentage int

	rand util.Randomizer
}

func NewRouteFinder(capability travel.TravelCapability, rule travel.TravelRule,
	startPaths []travel.Path, collector RouteSearchResultCollector, searchDone func()) RouteFinderBuilder {
	builder := &routeFinderBuilder{
		capability: capability,
		rule:       rule,
		startPaths: startPaths,
		waypoints:  make([]SearchCriterion, 0),
		collector:  collector,

		searchDone: searchDone,

		populationLimit:    50,
		generationLimit:    40000,
		mutationPercentage: 20,

		rand: util.DefaultRandomizer()}

	return builder
}

func (builder *routeFinderBuilder) AddWaypoint(criterion SearchCriterion) RouteFinderBuilder {
	builder.waypoints = append(builder.waypoints, criterion)

	return builder
}

func (builder *routeFinderBuilder) ForDestination(criterion SearchCriterion) RouteFinderBuilder {
	builder.destination = criterion

	return builder
}

func (builder *routeFinderBuilder) Build() RouteFinder {
	finder := &routeFinder{
		startPaths:    builder.startPaths,
		waypointCount: len(builder.waypoints),
		collector:     builder.collector,

		searchDone: builder.searchDone,

		populationLimit:    builder.populationLimit,
		generationLimit:    builder.generationLimit,
		uncontestedLimit:   builder.generationLimit / 4,
		mutationPercentage: builder.mutationPercentage,

		executor: util.SingleThreadExecutor(builder.populationLimit * 4),
		rand:     builder.rand,

		splicer:    newChromosomeSplicer(builder.rand),
		population: emptyRouteList(builder.rule)}

	finder.incubator = newRouteIncubator(builder.capability, builder.rule, builder.waypoints, builder.destination, builder.rand, finder)

	finder.IncubatorEmpty()

	return finder
}
<|endoftext|>


<commit_before>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package rfc5424

import (
	"fmt"
)

// These are the supported logging severity levels.
const (
	SeverityEmergency Severity = iota
	SeverityAlert
	SeverityCrit
	SeverityError
	SeverityWarning
	SeverityNotice
	SeverityInformational
	SeverityDebug

	severityTooLarge
)

// These are the supported logging facilities.
const (
	facilityDefault Facility = iota

	FacilityKern
	FacilityUser // default
	FacilityMail
	FacilityDaemon
	FacilityAuth
	FacilitySyslog
	FacilityLPR
	FacilityNews
	FacilityUUCP
	FacilityCron
	FacilityAuthpriv
	FacilityFTP
	FacilityNTP

	facilityLogAudit
	facilityLogAlert
	facilityCron2

	FacilityLocal0
	FacilityLocal1
	FacilityLocal2
	FacilityLocal3
	FacilityLocal4
	FacilityLocal5
	FacilityLocal6
	FacilityLocal7

	facilityTooLarge
)

// Priority identifies the importance of a log record.
type Priority struct {
	// Severity is the criticality of the log record.
	Severity Severity

	// Facility is the system component for which the log record
	// was created.
	Facility Facility
}

// ParsePriority converts a priority string back into a Priority.
func ParsePriority(str string) (Priority, error) {
	var code int
	if _, err := fmt.Sscanf(str, "<%d>", &code); err != nil {
		return Priority{}, err
	}
	p := decodePriority(code)
	return p, p.Validate()
}

// String returns the RFC 5424 representation of the priority.
func (p Priority) String() string {
	return fmt.Sprintf("<%d>", p.encode())
}

func (p Priority) encode() int {
	return p.Facility.encode()<<3 + p.Severity.encode()
}

func decodePriority(code int) Priority {
	return Priority{
		Severity: decodeSeverity(code & 0x07),
		Facility: decodeFacility(code >> 3),
	}
}

// Validated ensures that the priority is correct.
func (p Priority) Validate() error {
	if err := p.Severity.Validate(); err != nil {
		return fmt.Errorf("bad Severity: %v", err)
	}
	if err := p.Facility.Validate(); err != nil {
		return fmt.Errorf("bad Facility: %v", err)
	}
	return nil
}

// Severity is the criticality of the log record.
type Severity int

func (s Severity) encode() int {
	return int(s)
}

func decodeSeverity(code int) Severity {
	// The relationship between the code and the Severity's actual
	// underlying value is an implementation detail that we hide here.
	// It so happens that currently each Severity matches its code
	// exactly.
	return Severity(code)
}

// String returns the name of the severity.
func (s Severity) String() string {
	switch s {
	case SeverityEmergency:
		return "EMERGENCY"
	case SeverityAlert:
		return "ALERT"
	case SeverityCrit:
		return "CRIT"
	case SeverityError:
		return "ERROR"
	case SeverityWarning:
		return "WARNING"
	case SeverityNotice:
		return "NOTICE"
	case SeverityInformational:
		return "INFO"
	case SeverityDebug:
		return "DEBUG"
	default:
		return fmt.Sprintf("Severity %d", int(s))
	}
}

// Validate ensures that the severity is correct. This will fail only
// in cases where an unsupported int is converted into a Severity.
func (s Severity) Validate() error {
	if s < 0 || s >= severityTooLarge {
		return fmt.Errorf("severity %d not recognized", s)
	}
	return nil
}

// Facility is the system component for which the log record
// was created.
type Facility int

// String returns the name of the facility.
func (f Facility) String() string {
	if f == facilityDefault {
		f = FacilityUser
	}
	switch f {
	case FacilityKern:
		return "KERN"
	case FacilityUser:
		return "USER"
	case FacilityMail:
		return "MAIL"
	case FacilityDaemon:
		return "DAEMON"
	case FacilityAuth:
		return "AUTH"
	case FacilitySyslog:
		return "SYSLOG"
	case FacilityLPR:
		return "LPR"
	case FacilityNews:
		return "NEWS"
	case FacilityUUCP:
		return "UUCP"
	case FacilityCron:
		return "CRON"
	case FacilityAuthpriv:
		return "AUTHPRIV"
	case FacilityFTP:
		return "FTP"
	case FacilityLocal0:
		return "LOCAL0"
	case FacilityLocal1:
		return "LOCAL1"
	case FacilityLocal2:
		return "LOCAL2"
	case FacilityLocal3:
		return "LOCAL3"
	case FacilityLocal4:
		return "LOCAL4"
	case FacilityLocal5:
		return "LOCAL5"
	case FacilityLocal6:
		return "LOCAL6"
	case FacilityLocal7:
		return "LOCAL7"
	default:
		return fmt.Sprint("Facility %d", int(f))
	}
}

func (f Facility) encode() int {
	if f == facilityDefault {
		f = FacilityUser
	}
	return int(f) - 1
}

func decodeFacility(code int) Facility {
	return Facility(code + 1)
}

// Validate ensures that the facility is correct.
func (f Facility) Validate() error {
	if f == facilityDefault {
		return nil
	}
	if f < 0 || f >= facilityTooLarge {
		return fmt.Errorf("facility %d not recognized", f)
	}
	return nil
}
<commit_msg>Fix a typo.<commit_after>// Copyright 2016 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package rfc5424

import (
	"fmt"
)

// These are the supported logging severity levels.
const (
	SeverityEmergency Severity = iota
	SeverityAlert
	SeverityCrit
	SeverityError
	SeverityWarning
	SeverityNotice
	SeverityInformational
	SeverityDebug

	severityTooLarge
)

// These are the supported logging facilities.
const (
	facilityDefault Facility = iota

	FacilityKern
	FacilityUser // default
	FacilityMail
	FacilityDaemon
	FacilityAuth
	FacilitySyslog
	FacilityLPR
	FacilityNews
	FacilityUUCP
	FacilityCron
	FacilityAuthpriv
	FacilityFTP
	FacilityNTP

	facilityLogAudit
	facilityLogAlert
	facilityCron2

	FacilityLocal0
	FacilityLocal1
	FacilityLocal2
	FacilityLocal3
	FacilityLocal4
	FacilityLocal5
	FacilityLocal6
	FacilityLocal7

	facilityTooLarge
)

// Priority identifies the importance of a log record.
type Priority struct {
	// Severity is the criticality of the log record.
	Severity Severity

	// Facility is the system component for which the log record
	// was created.
	Facility Facility
}

// ParsePriority converts a priority string back into a Priority.
func ParsePriority(str string) (Priority, error) {
	var code int
	if _, err := fmt.Sscanf(str, "<%d>", &code); err != nil {
		return Priority{}, err
	}
	p := decodePriority(code)
	return p, p.Validate()
}

// String returns the RFC 5424 representation of the priority.
func (p Priority) String() string {
	return fmt.Sprintf("<%d>", p.encode())
}

func (p Priority) encode() int {
	return p.Facility.encode()<<3 + p.Severity.encode()
}

func decodePriority(code int) Priority {
	return Priority{
		Severity: decodeSeverity(code & 0x07),
		Facility: decodeFacility(code >> 3),
	}
}

// Validated ensures that the priority is correct.
func (p Priority) Validate() error {
	if err := p.Severity.Validate(); err != nil {
		return fmt.Errorf("bad Severity: %v", err)
	}
	if err := p.Facility.Validate(); err != nil {
		return fmt.Errorf("bad Facility: %v", err)
	}
	return nil
}

// Severity is the criticality of the log record.
type Severity int

func (s Severity) encode() int {
	return int(s)
}

func decodeSeverity(code int) Severity {
	// The relationship between the code and the Severity's actual
	// underlying value is an implementation detail that we hide here.
	// It so happens that currently each Severity matches its code
	// exactly.
	return Severity(code)
}

// String returns the name of the severity.
func (s Severity) String() string {
	switch s {
	case SeverityEmergency:
		return "EMERGENCY"
	case SeverityAlert:
		return "ALERT"
	case SeverityCrit:
		return "CRIT"
	case SeverityError:
		return "ERROR"
	case SeverityWarning:
		return "WARNING"
	case SeverityNotice:
		return "NOTICE"
	case SeverityInformational:
		return "INFO"
	case SeverityDebug:
		return "DEBUG"
	default:
		return fmt.Sprintf("Severity %d", int(s))
	}
}

// Validate ensures that the severity is correct. This will fail only
// in cases where an unsupported int is converted into a Severity.
func (s Severity) Validate() error {
	if s < 0 || s >= severityTooLarge {
		return fmt.Errorf("severity %d not recognized", s)
	}
	return nil
}

// Facility is the system component for which the log record
// was created.
type Facility int

// String returns the name of the facility.
func (f Facility) String() string {
	if f == facilityDefault {
		f = FacilityUser
	}
	switch f {
	case FacilityKern:
		return "KERN"
	case FacilityUser:
		return "USER"
	case FacilityMail:
		return "MAIL"
	case FacilityDaemon:
		return "DAEMON"
	case FacilityAuth:
		return "AUTH"
	case FacilitySyslog:
		return "SYSLOG"
	case FacilityLPR:
		return "LPR"
	case FacilityNews:
		return "NEWS"
	case FacilityUUCP:
		return "UUCP"
	case FacilityCron:
		return "CRON"
	case FacilityAuthpriv:
		return "AUTHPRIV"
	case FacilityFTP:
		return "FTP"
	case FacilityLocal0:
		return "LOCAL0"
	case FacilityLocal1:
		return "LOCAL1"
	case FacilityLocal2:
		return "LOCAL2"
	case FacilityLocal3:
		return "LOCAL3"
	case FacilityLocal4:
		return "LOCAL4"
	case FacilityLocal5:
		return "LOCAL5"
	case FacilityLocal6:
		return "LOCAL6"
	case FacilityLocal7:
		return "LOCAL7"
	default:
		return fmt.Sprintf("Facility %d", int(f))
	}
}

func (f Facility) encode() int {
	if f == facilityDefault {
		f = FacilityUser
	}
	return int(f) - 1
}

func decodeFacility(code int) Facility {
	return Facility(code + 1)
}

// Validate ensures that the facility is correct.
func (f Facility) Validate() error {
	if f == facilityDefault {
		return nil
	}
	if f < 0 || f >= facilityTooLarge {
		return fmt.Errorf("facility %d not recognized", f)
	}
	return nil
}
<|endoftext|>