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

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the concurrency primitive functions.

package golisp

import (
	"fmt"
	"time"
	"unsafe"
)

type Process struct {
	Env           *SymbolTableFrame
	Code          *Data
	Wake          chan bool
	Abort         chan bool
	Restart       chan bool
	ScheduleTimer *time.Timer
}

func RegisterConcurrencyPrimitives() {
	MakePrimitiveFunction("fork", 1, ForkImpl)
	MakePrimitiveFunction("proc-sleep", 2, ProcSleepImpl)
	MakePrimitiveFunction("wake", 1, WakeImpl)
	MakePrimitiveFunction("schedule", 2, ScheduleImpl)
	MakePrimitiveFunction("reset-timeout", 1, ResetTimeoutImpl)
	MakePrimitiveFunction("abandon", 1, AbandonImpl)
}

func ForkImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !FunctionP(f) {
		err = ProcessError(fmt.Sprintf("fork expected a function, but received %v.", f), env)
		return
	}

	if FunctionValue(f).RequiredArgCount != 1 {
		err = ProcessError(fmt.Sprintf("fork expected a function with arity of 1, but it was %d.", FunctionValue(f).RequiredArgCount), env)
		return
	}

	proc := &Process{Env: env, Code: f, Wake: make(chan bool, 1), Abort: make(chan bool, 1), Restart: make(chan bool, 1)}
	procObj := ObjectWithTypeAndValue("Process", unsafe.Pointer(proc))

	go func() {
		_, err = FunctionValue(f).ApplyWithoutEval(InternalMakeList(procObj), env)
	}()

	return procObj, nil
}

func ProcSleepImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("proc-sleep expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))

	millis, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !IntegerP(millis) {
		err = ProcessError(fmt.Sprintf("proc-sleep expected an integer as a delay, but received %v.", millis), env)
		return
	}

	woken := false
	select {
	case <-proc.Wake:
		woken = true
	case <-time.After(time.Duration(IntegerValue(millis)) * time.Millisecond):
	}

	return BooleanWithValue(woken), nil
}

func WakeImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("wake expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))
	proc.Wake <- true
	return StringWithValue("OK"), nil
}

func ScheduleImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	millis, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !IntegerP(millis) {
		err = ProcessError(fmt.Sprintf("schedule expected an integer as a delay, but received %v.", millis), env)
		return
	}
	f, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}

	if !FunctionP(f) {
		err = ProcessError(fmt.Sprintf("schedule expected a function, but received %v.", f), env)
		return
	}

	if FunctionValue(f).RequiredArgCount != 1 {
		err = ProcessError(fmt.Sprintf("schedule expected a function with arity of 1, but it was %d.", FunctionValue(f).RequiredArgCount), env)
		return
	}

	proc := &Process{
		Env:           env,
		Code:          f,
		Wake:          make(chan bool, 1),
		Abort:         make(chan bool, 1),
		Restart:       make(chan bool, 1),
		ScheduleTimer: time.NewTimer(time.Duration(IntegerValue(millis)) * time.Millisecond)}
	procObj := ObjectWithTypeAndValue("Process", unsafe.Pointer(proc))

	aborted := false
	go func() {
	Loop:
		for {
			select {
			case <-proc.Abort:
				aborted = true
				break Loop
			case <-proc.Restart:
				proc.ScheduleTimer.Reset(time.Duration(IntegerValue(millis)) * time.Millisecond)
			case <-proc.ScheduleTimer.C:
				_, err = FunctionValue(f).ApplyWithoutEval(InternalMakeList(procObj), env)
				break Loop
			}
		}
	}()

	return procObj, nil

}

func AbandonImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("adandon expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))
	proc.Abort <- true
	return StringWithValue("OK"), nil
}

func ResetTimeoutImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("restart expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))
	var result string
	select {
	case proc.Restart <- true:
		result = "OK"
	default:
		result = "task was already completed or abandoned"
	}
	return StringWithValue(result), nil
}
<commit_msg>Bug fix (reused result).<commit_after>// Copyright 2014 SteelSeries ApS.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// This package implements a basic LISP interpretor for embedding in a go program for scripting.
// This file contains the concurrency primitive functions.

package golisp

import (
	"fmt"
	"time"
	"unsafe"
)

type Process struct {
	Env           *SymbolTableFrame
	Code          *Data
	Wake          chan bool
	Abort         chan bool
	Restart       chan bool
	ScheduleTimer *time.Timer
}

func RegisterConcurrencyPrimitives() {
	MakePrimitiveFunction("fork", 1, ForkImpl)
	MakePrimitiveFunction("proc-sleep", 2, ProcSleepImpl)
	MakePrimitiveFunction("wake", 1, WakeImpl)
	MakePrimitiveFunction("schedule", 2, ScheduleImpl)
	MakePrimitiveFunction("reset-timeout", 1, ResetTimeoutImpl)
	MakePrimitiveFunction("abandon", 1, AbandonImpl)
}

func ForkImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	f, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !FunctionP(f) {
		err = ProcessError(fmt.Sprintf("fork expected a function, but received %v.", f), env)
		return
	}

	if FunctionValue(f).RequiredArgCount != 1 {
		err = ProcessError(fmt.Sprintf("fork expected a function with arity of 1, but it was %d.", FunctionValue(f).RequiredArgCount), env)
		return
	}

	proc := &Process{Env: env, Code: f, Wake: make(chan bool, 1), Abort: make(chan bool, 1), Restart: make(chan bool, 1)}
	procObj := ObjectWithTypeAndValue("Process", unsafe.Pointer(proc))

	go func() {
		_, err = FunctionValue(f).ApplyWithoutEval(InternalMakeList(procObj), env)
	}()

	return procObj, nil
}

func ProcSleepImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("proc-sleep expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))

	millis, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}
	if !IntegerP(millis) {
		err = ProcessError(fmt.Sprintf("proc-sleep expected an integer as a delay, but received %v.", millis), env)
		return
	}

	woken := false
	select {
	case <-proc.Wake:
		woken = true
	case <-time.After(time.Duration(IntegerValue(millis)) * time.Millisecond):
	}

	return BooleanWithValue(woken), nil
}

func WakeImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("wake expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))
	proc.Wake <- true
	return StringWithValue("OK"), nil
}

func ScheduleImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	millis, err := Eval(Car(args), env)
	if err != nil {
		return
	}
	if !IntegerP(millis) {
		err = ProcessError(fmt.Sprintf("schedule expected an integer as a delay, but received %v.", millis), env)
		return
	}
	f, err := Eval(Cadr(args), env)
	if err != nil {
		return
	}

	if !FunctionP(f) {
		err = ProcessError(fmt.Sprintf("schedule expected a function, but received %v.", f), env)
		return
	}

	if FunctionValue(f).RequiredArgCount != 1 {
		err = ProcessError(fmt.Sprintf("schedule expected a function with arity of 1, but it was %d.", FunctionValue(f).RequiredArgCount), env)
		return
	}

	proc := &Process{
		Env:           env,
		Code:          f,
		Wake:          make(chan bool, 1),
		Abort:         make(chan bool, 1),
		Restart:       make(chan bool, 1),
		ScheduleTimer: time.NewTimer(time.Duration(IntegerValue(millis)) * time.Millisecond)}
	procObj := ObjectWithTypeAndValue("Process", unsafe.Pointer(proc))

	aborted := false
	go func() {
	Loop:
		for {
			select {
			case <-proc.Abort:
				aborted = true
				break Loop
			case <-proc.Restart:
				proc.ScheduleTimer.Reset(time.Duration(IntegerValue(millis)) * time.Millisecond)
			case <-proc.ScheduleTimer.C:
				_, err = FunctionValue(f).ApplyWithoutEval(InternalMakeList(procObj), env)
				break Loop
			}
		}
	}()

	return procObj, nil

}

func AbandonImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("adandon expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))
	proc.Abort <- true
	return StringWithValue("OK"), nil
}

func ResetTimeoutImpl(args *Data, env *SymbolTableFrame) (result *Data, err error) {
	procObj, err := Eval(Car(args), env)
	if err != nil {
		return
	}

	if !ObjectP(procObj) || ObjectType(procObj) != "Process" {
		err = ProcessError(fmt.Sprintf("restart expects a Process object expected but received %s.", ObjectType(procObj)), env)
		return
	}

	proc := (*Process)(ObjectValue(procObj))
	var str string
	select {
	case proc.Restart <- true:
		str = "OK"
	default:
		str = "task was already completed or abandoned"
	}
	return StringWithValue(str), nil
}
<|endoftext|>


<commit_before>package process

import (
	"github.com/whosonfirst/go-whosonfirst-updated"
	"github.com/whosonfirst/go-whosonfirst-updated/queue"
	"log"
	"os"
	"path/filepath"
	"sync"
	"time"
)

type GitHooksProcessor struct {
	Processor
	queue     *queue.Queue
	data_root string
	flushing  bool
	mu        *sync.Mutex
}

func NewGitHooksProcessor(data_root string) (*GitHooksProcessor, error) {

	data_root, err := filepath.Abs(data_root)

	if err != nil {
		return nil, err
	}

	_, err = os.Stat(data_root)

	if os.IsNotExist(err) {
		return nil, err
	}

	q, err := queue.NewQueue()

	if err != nil {
		return nil, err
	}

	mu := new(sync.Mutex)

	gh := GitHooksProcessor{
		queue:     q,
		data_root: data_root,
		flushing:  false,
		mu:        mu,
	}

	gh.Monitor()

	return &gh, nil
}

func (gh *GitHooksProcessor) Monitor() {

	buffer := time.Second * 30

	for {

		timer := time.NewTimer(buffer)
		<-timer.C

		gh.Flush()
	}

}

func (gh *GitHooksProcessor) Flush() {

	gh.mu.Lock()

	if gh.flushing {
		gh.mu.Unlock()
		return
	}

	gh.flushing = true
	gh.mu.Unlock()

	for _, repo := range gh.queue.Pending() {
		go gh.ProcessRepo(repo)
	}

	gh.mu.Lock()

	gh.flushing = false
	gh.mu.Unlock()
}

func (gh *GitHooksProcessor) Process(task updated.UpdateTask) error {

	repo := task.Repo
	return gh.ProcessRepo(repo)
}

func (gh *GitHooksProcessor) ProcessRepo(repo string) error {

	if gh.queue.IsProcessing(repo) {
		return gh.queue.Schedule(repo)
	}

	err := gh.queue.Lock(repo)

	if err != nil {
		return err
	}

	err = gh._process(repo)

	if err != nil {
		return err
	}

	err = gh.queue.Release(repo)

	if err != nil {
		return err
	}

	return nil
}

func (gh *GitHooksProcessor) _process(repo string) error {

	abs_path := filepath.Join(gh.data_root, repo)
	log.Println("process", abs_path)

	return nil
}
<commit_msg>hooks to invoke git - untested<commit_after>package process

import (
	"fmt"
	"github.com/whosonfirst/go-whosonfirst-updated"
	"github.com/whosonfirst/go-whosonfirst-updated/queue"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"sync"
	"time"
)

type GitHooksProcessor struct {
	Processor
	queue     *queue.Queue
	data_root string
	flushing  bool
	mu        *sync.Mutex
}

func NewGitHooksProcessor(data_root string) (*GitHooksProcessor, error) {

	data_root, err := filepath.Abs(data_root)

	if err != nil {
		return nil, err
	}

	_, err = os.Stat(data_root)

	if os.IsNotExist(err) {
		return nil, err
	}

	q, err := queue.NewQueue()

	if err != nil {
		return nil, err
	}

	mu := new(sync.Mutex)

	gh := GitHooksProcessor{
		queue:     q,
		data_root: data_root,
		flushing:  false,
		mu:        mu,
	}

	gh.Monitor()

	return &gh, nil
}

func (gh *GitHooksProcessor) Monitor() {

	buffer := time.Second * 30

	for {

		timer := time.NewTimer(buffer)
		<-timer.C

		gh.Flush()
	}

}

func (gh *GitHooksProcessor) Flush() {

	gh.mu.Lock()

	if gh.flushing {
		gh.mu.Unlock()
		return
	}

	gh.flushing = true
	gh.mu.Unlock()

	for _, repo := range gh.queue.Pending() {
		go gh.ProcessRepo(repo)
	}

	gh.mu.Lock()

	gh.flushing = false
	gh.mu.Unlock()
}

func (gh *GitHooksProcessor) Process(task updated.UpdateTask) error {

	repo := task.Repo
	return gh.ProcessRepo(repo)
}

