<commit_before>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 graph

import (
	"errors"
	"fmt"
	"sync"

	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

// A visitor in a directed graph whose nodes are identified by strings.
type Visitor interface {
	// Process the supplied node and return a list of direct successors.
	Visit(ctx context.Context, node string) (adjacent []string, err error)
}

// Invoke v.Visit once on each node reachable from the supplied search roots,
// including the roots themselves. Use the supplied degree of parallelism.
//
// It is guaranteed that if a node N is fed to v.Visit, then either:
//
//  *  N is an element of roots, or
//  *  There exists a direct predecessor N' of N such that v.Visit(N') was
//     called and returned successfully.
//
// In particular, if the graph is a rooted tree and searching starts at the
// root, then parents will be successfully visited before children are visited.
// However note that in arbitrary DAGs it is *not* guaranteed that all of a
// node's predecessors have been visited before it is.
func Traverse(
	ctx context.Context,
	parallelism int,
	roots []string,
	v Visitor) (err error) {
	b := syncutil.NewBundle(ctx)

	// Set up initial state.
	ts := &traverseState{
		admitted: make(map[string]struct{}),
	}

	ts.mu = syncutil.NewInvariantMutex(ts.checkInvariants)
	ts.cond.L = &ts.mu

	ts.mu.Lock()
	ts.enqueueNodes(roots)
	ts.mu.Unlock()

	// Ensure that ts.cancelled is set when the context is cancelled (or when we
	// return from this function, if the context will never be cancelled).
	done := ctx.Done()
	if done == nil {
		doneChan := make(chan struct{})
		defer close(doneChan)

		done = doneChan
	}

	go watchForCancel(done, ts)

	// Run the appropriate number of workers.
	for i := 0; i < parallelism; i++ {
		b.Add(func(ctx context.Context) (err error) {
			err = traverse(ctx, ts, v)
			return
		})
	}

	err = b.Join()
	return
}

////////////////////////////////////////////////////////////////////////
// traverseState
////////////////////////////////////////////////////////////////////////

// State shared by each traverse worker.
type traverseState struct {
	mu syncutil.InvariantMutex

	// All nodes that have ever been seen. If a node is in this map, it will
	// eventually be visted (barring errors returned by the visitor).
	//
	// GUARDED_BY(mu)
	admitted map[string]struct{}

	// Admitted nodes that have yet to be visted.
	//
	// INVARIANT: For each n in toVisit, n is a key of admitted.
	//
	// GUARDED_BY(mu)
	toVisit []string

	// Set to the first error seen by a worker, if any. When non-nil, all workers
	// should wake up and return.
	//
	// We must track this explicitly rather than just using syncutil.Bundle's
	// support because we sleep on a condition variable, which can't be composed
	// with receiving from the context's Done channel.
	//
	// GUARDED_BY(mu)
	firstErr error

	// The number of workers that are doing something besides waiting on a node
	// to visit. If this hits zero with toVisit empty, it means that there is
	// nothing further to do.
	//
	// GUARDED_BY(mu)
	busyWorkers int

	// Broadcasted to with mu held when any of the following state changes:
	//
	//  *  toVisit
	//  *  cancelled
	//  *  busyWorkers
	//
	// GUARDED_BY(mu)
	cond sync.Cond
}

// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) checkInvariants() {
	// INVARIANT: For each n in toVisit, n is a key of admitted.
	for _, n := range ts.toVisit {
		if _, ok := ts.admitted[n]; !ok {
			panic(fmt.Sprintf("Expected %q to be in admitted map", n))
		}
	}
}

// Is there anything that needs a worker's attention?
//
// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) shouldWake() bool {
	return len(ts.toVisit) != 0 || ts.cancelled || ts.busyWorkers == 0
}

// Sleep until there's something interesting for a traverse worker.
//
// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) waitForSomethingToDo() {
	for !ts.shouldWake() {
		ts.cond.Wait()
	}
}

// Enqueue any of the supplied nodes that haven't already been enqueued.
//
// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) enqueueNodes(nodes []string) {
	for _, n := range nodes {
		if _, ok := ts.admitted[n]; !ok {
			ts.toVisit = append(ts.toVisit, n)
			ts.admitted[n] = struct{}{}
		}
	}

	ts.cond.Broadcast()
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// REQUIRES: len(ts.toVisit) > 0
//
// LOCKS_REQUIRED(ts.mu)
func visitOne(
	ctx context.Context,
	ts *traverseState,
	v Visitor) (err error) {
	// Mark this worker as busy for the duration of this function.
	ts.busyWorkers++
	ts.cond.Broadcast()

	defer func() {
		ts.busyWorkers--
		ts.cond.Broadcast()
	}()

	// Extract the node to visit.
	l := len(ts.toVisit)
	node := ts.toVisit[l-1]
	ts.toVisit = ts.toVisit[:l-1]
	ts.cond.Broadcast()

	// Unlock while visiting.
	ts.mu.Unlock()
	adjacent, err := v.Visit(ctx, node)
	ts.mu.Lock()

	if err != nil {
		return
	}

	// Enqueue the adjacent nodes that we haven't already admitted.
	ts.enqueueNodes(adjacent)

	return
}

// A single traverse worker.
func traverse(
	ctx context.Context,
	ts *traverseState,
	v Visitor) (err error) {
	ts.mu.Lock()
	defer ts.mu.Unlock()

	for {
		// Wait for something to do.
		ts.waitForSomethingToDo()

		// Why were we awoken?
		switch {
		// Return immediately if cancelled.
		case ts.cancelled:
			err = errors.New("Cancelled")
			return

		// Otherwise, handle work if it exists.
		case len(ts.toVisit) != 0:
			err = visitOne(ctx, ts, v)
			if err != nil {
				return
			}

		// Otherwise, are we done?
		case ts.busyWorkers == 0:
			return

		default:
			panic("Unexpected wake-up")
		}
	}
}

// Bridge context cancellation with traverseState.cancelled.
func watchForCancel(
	done <-chan struct{},
	ts *traverseState) {
	<-done

	ts.mu.Lock()
	defer ts.mu.Unlock()
	ts.cancelled = true
	ts.cond.Broadcast()
}
<commit_msg>Updated several places.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// 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 graph

import (
	"errors"
	"fmt"
	"sync"

	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

// A visitor in a directed graph whose nodes are identified by strings.
type Visitor interface {
	// Process the supplied node and return a list of direct successors.
	Visit(ctx context.Context, node string) (adjacent []string, err error)
}

// Invoke v.Visit once on each node reachable from the supplied search roots,
// including the roots themselves. Use the supplied degree of parallelism.
//
// It is guaranteed that if a node N is fed to v.Visit, then either:
//
//  *  N is an element of roots, or
//  *  There exists a direct predecessor N' of N such that v.Visit(N') was
//     called and returned successfully.
//
// In particular, if the graph is a rooted tree and searching starts at the
// root, then parents will be successfully visited before children are visited.
// However note that in arbitrary DAGs it is *not* guaranteed that all of a
// node's predecessors have been visited before it is.
func Traverse(
	ctx context.Context,
	parallelism int,
	roots []string,
	v Visitor) (err error) {
	b := syncutil.NewBundle(ctx)

	// Set up initial state.
	ts := &traverseState{
		admitted: make(map[string]struct{}),
	}

	ts.mu = syncutil.NewInvariantMutex(ts.checkInvariants)
	ts.cond.L = &ts.mu

	ts.mu.Lock()
	ts.enqueueNodes(roots)
	ts.mu.Unlock()

	// Ensure that ts.cancelled is set when the context is cancelled (or when we
	// return from this function, if the context will never be cancelled).
	done := ctx.Done()
	if done == nil {
		doneChan := make(chan struct{})
		defer close(doneChan)

		done = doneChan
	}

	go watchForCancel(done, ts)

	// Run the appropriate number of workers.
	for i := 0; i < parallelism; i++ {
		b.Add(func(ctx context.Context) (err error) {
			err = traverse(ctx, ts, v)
			return
		})
	}

	err = b.Join()
	return
}

////////////////////////////////////////////////////////////////////////
// traverseState
////////////////////////////////////////////////////////////////////////

// State shared by each traverse worker.
type traverseState struct {
	mu syncutil.InvariantMutex

	// All nodes that have ever been seen. If a node is in this map, it will
	// eventually be visted (barring errors returned by the visitor).
	//
	// GUARDED_BY(mu)
	admitted map[string]struct{}

	// Admitted nodes that have yet to be visted.
	//
	// INVARIANT: For each n in toVisit, n is a key of admitted.
	//
	// GUARDED_BY(mu)
	toVisit []string

	// Set to the first error seen by a worker, if any. When non-nil, all workers
	// should wake up and return.
	//
	// We must track this explicitly rather than just using syncutil.Bundle's
	// support because we sleep on a condition variable, which can't be composed
	// with receiving from the context's Done channel.
	//
	// GUARDED_BY(mu)
	firstErr error

	// The number of workers that are doing something besides waiting on a node
	// to visit. If this hits zero with toVisit empty, it means that there is
	// nothing further to do.
	//
	// GUARDED_BY(mu)
	busyWorkers int

	// Broadcasted to with mu held when any of the following state changes:
	//
	//  *  toVisit
	//  *  firstErr
	//  *  busyWorkers
	//
	// GUARDED_BY(mu)
	cond sync.Cond
}

// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) checkInvariants() {
	// INVARIANT: For each n in toVisit, n is a key of admitted.
	for _, n := range ts.toVisit {
		if _, ok := ts.admitted[n]; !ok {
			panic(fmt.Sprintf("Expected %q to be in admitted map", n))
		}
	}
}

// Is there anything that needs a worker's attention?
//
// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) shouldWake() bool {
	return len(ts.toVisit) != 0 || ts.firstErr != nil || ts.busyWorkers == 0
}

// Sleep until there's something interesting for a traverse worker.
//
// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) waitForSomethingToDo() {
	for !ts.shouldWake() {
		ts.cond.Wait()
	}
}

// Enqueue any of the supplied nodes that haven't already been enqueued.
//
// LOCKS_REQUIRED(ts.mu)
func (ts *traverseState) enqueueNodes(nodes []string) {
	for _, n := range nodes {
		if _, ok := ts.admitted[n]; !ok {
			ts.toVisit = append(ts.toVisit, n)
			ts.admitted[n] = struct{}{}
		}
	}

	ts.cond.Broadcast()
}

////////////////////////////////////////////////////////////////////////
// Helpers
////////////////////////////////////////////////////////////////////////

// REQUIRES: len(ts.toVisit) > 0
//
// LOCKS_REQUIRED(ts.mu)
func visitOne(
	ctx context.Context,
	ts *traverseState,
	v Visitor) (err error) {
	// Mark this worker as busy for the duration of this function.
	ts.busyWorkers++
	ts.cond.Broadcast()

	defer func() {
		ts.busyWorkers--
		ts.cond.Broadcast()
	}()

	// Extract the node to visit.
	l := len(ts.toVisit)
	node := ts.toVisit[l-1]
	ts.toVisit = ts.toVisit[:l-1]
	ts.cond.Broadcast()

	// Unlock while visiting.
	ts.mu.Unlock()
	adjacent, err := v.Visit(ctx, node)
	ts.mu.Lock()

	if err != nil {
		return
	}

	// Enqueue the adjacent nodes that we haven't already admitted.
	ts.enqueueNodes(adjacent)

	return
}

// A single traverse worker.
func traverse(
	ctx context.Context,
	ts *traverseState,
	v Visitor) (err error) {
	ts.mu.Lock()
	defer ts.mu.Unlock()

	for {
		// Wait for something to do.
		ts.waitForSomethingToDo()

		// Why were we awoken?
		switch {
		// Return immediately if another worker has seen an error.
		case ts.firstErr != nil:
			err = errors.New("Cancelled")
			return

		// Otherwise, handle work if it exists.
		case len(ts.toVisit) != 0:
			err = visitOne(ctx, ts, v)
			if err != nil {
				return
			}

		// Otherwise, are we done?
		case ts.busyWorkers == 0:
			return

		default:
			panic("Unexpected wake-up")
		}
	}
}
<|endoftext|>


<commit_before>package task

import (
	"context"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/go-task/task/v3/internal/logger"
	"github.com/go-task/task/v3/taskfile"
	"github.com/mattn/go-zglob"
	"github.com/radovskyb/watcher"
)

