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

package cmd

import (
	"context"
	"errors"
	pathutil "path"
	"runtime"
	"strings"
	"sync"

	"fmt"
	"time"

	"github.com/minio/dsync"
	"github.com/minio/lsync"
	"github.com/minio/minio-go/pkg/set"
	"github.com/minio/minio/cmd/logger"
	xnet "github.com/minio/minio/pkg/net"
)

// Global name space lock.
var globalNSMutex *nsLockMap

// Global lock server one per server.
var globalLockServer *lockRPCReceiver

// Instance of dsync for distributed clients.
var globalDsync *dsync.Dsync

// RWLocker - locker interface to introduce GetRLock, RUnlock.
type RWLocker interface {
	GetLock(timeout *dynamicTimeout) (timedOutErr error)
	Unlock()
	GetRLock(timeout *dynamicTimeout) (timedOutErr error)
	RUnlock()
}

// RWLockerSync - internal locker interface.
type RWLockerSync interface {
	GetLock(timeout time.Duration) bool
	Unlock()
	GetRLock(timeout time.Duration) bool
	RUnlock()
}

// Initialize distributed locking only in case of distributed setup.
// Returns lock clients and the node index for the current server.
func newDsyncNodes(endpoints EndpointList) (clnts []dsync.NetLocker, myNode int) {
	myNode = -1
	seenHosts := set.NewStringSet()
	for _, endpoint := range endpoints {
		if seenHosts.Contains(endpoint.Host) {
			continue
		}
		seenHosts.Add(endpoint.Host)

		var locker dsync.NetLocker
		if endpoint.IsLocal {
			myNode = len(clnts)

			receiver := &lockRPCReceiver{
				ll: localLocker{
					serverAddr:      endpoint.Host,
					serviceEndpoint: lockServicePath,
					lockMap:         make(map[string][]lockRequesterInfo),
				},
			}

			globalLockServer = receiver
			locker = &(receiver.ll)
		} else {
			host, err := xnet.ParseHost(endpoint.Host)
			logger.FatalIf(err, "Unable to parse Lock RPC Host", context.Background())
			locker, err = NewLockRPCClient(host)
			logger.FatalIf(err, "Unable to initialize Lock RPC Client", context.Background())
		}

		clnts = append(clnts, locker)
	}

	return clnts, myNode
}

// newNSLock - return a new name space lock map.
func newNSLock(isDistXL bool) *nsLockMap {
	nsMutex := nsLockMap{
		isDistXL: isDistXL,
		lockMap:  make(map[nsParam]*nsLock),
		counters: &lockStat{},
	}
	return &nsMutex
}

// initNSLock - initialize name space lock map.
func initNSLock(isDistXL bool) {
	globalNSMutex = newNSLock(isDistXL)
}

// nsParam - carries name space resource.
type nsParam struct {
	volume string
	path   string
}

// nsLock - provides primitives for locking critical namespace regions.
type nsLock struct {
	RWLockerSync
	ref uint
}

// nsLockMap - namespace lock map, provides primitives to Lock,
// Unlock, RLock and RUnlock.
type nsLockMap struct {
	// Lock counter used for lock debugging.
	counters *lockStat

	// Indicates if namespace is part of a distributed setup.
	isDistXL     bool
	lockMap      map[nsParam]*nsLock
	lockMapMutex sync.Mutex
}

// Lock the namespace resource.
func (n *nsLockMap) lock(volume, path string, lockSource, opsID string, readLock bool, timeout time.Duration) (locked bool) {
	var nsLk *nsLock
	n.lockMapMutex.Lock()

	param := nsParam{volume, path}
	nsLk, found := n.lockMap[param]
	if !found {
		nsLk = &nsLock{
			RWLockerSync: func() RWLockerSync {
				if n.isDistXL {
					return dsync.NewDRWMutex(pathJoin(volume, path), globalDsync)
				}
				return &lsync.LRWMutex{}
			}(),
			ref: 0,
		}
		n.lockMap[param] = nsLk
	}
	nsLk.ref++ // Update ref count here to avoid multiple races.

	// Unlock map before Locking NS which might block.
	n.lockMapMutex.Unlock()

	// Locking here will block (until timeout).
	if readLock {
		locked = nsLk.GetRLock(timeout)
	} else {
		locked = nsLk.GetLock(timeout)
	}

	if !locked { // We failed to get the lock
		n.lockMapMutex.Lock()
		defer n.lockMapMutex.Unlock()

		nsLk.ref-- // Decrement ref count since we failed to get the lock

		if nsLk.ref == 0 {
			// Remove from the map if there are no more references.
			delete(n.lockMap, param)
		}
	}
	return
}

// Unlock the namespace resource.
func (n *nsLockMap) unlock(volume, path, opsID string, readLock bool) {
	// nsLk.Unlock() will not block, hence locking the map for the
	// entire function is fine.
	n.lockMapMutex.Lock()
	defer n.lockMapMutex.Unlock()

	param := nsParam{volume, path}
	if nsLk, found := n.lockMap[param]; found {
		if readLock {
			nsLk.RUnlock()
		} else {
			nsLk.Unlock()
		}
		if nsLk.ref == 0 {
			logger.LogIf(context.Background(), errors.New("Namespace reference count cannot be 0"))
		}
		if nsLk.ref != 0 {
			nsLk.ref--
		}
		if nsLk.ref == 0 {
			// Remove from the map if there are no more references.
			delete(n.lockMap, param)
		}
	}
}

// Lock - locks the given resource for writes, using a previously
// allocated name space lock or initializing a new one.
func (n *nsLockMap) Lock(volume, path, opsID string, timeout time.Duration) (locked bool) {
	readLock := false // This is a write lock.

	lockSource := getSource() // Useful for debugging
	return n.lock(volume, path, lockSource, opsID, readLock, timeout)
}

// Unlock - unlocks any previously acquired write locks.
func (n *nsLockMap) Unlock(volume, path, opsID string) {
	readLock := false
	n.unlock(volume, path, opsID, readLock)
}

// RLock - locks any previously acquired read locks.
func (n *nsLockMap) RLock(volume, path, opsID string, timeout time.Duration) (locked bool) {
	readLock := true

	lockSource := getSource() // Useful for debugging
	return n.lock(volume, path, lockSource, opsID, readLock, timeout)
}

// RUnlock - unlocks any previously acquired read locks.
func (n *nsLockMap) RUnlock(volume, path, opsID string) {
	readLock := true
	n.unlock(volume, path, opsID, readLock)
}

// ForceUnlock - forcefully unlock a lock based on name.
func (n *nsLockMap) ForceUnlock(volume, path string) {
	n.lockMapMutex.Lock()
	defer n.lockMapMutex.Unlock()

	// Clarification on operation:
	// - In case of FS or XL we call ForceUnlock on the local globalNSMutex
	//   (since there is only a single server) which will cause the 'stuck'
	//   mutex to be removed from the map. Existing operations for this
	//   will continue to be blocked (and timeout). New operations on this
	//   resource will use a new mutex and proceed normally.
	//
	// - In case of Distributed setup (using dsync), there is no need to call
	//   ForceUnlock on the server where the lock was acquired and is presumably
	//   'stuck'. Instead dsync.ForceUnlock() will release the underlying locks
	//   that participated in granting the lock. Any pending dsync locks that
	//   are blocking can now proceed as normal and any new locks will also
	//   participate normally.
	if n.isDistXL { // For distributed mode, broadcast ForceUnlock message.
		dsync.NewDRWMutex(pathJoin(volume, path), globalDsync).ForceUnlock()
	}

	param := nsParam{volume, path}
	if _, found := n.lockMap[param]; found {
		// Remove lock from the map.
		delete(n.lockMap, param)
	}
}

// lockInstance - frontend/top-level interface for namespace locks.
type lockInstance struct {
	ns                  *nsLockMap
	volume, path, opsID string
}

// NewNSLock - returns a lock instance for a given volume and
// path. The returned lockInstance object encapsulates the nsLockMap,
// volume, path and operation ID.
func (n *nsLockMap) NewNSLock(volume, path string) RWLocker {
	opsID := mustGetUUID()
	return &lockInstance{n, volume, path, opsID}
}

// Lock - block until write lock is taken or timeout has occurred.
func (li *lockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error) {
	lockSource := getSource()
	start := UTCNow()
	readLock := false
	if !li.ns.lock(li.volume, li.path, lockSource, li.opsID, readLock, timeout.Timeout()) {
		timeout.LogFailure()
		return OperationTimedOut{Path: li.path}
	}
	timeout.LogSuccess(UTCNow().Sub(start))
	return
}

// Unlock - block until write lock is released.
func (li *lockInstance) Unlock() {
	readLock := false
	li.ns.unlock(li.volume, li.path, li.opsID, readLock)
}

// RLock - block until read lock is taken or timeout has occurred.
func (li *lockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr error) {
	lockSource := getSource()
	start := UTCNow()
	readLock := true
	if !li.ns.lock(li.volume, li.path, lockSource, li.opsID, readLock, timeout.Timeout()) {
		timeout.LogFailure()
		return OperationTimedOut{Path: li.path}
	}
	timeout.LogSuccess(UTCNow().Sub(start))
	return
}

// RUnlock - block until read lock is released.
func (li *lockInstance) RUnlock() {
	readLock := true
	li.ns.unlock(li.volume, li.path, li.opsID, readLock)
}