func (gh *GitHooksProcessor) ProcessRepo(repo string) error {

	if gh.queue.IsProcessing(repo) {
		return gh.queue.Schedule(repo)
	}

	err := gh.queue.Lock(repo)

	if err != nil {
		return err
	}

	err = gh._process(repo)

	if err != nil {
		return err
	}

	err = gh.queue.Release(repo)

	if err != nil {
		return err
	}

	return nil
}

func (gh *GitHooksProcessor) _process(repo string) error {

	abs_path := filepath.Join(gh.data_root, repo)
	log.Println("process", abs_path)

	dot_git := filepath.Join(abs_path, ".git")

	git_dir := fmt.Sprintf("--git-dir=%s", dot_git)
	work_tree := fmt.Sprintf("--work-tree=%s", dot_git)

	git_args := []string{git_dir, work_tree, "pull", "origin", "master"}

	cmd := exec.Command("git", git_args...)

	_, err := cmd.Output()

	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	`fmt`
	`os`
	`github.com/pdf/xbmc-callback-daemon/config`
	`github.com/pdf/xbmc-callback-daemon/hyperion`
	`github.com/pdf/xbmc-callback-daemon/logger`
	`github.com/pdf/xbmc-callback-daemon/shell`
	`github.com/pdf/xbmc-callback-daemon/xbmc`
)

const (
	VERSION = `0.1.0`
)

var (
	cfg config.Config
)

// usage simply prints the invocation requirements.
func usage() {
	fmt.Fprintf(os.Stderr, "\nXBMC Callback Daemon v%s\n\nUsage: %s [configFile]\n\n", VERSION, os.Args[0])
	os.Exit(1)
}

// init ensures we have a config path argument, and loads the configuration.
func init() {
	if len(os.Args) < 2 {
		usage()
	}
	cfg = config.Load(os.Args[1])
	if cfg.Debug != nil {
		logger.DebugEnabled = *cfg.Debug
	}
}

// execute iterates through a list of callbacks, and sends them to the backend
// defined in the `backend` property.
func execute(callbacks []interface{}) {
	for i := range callbacks {
		m := callbacks[i].(map[string]interface{})

		switch m[`backend`] {
		case `hyperion`:
			if cfg.Hyperion != nil {
				hyperion.Execute(m)
			}

		case `xbmc`:
			xbmc.Execute(m)

		case `shell`:
			shell.Execute(m)

		default:
			logger.Warn(`Unknown backend: `, m[`backend`])
		}
	}
}

// callbacksByType takes a type to match, and a list of callbacks. If the
// callback has a `types` property, and that contains a matching type, the
// callback is added to the returned list.  A callback without a `types`
// property will always be returned.
func callbacksByType(matchType string, callbacks []interface{}) []interface{} {
	result := make([]interface{}, 0)
	var cb map[string]interface{}

	for i := range callbacks {
		// Access internal callback map.
		cb = callbacks[i].(map[string]interface{})

		switch cb[`types`].(type) {
		// We have a list of types.
		case []interface{}:
			// Access internal types slice.
			cbTypes, ok := cb[`types`].([]interface{})
			if ok == false {
				logger.Panic(`Couldn't understand 'types' array, check your configuration.`)
			}
			for j := range cbTypes {
				if cbTypes[j].(string) == matchType {
					// Matched the required type, add this callback to the results.
					result = append(result, cb)
				}
			}
		// If there is no valid `types` property, add this callback to the results.
		default:
			result = append(result, cb)
		}
	}

	return result
}

// main program loop.
func main() {
	// Connect to XBMC, this is required.
	xbmc.Connect(fmt.Sprintf(`%s:%d`, cfg.XBMC.Address, cfg.XBMC.Port))
	defer xbmc.Close()

	// If the configuration specifies a Hyperion connection, use it.
	if cfg.Hyperion != nil {
		hyperion.Connect(fmt.Sprintf(`%s:%d`, cfg.Hyperion.Address, cfg.Hyperion.Port))
		defer hyperion.Close()
	}

	notification := &xbmc.Notification{}
	// Get callbacks from configuration.
	callbacks := cfg.Callbacks.(map[string]interface{})

	// Execute callbacks for the special `Startup` notification.
	if callbacks[`Startup`] != nil {
		execute(callbacks[`Startup`].([]interface{}))
	}

	// Loop while reading from XBMC.
	for {
		// Read from XBMC.
		xbmc.Read(notification)

		logger.Debug(`Received notification from XBMC: `, notification)
		// Match XBMC notification to our configured callbacks.
		if callbacks[notification.Method] != nil {
			cbs := callbacks[notification.Method].([]interface{})
			// The Player.OnPlay notification supports an filtering by item type.
			if notification.Method == `Player.OnPlay` {
				cbs = callbacksByType(notification.Params.Data.Item.Type, cbs)
			}
			execute(cbs)
		}
	}
}
<commit_msg>Bump version to v0.2.0<commit_after>package main

import (
	`fmt`
	`os`
	`github.com/pdf/xbmc-callback-daemon/config`
	`github.com/pdf/xbmc-callback-daemon/hyperion`
	`github.com/pdf/xbmc-callback-daemon/logger`
	`github.com/pdf/xbmc-callback-daemon/shell`
	`github.com/pdf/xbmc-callback-daemon/xbmc`
)

const (
	VERSION = `0.2.0`
)

var (
	cfg config.Config
)

// usage simply prints the invocation requirements.
func usage() {
	fmt.Fprintf(os.Stderr, "\nXBMC Callback Daemon v%s\n\nUsage: %s [configFile]\n\n", VERSION, os.Args[0])
	os.Exit(1)
}

// init ensures we have a config path argument, and loads the configuration.
func init() {
	if len(os.Args) < 2 {
		usage()
	}
	cfg = config.Load(os.Args[1])
	if cfg.Debug != nil {
		logger.DebugEnabled = *cfg.Debug
	}
}

// execute iterates through a list of callbacks, and sends them to the backend
// defined in the `backend` property.
func execute(callbacks []interface{}) {
	for i := range callbacks {
		m := callbacks[i].(map[string]interface{})

		switch m[`backend`] {
		case `hyperion`:
			if cfg.Hyperion != nil {
				hyperion.Execute(m)
			}

		case `xbmc`:
			xbmc.Execute(m)

		case `shell`:
			shell.Execute(m)

		default:
			logger.Warn(`Unknown backend: `, m[`backend`])
		}
	}
}

// callbacksByType takes a type to match, and a list of callbacks. If the
// callback has a `types` property, and that contains a matching type, the
// callback is added to the returned list.  A callback without a `types`
// property will always be returned.
func callbacksByType(matchType string, callbacks []interface{}) []interface{} {
	result := make([]interface{}, 0)
	var cb map[string]interface{}

	for i := range callbacks {
		// Access internal callback map.
		cb = callbacks[i].(map[string]interface{})

		switch cb[`types`].(type) {
		// We have a list of types.
		case []interface{}:
			// Access internal types slice.
			cbTypes, ok := cb[`types`].([]interface{})
			if ok == false {
				logger.Panic(`Couldn't understand 'types' array, check your configuration.`)
			}
			for j := range cbTypes {
				if cbTypes[j].(string) == matchType {
					// Matched the required type, add this callback to the results.
					result = append(result, cb)
				}
			}
		// If there is no valid `types` property, add this callback to the results.
		default:
			result = append(result, cb)
		}
	}

	return result
}

// main program loop.
func main() {
	// Connect to XBMC, this is required.
	xbmc.Connect(fmt.Sprintf(`%s:%d`, cfg.XBMC.Address, cfg.XBMC.Port))
	defer xbmc.Close()

	// If the configuration specifies a Hyperion connection, use it.
	if cfg.Hyperion != nil {
		hyperion.Connect(fmt.Sprintf(`%s:%d`, cfg.Hyperion.Address, cfg.Hyperion.Port))
		defer hyperion.Close()
	}

	notification := &xbmc.Notification{}
	// Get callbacks from configuration.
	callbacks := cfg.Callbacks.(map[string]interface{})

	// Execute callbacks for the special `Startup` notification.
	if callbacks[`Startup`] != nil {
		execute(callbacks[`Startup`].([]interface{}))
	}

	// Loop while reading from XBMC.
	for {
		// Read from XBMC.
		xbmc.Read(notification)

		logger.Debug(`Received notification from XBMC: `, notification)
		// Match XBMC notification to our configured callbacks.
		if callbacks[notification.Method] != nil {
			cbs := callbacks[notification.Method].([]interface{})
			// The Player.OnPlay notification supports an filtering by item type.
			if notification.Method == `Player.OnPlay` {
				cbs = callbacksByType(notification.Params.Data.Item.Type, cbs)
			}
			execute(cbs)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"flag"
	"fmt"
	mp "github.com/mackerelio/go-mackerel-plugin"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
)

var graphdef map[string]mp.Graphs = map[string]mp.Graphs{
	"memcached.connections": {
		Label: "Memcached Connections",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "curr_connections", Label: "Connections", Diff: false},
		},
	},
	"memcached.cmd": {
		Label: "Memcached Command",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "cmd_get", Label: "Get", Diff: true},
			{Name: "cmd_set", Label: "Set", Diff: true},
			{Name: "cmd_flush", Label: "Flush", Diff: true},
			{Name: "cmd_touch", Label: "Touch", Diff: true},
		},
	},
	"memcached.hitmiss": {
		Label: "Memcached Hits/Misses",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "get_hits", Label: "Get Hits", Diff: true},
			{Name: "get_misses", Label: "Get Misses", Diff: true},
			{Name: "delete_hits", Label: "Delete Hits", Diff: true},
			{Name: "delete_misses", Label: "Delete Misses", Diff: true},
			{Name: "incr_hits", Label: "Incr Hits", Diff: true},
			{Name: "incr_misses", Label: "Incr Misses", Diff: true},
			{Name: "cas_hits", Label: "Cas Hits", Diff: true},
			{Name: "cas_misses", Label: "Cas Misses", Diff: true},
			{Name: "touch_hits", Label: "Touch Hits", Diff: true},
			{Name: "touch_misses", Label: "Touch Misses", Diff: true},
		},
	},
	"memcached.evictions": {
		Label: "Memcached Evictions",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "evictions", Label: "Evictions", Diff: true},
		},
	},
	"memcached.unfetched": {
		Label: "Memcached Unfetched",
		Unit:  "integer",
		Metrics: []mp.Metrics{
			{Name: "expired_unfetched", Label: "Expired unfetched", Diff: true},
			{Name: "evicted_unfetched", Label: "Evicted unfetched", Diff: true},
		},
	},
	"memcached.rusage": {
		Label: "Memcached Resouce Usage",
		Unit:  "float",
		Metrics: []mp.Metrics{
			{Name: "rusage_user", Label: "User", Diff: true},
			{Name: "rusage_system", Label: "System", Diff: true},
		},
	},
	"memcached.bytes": {
		Label: "Memcached Traffics",
		Unit:  "bytes",
		Metrics: []mp.Metrics{
			{Name: "bytes_read", Label: "Read", Diff: true},
			{Name: "bytes_written", Label: "Write", Diff: true},
		},
	},
}

type MemcachedPlugin struct {
	Target   string
	Tempfile string
}

func (m MemcachedPlugin) FetchMetrics() (map[string]float64, error) {
	conn, err := net.Dial("tcp", m.Target)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(conn, "stats")
	scanner := bufio.NewScanner(conn)
	stat := make(map[string]float64)

	for scanner.Scan() {
		line := scanner.Text()
		s := string(line)
		if s == "END" {
			return stat, nil
		}

		res := strings.Split(s, " ")
		if res[0] == "STAT" {
			stat[res[1]], err = strconv.ParseFloat(res[2], 64)
			if err != nil {
				log.Println("FetchMetrics:", err)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return stat, err
	}
	return nil, nil
}

func (m MemcachedPlugin) GraphDefinition() map[string]mp.Graphs {
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "11211", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var memcached MemcachedPlugin

	memcached.Target = fmt.Sprintf("%s:%s", *optHost, *optPort)
	helper := mp.NewMackerelPlugin(memcached)

	if *optTempfile != "" {
		helper.Tempfile = *optTempfile
	} else {
		helper.Tempfile = fmt.Sprintf("/tmp/mackerel-plugin-memcached-%s-%s", *optHost, *optPort)
	}

	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		helper.OutputDefinitions()
	} else {
		helper.OutputValues()
	}
}
<commit_msg>update _example<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"log"
	"net"
	"strconv"
	"strings"

	"github.com/mackerelio/go-mackerel-plugin"
)