var watchIgnoredDirs = []string{
	".git",
	"node_modules",
}

// watchTasks start watching the given tasks
func (e *Executor) watchTasks(calls ...taskfile.Call) error {
	tasks := make([]string, len(calls))
	for i, c := range calls {
		tasks[i] = c.Task
	}
	e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s", strings.Join(tasks, ", "))

	ctx, cancel := context.WithCancel(context.Background())
	for _, c := range calls {
		c := c
		go func() {
			if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
				e.Logger.Errf(logger.Red, "%v", err)
			}
		}()
	}

	w := watcher.New()
	defer w.Close()
	w.SetMaxEvents(1)
	if err := w.Ignore(watchIgnoredDirs...); err != nil {
		cancel()
		return err
	}

	closeOnInterrupt(w)

	go func() {
		for {
			select {
			case event := <-w.Event:
				e.Logger.VerboseErrf(logger.Magenta, "task: received watch event: %v", event)

				cancel()
				ctx, cancel = context.WithCancel(context.Background())
				for _, c := range calls {
					c := c
					go func() {
						if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
							e.Logger.Errf(logger.Red, "%v", err)
						}
					}()
				}
			case err := <-w.Error:
				switch err {
				case watcher.ErrWatchedFileDeleted:
					go func() {
						w.TriggerEvent(watcher.Remove, nil)
					}()
				default:
					e.Logger.Errf(logger.Red, "%v", err)
				}
			case <-w.Closed:
				cancel()
				return
			}
		}
	}()

	go func() {
		// re-register each second because we can have new files
		for {
			if err := e.registerWatchedFiles(w, calls...); err != nil {
				e.Logger.Errf(logger.Red, "%v", err)
			}
			time.Sleep(time.Second)
		}
	}()

	return w.Start(time.Second)
}

func isContextError(err error) bool {
	if taskRunErr, ok := err.(*taskRunError); ok {
		err = taskRunErr.err
	}

	return err == context.Canceled || err == context.DeadlineExceeded
}

func closeOnInterrupt(w *watcher.Watcher) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func() {
		<-ch
		w.Close()
	}()
}