func getSource() string {
	var funcName string
	pc, filename, lineNum, ok := runtime.Caller(2)
	if ok {
		filename = pathutil.Base(filename)
		funcName = strings.TrimPrefix(runtime.FuncForPC(pc).Name(),
			"github.com/minio/minio/cmd.")
	} else {
		filename = "<unknown>"
		lineNum = 0
	}

	return fmt.Sprintf("[%s:%d:%s()]", filename, lineNum, funcName)
}
<commit_msg>Hold locks granularly in nslockMap (#6242)<commit_after>/*
 * Minio Cloud Storage, (C) 2016 Minio, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package cmd

import (
	"context"
	"errors"
	pathutil "path"
	"runtime"
	"strings"
	"sync"

	"fmt"
	"time"

	"github.com/minio/dsync"
	"github.com/minio/lsync"
	"github.com/minio/minio-go/pkg/set"
	"github.com/minio/minio/cmd/logger"
	xnet "github.com/minio/minio/pkg/net"
)

// Global name space lock.
var globalNSMutex *nsLockMap

// Global lock server one per server.
var globalLockServer *lockRPCReceiver

// Instance of dsync for distributed clients.
var globalDsync *dsync.Dsync

// RWLocker - locker interface to introduce GetRLock, RUnlock.
type RWLocker interface {
	GetLock(timeout *dynamicTimeout) (timedOutErr error)
	Unlock()
	GetRLock(timeout *dynamicTimeout) (timedOutErr error)
	RUnlock()
}

// RWLockerSync - internal locker interface.
type RWLockerSync interface {
	GetLock(timeout time.Duration) bool
	Unlock()
	GetRLock(timeout time.Duration) bool
	RUnlock()
}

// Initialize distributed locking only in case of distributed setup.
// Returns lock clients and the node index for the current server.
func newDsyncNodes(endpoints EndpointList) (clnts []dsync.NetLocker, myNode int) {
	myNode = -1
	seenHosts := set.NewStringSet()
	for _, endpoint := range endpoints {
		if seenHosts.Contains(endpoint.Host) {
			continue
		}
		seenHosts.Add(endpoint.Host)

		var locker dsync.NetLocker
		if endpoint.IsLocal {
			myNode = len(clnts)

			receiver := &lockRPCReceiver{
				ll: localLocker{
					serverAddr:      endpoint.Host,
					serviceEndpoint: lockServicePath,
					lockMap:         make(map[string][]lockRequesterInfo),
				},
			}

			globalLockServer = receiver
			locker = &(receiver.ll)
		} else {
			host, err := xnet.ParseHost(endpoint.Host)
			logger.FatalIf(err, "Unable to parse Lock RPC Host", context.Background())
			locker, err = NewLockRPCClient(host)
			logger.FatalIf(err, "Unable to initialize Lock RPC Client", context.Background())
		}

		clnts = append(clnts, locker)
	}

	return clnts, myNode
}

// newNSLock - return a new name space lock map.
func newNSLock(isDistXL bool) *nsLockMap {
	nsMutex := nsLockMap{
		isDistXL: isDistXL,
		lockMap:  make(map[nsParam]*nsLock),
		counters: &lockStat{},
	}
	return &nsMutex
}

// initNSLock - initialize name space lock map.
func initNSLock(isDistXL bool) {
	globalNSMutex = newNSLock(isDistXL)
}

// nsParam - carries name space resource.
type nsParam struct {
	volume string
	path   string
}

// nsLock - provides primitives for locking critical namespace regions.
type nsLock struct {
	RWLockerSync
	ref uint
}

// nsLockMap - namespace lock map, provides primitives to Lock,
// Unlock, RLock and RUnlock.
type nsLockMap struct {
	// Lock counter used for lock debugging.
	counters *lockStat

	// Indicates if namespace is part of a distributed setup.
	isDistXL     bool
	lockMap      map[nsParam]*nsLock
	lockMapMutex sync.RWMutex
}

// Lock the namespace resource.
func (n *nsLockMap) lock(volume, path string, lockSource, opsID string, readLock bool, timeout time.Duration) (locked bool) {
	var nsLk *nsLock

	n.lockMapMutex.Lock()
	param := nsParam{volume, path}
	nsLk, found := n.lockMap[param]
	if !found {
		n.lockMap[param] = &nsLock{
			RWLockerSync: func() RWLockerSync {
				if n.isDistXL {
					return dsync.NewDRWMutex(pathJoin(volume, path), globalDsync)
				}
				return &lsync.LRWMutex{}
			}(),
			ref: 1,
		}
		nsLk = n.lockMap[param]
	} else {
		// Update ref count here to avoid multiple races.
		nsLk.ref++
	}
	n.lockMapMutex.Unlock()

	// Locking here will block (until timeout).
	if readLock {
		locked = nsLk.GetRLock(timeout)
	} else {
		locked = nsLk.GetLock(timeout)
	}

	if !locked { // We failed to get the lock

		// Decrement ref count since we failed to get the lock
		n.lockMapMutex.Lock()
		nsLk.ref--
		if nsLk.ref == 0 {
			// Remove from the map if there are no more references.
			delete(n.lockMap, param)
		}
		n.lockMapMutex.Unlock()
	}
	return
}

// Unlock the namespace resource.
func (n *nsLockMap) unlock(volume, path, opsID string, readLock bool) {
	param := nsParam{volume, path}
	n.lockMapMutex.RLock()
	nsLk, found := n.lockMap[param]
	n.lockMapMutex.RUnlock()
	if !found {
		return
	}
	if readLock {
		nsLk.RUnlock()
	} else {
		nsLk.Unlock()
	}
	n.lockMapMutex.Lock()
	if nsLk.ref == 0 {
		logger.LogIf(context.Background(), errors.New("Namespace reference count cannot be 0"))
	} else {
		nsLk.ref--
		if nsLk.ref == 0 {
			// Remove from the map if there are no more references.
			delete(n.lockMap, param)
		}
	}
	n.lockMapMutex.Unlock()
}

// Lock - locks the given resource for writes, using a previously
// allocated name space lock or initializing a new one.
func (n *nsLockMap) Lock(volume, path, opsID string, timeout time.Duration) (locked bool) {
	readLock := false // This is a write lock.

	lockSource := getSource() // Useful for debugging
	return n.lock(volume, path, lockSource, opsID, readLock, timeout)
}

// Unlock - unlocks any previously acquired write locks.
func (n *nsLockMap) Unlock(volume, path, opsID string) {
	readLock := false
	n.unlock(volume, path, opsID, readLock)
}

// RLock - locks any previously acquired read locks.
func (n *nsLockMap) RLock(volume, path, opsID string, timeout time.Duration) (locked bool) {
	readLock := true

	lockSource := getSource() // Useful for debugging
	return n.lock(volume, path, lockSource, opsID, readLock, timeout)
}

// RUnlock - unlocks any previously acquired read locks.
func (n *nsLockMap) RUnlock(volume, path, opsID string) {
	readLock := true
	n.unlock(volume, path, opsID, readLock)
}

// ForceUnlock - forcefully unlock a lock based on name.
func (n *nsLockMap) ForceUnlock(volume, path string) {
	n.lockMapMutex.Lock()
	defer n.lockMapMutex.Unlock()

	// Clarification on operation:
	// - In case of FS or XL we call ForceUnlock on the local globalNSMutex
	//   (since there is only a single server) which will cause the 'stuck'
	//   mutex to be removed from the map. Existing operations for this
	//   will continue to be blocked (and timeout). New operations on this
	//   resource will use a new mutex and proceed normally.
	//
	// - In case of Distributed setup (using dsync), there is no need to call
	//   ForceUnlock on the server where the lock was acquired and is presumably
	//   'stuck'. Instead dsync.ForceUnlock() will release the underlying locks
	//   that participated in granting the lock. Any pending dsync locks that
	//   are blocking can now proceed as normal and any new locks will also
	//   participate normally.
	if n.isDistXL { // For distributed mode, broadcast ForceUnlock message.
		dsync.NewDRWMutex(pathJoin(volume, path), globalDsync).ForceUnlock()
	}

	param := nsParam{volume, path}
	if _, found := n.lockMap[param]; found {
		// Remove lock from the map.
		delete(n.lockMap, param)
	}
}

// lockInstance - frontend/top-level interface for namespace locks.
type lockInstance struct {
	ns                  *nsLockMap
	volume, path, opsID string
}

// NewNSLock - returns a lock instance for a given volume and
// path. The returned lockInstance object encapsulates the nsLockMap,
// volume, path and operation ID.
func (n *nsLockMap) NewNSLock(volume, path string) RWLocker {
	opsID := mustGetUUID()
	return &lockInstance{n, volume, path, opsID}
}

// Lock - block until write lock is taken or timeout has occurred.
func (li *lockInstance) GetLock(timeout *dynamicTimeout) (timedOutErr error) {
	lockSource := getSource()
	start := UTCNow()
	readLock := false
	if !li.ns.lock(li.volume, li.path, lockSource, li.opsID, readLock, timeout.Timeout()) {
		timeout.LogFailure()
		return OperationTimedOut{Path: li.path}
	}
	timeout.LogSuccess(UTCNow().Sub(start))
	return
}

// Unlock - block until write lock is released.
func (li *lockInstance) Unlock() {
	readLock := false
	li.ns.unlock(li.volume, li.path, li.opsID, readLock)
}

// RLock - block until read lock is taken or timeout has occurred.
func (li *lockInstance) GetRLock(timeout *dynamicTimeout) (timedOutErr error) {
	lockSource := getSource()
	start := UTCNow()
	readLock := true
	if !li.ns.lock(li.volume, li.path, lockSource, li.opsID, readLock, timeout.Timeout()) {
		timeout.LogFailure()
		return OperationTimedOut{Path: li.path}
	}
	timeout.LogSuccess(UTCNow().Sub(start))
	return
}

// RUnlock - block until read lock is released.
func (li *lockInstance) RUnlock() {
	readLock := true
	li.ns.unlock(li.volume, li.path, li.opsID, readLock)
}

func getSource() string {
	var funcName string
	pc, filename, lineNum, ok := runtime.Caller(2)
	if ok {
		filename = pathutil.Base(filename)
		funcName = strings.TrimPrefix(runtime.FuncForPC(pc).Name(),
			"github.com/minio/minio/cmd.")
	} else {
		filename = "<unknown>"
		lineNum = 0
	}

	return fmt.Sprintf("[%s:%d:%s()]", filename, lineNum, funcName)
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package priorities

import (
	"fmt"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util"
	schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
	schedulercache "k8s.io/kubernetes/pkg/scheduler/cache"
)

// ResourceAllocationPriority contains information to calculate resource allocation priority.
type ResourceAllocationPriority struct {
	Name   string
	scorer func(requested, allocable *schedulercache.Resource, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64
}

// PriorityMap priorities nodes according to the resource allocations on the node.
// It will use `scorer` function to calculate the score.
func (r *ResourceAllocationPriority) PriorityMap(
	pod *v1.Pod,
	meta interface{},
	nodeInfo *schedulercache.NodeInfo) (schedulerapi.HostPriority, error) {
	node := nodeInfo.Node()
	if node == nil {
		return schedulerapi.HostPriority{}, fmt.Errorf("node not found")
	}
	allocatable := nodeInfo.AllocatableResource()

	var requested schedulercache.Resource
	if priorityMeta, ok := meta.(*priorityMetadata); ok {
		requested = *priorityMeta.nonZeroRequest
	} else {
		// We couldn't parse metadata - fallback to computing it.
		requested = *getNonZeroRequests(pod)
	}

	requested.MilliCPU += nodeInfo.NonZeroRequest().MilliCPU
	requested.Memory += nodeInfo.NonZeroRequest().Memory
	var score int64
	// Check if the pod has volumes and this could be added to scorer function for balanced resource allocation.
	if len(pod.Spec.Volumes) >= 0 && nodeInfo.TransientInfo != nil {
		score = r.scorer(&requested, &allocatable, true, nodeInfo.TransientInfo.TransNodeInfo.RequestedVolumes, nodeInfo.TransientInfo.TransNodeInfo.AllocatableVolumesCount)
	} else {
		score = r.scorer(&requested, &allocatable, false, 0, 0)
	}

	if glog.V(10) {
		glog.Infof(
			"%v -> %v: %v, capacity %d millicores %d memory bytes, total request %d millicores %d memory bytes, score %d",
			pod.Name, node.Name, r.Name,
			allocatable.MilliCPU, allocatable.Memory,
			requested.MilliCPU+allocatable.MilliCPU, requested.Memory+allocatable.Memory,
			score,
		)
	}

	return schedulerapi.HostPriority{
		Host:  node.Name,
		Score: int(score),
	}, nil
}

func getNonZeroRequests(pod *v1.Pod) *schedulercache.Resource {
	result := &schedulercache.Resource{}
	for i := range pod.Spec.Containers {
		container := &pod.Spec.Containers[i]
		cpu, memory := priorityutil.GetNonzeroRequests(&container.Resources.Requests)
		result.MilliCPU += cpu
		result.Memory += memory
	}
	return result
}
<commit_msg>Fix incorrect reporting of total request including current pod in the resource allocation priority function.<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package priorities

import (
	"fmt"

	"github.com/golang/glog"
	"k8s.io/api/core/v1"
	priorityutil "k8s.io/kubernetes/pkg/scheduler/algorithm/priorities/util"
	schedulerapi "k8s.io/kubernetes/pkg/scheduler/api"
	schedulercache "k8s.io/kubernetes/pkg/scheduler/cache"
)

// ResourceAllocationPriority contains information to calculate resource allocation priority.
type ResourceAllocationPriority struct {
	Name   string
	scorer func(requested, allocable *schedulercache.Resource, includeVolumes bool, requestedVolumes int, allocatableVolumes int) int64
}

// PriorityMap priorities nodes according to the resource allocations on the node.
// It will use `scorer` function to calculate the score.
func (r *ResourceAllocationPriority) PriorityMap(
	pod *v1.Pod,
	meta interface{},
	nodeInfo *schedulercache.NodeInfo) (schedulerapi.HostPriority, error) {
	node := nodeInfo.Node()
	if node == nil {
		return schedulerapi.HostPriority{}, fmt.Errorf("node not found")
	}
	allocatable := nodeInfo.AllocatableResource()

	var requested schedulercache.Resource
	if priorityMeta, ok := meta.(*priorityMetadata); ok {
		requested = *priorityMeta.nonZeroRequest
	} else {
		// We couldn't parse metadata - fallback to computing it.
		requested = *getNonZeroRequests(pod)
	}

	requested.MilliCPU += nodeInfo.NonZeroRequest().MilliCPU
	requested.Memory += nodeInfo.NonZeroRequest().Memory
	var score int64
	// Check if the pod has volumes and this could be added to scorer function for balanced resource allocation.
	if len(pod.Spec.Volumes) >= 0 && nodeInfo.TransientInfo != nil {
		score = r.scorer(&requested, &allocatable, true, nodeInfo.TransientInfo.TransNodeInfo.RequestedVolumes, nodeInfo.TransientInfo.TransNodeInfo.AllocatableVolumesCount)
	} else {
		score = r.scorer(&requested, &allocatable, false, 0, 0)
	}

	if glog.V(10) {
		glog.Infof(
			"%v -> %v: %v, capacity %d millicores %d memory bytes, total request %d millicores %d memory bytes, score %d",
			pod.Name, node.Name, r.Name,
			allocatable.MilliCPU, allocatable.Memory,
			requested.MilliCPU, requested.Memory,
			score,
		)
	}

	return schedulerapi.HostPriority{
		Host:  node.Name,
		Score: int(score),
	}, nil
}

func getNonZeroRequests(pod *v1.Pod) *schedulercache.Resource {
	result := &schedulercache.Resource{}
	for i := range pod.Spec.Containers {
		container := &pod.Spec.Containers[i]
		cpu, memory := priorityutil.GetNonzeroRequests(&container.Resources.Requests)
		result.MilliCPU += cpu
		result.Memory += memory
	}
	return result
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"

	version "bosun.org/_version"

	"bosun.org/cmd/tsdbrelay/denormalize"
	"bosun.org/collect"
	"bosun.org/metadata"
	"bosun.org/opentsdb"
	"bosun.org/slog"
	"bosun.org/util"
)

var (
	listenAddr      = flag.String("l", ":4242", "Listen address.")
	bosunServer     = flag.String("b", "bosun", "Target Bosun server. Can specify port with host:port.")
	secondaryRelays = flag.String("r", "", "Additional relays to send data to. Intended for secondary data center replication. Only response from primary tsdb server wil be relayed to clients.")
	tsdbServer      = flag.String("t", "", "Target OpenTSDB server. Can specify port with host:port.")
	logVerbose      = flag.Bool("v", false, "enable verbose logging")
	toDenormalize   = flag.String("denormalize", "", "List of metrics to denormalize. Comma seperated list of `metric__tagname__tagname` rules. Will be translated to `__tagvalue.tagvalue.metric`")
	flagVersion     = flag.Bool("version", false, "Prints the version and exits.")

	redisHost = flag.String("redis", "", "redis host for aggregating external counters")
	redisDb   = flag.Int("db", 0, "redis db to use for counters")
)

var (
	tsdbPutURL    string
	bosunIndexURL string

	denormalizationRules map[string]*denormalize.DenormalizationRule

	relayPutUrls []string

	tags = opentsdb.TagSet{}
)

func main() {
	var err error
	myHost, err = os.Hostname()
	if err != nil || myHost == "" {
		myHost = "tsdbrelay"
	}

	flag.Parse()
	if *flagVersion {
		fmt.Println(version.GetVersionInfo("tsdbrelay"))
		os.Exit(0)
	}
	if *bosunServer == "" || *tsdbServer == "" {
		slog.Fatal("must specify both bosun and tsdb server")
	}
	slog.Infoln(version.GetVersionInfo("tsdbrelay"))
	slog.Infoln("listen on", *listenAddr)
	slog.Infoln("relay to bosun at", *bosunServer)
	slog.Infoln("relay to tsdb at", *tsdbServer)
	if *toDenormalize != "" {
		var err error
		denormalizationRules, err = denormalize.ParseDenormalizationRules(*toDenormalize)
		if err != nil {
			slog.Fatal(err)
		}
	}

	tsdbURL := &url.URL{
		Scheme: "http",
		Host:   *tsdbServer,
	}

	u := url.URL{
		Scheme: "http",
		Host:   *tsdbServer,
		Path:   "/api/put",
	}
	tsdbPutURL = u.String()
	bosunURL := &url.URL{
		Scheme: "http",
		Host:   *bosunServer,
	}
	u = url.URL{
		Scheme: "http",
		Host:   *bosunServer,
		Path:   "/api/index",
	}
	bosunIndexURL = u.String()
	if *secondaryRelays != "" {
		for _, rUrl := range strings.Split(*secondaryRelays, ",") {
			u = url.URL{
				Scheme: "http",
				Host:   rUrl,
				Path:   "/api/put",
			}
			relayPutUrls = append(relayPutUrls, u.String())
		}
	}

	tsdbProxy := util.NewSingleHostProxy(tsdbURL)
	bosunProxy := util.NewSingleHostProxy(bosunURL)
	rp := &relayProxy{
		TSDBProxy:  tsdbProxy,
		BosunProxy: bosunProxy,
	}
	http.HandleFunc("/api/put", func(w http.ResponseWriter, r *http.Request) {
		rp.relayPut(w, r, true)
	})
	if *redisHost != "" {
		http.HandleFunc("/api/count", collect.HandleCounterPut(*redisHost, *redisDb))
	}
	http.HandleFunc("/api/metadata/put", func(w http.ResponseWriter, r *http.Request) {
		rp.relayMetadata(w, r)
	})
	http.Handle("/", tsdbProxy)

	collectUrl := &url.URL{
		Scheme: "http",
		Host:   *listenAddr,
		Path:   "/api/put",
	}
	if err = collect.Init(collectUrl, "tsdbrelay"); err != nil {
		slog.Fatal(err)
	}
	if err := metadata.Init(collectUrl, false); err != nil {
		slog.Fatal(err)
	}
	// Make sure these get zeroed out instead of going unknown on restart
	collect.Add("puts.relayed", tags, 0)
	collect.Add("puts.error", tags, 0)
	collect.Add("metadata.relayed", tags, 0)
	collect.Add("metadata.error", tags, 0)
	collect.Add("additional.puts.relayed", tags, 0)
	collect.Add("additional.puts.error", tags, 0)
	metadata.AddMetricMeta("tsdbrelay.puts.relayed", metadata.Counter, metadata.Count, "Number of successful puts relayed to opentsdb target")
	metadata.AddMetricMeta("tsdbrelay.puts.error", metadata.Counter, metadata.Count, "Number of puts that could not be relayed to opentsdb target")
	metadata.AddMetricMeta("tsdbrelay.metadata.relayed", metadata.Counter, metadata.Count, "Number of successful metadata puts relayed to bosun target")
	metadata.AddMetricMeta("tsdbrelay.metadata.error", metadata.Counter, metadata.Count, "Number of metadata puts that could not be relayed to bosun target")
	metadata.AddMetricMeta("tsdbrelay.additional.puts.relayed", metadata.Counter, metadata.Count, "Number of successful puts relayed to additional targets")
	metadata.AddMetricMeta("tsdbrelay.additional.puts.error", metadata.Counter, metadata.Count, "Number of puts that could not be relayed to additional targets")
	slog.Fatal(http.ListenAndServe(*listenAddr, nil))
}

func verbose(format string, a ...interface{}) {
	if *logVerbose {
		slog.Infof(format, a...)
	}
}

type relayProxy struct {
	TSDBProxy  *httputil.ReverseProxy
	BosunProxy *httputil.ReverseProxy
}

type passthru struct {
	io.ReadCloser
	buf bytes.Buffer
}

func (p *passthru) Read(b []byte) (int, error) {
	n, err := p.ReadCloser.Read(b)
	p.buf.Write(b[:n])
	return n, err
}

type relayWriter struct {
	http.ResponseWriter
	code int
}

func (rw *relayWriter) WriteHeader(code int) {
	rw.code = code
	rw.ResponseWriter.WriteHeader(code)
}

var (
	relayHeader = "X-Relayed-From"
	myHost      string
)

func (rp *relayProxy) relayPut(responseWriter http.ResponseWriter, r *http.Request, parse bool) {
	isRelayed := r.Header.Get(relayHeader) != ""
	reader := &passthru{ReadCloser: r.Body}
	r.Body = reader
	w := &relayWriter{ResponseWriter: responseWriter}
	rp.TSDBProxy.ServeHTTP(w, r)
	if w.code/100 != 2 {
		verbose("relayPut got status %d", w.code)
		collect.Add("puts.error", tags, 1)
		return
	}
	verbose("relayed to tsdb")
	collect.Add("puts.relayed", tags, 1)
	// Send to bosun in a separate go routine so we can end the source's request.
	go func() {
		body := bytes.NewBuffer(reader.buf.Bytes())
		req, err := http.NewRequest(r.Method, bosunIndexURL, body)
		if err != nil {
			verbose("bosun connect error: %v", err)
			return
		}
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			verbose("bosun relay error: %v", err)
			return
		}
		resp.Body.Close()
		verbose("bosun relay success")
	}()
	// Parse and denormalize datapoints
	if !isRelayed && parse && denormalizationRules != nil {
		go rp.denormalize(bytes.NewReader(reader.buf.Bytes()))
	}

	if !isRelayed && len(relayPutUrls) > 0 {
		go func() {
			for _, relayUrl := range relayPutUrls {
				body := bytes.NewBuffer(reader.buf.Bytes())
				req, err := http.NewRequest(r.Method, relayUrl, body)
				if err != nil {
					verbose("relayPutUrls connect error: %v", err)
					return
				}
				req.Header.Set("Content-Type", "application/json")
				req.Header.Set("Content-Encoding", "gzip")
				req.Header.Add(relayHeader, myHost)
				resp, err := http.DefaultClient.Do(req)
				if err != nil {
					verbose("secondary relay error: %v", err)
					collect.Add("puts.additional.error", tags, 1)
					return
				}
				resp.Body.Close()
				verbose("secondary relay success")
				collect.Add("puts.additional.relayed", tags, 1)
			}
		}()
	}
}

func (rp *relayProxy) denormalize(body io.Reader) {
	gReader, err := gzip.NewReader(body)
	if err != nil {
		verbose("error making gzip reader: %v", err)
		return
	}
	decoder := json.NewDecoder(gReader)
	dps := []*opentsdb.DataPoint{}
	err = decoder.Decode(&dps)
	if err != nil {
		verbose("error decoding data points: %v", err)
		return
	}
	relayDps := []*opentsdb.DataPoint{}
	for _, dp := range dps {
		if rule, ok := denormalizationRules[dp.Metric]; ok {
			if err = rule.Translate(dp); err == nil {
				relayDps = append(relayDps, dp)
			} else {
				verbose("error translating points: %v", err.Error())
			}
		}
	}
	if len(relayDps) == 0 {
		return
	}
	buf := &bytes.Buffer{}
	gWriter := gzip.NewWriter(buf)
	encoder := json.NewEncoder(gWriter)
	err = encoder.Encode(relayDps)
	if err != nil {
		verbose("error encoding denormalized data points: %v", err)
		return
	}
	if err = gWriter.Close(); err != nil {
		verbose("error zipping denormalized data points: %v", err)
		return
	}
	req, err := http.NewRequest("POST", tsdbPutURL, buf)
	if err != nil {
		verbose("error posting denormalized data points: %v", err)
		return
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Content-Encoding", "gzip")

	responseWriter := httptest.NewRecorder()
	rp.relayPut(responseWriter, req, false)

	verbose("relayed %d denormalized data points. Tsdb response: %d", len(relayDps), responseWriter.Code)
}

func (rp *relayProxy) relayMetadata(responseWriter http.ResponseWriter, r *http.Request) {
	reader := &passthru{ReadCloser: r.Body}
	r.Body = reader
	w := &relayWriter{ResponseWriter: responseWriter}
	rp.BosunProxy.ServeHTTP(w, r)
	if w.code != 204 {
		verbose("relayMetadata got status %d", w.code)
		collect.Add("metadata.error", tags, 1)
		return
	}
	verbose("relayed metadata to bosun")
	collect.Add("metadata.relayed", tags, 1)
	if r.Header.Get(relayHeader) != "" {
		return
	}
	if len(relayPutUrls) != 0 {
		go func() {
			for _, relayUrl := range relayPutUrls {
				relayUrl = strings.Replace(relayUrl, "/put", "/metadata/put", 1)
				body := bytes.NewBuffer(reader.buf.Bytes())
				req, err := http.NewRequest(r.Method, relayUrl, body)
				if err != nil {
					verbose("metadata relayPutUrls error %v", err)
					return
				}
				req.Header.Set("Content-Type", "application/json")
				req.Header.Add(relayHeader, myHost)
				resp, err := http.DefaultClient.Do(req)
				if err != nil {
					verbose("secondary relay metadata error: %v", err)
					return
				}
				resp.Body.Close()
				verbose("secondary relay metadata success")
			}
		}()
	}
}
<commit_msg>cmd/tsdbrelay: include connection failures in additional.error metric. Also continue additional relay urls on failure.<commit_after>package main

import (
	"bytes"
	"compress/gzip"
	"encoding/json"
	"flag"
	"fmt"
	"io"
	"net/http"
	"net/http/httptest"
	"net/http/httputil"
	"net/url"
	"os"
	"strings"

	version "bosun.org/_version"

	"bosun.org/cmd/tsdbrelay/denormalize"
	"bosun.org/collect"
	"bosun.org/metadata"
	"bosun.org/opentsdb"
	"bosun.org/slog"
	"bosun.org/util"
)

var (
	listenAddr      = flag.String("l", ":4242", "Listen address.")
	bosunServer     = flag.String("b", "bosun", "Target Bosun server. Can specify port with host:port.")
	secondaryRelays = flag.String("r", "", "Additional relays to send data to. Intended for secondary data center replication. Only response from primary tsdb server wil be relayed to clients.")
	tsdbServer      = flag.String("t", "", "Target OpenTSDB server. Can specify port with host:port.")
	logVerbose      = flag.Bool("v", false, "enable verbose logging")
	toDenormalize   = flag.String("denormalize", "", "List of metrics to denormalize. Comma seperated list of `metric__tagname__tagname` rules. Will be translated to `__tagvalue.tagvalue.metric`")
	flagVersion     = flag.Bool("version", false, "Prints the version and exits.")

	redisHost = flag.String("redis", "", "redis host for aggregating external counters")
	redisDb   = flag.Int("db", 0, "redis db to use for counters")
)

var (
	tsdbPutURL    string
	bosunIndexURL string

	denormalizationRules map[string]*denormalize.DenormalizationRule

	relayPutUrls []string

	tags = opentsdb.TagSet{}
)

func main() {
	var err error
	myHost, err = os.Hostname()
	if err != nil || myHost == "" {
		myHost = "tsdbrelay"
	}

	flag.Parse()
	if *flagVersion {
		fmt.Println(version.GetVersionInfo("tsdbrelay"))
		os.Exit(0)
	}
	if *bosunServer == "" || *tsdbServer == "" {
		slog.Fatal("must specify both bosun and tsdb server")
	}
	slog.Infoln(version.GetVersionInfo("tsdbrelay"))
	slog.Infoln("listen on", *listenAddr)
	slog.Infoln("relay to bosun at", *bosunServer)
	slog.Infoln("relay to tsdb at", *tsdbServer)
	if *toDenormalize != "" {
		var err error
		denormalizationRules, err = denormalize.ParseDenormalizationRules(*toDenormalize)
		if err != nil {
			slog.Fatal(err)
		}
	}

	tsdbURL := &url.URL{
		Scheme: "http",
		Host:   *tsdbServer,
	}

	u := url.URL{
		Scheme: "http",
		Host:   *tsdbServer,
		Path:   "/api/put",
	}
	tsdbPutURL = u.String()
	bosunURL := &url.URL{
		Scheme: "http",
		Host:   *bosunServer,
	}
	u = url.URL{
		Scheme: "http",
		Host:   *bosunServer,
		Path:   "/api/index",
	}
	bosunIndexURL = u.String()
	if *secondaryRelays != "" {
		for _, rUrl := range strings.Split(*secondaryRelays, ",") {
			u = url.URL{
				Scheme: "http",
				Host:   rUrl,
				Path:   "/api/put",
			}
			relayPutUrls = append(relayPutUrls, u.String())
		}
	}

	tsdbProxy := util.NewSingleHostProxy(tsdbURL)
	bosunProxy := util.NewSingleHostProxy(bosunURL)
	rp := &relayProxy{
		TSDBProxy:  tsdbProxy,
		BosunProxy: bosunProxy,
	}
	http.HandleFunc("/api/put", func(w http.ResponseWriter, r *http.Request) {
		rp.relayPut(w, r, true)
	})
	if *redisHost != "" {
		http.HandleFunc("/api/count", collect.HandleCounterPut(*redisHost, *redisDb))
	}
	http.HandleFunc("/api/metadata/put", func(w http.ResponseWriter, r *http.Request) {
		rp.relayMetadata(w, r)
	})
	http.Handle("/", tsdbProxy)

	collectUrl := &url.URL{
		Scheme: "http",
		Host:   *listenAddr,
		Path:   "/api/put",
	}
	if err = collect.Init(collectUrl, "tsdbrelay"); err != nil {
		slog.Fatal(err)
	}
	if err := metadata.Init(collectUrl, false); err != nil {
		slog.Fatal(err)
	}
	// Make sure these get zeroed out instead of going unknown on restart
	collect.Add("puts.relayed", tags, 0)
	collect.Add("puts.error", tags, 0)
	collect.Add("metadata.relayed", tags, 0)
	collect.Add("metadata.error", tags, 0)
	collect.Add("additional.puts.relayed", tags, 0)
	collect.Add("additional.puts.error", tags, 0)
	metadata.AddMetricMeta("tsdbrelay.puts.relayed", metadata.Counter, metadata.Count, "Number of successful puts relayed to opentsdb target")
	metadata.AddMetricMeta("tsdbrelay.puts.error", metadata.Counter, metadata.Count, "Number of puts that could not be relayed to opentsdb target")
	metadata.AddMetricMeta("tsdbrelay.metadata.relayed", metadata.Counter, metadata.Count, "Number of successful metadata puts relayed to bosun target")
	metadata.AddMetricMeta("tsdbrelay.metadata.error", metadata.Counter, metadata.Count, "Number of metadata puts that could not be relayed to bosun target")
	metadata.AddMetricMeta("tsdbrelay.additional.puts.relayed", metadata.Counter, metadata.Count, "Number of successful puts relayed to additional targets")
	metadata.AddMetricMeta("tsdbrelay.additional.puts.error", metadata.Counter, metadata.Count, "Number of puts that could not be relayed to additional targets")
	slog.Fatal(http.ListenAndServe(*listenAddr, nil))
}

func verbose(format string, a ...interface{}) {
	if *logVerbose {
		slog.Infof(format, a...)
	}
}

type relayProxy struct {
	TSDBProxy  *httputil.ReverseProxy
	BosunProxy *httputil.ReverseProxy
}

type passthru struct {
	io.ReadCloser
	buf bytes.Buffer
}

func (p *passthru) Read(b []byte) (int, error) {
	n, err := p.ReadCloser.Read(b)
	p.buf.Write(b[:n])
	return n, err
}

type relayWriter struct {
	http.ResponseWriter
	code int
}

func (rw *relayWriter) WriteHeader(code int) {
	rw.code = code
	rw.ResponseWriter.WriteHeader(code)
}

var (
	relayHeader = "X-Relayed-From"
	myHost      string
)

func (rp *relayProxy) relayPut(responseWriter http.ResponseWriter, r *http.Request, parse bool) {
	isRelayed := r.Header.Get(relayHeader) != ""
	reader := &passthru{ReadCloser: r.Body}
	r.Body = reader
	w := &relayWriter{ResponseWriter: responseWriter}
	rp.TSDBProxy.ServeHTTP(w, r)
	if w.code/100 != 2 {
		verbose("relayPut got status %d", w.code)
		collect.Add("puts.error", tags, 1)
		return
	}
	verbose("relayed to tsdb")
	collect.Add("puts.relayed", tags, 1)
	// Send to bosun in a separate go routine so we can end the source's request.
	go func() {
		body := bytes.NewBuffer(reader.buf.Bytes())
		req, err := http.NewRequest(r.Method, bosunIndexURL, body)
		if err != nil {
			verbose("bosun connect error: %v", err)
			return
		}
		resp, err := http.DefaultClient.Do(req)
		if err != nil {
			verbose("bosun relay error: %v", err)
			return
		}
		resp.Body.Close()
		verbose("bosun relay success")
	}()
	// Parse and denormalize datapoints
	if !isRelayed && parse && denormalizationRules != nil {
		go rp.denormalize(bytes.NewReader(reader.buf.Bytes()))
	}

	if !isRelayed && len(relayPutUrls) > 0 {
		go func() {
			for _, relayUrl := range relayPutUrls {
				body := bytes.NewBuffer(reader.buf.Bytes())
				req, err := http.NewRequest(r.Method, relayUrl, body)
				if err != nil {
					verbose("relayPutUrls connect error: %v", err)
					collect.Add("puts.additional.error", tags, 1)
					continue
				}
				req.Header.Set("Content-Type", "application/json")
				req.Header.Set("Content-Encoding", "gzip")
				req.Header.Add(relayHeader, myHost)
				resp, err := http.DefaultClient.Do(req)
				if err != nil {
					verbose("secondary relay error: %v", err)
					collect.Add("puts.additional.error", tags, 1)
					continue
				}
				resp.Body.Close()
				verbose("secondary relay success")
				collect.Add("puts.additional.relayed", tags, 1)
			}
		}()
	}
}

func (rp *relayProxy) denormalize(body io.Reader) {
	gReader, err := gzip.NewReader(body)
	if err != nil {
		verbose("error making gzip reader: %v", err)
		return
	}
	decoder := json.NewDecoder(gReader)
	dps := []*opentsdb.DataPoint{}
	err = decoder.Decode(&dps)
	if err != nil {
		verbose("error decoding data points: %v", err)
		return
	}
	relayDps := []*opentsdb.DataPoint{}
	for _, dp := range dps {
		if rule, ok := denormalizationRules[dp.Metric]; ok {
			if err = rule.Translate(dp); err == nil {
				relayDps = append(relayDps, dp)
			} else {
				verbose("error translating points: %v", err.Error())
			}
		}
	}
	if len(relayDps) == 0 {
		return
	}
	buf := &bytes.Buffer{}
	gWriter := gzip.NewWriter(buf)
	encoder := json.NewEncoder(gWriter)
	err = encoder.Encode(relayDps)
	if err != nil {
		verbose("error encoding denormalized data points: %v", err)
		return
	}
	if err = gWriter.Close(); err != nil {
		verbose("error zipping denormalized data points: %v", err)
		return
	}
	req, err := http.NewRequest("POST", tsdbPutURL, buf)
	if err != nil {
		verbose("error posting denormalized data points: %v", err)
		return
	}
	req.Header.Set("Content-Type", "application/json")
	req.Header.Set("Content-Encoding", "gzip")

	responseWriter := httptest.NewRecorder()
	rp.relayPut(responseWriter, req, false)

	verbose("relayed %d denormalized data points. Tsdb response: %d", len(relayDps), responseWriter.Code)
}

func (rp *relayProxy) relayMetadata(responseWriter http.ResponseWriter, r *http.Request) {
	reader := &passthru{ReadCloser: r.Body}
	r.Body = reader
	w := &relayWriter{ResponseWriter: responseWriter}
	rp.BosunProxy.ServeHTTP(w, r)
	if w.code != 204 {
		verbose("relayMetadata got status %d", w.code)
		collect.Add("metadata.error", tags, 1)
		return
	}
	verbose("relayed metadata to bosun")
	collect.Add("metadata.relayed", tags, 1)
	if r.Header.Get(relayHeader) != "" {
		return
	}
	if len(relayPutUrls) != 0 {
		go func() {
			for _, relayUrl := range relayPutUrls {
				relayUrl = strings.Replace(relayUrl, "/put", "/metadata/put", 1)
				body := bytes.NewBuffer(reader.buf.Bytes())
				req, err := http.NewRequest(r.Method, relayUrl, body)
				if err != nil {
					verbose("metadata relayPutUrls error %v", err)
					continue
				}
				req.Header.Set("Content-Type", "application/json")
				req.Header.Add(relayHeader, myHost)
				resp, err := http.DefaultClient.Do(req)
				if err != nil {
					verbose("secondary relay metadata error: %v", err)
					continue
				}
				resp.Body.Close()
				verbose("secondary relay metadata success")
			}
		}()
	}
}
<|endoftext|>


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

// Package vpp-agent implements the main entry point into the VPP Agent
// and it is used to build the VPP Agent executable.
package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ligato/cn-infra/agent"
	"github.com/ligato/cn-infra/logging"

	"github.com/ligato/vpp-agent/cmd/vpp-agent/app"
)

const logo = `                                      __
 _  _____  ___ _______ ____ ____ ___ / /_
| |/ / _ \/ _ /___/ _ '/ _ '/ -_/ _ / __/
|___/ .__/ .__/   \_'_/\_' /\__/_//_\__/  %s
   /_/  /_/           /___/

`

var (
	startTimeout = agent.DefaultStartTimeout
	stopTimeout  = agent.DefaultStopTimeout
	// FIXME: global flags are not currently compatible with config package (flags are parsed in NewAgent)
	//startTimeout = flag.Duration("start-timeout", agent.DefaultStartTimeout, "Timeout duration for starting agent.")
	//stopTimeout  = flag.Duration("stop-timeout", agent.DefaultStopTimeout, "Timeout duration for stopping agent.")
)

var debugging func() func()

func main() {
	fmt.Fprintf(os.Stdout, logo, agent.BuildVersion)

	if debugging != nil {
		defer debugging()()
	}

	vppAgent := app.New()
	a := agent.NewAgent(
		agent.AllPlugins(vppAgent),
		agent.StartTimeout(startTimeout),
		agent.StopTimeout(stopTimeout),
	)

	if err := a.Run(); err != nil {
		logging.DefaultLogger.Fatal(err)
	}
}

func init() {
	logging.DefaultLogger.SetOutput(os.Stdout)
	logging.DefaultLogger.SetLevel(logging.DebugLevel)
	// Setup start/stop timeouts for agent
	if t := os.Getenv("START_TIMEOUT"); t != "" {
		dur, err := time.ParseDuration(t)
		if err != nil {
			log.Fatalf("Invalid duration (%s) for start timeout!", t)
		} else {
			startTimeout = dur
		}
	}
	if t := os.Getenv("STOP_TIMEOUT"); t != "" {
		dur, err := time.ParseDuration(t)
		if err != nil {
			log.Fatalf("Invalid duration (%s) for stop timeout!", t)
		} else {
			stopTimeout = dur
		}
	}
}
<commit_msg>Add env var (RESYNC_TIMEOUT) for setting resync timeout (#1372)<commit_after>// Copyright (c) 2017 Cisco and/or its affiliates.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at:
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package vpp-agent implements the main entry point into the VPP Agent
// and it is used to build the VPP Agent executable.
package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/ligato/cn-infra/agent"
	"github.com/ligato/cn-infra/datasync/kvdbsync"
	"github.com/ligato/cn-infra/datasync/resync"
	"github.com/ligato/cn-infra/logging"

	"github.com/ligato/vpp-agent/cmd/vpp-agent/app"
)

const logo = `                                      __
 _  _____  ___ _______ ____ ____ ___ / /_
| |/ / _ \/ _ /___/ _ '/ _ '/ -_/ _ / __/
|___/ .__/ .__/   \_'_/\_' /\__/_//_\__/  %s
   /_/  /_/           /___/

`

var (
	startTimeout = agent.DefaultStartTimeout
	stopTimeout  = agent.DefaultStopTimeout

	resyncTimeout = time.Second * 10
)

var debugging func() func()

func main() {
	fmt.Fprintf(os.Stdout, logo, agent.BuildVersion)

	if debugging != nil {
		defer debugging()()
	}

	vppAgent := app.New()
	a := agent.NewAgent(
		agent.AllPlugins(vppAgent),
		agent.StartTimeout(startTimeout),
		agent.StopTimeout(stopTimeout),
	)

	if err := a.Run(); err != nil {
		logging.DefaultLogger.Fatal(err)
	}
}

func init() {
	logging.DefaultLogger.SetOutput(os.Stdout)
	logging.DefaultLogger.SetLevel(logging.DebugLevel)

	// Overrides for start/stop timeouts of agent
	if t := os.Getenv("START_TIMEOUT"); t != "" {
		dur, err := time.ParseDuration(t)
		if err != nil {
			log.Fatalf("Invalid duration (%s) for start timeout!", t)
		} else {
			log.Printf("setting agent start timeout to: %v (via START_TIMEOUT)", dur)
			startTimeout = dur
		}
	}
	if t := os.Getenv("STOP_TIMEOUT"); t != "" {
		dur, err := time.ParseDuration(t)
		if err != nil {
			log.Fatalf("Invalid duration (%s) for stop timeout!", t)
		} else {
			log.Printf("setting agent stop timeout to: %v (via STOP_TIMEOUT)", dur)
			stopTimeout = dur
		}
	}

	// Override resync timeouts
	if t := os.Getenv("RESYNC_TIMEOUT"); t != "" {
		dur, err := time.ParseDuration(t)
		if err != nil {
			log.Fatalf("Invalid duration (%s) for resync timeout!", t)
		} else {
			log.Printf("setting resync timeout to: %v (via RESYNC_TIMEOUT)", dur)
			resyncTimeout = dur
		}
	}
	kvdbsync.ResyncDoneTimeout = resyncTimeout
	resync.SingleResyncAckTimeout = resyncTimeout
}
<|endoftext|>


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

package player

// #cgo pkg-config: mpv
// #include <mpv/client.h>
import "C"

import "fmt"
import "log"
import "strconv"
import "sync"

import "github.com/vaughan0/go-ini"

import "library"
import "notify"

type Event byte;
type Status byte;

const (
	StatusStarting Status = iota;
	StatusPlaying;
	StatusPaused;
	StatusStopped;
);

func (s Status) String() string {
	switch s {
		case StatusStarting:
			return "start";

		case StatusPlaying:
			return "play";

		case StatusPaused:
			return "pause";

		case StatusStopped:
			return "stop";
	}

	return "invalid";
}

type Player struct {
	handle *C.mpv_handle;
	Status Status;

	library string;
	notify  bool;

	HandleStatusChange func();
	HandleTrackChange func();

	Wait sync.WaitGroup;
}

func (p *Player) ChangeStatus(status Status) {
	p.Status = status;
	p.HandleStatusChange();
}

func (p *Player) Play() error {
	switch p.Status {
		case StatusStarting, StatusPlaying:
			return nil;

		case StatusStopped:
			count, err := p.GetProperty("playlist-count");
			if err == nil && count.(int64) > 0 {
				return p.GotoTrack(0);
			}

			return p.AddTrack("", true);

		case StatusPaused:
			return p.SetProperty("pause", false);
	}

	return fmt.Errorf("Invalid player state");
}

func (p *Player) Pause() error {
	switch p.Status {
		case StatusStarting, StatusPaused, StatusStopped:
			return nil;

		case StatusPlaying:
			return p.SetProperty("pause", true);
	}

	return fmt.Errorf("Invalid player state");
}

func (p *Player) Toggle() error {
	switch p.Status {
		case StatusStarting:
			return nil;

		case StatusPaused, StatusStopped:
			return p.Play();

		case StatusPlaying:
			return p.Pause();
	}

	return fmt.Errorf("Invalid player state");
}

func (p *Player) Next() error {
	return p.Command([]string{"playlist_next", "force"})
}

func (p *Player) Prev() error {
	return p.Command([]string{"playlist_prev", "weak"})
}

func (p *Player) Stop() error {
	err := p.Command([]string{"playlist_prev", "weak"})

	p.HandleTrackChange();

	return err;
}

func (p *Player) Seek(seconds int64) error {
	secs := strconv.FormatInt(seconds, 10);
	return p.Command([]string{"seek", secs});
}

func (p *Player) List() ([]string, int64, int64, error) {
	count, err := p.GetProperty("playlist-count");
	if err != nil {
		return nil, 0, -1, nil;
	}

	pos, err := p.GetProperty("playlist-pos");
	if err != nil {
		return nil, 0, -1, nil;
	}

	playlist, err := p.GetProperty("playlist");
	if err != nil {
		return nil, 0, -1, nil;
	}

	var files []string;

	for _, entry := range playlist.([]interface{}) {
		if entry == nil {
			continue;
		}

		entry_map := entry.(map[string]interface{});
		files = append(files, entry_map["filename"].(string));
	}

	return files, count.(int64), pos.(int64), nil;
}

func (p *Player) AddTrack(path string, play bool) error {
	var mode string;

	if play {
		mode = "append-play";
	} else {
		mode = "append";
	}

	if path == "" {
		var err error;
		path, err = library.Random(p.library);
		if err != nil {
			return fmt.Errorf("Could not get random track: %s",err);
		}
	}

	return p.Command([]string{"loadfile", path, mode});
}

func (p *Player) AddList(path string) error {
	return p.Command([]string{"loadlist", path, "append"});
}

func (p *Player) GotoTrack(index int64) error {
	return p.SetProperty("playlist-pos", index);
}

func (p *Player) RemoveTrack(index int64) error {
	var track string;

	if index < 0 {
		track = "current";
	} else {
		track = strconv.FormatInt(index, 10);
	}

	return p.Command([]string{"playlist_remove", track});
}

func (p *Player) Quit() error {
	return p.Command([]string{"quit"});
}

func (p *Player) GetTrackMetadata() (map[string]string, error) {
	metadata, err := p.GetProperty("metadata");

	metadata_str := map[string]string{};

	for key, val := range metadata.(map[string]interface{}) {
		metadata_str[key] = val.(string);
	}

	return metadata_str, err;
}

func (p *Player) GetTrackLength() (float64, error) {
	length, err := p.GetProperty("length");
	if err != nil {
		return 0.0, nil;
	}

	return length.(float64), nil;
}

func (p *Player) GetTrackPath() (string, error) {
	path, err := p.GetProperty("path");
	return path.(string), err;
}

func (p *Player) GetTrackPosition(percent bool) (float64, error) {
	var err error;
	var pos interface{};

	if !percent {
		pos, err = p.GetProperty("time-pos");
	} else {
		pos, err = p.GetProperty("percent-pos");
	}

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

	return pos.(float64), nil;
}

func (p *Player) GetTrackTitle() (string, error) {
	title, err := p.GetProperty("media-title");
	if err != nil {
		return "", nil;
	}

	metadata, err := p.GetTrackMetadata();
	if err != nil {
		return title.(string), nil;
	}

	artist := metadata["artist"];
	if artist != "" {
		return fmt.Sprintf("%s - %s", artist, title.(string)), nil;
	}

	return title.(string), nil;
}

func (p *Player) SetLoopStatus(mode string) error {
	switch mode {
		case "none":
			p.SetProperty("loop-file", false);
			p.SetProperty("loop", "no");

		case "track":
			p.SetLoopStatus("none");
			p.SetProperty("loop-file", true);

		case "list":
			p.SetLoopStatus("none");
			p.SetProperty("loop", "inf");

		default:
			return fmt.Errorf("Invalid mode");
	}

	return nil;
}

func Run(cfg ini.File) (*Player, error) {
	p := new(Player);

	p.Status = StatusStarting;

	p.handle = C.mpv_create();
	if p.handle == nil {
		return nil, fmt.Errorf("Could not create player");
	}

	err := p.SetOptionString("no-config", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-config': %s", err);
	}

	err = p.SetOptionString("no-video", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-video': %s", err);
	}

	err = p.SetOptionString("no-sub", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-sub': %s", err);
	}

	err = p.SetOptionString("no-softvol", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-softvol': %s", err);
	}

	if cfg["default"]["cache"] != "" {
		p.SetOptionString("cache", cfg["default"]["cache"]);
	}

	if cfg["default"]["gapless"] != "" {
		p.SetOptionString("gapless-audio", cfg["default"]["gapless"]);
	}

	p.library = cfg["default"]["library"];

	if cfg["default"]["notify"] == "yes" {
		p.notify = true;
	} else {
		p.notify = false;
	}

	if cfg["default"]["replaygain"] != "" {
		rgain_af := fmt.Sprintf("volume=replaygain-%s", cfg["default"]["replaygain"]);
		if cfg["default"]["filters"] != "" {
			cfg["default"]["filters"] += "," + rgain_af;
		} else {
			cfg["default"]["filters"] = rgain_af;
		}
	}

	if cfg["default"]["output"] != "" {
		p.SetOptionString("ao", cfg["default"]["output"]);
	}

	if cfg["default"]["filters"] != "" {
		p.SetOptionString("af", cfg["default"]["filters"]);
	}

	if cfg["default"]["scripts"] != "" {
		p.SetOptionString("lua", cfg["default"]["scripts"]);
	}

	C.mpv_request_log_messages(p.handle, C.CString("warn"));

	mp_err := C.mpv_initialize(p.handle);
	if mp_err != 0 {
		return nil, ErrorString(mp_err);
	}

	p.Wait.Add(1);

	go p.EventLoop();

	return p, nil;
}

func (p *Player) EventLoop() {
	for {
		ev := C.mpv_wait_event(p.handle, -1);
		ev_name := C.GoString(C.mpv_event_name(ev.event_id));

		log.Printf("event: %s", ev_name);

		switch ev_name {
			case "idle":
				if p.Status == StatusStarting {
					p.Status = StatusStopped;
				}

				if p.Status == StatusStopped {
					break;
				}

				err := p.AddTrack("", true);
				if err != nil {
					log.Println(
						"Could not add track: %s", err,
					);

					p.ChangeStatus(StatusStopped);
				}

			case "pause":
				p.ChangeStatus(StatusPaused);

			case "playback-restart", "unpause":
				p.ChangeStatus(StatusPlaying);

			case "metadata-update":
				if p.notify {
					msg, _ := p.GetTrackTitle();
					notify.Notify("Now Playing:", msg,
					              "media-playback-start");
				}

				p.HandleTrackChange();

			case "log-message":
				mp_log := (*C.mpv_event_log_message)(ev.data);
				log.Printf("%s: %s: %s",
				           C.GoString(mp_log.level),
				           C.GoString(mp_log.prefix),
				           C.GoString(mp_log.text));

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

package player

// #cgo pkg-config: mpv
// #include <mpv/client.h>
import "C"

import "fmt"
import "log"
import "strconv"
import "sync"

import "github.com/vaughan0/go-ini"

import "library"
import "notify"

type Event byte;
type Status byte;

const (
	StatusStarting Status = iota;
	StatusPlaying;
	StatusPaused;
	StatusStopped;
);

func (s Status) String() string {
	switch s {
		case StatusStarting:
			return "start";

		case StatusPlaying:
			return "play";

		case StatusPaused:
			return "pause";

		case StatusStopped:
			return "stop";
	}

	return "invalid";
}

type Player struct {
	handle *C.mpv_handle;
	Status Status;

	library string;
	notify  bool;

	HandleStatusChange func();
	HandleTrackChange func();

	Wait sync.WaitGroup;
}

func (p *Player) ChangeStatus(status Status) {
	p.Status = status;
	p.HandleStatusChange();
}

func (p *Player) Play() error {
	switch p.Status {
		case StatusStarting, StatusPlaying:
			return nil;

		case StatusStopped:
			count, err := p.GetProperty("playlist-count");
			if err == nil && count.(int64) > 0 {
				return p.GotoTrack(0);
			}

			return p.AddTrack("", true);

		case StatusPaused:
			return p.SetProperty("pause", false);
	}

	return fmt.Errorf("Invalid player state");
}

func (p *Player) Pause() error {
	switch p.Status {
		case StatusStarting, StatusPaused, StatusStopped:
			return nil;

		case StatusPlaying:
			return p.SetProperty("pause", true);
	}

	return fmt.Errorf("Invalid player state");
}

func (p *Player) Toggle() error {
	switch p.Status {
		case StatusStarting:
			return nil;

		case StatusPaused, StatusStopped:
			return p.Play();

		case StatusPlaying:
			return p.Pause();
	}

	return fmt.Errorf("Invalid player state");
}

func (p *Player) Next() error {
	return p.Command([]string{"playlist_next", "force"})
}

func (p *Player) Prev() error {
	return p.Command([]string{"playlist_prev", "weak"})
}

func (p *Player) Stop() error {
	err := p.Command([]string{"playlist_prev", "weak"})

	p.HandleTrackChange();

	return err;
}

func (p *Player) Seek(seconds int64) error {
	secs := strconv.FormatInt(seconds, 10);
	return p.Command([]string{"seek", secs});
}

func (p *Player) List() ([]string, int64, int64, error) {
	count, err := p.GetProperty("playlist-count");
	if err != nil {
		return nil, 0, -1, nil;
	}

	pos, err := p.GetProperty("playlist-pos");
	if err != nil {
		return nil, 0, -1, nil;
	}

	playlist, err := p.GetProperty("playlist");
	if err != nil {
		return nil, 0, -1, nil;
	}

	var files []string;

	for _, entry := range playlist.([]interface{}) {
		if entry == nil {
			continue;
		}

		entry_map := entry.(map[string]interface{});
		files = append(files, entry_map["filename"].(string));
	}

	return files, count.(int64), pos.(int64), nil;
}

func (p *Player) AddTrack(path string, play bool) error {
	var mode string;

	if play {
		mode = "append-play";
	} else {
		mode = "append";
	}

	if path == "" {
		var err error;
		path, err = library.Random(p.library);
		if err != nil {
			return fmt.Errorf("Could not get random track: %s",err);
		}
	}

	return p.Command([]string{"loadfile", path, mode});
}

func (p *Player) AddList(path string) error {
	return p.Command([]string{"loadlist", path, "append"});
}

func (p *Player) GotoTrack(index int64) error {
	return p.SetProperty("playlist-pos", index);
}

func (p *Player) RemoveTrack(index int64) error {
	var track string;

	if index < 0 {
		track = "current";
	} else {
		track = strconv.FormatInt(index, 10);
	}

	return p.Command([]string{"playlist_remove", track});
}

func (p *Player) Quit() error {
	return p.Command([]string{"quit"});
}

func (p *Player) GetTrackMetadata() (map[string]string, error) {
	metadata, err := p.GetProperty("metadata");

	metadata_str := map[string]string{};

	for key, val := range metadata.(map[string]interface{}) {
		metadata_str[key] = val.(string);
	}

	return metadata_str, err;
}

func (p *Player) GetTrackLength() (float64, error) {
	length, err := p.GetProperty("length");
	if err != nil {
		return 0.0, nil;
	}

	return length.(float64), nil;
}

func (p *Player) GetTrackPath() (string, error) {
	path, err := p.GetProperty("path");
	return path.(string), err;
}

func (p *Player) GetTrackPosition(percent bool) (float64, error) {
	var err error;
	var pos interface{};

	if !percent {
		pos, err = p.GetProperty("time-pos");
	} else {
		pos, err = p.GetProperty("percent-pos");
	}

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

	return pos.(float64), nil;
}

func (p *Player) GetTrackTitle() (string, error) {
	title, err := p.GetProperty("media-title");
	if err != nil {
		return "", nil;
	}

	metadata, err := p.GetTrackMetadata();
	if err != nil {
		return title.(string), nil;
	}

	artist := metadata["artist"];
	if artist != "" {
		return fmt.Sprintf("%s - %s", artist, title.(string)), nil;
	}

	return title.(string), nil;
}

func (p *Player) SetLoopStatus(mode string) error {
	switch mode {
		case "none":
			p.SetProperty("loop-file", false);
			p.SetProperty("loop", "no");

		case "track":
			p.SetLoopStatus("none");
			p.SetProperty("loop-file", true);

		case "list":
			p.SetLoopStatus("none");
			p.SetProperty("loop", "inf");

		default:
			return fmt.Errorf("Invalid mode");
	}

	return nil;
}

func Run(cfg ini.File) (*Player, error) {
	p := new(Player);

	p.Status = StatusStarting;

	p.handle = C.mpv_create();
	if p.handle == nil {
		return nil, fmt.Errorf("Could not create player");
	}

	err := p.SetOptionString("no-config", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-config': %s", err);
	}

	err = p.SetOptionString("no-video", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-video': %s", err);
	}

	err = p.SetOptionString("no-sub", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-sub': %s", err);
	}

	err = p.SetOptionString("no-softvol", "");
	if err != nil {
		return nil, fmt.Errorf("Could not set option 'no-softvol': %s", err);
	}

	if cfg["default"]["cache"] != "" {
		p.SetOptionString("cache", cfg["default"]["cache"]);
	}

	if cfg["default"]["gapless"] != "" {
		p.SetOptionString("gapless-audio", cfg["default"]["gapless"]);
	}

	p.library = cfg["default"]["library"];

	if cfg["default"]["notify"] == "yes" {
		p.notify = true;
	} else {
		p.notify = false;
	}

	if cfg["default"]["replaygain"] != "" {
		rgain_af := fmt.Sprintf("volume=replaygain-%s", cfg["default"]["replaygain"]);
		if cfg["default"]["filters"] != "" {
			cfg["default"]["filters"] += "," + rgain_af;
		} else {
			cfg["default"]["filters"] = rgain_af;
		}
	}

	if cfg["default"]["output"] != "" {
		p.SetOptionString("ao", cfg["default"]["output"]);
	}

	if cfg["default"]["filters"] != "" {
		p.SetOptionString("af", cfg["default"]["filters"]);
	}

	if cfg["default"]["scripts"] != "" {
		p.SetOptionString("lua", cfg["default"]["scripts"]);
	}

	C.mpv_request_log_messages(p.handle, C.CString("warn"));

	mp_err := C.mpv_initialize(p.handle);
	if mp_err != 0 {
		return nil, ErrorString(mp_err);
	}

	p.Wait.Add(1);

	go p.EventLoop();

	return p, nil;
}

func (p *Player) EventLoop() {
	for {
		ev := C.mpv_wait_event(p.handle, -1);
		ev_name := C.GoString(C.mpv_event_name(ev.event_id));

		switch ev_name {
			case "idle":
				if p.Status == StatusStarting {
					p.Status = StatusStopped;
				}

				if p.Status == StatusStopped {
					break;
				}

				err := p.AddTrack("", true);
				if err != nil {
					log.Println(
						"Could not add track: %s", err,
					);

					p.ChangeStatus(StatusStopped);
				}

			case "pause":
				p.ChangeStatus(StatusPaused);

			case "playback-restart", "unpause":
				p.ChangeStatus(StatusPlaying);

			case "metadata-update":
				if p.notify {
					msg, _ := p.GetTrackTitle();
					notify.Notify("Now Playing:", msg,
					              "media-playback-start");
				}

				p.HandleTrackChange();

			case "log-message":
				mp_log := (*C.mpv_event_log_message)(ev.data);
				log.Printf("%s: %s: %s",
				           C.GoString(mp_log.level),
				           C.GoString(mp_log.prefix),
				           C.GoString(mp_log.text));

			case "shutdown":
				p.Wait.Done();
				return;
		}
	}
}
<|endoftext|>


<commit_before>package remote

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/usi-lfkeitel/saturn/src/utils"
)

func GenerateScript(config *utils.Config, modules []string) (string, error) {
	tempFile, err := ioutil.TempFile(config.Core.TempDir, "")
	if err != nil {
		return "", err
	}
	tempFileName := tempFile.Name()

	if config.Core.Debug {
		log.Printf("Generated Script: %s", tempFileName)
	}

	if err := generateRemoteScript(tempFile, config.Core.ModuleDir, modules); err != nil {
		tempFile.Close()
		return tempFileName, err
	}
	tempFile.Close()

	if err := os.Chmod(tempFileName, 0755); err != nil {
		return tempFileName, err
	}

	return tempFileName, nil
}

func generateRemoteScript(file *os.File, modulesDir string, modules []string) error {
	file.WriteString("#!/bin/bash\n\n")

	file.WriteString("MODULES=")
	file.WriteString(`(` + strings.Join(modules, " ") + `)`)

	file.WriteString(`
main() {
	echo -n '{'

	i=1
	for var in "${MODULES[@]}"; do
		echo -n "\"$var\": "
		echo -n $($var)
		if [ $i -lt ${#MODULES[@]} ]; then
				i=$[i + 1]
				echo -n ', '
		fi
	done

	echo -n '}'
}

`)

	goodModules := make(map[string]bool)

	for _, module := range modules {
		moduleFile := filepath.Join(modulesDir, module+".sh")

		m, err := getBinData(moduleFile)
		if err != nil {
			return fmt.Errorf("Module not found %s", module)
		}

		goodModules[module] = true

		file.WriteString(module + "() {\n")
		file.Write(m)
		file.WriteString("\n}\n\n")
	}

	file.WriteString(`main

if [ "$1" = "-d" ]; then
	rm "$0"
fi
`)

	return nil
}

func GetModuleList() []string {
	m := getFileList()
	for i, v := range m {
		m[i] = strings.Split(v, "/")[1]
		m[i] = m[i][:len(m[i])-3]
	}
	return m
}
<commit_msg>Attempt to create temporary directory if it doesn't exist<commit_after>package remote

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/usi-lfkeitel/saturn/src/utils"
)

func GenerateScript(config *utils.Config, modules []string) (string, error) {
	if !utils.FileExists(config.Core.TempDir) {
		if err := os.MkdirAll(config.Core.TempDir, 0755); err != nil {
			return "", err
		}
	}

	tempFile, err := ioutil.TempFile(config.Core.TempDir, "")
	if err != nil {
		return "", err
	}
	tempFileName := tempFile.Name()

	if config.Core.Debug {
		log.Printf("Generated Script: %s", tempFileName)
	}

	if err := generateRemoteScript(tempFile, config.Core.ModuleDir, modules); err != nil {
		tempFile.Close()
		return tempFileName, err
	}
	tempFile.Close()

	if err := os.Chmod(tempFileName, 0755); err != nil {
		return tempFileName, err
	}

	return tempFileName, nil
}

func generateRemoteScript(file *os.File, modulesDir string, modules []string) error {
	file.WriteString("#!/bin/bash\n\n")

	file.WriteString("MODULES=")
	file.WriteString(`(` + strings.Join(modules, " ") + `)`)

	file.WriteString(`
main() {
	echo -n '{'

	i=1
	for var in "${MODULES[@]}"; do
		echo -n "\"$var\": "
		echo -n $($var)
		if [ $i -lt ${#MODULES[@]} ]; then
				i=$[i + 1]
				echo -n ', '
		fi
	done

	echo -n '}'
}

`)

	goodModules := make(map[string]bool)

	for _, module := range modules {
		moduleFile := filepath.Join(modulesDir, module+".sh")

		m, err := getBinData(moduleFile)
		if err != nil {
			return fmt.Errorf("Module not found %s", module)
		}

		goodModules[module] = true

		file.WriteString(module + "() {\n")
		file.Write(m)
		file.WriteString("\n}\n\n")
	}

	file.WriteString(`main

if [ "$1" = "-d" ]; then
	rm "$0"
fi
`)

	return nil
}

func GetModuleList() []string {
	m := getFileList()
	for i, v := range m {
		m[i] = strings.Split(v, "/")[1]
		m[i] = m[i][:len(m[i])-3]
	}
	return m
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package integration

import (
	"fmt"
	"sync"
	"testing"
	"time"

	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/dynamic"
)

func TestChangeCRD(t *testing.T) {
	tearDown, config, _, err := fixtures.StartDefaultServer(t)
	if err != nil {
		t.Fatal(err)
	}
	defer tearDown()
	config.QPS = 1000
	config.Burst = 1000
	apiExtensionsClient, err := clientset.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}

	noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
	noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionsClient, dynamicClient)
	if err != nil {
		t.Fatal(err)
	}

	ns := "default"
	noxuNamespacedResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, "v1beta1")

	stopChan := make(chan struct{})

	wg := &sync.WaitGroup{}

	// Set up loop to modify CRD in the background
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-stopChan:
				return
			default:
			}

			noxuDefinitionToUpdate, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
			if err != nil {
				t.Fatal(err)
			}
			if len(noxuDefinitionToUpdate.Spec.Versions) == 1 {
				v2 := noxuDefinitionToUpdate.Spec.Versions[0]
				v2.Name = "v2"
				v2.Served = true
				v2.Storage = false
				noxuDefinitionToUpdate.Spec.Versions = append(noxuDefinitionToUpdate.Spec.Versions, v2)
			} else {
				noxuDefinitionToUpdate.Spec.Versions = noxuDefinitionToUpdate.Spec.Versions[0:1]
			}
			if _, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(noxuDefinitionToUpdate); err != nil && !apierrors.IsConflict(err) {
				t.Fatal(err)
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()

	// Set up 100 loops creating and reading custom resources
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, fmt.Sprintf("foo-%d", i))
			if _, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{}); err != nil {
				t.Fatal(err)
			}
			for {
				select {
				case <-stopChan:
					return
				default:
					if _, err := noxuNamespacedResourceClient.Get(noxuInstanceToCreate.GetName(), metav1.GetOptions{}); err != nil {
						t.Fatal(err)
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)
	}

	// Let all the established get request loops soak
	time.Sleep(5 * time.Second)

	// Tear down
	close(stopChan)

	// Let loops drain
	wg.Wait()
}
<commit_msg>Add CRD integration test for dropping watches<commit_after>/*
Copyright 2019 The Kubernetes Authors.

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

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

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

package integration

import (
	"fmt"
	"sync"
	"testing"
	"time"

	apiextensionsv1beta1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1beta1"
	"k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
	"k8s.io/apiextensions-apiserver/test/integration/fixtures"
	apierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/apimachinery/pkg/watch"
	"k8s.io/client-go/dynamic"
)

func TestChangeCRD(t *testing.T) {
	tearDown, config, _, err := fixtures.StartDefaultServer(t)
	if err != nil {
		t.Fatal(err)
	}
	defer tearDown()
	config.QPS = 1000
	config.Burst = 1000
	apiExtensionsClient, err := clientset.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}
	dynamicClient, err := dynamic.NewForConfig(config)
	if err != nil {
		t.Fatal(err)
	}

	noxuDefinition := fixtures.NewNoxuCustomResourceDefinition(apiextensionsv1beta1.NamespaceScoped)
	noxuDefinition, err = fixtures.CreateNewCustomResourceDefinition(noxuDefinition, apiExtensionsClient, dynamicClient)
	if err != nil {
		t.Fatal(err)
	}

	ns := "default"
	noxuNamespacedResourceClient := newNamespacedCustomResourceVersionedClient(ns, dynamicClient, noxuDefinition, "v1beta1")

	stopChan := make(chan struct{})

	wg := &sync.WaitGroup{}

	// Set up loop to modify CRD in the background
	wg.Add(1)
	go func() {
		defer wg.Done()
		for {
			select {
			case <-stopChan:
				return
			default:
			}

			noxuDefinitionToUpdate, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Get(noxuDefinition.Name, metav1.GetOptions{})
			if err != nil {
				t.Fatal(err)
			}
			if len(noxuDefinitionToUpdate.Spec.Versions) == 1 {
				v2 := noxuDefinitionToUpdate.Spec.Versions[0]
				v2.Name = "v2"
				v2.Served = true
				v2.Storage = false
				noxuDefinitionToUpdate.Spec.Versions = append(noxuDefinitionToUpdate.Spec.Versions, v2)
			} else {
				noxuDefinitionToUpdate.Spec.Versions = noxuDefinitionToUpdate.Spec.Versions[0:1]
			}
			if _, err := apiExtensionsClient.ApiextensionsV1beta1().CustomResourceDefinitions().Update(noxuDefinitionToUpdate); err != nil && !apierrors.IsConflict(err) {
				t.Fatal(err)
			}
			time.Sleep(10 * time.Millisecond)
		}
	}()

	// Set up 100 loops creating and reading and watching custom resources
	for i := 0; i < 100; i++ {
		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			noxuInstanceToCreate := fixtures.NewNoxuInstance(ns, fmt.Sprintf("foo-%d", i))
			if _, err := noxuNamespacedResourceClient.Create(noxuInstanceToCreate, metav1.CreateOptions{}); err != nil {
				t.Fatal(err)
			}
			for {
				select {
				case <-stopChan:
					return
				default:
					if _, err := noxuNamespacedResourceClient.Get(noxuInstanceToCreate.GetName(), metav1.GetOptions{}); err != nil {
						t.Fatal(err)
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)

		wg.Add(1)
		go func(i int) {
			defer wg.Done()
			for {
				select {
				case <-stopChan:
					return
				default:
					w, err := noxuNamespacedResourceClient.Watch(metav1.ListOptions{})
					if err != nil {
						t.Fatalf("unexpected error establishing watch: %v", err)
					}
					for event := range w.ResultChan() {
						switch event.Type {
						case watch.Added, watch.Modified, watch.Deleted:
							// all expected
						default:
							t.Errorf("unexpected watch event: %#v", event)
						}
					}
				}
				time.Sleep(10 * time.Millisecond)
			}
		}(i)
	}

	// Let all the established get request loops soak
	time.Sleep(5 * time.Second)

	// Tear down
	close(stopChan)

	// Let loops drain
	drained := make(chan struct{})
	go func() {
		defer close(drained)
		wg.Wait()
	}()

	select {
	case <-drained:
	case <-time.After(wait.ForeverTestTimeout):
		t.Error("timed out waiting for clients to complete")
	}
}
<|endoftext|>


<commit_before>package goboots

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"encoding/xml"
	"github.com/gabstv/i18ngo"
	"io"
	"log"
	"net/http"
	"reflect"
	"text/template"
)

const (
	outPre          = 0
	outJSON         = 1
	outXML          = 2
	outTemplate     = 3
	outTemplateSolo = 4
	outString       = 5
	outBytes        = 6
)

type In struct {
	R             *http.Request
	W             http.ResponseWriter
	URLParts      []string
	session       *Session
	Content       *InContent
	LayoutContent *InContent
	App           *App
	Controller    IController
	LangCode      string
	GlobalTitle   string
	closers       []io.Closer
}

// New clones a new In but without the content.
// Useful to render separate parts
func (in *In) New() *In {
	in2 := &In{}
	in2.R = in.R
	in2.W = in.W
	in2.URLParts = in.URLParts
	in2.session = in.session
	in2.Content = &InContent{}
	in2.LayoutContent = &InContent{}
	in2.App = in.App
	in2.Controller = in.Controller
	in2.LangCode = in.LangCode
	in2.GlobalTitle = in.GlobalTitle
	return in2
}

func (in *In) closeall() {
	if in.closers == nil {
		return
	}
	for _, v := range in.closers {
		v.Close()
	}
}

type InContent struct {
	vals map[string]interface{}
}

func (c *InContent) init() {
	if c.vals == nil {
		c.vals = make(map[string]interface{})
	}
}

func (c *InContent) Merge(v interface{}) *InContent {
	if v == nil {
		return c
	}
	c.init()
	vtype := reflect.TypeOf(v)
	switch vtype.Kind() {
	case reflect.Map, reflect.Struct:
		// PASS
	case reflect.Ptr:
		if vtype.Elem().Kind() != reflect.Struct {
			return c
		}
	default:
		// tried to merge an invalid type
		return c
	}
	vl := reflect.ValueOf(v)
	if vtype.Kind() == reflect.Ptr {
		vl = vl.Elem()
		vtype = vtype.Elem()
		log.Println(vl.Kind().String())
	}
	if vtype.Kind() == reflect.Map {
		// merge mappy things
		if vtype.Key().Kind() != reflect.String {
			log.Println("MAP KEY IS NOT A STRING!")
			return c
		}
		keys := vl.MapKeys()
		for _, key := range keys {
			val := vl.MapIndex(key)
			c.vals[key.String()] = val.Interface()
		}
	} else {
		// merge structy things
		len0 := vl.NumField()
		for i := 0; i < len0; i++ {
			field := vl.Field(i)
			c.vals[vtype.Field(i).Name] = field.Interface()
		}
	}
	return c
}

func (c *InContent) Get2(key string) (val interface{}, ok bool) {
	c.init()
	val, ok = c.vals[key]
	return
}

func (c *InContent) GetString2(key string) (val string, ok bool) {
	c.init()
	var rval interface{}
	rval, ok = c.vals[key]
	if ok {
		val = rval.(string)
	}
	return
}

func (c *InContent) GetInt2(key string) (val int, ok bool) {
	c.init()
	var rval interface{}
	rval, ok = c.vals[key]
	if ok {
		val, _ = rval.(int)
	}
	return
}

func (c *InContent) Set(key string, val interface{}) *InContent {
	c.init()
	c.vals[key] = val
	return c
}

func (c *InContent) All() map[string]interface{} {
	c.init()
	return c.vals
}

// Translates text to the user language (if available)
func (in *In) T(format string, v ...interface{}) string {
	return i18ngo.TL(in.LangCode, format, v...)
}

func (in *In) Session() *Session {
	if in.session == nil {
		in.session = GetSession(in.W, in.R)
	}
	return in.session
}

func (in *In) OutputTpl(tplPath string) *Out {
	return in.outputTpl(tplPath, "")
}

func (in *In) OutputLayTpl(layout, tplPath string) *Out {
	return in.outputTpl(tplPath, layout)
}

func (in *In) OutputLay(layout string) *Out {
	return in.outputTpl("", layout)
}

func (in *In) outputTpl(tplPath, customLayout string) *Out {
	o := &Out{}
	if len(tplPath) > 0 {
		in.LayoutContent.Set("Content", in.OutputSoloTpl(tplPath).String())
	}
	if v, ok := in.LayoutContent.GetString2("Title"); ok {
		in.LayoutContent.Set("Title", in.GlobalTitle+v)
	} else {
		in.LayoutContent.Set("Title", in.GlobalTitle+in.T(in.Controller.GetPageTitle()))
	}
	o.kind = outTemplate
	o.contentObj = in.LayoutContent.Set("Flash", in.Session().Flash.All()).All()
	ln := in.Controller.GetLayoutName()
	if len(ln) == 0 && len(customLayout) == 0 {
		// no layout defined!
		o.kind = outBytes
		var buff bytes.Buffer
		binary.Write(&buff, binary.BigEndian, o.contentObj)
		o.contentBytes = buff.Bytes()
		o.contentObj = nil
		return o
	}
	if len(customLayout) > 0 {
		ln = customLayout
	}
	var layoutTpl *template.Template
	if len(in.App.Config.DefaultLanguage) > 0 && in.R != nil && in.W != nil {
		layoutTpl = in.App.GetLocalizedLayout(ln, in.W, in.R)
	} else {
		layoutTpl = in.App.GetLayout(ln)
	}
	o.tpl = layoutTpl
	return o
}

func (in *In) OutputSoloTpl(tplPath string) *Out {
	o := &Out{}
	o.kind = outTemplateSolo
	o.contentObj = in.Content.Set("Flash", in.Session().Flash.All()).All()
	var tpl *template.Template
	if len(in.App.Config.DefaultLanguage) > 0 && in.R != nil && in.W != nil {
		tpl = in.App.GetLocalizedViewTemplate(tplPath, in.W, in.R)
	} else {
		tpl = in.App.GetViewTemplate(tplPath)
	}
	o.tpl = tpl
	return o
}

func (in *In) OutputJSON(jobj interface{}) *Out {
	o := &Out{}
	o.kind = outJSON
	o.contentObj = jobj
	return o
}

func (in *In) OutputXML(xobj interface{}) *Out {
	o := &Out{}
	o.kind = outXML
	o.contentObj = xobj
	return o
}

func (in *In) OutputString(str string) *Out {
	o := &Out{}
	o.kind = outString
	o.contentStr = str
	return o
}

func (in *In) Continue() *Out {
	o := &Out{}
	o.kind = outPre
	return o
}

type Out struct {
	kind         int
	contentObj   interface{}
	contentStr   string
	contentBytes []byte
	tpl          *template.Template
}

func (o *Out) IsContinue() bool {
	return o.kind == outPre
}

func (o *Out) mustb(b []byte, err error) []byte {
	if err != nil {
		panic(err)
		return b
	}
	return b
}

func (o *Out) render(w http.ResponseWriter) {
	switch o.kind {
	case outJSON:
		w.Header().Set("Content-Type", "application/json; charset=utf8")
		w.Write(o.mustb(json.Marshal(o.contentObj)))
	case outXML:
		w.Write(o.mustb(xml.Marshal(o.contentObj)))
	case outTemplateSolo, outTemplate:
		o.tpl.Execute(w, o.contentObj)
	case outString:
		w.Write([]byte(o.contentStr))
	case outBytes:
		w.Write(o.contentBytes)
	}
}

func (o *Out) String() string {
	switch o.kind {
	case outJSON:
		return string(o.mustb(json.Marshal(o.contentObj)))
	case outXML:
		return string(o.mustb(xml.Marshal(o.contentObj)))
	case outTemplateSolo, outTemplate:
		var buffer bytes.Buffer
		o.tpl.Execute(&buffer, o.contentObj)
		return buffer.String()
	case outString:
		return o.contentStr
	case outBytes:
		return string(o.contentBytes)
	}
	return ""
}
<commit_msg>added OutputBytes func<commit_after>package goboots

import (
	"bytes"
	"encoding/binary"
	"encoding/json"
	"encoding/xml"
	"github.com/gabstv/i18ngo"
	"io"
	"log"
	"net/http"
	"reflect"
	"text/template"
)

const (
	outPre          = 0
	outJSON         = 1
	outXML          = 2
	outTemplate     = 3
	outTemplateSolo = 4
	outString       = 5
	outBytes        = 6
)

type In struct {
	R             *http.Request
	W             http.ResponseWriter
	URLParts      []string
	session       *Session
	Content       *InContent
	LayoutContent *InContent
	App           *App
	Controller    IController
	LangCode      string
	GlobalTitle   string
	closers       []io.Closer
}

// New clones a new In but without the content.
// Useful to render separate parts
func (in *In) New() *In {
	in2 := &In{}
	in2.R = in.R
	in2.W = in.W
	in2.URLParts = in.URLParts
	in2.session = in.session
	in2.Content = &InContent{}
	in2.LayoutContent = &InContent{}
	in2.App = in.App
	in2.Controller = in.Controller
	in2.LangCode = in.LangCode
	in2.GlobalTitle = in.GlobalTitle
	return in2
}

func (in *In) closeall() {
	if in.closers == nil {
		return
	}
	for _, v := range in.closers {
		v.Close()
	}
}

type InContent struct {
	vals map[string]interface{}
}

func (c *InContent) init() {
	if c.vals == nil {
		c.vals = make(map[string]interface{})
	}
}

func (c *InContent) Merge(v interface{}) *InContent {
	if v == nil {
		return c
	}
	c.init()
	vtype := reflect.TypeOf(v)
	switch vtype.Kind() {
	case reflect.Map, reflect.Struct:
		// PASS
	case reflect.Ptr:
		if vtype.Elem().Kind() != reflect.Struct {
			return c
		}
	default:
		// tried to merge an invalid type
		return c
	}
	vl := reflect.ValueOf(v)
	if vtype.Kind() == reflect.Ptr {
		vl = vl.Elem()
		vtype = vtype.Elem()
		log.Println(vl.Kind().String())
	}
	if vtype.Kind() == reflect.Map {
		// merge mappy things
		if vtype.Key().Kind() != reflect.String {
			log.Println("MAP KEY IS NOT A STRING!")
			return c
		}
		keys := vl.MapKeys()
		for _, key := range keys {
			val := vl.MapIndex(key)
			c.vals[key.String()] = val.Interface()
		}
	} else {
		// merge structy things
		len0 := vl.NumField()
		for i := 0; i < len0; i++ {
			field := vl.Field(i)
			c.vals[vtype.Field(i).Name] = field.Interface()
		}
	}
	return c
}

func (c *InContent) Get2(key string) (val interface{}, ok bool) {
	c.init()
	val, ok = c.vals[key]
	return
}

func (c *InContent) GetString2(key string) (val string, ok bool) {
	c.init()
	var rval interface{}
	rval, ok = c.vals[key]
	if ok {
		val = rval.(string)
	}
	return
}

func (c *InContent) GetInt2(key string) (val int, ok bool) {
	c.init()
	var rval interface{}
	rval, ok = c.vals[key]
	if ok {
		val, _ = rval.(int)
	}
	return
}

func (c *InContent) Set(key string, val interface{}) *InContent {
	c.init()
	c.vals[key] = val
	return c
}

func (c *InContent) All() map[string]interface{} {
	c.init()
	return c.vals
}

// Translates text to the user language (if available)
func (in *In) T(format string, v ...interface{}) string {
	return i18ngo.TL(in.LangCode, format, v...)
}

func (in *In) Session() *Session {
	if in.session == nil {
		in.session = GetSession(in.W, in.R)
	}
	return in.session
}

func (in *In) OutputTpl(tplPath string) *Out {
	return in.outputTpl(tplPath, "")
}

func (in *In) OutputLayTpl(layout, tplPath string) *Out {
	return in.outputTpl(tplPath, layout)
}

func (in *In) OutputLay(layout string) *Out {
	return in.outputTpl("", layout)
}

func (in *In) outputTpl(tplPath, customLayout string) *Out {
	o := &Out{}
	if len(tplPath) > 0 {
		in.LayoutContent.Set("Content", in.OutputSoloTpl(tplPath).String())
	}
	if v, ok := in.LayoutContent.GetString2("Title"); ok {
		in.LayoutContent.Set("Title", in.GlobalTitle+v)
	} else {
		in.LayoutContent.Set("Title", in.GlobalTitle+in.T(in.Controller.GetPageTitle()))
	}
	o.kind = outTemplate
	o.contentObj = in.LayoutContent.Set("Flash", in.Session().Flash.All()).All()
	ln := in.Controller.GetLayoutName()
	if len(ln) == 0 && len(customLayout) == 0 {
		// no layout defined!
		o.kind = outBytes
		var buff bytes.Buffer
		binary.Write(&buff, binary.BigEndian, o.contentObj)
		o.contentBytes = buff.Bytes()
		o.contentObj = nil
		return o
	}
	if len(customLayout) > 0 {
		ln = customLayout
	}
	var layoutTpl *template.Template
	if len(in.App.Config.DefaultLanguage) > 0 && in.R != nil && in.W != nil {
		layoutTpl = in.App.GetLocalizedLayout(ln, in.W, in.R)
	} else {
		layoutTpl = in.App.GetLayout(ln)
	}
	o.tpl = layoutTpl
	return o
}

func (in *In) OutputSoloTpl(tplPath string) *Out {
	o := &Out{}
	o.kind = outTemplateSolo
	o.contentObj = in.Content.Set("Flash", in.Session().Flash.All()).All()
	var tpl *template.Template
	if len(in.App.Config.DefaultLanguage) > 0 && in.R != nil && in.W != nil {
		tpl = in.App.GetLocalizedViewTemplate(tplPath, in.W, in.R)
	} else {
		tpl = in.App.GetViewTemplate(tplPath)
	}
	o.tpl = tpl
	return o
}

func (in *In) OutputJSON(jobj interface{}) *Out {
	o := &Out{}
	o.kind = outJSON
	o.contentObj = jobj
	return o
}

func (in *In) OutputXML(xobj interface{}) *Out {
	o := &Out{}
	o.kind = outXML
	o.contentObj = xobj
	return o
}

func (in *In) OutputString(str string) *Out {
	o := &Out{}
	o.kind = outString
	o.contentStr = str
	return o
}

func (in *In) OutputBytes(b []byte) *Out {
	o := &Out{}
	o.kind = outBytes
	o.contentBytes = b
	return o
}

func (in *In) Continue() *Out {
	o := &Out{}
	o.kind = outPre
	return o
}

type Out struct {
	kind         int
	contentObj   interface{}
	contentStr   string
	contentBytes []byte
	tpl          *template.Template
}

func (o *Out) IsContinue() bool {
	return o.kind == outPre
}

func (o *Out) mustb(b []byte, err error) []byte {
	if err != nil {
		panic(err)
		return b
	}
	return b
}

func (o *Out) render(w http.ResponseWriter) {
	switch o.kind {
	case outJSON:
		w.Header().Set("Content-Type", "application/json; charset=utf8")
		w.Write(o.mustb(json.Marshal(o.contentObj)))
	case outXML:
		w.Write(o.mustb(xml.Marshal(o.contentObj)))
	case outTemplateSolo, outTemplate:
		o.tpl.Execute(w, o.contentObj)
	case outString:
		w.Write([]byte(o.contentStr))
	case outBytes:
		w.Write(o.contentBytes)
	}
}

func (o *Out) String() string {
	switch o.kind {
	case outJSON:
		return string(o.mustb(json.Marshal(o.contentObj)))
	case outXML:
		return string(o.mustb(xml.Marshal(o.contentObj)))
	case outTemplateSolo, outTemplate:
		var buffer bytes.Buffer
		o.tpl.Execute(&buffer, o.contentObj)
		return buffer.String()
	case outString:
		return o.contentStr
	case outBytes:
		return string(o.contentBytes)
	}
	return ""
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/layeh/gumble/gumble"
	"github.com/layeh/gumble/gumble_ffmpeg"
	"github.com/layeh/gumble/gumbleutil"
)

func main() {
	files := make(map[string]string)
	var stream *gumble_ffmpeg.Stream

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s: [flags] [audio files...]\n", os.Args[0])
		flag.PrintDefaults()
	}

	gumbleutil.Main(func(client *gumble.Client) {
		stream = gumble_ffmpeg.New(client)

		client.Attach(gumbleutil.AutoBitrate)

		for _, file := range flag.Args() {
			key := filepath.Base(file)
			files[key] = file
		}
	}, gumbleutil.Listener{
		// Connect event
		Connect: func(e *gumble.ConnectEvent) {
			fmt.Printf("audio player loaded! (%d files)\n", len(files))
		},

		// Text message event
		TextMessage: func(e *gumble.TextMessageEvent) {
			if e.Sender == nil {
				return
			}
			file, ok := files[e.Message]
			if !ok {
				return
			}
			stream.Source = gumble_ffmpeg.SourceFile(file)
			if err := stream.Play(); err != nil {
				fmt.Printf("%s\n", err)
			} else {
				fmt.Printf("Playing %s\n", file)
			}
		},
	})
}
<commit_msg>_examples: fix mumble-audio-player no audio codec<commit_after>package main

import (
	"flag"
	"fmt"
	"os"
	"path/filepath"

	"github.com/layeh/gumble/gumble"
	"github.com/layeh/gumble/gumble_ffmpeg"
	"github.com/layeh/gumble/gumbleutil"
	_ "github.com/layeh/gumble/opus"
)

func main() {
	files := make(map[string]string)
	var stream *gumble_ffmpeg.Stream

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage of %s: [flags] [audio files...]\n", os.Args[0])
		flag.PrintDefaults()
	}

	gumbleutil.Main(func(client *gumble.Client) {
		stream = gumble_ffmpeg.New(client)

		client.Attach(gumbleutil.AutoBitrate)

		for _, file := range flag.Args() {
			key := filepath.Base(file)
			files[key] = file
		}
	}, gumbleutil.Listener{
		// Connect event
		Connect: func(e *gumble.ConnectEvent) {
			fmt.Printf("audio player loaded! (%d files)\n", len(files))
		},

		// Text message event
		TextMessage: func(e *gumble.TextMessageEvent) {
			if e.Sender == nil {
				return
			}
			file, ok := files[e.Message]
			if !ok {
				return
			}
			stream.Source = gumble_ffmpeg.SourceFile(file)
			if err := stream.Play(); err != nil {
				fmt.Printf("%s\n", err)
			} else {
				fmt.Printf("Playing %s\n", file)
			}
		},
	})
}
<|endoftext|>