var graphdef map[string]mackerelplugin.Graphs = map[string]mackerelplugin.Graphs{
	"memcached.connections": {
		Label: "Memcached Connections",
		Unit:  "integer",
		Metrics: []mackerelplugin.Metrics{
			{Name: "curr_connections", Label: "Connections", Diff: false},
		},
	},
	"memcached.cmd": {
		Label: "Memcached Command",
		Unit:  "integer",
		Metrics: []mackerelplugin.Metrics{
			{Name: "cmd_get", Label: "Get", Diff: true},
			{Name: "cmd_set", Label: "Set", Diff: true},
			{Name: "cmd_flush", Label: "Flush", Diff: true},
			{Name: "cmd_touch", Label: "Touch", Diff: true},
		},
	},
	"memcached.hitmiss": {
		Label: "Memcached Hits/Misses",
		Unit:  "integer",
		Metrics: []mackerelplugin.Metrics{
			{Name: "get_hits", Label: "Get Hits", Diff: true},
			{Name: "get_misses", Label: "Get Misses", Diff: true},
			{Name: "delete_hits", Label: "Delete Hits", Diff: true},
			{Name: "delete_misses", Label: "Delete Misses", Diff: true},
			{Name: "incr_hits", Label: "Incr Hits", Diff: true},
			{Name: "incr_misses", Label: "Incr Misses", Diff: true},
			{Name: "cas_hits", Label: "Cas Hits", Diff: true},
			{Name: "cas_misses", Label: "Cas Misses", Diff: true},
			{Name: "touch_hits", Label: "Touch Hits", Diff: true},
			{Name: "touch_misses", Label: "Touch Misses", Diff: true},
		},
	},
	"memcached.evictions": {
		Label: "Memcached Evictions",
		Unit:  "integer",
		Metrics: []mackerelplugin.Metrics{
			{Name: "evictions", Label: "Evictions", Diff: true},
		},
	},
	"memcached.unfetched": {
		Label: "Memcached Unfetched",
		Unit:  "integer",
		Metrics: []mackerelplugin.Metrics{
			{Name: "expired_unfetched", Label: "Expired unfetched", Diff: true},
			{Name: "evicted_unfetched", Label: "Evicted unfetched", Diff: true},
		},
	},
	"memcached.rusage": {
		Label: "Memcached Resouce Usage",
		Unit:  "float",
		Metrics: []mackerelplugin.Metrics{
			{Name: "rusage_user", Label: "User", Diff: true},
			{Name: "rusage_system", Label: "System", Diff: true},
		},
	},
	"memcached.bytes": {
		Label: "Memcached Traffics",
		Unit:  "bytes",
		Metrics: []mackerelplugin.Metrics{
			{Name: "bytes_read", Label: "Read", Diff: true},
			{Name: "bytes_written", Label: "Write", Diff: true},
		},
	},
}

type MemcachedPlugin struct {
	Target   string
	Tempfile string
}