func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Call) error {
	oldWatchedFiles := make(map[string]struct{})
	for f := range w.WatchedFiles() {
		oldWatchedFiles[f] = struct{}{}
	}

	var registerTaskFiles func(taskfile.Call) error
	registerTaskFiles = func(c taskfile.Call) error {
		task, err := e.CompiledTask(c)
		if err != nil {
			return err
		}

		for _, d := range task.Deps {
			if err := registerTaskFiles(taskfile.Call{Task: d.Task, Vars: d.Vars}); err != nil {
				return err
			}
		}
		for _, c := range task.Cmds {
			if c.Task != "" {
				if err := registerTaskFiles(taskfile.Call{Task: c.Task, Vars: c.Vars}); err != nil {
					return err
				}
			}
		}

		for _, s := range task.Sources {
			files, err := zglob.Glob(s)
			if err != nil {
				return err
			}
			for _, f := range files {
				absFile, err := filepath.Abs(f)
				if err != nil {
					return err
				}
				if _, ok := oldWatchedFiles[absFile]; ok {
					continue
				}
				if err := w.Add(absFile); err != nil {
					return err
				}
				e.Logger.VerboseOutf(logger.Green, "task: watching new file: %v", absFile)
			}
		}
		return nil
	}

	for _, c := range calls {
		if err := registerTaskFiles(c); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Update watch.go<commit_after>package task

import (
	"context"
	"os"
	"os/signal"
	"path/filepath"
	"strings"
	"syscall"
	"time"

	"github.com/go-task/task/v3/internal/logger"
	"github.com/go-task/task/v3/taskfile"
	"github.com/mattn/go-zglob"
	"github.com/radovskyb/watcher"
)

var watchIgnoredDirs = []string{
	".git",
	"node_modules",
}

// watchTasks start watching the given tasks
func (e *Executor) watchTasks(calls ...taskfile.Call) error {
	tasks := make([]string, len(calls))
	for i, c := range calls {
		tasks[i] = c.Task
	}
	e.Logger.Errf(logger.Green, "task: Started watching for tasks: %s", strings.Join(tasks, ", "))

	ctx, cancel := context.WithCancel(context.Background())
	for _, c := range calls {
		c := c
		go func() {
			if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
				e.Logger.Errf(logger.Red, "%v", err)
			}
		}()
	}

	w := watcher.New()
	defer w.Close()
	w.SetMaxEvents(1)
	if err := w.Ignore(watchIgnoredDirs...); err != nil {
		cancel()
		return err
	}

	closeOnInterrupt(w)

	go func() {
		for {
			select {
			case event := <-w.Event:
				e.Logger.VerboseErrf(logger.Magenta, "task: received watch event: %v", event)

				cancel()
				ctx, cancel = context.WithCancel(context.Background())
				for _, c := range calls {
					c := c
					go func() {
						if err := e.RunTask(ctx, c); err != nil && !isContextError(err) {
							e.Logger.Errf(logger.Red, "%v", err)
						}
					}()
				}
			case err := <-w.Error:
				switch err {
				case watcher.ErrWatchedFileDeleted:
					go func() {
						w.TriggerEvent(watcher.Remove, nil)
					}()
				default:
					e.Logger.Errf(logger.Red, "%v", err)
				}
			case <-w.Closed:
				cancel()
				return
			}
		}
	}()

	go func() {
		// re-register every 20 seconds because we can have new files, but this process is expensive to run
		for {
			if err := e.registerWatchedFiles(w, calls...); err != nil {
				e.Logger.Errf(logger.Red, "%v", err)
			}
			time.Sleep(time.Second * 20)
		}
	}()

	return w.Start(time.Second)
}

func isContextError(err error) bool {
	if taskRunErr, ok := err.(*taskRunError); ok {
		err = taskRunErr.err
	}

	return err == context.Canceled || err == context.DeadlineExceeded
}

func closeOnInterrupt(w *watcher.Watcher) {
	ch := make(chan os.Signal, 1)
	signal.Notify(ch, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func() {
		<-ch
		w.Close()
	}()
}

func (e *Executor) registerWatchedFiles(w *watcher.Watcher, calls ...taskfile.Call) error {
	oldWatchedFiles := make(map[string]struct{})
	for f := range w.WatchedFiles() {
		oldWatchedFiles[f] = struct{}{}
	}

	var registerTaskFiles func(taskfile.Call) error
	registerTaskFiles = func(c taskfile.Call) error {
		task, err := e.CompiledTask(c)
		if err != nil {
			return err
		}

		for _, d := range task.Deps {
			if err := registerTaskFiles(taskfile.Call{Task: d.Task, Vars: d.Vars}); err != nil {
				return err
			}
		}
		for _, c := range task.Cmds {
			if c.Task != "" {
				if err := registerTaskFiles(taskfile.Call{Task: c.Task, Vars: c.Vars}); err != nil {
					return err
				}
			}
		}

		for _, s := range task.Sources {
			files, err := zglob.Glob(s)
			if err != nil {
				return err
			}
			for _, f := range files {
				absFile, err := filepath.Abs(f)
				if err != nil {
					return err
				}
				if _, ok := oldWatchedFiles[absFile]; ok {
					continue
				}
				if err := w.Add(absFile); err != nil {
					return err
				}
				e.Logger.VerboseOutf(logger.Green, "task: watching new file: %v", absFile)
			}
		}
		return nil
	}

	for _, c := range calls {
		if err := registerTaskFiles(c); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"os/exec"
	"reflect"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/armon/consul-kv"
)

// Configuration for watches.
type WatchConfig struct {
	ConsulAddr string
	ConsulDC   string
	Cmd        []string
	ErrExit    bool
	Prefix     string
	Reload     bool
	Sanitize   bool
	Upcase     bool
}

var (
	// Regexp for invalid characters in keys
	InvalidRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]`)
)

// Connects to Consul and watches a given K/V prefix and uses that to
// execute a child process.
func watchAndExec(config *WatchConfig) (int, error) {
	kvConfig := consulkv.DefaultConfig()
	kvConfig.Address = config.ConsulAddr
	kvConfig.Datacenter = config.ConsulDC

	client, err := consulkv.NewClient(kvConfig)
	if err != nil {
		return 0, err
	}

	// Start the watcher goroutine that watches for changes in the
	// K/V and notifies us on a channel.
	errCh := make(chan error, 1)
	pairCh := make(chan consulkv.KVPairs)
	quitCh := make(chan struct{})
	defer close(quitCh)
	go watch(
		client, config.Prefix, pairCh, errCh, quitCh,
		config.ErrExit, config.Reload)

	// This channel is what is sent to when a process exits that we
	// are running. We start it out as `nil` since we have no process.
	var exitCh chan int

	var env map[string]string
	var cmd *exec.Cmd
	for {
		var pairs consulkv.KVPairs

		// Wait for new pairs to come on our channel or an error
		// to occur.
		select {
		case exit := <-exitCh:
			return exit, nil
		case pairs = <-pairCh:
		case err := <-errCh:
			return 0, err
		}

		newEnv := make(map[string]string)
		for _, pair := range pairs {
			k := strings.TrimPrefix(pair.Key, config.Prefix)
			k = strings.TrimLeft(k, "/")
			if config.Sanitize {
				k = InvalidRegexp.ReplaceAllString(k, "_")
			}
			if config.Upcase {
				k = strings.ToUpper(k)
			}
			newEnv[k] = string(pair.Value)
		}

		// If the environmental variables didn't actually change,
		// then don't do anything.
		if reflect.DeepEqual(env, newEnv) {
			continue
		}

		// Replace the env so we can detect future changes
		env = newEnv

		// Configuration changed, reload the process.
		if cmd != nil {
			if !config.Reload {
				// We don't want to reload the process... just ignore.
				continue
			}

			// Kill the process
			exited := false
			if err := cmd.Process.Signal(syscall.SIGTERM); err == nil {
				// Wait a few seconds for it to exit
				killCh := make(chan struct{})
				go func() {
					defer close(killCh)
					cmd.Process.Wait()
				}()

				select {
				case <-killCh:
					exited = true
				case <-time.After(3 * time.Second):
				}
			}

			// If we still haven't exited from a SIGKILL
			if !exited {
				cmd.Process.Kill()
			}

			cmd = nil
		}

		processEnv := os.Environ()
		cmdEnv := make(
			[]string, len(processEnv), len(newEnv)+len(processEnv))
		copy(cmdEnv, processEnv)
		for k, v := range newEnv {
			cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v))
		}
		cmd = exec.Command(config.Cmd[0], config.Cmd[1:]...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Env = cmdEnv
		err := cmd.Start()
		if err != nil {
			return 111, err
		}

		// Create a new exitCh so that previously invoked commands
		// (if any) don't cause us to exit, and start a goroutine
		// to wait for that process to end.
		exitCh = make(chan int, 1)
		go func(cmd *exec.Cmd, exitCh chan<- int) {
			err := cmd.Wait()
			if err == nil {
				exitCh <- 0
				return
			}

			if exiterr, ok := err.(*exec.ExitError); ok {
				// The program has exited with an exit code != 0
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					exitCh <- status.ExitStatus()
					return
				}
			}

			exitCh <- 111
		}(cmd, exitCh)
	}

	return 0, nil
}

func watch(
	client *consulkv.Client,
	prefix string,
	pairCh chan<- consulkv.KVPairs,
	errCh chan<- error,
	quitCh <-chan struct{},
	errExit bool,
	watch bool) {
	// Get the initial list of k/v pairs. We don't do a retryableList
	// here because we want a fast fail if the initial request fails.
	meta, pairs, err := client.List(prefix)
	if err != nil {
		errCh <- err
		return
	}

	// Send the initial list out right away
	pairCh <- pairs

	// If we're not watching, just return right away
	if !watch {
		return
	}

	// Loop forever (or until quitCh is closed) and watch the keys
	// for changes.
	curIndex := meta.ModifyIndex
	for {
		select {
		case <-quitCh:
			return
		default:
		}

		meta, pairs, err = retryableList(
			func() (*consulkv.KVMeta, consulkv.KVPairs, error) {
				return client.WatchList(prefix, curIndex)
			})
		if err != nil && errExit {
			errCh <- err
			return
		}

		pairCh <- pairs
		curIndex = meta.ModifyIndex
	}
}

// This function is able to call KV listing functions and retry them.
// We want to retry if there are errors because it is safe (GET request),
// and erroring early is MUCH more costly than retrying over time and
// delaying the configuration propagation.
func retryableList(f func() (*consulkv.KVMeta, consulkv.KVPairs, error)) (*consulkv.KVMeta, consulkv.KVPairs, error) {
	i := 0
	for {
		m, p, e := f()
		if e != nil {
			if i >= 3 {
				return nil, nil, e
			}

			i++

			// Reasonably arbitrary sleep to just try again... It is
			// a GET request so this is safe.
			time.Sleep(time.Duration(i*2) * time.Second)
		}

		return m, p, e
	}
}
<commit_msg>Switch the Consul client to consul-api<commit_after>package main

import (
	"fmt"
	"os"
	"os/exec"
	"reflect"
	"regexp"
	"strings"
	"syscall"
	"time"

	"github.com/armon/consul-api"
)

// Configuration for watches.
type WatchConfig struct {
	ConsulAddr string
	ConsulDC   string
	Cmd        []string
	ErrExit    bool
	Prefix     string
	Reload     bool
	Sanitize   bool
	Upcase     bool
}

var (
	// Regexp for invalid characters in keys
	InvalidRegexp = regexp.MustCompile(`[^a-zA-Z0-9_]`)
)

// Connects to Consul and watches a given K/V prefix and uses that to
// execute a child process.
func watchAndExec(config *WatchConfig) (int, error) {
	kvConfig := consulapi.DefaultConfig()
	kvConfig.Address = config.ConsulAddr
	kvConfig.Datacenter = config.ConsulDC

	client, err := consulapi.NewClient(kvConfig)
	if err != nil {
		return 0, err
	}

	// Start the watcher goroutine that watches for changes in the
	// K/V and notifies us on a channel.
	errCh := make(chan error, 1)
	pairCh := make(chan consulapi.KVPairs)
	quitCh := make(chan struct{})
	defer close(quitCh)
	go watch(
		client, config.Prefix, pairCh, errCh, quitCh,
		config.ErrExit, config.Reload)

	// This channel is what is sent to when a process exits that we
	// are running. We start it out as `nil` since we have no process.
	var exitCh chan int

	var env map[string]string
	var cmd *exec.Cmd
	for {
		var pairs consulapi.KVPairs

		// Wait for new pairs to come on our channel or an error
		// to occur.
		select {
		case exit := <-exitCh:
			return exit, nil
		case pairs = <-pairCh:
		case err := <-errCh:
			return 0, err
		}

		newEnv := make(map[string]string)
		for _, pair := range pairs {
			k := strings.TrimPrefix(pair.Key, config.Prefix)
			k = strings.TrimLeft(k, "/")
			if config.Sanitize {
				k = InvalidRegexp.ReplaceAllString(k, "_")
			}
			if config.Upcase {
				k = strings.ToUpper(k)
			}
			newEnv[k] = string(pair.Value)
		}

		// If the environmental variables didn't actually change,
		// then don't do anything.
		if reflect.DeepEqual(env, newEnv) {
			continue
		}

		// Replace the env so we can detect future changes
		env = newEnv

		// Configuration changed, reload the process.
		if cmd != nil {
			if !config.Reload {
				// We don't want to reload the process... just ignore.
				continue
			}

			// Kill the process
			exited := false
			if err := cmd.Process.Signal(syscall.SIGTERM); err == nil {
				// Wait a few seconds for it to exit
				killCh := make(chan struct{})
				go func() {
					defer close(killCh)
					cmd.Process.Wait()
				}()

				select {
				case <-killCh:
					exited = true
				case <-time.After(3 * time.Second):
				}
			}

			// If we still haven't exited from a SIGKILL
			if !exited {
				cmd.Process.Kill()
			}

			cmd = nil
		}

		processEnv := os.Environ()
		cmdEnv := make(
			[]string, len(processEnv), len(newEnv)+len(processEnv))
		copy(cmdEnv, processEnv)
		for k, v := range newEnv {
			cmdEnv = append(cmdEnv, fmt.Sprintf("%s=%s", k, v))
		}
		cmd = exec.Command(config.Cmd[0], config.Cmd[1:]...)
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
		cmd.Env = cmdEnv
		err := cmd.Start()
		if err != nil {
			return 111, err
		}

		// Create a new exitCh so that previously invoked commands
		// (if any) don't cause us to exit, and start a goroutine
		// to wait for that process to end.
		exitCh = make(chan int, 1)
		go func(cmd *exec.Cmd, exitCh chan<- int) {
			err := cmd.Wait()
			if err == nil {
				exitCh <- 0
				return
			}

			if exiterr, ok := err.(*exec.ExitError); ok {
				// The program has exited with an exit code != 0
				if status, ok := exiterr.Sys().(syscall.WaitStatus); ok {
					exitCh <- status.ExitStatus()
					return
				}
			}

			exitCh <- 111
		}(cmd, exitCh)
	}

	return 0, nil
}

func watch(
	client *consulapi.Client,
	prefix string,
	pairCh chan<- consulapi.KVPairs,
	errCh chan<- error,
	quitCh <-chan struct{},
	errExit bool,
	watch bool) {
	// Get the initial list of k/v pairs. We don't do a retryableList
	// here because we want a fast fail if the initial request fails.
	pairs, meta, err := client.KV().List(prefix, nil)
	if err != nil {
		errCh <- err
		return
	}

	// Send the initial list out right away
	pairCh <- pairs

	// If we're not watching, just return right away
	if !watch {
		return
	}

	// Loop forever (or until quitCh is closed) and watch the keys
	// for changes.
	curIndex := meta.LastIndex
	for {
		select {
		case <-quitCh:
			return
		default:
		}

		pairs, meta, err = retryableList(
			func() (consulapi.KVPairs, *consulapi.QueryMeta, error) {
				opts := &consulapi.QueryOptions{WaitIndex: curIndex}
				return client.KV().List(prefix, opts)
			})
		if err != nil && errExit {
			errCh <- err
			return
		}

		pairCh <- pairs
		curIndex = meta.LastIndex
	}
}

// This function is able to call KV listing functions and retry them.
// We want to retry if there are errors because it is safe (GET request),
// and erroring early is MUCH more costly than retrying over time and
// delaying the configuration propagation.
func retryableList(f func() (consulapi.KVPairs, *consulapi.QueryMeta, error)) (consulapi.KVPairs, *consulapi.QueryMeta, error) {
	i := 0
	for {
		p, m, e := f()
		if e != nil {
			if i >= 3 {
				return nil, nil, e
			}

			i++

			// Reasonably arbitrary sleep to just try again... It is
			// a GET request so this is safe.
			time.Sleep(time.Duration(i*2) * time.Second)
		}

		return p, m, e
	}
}
<|endoftext|>


<commit_before>package configgtm

import (
	"github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"

	"fmt"
)

//
// Handle Operations on gtm resources
// Based on 1.3 schema
//

// ResourceInstance
type ResourceInstance struct {
	DatacenterId         int  `json:"datacenterId"`
	UseDefaultLoadObject bool `json:"useDefaultLoadObject,omitempty"`
	LoadObject
}

// Resource represents a GTM resource
type Resource struct {
	Type                        string              `json:"type"`
	HostHeader                  string              `json:"hostHeader,omitempty"`
	LeastSquaresDecay           int                 `json:"leastSquaresDecay,omitempty"`
	Description                 string              `json:"description,omitempty"`
	LeaderString                string              `json:"leaderString,omitempty"`
	ConstrainedProperty         string              `json:"constrainedProperty,omitempty"`
	ResourceInstances           []*ResourceInstance `json:"resourceInstances,omitempty"`
	AggregationType             string              `json:"aggregationType"`
	Links                       []*Link             `json:"links,omitempty"`
	LoadImbalancePercentage     float64             `json:"loadImbalancePercentage,omitempty"`
	UpperBound                  int                 `json:"upperBound,omitempty"`
	Name                        string              `json:"name"`
	MaxUMultiplicativeIncrement float64             `json:"maxUMultiplicativeIncrement,omitempty"`
	DecayRate                   float64             `json:"decayRate,omitempty"`
}

// ResourceList is the structure returned by List Resources
type ResourceList struct {
	ResourceItems []*Resource `json:"items"`
}

// NewResourceInstance instantiates a new ResourceInstance.
func (rsrc *Resource) NewResourceInstance(dcID int) *ResourceInstance {

	return &ResourceInstance{DatacenterId: dcID}

}

// NewResource creates a new Resource object.
func NewResource(name string) *Resource {
	resource := &Resource{Name: name}
	return resource
}

// ListResources retreieves all Resources in the specified domain.
func ListResources(domainName string) ([]*Resource, error) {
	rsrcs := &ResourceList{}
	req, err := client.NewRequest(
		Config,
		"GET",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources", domainName),
		nil,
	)
	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	printHttpResponse(res, true)

	if client.IsError(res) && res.StatusCode != 404 {
		return nil, client.NewAPIError(res)
	} else if res.StatusCode == 404 {
		return nil, CommonError{entityName: "Resources"}
	}
	err = client.BodyJSON(res, rsrcs)
	if err != nil {
		return nil, err
	}

	return rsrcs.ResourceItems, nil

}

// GetResource retrieves a Resource with the given name in the specified domain.
func GetResource(name, domainName string) (*Resource, error) {
	rsc := NewResource(name)
	req, err := client.NewRequest(
		Config,
		"GET",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources/%s", domainName, name),
		nil,
	)
	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	printHttpResponse(res, true)

	if client.IsError(res) && res.StatusCode != 404 {
		return nil, client.NewAPIError(res)
	} else if res.StatusCode == 404 {
		return nil, CommonError{entityName: "Resource", name: name}
	} else {
		err = client.BodyJSON(res, rsc)
		if err != nil {
			return nil, err
		}

		return rsc, nil
	}
}

// Create the resource identified by the receiver argument in the specified domain.
func (rsrc *Resource) Create(domainName string) (*ResourceResponse, error) {

	// Use common code. Any specific validation needed?

	return rsrc.save(domainName)

}

// Update the resourceidentified in the receiver argument in the specified domain.
func (rsrc *Resource) Update(domainName string) (*ResponseStatus, error) {

	// common code

	stat, err := rsrc.save(domainName)
	if err != nil {
		return nil, err
	}
	return stat.Status, err

}

// Save Resource in given domain. Common path for Create and Update.
func (rsrc *Resource) save(domainName string) (*ResourceResponse, error) {

	fmt.Println("Creating request!!!!")
	req, err := client.NewJSONRequest(
		Config,
		"PUT",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources/%s", domainName, rsrc.Name),
		rsrc,
	)

	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)

	printHttpResponse(res, true)

	// Network error
	if err != nil {
		return nil, CommonError{
			entityName:       "Resource",
			name:             rsrc.Name,
			httpErrorMessage: err.Error(),
			err:              err,
		}
	}

	// API error
	if client.IsError(res) {
		err := client.NewAPIError(res)
		return nil, CommonError{entityName: "Resource", name: rsrc.Name, apiErrorMessage: err.Detail, err: err}
	}

	responseBody := &ResourceResponse{}
	// Unmarshall whole response body for updated entity and in case want status
	err = client.BodyJSON(res, responseBody)
	if err != nil {
		return nil, err
	}

	return responseBody, nil

}

// Delete the resource identified in the receiver argument from the specified domain.
func (rsrc *Resource) Delete(domainName string) (*ResponseStatus, error) {

	req, err := client.NewRequest(
		Config,
		"DELETE",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources/%s", domainName, rsrc.Name),
		nil,
	)
	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	printHttpResponse(res, true)

	// Network error
	if err != nil {
		return nil, CommonError{
			entityName:       "Resource",
			name:             rsrc.Name,
			httpErrorMessage: err.Error(),
			err:              err,
		}
	}

	// API error
	if client.IsError(res) {
		err := client.NewAPIError(res)
		return nil, CommonError{entityName: "Resource", name: rsrc.Name, apiErrorMessage: err.Detail, err: err}
	}

	responseBody := &ResponseBody{}
	// Unmarshall whole response body in case want status
	err = client.BodyJSON(res, responseBody)
	if err != nil {
		return nil, err
	}

	return responseBody.Status, nil

}
<commit_msg>Removed message<commit_after>package configgtm

import (
	"github.com/akamai/AkamaiOPEN-edgegrid-golang/client-v1"

	"fmt"
)

//
// Handle Operations on gtm resources
// Based on 1.3 schema
//

// ResourceInstance
type ResourceInstance struct {
	DatacenterId         int  `json:"datacenterId"`
	UseDefaultLoadObject bool `json:"useDefaultLoadObject,omitempty"`
	LoadObject
}

// Resource represents a GTM resource
type Resource struct {
	Type                        string              `json:"type"`
	HostHeader                  string              `json:"hostHeader,omitempty"`
	LeastSquaresDecay           int                 `json:"leastSquaresDecay,omitempty"`
	Description                 string              `json:"description,omitempty"`
	LeaderString                string              `json:"leaderString,omitempty"`
	ConstrainedProperty         string              `json:"constrainedProperty,omitempty"`
	ResourceInstances           []*ResourceInstance `json:"resourceInstances,omitempty"`
	AggregationType             string              `json:"aggregationType"`
	Links                       []*Link             `json:"links,omitempty"`
	LoadImbalancePercentage     float64             `json:"loadImbalancePercentage,omitempty"`
	UpperBound                  int                 `json:"upperBound,omitempty"`
	Name                        string              `json:"name"`
	MaxUMultiplicativeIncrement float64             `json:"maxUMultiplicativeIncrement,omitempty"`
	DecayRate                   float64             `json:"decayRate,omitempty"`
}

// ResourceList is the structure returned by List Resources
type ResourceList struct {
	ResourceItems []*Resource `json:"items"`
}

// NewResourceInstance instantiates a new ResourceInstance.
func (rsrc *Resource) NewResourceInstance(dcID int) *ResourceInstance {

	return &ResourceInstance{DatacenterId: dcID}

}

// NewResource creates a new Resource object.
func NewResource(name string) *Resource {
	resource := &Resource{Name: name}
	return resource
}

// ListResources retreieves all Resources in the specified domain.
func ListResources(domainName string) ([]*Resource, error) {
	rsrcs := &ResourceList{}
	req, err := client.NewRequest(
		Config,
		"GET",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources", domainName),
		nil,
	)
	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	printHttpResponse(res, true)

	if client.IsError(res) && res.StatusCode != 404 {
		return nil, client.NewAPIError(res)
	} else if res.StatusCode == 404 {
		return nil, CommonError{entityName: "Resources"}
	}
	err = client.BodyJSON(res, rsrcs)
	if err != nil {
		return nil, err
	}

	return rsrcs.ResourceItems, nil

}

// GetResource retrieves a Resource with the given name in the specified domain.
func GetResource(name, domainName string) (*Resource, error) {
	rsc := NewResource(name)
	req, err := client.NewRequest(
		Config,
		"GET",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources/%s", domainName, name),
		nil,
	)
	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	printHttpResponse(res, true)

	if client.IsError(res) && res.StatusCode != 404 {
		return nil, client.NewAPIError(res)
	} else if res.StatusCode == 404 {
		return nil, CommonError{entityName: "Resource", name: name}
	} else {
		err = client.BodyJSON(res, rsc)
		if err != nil {
			return nil, err
		}

		return rsc, nil
	}
}

// Create the resource identified by the receiver argument in the specified domain.
func (rsrc *Resource) Create(domainName string) (*ResourceResponse, error) {

	// Use common code. Any specific validation needed?

	return rsrc.save(domainName)

}

// Update the resourceidentified in the receiver argument in the specified domain.
func (rsrc *Resource) Update(domainName string) (*ResponseStatus, error) {

	// common code

	stat, err := rsrc.save(domainName)
	if err != nil {
		return nil, err
	}
	return stat.Status, err

}

// Save Resource in given domain. Common path for Create and Update.
func (rsrc *Resource) save(domainName string) (*ResourceResponse, error) {

	req, err := client.NewJSONRequest(
		Config,
		"PUT",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources/%s", domainName, rsrc.Name),
		rsrc,
	)

	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)

	printHttpResponse(res, true)

	// Network error
	if err != nil {
		return nil, CommonError{
			entityName:       "Resource",
			name:             rsrc.Name,
			httpErrorMessage: err.Error(),
			err:              err,
		}
	}

	// API error
	if client.IsError(res) {
		err := client.NewAPIError(res)
		return nil, CommonError{entityName: "Resource", name: rsrc.Name, apiErrorMessage: err.Detail, err: err}
	}

	responseBody := &ResourceResponse{}
	// Unmarshall whole response body for updated entity and in case want status
	err = client.BodyJSON(res, responseBody)
	if err != nil {
		return nil, err
	}

	return responseBody, nil

}

// Delete the resource identified in the receiver argument from the specified domain.
func (rsrc *Resource) Delete(domainName string) (*ResponseStatus, error) {

	req, err := client.NewRequest(
		Config,
		"DELETE",
		fmt.Sprintf("/config-gtm/v1/domains/%s/resources/%s", domainName, rsrc.Name),
		nil,
	)
	if err != nil {
		return nil, err
	}

	setVersionHeader(req, schemaVersion)

	printHttpRequest(req, true)

	res, err := client.Do(Config, req)
	if err != nil {
		return nil, err
	}

	printHttpResponse(res, true)

	// Network error
	if err != nil {
		return nil, CommonError{
			entityName:       "Resource",
			name:             rsrc.Name,
			httpErrorMessage: err.Error(),
			err:              err,
		}
	}

	// API error
	if client.IsError(res) {
		err := client.NewAPIError(res)
		return nil, CommonError{entityName: "Resource", name: rsrc.Name, apiErrorMessage: err.Detail, err: err}
	}

	responseBody := &ResponseBody{}
	// Unmarshall whole response body in case want status
	err = client.BodyJSON(res, responseBody)
	if err != nil {
		return nil, err
	}

	return responseBody.Status, nil

}
<|endoftext|>


<commit_before>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2022, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* 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 dynamic

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"strconv"
	"strings"
)

// Template represents an OpenNebula syntax template
type Template struct {
	Elements []Element
}

// Element is an interface that must implement the String
// function
type Element interface {
	String() string
	Key() string
}

// Pair is a key / value pair
type Pair struct {
	XMLName xml.Name
	Value   string `xml:",innerxml"`
}

// Vector contains an array of keyvalue pairs
type Vector struct {
	XMLName xml.Name
	Pairs   []Pair
}

type TemplateAny struct {
	Template
}

// Key return the pair key
func (t *Pair) Key() string { return t.XMLName.Local }

// Key return the vector key
func (t *Vector) Key() string { return t.XMLName.Local }

// String prints the Template in OpenNebula syntax
func (t *Template) String() string {
	var s strings.Builder
	endToken := "\n"

	for i, element := range t.Elements {
		if i == len(t.Elements)-1 {
			endToken = ""
		}
		s.WriteString(element.String() + endToken)
	}

	return s.String()
}

// String prints a Pair in OpenNebula syntax
func (p *Pair) String() string {

	if strconv.CanBackquote(p.Value) {
		return fmt.Sprintf("%s=%s", p.XMLName.Local, strconv.Quote(p.Value))
	} else {
		buf := bytes.NewBufferString(p.XMLName.Local)
		buf.WriteString("=\"")
		buf.WriteString(strings.ReplaceAll(p.Value, `"`, `\"`))
		buf.WriteString(strings.ReplaceAll(p.Value, `\`, `\\`))
		buf.WriteByte('"')

		return buf.String()

	}
}

func (v *Vector) String() string {
	s := fmt.Sprintf("%s=[\n", strings.ToUpper(v.XMLName.Local))

	endToken := ",\n"
	for i, pair := range v.Pairs {
		if i == len(v.Pairs)-1 {
			endToken = ""
		}

		s += fmt.Sprintf("    %s%s", pair.String(), endToken)

	}
	s += " ]"

	return s
}

// GetPair retrieve a pair by it's key
func (t *Template) GetPairs(key string) []*Pair {

	pairs := make([]*Pair, 0, 1)

	for i, _ := range t.Elements {

		pair, ok := t.Elements[i].(*Pair)
		if !ok {
			continue
		}

		if pair.XMLName.Local != key {
			continue
		}

		pairs = append(pairs, pair)
	}

	return pairs
}

// GetPair retrieve a pair by it's key
func (v *Vector) GetPairs(key string) []*Pair {

	pairs := make([]*Pair, 0, 1)

	for i, _ := range v.Pairs {

		if v.Pairs[i].XMLName.Local != key {
			continue
		}

		pairs = append(pairs, &v.Pairs[i])
	}

	return pairs
}

// GetPair retrieve a pair by key
func (t *Template) GetPair(key string) (*Pair, error) {

	pairs := t.GetPairs(key)
	switch len(pairs) {
	case 0:
		return nil, fmt.Errorf("Template GetPair: key %s not found", key)
	case 1:
		return pairs[0], nil
	}

	return nil, fmt.Errorf("Template GetPair: multiple key %s found", key)
}

// GetPair retrieve a pair by it's key
func (v *Vector) GetPair(key string) (*Pair, error) {

	pairs := v.GetPairs(key)
	switch len(pairs) {
	case 0:
		return nil, fmt.Errorf("Template GetPair: key %s not found", key)
	case 1:
		return pairs[0], nil
	}

	return nil, fmt.Errorf("Template GetPair: multiple key %s found", key)
}

// GetVectors retrieve slice of vectors by key
func (t *Template) GetVectors(key string) []*Vector {

	vecs := make([]*Vector, 0, 1)

	for i, _ := range t.Elements {

		vec, ok := t.Elements[i].(*Vector)
		if !ok {
			continue
		}

		if vec.XMLName.Local != key {
			continue
		}

		vecs = append(vecs, vec)
	}

	return vecs
}

// GetVector retrieve a vector by key
func (t *Template) GetVector(key string) (*Vector, error) {

	vectors := t.GetVectors(key)
	switch len(vectors) {
	case 0:
		return nil, fmt.Errorf("Template GetVector: key %s not found", key)
	case 1:
		return vectors[0], nil
	}

	return nil, fmt.Errorf("Template GetVector: multiple key %s found", key)
}

// GetStr allow to retrieve the value of a pair
func (t *Template) GetStr(key string) (string, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return "", err
	}
	return pair.Value, nil
}

// GetStr allow to retrieve the value of a pair
func (t *Vector) GetStr(key string) (string, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return "", err
	}
	return pair.Value, nil
}

// GetStrs allow to retrieve a slice of string from pairs with the same key
func (t *Template) GetStrs(key string) []string {

	pairs := t.GetPairs(key)
	strs := make([]string, len(pairs))

	for i, p := range pairs {
		strs[i] = p.Value
	}

	return strs
}

// GetStrs allow to retrieve a slice of string from pairs with the same key
func (v *Vector) GetStrs(key string) []string {

	pairs := v.GetPairs(key)
	strs := make([]string, len(pairs))

	for i, p := range pairs {
		strs[i] = p.Value
	}

	return strs
}

// GetInt returns a pair value as an int
func (t *Template) GetInt(key string) (int, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	intVal, err := strconv.ParseInt(pair.Value, 10, 0)
	if err != nil {
		return -1, err
	}
	return int(intVal), nil
}

// GetInt returns a pair value as an int
func (t *Vector) GetInt(key string) (int, error) {

	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	intVal, err := strconv.ParseInt(pair.Value, 10, 0)
	if err != nil {
		return -1, err
	}
	return int(intVal), nil
}

// GetInts allow to retrieve a slice of int from pairs with the same key
func (t *Template) GetInts(key string) []int {

	pairs := t.GetPairs(key)
	ints := make([]int, 0, len(pairs))

	for _, p := range pairs {

		intVal, err := strconv.ParseInt(p.Value, 10, 0)
		if err != nil {
			continue
		}

		ints = append(ints, int(intVal))
	}

	return ints
}

// GetInts allow to retrieve a slice of int from pairs with the same key
func (v *Vector) GetInts(key string) []int {

	pairs := v.GetPairs(key)
	ints := make([]int, 0, len(pairs))

	for _, p := range pairs {

		intVal, err := strconv.ParseInt(p.Value, 10, 0)
		if err != nil {
			continue
		}

		ints = append(ints, int(intVal))
	}

	return ints
}

// GetFloat returns a pair value as an float
func (t *Template) GetFloat(key string) (float64, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	id, err := strconv.ParseFloat(pair.Value, 64)
	if err != nil {
		return -1, err
	}
	return id, nil
}

// GetFloat returns a pair value as an float
func (t *Vector) GetFloat(key string) (float64, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	id, err := strconv.ParseFloat(pair.Value, 64)
	if err != nil {
		return -1, err
	}
	return id, nil
}

// GetStrFromVec returns a pair value contained from a vector
func (t *Template) GetStrFromVec(vecKey, key string) (string, error) {
	vector, err := t.GetVector(vecKey)
	if err != nil {
		return "", err
	}
	pair, err := vector.GetPair(key)
	if err != nil {
		return "", err
	}
	return pair.Value, nil
}

//  template building

// NewTemplate returns a new Template object
func NewTemplate() *Template {
	return &Template{}
}

// NewTemplate returns a new Template object
func NewVector(name string) *Vector {
	return &Vector{
		XMLName: xml.Name{Local: name},
	}
}

// AddVector creates a new vector in the template
func (t *Template) AddVector(key string) *Vector {
	vector := &Vector{XMLName: xml.Name{Local: key}}

	t.Elements = append(t.Elements, vector)
	return vector
}

func MakePair(key string, v interface{}) (*Pair, error) {
	var val string

	switch v := v.(type) {
	default:
		return nil, fmt.Errorf("MakePair: Unexpected type")
	case float32, float64:
		val = fmt.Sprintf("%f", v)
	case int, uint:
		val = fmt.Sprintf("%d", v)
	case string:
		val = v
	}
	pair := &Pair{XMLName: xml.Name{Local: strings.ToUpper(key)}, Value: val}

	return pair, nil
}

// AddPair adds a new pair to a Template objects
func (t *Template) AddPair(key string, v interface{}) error {

	pair, err := MakePair(key, v)
	if err != nil {
		return fmt.Errorf("AddPair: %s", err)
	}
	t.Elements = append(t.Elements, pair)

	return nil
}

// AddPair adds a new pair to a Template
func (t *Vector) AddPair(key string, v interface{}) error {

	pair, err := MakePair(key, v)
	if err != nil {
		return fmt.Errorf("AddPair: %s", err)
	}
	t.Pairs = append(t.Pairs, *pair)

	return nil
}

func (t *Template) AddPairToVec(vecKey, key string, value interface{}) error {
	var vector *Vector

	vectors := t.GetVectors(vecKey)
	switch len(vectors) {
	case 0:
		vector = t.AddVector(vecKey)
	case 1:
		vector = vectors[0]
	default:
		return fmt.Errorf("Can't add pair to vector: multiple entries with key %s", key)
	}

	return vector.AddPair(key, value)
}

// Del remove an element from Template objects
func (t *Template) Del(key string) {

	size := len(t.Elements)
	for i := 0; i < size; {

		if t.Elements[i].Key() != key {
			i++
			continue
		}

		t.Elements = append(t.Elements[:i], t.Elements[i+1:]...)
		size--

	}
}

// Del remove a pair from Template
func (t *Vector) Del(key string) {

	size := len(t.Pairs)
	for i := 0; i < size; {

		if t.Pairs[i].Key() != key {
			i++
			continue
		}

		t.Pairs = append(t.Pairs[:i], t.Pairs[i+1:]...)
		size--

	}
}
<commit_msg>B #5914: fix double lines<commit_after>/* -------------------------------------------------------------------------- */
/* Copyright 2002-2022, OpenNebula Project, OpenNebula Systems                */
/*                                                                            */
/* 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 dynamic

import (
	"bytes"
	"encoding/xml"
	"fmt"
	"strconv"
	"strings"
)

// Template represents an OpenNebula syntax template
type Template struct {
	Elements []Element
}

// Element is an interface that must implement the String
// function
type Element interface {
	String() string
	Key() string
}

// Pair is a key / value pair
type Pair struct {
	XMLName xml.Name
	Value   string `xml:",innerxml"`
}

// Vector contains an array of keyvalue pairs
type Vector struct {
	XMLName xml.Name
	Pairs   []Pair
}

type TemplateAny struct {
	Template
}

// Key return the pair key
func (t *Pair) Key() string { return t.XMLName.Local }

// Key return the vector key
func (t *Vector) Key() string { return t.XMLName.Local }

// String prints the Template in OpenNebula syntax
func (t *Template) String() string {
	var s strings.Builder
	endToken := "\n"

	for i, element := range t.Elements {
		if i == len(t.Elements)-1 {
			endToken = ""
		}
		s.WriteString(element.String() + endToken)
	}

	return s.String()
}

// String prints a Pair in OpenNebula syntax
func (p *Pair) String() string {

	if strconv.CanBackquote(p.Value) {
		return fmt.Sprintf("%s=%s", p.XMLName.Local, strconv.Quote(p.Value))
	} else {
		buf := bytes.NewBufferString(p.XMLName.Local)
		buf.WriteString("=\"")
		newValue := strings.ReplaceAll(p.Value, `"`, `\"`)
		newValue = strings.ReplaceAll(newValue, `\`, `\\`)
		buf.WriteString(newValue)
		buf.WriteByte('"')

		return buf.String()

	}
}

func (v *Vector) String() string {
	s := fmt.Sprintf("%s=[\n", strings.ToUpper(v.XMLName.Local))

	endToken := ",\n"
	for i, pair := range v.Pairs {
		if i == len(v.Pairs)-1 {
			endToken = ""
		}

		s += fmt.Sprintf("    %s%s", pair.String(), endToken)

	}
	s += " ]"

	return s
}

// GetPair retrieve a pair by it's key
func (t *Template) GetPairs(key string) []*Pair {

	pairs := make([]*Pair, 0, 1)

	for i, _ := range t.Elements {

		pair, ok := t.Elements[i].(*Pair)
		if !ok {
			continue
		}

		if pair.XMLName.Local != key {
			continue
		}

		pairs = append(pairs, pair)
	}

	return pairs
}

// GetPair retrieve a pair by it's key
func (v *Vector) GetPairs(key string) []*Pair {

	pairs := make([]*Pair, 0, 1)

	for i, _ := range v.Pairs {

		if v.Pairs[i].XMLName.Local != key {
			continue
		}

		pairs = append(pairs, &v.Pairs[i])
	}

	return pairs
}

// GetPair retrieve a pair by key
func (t *Template) GetPair(key string) (*Pair, error) {

	pairs := t.GetPairs(key)
	switch len(pairs) {
	case 0:
		return nil, fmt.Errorf("Template GetPair: key %s not found", key)
	case 1:
		return pairs[0], nil
	}

	return nil, fmt.Errorf("Template GetPair: multiple key %s found", key)
}

// GetPair retrieve a pair by it's key
func (v *Vector) GetPair(key string) (*Pair, error) {

	pairs := v.GetPairs(key)
	switch len(pairs) {
	case 0:
		return nil, fmt.Errorf("Template GetPair: key %s not found", key)
	case 1:
		return pairs[0], nil
	}

	return nil, fmt.Errorf("Template GetPair: multiple key %s found", key)
}

// GetVectors retrieve slice of vectors by key
func (t *Template) GetVectors(key string) []*Vector {

	vecs := make([]*Vector, 0, 1)

	for i, _ := range t.Elements {

		vec, ok := t.Elements[i].(*Vector)
		if !ok {
			continue
		}

		if vec.XMLName.Local != key {
			continue
		}

		vecs = append(vecs, vec)
	}

	return vecs
}

// GetVector retrieve a vector by key
func (t *Template) GetVector(key string) (*Vector, error) {

	vectors := t.GetVectors(key)
	switch len(vectors) {
	case 0:
		return nil, fmt.Errorf("Template GetVector: key %s not found", key)
	case 1:
		return vectors[0], nil
	}

	return nil, fmt.Errorf("Template GetVector: multiple key %s found", key)
}

// GetStr allow to retrieve the value of a pair
func (t *Template) GetStr(key string) (string, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return "", err
	}
	return pair.Value, nil
}

// GetStr allow to retrieve the value of a pair
func (t *Vector) GetStr(key string) (string, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return "", err
	}
	return pair.Value, nil
}

// GetStrs allow to retrieve a slice of string from pairs with the same key
func (t *Template) GetStrs(key string) []string {

	pairs := t.GetPairs(key)
	strs := make([]string, len(pairs))

	for i, p := range pairs {
		strs[i] = p.Value
	}

	return strs
}

// GetStrs allow to retrieve a slice of string from pairs with the same key
func (v *Vector) GetStrs(key string) []string {

	pairs := v.GetPairs(key)
	strs := make([]string, len(pairs))

	for i, p := range pairs {
		strs[i] = p.Value
	}

	return strs
}

// GetInt returns a pair value as an int
func (t *Template) GetInt(key string) (int, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	intVal, err := strconv.ParseInt(pair.Value, 10, 0)
	if err != nil {
		return -1, err
	}
	return int(intVal), nil
}

// GetInt returns a pair value as an int
func (t *Vector) GetInt(key string) (int, error) {

	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	intVal, err := strconv.ParseInt(pair.Value, 10, 0)
	if err != nil {
		return -1, err
	}
	return int(intVal), nil
}

// GetInts allow to retrieve a slice of int from pairs with the same key
func (t *Template) GetInts(key string) []int {

	pairs := t.GetPairs(key)
	ints := make([]int, 0, len(pairs))

	for _, p := range pairs {

		intVal, err := strconv.ParseInt(p.Value, 10, 0)
		if err != nil {
			continue
		}

		ints = append(ints, int(intVal))
	}

	return ints
}

// GetInts allow to retrieve a slice of int from pairs with the same key
func (v *Vector) GetInts(key string) []int {

	pairs := v.GetPairs(key)
	ints := make([]int, 0, len(pairs))

	for _, p := range pairs {

		intVal, err := strconv.ParseInt(p.Value, 10, 0)
		if err != nil {
			continue
		}

		ints = append(ints, int(intVal))
	}

	return ints
}

// GetFloat returns a pair value as an float
func (t *Template) GetFloat(key string) (float64, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	id, err := strconv.ParseFloat(pair.Value, 64)
	if err != nil {
		return -1, err
	}
	return id, nil
}

// GetFloat returns a pair value as an float
func (t *Vector) GetFloat(key string) (float64, error) {
	pair, err := t.GetPair(key)
	if err != nil {
		return -1, err
	}
	id, err := strconv.ParseFloat(pair.Value, 64)
	if err != nil {
		return -1, err
	}
	return id, nil
}

// GetStrFromVec returns a pair value contained from a vector
func (t *Template) GetStrFromVec(vecKey, key string) (string, error) {
	vector, err := t.GetVector(vecKey)
	if err != nil {
		return "", err
	}
	pair, err := vector.GetPair(key)
	if err != nil {
		return "", err
	}
	return pair.Value, nil
}

//  template building

// NewTemplate returns a new Template object
func NewTemplate() *Template {
	return &Template{}
}

// NewTemplate returns a new Template object
func NewVector(name string) *Vector {
	return &Vector{
		XMLName: xml.Name{Local: name},
	}
}

// AddVector creates a new vector in the template
func (t *Template) AddVector(key string) *Vector {
	vector := &Vector{XMLName: xml.Name{Local: key}}

	t.Elements = append(t.Elements, vector)
	return vector
}

func MakePair(key string, v interface{}) (*Pair, error) {
	var val string

	switch v := v.(type) {
	default:
		return nil, fmt.Errorf("MakePair: Unexpected type")
	case float32, float64:
		val = fmt.Sprintf("%f", v)
	case int, uint:
		val = fmt.Sprintf("%d", v)
	case string:
		val = v
	}
	pair := &Pair{XMLName: xml.Name{Local: strings.ToUpper(key)}, Value: val}

	return pair, nil
}

// AddPair adds a new pair to a Template objects
func (t *Template) AddPair(key string, v interface{}) error {

	pair, err := MakePair(key, v)
	if err != nil {
		return fmt.Errorf("AddPair: %s", err)
	}
	t.Elements = append(t.Elements, pair)

	return nil
}

// AddPair adds a new pair to a Template
func (t *Vector) AddPair(key string, v interface{}) error {

	pair, err := MakePair(key, v)
	if err != nil {
		return fmt.Errorf("AddPair: %s", err)
	}
	t.Pairs = append(t.Pairs, *pair)

	return nil
}

func (t *Template) AddPairToVec(vecKey, key string, value interface{}) error {
	var vector *Vector

	vectors := t.GetVectors(vecKey)
	switch len(vectors) {
	case 0:
		vector = t.AddVector(vecKey)
	case 1:
		vector = vectors[0]
	default:
		return fmt.Errorf("Can't add pair to vector: multiple entries with key %s", key)
	}

	return vector.AddPair(key, value)
}

// Del remove an element from Template objects
func (t *Template) Del(key string) {

	size := len(t.Elements)
	for i := 0; i < size; {

		if t.Elements[i].Key() != key {
			i++
			continue
		}

		t.Elements = append(t.Elements[:i], t.Elements[i+1:]...)
		size--

	}
}

// Del remove a pair from Template
func (t *Vector) Del(key string) {

	size := len(t.Pairs)
	for i := 0; i < size; {

		if t.Pairs[i].Key() != key {
			i++
			continue
		}

		t.Pairs = append(t.Pairs[:i], t.Pairs[i+1:]...)
		size--

	}
}
<|endoftext|>


<commit_before>package command

import (
	"context"
	"fmt"
	"log"
	"strings"

	"github.com/igungor/ilber/bot"
	"github.com/igungor/telegram"
)

func init() {
	register(cmdEcho)
}

var cmdEcho = &Command{
	Name:      "echo",
	ShortLine: "çok cahilsin",
	Run:       runEcho,
}

func runEcho(ctx context.Context, b *bot.Bot, msg *telegram.Message) {
	args := msg.Args()
	if len(args) == 0 {
		args = []string{"çok cahilsin"}
	}
	txt := fmt.Sprintf("*%v*", strings.Join(args, " "))
	_, err := b.SendMessage(msg.Chat.ID, txt, nil)
	if err != nil {
		log.Printf("Error while sending message: %v\n", err)
		return
	}
}
<commit_msg>echo: set parsemode as markdown<commit_after>package command

import (
	"context"
	"fmt"
	"log"
	"strings"

	"github.com/igungor/ilber/bot"
	"github.com/igungor/telegram"
)

func init() {
	register(cmdEcho)
}

var cmdEcho = &Command{
	Name:      "echo",
	ShortLine: "çok cahilsin",
	Run:       runEcho,
}

func runEcho(ctx context.Context, b *bot.Bot, msg *telegram.Message) {
	args := msg.Args()
	if len(args) == 0 {
		args = []string{"çok cahilsin"}
	}

	opts := telegram.SendOptions{
		ParseMode: telegram.ModeMarkdown,
	}
	txt := fmt.Sprintf("*%v*", strings.Join(args, " "))
	_, err := b.SendMessage(msg.Chat.ID, txt, &opts)
	if err != nil {
		log.Printf("Error while sending message: %v\n", err)
		return
	}
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"github.com/digitalocean/godo"
	"github.com/mitchellh/cli"
	"strings"
)

type InfoCommand struct {
	Ui     cli.Ui
	Client *godo.Client
}

func (c *InfoCommand) Help() string {
	helpText := `
Usage: godo-cli info [options]

Options:
  -id=int The id of the droplet (required)

e.g.
  godo-cli info -id=droplet id
`
	return strings.TrimSpace(helpText)
}

func (c *InfoCommand) Run(args []string) int {
	cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)

	var id = 0
	cmdFlags.IntVar(&id, "id", 0, "")

	err := cmdFlags.Parse(args)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to parse %v", err))
		return -1
	}

	dropletRoot, _, err := c.Client.Droplets.Get(id)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to request %v", err))
		return -1
	}

	droplet := dropletRoot.Droplet
	c.Ui.Output(fmt.Sprintf("%s (status: %s, region :%s, id: %d, image id:%d size:%s)\n",
		droplet.Name, droplet.Status, droplet.Region.Slug, droplet.ID, droplet.Image.ID, droplet.Size.Slug))

	return 0
}

func (c *InfoCommand) Synopsis() string {
	return fmt.Sprintf("Show a droplet's information.")
}
<commit_msg>Added DropletByName on Info Command<commit_after>package command

import (
	"flag"
	"fmt"
	"github.com/digitalocean/godo"
	"github.com/mitchellh/cli"
	"strings"
)

type InfoCommand struct {
	Ui     cli.Ui
	Client *godo.Client
}

func (c *InfoCommand) Help() string {
	helpText := `
Usage: godo-cli info [options]

Options:
  -id=int The id of the droplet
  -id=string The name of the droplet

e.g.
  droplet by id
    godo-cli info -id=droplet id
  droplet by name
    godo-cli info -name=name

`
	return strings.TrimSpace(helpText)
}

func (c *InfoCommand) DropletById(id int) (*godo.Droplet, error) {
	dropletRoot, _, err := c.Client.Droplets.Get(id)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to request %v", err))
		return nil, err
	}

	return dropletRoot.Droplet, nil

}

func (c *InfoCommand) DropletByName(name string) (*godo.Droplet, error) {

	util := GodoUtil{Client: c.Client}
	droplet, err := util.GetDropletByName(name)

	if err != nil {
		return nil, err
	}

	return droplet, nil
}

func (c *InfoCommand) Run(args []string) int {
	cmdFlags := flag.NewFlagSet("build", flag.ContinueOnError)

	var id = 0
	var name = ""
	cmdFlags.IntVar(&id, "id", 0, "")
	cmdFlags.StringVar(&name, "name", "", "")

	err := cmdFlags.Parse(args)

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to parse %v", err))
		return -1
	}

	if name == "" && id == 0 {
		c.Help()
		return -1
	}

	var droplet *godo.Droplet
	if name != "" {
		droplet, err = c.DropletByName(name)
	} else {

		if id != 0 {
			droplet, err = c.DropletById(id)
		}
	}

	if err != nil {
		c.Ui.Error(fmt.Sprintf("Failed to request %v", err))
		return -1
	}

	c.Ui.Output(fmt.Sprintf("%s (status: %s, region :%s, id: %d, image id:%d size:%s)\n",
		droplet.Name, droplet.Status, droplet.Region.Slug, droplet.ID, droplet.Image.ID, droplet.Size.Slug))

	return 0
}

func (c *InfoCommand) Synopsis() string {
	return fmt.Sprintf("Show a droplet's information.")
}
<|endoftext|>


<commit_before>package command

import (
	"flag"
	"fmt"
	"os"
	"path"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/agent"
	"github.com/mitchellh/cli"
)

const (
	// lockKillGracePeriod is how long we allow a child between
	// a SIGTERM and a SIGKILL. This is to let the child cleanup
	// any necessary state. We have to balance this with the risk
	// of a split-brain where multiple children may be acting as if
	// they hold a lock. This value is currently based on the default
	// lock-delay value of 15 seconds. This only affects locks and not
	// semaphores.
	lockKillGracePeriod = 5 * time.Second
)

// LockCommand is a Command implementation that is used to setup
// a "lock" which manages lock acquasition and invokes a sub-process
type LockCommand struct {
	ShutdownCh <-chan struct{}
	Ui         cli.Ui

	child     *os.Process
	childLock sync.Mutex
	verbose   bool
}

func (c *LockCommand) Help() string {
	helpText := `
Usage: consul lock [options] prefix child...

  Acquires a lock or semaphore at a given path, and invokes a child
  process when successful. The child process can assume the lock is
  held while it executes. If the lock is lost or communication is disrupted
  the child process will be sent a SIGTERM signal and given time to
  gracefully exit. After the grace period expires the process will
  be hard terminated.

  When -n=1, only a single lock holder or leader exists providing
  mutual exclusion. Setting a higher value switches to a semaphore
  allowing multiple holders to coordinate.

  The prefix provided must have write privileges.

Options:

  -http-addr=127.0.0.1:8500  HTTP address of the Consul agent.
  -n=1                       Maximum number of allowed lock holders. If this
                             value is one, it operates as a lock, otherwise
							 a semaphore is used.
  -name=""                   Optional name to associate with lock session.
  -token=""                  ACL token to use. Defaults to that of agent.
  -verbose                   Enables verbose output
`
	return strings.TrimSpace(helpText)
}

func (c *LockCommand) Run(args []string) int {
	var name, token string
	var limit int
	cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.IntVar(&limit, "n", 1, "")
	cmdFlags.StringVar(&name, "name", "", "")
	cmdFlags.StringVar(&token, "token", "", "")
	cmdFlags.BoolVar(&c.verbose, "verbose", false, "")
	httpAddr := HTTPAddrFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check the limit
	if limit <= 0 {
		c.Ui.Error(fmt.Sprintf("Lock holder limit must be positive"))
		return 1
	}

	// Verify the prefix and child are provided
	extra := cmdFlags.Args()
	if len(extra) < 2 {
		c.Ui.Error("Key prefix and child command must be specified")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}
	prefix := extra[0]
	script := strings.Join(extra[1:], " ")

	// Create and test the HTTP client
	client, err := HTTPClient(*httpAddr)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}
	_, err = client.Agent().NodeName()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
		return 1
	}

	// Setup the lock or semaphore
	var lu *LockUnlock
	if limit == 1 {
		lu, err = c.setupLock(client, prefix, name)
	} else {
		lu, err = c.setupSemaphore(client, limit, prefix, name)
	}
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Lock setup failed: %s", err))
		return 1
	}

	// Attempt the acquisition
	if c.verbose {
		c.Ui.Info("Attempting lock acquisition")
	}
	lockCh, err := lu.lockFn(c.ShutdownCh)
	if err != nil || lockCh == nil {
		c.Ui.Error(fmt.Sprintf("Lock acquisition failed: %s", err))
		return 1
	}

	// Start the child process
	childDone := make(chan struct{})
	go func() {
		if err := c.startChild(script, childDone); err != nil {
			c.Ui.Error(fmt.Sprintf("%s", err))
		}
	}()

	// Monitor for shutdown, child termination, or lock loss
	select {
	case <-c.ShutdownCh:
		if c.verbose {
			c.Ui.Info("Shutdown triggered, killing child")
		}
	case <-lockCh:
		if c.verbose {
			c.Ui.Info("Lock lost, killing child")
		}
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child terminated, releasing lock")
		}
		goto RELEASE
	}

	// Kill the child
	if err := c.killChild(childDone); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
	}

RELEASE:
	// Release the lock before termination
	if err := lu.unlockFn(); err != nil {
		c.Ui.Error(fmt.Sprintf("Lock release failed: %s", err))
		return 1
	}

	// Cleanup the lock if no longer in use
	if err := lu.cleanupFn(); err != nil {
		if err != lu.inUseErr {
			c.Ui.Error(fmt.Sprintf("Lock cleanup failed: %s", err))
			return 1
		} else if c.verbose {
			c.Ui.Info("Cleanup aborted, lock in use")
		}
	} else if c.verbose {
		c.Ui.Info("Cleanup succeeded")
	}
	return 0
}

// setupLock is used to setup a new Lock given the API client,
// the key prefix to operate on, and an optional session name.
func (c *LockCommand) setupLock(client *api.Client, prefix, name string) (*LockUnlock, error) {
	// Use the DefaultSemaphoreKey extention, this way if a lock and
	// semaphore are both used at the same prefix, we will get a conflict
	// which we can report to the user.
	key := path.Join(prefix, api.DefaultSemaphoreKey)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up lock at path: %s", key))
	}
	opts := api.LockOptions{
		Key:         key,
		SessionName: name,
	}
	l, err := client.LockOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    l.Lock,
		unlockFn:  l.Unlock,
		cleanupFn: l.Destroy,
		inUseErr:  api.ErrLockInUse,
	}
	return lu, nil
}

// setupSemaphore is used to setup a new Semaphore given the
// API client, key prefix, session name, and slot holder limit.
func (c *LockCommand) setupSemaphore(client *api.Client, limit int, prefix, name string) (*LockUnlock, error) {
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up semaphore (limit %d) at prefix: %s", limit, prefix))
	}
	opts := api.SemaphoreOptions{
		Prefix:      prefix,
		Limit:       limit,
		SessionName: name,
	}
	s, err := client.SemaphoreOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    s.Acquire,
		unlockFn:  s.Release,
		cleanupFn: s.Destroy,
		inUseErr:  api.ErrSemaphoreInUse,
	}
	return lu, nil
}

// startChild is a long running routine used to start and
// wait for the child process to exit.
func (c *LockCommand) startChild(script string, doneCh chan struct{}) error {
	defer close(doneCh)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Starting handler '%s'", script))
	}
	// Create the command
	cmd, err := agent.ExecScript(script)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error executing handler: %s", err))
		return err
	}

	// Setup the command streams
	cmd.Env = append(os.Environ(),
		"CONSUL_LOCK_HELD=true",
	)
	cmd.Stdin = nil
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Start the child process
	if err := cmd.Start(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error starting handler: %s", err))
		return err
	}

	// Setup the child info
	c.childLock.Lock()
	c.child = cmd.Process
	c.childLock.Unlock()

	// Wait for the child process
	if err := cmd.Wait(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error running handler: %s", err))
		return err
	}
	return nil
}

// killChild is used to forcefully kill the child, first using SIGTERM
// to allow for a graceful cleanup and then using SIGKILL for a hard
// termination.
func (c *LockCommand) killChild(childDone chan struct{}) error {
	// Get the child process
	c.childLock.Lock()
	child := c.child
	c.childLock.Unlock()

	// If there is no child process (failed to start), we can quit early
	if child == nil {
		if c.verbose {
			c.Ui.Info("No child process to kill")
		}
		return nil
	}

	// Attempt a SIGTERM first
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Sending SIGTERM to child pid %d", child.Pid))
	}
	if err := syscall.Kill(child.Pid, syscall.SIGTERM); err != nil {
		return fmt.Errorf("Failed to terminate %d: %v", child.Pid, err)
	}

	// Wait for termination, or until a timeout
	select {
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child exited after SIGTERM")
		}
		return nil
	case <-time.After(lockKillGracePeriod):
		if c.verbose {
			c.Ui.Info(fmt.Sprintf("Child did not exit after grace period of %v",
				lockKillGracePeriod))
		}
	}

	// Send a final SIGKILL first
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Sending SIGKILL to child pid %d", child.Pid))
	}
	if err := syscall.Kill(child.Pid, syscall.SIGKILL); err != nil {
		return fmt.Errorf("Failed to kill %d: %v", child.Pid, err)
	}
	return nil
}

func (c *LockCommand) Synopsis() string {
	return "Execute a command holding a lock"
}

// LockUnlock is used to abstract over the differences between
// a lock and a semaphore.
type LockUnlock struct {
	lockFn    func(<-chan struct{}) (<-chan struct{}, error)
	unlockFn  func() error
	cleanupFn func() error
	inUseErr  error
}
<commit_msg>command/lock: Calculate name, use provided token<commit_after>package command

import (
	"flag"
	"fmt"
	"os"
	"path"
	"strings"
	"sync"
	"syscall"
	"time"

	"github.com/hashicorp/consul/api"
	"github.com/hashicorp/consul/command/agent"
	"github.com/mitchellh/cli"
)

const (
	// lockKillGracePeriod is how long we allow a child between
	// a SIGTERM and a SIGKILL. This is to let the child cleanup
	// any necessary state. We have to balance this with the risk
	// of a split-brain where multiple children may be acting as if
	// they hold a lock. This value is currently based on the default
	// lock-delay value of 15 seconds. This only affects locks and not
	// semaphores.
	lockKillGracePeriod = 5 * time.Second
)

// LockCommand is a Command implementation that is used to setup
// a "lock" which manages lock acquasition and invokes a sub-process
type LockCommand struct {
	ShutdownCh <-chan struct{}
	Ui         cli.Ui

	child     *os.Process
	childLock sync.Mutex
	verbose   bool
}

func (c *LockCommand) Help() string {
	helpText := `
Usage: consul lock [options] prefix child...

  Acquires a lock or semaphore at a given path, and invokes a child
  process when successful. The child process can assume the lock is
  held while it executes. If the lock is lost or communication is disrupted
  the child process will be sent a SIGTERM signal and given time to
  gracefully exit. After the grace period expires the process will
  be hard terminated.

  When -n=1, only a single lock holder or leader exists providing
  mutual exclusion. Setting a higher value switches to a semaphore
  allowing multiple holders to coordinate.

  The prefix provided must have write privileges.

Options:

  -http-addr=127.0.0.1:8500  HTTP address of the Consul agent.
  -n=1                       Maximum number of allowed lock holders. If this
                             value is one, it operates as a lock, otherwise
							 a semaphore is used.
  -name=""                   Optional name to associate with lock session.
  -token=""                  ACL token to use. Defaults to that of agent.
  -verbose                   Enables verbose output
`
	return strings.TrimSpace(helpText)
}

func (c *LockCommand) Run(args []string) int {
	var name, token string
	var limit int
	cmdFlags := flag.NewFlagSet("watch", flag.ContinueOnError)
	cmdFlags.Usage = func() { c.Ui.Output(c.Help()) }
	cmdFlags.IntVar(&limit, "n", 1, "")
	cmdFlags.StringVar(&name, "name", "", "")
	cmdFlags.StringVar(&token, "token", "", "")
	cmdFlags.BoolVar(&c.verbose, "verbose", false, "")
	httpAddr := HTTPAddrFlag(cmdFlags)
	if err := cmdFlags.Parse(args); err != nil {
		return 1
	}

	// Check the limit
	if limit <= 0 {
		c.Ui.Error(fmt.Sprintf("Lock holder limit must be positive"))
		return 1
	}

	// Verify the prefix and child are provided
	extra := cmdFlags.Args()
	if len(extra) < 2 {
		c.Ui.Error("Key prefix and child command must be specified")
		c.Ui.Error("")
		c.Ui.Error(c.Help())
		return 1
	}
	prefix := extra[0]
	script := strings.Join(extra[1:], " ")

	// Calculate a session name if none provided
	if name == "" {
		name = fmt.Sprintf("Consul lock for '%s' at '%s'", script, prefix)
	}

	// Create and test the HTTP client
	conf := api.DefaultConfig()
	conf.Address = *httpAddr
	conf.Token = token
	client, err := api.NewClient(conf)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error connecting to Consul agent: %s", err))
		return 1
	}
	_, err = client.Agent().NodeName()
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error querying Consul agent: %s", err))
		return 1
	}

	// Setup the lock or semaphore
	var lu *LockUnlock
	if limit == 1 {
		lu, err = c.setupLock(client, prefix, name)
	} else {
		lu, err = c.setupSemaphore(client, limit, prefix, name)
	}
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Lock setup failed: %s", err))
		return 1
	}

	// Attempt the acquisition
	if c.verbose {
		c.Ui.Info("Attempting lock acquisition")
	}
	lockCh, err := lu.lockFn(c.ShutdownCh)
	if err != nil || lockCh == nil {
		c.Ui.Error(fmt.Sprintf("Lock acquisition failed: %s", err))
		return 1
	}

	// Start the child process
	childDone := make(chan struct{})
	go func() {
		if err := c.startChild(script, childDone); err != nil {
			c.Ui.Error(fmt.Sprintf("%s", err))
		}
	}()

	// Monitor for shutdown, child termination, or lock loss
	select {
	case <-c.ShutdownCh:
		if c.verbose {
			c.Ui.Info("Shutdown triggered, killing child")
		}
	case <-lockCh:
		if c.verbose {
			c.Ui.Info("Lock lost, killing child")
		}
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child terminated, releasing lock")
		}
		goto RELEASE
	}

	// Kill the child
	if err := c.killChild(childDone); err != nil {
		c.Ui.Error(fmt.Sprintf("%s", err))
	}

RELEASE:
	// Release the lock before termination
	if err := lu.unlockFn(); err != nil {
		c.Ui.Error(fmt.Sprintf("Lock release failed: %s", err))
		return 1
	}

	// Cleanup the lock if no longer in use
	if err := lu.cleanupFn(); err != nil {
		if err != lu.inUseErr {
			c.Ui.Error(fmt.Sprintf("Lock cleanup failed: %s", err))
			return 1
		} else if c.verbose {
			c.Ui.Info("Cleanup aborted, lock in use")
		}
	} else if c.verbose {
		c.Ui.Info("Cleanup succeeded")
	}
	return 0
}

// setupLock is used to setup a new Lock given the API client,
// the key prefix to operate on, and an optional session name.
func (c *LockCommand) setupLock(client *api.Client, prefix, name string) (*LockUnlock, error) {
	// Use the DefaultSemaphoreKey extention, this way if a lock and
	// semaphore are both used at the same prefix, we will get a conflict
	// which we can report to the user.
	key := path.Join(prefix, api.DefaultSemaphoreKey)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up lock at path: %s", key))
	}
	opts := api.LockOptions{
		Key:         key,
		SessionName: name,
	}
	l, err := client.LockOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    l.Lock,
		unlockFn:  l.Unlock,
		cleanupFn: l.Destroy,
		inUseErr:  api.ErrLockInUse,
	}
	return lu, nil
}

// setupSemaphore is used to setup a new Semaphore given the
// API client, key prefix, session name, and slot holder limit.
func (c *LockCommand) setupSemaphore(client *api.Client, limit int, prefix, name string) (*LockUnlock, error) {
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Setting up semaphore (limit %d) at prefix: %s", limit, prefix))
	}
	opts := api.SemaphoreOptions{
		Prefix:      prefix,
		Limit:       limit,
		SessionName: name,
	}
	s, err := client.SemaphoreOpts(&opts)
	if err != nil {
		return nil, err
	}
	lu := &LockUnlock{
		lockFn:    s.Acquire,
		unlockFn:  s.Release,
		cleanupFn: s.Destroy,
		inUseErr:  api.ErrSemaphoreInUse,
	}
	return lu, nil
}

// startChild is a long running routine used to start and
// wait for the child process to exit.
func (c *LockCommand) startChild(script string, doneCh chan struct{}) error {
	defer close(doneCh)
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Starting handler '%s'", script))
	}
	// Create the command
	cmd, err := agent.ExecScript(script)
	if err != nil {
		c.Ui.Error(fmt.Sprintf("Error executing handler: %s", err))
		return err
	}

	// Setup the command streams
	cmd.Env = append(os.Environ(),
		"CONSUL_LOCK_HELD=true",
	)
	cmd.Stdin = nil
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr

	// Start the child process
	if err := cmd.Start(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error starting handler: %s", err))
		return err
	}

	// Setup the child info
	c.childLock.Lock()
	c.child = cmd.Process
	c.childLock.Unlock()

	// Wait for the child process
	if err := cmd.Wait(); err != nil {
		c.Ui.Error(fmt.Sprintf("Error running handler: %s", err))
		return err
	}
	return nil
}

// killChild is used to forcefully kill the child, first using SIGTERM
// to allow for a graceful cleanup and then using SIGKILL for a hard
// termination.
func (c *LockCommand) killChild(childDone chan struct{}) error {
	// Get the child process
	c.childLock.Lock()
	child := c.child
	c.childLock.Unlock()

	// If there is no child process (failed to start), we can quit early
	if child == nil {
		if c.verbose {
			c.Ui.Info("No child process to kill")
		}
		return nil
	}

	// Attempt a SIGTERM first
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Sending SIGTERM to child pid %d", child.Pid))
	}
	if err := syscall.Kill(child.Pid, syscall.SIGTERM); err != nil {
		return fmt.Errorf("Failed to terminate %d: %v", child.Pid, err)
	}

	// Wait for termination, or until a timeout
	select {
	case <-childDone:
		if c.verbose {
			c.Ui.Info("Child exited after SIGTERM")
		}
		return nil
	case <-time.After(lockKillGracePeriod):
		if c.verbose {
			c.Ui.Info(fmt.Sprintf("Child did not exit after grace period of %v",
				lockKillGracePeriod))
		}
	}

	// Send a final SIGKILL first
	if c.verbose {
		c.Ui.Info(fmt.Sprintf("Sending SIGKILL to child pid %d", child.Pid))
	}
	if err := syscall.Kill(child.Pid, syscall.SIGKILL); err != nil {
		return fmt.Errorf("Failed to kill %d: %v", child.Pid, err)
	}
	return nil
}

func (c *LockCommand) Synopsis() string {
	return "Execute a command holding a lock"
}

// LockUnlock is used to abstract over the differences between
// a lock and a semaphore.
type LockUnlock struct {
	lockFn    func(<-chan struct{}) (<-chan struct{}, error)
	unlockFn  func() error
	cleanupFn func() error
	inUseErr  error
}
<|endoftext|>


<commit_before>
package core

import ("fmt"
	"os"
	"strconv"
	"github.com/jackyb/go-sdl2/sdl"
	"github.com/op/go-nanomsg"
	"github.com/fire/go-ogre3d"
	"github.com/ugorji/go/codec"
	"runtime"
	"math")

type InputState struct {
	yawSens float32
	pitchSens float32
	orientationFactor float32 // +1/-1 easy switch between look around and manipulate something
	yaw float32 // degrees, modulo [-180,180] range
	pitch float32 // degrees, clamped [-90,90] range
	roll float32
	// orientation ogre.Quaternion // current orientation
}

func InitCore() {
	sdl.Init(sdl.INIT_EVERYTHING)
	window := sdl.CreateWindow("es_core::SDL",
		sdl.WINDOWPOS_UNDEFINED,
		sdl.WINDOWPOS_UNDEFINED,
		800,
		600,
		sdl.WINDOW_SHOWN)
	if window == nil {
		panic(fmt.Sprintf("sdl.CreateWindow failed: %s\n", sdl.GetError()))
	}
	defer sdl.Quit()
	var info sdl.SysWMInfo
	if !window.GetWMInfo(&info) {
		panic(fmt.Sprintf("window.GetWMInfo failed.\n"))
	}
	// Parse and print info's version
	// Parse and print info's SYSWM_TYPE
	root := ogre.NewRoot("", "", "ogre.log")
	defer root.Destroy()
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if runtime.GOOS == "windows" {
		root.LoadPlugin(wd  + "/RenderSystem_GL")
		}
	if runtime.GOOS == "darwin" {
		root.LoadPlugin(wd  + "/../frameworks/RenderSystem_GL")
	}

	renderers := root.GetAvailableRenderers()
	if renderers.RenderSystemListSize() != 1 {

		panic(fmt.Sprintf("Failed to initalize RendererRenderSystem_GL"))
	}
	root.SetRenderSystem(renderers.RenderSystemListGet(0))
	root.Initialise(false, "es_core::ogre")
	params := ogre.CreateNameValuePairList()
	if runtime.GOOS == "windows" {
		windowsInfo := info.GetWindowsInfo()
		windowString := strconv.FormatUint(uint64(*(*uint32)(windowsInfo.Window)), 10)
		params.AddPair("parentWindowHandle", windowString)
	}
	if runtime.GOOS == "darwin" {
		params.AddPair("macAPI", "cocoa")
		cocoaInfo := info.GetCocoaInfo()
		windowString := strconv.FormatUint(uint64(*(*uint32)(cocoaInfo.Window)), 10)
		params.AddPair("parentWindowHandle", windowString)
	}

	renderWindow := root.CreateRenderWindow("es_core::ogre", 800, 600, false, params)
//	renderWindow.SetVisible(true)

	nnGameSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
        if err != nil {
                panic(err)
        }
        _, err = nnGameSocket.Bind("tcp://127.0.0.1:60206")
        if err != nil {
                panic(err)
        }

	nnRenderSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
	if err != nil {
                panic(err)
        }
        _, err = nnRenderSocket.Bind("tcp://127.0.0.1:60207")
        if err != nil {
                panic(err)
        }

	nnInputPub, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PUB)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPub.Bind("tcp://127.0.0.1:60208")
        if err != nil {
                panic(err)
        }

	nnInputPull, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PULL)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPull.Bind("tcp://127.0.0.1:60209")
        if err != nil {
                panic(err)
        }
	go gameThread()
	var renderThreadParams RenderThreadParams
	renderThreadParams.root = root
	renderThreadParams.window = window
	renderThreadParams.ogreWindow = renderWindow

	go renderThread(renderThreadParams)

	window.SetGrab(true)
	sdl.SetRelativeMouseMode(true)

	shutdownRequested := false
	var is InputState
	is.yawSens = 0.1
	is.yaw = 0.0
	is.pitchSens = 0.1
	is.pitch = 0.0
	is.roll = 0.0
	is.orientationFactor = -1.0 // Look around config

	// Msgpack
	var (
		v interface{} //Value to decode into
		mh codec.MsgpackHandle
	)

	for !shutdownRequested {
		var b []byte
		// We wait here.
		b, err = nnInputPull.Recv(0)
		fmt.Printf("Game push received:\n")
		if err != nil {
			fmt.Printf("%s\n", err)
		}
		dec := codec.NewDecoderBytes(b, &mh)
		err = dec.Decode(&v)
		if err != nil {
			fmt.Printf("%s\n", err)
		}

		// poll for events before processing the request
		// NOTE: this is how SDL builds the internal mouse and keyboard state
		// TODO: done this way does not meet the objectives of smooth, frame independent mouse view control,
		// Plus it throws some latency into the calling thread

		var event sdl.Event
		for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent {
			switch t := event.(type) {
			case *sdl.KeyDownEvent:
				fmt.Printf("SDL keyboard event:\n")
			case *sdl.KeyUpEvent:
				fmt.Printf("SDL keyboard event:\n")
				if t.Keysym.Scancode == sdl.SCANCODE_ESCAPE {
					// Todo
					sendShutdown(nnRenderSocket, nnGameSocket)
					shutdownRequested = true
				}
			case *sdl.MouseMotionEvent:
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.yaw += is.orientationFactor * is.yawSens * float32(t.XRel)
				if is.yaw >= 0.0 {
					is.yaw = float32(math.Mod(float64(is.yaw) + 180.0, 360.0) - 180.0)
				} else {
					is.yaw = float32(math.Mod(float64(is.yaw) - 180.0, 360.0) + 180.0)
				}
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.pitch += is.orientationFactor * is.pitchSens * float32(t.YRel)
				if is.pitch > 90.0 {
					is.pitch = 90.0
				} else if ( is.pitch < -90.0 ) {
					is.pitch = -90.0
				}
				// build a quaternion of the current orientation
			}
		}
	}
}

func sendShutdown (nnRenderSocket *nanomsg.Socket, nnGameSocket *nanomsg.Socket) {
	fmt.Printf("Render socket shutdown.\n")
	fmt.Printf("Game socket shutdown.\n")
}
<commit_msg>Code fromEulerAnglesYXZ().<commit_after>
package core

import ("fmt"
	"os"
	"strconv"
	"github.com/jackyb/go-sdl2/sdl"
	"github.com/op/go-nanomsg"
	"github.com/fire/go-ogre3d"
	"github.com/ugorji/go/codec"
	"runtime"
	"math")

type InputState struct {
	yawSens float32
	pitchSens float32
	orientationFactor float32 // +1/-1 easy switch between look around and manipulate something
	yaw float32 // degrees, modulo [-180,180] range
	pitch float32 // degrees, clamped [-90,90] range
	roll float32
	// orientation ogre.Quaternion // current orientation
}

func InitCore() {
	sdl.Init(sdl.INIT_EVERYTHING)
	window := sdl.CreateWindow("es_core::SDL",
		sdl.WINDOWPOS_UNDEFINED,
		sdl.WINDOWPOS_UNDEFINED,
		800,
		600,
		sdl.WINDOW_SHOWN)
	if window == nil {
		panic(fmt.Sprintf("sdl.CreateWindow failed: %s\n", sdl.GetError()))
	}
	defer sdl.Quit()
	var info sdl.SysWMInfo
	if !window.GetWMInfo(&info) {
		panic(fmt.Sprintf("window.GetWMInfo failed.\n"))
	}
	// Parse and print info's version
	// Parse and print info's SYSWM_TYPE
	root := ogre.NewRoot("", "", "ogre.log")
	defer root.Destroy()
	wd, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if runtime.GOOS == "windows" {
		root.LoadPlugin(wd  + "/RenderSystem_GL")
		}
	if runtime.GOOS == "darwin" {
		root.LoadPlugin(wd  + "/../frameworks/RenderSystem_GL")
	}

	renderers := root.GetAvailableRenderers()
	if renderers.RenderSystemListSize() != 1 {

		panic(fmt.Sprintf("Failed to initalize RendererRenderSystem_GL"))
	}
	root.SetRenderSystem(renderers.RenderSystemListGet(0))
	root.Initialise(false, "es_core::ogre")
	params := ogre.CreateNameValuePairList()
	if runtime.GOOS == "windows" {
		windowsInfo := info.GetWindowsInfo()
		windowString := strconv.FormatUint(uint64(*(*uint32)(windowsInfo.Window)), 10)
		params.AddPair("parentWindowHandle", windowString)
	}
	if runtime.GOOS == "darwin" {
		params.AddPair("macAPI", "cocoa")
		cocoaInfo := info.GetCocoaInfo()
		windowString := strconv.FormatUint(uint64(*(*uint32)(cocoaInfo.Window)), 10)
		params.AddPair("parentWindowHandle", windowString)
	}

	renderWindow := root.CreateRenderWindow("es_core::ogre", 800, 600, false, params)
//	renderWindow.SetVisible(true)

	nnGameSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
        if err != nil {
                panic(err)
        }
        _, err = nnGameSocket.Bind("tcp://127.0.0.1:60206")
        if err != nil {
                panic(err)
        }

	nnRenderSocket, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.BUS)
	if err != nil {
                panic(err)
        }
        _, err = nnRenderSocket.Bind("tcp://127.0.0.1:60207")
        if err != nil {
                panic(err)
        }

	nnInputPub, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PUB)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPub.Bind("tcp://127.0.0.1:60208")
        if err != nil {
                panic(err)
        }

	nnInputPull, err := nanomsg.NewSocket(nanomsg.AF_SP, nanomsg.PULL)
        if err != nil {
                panic(err)
        }
        _, err = nnInputPull.Bind("tcp://127.0.0.1:60209")
        if err != nil {
                panic(err)
        }
	go gameThread()
	var renderThreadParams RenderThreadParams
	renderThreadParams.root = root
	renderThreadParams.window = window
	renderThreadParams.ogreWindow = renderWindow

	go renderThread(renderThreadParams)

	window.SetGrab(true)
	sdl.SetRelativeMouseMode(true)

	shutdownRequested := false
	var is InputState
	is.yawSens = 0.1
	is.yaw = 0.0
	is.pitchSens = 0.1
	is.pitch = 0.0
	is.roll = 0.0
	is.orientationFactor = -1.0 // Look around config

	// Msgpack
	var (
		v interface{} //Value to decode into
		mh codec.MsgpackHandle
	)

	for !shutdownRequested {
		var b []byte
		// We wait here.
		b, err = nnInputPull.Recv(0)
		fmt.Printf("Game push received:\n")
		if err != nil {
			fmt.Printf("%s\n", err)
		}
		dec := codec.NewDecoderBytes(b, &mh)
		err = dec.Decode(&v)
		if err != nil {
			fmt.Printf("%s\n", err)
		}

		// poll for events before processing the request
		// NOTE: this is how SDL builds the internal mouse and keyboard state
		// TODO: done this way does not meet the objectives of smooth, frame independent mouse view control,
		// Plus it throws some latency into the calling thread

		var event sdl.Event
		for event = sdl.PollEvent(); event != nil; event = sdl.PollEvent {
			switch t := event.(type) {
			case *sdl.KeyDownEvent:
				fmt.Printf("SDL keyboard event:\n")
			case *sdl.KeyUpEvent:
				fmt.Printf("SDL keyboard event:\n")
				if t.Keysym.Scancode == sdl.SCANCODE_ESCAPE {
					// Todo
					sendShutdown(nnRenderSocket, nnGameSocket)
					shutdownRequested = true
				}
			case *sdl.MouseMotionEvent:
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.yaw += is.orientationFactor * is.yawSens * float32(t.XRel)
				if is.yaw >= 0.0 {
					is.yaw = float32(math.Mod(float64(is.yaw) + 180.0, 360.0) - 180.0)
				} else {
					is.yaw = float32(math.Mod(float64(is.yaw) - 180.0, 360.0) + 180.0)
				}
				// + when manipulating an object, - when doing a first person view .. needs to be configurable?
				is.pitch += is.orientationFactor * is.pitchSens * float32(t.YRel)
				if is.pitch > 90.0 {
					is.pitch = 90.0
				} else if ( is.pitch < -90.0 ) {
					is.pitch = -90.0
				}
				// build a quaternion of the current orientation
				var r ogre.Matrix3
				r.FromEulerAnglesYXZ( degToRad(is.yaw), degToRad(is.pitch), degToRad(is.roll))
			}
		}
	}
}

func degToRad(deg float32) float32 {
	return deg * math.Pi / 180
}

func sendShutdown(nnRenderSocket *nanomsg.Socket, nnGameSocket *nanomsg.Socket) {
	fmt.Printf("Render socket shutdown.\n")
	fmt.Printf("Game socket shutdown.\n")
}
<|endoftext|>