func (m MemcachedPlugin) FetchMetrics() (map[string]float64, error) {
	conn, err := net.Dial("tcp", m.Target)
	if err != nil {
		return nil, err
	}
	fmt.Fprintln(conn, "stats")
	scanner := bufio.NewScanner(conn)
	stat := make(map[string]float64)

	for scanner.Scan() {
		line := scanner.Text()
		s := string(line)
		if s == "END" {
			return stat, nil
		}

		res := strings.Split(s, " ")
		if res[0] == "STAT" {
			stat[res[1]], err = strconv.ParseFloat(res[2], 64)
			if err != nil {
				log.Println("FetchMetrics:", err)
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return stat, err
	}
	return nil, nil
}

func (m MemcachedPlugin) GraphDefinition() map[string]mackerelplugin.Graphs {
	return graphdef
}

func main() {
	optHost := flag.String("host", "localhost", "Hostname")
	optPort := flag.String("port", "11211", "Port")
	optTempfile := flag.String("tempfile", "", "Temp file name")
	flag.Parse()

	var memcached MemcachedPlugin

	memcached.Target = fmt.Sprintf("%s:%s", *optHost, *optPort)
	helper := mackerelplugin.NewMackerelPlugin(memcached)
	helper.Tempfile = *optTempfile
	helper.Run()
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/alecthomas/participle"
	"github.com/alecthomas/participle/lexer"
	"github.com/alecthomas/repr"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	sqlArg = kingpin.Arg("sql", "SQL to parse.").Required().String()

	sqlLexer = lexer.Unquote(lexer.Upper(lexer.Must(lexer.Regexp(`(\s+)`+
		`|(?P<Keyword>(?i)SELECT|FROM|TOP|DISTINCT|ALL|WHERE|GROUP|BY|HAVING|UNION|MINUS|EXCEPT|INTERSECT|ORDER|LIMIT|OFFSET|TRUE|FALSE|NULL|IS|NOT|ANY|SOME|BETWEEN|AND|OR|LIKE|AS|IN)`+
		`|(?P<Ident>[a-zA-Z_][a-zA-Z0-9_]*)`+
		`|(?P<Number>[-+]?\d*\.?\d+([eE][-+]?\d+)?)`+
		`|(?P<String>'[^']*'|"[^"]*")`+
		`|(?P<Operators><>|!=|<=|>=|[-+*/%,.()=<>])`,
	)), "Keyword"), "String")
	sqlParser = participle.MustBuild(&Select{}, sqlLexer)
)

type Boolean bool

func (b *Boolean) Capture(values []string) error {
	*b = values[0] == "TRUE"
	return nil
}

// Select, based on http://www.h2database.com/html/grammar.html
type Select struct {
	Top        *Term             `"SELECT" [ "TOP" @@ ]`
	Distinct   bool              `[  @"DISTINCT"`
	All        bool              ` | @"ALL" ]`
	Expression *SelectExpression `@@`
	From       *From             `"FROM" @@`
}

type From struct {
	TableExpressions []*TableExpression `@@ { "," @@ }`
	Where            *Expression        `[ "WHERE" @@ ]`
}

type TableExpression struct {
	Table  string        `( @Ident { "." @Ident }`
	Select *Select       `  | "(" @@ ")"`
	Values []*Expression `  | "VALUES" "(" @@ { "," @@ } ")")`
	As     string        `[ "AS" @Ident ]`
}

type SelectExpression struct {
	All         bool                 `  @"*"`
	Expressions []*AliasedExpression `| @@ { "," @@ }`
}

type AliasedExpression struct {
	Expression *Expression `@@`
	As         string      `[ "AS" @Ident ]`
}

type Expression struct {
	And *AndCondition `@@ { "OR" @@ }`
}

type AndCondition struct {
	Or []*Condition `@@ { "AND" @@ }`
}

type Condition struct {
	Operand *ConditionOperand `  @@`
	Not     *Condition        `| "NOT" @@`
	Exists  *Select           `| "EXISTS" "(" @@ ")"`
}

type ConditionOperand struct {
	Operand      *Operand      `@@`
	ConditionRHS *ConditionRHS `[ @@ ]`
}

type ConditionRHS struct {
	Compare *Compare `  @@`
	Is      *Is      `| "IS" @@`
	Between *Between `| "BETWEEN" @@`
	In      *In      `| "IN" "(" @@ ")"`
	Like    *Like    `| "LIKE" @@`
}

type Compare struct {
	Operator string         `@( "<>" | "<=" | ">=" | "=" | "<" | ">" | "!=" )`
	Operand  *Operand       `(  @@`
	Select   *CompareSelect ` | @@ )`
}

type CompareSelect struct {
	All    bool    `(  @"ALL"`
	Any    bool    ` | @"ANY"`
	Some   bool    ` | @"SOME" )`
	Select *Select `"(" @@ ")"`
}

type Like struct {
	Not     bool     `[ @"NOT" ]`
	Operand *Operand `@@`
}

type Is struct {
	Not          bool     `[ @"NOT" ]`
	Null         bool     `( @"NULL"`
	DistinctFrom *Operand `  | "DISTINCT" "FROM" @@ )`
}

type Between struct {
	Start *Operand `@@`
	End   *Operand `"AND" @@`
}

type In struct {
	Select      *Select       `  @@`
	Expressions []*Expression `| @@ { "," @@ }`
}

type Operand struct {
	Summand []*Summand `@@ { "|" "|" @@ }`
}

type Summand struct {
	LHS *Factor `@@`
	Op  string  `[ @("+" | "-")`
	RHS *Factor `  @@ ]`
}

type Factor struct {
	LHS *Term  `@@`
	Op  string `[ @("*" | "/" | "%")`
	RHS *Term  `  @@ ]`
}

type Term struct {
	Select    *Select    `(  "(" @@ ")"`
	SymbolRef *SymbolRef ` | @@`
	Value     *Value     ` | @@ )`
}

type SymbolRef struct {
	Symbol     string        `@Ident @{ "." Ident }`
	Parameters []*Expression `[ "(" @@ { "," @@ } ")" ]`
}

type Value struct {
	Negated bool `[ @"-" | "+" ]`

	Wildcard bool     `(  @"*"`
	Number   *float64 ` | @Number`
	String   *string  ` | @String`
	Boolean  *Boolean ` | @("TRUE" | "FALSE")`
	Null     bool     ` | @"NULL"`
	Array    *Array   ` | @@ )`
}

type Array struct {
	Expressions []*Expression `"(" @@ { "," @@ } ")"`
}

func main() {
	kingpin.Parse()
	sql := &Select{}
	err := sqlParser.ParseString(*sqlArg, sql)
	kingpin.FatalIfError(err, "")
	repr.Println(sql, repr.Indent("  "), repr.OmitEmpty())
}
<commit_msg>SQL example: LIMIT + OFFSET and GROUP BY support.<commit_after>package main

import (
	"github.com/alecthomas/participle"
	"github.com/alecthomas/participle/lexer"
	"github.com/alecthomas/repr"
	"gopkg.in/alecthomas/kingpin.v2"
)

var (
	sqlArg = kingpin.Arg("sql", "SQL to parse.").Required().String()

	sqlLexer = lexer.Unquote(lexer.Upper(lexer.Must(lexer.Regexp(`(\s+)`+
		`|(?P<Keyword>(?i)SELECT|FROM|TOP|DISTINCT|ALL|WHERE|GROUP|BY|HAVING|UNION|MINUS|EXCEPT|INTERSECT|ORDER|LIMIT|OFFSET|TRUE|FALSE|NULL|IS|NOT|ANY|SOME|BETWEEN|AND|OR|LIKE|AS|IN)`+
		`|(?P<Ident>[a-zA-Z_][a-zA-Z0-9_]*)`+
		`|(?P<Number>[-+]?\d*\.?\d+([eE][-+]?\d+)?)`+
		`|(?P<String>'[^']*'|"[^"]*")`+
		`|(?P<Operators><>|!=|<=|>=|[-+*/%,.()=<>])`,
	)), "Keyword"), "String")
	sqlParser = participle.MustBuild(&Select{}, sqlLexer)
)

type Boolean bool

func (b *Boolean) Capture(values []string) error {
	*b = values[0] == "TRUE"
	return nil
}

// Select, based on http://www.h2database.com/html/grammar.html
type Select struct {
	Top        *Term             `"SELECT" [ "TOP" @@ ]`
	Distinct   bool              `[  @"DISTINCT"`
	All        bool              ` | @"ALL" ]`
	Expression *SelectExpression `@@`
	From       *From             `"FROM" @@`
	Limit      *Expression       `[ "LIMIT" @@ ]`
	Offset     *Expression       `[ "OFFSET" @@ ]`
	GroupBy    *Expression       `[ "GROUP" "BY" @@ ]`
}

type From struct {
	TableExpressions []*TableExpression `@@ { "," @@ }`
	Where            *Expression        `[ "WHERE" @@ ]`
}

type TableExpression struct {
	Table  string        `( @Ident { "." @Ident }`
	Select *Select       `  | "(" @@ ")"`
	Values []*Expression `  | "VALUES" "(" @@ { "," @@ } ")")`
	As     string        `[ "AS" @Ident ]`
}

type SelectExpression struct {
	All         bool                 `  @"*"`
	Expressions []*AliasedExpression `| @@ { "," @@ }`
}

type AliasedExpression struct {
	Expression *Expression `@@`
	As         string      `[ "AS" @Ident ]`
}

type Expression struct {
	And *AndCondition `@@ { "OR" @@ }`
}

type AndCondition struct {
	Or []*Condition `@@ { "AND" @@ }`
}

type Condition struct {
	Operand *ConditionOperand `  @@`
	Not     *Condition        `| "NOT" @@`
	Exists  *Select           `| "EXISTS" "(" @@ ")"`
}

type ConditionOperand struct {
	Operand      *Operand      `@@`
	ConditionRHS *ConditionRHS `[ @@ ]`
}

type ConditionRHS struct {
	Compare *Compare `  @@`
	Is      *Is      `| "IS" @@`
	Between *Between `| "BETWEEN" @@`
	In      *In      `| "IN" "(" @@ ")"`
	Like    *Like    `| "LIKE" @@`
}

type Compare struct {
	Operator string         `@( "<>" | "<=" | ">=" | "=" | "<" | ">" | "!=" )`
	Operand  *Operand       `(  @@`
	Select   *CompareSelect ` | @@ )`
}

type CompareSelect struct {
	All    bool    `(  @"ALL"`
	Any    bool    ` | @"ANY"`
	Some   bool    ` | @"SOME" )`
	Select *Select `"(" @@ ")"`
}

type Like struct {
	Not     bool     `[ @"NOT" ]`
	Operand *Operand `@@`
}

type Is struct {
	Not          bool     `[ @"NOT" ]`
	Null         bool     `( @"NULL"`
	DistinctFrom *Operand `  | "DISTINCT" "FROM" @@ )`
}

type Between struct {
	Start *Operand `@@`
	End   *Operand `"AND" @@`
}

type In struct {
	Select      *Select       `  @@`
	Expressions []*Expression `| @@ { "," @@ }`
}

type Operand struct {
	Summand []*Summand `@@ { "|" "|" @@ }`
}

type Summand struct {
	LHS *Factor `@@`
	Op  string  `[ @("+" | "-")`
	RHS *Factor `  @@ ]`
}

type Factor struct {
	LHS *Term  `@@`
	Op  string `[ @("*" | "/" | "%")`
	RHS *Term  `  @@ ]`
}

type Term struct {
	Select        *Select     `  @@`
	SymbolRef     *SymbolRef  `| @@`
	Value         *Value      `| @@`
	SubExpression *Expression `| "(" @@ ")"`
}

type SymbolRef struct {
	Symbol     string        `@Ident @{ "." Ident }`
	Parameters []*Expression `[ "(" @@ { "," @@ } ")" ]`
}

type Value struct {
	Negated bool `[ @"-" | "+" ]`

	Wildcard bool     `(  @"*"`
	Number   *float64 ` | @Number`
	String   *string  ` | @String`
	Boolean  *Boolean ` | @("TRUE" | "FALSE")`
	Null     bool     ` | @"NULL"`
	Array    *Array   ` | @@ )`
}

type Array struct {
	Expressions []*Expression `"(" @@ { "," @@ } ")"`
}

func main() {
	kingpin.Parse()
	sql := &Select{}
	err := sqlParser.ParseString(*sqlArg, sql)
	kingpin.FatalIfError(err, "")
	repr.Println(sql, repr.Indent("  "), repr.OmitEmpty())
}
<|endoftext|>


<commit_before>/*
 Copyright 2016 Padduck, LLC

 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 programs

import (
	"encoding/json"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/utils"
	"io/ioutil"
	"os"
)

type Program interface {
	//Starts the program.
	//This includes starting the environment if it is not running.
	Start() (err error)

	//Stops the program.
	//This will also stop the environment it is ran in.
	Stop() (err error)

	//Kills the program.
	//This will also stop the environment it is ran in.
	Kill() (err error)

	//Creates any files needed for the program.
	//This includes creating the environment.
	Create() (err error)

	//Destroys the server.
	//This will delete the server, environment, and any files related to it.
	Destroy() (err error)

	Update() (err error)

	Install() (err error)

	//Determines if the server is running.
	IsRunning() (isRunning bool)

	//Sends a command to the process
	//If the program supports input, this will send the arguments to that.
	Execute(command string) (err error)

	SetEnabled(isEnabled bool) (err error)

	IsEnabled() (isEnabled bool)

	SetAutoStart(isAutoStart bool) (err error)

	IsAutoStart() (isAutoStart bool)

	SetEnvironment(environment environments.Environment) (err error)

	Id() string

	GetEnvironment() environments.Environment

	Save(file string) (err error)

	Edit(data map[string]interface{}) (err error)

	Reload(data Program)
}

type ProgramStruct struct {
	RunData     Runtime
	InstallData install.InstallSection
	Environment environments.Environment
	Identifier  string
	Data        map[string]interface{}
}

//Starts the program.
//This includes starting the environment if it is not running.
func (p *ProgramStruct) Start() (err error) {
	logging.Debugf("Starting server %s", p.Id())
	data := make(map[string]interface{})
	for k, v := range p.Data {
		data[k] = v.(map[string]interface{})["value"]
	}
	p.Environment.ExecuteAsync(p.RunData.Program, utils.ReplaceTokensInArr(p.RunData.Arguments, data))
	return
}

//Stops the program.
//This will also stop the environment it is ran in.
func (p *ProgramStruct) Stop() (err error) {
	err = p.Environment.ExecuteInMainProcess(p.RunData.Stop)
	return
}

//Kills the program.
//This will also stop the environment it is ran in.
func (p *ProgramStruct) Kill() (err error) {
	err = p.Environment.Kill()
	return
}

//Creates any files needed for the program.
//This includes creating the environment.
func (p *ProgramStruct) Create() (err error) {
	err = p.Environment.Create()
	return
}

//Destroys the server.
//This will delete the server, environment, and any files related to it.
func (p *ProgramStruct) Destroy() (err error) {
	err = p.Environment.Delete()
	return
}

func (p *ProgramStruct) Update() (err error) {
	err = p.Install()
	return
}

func (p *ProgramStruct) Install() (err error) {
	if p.IsRunning() {
		p.Stop()
	}

	os.MkdirAll(p.Environment.GetRootDirectory(), 0755)

	process := install.GenerateInstallProcess(&p.InstallData, p.Environment, p.Data)
	for process.HasNext() {
		err := process.RunNext()
		if err != nil {
			logging.Error("Error running installer: ", err)
			break
		}
	}
	return
}

//Determines if the server is running.
func (p *ProgramStruct) IsRunning() (isRunning bool) {
	isRunning = p.Environment.IsRunning()
	return
}

//Sends a command to the process
//If the program supports input, this will send the arguments to that.
func (p *ProgramStruct) Execute(command string) (err error) {
	err = p.Environment.ExecuteInMainProcess(command)
	return
}

func (p *ProgramStruct) SetEnabled(isEnabled bool) (err error) {
	p.RunData.Enabled = isEnabled
	return
}

func (p *ProgramStruct) IsEnabled() (isEnabled bool) {
	isEnabled = p.RunData.Enabled
	return
}

func (p *ProgramStruct) SetEnvironment(environment environments.Environment) (err error) {
	p.Environment = environment
	return
}

func (p *ProgramStruct) Id() string {
	return p.Identifier
}

func (p *ProgramStruct) GetEnvironment() environments.Environment {
	return p.Environment
}

func (p *ProgramStruct) SetAutoStart(isAutoStart bool) (err error) {
	p.RunData.AutoStart = isAutoStart
	return
}

func (p *ProgramStruct) IsAutoStart() (isAutoStart bool) {
	return p.RunData.AutoStart
}

func (p *ProgramStruct) Save(file string) (err error) {
	result := make(map[string]interface{})
	result["data"] = p.Data
	result["install"] = p.InstallData
	result["run"] = p.RunData

	endResult := make(map[string]interface{})
	endResult["pufferd"] = result

	data, err := json.Marshal(endResult)
	if err != nil {
		return
	}

	err = ioutil.WriteFile(file, data, 0664)
	return
}

func (p *ProgramStruct) Edit(data map[string]interface{}) (err error) {
	for k, v := range data {
		if v == nil || v == "" {
			delete(p.Data, k)
		}
		p.Data[k] = v
	}
	Save(p.Id())
	return
}

func (p *ProgramStruct) Reload(data Program) {
	replacement := data.(*ProgramStruct)
	p.Data = replacement.Data
	p.InstallData = replacement.InstallData
	p.RunData = replacement.RunData
}

type Runtime struct {
	Stop      string   `json:"stop"`
	Pre       []string `json:"pre,omitempty"`
	Post      []string `json:"post,omitempty"`
	Program   string   `json:"program"`
	Arguments []string `json:"arguments"`
	Enabled   bool     `json:"enabled"`
	AutoStart bool     `json:"autostart"`
}
<commit_msg>Add pretty-print to program Save<commit_after>/*
 Copyright 2016 Padduck, LLC

 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 programs

import (
	"encoding/json"
	"github.com/pufferpanel/pufferd/environments"
	"github.com/pufferpanel/pufferd/install"
	"github.com/pufferpanel/pufferd/logging"
	"github.com/pufferpanel/pufferd/utils"
	"io/ioutil"
	"os"
)

type Program interface {
	//Starts the program.
	//This includes starting the environment if it is not running.
	Start() (err error)

	//Stops the program.
	//This will also stop the environment it is ran in.
	Stop() (err error)

	//Kills the program.
	//This will also stop the environment it is ran in.
	Kill() (err error)

	//Creates any files needed for the program.
	//This includes creating the environment.
	Create() (err error)

	//Destroys the server.
	//This will delete the server, environment, and any files related to it.
	Destroy() (err error)

	Update() (err error)

	Install() (err error)

	//Determines if the server is running.
	IsRunning() (isRunning bool)

	//Sends a command to the process
	//If the program supports input, this will send the arguments to that.
	Execute(command string) (err error)

	SetEnabled(isEnabled bool) (err error)

	IsEnabled() (isEnabled bool)

	SetAutoStart(isAutoStart bool) (err error)

	IsAutoStart() (isAutoStart bool)

	SetEnvironment(environment environments.Environment) (err error)

	Id() string

	GetEnvironment() environments.Environment

	Save(file string) (err error)

	Edit(data map[string]interface{}) (err error)

	Reload(data Program)
}

type ProgramStruct struct {
	RunData     Runtime
	InstallData install.InstallSection
	Environment environments.Environment
	Identifier  string
	Data        map[string]interface{}
}

//Starts the program.
//This includes starting the environment if it is not running.
func (p *ProgramStruct) Start() (err error) {
	logging.Debugf("Starting server %s", p.Id())
	data := make(map[string]interface{})
	for k, v := range p.Data {
		data[k] = v.(map[string]interface{})["value"]
	}
	p.Environment.ExecuteAsync(p.RunData.Program, utils.ReplaceTokensInArr(p.RunData.Arguments, data))
	return
}

//Stops the program.
//This will also stop the environment it is ran in.
func (p *ProgramStruct) Stop() (err error) {
	err = p.Environment.ExecuteInMainProcess(p.RunData.Stop)
	return
}

//Kills the program.
//This will also stop the environment it is ran in.
func (p *ProgramStruct) Kill() (err error) {
	err = p.Environment.Kill()
	return
}

//Creates any files needed for the program.
//This includes creating the environment.
func (p *ProgramStruct) Create() (err error) {
	err = p.Environment.Create()
	return
}

//Destroys the server.
//This will delete the server, environment, and any files related to it.
func (p *ProgramStruct) Destroy() (err error) {
	err = p.Environment.Delete()
	return
}

func (p *ProgramStruct) Update() (err error) {
	err = p.Install()
	return
}

func (p *ProgramStruct) Install() (err error) {
	if p.IsRunning() {
		p.Stop()
	}

	os.MkdirAll(p.Environment.GetRootDirectory(), 0755)

	process := install.GenerateInstallProcess(&p.InstallData, p.Environment, p.Data)
	for process.HasNext() {
		err := process.RunNext()
		if err != nil {
			logging.Error("Error running installer: ", err)
			break
		}
	}
	return
}

//Determines if the server is running.
func (p *ProgramStruct) IsRunning() (isRunning bool) {
	isRunning = p.Environment.IsRunning()
	return
}

//Sends a command to the process
//If the program supports input, this will send the arguments to that.
func (p *ProgramStruct) Execute(command string) (err error) {
	err = p.Environment.ExecuteInMainProcess(command)
	return
}

func (p *ProgramStruct) SetEnabled(isEnabled bool) (err error) {
	p.RunData.Enabled = isEnabled
	return
}

func (p *ProgramStruct) IsEnabled() (isEnabled bool) {
	isEnabled = p.RunData.Enabled
	return
}

func (p *ProgramStruct) SetEnvironment(environment environments.Environment) (err error) {
	p.Environment = environment
	return
}

func (p *ProgramStruct) Id() string {
	return p.Identifier
}

func (p *ProgramStruct) GetEnvironment() environments.Environment {
	return p.Environment
}

func (p *ProgramStruct) SetAutoStart(isAutoStart bool) (err error) {
	p.RunData.AutoStart = isAutoStart
	return
}

func (p *ProgramStruct) IsAutoStart() (isAutoStart bool) {
	return p.RunData.AutoStart
}

func (p *ProgramStruct) Save(file string) (err error) {
	result := make(map[string]interface{})
	result["data"] = p.Data
	result["install"] = p.InstallData
	result["run"] = p.RunData

	endResult := make(map[string]interface{})
	endResult["pufferd"] = result

	data, err := json.MarshalIndent(endResult, "", "  ")
	if err != nil {
		return
	}

	err = ioutil.WriteFile(file, data, 0664)
	return
}

func (p *ProgramStruct) Edit(data map[string]interface{}) (err error) {
	for k, v := range data {
		if v == nil || v == "" {
			delete(p.Data, k)
		}
		p.Data[k] = v
	}
	Save(p.Id())
	return
}

func (p *ProgramStruct) Reload(data Program) {
	replacement := data.(*ProgramStruct)
	p.Data = replacement.Data
	p.InstallData = replacement.InstallData
	p.RunData = replacement.RunData
}

type Runtime struct {
	Stop      string   `json:"stop"`
	Pre       []string `json:"pre,omitempty"`
	Post      []string `json:"post,omitempty"`
	Program   string   `json:"program"`
	Arguments []string `json:"arguments"`
	Enabled   bool     `json:"enabled"`
	AutoStart bool     `json:"autostart"`
}
<|endoftext|>


<commit_before>// Copyright 2019 Google LLC
//
// 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 cablib is a library of shared constants and functions.
package cablib

import (
	"fmt"
	"os"
	"reflect"
	"strings"
	"time"

	"github.com/google/cabbie/notification"
	"github.com/google/cabbie/reboot"
	"golang.org/x/sys/windows/registry"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

const (
	// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
	S_FALSE = 0x00000001
	// S_OK is the return HResult for successful method calls.
	S_OK = 0x00000000
	// LogSrcName is the name of event log source.
	LogSrcName = "Cabbie"
	// SvcName is the name of the registered Service.
	SvcName = "Cabbie"
	// ExePath is the windows path to the cabbie executable.
	ExePath = `C:\Program Files\Google\Cabbie\cabbie.exe`
	// WUReg is the registry path to the local update client configuration.
	WUReg = `SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate`
	// MetricSvc is service name of a metric.
	MetricSvc = "Cabbie"
	// MetricRoot is the root path for a metric.
	MetricRoot = `Cabbie\metrics`

	rebootValue = "RebootTime"
)

var (
	now            = time.Now
	rebootRequired = RebootRequired
	// RegPath is the registry path to the cabbie settings.
	RegPath = `SOFTWARE\Google\Cabbie\`
)

// StringToSlice converts a comma separated string to a slice.
func StringToSlice(s string) []string {

	if strings.TrimSpace(s) == "" {
		return nil
	}

	a := strings.Split(s, ",")
	for i, item := range a {
		a[i] = strings.TrimSpace(item)
	}
	return a
}

// StringInSlice checks if a slice contains a string.
func StringInSlice(e string, s []string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// InitializeCOM safely initializes the COM library for use by the calling thread.
func InitializeCOM() error {
	//TODO: remove multiple calls to this function.
	if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil {
		oleCode := err.(*ole.OleError).Code()
		if oleCode != ole.S_OK && oleCode != S_FALSE {
			return fmt.Errorf("failed to start OLE initialization: %v", err)
		}
	}
	return nil
}

// SetRebootTime creates the reboot time key.
func SetRebootTime(seconds uint64) error {
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, RegPath, registry.SET_VALUE)
	if err != nil {
		return err
	}
	defer k.Close()

	t := now().Add(time.Second * time.Duration(seconds))
	b, err := t.MarshalBinary()
	if err != nil {
		return err
	}
	return k.SetBinaryValue(rebootValue, b)
}

// RebootTime gets the value of "rebootValue" from the registry.
func RebootTime() (time.Time, error) {
	var t time.Time
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, RegPath, registry.ALL_ACCESS)
	if err != nil {
		return t, err
	}
	defer k.Close()

	b, _, err := k.GetBinaryValue(rebootValue)
	if err != nil {
		if err == registry.ErrNotExist {
			return t, nil
		}
		return t, fmt.Errorf("unable to get scheduled reboot time: %v", err)
	}

	// Remove timer if no longer pending a reboot.
	rbr, err := rebootRequired()
	if err != nil {
		return t, err
	}
	if !rbr {
		return t, k.DeleteValue(rebootValue)
	}

	if err := t.UnmarshalBinary(b); err != nil {
		return t, fmt.Errorf("unable to Unmarshal binary data: %v", err)
	}

	return t, nil
}

// SystemReboot initates a restart when the set reboot time has passed. This should be called within a goroutine
func SystemReboot(t time.Time) error {
	time.Sleep(time.Until(t))

	notification.RebootPopup(2)
	time.Sleep(2 * time.Minute)

	return reboot.Now()
}

// Count gets the count property of an IDispatch object.
func Count(id *ole.IDispatch) (int, error) {
	count, err := oleutil.GetProperty(id, "Count")
	if err != nil {
		return 0, fmt.Errorf("error getting update count, %v", err)
	}
	defer count.Clear()
	return int(count.Val), nil
}

// NewCOMObject creates a new COM object for the specifed ProgramID.
func NewCOMObject(id string) (*ole.IDispatch, error) {
	unknown, err := oleutil.CreateObject(id)
	if err != nil {
		return nil, fmt.Errorf("unable to create initial unknown object: %v", err)
	}
	defer unknown.Release()

	obj, err := unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		return nil, fmt.Errorf("Unable to create query interface: %v", err)
	}

	return obj, nil
}

// RebootRequired indicates whether a system restart is required.
func RebootRequired() (bool, error) {

	sysinfo, err := NewCOMObject("Microsoft.Update.SystemInfo")
	if err != nil {
		return false, err
	}
	defer sysinfo.Release()

	r, err := oleutil.GetProperty(sysinfo, "RebootRequired")
	if err != nil {
		return false, fmt.Errorf("failed to get RebootRequired property: %v", err)
	}
	defer r.Clear()

	return r.Value().(bool), nil
}

// GetUpdateTitles loops through an update collection and returns a list of titles.
func GetUpdateTitles(collection *ole.IDispatch, count int) ([]string, []error) {
	var errors []error
	var u []string

	for i := 0; i < count; i++ {
		// Get update at position i
		item, err := oleutil.GetProperty(collection, "item", i)
		if err != nil {
			errors = append(errors, err)
			continue
		}
		itemd := item.ToIDispatch()

		// Get selected updates title
		title, err := oleutil.GetProperty(itemd, "Title")
		if err != nil {
			errors = append(errors, err)
			continue
		}

		u = append(u, title.ToString())
		itemd.Release()
		title.Clear()
	}

	if len(errors) > 0 {
		return nil, errors
	}
	return u, nil
}

// SetField sets the value of a struct field based on the field name.
func SetField(obj interface{}, name string, value interface{}) error {
	structValue := reflect.ValueOf(obj).Elem()
	structFieldValue := structValue.FieldByName(name)

	if !structFieldValue.IsValid() {
		return fmt.Errorf("no such field: %s in obj", name)
	}

	if !structFieldValue.CanSet() {
		return fmt.Errorf("cannot set %s field value", name)
	}

	structFieldType := structFieldValue.Type()
	val := reflect.ValueOf(value)

	if structFieldType.AssignableTo(val.Type()) {
		structFieldValue.Set(val)
		return nil
	}

	return fmt.Errorf("provided value type (%v) didn't match obj field type (%v)", val.Type(), structFieldType)
}

// PathExists used for determining if given path exists.
func PathExists(path string) (bool, error) {
	if path == "" {
		return false, fmt.Errorf("pathExists: received empty string to test")
	}

	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	return true, nil
}

// SliceContains evaluates if a given value is in the passed slice.
func SliceContains(slice interface{}, v interface{}) bool {
	list := reflect.ValueOf(slice)
	for i := 0; i < list.Len(); i++ {
		if list.Index(i).Interface() == v {
			return true
		}
	}
	return false
}
<commit_msg>Internal Change<commit_after>// Copyright 2019 Google LLC
//
// 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 cablib is a library of shared constants and functions.
package cablib

import (
	"fmt"
	"os"
	"reflect"
	"strings"
	"time"

	"github.com/google/cabbie/notification"
	"github.com/google/cabbie/reboot"
	"golang.org/x/sys/windows/registry"
	"github.com/go-ole/go-ole"
	"github.com/go-ole/go-ole/oleutil"
)

const (
	// S_FALSE is returned by CoInitializeEx if it was already called on this thread.
	S_FALSE = 0x00000001
	// S_OK is the return HResult for successful method calls.
	S_OK = 0x00000000
	// LogSrcName is the name of event log source.
	LogSrcName = "Cabbie"
	// SvcName is the name of the registered Service.
	SvcName = "Cabbie"
	// ExePath is the windows path to the cabbie executable.
	ExePath = `C:\Program Files\Google\Cabbie\cabbie.exe`
	// WUReg is the registry path to the local update client configuration.
	WUReg = `SOFTWARE\Policies\Microsoft\Windows\WindowsUpdate`
	// MetricSvc is service name of a metric.
	MetricSvc = "Cabbie"
	// MetricRoot is the root path for a metric.
	MetricRoot = `Cabbie\metrics`

	rebootValue = "RebootTime"
)

var (
	now            = time.Now
	rebootRequired = RebootRequired
	// RegPath is the registry path to the cabbie settings.
	RegPath = `SOFTWARE\Google\Cabbie\`
)

// StringToSlice converts a comma separated string to a slice.
func StringToSlice(s string) []string {

	if strings.TrimSpace(s) == "" {
		return nil
	}

	a := strings.Split(s, ",")
	for i, item := range a {
		a[i] = strings.TrimSpace(item)
	}
	return a
}

// StringInSlice checks if a slice contains a string.
func StringInSlice(e string, s []string) bool {
	for _, a := range s {
		if a == e {
			return true
		}
	}
	return false
}

// InitializeCOM safely initializes the COM library for use by the calling thread.
func InitializeCOM() error {
	//TODO: remove multiple calls to this function.
	if err := ole.CoInitializeEx(0, ole.COINIT_MULTITHREADED); err != nil {
		oleCode := err.(*ole.OleError).Code()
		if oleCode != ole.S_OK && oleCode != S_FALSE {
			return fmt.Errorf("failed to start OLE initialization: %v", err)
		}
	}
	return nil
}

// SetRebootTime creates the reboot time key.
func SetRebootTime(seconds uint64) error {
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, RegPath, registry.SET_VALUE)
	if err != nil {
		return err
	}
	defer k.Close()

	t := now().Add(time.Second * time.Duration(seconds))
	b, err := t.MarshalBinary()
	if err != nil {
		return err
	}
	return k.SetBinaryValue(rebootValue, b)
}

// RebootTime gets the value of "rebootValue" from the registry.
func RebootTime() (time.Time, error) {
	var t time.Time
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, RegPath, registry.ALL_ACCESS)
	if err != nil {
		return t, err
	}
	defer k.Close()

	b, _, err := k.GetBinaryValue(rebootValue)
	if err != nil {
		if err == registry.ErrNotExist {
			return t, nil
		}
		return t, fmt.Errorf("unable to get scheduled reboot time: %v", err)
	}

	// Remove timer if no longer pending a reboot.
	rbr, err := rebootRequired()
	if err != nil {
		return t, err
	}
	if !rbr {
		return t, k.DeleteValue(rebootValue)
	}

	if err := t.UnmarshalBinary(b); err != nil {
		return t, fmt.Errorf("unable to Unmarshal binary data: %v", err)
	}

	return t, nil
}

func cleanRebootValue() error {
	k, err := registry.OpenKey(registry.LOCAL_MACHINE, RegPath, registry.SET_VALUE)
	if err != nil {
		return err
	}
	defer k.Close()

	return k.DeleteValue(rebootValue)
}

// SystemReboot initates a restart when the set reboot time has passed. This should be called within a goroutine
func SystemReboot(t time.Time) error {
	time.Sleep(time.Until(t))

	if err := notification.NewNotification(SvcName, notification.RebootPopup(2), "rebootPending"); err != nil {
		return fmt.Errorf("failed to create system reboot notification:%v", err)
	}

	time.Sleep(2 * time.Minute)

	if err := cleanRebootValue(); err != nil {
		return fmt.Errorf("failed to clean up registry value %q: %v", rebootValue, err)
	}
	return reboot.Now()
}

// Count gets the count property of an IDispatch object.
func Count(id *ole.IDispatch) (int, error) {
	count, err := oleutil.GetProperty(id, "Count")
	if err != nil {
		return 0, fmt.Errorf("error getting update count, %v", err)
	}
	defer count.Clear()
	return int(count.Val), nil
}

// NewCOMObject creates a new COM object for the specifed ProgramID.
func NewCOMObject(id string) (*ole.IDispatch, error) {
	unknown, err := oleutil.CreateObject(id)
	if err != nil {
		return nil, fmt.Errorf("unable to create initial unknown object: %v", err)
	}
	defer unknown.Release()

	obj, err := unknown.QueryInterface(ole.IID_IDispatch)
	if err != nil {
		return nil, fmt.Errorf("Unable to create query interface: %v", err)
	}

	return obj, nil
}

// RebootRequired indicates whether a system restart is required.
func RebootRequired() (bool, error) {

	sysinfo, err := NewCOMObject("Microsoft.Update.SystemInfo")
	if err != nil {
		return false, err
	}
	defer sysinfo.Release()

	r, err := oleutil.GetProperty(sysinfo, "RebootRequired")
	if err != nil {
		return false, fmt.Errorf("failed to get RebootRequired property: %v", err)
	}
	defer r.Clear()

	return r.Value().(bool), nil
}

// GetUpdateTitles loops through an update collection and returns a list of titles.
func GetUpdateTitles(collection *ole.IDispatch, count int) ([]string, []error) {
	var errors []error
	var u []string

	for i := 0; i < count; i++ {
		// Get update at position i
		item, err := oleutil.GetProperty(collection, "item", i)
		if err != nil {
			errors = append(errors, err)
			continue
		}
		itemd := item.ToIDispatch()

		// Get selected updates title
		title, err := oleutil.GetProperty(itemd, "Title")
		if err != nil {
			errors = append(errors, err)
			continue
		}

		u = append(u, title.ToString())
		itemd.Release()
		title.Clear()
	}

	if len(errors) > 0 {
		return nil, errors
	}
	return u, nil
}

// SetField sets the value of a struct field based on the field name.
func SetField(obj interface{}, name string, value interface{}) error {
	structValue := reflect.ValueOf(obj).Elem()
	structFieldValue := structValue.FieldByName(name)

	if !structFieldValue.IsValid() {
		return fmt.Errorf("no such field: %s in obj", name)
	}

	if !structFieldValue.CanSet() {
		return fmt.Errorf("cannot set %s field value", name)
	}

	structFieldType := structFieldValue.Type()
	val := reflect.ValueOf(value)

	if structFieldType.AssignableTo(val.Type()) {
		structFieldValue.Set(val)
		return nil
	}

	return fmt.Errorf("provided value type (%v) didn't match obj field type (%v)", val.Type(), structFieldType)
}

// PathExists used for determining if given path exists.
func PathExists(path string) (bool, error) {
	if path == "" {
		return false, fmt.Errorf("pathExists: received empty string to test")
	}

	_, err := os.Stat(path)
	if os.IsNotExist(err) {
		return false, nil
	}
	if err != nil {
		return false, err
	}
	return true, nil
}

// SliceContains evaluates if a given value is in the passed slice.
func SliceContains(slice interface{}, v interface{}) bool {
	list := reflect.ValueOf(slice)
	for i := 0; i < list.Len(); i++ {
		if list.Index(i).Interface() == v {
			return true
		}
	}
	return false
}
<|endoftext|>


<commit_before>package cephfs

/*
#include <errno.h>
*/
import "C"

import (
	"fmt"

	"github.com/ceph/go-ceph/internal/errutil"
)

// cephFSError represents an error condition returned from the CephFS APIs.
type cephFSError int

// Error returns the error string for the cephFSError type.
func (e cephFSError) Error() string {
	errno, s := errutil.FormatErrno(int(e))
	if s == "" {
		return fmt.Sprintf("cephfs: ret=%d", errno)
	}
	return fmt.Sprintf("cephfs: ret=%d, %s", errno, s)
}

func (e cephFSError) Errno() int {
	return int(e)
}

func getError(e C.int) error {
	if e == 0 {
		return nil
	}
	return cephFSError(e)
}

// Public go errors:

const (
	// ErrNotConnected may be returned when client is not connected
	// to a cluster.
	ErrNotConnected = cephFSError(-C.ENOTCONN)
)

// Private errors:

const (
	errInvalid     = cephFSError(-C.EINVAL)
	errNameTooLong = cephFSError(-C.ENAMETOOLONG)
	errNoEntry     = cephFSError(-C.ENOENT)
)
<commit_msg>cephfs: add error for function args that must have data<commit_after>package cephfs

/*
#include <errno.h>
*/
import "C"

import (
	"errors"
	"fmt"

	"github.com/ceph/go-ceph/internal/errutil"
)

// cephFSError represents an error condition returned from the CephFS APIs.
type cephFSError int

// Error returns the error string for the cephFSError type.
func (e cephFSError) Error() string {
	errno, s := errutil.FormatErrno(int(e))
	if s == "" {
		return fmt.Sprintf("cephfs: ret=%d", errno)
	}
	return fmt.Sprintf("cephfs: ret=%d, %s", errno, s)
}

func (e cephFSError) Errno() int {
	return int(e)
}

func getError(e C.int) error {
	if e == 0 {
		return nil
	}
	return cephFSError(e)
}

// Public go errors:

var (
	// ErrEmptyArgument may be returned if a function argument is passed
	// a zero-length slice or map.
	ErrEmptyArgument = errors.New("Argument must contain at least one item")
)

// Public CephFSErrors:

const (
	// ErrNotConnected may be returned when client is not connected
	// to a cluster.
	ErrNotConnected = cephFSError(-C.ENOTCONN)
)

// Private errors:

const (
	errInvalid     = cephFSError(-C.EINVAL)
	errNameTooLong = cephFSError(-C.ENAMETOOLONG)
	errNoEntry     = cephFSError(-C.ENOENT)
)
<|endoftext|>


<commit_before>package task

import (
	"bufio"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cenkalti/yap/datetime"
	"github.com/satori/go.uuid"
)

const taskExt = ".task"

// Task is a file stored in tasks dir.
type Task struct {
	ID          uint16
	UUID        uuid.UUID
	Title       string
	CreatedAt   time.Time
	CompletedAt *time.Time
	DueDate     *datetime.DateTime
	WaitDate    *datetime.DateTime
}

func readFile(filename string) (t Task, err error) {
	base := filepath.Base(filename)
	t.UUID, err = uuid.FromString(base[:len(base)-len(taskExt)])
	if err != nil {
		return
	}
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer checkClose(f)
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		text := strings.TrimSpace(scanner.Text())
		if text == "" {
			continue
		}
		parts := strings.SplitN(text, " ", 2)
		if len(parts) != 2 {
			err = errors.New("invalid task file")
			return
		}
		err = t.setKeyVal(parts[0], parts[1])
		if err != nil {
			return
		}
	}
	err = scanner.Err()
	return
}

func (t *Task) setKeyVal(key, value string) (err error) {
	switch key {
	case "title":
		t.Title = value
	case "created_at":
		t.CreatedAt, err = time.Parse(time.RFC3339Nano, value)
		if err != nil {
			return
		}
	case "completed_at":
		var ctime time.Time
		ctime, err = time.Parse(time.RFC3339Nano, value)
		if err != nil {
			return
		}
		t.CompletedAt = &ctime
	case "due_date":
		var dt datetime.DateTime
		dt, err = datetime.Parse(value)
		if err != nil {
			return
		}
		t.DueDate = &dt
	case "wait_date":
		var dt datetime.DateTime
		dt, err = datetime.Parse(value)
		if err != nil {
			return
		}
		t.WaitDate = &dt
	default:
		err = errors.New("invalid key")
	}
	return
}

// write the task to file at <dirTasks>/<UUID>.task
func (t Task) write() error {
	path := filepath.Join(dirTasks, t.UUID.String()) + taskExt
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(f)
	if _, err = w.WriteString("title " + t.Title + "\n"); err != nil {
		return err
	}
	if _, err = w.WriteString("created_at " + t.CreatedAt.Format(time.RFC3339Nano) + "\n"); err != nil {
		return err
	}
	if t.CompletedAt != nil {
		if _, err = w.WriteString("completed_at " + t.CompletedAt.Format(time.RFC3339Nano) + "\n"); err != nil {
			return err
		}
	}
	if t.DueDate != nil {
		if _, err = w.WriteString("due_date " + t.DueDate.String() + "\n"); err != nil {
			return err
		}
	}
	if t.WaitDate != nil {
		if _, err = w.WriteString("wait_date " + t.WaitDate.String() + "\n"); err != nil {
			return err
		}
	}
	if err = w.Flush(); err != nil {
		return err
	}
	if err = f.Sync(); err != nil {
		return err
	}
	return f.Close()
}
<commit_msg>reduce cyclomatic complexity<commit_after>package task

import (
	"bufio"
	"errors"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/cenkalti/yap/datetime"
	"github.com/satori/go.uuid"
)

const taskExt = ".task"

// Task is a file stored in tasks dir.
type Task struct {
	ID          uint16
	UUID        uuid.UUID
	Title       string
	CreatedAt   time.Time
	CompletedAt *time.Time
	DueDate     *datetime.DateTime
	WaitDate    *datetime.DateTime
}

func readFile(filename string) (t Task, err error) {
	base := filepath.Base(filename)
	t.UUID, err = uuid.FromString(base[:len(base)-len(taskExt)])
	if err != nil {
		return
	}
	f, err := os.Open(filename)
	if err != nil {
		return
	}
	defer checkClose(f)
	scanner := bufio.NewScanner(f)
	for scanner.Scan() {
		text := strings.TrimSpace(scanner.Text())
		if text == "" {
			continue
		}
		parts := strings.SplitN(text, " ", 2)
		if len(parts) != 2 {
			err = errors.New("invalid task file")
			return
		}
		err = t.setKeyVal(parts[0], parts[1])
		if err != nil {
			return
		}
	}
	err = scanner.Err()
	return
}

var parsers = map[string]func(t *Task, value string) error{
	"title": func(t *Task, value string) (err error) {
		t.Title = value
		return
	},
	"created_at": func(t *Task, value string) (err error) {
		t.CreatedAt, err = time.Parse(time.RFC3339Nano, value)
		return
	},
	"completed_at": func(t *Task, value string) (err error) {
		var ctime time.Time
		ctime, err = time.Parse(time.RFC3339Nano, value)
		t.CompletedAt = &ctime
		return
	},
	"due_date": func(t *Task, value string) (err error) {
		var dt datetime.DateTime
		dt, err = datetime.Parse(value)
		t.DueDate = &dt
		return
	},
	"wait_date": func(t *Task, value string) (err error) {
		var dt datetime.DateTime
		dt, err = datetime.Parse(value)
		t.WaitDate = &dt
		return
	},
}

func (t *Task) setKeyVal(key, value string) error {
	f, ok := parsers[key]
	if !ok {
		return errors.New("invalid key")
	}
	return f(t, value)
}

// write the task to file at <dirTasks>/<UUID>.task
func (t Task) write() error {
	path := filepath.Join(dirTasks, t.UUID.String()) + taskExt
	f, err := os.Create(path)
	if err != nil {
		return err
	}
	w := bufio.NewWriter(f)
	if _, err = w.WriteString("title " + t.Title + "\n"); err != nil {
		return err
	}
	if _, err = w.WriteString("created_at " + t.CreatedAt.Format(time.RFC3339Nano) + "\n"); err != nil {
		return err
	}
	if t.CompletedAt != nil {
		if _, err = w.WriteString("completed_at " + t.CompletedAt.Format(time.RFC3339Nano) + "\n"); err != nil {
			return err
		}
	}
	if t.DueDate != nil {
		if _, err = w.WriteString("due_date " + t.DueDate.String() + "\n"); err != nil {
			return err
		}
	}
	if t.WaitDate != nil {
		if _, err = w.WriteString("wait_date " + t.WaitDate.String() + "\n"); err != nil {
			return err
		}
	}
	if err = w.Flush(); err != nil {
		return err
	}
	if err = f.Sync(); err != nil {
		return err
	}
	return f.Close()
}
<|endoftext|>


<commit_before>package scipipe

import (
	"os"
	"testing"
)

func TestTempDirsExist(t *testing.T) {
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{"in1": NewFileIP("infile.txt"), "in2": NewFileIP("infile2.txt")}, nil, nil, map[string]string{"p1": "p1val", "p2": "p2val"}, nil, "", nil, 4)
	err := os.Mkdir(tsk.TempDir(), 0644)
	Check(err)
	exists := tsk.tempDirsExist()
	if !exists {
		t.Errorf("TempDirsExist returned false, even though directory exists: %s\n", tsk.TempDir())
	}
	err = os.Remove(tsk.TempDir())
	Check(err)
}

func TestTempDir(t *testing.T) {
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{"in1": NewFileIP("infile.txt"), "in2": NewFileIP("infile2.txt")}, nil, nil, map[string]string{"p1": "p1val", "p2": "p2val"}, nil, "", nil, 4)

	expected := tempDirPrefix + ".test_task.aaa94846ee057056e7f2d4d3aa2236bdf353d5a1"
	actual := tsk.TempDir()
	if actual != expected {
		t.Errorf("TempDir() was %s Expected: %s", actual, expected)
	}
}

func TestTempDirNotOver255(t *testing.T) {
	longFileName := "very_long_filename_______________________________50_______________________________________________100_______________________________________________150_______________________________________________200_______________________________________________250__255_____"
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{"in1": NewFileIP(longFileName), "in2": NewFileIP("infile2.txt")}, nil, nil, map[string]string{"p1": "p1val", "p2": "p2val"}, nil, "", nil, 4)

	actual := len(tsk.TempDir())
	maxLen := 255
	if actual > 256 {
		t.Errorf("TempDir() generated too long a string: %d chars, should be max %d chars\nString was: %s", actual, maxLen, tsk.TempDir())
	}
}
<commit_msg>Add test for AtomizeIP's extra file moving'<commit_after>package scipipe

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

func TestTempDirsExist(t *testing.T) {
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{"in1": NewFileIP("infile.txt"), "in2": NewFileIP("infile2.txt")}, nil, nil, map[string]string{"p1": "p1val", "p2": "p2val"}, nil, "", nil, 4)
	err := os.Mkdir(tsk.TempDir(), 0644)
	Check(err)
	exists := tsk.tempDirsExist()
	if !exists {
		t.Errorf("TempDirsExist returned false, even though directory exists: %s\n", tsk.TempDir())
	}
	err = os.Remove(tsk.TempDir())
	Check(err)
}

func TestTempDir(t *testing.T) {
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{"in1": NewFileIP("infile.txt"), "in2": NewFileIP("infile2.txt")}, nil, nil, map[string]string{"p1": "p1val", "p2": "p2val"}, nil, "", nil, 4)

	expected := tempDirPrefix + ".test_task.aaa94846ee057056e7f2d4d3aa2236bdf353d5a1"
	actual := tsk.TempDir()
	if actual != expected {
		t.Errorf("TempDir() was %s Expected: %s", actual, expected)
	}
}

func TestTempDirNotOver255(t *testing.T) {
	longFileName := "very_long_filename_______________________________50_______________________________________________100_______________________________________________150_______________________________________________200_______________________________________________250__255_____"
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{"in1": NewFileIP(longFileName), "in2": NewFileIP("infile2.txt")}, nil, nil, map[string]string{"p1": "p1val", "p2": "p2val"}, nil, "", nil, 4)

	actual := len(tsk.TempDir())
	maxLen := 255
	if actual > 256 {
		t.Errorf("TempDir() generated too long a string: %d chars, should be max %d chars\nString was: %s", actual, maxLen, tsk.TempDir())
	}
}

func TestExtraFilesAtomize(t *testing.T) {
	// Since Atomize calls Debug, the logger needs to be non-nil
	InitLogError()
	tsk := NewTask(nil, nil, "test_task", "echo foo", map[string]*FileIP{}, nil, nil, map[string]string{}, nil, "", nil, 4)
	// Create extra file
	tmpDir := tsk.TempDir()
	os.MkdirAll(tmpDir, 0777)
	fName := filepath.Join(tmpDir, "letterfile_a.txt")
	_, err := os.Create(fName)
	if err != nil {
		t.Fatalf("File could not be created: %s\n", fName)
	}
	tsk.atomizeIPs()
	filePath := filepath.Join(".", "letterfile_a.txt")
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		t.Error("File did not exist: " + filePath)
	}
}

func TestExtraFilesAtomizeAbsolute(t *testing.T) {
	// Since Atomize calls Debug, the logger needs to be non-nil
	InitLogError()

	// Create extra file
	tmpDir, err := ioutil.TempDir("", "TestExtraFilesAtomizeAbsolute")
	if err != nil {
		t.Fatal("could not create tmpDir: ", err)
	}

	absDir := filepath.Join(tmpDir, FSRootPlaceHolder, tmpDir)
	os.MkdirAll(absDir, 0777)
	fName := filepath.Join(absDir, "letterfile_a.txt")
	_, err = os.Create(fName)
	if err != nil {
		t.Fatalf("File could not be created: %s\n", fName)
	}
	AtomizeIPs(tmpDir)
	filePath := filepath.Join(tmpDir, "letterfile_a.txt")
	if _, err := os.Stat(filePath); os.IsNotExist(err) {
		t.Error("File did not exist: " + filePath)
	}
	// Ensure tmpDir wasn't removed by Atomize
	if _, err := os.Stat(tmpDir); os.IsNotExist(err) {
		t.Error("Atomize removed absolute directory")
	}
}
<|endoftext|>


<commit_before>package task_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestDeps(t *testing.T) {
	const dir = "testdata/deps"

	files := []string{
		"d1.txt",
		"d2.txt",
		"d3.txt",
		"d11.txt",
		"d12.txt",
		"d13.txt",
		"d21.txt",
		"d22.txt",
		"d23.txt",
		"d31.txt",
		"d32.txt",
		"d33.txt",
	}

	for _, f := range files {
		_ = os.Remove(f)
	}

	c := exec.Command("task")
	c.Dir = dir
	if err := c.Run(); err != nil {
		t.Error(err)
		return
	}

	for _, f := range files {
		f = filepath.Join(dir, f)
		if _, err := os.Stat(f); err != nil {
			t.Errorf("File %s should exists", f)
		}
	}
}

func TestVars(t *testing.T) {
	const dir = "testdata/vars"

	files := []struct {
		file    string
		content string
	}{
		{"foo.txt", "foo"},
		{"bar.txt", "bar"},
		{"foo2.txt", "foo2"},
		{"bar2.txt", "bar2"},
	}

	for _, f := range files {
		_ = os.Remove(filepath.Join(dir, f.file))
	}

	c := exec.Command("task")
	c.Dir = dir

	if err := c.Run(); err != nil {
		t.Error(err)
		return
	}

	for _, f := range files {
		d, err := ioutil.ReadFile(filepath.Join(dir, f.file))
		if err != nil {
			t.Errorf("Error reading %s: %v", f.file, err)
		}
		s := string(d)
		s = strings.TrimSpace(s)

		if s != f.content {
			t.Errorf("File content should be %s but is %s", f.content, s)
		}
	}
}
<commit_msg>Fix delete of files before test run<commit_after>package task_test

import (
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"
	"testing"
)

func TestDeps(t *testing.T) {
	const dir = "testdata/deps"

	files := []string{
		"d1.txt",
		"d2.txt",
		"d3.txt",
		"d11.txt",
		"d12.txt",
		"d13.txt",
		"d21.txt",
		"d22.txt",
		"d23.txt",
		"d31.txt",
		"d32.txt",
		"d33.txt",
	}

	for _, f := range files {
		_ = os.Remove(filepath.Join(dir, f))
	}

	c := exec.Command("task")
	c.Dir = dir
	if err := c.Run(); err != nil {
		t.Error(err)
		return
	}

	for _, f := range files {
		f = filepath.Join(dir, f)
		if _, err := os.Stat(f); err != nil {
			t.Errorf("File %s should exists", f)
		}
	}
}

func TestVars(t *testing.T) {
	const dir = "testdata/vars"

	files := []struct {
		file    string
		content string
	}{
		{"foo.txt", "foo"},
		{"bar.txt", "bar"},
		{"foo2.txt", "foo2"},
		{"bar2.txt", "bar2"},
	}

	for _, f := range files {
		_ = os.Remove(filepath.Join(dir, f.file))
	}

	c := exec.Command("task")
	c.Dir = dir

	if err := c.Run(); err != nil {
		t.Error(err)
		return
	}

	for _, f := range files {
		d, err := ioutil.ReadFile(filepath.Join(dir, f.file))
		if err != nil {
			t.Errorf("Error reading %s: %v", f.file, err)
		}
		s := string(d)
		s = strings.TrimSpace(s)

		if s != f.content {
			t.Errorf("File content should be %s but is %s", f.content, s)
		}
	}
}
<|endoftext|>


<commit_before>package main

// index template
const index = `[[define "index"]]<!doctype html>

[[template "head" . ]]

<ng-include src="'pages/global.html'"></ng-include>

[[template "header" . ]]

[[end]]`

// head items
const head = `[[define "head"]]
<base href="/[[ .base ]]">
<title ng-bind="page.title">[[ .title ]]</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="[[ .desc ]]" />[[if .nsfw]]
<meta name="rating" content="adult" />
<meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />
[[end]]
<link rel="stylesheet" href="/assets/prim/[[ .primcss ]]" />
<link rel="stylesheet" href="/assets/styles/[[ .style ]]" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

[[template "angular" . ]][[template "headinclude" . ]]
[[end]]`

// angular config
const angular = `[[define "angular"]][[end]]`

// site header
const header = `[[define "header"]]

[[template "navmenuinclude" . ]][[template "navmenu" . ]]

<ng-include src="'pages/menus/nav.html'"></ng-include>

Sign in

<ng-include src="'pages/menus/user.html'"></ng-include>

[[end]]`

const navmenu = `[[define "navmenu"]][[ range $ib := .imageboards]]
[[ $ib.Title ]]

[[end]][[end]]`
<commit_msg>ng-csp breaks typeahead dropdown<commit_after>package main

// index template
const index = `[[define "index"]]<!doctype html>

[[template "head" . ]]

<ng-include src="'pages/global.html'"></ng-include>

[[template "header" . ]]

[[end]]`

// head items
const head = `[[define "head"]]
<base href="/[[ .base ]]">
<title ng-bind="page.title">[[ .title ]]</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="[[ .desc ]]" />[[if .nsfw]]
<meta name="rating" content="adult" />
<meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />
[[end]]
<link rel="stylesheet" href="/assets/prim/[[ .primcss ]]" />
<link rel="stylesheet" href="/assets/styles/[[ .style ]]" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

[[template "angular" . ]][[template "headinclude" . ]]
[[end]]`

// angular config
const angular = `[[define "angular"]][[end]]`

// site header
const header = `[[define "header"]]

[[template "navmenuinclude" . ]][[template "navmenu" . ]]

<ng-include src="'pages/menus/nav.html'"></ng-include>

Sign in

<ng-include src="'pages/menus/user.html'"></ng-include>

[[end]]`

const navmenu = `[[define "navmenu"]][[ range $ib := .imageboards]]
[[ $ib.Title ]]

[[end]][[end]]`
<|endoftext|>


<commit_before>package main

// index template
const index = `[[define "index"]]<!doctype html>

[[template "head" . ]]

<ng-include src="'pages/global.html'"></ng-include>

[[template "header" . ]]

[[end]]`

// head items
const head = `[[define "head"]]
<base href="/[[ .base ]]">
<title ng-bind="page.title">[[ .title ]]</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="[[ .desc ]]" />[[if .nsfw]]
<meta name="rating" content="adult" />
<meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />
[[end]]
<link rel="stylesheet" href="/assets/prim/[[ .primcss ]]" />
<link rel="stylesheet" href="/assets/styles/[[ .style ]]" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

[[template "angular" . ]][[template "headinclude" . ]]
[[end]]`

// angular config
const angular = `[[define "angular"]][[end]]`

// site header
const header = `[[define "header"]]

[[template "navmenuinclude" . ]][[template "navmenu" . ]]

<ng-include src="'pages/menus/nav.html'"></ng-include>

Sign in

<ng-include src="'pages/menus/user.html'"></ng-include>

[[end]]`

const navmenu = `[[define "navmenu"]][[ range $ib := .imageboards]]
[[ $ib.Title ]]

[[end]][[end]]`
<commit_msg>try autoscroll on ngview<commit_after>package main

// index template
const index = `[[define "index"]]<!doctype html>

[[template "head" . ]]

<ng-include src="'pages/global.html'"></ng-include>

[[template "header" . ]]

[[end]]`

// head items
const head = `[[define "head"]]
<base href="/[[ .base ]]">
<title ng-bind="page.title">[[ .title ]]</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="description" content="[[ .desc ]]" />[[if .nsfw]]
<meta name="rating" content="adult" />
<meta name="rating" content="RTA-5042-1996-1400-1577-RTA" />
[[end]]
<link rel="stylesheet" href="/assets/prim/[[ .primcss ]]" />
<link rel="stylesheet" href="/assets/styles/[[ .style ]]" />
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.4.0/css/font-awesome.min.css">

[[template "angular" . ]][[template "headinclude" . ]]
[[end]]`

// angular config
const angular = `[[define "angular"]][[end]]`

// site header
const header = `[[define "header"]]

[[template "navmenuinclude" . ]][[template "navmenu" . ]]

<ng-include src="'pages/menus/nav.html'"></ng-include>

Sign in

<ng-include src="'pages/menus/user.html'"></ng-include>

[[end]]`

const navmenu = `[[define "navmenu"]][[ range $ib := .imageboards]]
[[ $ib.Title ]]

[[end]][[end]]`
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"os"
)

func main() {
	input := bufio.NewReader(os.Stdin)
	output := bufio.NewWriter(os.Stdout)
	for {
		if r, _, err := input.ReadRune(); err == nil {
			if _, err := output.WriteRune(r); err != nil {
				panic("Error writing to STDOUT:" + err.Error())
			}
		} else if err == io.EOF {
			return
		} else {
			panic("Error reading from STDIN:" + err.Error())
		}
	}
}
<commit_msg>flush output for copyrunes<commit_after>package main

import (
	"bufio"
	"io"
	"os"
)

func main() {
	input := bufio.NewReader(os.Stdin)
	output := bufio.NewWriter(os.Stdout)
	for {
		if r, _, err := input.ReadRune(); err == nil {
			if _, err := output.WriteRune(r); err != nil {
				panic("Error writing to STDOUT:" + err.Error())
			}
			if r == '\n' {
				output.Flush()
			}
		} else if err == io.EOF {
			return
		} else {
			panic("Error reading from STDIN:" + err.Error())
		}
	}
}
<|endoftext|>


<commit_before>package swp

import (
	"fmt"
	"time"

	cv "github.com/glycerine/goconvey/convey"
	"testing"
)

func Test015TerminationOfSessions(t *testing.T) {

	cv.Convey(`Given these two parameters that are both set (non zero):

	     TermWindowDur time.Duration
	     TermUnackedLimit  int

	   when a downstream subscriber doesn't respond to TermUnackedLimit messages in within any TermWindowDur time window (backward looking from Now), then the Sender should stop sending to that subscriber, effectively dropping that endpoint, and terminate the session with prejudice/an error/log.`, t, func() {

		lossProb := float64(0)
		lat := 1 * time.Millisecond
		net := NewSimNet(lossProb, lat)
		net.AllowBlackHoleSends = true
		rtt := 2 * lat

		n := 10
		var simClk = &SimClock{}
		t0 := time.Now()
		t1 := t0.Add(time.Duration(n) * time.Second)
		//t2 := t1.Add(time.Second)
		simClk.Set(t0)

		tc := TermConfig{
			TermWindowDur:    time.Duration(n) * time.Second,
			TermUnackedLimit: n - 1,
		}

		A, err := NewSession(SessionConfig{Net: net, LocalInbox: "A", DestInbox: "B",
			WindowMsgCount: 20, WindowByteSz: -1, Timeout: rtt, Clk: simClk,
			TermCfg: tc,
			NumFailedKeepAlivesBeforeClosing: -1,
		})
		panicOn(err)

		A.SelfConsumeForTesting()

		p1 := &Packet{
			From:     "A",
			Dest:     "B",
			Data:     []byte("one"),
			TcpEvent: EventData,
		}

		for i := 0; i < n; i++ {
			A.Push(p1)
		}
		simClk.Set(t1)
		A.Push(p1)

		select {
		case <-A.Halt.Done.Chan:
		case <-time.After(time.Second):
			panic("should have gotten session Done closed and TerminatedError by now")
		}
		msg := fmt.Sprintf("Sender sees %v ack fails in window of %v, terminating session",
			tc.TermUnackedLimit, tc.TermWindowDur)
		cv.So(A.GetErr(), cv.ShouldResemble, &TerminatedError{Msg: msg})
		A.Stop()
	})
}
<commit_msg>atg. eliminate 015 term_test<commit_after><|endoftext|>


<commit_before>package cloudwatch

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"testing"

	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/teststat"
)

type mockCloudWatch struct {
	cloudwatchiface.CloudWatchAPI
	mtx                sync.RWMutex
	valuesReceived     map[string]float64
	dimensionsReceived map[string][]*cloudwatch.Dimension
}

func newMockCloudWatch() *mockCloudWatch {
	return &mockCloudWatch{
		valuesReceived:     map[string]float64{},
		dimensionsReceived: map[string][]*cloudwatch.Dimension{},
	}
}

func (mcw *mockCloudWatch) PutMetricData(input *cloudwatch.PutMetricDataInput) (*cloudwatch.PutMetricDataOutput, error) {
	mcw.mtx.Lock()
	defer mcw.mtx.Unlock()
	for _, datum := range input.MetricData {
		mcw.valuesReceived[*datum.MetricName] = *datum.Value
		mcw.dimensionsReceived[*datum.MetricName] = datum.Dimensions
	}
	return nil, nil
}

func (mcw *mockCloudWatch) testDimensions(name string, labelValues ...string) error {
	mcw.mtx.RLock()
	dimensions, ok := mcw.dimensionsReceived[name]
	mcw.mtx.RUnlock()

	if !ok {
		if len(labelValues) > 0 {
			return errors.New("Expected dimensions to be available, but none were")
		}
	}
LabelValues:
	for i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {
		name, value := labelValues[i], labelValues[i+1]
		for _, dimension := range dimensions {
			if *dimension.Name == name {
				if *dimension.Value == value {
					break LabelValues
				}
			}
		}
		return fmt.Errorf("Could not find dimension with name %s and value %s", name, value)
	}

	return nil
}

func TestCounter(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, WithLogger(log.NewNopLogger()))
	counter := cw.NewCounter(name).With(label, value)
	valuef := func() float64 {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		return svc.valuesReceived[name]
	}
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal(err)
	}
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal("Fill and flush counter 2nd time: ", err)
	}
	if err := svc.testDimensions(name, label, value); err != nil {
		t.Fatal(err)
	}
}

func TestCounterLowSendConcurrency(t *testing.T) {
	namespace := "abc"
	var names, labels, values []string
	for i := 1; i <= 45; i++ {
		num := strconv.Itoa(i)
		names = append(names, "name"+num)
		labels = append(labels, "label"+num)
		values = append(values, "value"+num)
	}
	svc := newMockCloudWatch()
	cw := New(namespace, svc,
		WithLogger(log.NewNopLogger()),
		WithConcurrentRequests(2),
	)

	counters := make(map[string]metrics.Counter)
	var wants []float64
	for i, name := range names {
		counters[name] = cw.NewCounter(name).With(labels[i], values[i])
		wants = append(wants, teststat.FillCounter(counters[name]))
	}

	err := cw.Send()
	if err != nil {
		t.Fatal(err)
	}

	for i, name := range names {
		if svc.valuesReceived[name] != wants[i] {
			t.Fatalf("want %f, have %f", wants[i], svc.valuesReceived[name])
		}
		if err := svc.testDimensions(name, labels[i], values[i]); err != nil {
			t.Fatal(err)
		}
	}
}

func TestGauge(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, WithLogger(log.NewNopLogger()))
	gauge := cw.NewGauge(name).With(label, value)
	valuef := func() float64 {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		return svc.valuesReceived[name]
	}
	if err := teststat.TestGauge(gauge, valuef); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(name, label, value); err != nil {
		t.Fatal(err)
	}
}

func TestHistogram(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, WithLogger(log.NewNopLogger()))
	histogram := cw.NewHistogram(name).With(label, value)
	n50 := fmt.Sprintf("%s_50", name)
	n90 := fmt.Sprintf("%s_90", name)
	n95 := fmt.Sprintf("%s_95", name)
	n99 := fmt.Sprintf("%s_99", name)
	quantiles := func() (p50, p90, p95, p99 float64) {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		p50 = svc.valuesReceived[n50]
		p90 = svc.valuesReceived[n90]
		p95 = svc.valuesReceived[n95]
		p99 = svc.valuesReceived[n99]
		return
	}
	if err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n50, label, value); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n90, label, value); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n95, label, value); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n99, label, value); err != nil {
		t.Fatal(err)
	}
}
<commit_msg>Tolerate that there may not be any lables, if the teststat.FillCounter() did not add any samples.<commit_after>package cloudwatch

import (
	"errors"
	"fmt"
	"strconv"
	"sync"
	"testing"

	"github.com/aws/aws-sdk-go/service/cloudwatch"
	"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"

	"github.com/go-kit/kit/log"
	"github.com/go-kit/kit/metrics"
	"github.com/go-kit/kit/metrics/teststat"
)

type mockCloudWatch struct {
	cloudwatchiface.CloudWatchAPI
	mtx                sync.RWMutex
	valuesReceived     map[string]float64
	dimensionsReceived map[string][]*cloudwatch.Dimension
}

func newMockCloudWatch() *mockCloudWatch {
	return &mockCloudWatch{
		valuesReceived:     map[string]float64{},
		dimensionsReceived: map[string][]*cloudwatch.Dimension{},
	}
}

func (mcw *mockCloudWatch) PutMetricData(input *cloudwatch.PutMetricDataInput) (*cloudwatch.PutMetricDataOutput, error) {
	mcw.mtx.Lock()
	defer mcw.mtx.Unlock()
	for _, datum := range input.MetricData {
		mcw.valuesReceived[*datum.MetricName] = *datum.Value
		mcw.dimensionsReceived[*datum.MetricName] = datum.Dimensions
	}
	return nil, nil
}

func (mcw *mockCloudWatch) testDimensions(name string, labelValues ...string) error {
	mcw.mtx.RLock()
	_, hasValue := mcw.valuesReceived[name]
	if !hasValue {
		return nil // nothing to check; 0 samples were received
	}
	dimensions, ok := mcw.dimensionsReceived[name]
	mcw.mtx.RUnlock()

	if !ok {
		if len(labelValues) > 0 {
			return errors.New("Expected dimensions to be available, but none were")
		}
	}
LabelValues:
	for i, j := 0, 0; i < len(labelValues); i, j = i+2, j+1 {
		name, value := labelValues[i], labelValues[i+1]
		for _, dimension := range dimensions {
			if *dimension.Name == name {
				if *dimension.Value == value {
					break LabelValues
				}
			}
		}
		return fmt.Errorf("Could not find dimension with name %s and value %s", name, value)
	}

	return nil
}

func TestCounter(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, WithLogger(log.NewNopLogger()))
	counter := cw.NewCounter(name).With(label, value)
	valuef := func() float64 {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		return svc.valuesReceived[name]
	}
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal(err)
	}
	if err := teststat.TestCounter(counter, valuef); err != nil {
		t.Fatal("Fill and flush counter 2nd time: ", err)
	}
	if err := svc.testDimensions(name, label, value); err != nil {
		t.Fatal(err)
	}
}

func TestCounterLowSendConcurrency(t *testing.T) {
	namespace := "abc"
	var names, labels, values []string
	for i := 1; i <= 45; i++ {
		num := strconv.Itoa(i)
		names = append(names, "name"+num)
		labels = append(labels, "label"+num)
		values = append(values, "value"+num)
	}
	svc := newMockCloudWatch()
	cw := New(namespace, svc,
		WithLogger(log.NewNopLogger()),
		WithConcurrentRequests(2),
	)

	counters := make(map[string]metrics.Counter)
	var wants []float64
	for i, name := range names {
		counters[name] = cw.NewCounter(name).With(labels[i], values[i])
		wants = append(wants, teststat.FillCounter(counters[name]))
	}

	err := cw.Send()
	if err != nil {
		t.Fatal(err)
	}

	for i, name := range names {
		if svc.valuesReceived[name] != wants[i] {
			t.Fatalf("want %f, have %f", wants[i], svc.valuesReceived[name])
		}
		if err := svc.testDimensions(name, labels[i], values[i]); err != nil {
			t.Fatal(err)
		}
	}
}

func TestGauge(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, WithLogger(log.NewNopLogger()))
	gauge := cw.NewGauge(name).With(label, value)
	valuef := func() float64 {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		return svc.valuesReceived[name]
	}
	if err := teststat.TestGauge(gauge, valuef); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(name, label, value); err != nil {
		t.Fatal(err)
	}
}

func TestHistogram(t *testing.T) {
	namespace, name := "abc", "def"
	label, value := "label", "value"
	svc := newMockCloudWatch()
	cw := New(namespace, svc, WithLogger(log.NewNopLogger()))
	histogram := cw.NewHistogram(name).With(label, value)
	n50 := fmt.Sprintf("%s_50", name)
	n90 := fmt.Sprintf("%s_90", name)
	n95 := fmt.Sprintf("%s_95", name)
	n99 := fmt.Sprintf("%s_99", name)
	quantiles := func() (p50, p90, p95, p99 float64) {
		err := cw.Send()
		if err != nil {
			t.Fatal(err)
		}
		svc.mtx.RLock()
		defer svc.mtx.RUnlock()
		p50 = svc.valuesReceived[n50]
		p90 = svc.valuesReceived[n90]
		p95 = svc.valuesReceived[n95]
		p99 = svc.valuesReceived[n99]
		return
	}
	if err := teststat.TestHistogram(histogram, quantiles, 0.01); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n50, label, value); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n90, label, value); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n95, label, value); err != nil {
		t.Fatal(err)
	}
	if err := svc.testDimensions(n99, label, value); err != nil {
		t.Fatal(err)
	}
}
<|endoftext|>