<commit_before>package cmdex

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/bitrise-io/go-utils/errorutil"
)

// ----------

// CommandModel ...
type CommandModel struct {
	cmd *exec.Cmd
}

// NewCommand ...
func NewCommand(name string, args ...string) *CommandModel {
	return &CommandModel{
		cmd: exec.Command(name, args...),
	}
}

// NewCommandFromSlice ...
func NewCommandFromSlice(cmdSlice []string) (*CommandModel, error) {
	if len(cmdSlice) == 0 {
		return nil, errors.New("no command provided")
	} else if len(cmdSlice) == 1 {
		return NewCommand(cmdSlice[0]), nil
	}

	return NewCommand(cmdSlice[0], cmdSlice[1:]...), nil
}

// NewCommandWithCmd ...
func NewCommandWithCmd(cmd *exec.Cmd) *CommandModel {
	return &CommandModel{
		cmd: cmd,
	}
}

// GetCmd ...
func (command *CommandModel) GetCmd() *exec.Cmd {
	return command.cmd
}

// SetDir ...
func (command *CommandModel) SetDir(dir string) *CommandModel {
	command.cmd.Dir = dir
	return command
}

// SetEnvs ...
func (command *CommandModel) SetEnvs(envs []string) *CommandModel {
	command.cmd.Env = envs
	return command
}

// SetStdin ...
func (command *CommandModel) SetStdin(in io.Reader) *CommandModel {
	command.cmd.Stdin = in
	return command
}

// SetStdout ...
func (command *CommandModel) SetStdout(out io.Writer) *CommandModel {
	command.cmd.Stdout = out
	return command
}

// SetStderr ...
func (command *CommandModel) SetStderr(err io.Writer) *CommandModel {
	command.cmd.Stderr = err
	return command
}

// Run ...
func (command CommandModel) Run() error {
	return command.cmd.Run()
}

// RunAndReturnExitCode ...
func (command CommandModel) RunAndReturnExitCode() (int, error) {
	return RunCmdAndReturnExitCode(command.cmd)
}

// RunAndReturnTrimmedOutput ...
func (command CommandModel) RunAndReturnTrimmedOutput() (string, error) {
	return RunCmdAndReturnTrimmedOutput(command.cmd)
}

// RunAndReturnTrimmedCombinedOutput ...
func (command CommandModel) RunAndReturnTrimmedCombinedOutput() (string, error) {
	return RunCmdAndReturnTrimmedCombinedOutput(command.cmd)
}

// ----------

// PrintableCommandArgs ...
func PrintableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string {
	cmdArgsDecorated := []string{}
	for idx, anArg := range fullCommandArgs {
		quotedArg := strconv.Quote(anArg)
		if idx == 0 && !isQuoteFirst {
			quotedArg = anArg
		}
		cmdArgsDecorated = append(cmdArgsDecorated, quotedArg)
	}

	return strings.Join(cmdArgsDecorated, " ")
}

// RunCmdAndReturnExitCode ...
func RunCmdAndReturnExitCode(cmd *exec.Cmd) (int, error) {
	err := cmd.Run()
	if err != nil {
		exitCode, castErr := errorutil.CmdExitCodeFromError(err)
		if castErr != nil {
			return 1, fmt.Errorf("failed get exit code from error: %s, error: %s", err, castErr)
		}

		return exitCode, err
	}

	return 0, nil
}

// RunCmdAndReturnTrimmedOutput ...
func RunCmdAndReturnTrimmedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.Output()
	if err != nil {
		return "", err
	}
	if outBytes == nil {
		return "", nil
	}
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCmdAndReturnTrimmedCombinedOutput ...
func RunCmdAndReturnTrimmedCombinedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}
	if outBytes == nil {
		return "", nil
	}
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCommandWithReaderAndWriters ...
func RunCommandWithReaderAndWriters(inReader io.Reader, outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = inReader
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandWithWriters ...
func RunCommandWithWriters(outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandInDirWithEnvsAndReturnExitCode ...
func RunCommandInDirWithEnvsAndReturnExitCode(envs []string, dir, name string, args ...string) (int, error) {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	if len(envs) > 0 {
		cmd.Env = envs
	}

	return RunCmdAndReturnExitCode(cmd)
}

// RunCommandInDirAndReturnExitCode ...
func RunCommandInDirAndReturnExitCode(dir, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode([]string{}, dir, name, args...)
}

// RunCommandWithEnvsAndReturnExitCode ...
func RunCommandWithEnvsAndReturnExitCode(envs []string, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode(envs, "", name, args...)
}

// RunCommandInDir ...
func RunCommandInDir(dir, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	return cmd.Run()
}

// RunCommand ...
func RunCommand(name string, args ...string) error {
	return RunCommandInDir("", name, args...)
}

// RunCommandAndReturnStdout ..
func RunCommandAndReturnStdout(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	return RunCmdAndReturnTrimmedOutput(cmd)
}

// RunCommandInDirAndReturnCombinedStdoutAndStderr ...
func RunCommandInDirAndReturnCombinedStdoutAndStderr(dir, name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	if dir != "" {
		cmd.Dir = dir
	}
	return RunCmdAndReturnTrimmedCombinedOutput(cmd)
}

// RunCommandAndReturnCombinedStdoutAndStderr ..
func RunCommandAndReturnCombinedStdoutAndStderr(name string, args ...string) (string, error) {
	return RunCommandInDirAndReturnCombinedStdoutAndStderr("", name, args...)
}

// RunBashCommand ...
func RunBashCommand(cmdStr string) error {
	return RunCommand("bash", "-c", cmdStr)
}

// RunBashCommandLines ...
func RunBashCommandLines(cmdLines []string) error {
	for _, aLine := range cmdLines {
		if err := RunCommand("bash", "-c", aLine); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>Command - utility method `AppendEnvs` added<commit_after>package cmdex

import (
	"errors"
	"fmt"
	"io"
	"os"
	"os/exec"
	"strconv"
	"strings"

	"github.com/bitrise-io/go-utils/errorutil"
)

// ----------

// CommandModel ...
type CommandModel struct {
	cmd *exec.Cmd
}

// NewCommand ...
func NewCommand(name string, args ...string) *CommandModel {
	return &CommandModel{
		cmd: exec.Command(name, args...),
	}
}

// NewCommandFromSlice ...
func NewCommandFromSlice(cmdSlice []string) (*CommandModel, error) {
	if len(cmdSlice) == 0 {
		return nil, errors.New("no command provided")
	} else if len(cmdSlice) == 1 {
		return NewCommand(cmdSlice[0]), nil
	}

	return NewCommand(cmdSlice[0], cmdSlice[1:]...), nil
}

// NewCommandWithCmd ...
func NewCommandWithCmd(cmd *exec.Cmd) *CommandModel {
	return &CommandModel{
		cmd: cmd,
	}
}

// GetCmd ...
func (command *CommandModel) GetCmd() *exec.Cmd {
	return command.cmd
}

// SetDir ...
func (command *CommandModel) SetDir(dir string) *CommandModel {
	command.cmd.Dir = dir
	return command
}

// SetEnvs ...
func (command *CommandModel) SetEnvs(envs []string) *CommandModel {
	command.cmd.Env = envs
	return command
}

// AppendEnvs - appends the envs to the current os.Environ()
// Calling this multiple times will NOT appens the envs one by one,
// only the last "envs" set will be appended to os.Environ()!
func (command *CommandModel) AppendEnvs(envs []string) *CommandModel {
	return command.SetEnvs(append(os.Environ(), envs...))
}

// SetStdin ...
func (command *CommandModel) SetStdin(in io.Reader) *CommandModel {
	command.cmd.Stdin = in
	return command
}

// SetStdout ...
func (command *CommandModel) SetStdout(out io.Writer) *CommandModel {
	command.cmd.Stdout = out
	return command
}

// SetStderr ...
func (command *CommandModel) SetStderr(err io.Writer) *CommandModel {
	command.cmd.Stderr = err
	return command
}

// Run ...
func (command CommandModel) Run() error {
	return command.cmd.Run()
}

// RunAndReturnExitCode ...
func (command CommandModel) RunAndReturnExitCode() (int, error) {
	return RunCmdAndReturnExitCode(command.cmd)
}

// RunAndReturnTrimmedOutput ...
func (command CommandModel) RunAndReturnTrimmedOutput() (string, error) {
	return RunCmdAndReturnTrimmedOutput(command.cmd)
}

// RunAndReturnTrimmedCombinedOutput ...
func (command CommandModel) RunAndReturnTrimmedCombinedOutput() (string, error) {
	return RunCmdAndReturnTrimmedCombinedOutput(command.cmd)
}

// ----------

// PrintableCommandArgs ...
func PrintableCommandArgs(isQuoteFirst bool, fullCommandArgs []string) string {
	cmdArgsDecorated := []string{}
	for idx, anArg := range fullCommandArgs {
		quotedArg := strconv.Quote(anArg)
		if idx == 0 && !isQuoteFirst {
			quotedArg = anArg
		}
		cmdArgsDecorated = append(cmdArgsDecorated, quotedArg)
	}

	return strings.Join(cmdArgsDecorated, " ")
}

// RunCmdAndReturnExitCode ...
func RunCmdAndReturnExitCode(cmd *exec.Cmd) (int, error) {
	err := cmd.Run()
	if err != nil {
		exitCode, castErr := errorutil.CmdExitCodeFromError(err)
		if castErr != nil {
			return 1, fmt.Errorf("failed get exit code from error: %s, error: %s", err, castErr)
		}

		return exitCode, err
	}

	return 0, nil
}

// RunCmdAndReturnTrimmedOutput ...
func RunCmdAndReturnTrimmedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.Output()
	if err != nil {
		return "", err
	}
	if outBytes == nil {
		return "", nil
	}
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCmdAndReturnTrimmedCombinedOutput ...
func RunCmdAndReturnTrimmedCombinedOutput(cmd *exec.Cmd) (string, error) {
	outBytes, err := cmd.CombinedOutput()
	if err != nil {
		return "", err
	}
	if outBytes == nil {
		return "", nil
	}
	outStr := string(outBytes)
	return strings.TrimSpace(outStr), err
}

// RunCommandWithReaderAndWriters ...
func RunCommandWithReaderAndWriters(inReader io.Reader, outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = inReader
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandWithWriters ...
func RunCommandWithWriters(outWriter, errWriter io.Writer, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdout = outWriter
	cmd.Stderr = errWriter
	return cmd.Run()
}

// RunCommandInDirWithEnvsAndReturnExitCode ...
func RunCommandInDirWithEnvsAndReturnExitCode(envs []string, dir, name string, args ...string) (int, error) {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	if len(envs) > 0 {
		cmd.Env = envs
	}

	return RunCmdAndReturnExitCode(cmd)
}

// RunCommandInDirAndReturnExitCode ...
func RunCommandInDirAndReturnExitCode(dir, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode([]string{}, dir, name, args...)
}

// RunCommandWithEnvsAndReturnExitCode ...
func RunCommandWithEnvsAndReturnExitCode(envs []string, name string, args ...string) (int, error) {
	return RunCommandInDirWithEnvsAndReturnExitCode(envs, "", name, args...)
}

// RunCommandInDir ...
func RunCommandInDir(dir, name string, args ...string) error {
	cmd := exec.Command(name, args...)
	cmd.Stdin = os.Stdin
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if dir != "" {
		cmd.Dir = dir
	}
	return cmd.Run()
}

// RunCommand ...
func RunCommand(name string, args ...string) error {
	return RunCommandInDir("", name, args...)
}

// RunCommandAndReturnStdout ..
func RunCommandAndReturnStdout(name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	return RunCmdAndReturnTrimmedOutput(cmd)
}

// RunCommandInDirAndReturnCombinedStdoutAndStderr ...
func RunCommandInDirAndReturnCombinedStdoutAndStderr(dir, name string, args ...string) (string, error) {
	cmd := exec.Command(name, args...)
	if dir != "" {
		cmd.Dir = dir
	}
	return RunCmdAndReturnTrimmedCombinedOutput(cmd)
}

// RunCommandAndReturnCombinedStdoutAndStderr ..
func RunCommandAndReturnCombinedStdoutAndStderr(name string, args ...string) (string, error) {
	return RunCommandInDirAndReturnCombinedStdoutAndStderr("", name, args...)
}

// RunBashCommand ...
func RunBashCommand(cmdStr string) error {
	return RunCommand("bash", "-c", cmdStr)
}

// RunBashCommandLines ...
func RunBashCommandLines(cmdLines []string) error {
	for _, aLine := range cmdLines {
		if err := RunCommand("bash", "-c", aLine); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


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

package tchannel

import (
	"flag"
	"strings"
	"time"

	"github.com/spf13/viper"
	"go.uber.org/zap"
)

const (
	defaultConnCheckTimeout   = 250 * time.Millisecond
	tchannelPrefix            = "reporter.tchannel."
	collectorHostPort         = "collector.host-port"
	hostPort                  = "host-port"
	discoveryMinPeers         = "discovery.min-peers"
	discoveryConnCheckTimeout = "discovery.conn-check-timeout"
	reportTimeout             = "report-timeout"
)

// AddFlags adds flags for Builder.
func AddFlags(flags *flag.FlagSet) {
	flags.String(
		tchannelPrefix+hostPort,
		"",
		"comma-separated string representing host:ports of a static list of collectors to connect to directly (e.g. when not using service discovery)")
	flags.Int(
		tchannelPrefix+discoveryMinPeers,
		defaultMinPeers,
		"if using service discovery, the min number of connections to maintain to the backend")
	flags.Duration(
		tchannelPrefix+discoveryConnCheckTimeout,
		defaultConnCheckTimeout,
		"sets the timeout used when establishing new connections")
	flags.Duration(
		tchannelPrefix+reportTimeout,
		time.Second,
		"sets the timeout used when reporting spans")
	// TODO remove deprecated in 2.0
	flags.String(
		collectorHostPort,
		"",
		"(deprecated) see --"+tchannelPrefix+hostPort)
	flags.Int(
		discoveryMinPeers,
		defaultMinPeers,
		"(deprecated) see --"+tchannelPrefix+discoveryMinPeers)
	flags.Duration(
		discoveryConnCheckTimeout,
		defaultConnCheckTimeout,
		"(deprecated) see --"+tchannelPrefix+discoveryConnCheckTimeout)
}

// InitFromViper initializes Builder with properties retrieved from Viper.
func (b *Builder) InitFromViper(v *viper.Viper, logger *zap.Logger) *Builder {
	if len(v.GetString(collectorHostPort)) > 0 {
		logger.Warn("Using deprecated configuration", zap.String("option", collectorHostPort))
		b.CollectorHostPorts = strings.Split(v.GetString(collectorHostPort), ",")
	}
	b.DiscoveryMinPeers = v.GetInt(discoveryMinPeers)
	if b.DiscoveryMinPeers != defaultMinPeers {
		logger.Warn("Using deprecated configuration", zap.String("option", discoveryMinPeers))
	}
	b.ConnCheckTimeout = v.GetDuration(discoveryConnCheckTimeout)
	if b.ConnCheckTimeout != defaultConnCheckTimeout {
		logger.Warn("Using deprecated configuration", zap.String("option", discoveryConnCheckTimeout))
	}

	if len(v.GetString(tchannelPrefix+hostPort)) > 0 {
		b.CollectorHostPorts = strings.Split(v.GetString(tchannelPrefix+hostPort), ",")
	}

	if value := v.GetInt(tchannelPrefix + discoveryMinPeers); value != defaultMinPeers {
		b.DiscoveryMinPeers = value
	}
	if value := v.GetDuration(tchannelPrefix + discoveryConnCheckTimeout); value != defaultConnCheckTimeout {
		b.ConnCheckTimeout = value
	}
	b.ReportTimeout = v.GetDuration(tchannelPrefix + reportTimeout)
	return b
}
<commit_msg>Deprecate tchannel parameters (#1978)<commit_after>// Copyright (c) 2018 The Jaeger Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package tchannel

import (
	"flag"
	"strings"
	"time"

	"github.com/spf13/viper"
	"go.uber.org/zap"
)

const (
	defaultConnCheckTimeout   = 250 * time.Millisecond
	tchannelPrefix            = "reporter.tchannel."
	collectorHostPort         = "collector.host-port"
	hostPort                  = "host-port"
	discoveryMinPeers         = "discovery.min-peers"
	discoveryConnCheckTimeout = "discovery.conn-check-timeout"
	reportTimeout             = "report-timeout"
)

// AddFlags adds flags for Builder.
func AddFlags(flags *flag.FlagSet) {
	flags.String(
		tchannelPrefix+hostPort,
		"",
		"(deprecated) comma-separated string representing host:ports of a static list of collectors to connect to directly (e.g. when not using service discovery)")
	flags.Int(
		tchannelPrefix+discoveryMinPeers,
		defaultMinPeers,
		"(deprecated) if using service discovery, the min number of connections to maintain to the backend")
	flags.Duration(
		tchannelPrefix+discoveryConnCheckTimeout,
		defaultConnCheckTimeout,
		"(deprecated) sets the timeout used when establishing new connections")
	flags.Duration(
		tchannelPrefix+reportTimeout,
		time.Second,
		"(deprecated) sets the timeout used when reporting spans")
	// TODO remove deprecated in 2.0
	flags.String(
		collectorHostPort,
		"",
		"(deprecated) see --"+tchannelPrefix+hostPort)
	flags.Int(
		discoveryMinPeers,
		defaultMinPeers,
		"(deprecated) see --"+tchannelPrefix+discoveryMinPeers)
	flags.Duration(
		discoveryConnCheckTimeout,
		defaultConnCheckTimeout,
		"(deprecated) see --"+tchannelPrefix+discoveryConnCheckTimeout)
}

// InitFromViper initializes Builder with properties retrieved from Viper.
func (b *Builder) InitFromViper(v *viper.Viper, logger *zap.Logger) *Builder {
	if len(v.GetString(collectorHostPort)) > 0 {
		logger.Warn("Using deprecated configuration", zap.String("option", collectorHostPort))
		b.CollectorHostPorts = strings.Split(v.GetString(collectorHostPort), ",")
	}
	b.DiscoveryMinPeers = v.GetInt(discoveryMinPeers)
	if b.DiscoveryMinPeers != defaultMinPeers {
		logger.Warn("Using deprecated configuration", zap.String("option", discoveryMinPeers))
	}
	b.ConnCheckTimeout = v.GetDuration(discoveryConnCheckTimeout)
	if b.ConnCheckTimeout != defaultConnCheckTimeout {
		logger.Warn("Using deprecated configuration", zap.String("option", discoveryConnCheckTimeout))
	}

	if len(v.GetString(tchannelPrefix+hostPort)) > 0 {
		b.CollectorHostPorts = strings.Split(v.GetString(tchannelPrefix+hostPort), ",")
	}

	if value := v.GetInt(tchannelPrefix + discoveryMinPeers); value != defaultMinPeers {
		b.DiscoveryMinPeers = value
	}
	if value := v.GetDuration(tchannelPrefix + discoveryConnCheckTimeout); value != defaultConnCheckTimeout {
		b.ConnCheckTimeout = value
	}
	b.ReportTimeout = v.GetDuration(tchannelPrefix + reportTimeout)
	return b
}
<|endoftext|>


<commit_before>// +build ignore

package main

import (
	"encoding/json"
	"os"

	"github.com/Sirupsen/logrus"

	"github.com/docker/distribution/registry/storage/driver/ipc"
	"github.com/docker/distribution/registry/storage/driver/swift"
)

// An out-of-process Swift driver, intended to be run by ipc.NewDriverClient
func main() {
	parametersBytes := []byte(os.Args[1])
	var parameters map[string]string
	err := json.Unmarshal(parametersBytes, &parameters)
	if err != nil {
		panic(err)
	}

	driver, err := swift.FromParameters(parameters)
	if err != nil {
		panic(err)
	}
}
<commit_msg>Remove IPC support for Swift driver<commit_after><|endoftext|>


<commit_before>package collectors

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
	"time"

	"github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
)

var (
	wmiLock      sync.Mutex
	wmiReadLock  sync.Mutex
	wmiCount     = 0
	wmiCmd       *exec.Cmd
	wmiIn        io.WriteCloser
	wmiOut       io.ReadCloser
	wmiOutReader *bufio.Reader
)

func queryWmi(query string, dst interface{}) error {
	return queryWmiNamespace(query, dst, "")
}

func queryWmiNamespace(query string, dst interface{}, namespace string) (err error) {
	wmiLock.Lock()
	defer wmiLock.Unlock()
	collect.Add("wmi.queries", 1, nil)
	if wmiCount == 0 || wmiCmd == nil {
		collect.Add("wmi.exec", 1, nil)
		wmiCmd = exec.Command(os.Args[0], "-w")
		if wmiIn != nil {
			wmiIn.Close()
		}
		wmiIn, err = wmiCmd.StdinPipe()
		if err != nil {
			return
		}
		if wmiOut != nil {
			wmiOut.Close()
		}
		wmiOut, err = wmiCmd.StdoutPipe()
		if err != nil {
			return
		}
		wmiOutReader = bufio.NewReader(wmiOut)
		if err = wmiCmd.Start(); err != nil {
			wmiCmd = nil
			return
		}
	}
	wmiCount++
	defer func() {
		if wmiCount > 50 {
			if err := wmiCmd.Process.Kill(); err != nil {
				slog.Infoln(err)
			}
			wmiCmd = nil
			wmiCount = 0
		}
	}()

	q := wmi.WmiQuery{
		Query:     query,
		Namespace: namespace,
	}
	b, err := json.Marshal(&q)
	if err != nil {
		return err
	}
	fmt.Fprintln(wmiIn, string(b))
	done := make(chan error, 1)
	go func() {
		wmiReadLock.Lock()
		defer wmiReadLock.Unlock()
		b, err = wmiOutReader.ReadBytes('\n')
		if err != nil {
			done <- err
			return
		}
		done <- wmi.LoadJSON(b, dst)
	}()
	select {
	case err = <-done:
		// return
	case <-time.After(time.Second * 20):
		err = fmt.Errorf("wmi query timeout")
	}
	return
}
<commit_msg>cmd/scollector: Update wmi_windows.go<commit_after>package collectors

import (
	"bufio"
	"encoding/json"
	"fmt"
	"io"
	"os"
	"os/exec"
	"sync"
	"time"

	"github.com/StackExchange/scollector/opentsdb"
	"github.com/StackExchange/scollector/collect"
	"github.com/StackExchange/slog"
	"github.com/StackExchange/wmi"
)

var (
	wmiLock      sync.Mutex
	wmiReadLock  sync.Mutex
	wmiCount     = 0
	wmiCmd       *exec.Cmd
	wmiIn        io.WriteCloser
	wmiOut       io.ReadCloser
	wmiOutReader *bufio.Reader
)

func queryWmi(query string, dst interface{}) error {
	return queryWmiNamespace(query, dst, "")
}

func queryWmiNamespace(query string, dst interface{}, namespace string) (err error) {
	wmiLock.Lock()
	defer wmiLock.Unlock()
	collect.Add("wmi.queries", opentsdb.TagSet{}, 1)
	if wmiCount == 0 || wmiCmd == nil {
		collect.Add("wmi.exec", opentsdb.TagSet{}, 1)
		wmiCmd = exec.Command(os.Args[0], "-w")
		if wmiIn != nil {
			wmiIn.Close()
		}
		wmiIn, err = wmiCmd.StdinPipe()
		if err != nil {
			return
		}
		if wmiOut != nil {
			wmiOut.Close()
		}
		wmiOut, err = wmiCmd.StdoutPipe()
		if err != nil {
			return
		}
		wmiOutReader = bufio.NewReader(wmiOut)
		if err = wmiCmd.Start(); err != nil {
			wmiCmd = nil
			return
		}
	}
	wmiCount++
	defer func() {
		if wmiCount > 50 {
			if err := wmiCmd.Process.Kill(); err != nil {
				slog.Infoln(err)
			}
			wmiCmd = nil
			wmiCount = 0
		}
	}()

	q := wmi.WmiQuery{
		Query:     query,
		Namespace: namespace,
	}
	b, err := json.Marshal(&q)
	if err != nil {
		return err
	}
	fmt.Fprintln(wmiIn, string(b))
	done := make(chan error, 1)
	go func() {
		wmiReadLock.Lock()
		defer wmiReadLock.Unlock()
		b, err = wmiOutReader.ReadBytes('\n')
		if err != nil {
			done <- err
			return
		}
		done <- wmi.LoadJSON(b, dst)
	}()
	select {
	case err = <-done:
		// return
	case <-time.After(time.Second * 20):
		err = fmt.Errorf("wmi query timeout")
	}
	return
}
<|endoftext|>


<commit_before>/*
Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.

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

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

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

package vm

import (
	"context"
	"flag"
	"fmt"
	"strconv"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vim25/types"
)

type putusbscancode struct {
	*flags.ClientFlag
	*flags.SearchFlag

	UsbHidCodeValue int32
	UsbHidCode      string
	UsbHideString   string
	LeftControl     bool
	LeftShift       bool
	LeftAlt         bool
	LeftGui         bool
	RightControl    bool
	RightShift      bool
	RightAlt        bool
	RightGui        bool
}

func init() {
	cli.Register("vm.putusbscancode", &putusbscancode{})
}

func (cmd *putusbscancode) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.ClientFlag, ctx = flags.NewClientFlag(ctx)
	cmd.ClientFlag.Register(ctx, f)

	cmd.SearchFlag, ctx = flags.NewSearchFlag(ctx, flags.SearchVirtualMachines)
	cmd.SearchFlag.Register(ctx, f)

	f.Var(flags.NewInt32(&cmd.UsbHidCodeValue), "r", "Raw USB HID Code Value (int32)")
	f.StringVar(&cmd.UsbHidCode, "c", "0x00", "USB HID Code (hex)")
	f.StringVar(&cmd.UsbHideString, "s", "", "Raw String to Send")
	f.BoolVar(&cmd.LeftControl, "lc", false, "Enable/Disable Left Control")
	f.BoolVar(&cmd.LeftShift, "ls", false, "Enable/Disable Left Shift")
	f.BoolVar(&cmd.LeftAlt, "la", false, "Enable/Disable Left Alt")
	f.BoolVar(&cmd.LeftGui, "lg", false, "Enable/Disable Left Gui")
	f.BoolVar(&cmd.RightControl, "rc", false, "Enable/Disable Right Control")
	f.BoolVar(&cmd.RightShift, "rs", false, "Enable/Disable Right Shift")
	f.BoolVar(&cmd.RightAlt, "ra", false, "Enable/Disable Right Alt")
	f.BoolVar(&cmd.RightGui, "rg", false, "Enable/Disable Right Gui")
}

func (cmd *putusbscancode) Usage() string {
	return ""
}

func (cmd *putusbscancode) Description() string {
	return `Send Keystroke to VM.

Examples:
 Default Scenario
  govc vm.putusbscancode $vm -r 1376263 (writes an 'r' to the console)
  govc vm.putusbscancode -c 0x15 (writes an 'r' to the console)
  govc vm.putusbscancode -s "root" (writes 'root' to the console)
  govc vm.putusbscancode -c 0x58 (presses ENTER on the console)
  govc vm.putusbscancode -c 0x4c -la true -lc true (sends CTRL+ALT+DEL to console)`
}

func (cmd *putusbscancode) Process(ctx context.Context) error {
	if err := cmd.ClientFlag.Process(ctx); err != nil {
		return err
	}
	if err := cmd.SearchFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *putusbscancode) Run(ctx context.Context, f *flag.FlagSet) error {
	vms, err := cmd.VirtualMachines(f.Args())
	if err != nil {
		return err
	}

	for _, vm := range vms {
		err := cmd.processUserInput(ctx, vm)
		if err != nil {
			return err
		}
	}
	return err
}

func (cmd *putusbscancode) processUserInput(ctx context.Context, vm *object.VirtualMachine) error {
	if err := cmd.checkValidInputs(); err != nil {
		return err
	}
	modifiers := types.UsbScanCodeSpecModifierType{
		LeftControl:  &cmd.LeftControl,
		LeftShift:    &cmd.LeftShift,
		LeftAlt:      &cmd.LeftAlt,
		LeftGui:      &cmd.LeftGui,
		RightControl: &cmd.RightControl,
		RightShift:   &cmd.RightShift,
		RightAlt:     &cmd.RightAlt,
		RightGui:     &cmd.RightGui,
	}
	codes, err := cmd.processUsbCode()
	if err != nil {
		return err
	}
	var keyEventArray []types.UsbScanCodeSpecKeyEvent
	for code := range codes {
		keyEvent := types.UsbScanCodeSpecKeyEvent{
			UsbHidCode: int32(code),
			Modifiers:  &modifiers,
		}

		keyEventArray = append(keyEventArray, keyEvent)
	}
	spec := types.UsbScanCodeSpec{
		KeyEvents: keyEventArray,
	}
	_, err = vm.PutUsbScanCodes(ctx, spec)
	return err
}

func (cmd *putusbscancode) processUsbCode() ([]int32, error) {
	// check to ensure only 1 input is specified
	if cmd.UsbHidCode != "" &&
		cmd.UsbHidCodeValue != 0 &&
		cmd.UsbHideString != "" {
		return nil, fmt.Errorf("Specify only 1 argument for HID code")
	}
	if cmd.rawCodeProvided() {
		return []int32{cmd.UsbHidCodeValue}, nil
	}
	if cmd.hexCodeProvided() {
		s, err := hexToHidCode(cmd.UsbHidCode)
		if err != nil {
			return nil, err
		}
		return []int32{int32(s)}, nil
	}
	if cmd.stringProvided() {
		return nil, fmt.Errorf("Not yet supported")
	}
	return nil, nil
}

func hexToHidCode(hex string) (int32, error) {
	s, err := strconv.ParseInt(hex, 0, 32)
	if err != nil {
		return 0, err
	}
	s = s << 16
	s = s | 7
	return int32(s), nil
}

func (cmd *putusbscancode) checkValidInputs() error {
	// poor man's boolean XOR -> A xor B xor C = A'BC' + AB'C' + A'B'C + ABC
	if (!cmd.rawCodeProvided() && cmd.hexCodeProvided() && !cmd.stringProvided()) || // A'BC'
		(cmd.rawCodeProvided() && !cmd.hexCodeProvided() && !cmd.stringProvided()) || // AB'C'
		(!cmd.rawCodeProvided() && !cmd.hexCodeProvided() && cmd.stringProvided()) || // A'B'C
		(cmd.rawCodeProvided() && cmd.hexCodeProvided() && cmd.stringProvided()) { // ABC
		return nil
	}
	return fmt.Errorf("Specify only 1 argument")
}

func (cmd putusbscancode) rawCodeProvided() bool {
	return cmd.UsbHidCodeValue != 0
}

func (cmd putusbscancode) hexCodeProvided() bool {
	return cmd.UsbHidCode != ""
}

func (cmd putusbscancode) stringProvided() bool {
	return cmd.UsbHideString != ""
}
<commit_msg>Added string support<commit_after>/*
Copyright (c) 2014-2016 VMware, Inc. All Rights Reserved.

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

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

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

package vm

import (
	"context"
	"flag"
	"fmt"
	"strconv"

	"github.com/vmware/govmomi/govc/cli"
	"github.com/vmware/govmomi/govc/flags"
	"github.com/vmware/govmomi/object"
	"github.com/vmware/govmomi/vim25/types"
)

type hidKey struct {
	Code         int32
	ShiftPressed bool
}

var hidCharacterMap = map[string]hidKey{
	"a": {0x04, false},
	"b": {0x05, false},
	"c": {0x06, false},
	"d": {0x07, false},
	"e": {0x08, false},
	"f": {0x09, false},
	"g": {0x0a, false},
	"h": {0x0b, false},
	"i": {0x0c, false},
	"j": {0x0d, false},
	"k": {0x0e, false},
	"l": {0x0f, false},
	"m": {0x10, false},
	"n": {0x11, false},
	"o": {0x12, false},
	"p": {0x13, false},
	"q": {0x14, false},
	"r": {0x15, false},
	"s": {0x16, false},
	"t": {0x17, false},
	"u": {0x18, false},
	"v": {0x19, false},
	"w": {0x1a, false},
	"x": {0x1b, false},
	"y": {0x1c, false},
	"z": {0x1d, false},
	"1": {0x1e, false},
	"2": {0x1f, false},
	"3": {0x20, false},
	"4": {0x21, false},
	"5": {0x22, false},
	"6": {0x23, false},
	"7": {0x24, false},
	"8": {0x25, false},
	"9": {0x26, false},
	"0": {0x27, false},
	"A": {0x04, true},
	"B": {0x05, true},
	"C": {0x06, true},
	"D": {0x07, true},
	"E": {0x08, true},
	"F": {0x09, true},
	"G": {0x0a, true},
	"H": {0x0b, true},
	"I": {0x0c, true},
	"J": {0x0d, true},
	"K": {0x0e, true},
	"L": {0x0f, true},
	"M": {0x10, true},
	"N": {0x11, true},
	"O": {0x12, true},
	"P": {0x13, true},
	"Q": {0x14, true},
	"R": {0x15, true},
	"S": {0x16, true},
	"T": {0x17, true},
	"U": {0x18, true},
	"V": {0x19, true},
	"W": {0x1a, true},
	"X": {0x1b, true},
	"Y": {0x1c, true},
	"Z": {0x1d, true},
	"!": {0x1e, true},
	"@": {0x1f, true},
	"#": {0x20, true},
	"$": {0x21, true},
	"%": {0x22, true},
	"^": {0x23, true},
	"&": {0x24, true},
	"*": {0x25, true},
	"(": {0x26, true},
	")": {0x27, true},
	"-": {0x2d, false},
	"_": {0x2d, true},
	"=": {0x2e, false},
	"+": {0x2e, true},
	"[": {0x2f, false},
	"{": {0x2f, true},
	"]": {0x30, false},
	"}": {0x30, true},
	`\`: {0x31, false},
	"|": {0x31, true},
	";": {0x33, false},
	":": {0x33, true},
	"'": {0x34, false},
	`"`: {0x34, true},
	"`": {0x35, false},
	"~": {0x35, true},
	",": {0x36, false},
	"<": {0x36, true},
	".": {0x37, false},
	">": {0x37, true},
	"/": {0x38, false},
	"?": {0x38, true},
}

type putusbscancode struct {
	*flags.VirtualMachineFlag

	UsbHidCodeValue int32
	UsbHidCode      string
	UsbHidString    string
	LeftControl     bool
	LeftShift       bool
	LeftAlt         bool
	LeftGui         bool
	RightControl    bool
	RightShift      bool
	RightAlt        bool
	RightGui        bool
}

func init() {
	cli.Register("vm.putusbscancode", &putusbscancode{})
}

func (cmd *putusbscancode) Register(ctx context.Context, f *flag.FlagSet) {
	cmd.VirtualMachineFlag, ctx = flags.NewVirtualMachineFlag(ctx)
	cmd.VirtualMachineFlag.Register(ctx, f)

	f.Var(flags.NewInt32(&cmd.UsbHidCodeValue), "r", "Raw USB HID Code Value (int32)")
	f.StringVar(&cmd.UsbHidCode, "c", "", "USB HID Code (hex)")
	f.StringVar(&cmd.UsbHidString, "s", "", "Raw String to Send")
	f.BoolVar(&cmd.LeftControl, "lc", false, "Enable/Disable Left Control")
	f.BoolVar(&cmd.LeftShift, "ls", false, "Enable/Disable Left Shift")
	f.BoolVar(&cmd.LeftAlt, "la", false, "Enable/Disable Left Alt")
	f.BoolVar(&cmd.LeftGui, "lg", false, "Enable/Disable Left Gui")
	f.BoolVar(&cmd.RightControl, "rc", false, "Enable/Disable Right Control")
	f.BoolVar(&cmd.RightShift, "rs", false, "Enable/Disable Right Shift")
	f.BoolVar(&cmd.RightAlt, "ra", false, "Enable/Disable Right Alt")
	f.BoolVar(&cmd.RightGui, "rg", false, "Enable/Disable Right Gui")
}

func (cmd *putusbscancode) Usage() string {
	return ""
}

func (cmd *putusbscancode) Description() string {
	return `Send Keystroke to VM.

Examples:
 Default Scenario
  govc vm.putusbscancode $vm -r 1376263 (writes an 'r' to the console)
  govc vm.putusbscancode $vm -c 0x15 (writes an 'r' to the console)
  govc vm.putusbscancode $vm -s "root" (writes 'root' to the console)
  govc vm.putusbscancode $vm -c 0x28 (presses ENTER on the console)
  govc vm.putusbscancode $vm -c 0x4c -la true -lc true (sends CTRL+ALT+DEL to console)`
}

func (cmd *putusbscancode) Process(ctx context.Context) error {
	if err := cmd.VirtualMachineFlag.Process(ctx); err != nil {
		return err
	}
	return nil
}

func (cmd *putusbscancode) Run(ctx context.Context, f *flag.FlagSet) error {
	vm, err := cmd.VirtualMachine()
	if err != nil {
		return err
	}

	if vm == nil {
		return flag.ErrHelp
	}

	err = cmd.processUserInput(ctx, vm)
	if err != nil {
		return err
	}
	return nil
}

func (cmd *putusbscancode) processUserInput(ctx context.Context, vm *object.VirtualMachine) error {
	if err := cmd.checkValidInputs(); err != nil {
		return err
	}

	codes, err := cmd.processUsbCode()

	if err != nil {
		return err
	}

	var keyEventArray []types.UsbScanCodeSpecKeyEvent
	for _, code := range codes {
		leftShiftSetting := false
		if code.ShiftPressed || cmd.LeftShift {
			leftShiftSetting = true
		}
		modifiers := types.UsbScanCodeSpecModifierType{
			LeftControl:  &cmd.LeftControl,
			LeftShift:    &leftShiftSetting,
			LeftAlt:      &cmd.LeftAlt,
			LeftGui:      &cmd.LeftGui,
			RightControl: &cmd.RightControl,
			RightShift:   &cmd.RightShift,
			RightAlt:     &cmd.RightAlt,
			RightGui:     &cmd.RightGui,
		}
		keyEvent := types.UsbScanCodeSpecKeyEvent{
			UsbHidCode: code.Code,
			Modifiers:  &modifiers,
		}
		keyEventArray = append(keyEventArray, keyEvent)
	}

	spec := types.UsbScanCodeSpec{
		KeyEvents: keyEventArray,
	}

	_, err = vm.PutUsbScanCodes(ctx, spec)

	return err
}

func (cmd *putusbscancode) processUsbCode() ([]hidKey, error) {
	if cmd.rawCodeProvided() {
		return []hidKey{{cmd.UsbHidCodeValue, false}}, nil
	}
	if cmd.hexCodeProvided() {
		s, err := hexStringToHidCode(cmd.UsbHidCode)
		if err != nil {
			return nil, err
		}
		return []hidKey{{s, false}}, nil
	}

	if cmd.stringProvided() {
		var retKeyArray []hidKey
		for _, c := range cmd.UsbHidString {
			lookupValue, ok := hidCharacterMap[string(c)]
			if !ok {
				return nil, fmt.Errorf("Invalid Character %s in String: %s", string(c), cmd.UsbHidString)
			}
			lookupValue.Code = intToHidCode(lookupValue.Code)
			retKeyArray = append(retKeyArray, lookupValue)
		}
		return retKeyArray, nil
	}
	return nil, nil
}

func hexStringToHidCode(hex string) (int32, error) {
	s, err := strconv.ParseInt(hex, 0, 32)
	if err != nil {
		return 0, err
	}
	return intToHidCode(int32(s)), nil
}

func intToHidCode(v int32) int32 {
	var s int32
	s = v << 16
	s = s | 7
	return s
}

func (cmd *putusbscancode) checkValidInputs() error {
	// poor man's boolean XOR -> A xor B xor C = A'BC' + AB'C' + A'B'C + ABC
	if (!cmd.rawCodeProvided() && cmd.hexCodeProvided() && !cmd.stringProvided()) || // A'BC'
		(cmd.rawCodeProvided() && !cmd.hexCodeProvided() && !cmd.stringProvided()) || // AB'C'
		(!cmd.rawCodeProvided() && !cmd.hexCodeProvided() && cmd.stringProvided()) || // A'B'C
		(cmd.rawCodeProvided() && cmd.hexCodeProvided() && cmd.stringProvided()) { // ABC
		return nil
	}
	return fmt.Errorf("Specify only 1 argument")
}

func (cmd putusbscancode) rawCodeProvided() bool {
	return cmd.UsbHidCodeValue != 0
}

func (cmd putusbscancode) hexCodeProvided() bool {
	return cmd.UsbHidCode != ""
}

func (cmd putusbscancode) stringProvided() bool {
	return cmd.UsbHidString != ""
}
<|endoftext|>


<commit_before>package rclone_test

import (
	"context"
	"encoding/base64"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"

	"github.com/kopia/kopia/internal/blobtesting"
	"github.com/kopia/kopia/internal/clock"
	"github.com/kopia/kopia/internal/testlogging"
	"github.com/kopia/kopia/internal/testutil"
	"github.com/kopia/kopia/repo/blob"
	"github.com/kopia/kopia/repo/blob/logging"
	"github.com/kopia/kopia/repo/blob/rclone"
)

const defaultCleanupAge = time.Hour

var rcloneExternalProviders = map[string]string{
	"GoogleDrive": "gdrive:/kopia",
	"OneDrive":    "onedrive:/kopia",
}

func mustGetRcloneExeOrSkip(t *testing.T) string {
	t.Helper()

	rcloneExe := os.Getenv("RCLONE_EXE")
	if rcloneExe == "" {
		rcloneExe = "rclone"
	}

	if err := exec.Command(rcloneExe, "version").Run(); err != nil {
		if os.Getenv("CI") == "" {
			t.Skip("rclone not installed")
		} else {
			// on CI fail hard
			t.Fatal("rclone not installed")
		}
	}

	t.Logf("using rclone exe: %v", rcloneExe)

	return rcloneExe
}

func TestRCloneStorage(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	ctx := testlogging.Context(t)

	rcloneExe := mustGetRcloneExeOrSkip(t)
	dataDir := testutil.TempDirectory(t)

	st, err := rclone.New(ctx, &rclone.Options{
		// pass local file as remote path.
		RemotePath: dataDir,
		RCloneExe:  rcloneExe,
	})
	if err != nil {
		t.Fatalf("unable to connect to rclone backend: %v", err)
	}

	defer st.Close(ctx)

	var eg errgroup.Group

	// trigger multiple parallel reads to ensure we're properly preventing race
	// described in https://github.com/kopia/kopia/issues/624
	for i := 0; i < 100; i++ {
		eg.Go(func() error {
			if _, err := st.GetBlob(ctx, blob.ID(uuid.New().String()), 0, -1); !errors.Is(err, blob.ErrBlobNotFound) {
				return errors.Errorf("unexpected error when downloading non-existent blob: %v", err)
			}

			return nil
		})
	}

	if err := eg.Wait(); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	blobtesting.VerifyStorage(ctx, t, st)
	blobtesting.AssertConnectionInfoRoundTrips(ctx, t, st)
}

func TestRCloneStorageInvalidExe(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	ctx := testlogging.Context(t)

	_, err := rclone.New(ctx, &rclone.Options{
		RCloneExe:  "no-such-rclone",
		RemotePath: "mmm:/tmp/rclonetest",
	})
	if err == nil {
		t.Fatalf("unexpected success wen starting rclone")
	}
}

func TestRCloneStorageInvalidFlags(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	ctx := testlogging.Context(t)

	_, err := rclone.New(ctx, &rclone.Options{
		RCloneExe:  mustGetRcloneExeOrSkip(t),
		RemotePath: "mmm:/tmp/rclonetest",
		RCloneArgs: []string{"--no-such-flag"},
	})
	if err == nil {
		t.Fatalf("unexpected success wen starting rclone")
	}

	if !strings.Contains(err.Error(), "--no-such-flag") {
		t.Fatalf("error does not mention invalid flag (got '%v')", err)
	}
}

func TestRCloneProviders(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	var (
		rcloneArgs     []string
		embeddedConfig string
	)

	if cfg := os.Getenv("KOPIA_RCLONE_EMBEDDED_CONFIG_B64"); cfg != "" {
		b, err := base64.StdEncoding.DecodeString(cfg)
		if err != nil {
			t.Fatalf("unable to decode KOPIA_RCLONE_EMBEDDED_CONFIG_B64: %v", err)
		}

		embeddedConfig = string(b)
	}

	if cfg := os.Getenv("KOPIA_RCLONE_CONFIG_FILE"); cfg != "" {
		rcloneArgs = append(rcloneArgs, "--config="+cfg)
	}

	if len(rcloneArgs)+len(embeddedConfig) == 0 {
		t.Skipf("Either KOPIA_RCLONE_EMBEDDED_CONFIG_B64 or KOPIA_RCLONE_CONFIG_FILE must be provided")
	}

	rcloneExe := mustGetRcloneExeOrSkip(t)

	for name, rp := range rcloneExternalProviders {
		rp := rp

		opt := &rclone.Options{
			RemotePath:     rp,
			RCloneExe:      rcloneExe,
			RCloneArgs:     rcloneArgs,
			EmbeddedConfig: embeddedConfig,
		}

		t.Run(name, func(t *testing.T) {
			t.Parallel()

			ctx := testlogging.Context(t)

			cleanupOldData(ctx, t, opt, defaultCleanupAge)

			// we are using shared storage, append a guid so that tests don't collide
			opt.RemotePath += "/" + uuid.NewString()

			st, err := rclone.New(ctx, opt)
			if err != nil {
				t.Fatalf("unable to connect to rclone backend: %v", err)
			}

			// at the end of a test delete all blobs that were created.
			defer cleanupAllBlobs(ctx, t, st, 0)
			defer st.Close(ctx)

			blobtesting.VerifyStorage(ctx, t, logging.NewWrapper(st, t.Logf, "[RCLONE-STORAGE] "))
			blobtesting.AssertConnectionInfoRoundTrips(ctx, t, st)
		})
	}
}

func cleanupOldData(ctx context.Context, t *testing.T, opt *rclone.Options, cleanupAge time.Duration) {
	t.Helper()

	t.Logf("cleaning up %v", opt.RemotePath)
	defer t.Logf("finished cleaning up %v", opt.RemotePath)

	// cleanup old data from the bucket
	st, err := rclone.New(ctx, opt)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	defer st.Close(ctx)

	cleanupAllBlobs(ctx, t, st, cleanupAge)
}

func cleanupAllBlobs(ctx context.Context, t *testing.T, st blob.Storage, cleanupAge time.Duration) {
	t.Helper()

	_ = st.ListBlobs(ctx, "", func(it blob.Metadata) error {
		age := clock.Since(it.Timestamp)
		if age > cleanupAge {
			if err := st.DeleteBlob(ctx, it.BlobID); err != nil {
				t.Errorf("warning: unable to delete %q: %v", it.BlobID, err)
			}
		}
		return nil
	})
}
<commit_msg>rclone: fixed test cleanup (#1075)<commit_after>package rclone_test

import (
	"context"
	"encoding/base64"
	"os"
	"os/exec"
	"strings"
	"testing"
	"time"

	"github.com/google/uuid"
	"github.com/pkg/errors"
	"golang.org/x/sync/errgroup"

	"github.com/kopia/kopia/internal/blobtesting"
	"github.com/kopia/kopia/internal/clock"
	"github.com/kopia/kopia/internal/testlogging"
	"github.com/kopia/kopia/internal/testutil"
	"github.com/kopia/kopia/repo/blob"
	"github.com/kopia/kopia/repo/blob/logging"
	"github.com/kopia/kopia/repo/blob/rclone"
)

const defaultCleanupAge = time.Hour

var rcloneExternalProviders = map[string]string{
	"GoogleDrive": "gdrive:/kopia",
	"OneDrive":    "onedrive:/kopia",
}

func mustGetRcloneExeOrSkip(t *testing.T) string {
	t.Helper()

	rcloneExe := os.Getenv("RCLONE_EXE")
	if rcloneExe == "" {
		rcloneExe = "rclone"
	}

	if err := exec.Command(rcloneExe, "version").Run(); err != nil {
		if os.Getenv("CI") == "" {
			t.Skip("rclone not installed")
		} else {
			// on CI fail hard
			t.Fatal("rclone not installed")
		}
	}

	t.Logf("using rclone exe: %v", rcloneExe)

	return rcloneExe
}

func TestRCloneStorage(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	ctx := testlogging.Context(t)

	rcloneExe := mustGetRcloneExeOrSkip(t)
	dataDir := testutil.TempDirectory(t)

	st, err := rclone.New(ctx, &rclone.Options{
		// pass local file as remote path.
		RemotePath: dataDir,
		RCloneExe:  rcloneExe,
	})
	if err != nil {
		t.Fatalf("unable to connect to rclone backend: %v", err)
	}

	defer st.Close(ctx)

	var eg errgroup.Group

	// trigger multiple parallel reads to ensure we're properly preventing race
	// described in https://github.com/kopia/kopia/issues/624
	for i := 0; i < 100; i++ {
		eg.Go(func() error {
			if _, err := st.GetBlob(ctx, blob.ID(uuid.New().String()), 0, -1); !errors.Is(err, blob.ErrBlobNotFound) {
				return errors.Errorf("unexpected error when downloading non-existent blob: %v", err)
			}

			return nil
		})
	}

	if err := eg.Wait(); err != nil {
		t.Fatalf("unexpected error: %v", err)
	}

	blobtesting.VerifyStorage(ctx, t, st)
	blobtesting.AssertConnectionInfoRoundTrips(ctx, t, st)
}

func TestRCloneStorageInvalidExe(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	ctx := testlogging.Context(t)

	_, err := rclone.New(ctx, &rclone.Options{
		RCloneExe:  "no-such-rclone",
		RemotePath: "mmm:/tmp/rclonetest",
	})
	if err == nil {
		t.Fatalf("unexpected success wen starting rclone")
	}
}

func TestRCloneStorageInvalidFlags(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	ctx := testlogging.Context(t)

	_, err := rclone.New(ctx, &rclone.Options{
		RCloneExe:  mustGetRcloneExeOrSkip(t),
		RemotePath: "mmm:/tmp/rclonetest",
		RCloneArgs: []string{"--no-such-flag"},
	})
	if err == nil {
		t.Fatalf("unexpected success wen starting rclone")
	}

	if !strings.Contains(err.Error(), "--no-such-flag") {
		t.Fatalf("error does not mention invalid flag (got '%v')", err)
	}
}

func TestRCloneProviders(t *testing.T) {
	t.Parallel()
	testutil.ProviderTest(t)

	var (
		rcloneArgs     []string
		embeddedConfig string
	)

	if cfg := os.Getenv("KOPIA_RCLONE_EMBEDDED_CONFIG_B64"); cfg != "" {
		b, err := base64.StdEncoding.DecodeString(cfg)
		if err != nil {
			t.Fatalf("unable to decode KOPIA_RCLONE_EMBEDDED_CONFIG_B64: %v", err)
		}

		embeddedConfig = string(b)
	}

	if cfg := os.Getenv("KOPIA_RCLONE_CONFIG_FILE"); cfg != "" {
		rcloneArgs = append(rcloneArgs, "--config="+cfg)
	}

	if len(rcloneArgs)+len(embeddedConfig) == 0 {
		t.Skipf("Either KOPIA_RCLONE_EMBEDDED_CONFIG_B64 or KOPIA_RCLONE_CONFIG_FILE must be provided")
	}

	rcloneExe := mustGetRcloneExeOrSkip(t)

	for name, rp := range rcloneExternalProviders {
		rp := rp

		opt := &rclone.Options{
			RemotePath:     rp,
			RCloneExe:      rcloneExe,
			RCloneArgs:     rcloneArgs,
			EmbeddedConfig: embeddedConfig,
		}

		t.Run(name, func(t *testing.T) {
			t.Parallel()

			ctx := testlogging.Context(t)

			cleanupOldData(ctx, t, opt, defaultCleanupAge)

			// we are using shared storage, append a guid so that tests don't collide
			opt.RemotePath += "/" + uuid.NewString()

			st, err := rclone.New(ctx, opt)
			if err != nil {
				t.Fatalf("unable to connect to rclone backend: %v", err)
			}

			defer st.Close(ctx)

			// at the end of a test delete all blobs that were created.
			defer cleanupAllBlobs(ctx, t, st, 0)

			blobtesting.VerifyStorage(ctx, t, logging.NewWrapper(st, t.Logf, "[RCLONE-STORAGE] "))
			blobtesting.AssertConnectionInfoRoundTrips(ctx, t, st)
		})
	}
}

func cleanupOldData(ctx context.Context, t *testing.T, opt *rclone.Options, cleanupAge time.Duration) {
	t.Helper()

	t.Logf("cleaning up %v", opt.RemotePath)
	defer t.Logf("finished cleaning up %v", opt.RemotePath)

	// cleanup old data from the bucket
	st, err := rclone.New(ctx, opt)
	if err != nil {
		t.Fatalf("err: %v", err)
	}

	defer st.Close(ctx)

	cleanupAllBlobs(ctx, t, st, cleanupAge)
}

func cleanupAllBlobs(ctx context.Context, t *testing.T, st blob.Storage, cleanupAge time.Duration) {
	t.Helper()

	_ = st.ListBlobs(ctx, "", func(it blob.Metadata) error {
		age := clock.Since(it.Timestamp)
		if age > cleanupAge {
			if err := st.DeleteBlob(ctx, it.BlobID); err != nil {
				t.Errorf("warning: unable to delete %q: %v", it.BlobID, err)
			}
		}
		return nil
	})
}
<|endoftext|>


<commit_before>// +build integration

/*
Copyright 2020 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"testing"
	"time"

	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/hashicorp/go-getter"
	"k8s.io/minikube/pkg/util/retry"
)

func TestSkaffold(t *testing.T) {
	if NoneDriver() {
		t.Skip("none driver doesn't support `minikube docker-env`; skaffold depends on this command")
	}
	// can't use a unique profile, as skaffold only recognizes the
	// profile name 'minikube' as a local cluster
	profile := "minikube"
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(5))
	defer CleanupWithLogs(t, profile, cancel)

	// install latest skaffold release
	tf, err := installSkaffold()
	if err != nil {
		t.Fatalf("skaffold release installation failed: %v", err)
	}
	defer os.Remove(tf.Name())

	// start minikube cluster
	args := append([]string{"start", "-p", profile, "--memory=2200"}, StartArgs()...)
	rr, err := Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("starting minikube: %v\n%s", err, rr.Output())
	}

	// make sure minikube binary is in path so that skaffold can access it
	abs, err := filepath.Abs(Target())
	// copy minikube binary to minikube
	if err := mcnutils.CopyFile(Target(), filepath.Join(filepath.Dir(abs), "minikube")); err != nil {
		t.Fatalf("error copying to minikube")
	}
	if err != nil {
		t.Fatalf("absolute path to minikube binary: %v", err)
	}
	os.Setenv("PATH", fmt.Sprintf("%s:%s", filepath.Dir(abs), os.Getenv("PATH")))
	// make sure 'docker' and 'minikube' are on PATH
	for _, binary := range []string{"minikube", "docker"} {
		rr, err := Run(t, exec.CommandContext(ctx, "which", binary))
		if err != nil {
			t.Fatalf("'which %v' failed: check if %v is on PATH\n%v", binary, binary, rr.Output())
		}
	}
	// make sure "skaffold run" exits without failure
	cmd := exec.CommandContext(ctx, tf.Name(), "run", "--kube-context", profile, "--status-check=true", "--port-forward=false")
	cmd.Dir = "testdata/skaffold"
	rr, err = Run(t, cmd)
	if err != nil {
		t.Fatalf("error running skaffold: %v\n%s", err, rr.Output())
	}

	// make sure expected deployment is running
	if _, err := PodWait(ctx, t, profile, "default", "app=leeroy-app", Minutes(1)); err != nil {
		t.Fatalf("failed waiting for pod leeroy-app: %v", err)
	}
	if _, err := PodWait(ctx, t, profile, "default", "app=leeroy-web", Minutes(1)); err != nil {
		t.Fatalf("failed waiting for pod leeroy-web: %v", err)
	}
}

// installSkaffold installs the latest release of skaffold
func installSkaffold() (f *os.File, err error) {
	tf, err := ioutil.TempFile("", "skaffold.exe")
	if err != nil {
		return tf, err
	}
	tf.Close()

	url := "https://storage.googleapis.com/skaffold/releases/latest/skaffold-%s-amd64"
	url = fmt.Sprintf(url, runtime.GOOS)
	if runtime.GOOS == "windows" {
		url += ".exe"
	}

	if err := retry.Expo(func() error { return getter.GetFile(tf.Name(), url) }, 3*time.Second, Minutes(3)); err != nil {
		return tf, err
	}

	if runtime.GOOS != "windows" {
		if err := os.Chmod(tf.Name(), 0700); err != nil {
			return tf, err
		}
	}
	return tf, nil
}
<commit_msg>Update TestSkaffold to use unique profile names, more debuggable<commit_after>// +build integration

/*
Copyright 2020 The Kubernetes Authors All rights reserved.

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

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

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

package integration

import (
	"context"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"testing"
	"time"

	"github.com/hashicorp/go-getter"
	"github.com/otiai10/copy"
	"k8s.io/minikube/pkg/util/retry"
)

func TestSkaffold(t *testing.T) {
	if NoneDriver() {
		t.Skip("none driver doesn't support `minikube docker-env`; skaffold depends on this command")
	}

	profile := UniqueProfileName("skaffold")
	ctx, cancel := context.WithTimeout(context.Background(), Minutes(5))
	defer CleanupWithLogs(t, profile, cancel)

	// install latest skaffold release
	tf, err := installSkaffold()
	if err != nil {
		t.Fatalf("skaffold release installation failed: %v", err)
	}
	defer os.Remove(tf.Name())

	rr, err := Run(t, exec.CommandContext(ctx, tf.Name(), "version"))
	if err != nil {
		t.Fatalf("error running skaffold version: %v\n%s", err, rr.Output())
	}
	t.Logf("skaffold version: %s", rr.Stdout.Bytes())

	args := append([]string{"start", "-p", profile, "--memory=2600"}, StartArgs()...)
	rr, err = Run(t, exec.CommandContext(ctx, Target(), args...))
	if err != nil {
		t.Fatalf("starting minikube: %v\n%s", err, rr.Output())
	}

	// make sure minikube binary is in path so that skaffold can access it
	abs, err := filepath.Abs(Target())
	if err != nil {
		t.Fatalf("unable to determine abs path: %v", err)
	}

	oldPath := os.Getenv("PATH")
	os.Setenv("PATH", fmt.Sprintf("%s:%s", filepath.Dir(abs), os.Getenv("PATH")))
	defer func() {
		os.Setenv("PATH", oldPath)
		t.Logf("PATH is now %s", os.Getenv("PATH"))
	}()

	t.Logf("PATH is now %s", os.Getenv("PATH"))

	if filepath.Base(Target()) != "minikube" {
		new := filepath.Join(filepath.Dir(abs), "minikube")
		t.Logf("copying %s to %s", Target(), new)
		if err := copy.Copy(Target(), new); err != nil {
			t.Fatalf("error copying to minikube")
		}
	}

	// make sure 'docker' and 'minikube' are on PATH
	for _, binary := range []string{"minikube", "docker"} {
		_, err := exec.LookPath(binary)
		if err != nil {
			t.Fatalf("%q is not in path", binary)
		}
	}

	// make sure "skaffold run" exits without failure
	cmd := exec.CommandContext(ctx, tf.Name(), "run", "--minikube-profile", profile, "--kube-context", profile, "--status-check=true", "--port-forward=false")
	cmd.Dir = "testdata/skaffold"
	rr, err = Run(t, cmd)
	if err != nil {
		t.Fatalf("error running skaffold: %v\n%s", err, rr.Output())
	}

	// make sure expected deployment is running
	if _, err := PodWait(ctx, t, profile, "default", "app=leeroy-app", Minutes(1)); err != nil {
		t.Fatalf("failed waiting for pod leeroy-app: %v", err)
	}
	if _, err := PodWait(ctx, t, profile, "default", "app=leeroy-web", Minutes(1)); err != nil {
		t.Fatalf("failed waiting for pod leeroy-web: %v", err)
	}
}

// installSkaffold installs the latest release of skaffold
func installSkaffold() (f *os.File, err error) {
	tf, err := ioutil.TempFile("", "skaffold.exe")
	if err != nil {
		return tf, err
	}
	tf.Close()

	url := "https://storage.googleapis.com/skaffold/releases/latest/skaffold-%s-amd64"
	url = fmt.Sprintf(url, runtime.GOOS)
	if runtime.GOOS == "windows" {
		url += ".exe"
	}

	if err := retry.Expo(func() error { return getter.GetFile(tf.Name(), url) }, 3*time.Second, Minutes(3)); err != nil {
		return tf, err
	}

	if runtime.GOOS != "windows" {
		if err := os.Chmod(tf.Name(), 0700); err != nil {
			return tf, err
		}
	}
	return tf, nil
}
<|endoftext|>


<commit_before>package bot

import (
	"fmt"
	"strings"

	"github.com/svenwiltink/go-musicbot/music"
	"strconv"
)

type Command struct {
	Name       string
	MasterOnly bool
	Function   func(bot *MusicBot, message Message)
}

var helpCommand = &Command{
	Name: "help",
	Function: func(bot *MusicBot, message Message) {
		helpString := "Available commands: "
		for _, command := range bot.commands {
			helpString += command.Name + " "
		}

		bot.ReplyToMessage(message, helpString)
	},
}

var addCommand = &Command{
	Name: "add",
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 3)
		if len(words) <= 2 {
			bot.ReplyToMessage(message, "No song provided")
			return
		}

		song := &music.Song{
			Path: strings.TrimSpace(words[2]),
		}

		err := bot.musicPlayer.AddSong(song)
		if err != nil {
			bot.ReplyToMessage(message, err.Error())
		} else {
			if message.IsPrivate {
				bot.BroadcastMessage(fmt.Sprintf("%s added by %s", song.Name, message.Sender.NickName))
			}
			bot.ReplyToMessage(message, fmt.Sprintf("%s added", song.Name))
		}
	},
}

var searchAddCommand = &Command{
	Name: "search-add",
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 3)
		if len(words) <= 2 {
			bot.ReplyToMessage(message, "No song provided")
			return
		}

		songs, _ := bot.musicPlayer.Search(words[2])

		if len(songs) == 0 {
			bot.ReplyToMessage(message, "No song found")
			return
		}

		song := songs[0]
		err := bot.musicPlayer.AddSong(song)

		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Error: %v", err))
			return
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("%s added", song.Name))
		}

		bot.ReplyToMessage(message, fmt.Sprintf("%s added", song.Name))
	},
}

var nextCommand = &Command{
	Name: "next",
	Function: func(bot *MusicBot, message Message) {
		err := bot.musicPlayer.Next()
		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Could not skip song: %v", err))
		} else {
			if message.IsPrivate {
				bot.BroadcastMessage("Skipping song")
			}
			bot.ReplyToMessage(message, "Skipping song")
		}
	},
}

var pausedCommand = &Command{
	Name: "pause",
	Function: func(bot *MusicBot, message Message) {
		err := bot.musicPlayer.Pause()
		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Error: %v", err))
			return
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("%s stopped the music", message.Sender.NickName))
		}

		bot.ReplyToMessage(message, "Music paused")

	},
}

var playCommand = &Command{
	Name: "play",
	Function: func(bot *MusicBot, message Message) {
		err := bot.musicPlayer.Play()
		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Error: %v", err))
			return
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("%s resumed the music", message.Sender.NickName))
		}

		bot.ReplyToMessage(message, "Music resumed")

	},
}

var currentCommand = &Command{
	Name: "current",
	Function: func(bot *MusicBot, message Message) {
		song := bot.musicPlayer.GetCurrentSong()
		if song == nil {
			bot.ReplyToMessage(message, "Nothing currently playing")
			return
		}
		bot.ReplyToMessage(message, fmt.Sprintf("Current song: %s %s", song.Artist, song.Name))
	},
}

var whiteListCommand = &Command{
	Name:       "whitelist",
	MasterOnly: true,
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 4)
		if len(words) <= 3 {
			bot.ReplyToMessage(message, "whitelist <add|remove> <name>")
			return
		}

		name := strings.TrimSpace(words[3])
		if len(name) == 0 {
			bot.ReplyToMessage(message, "whitelist <add|remove> <name>")
			return
		}

		if words[2] == "add" {
			err := bot.whitelist.Add(name)
			if err == nil {
				bot.ReplyToMessage(message, fmt.Sprintf("added %s to the whitelist", name))
			} else {
				bot.ReplyToMessage(message, fmt.Sprintf("error: %v", err))
			}
		} else if words[2] == "remove" {
			err := bot.whitelist.Remove(name)
			if err == nil {
				bot.ReplyToMessage(message, fmt.Sprintf("added %s to the whitelist", name))
			} else {
				bot.ReplyToMessage(message, fmt.Sprintf("error: %v", err))
			}
		} else {
			bot.ReplyToMessage(message, "whitelist <add|remove> <name>")
			return
		}
	},
}

var volCommand = &Command{
	Name: "vol",
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 3)

		if len(words) == 2 {
			volume, err := bot.musicPlayer.GetVolume()

			if err != nil {
				bot.ReplyToMessage(message, fmt.Sprintf("unable to get volume: %v", err))
				return
			}

			bot.ReplyToMessage(message, fmt.Sprintf("Current volume: %d", volume))
			return
		}

		// init vars here so we can use them after the switch statement
		volumeString := strings.TrimSpace(words[2])
		var volume int
		var err error

		switch volumeString {
		case "++":
			{
				volume, err = bot.musicPlayer.IncreaseVolume(10)
				if err != nil {
					bot.ReplyToMessage(message, fmt.Sprintf("unable to increase volume: %s", err))
					return
				}
			}
		case "--":
			{
				volume, err = bot.musicPlayer.DecreaseVolume(10)
				if err != nil {
					bot.ReplyToMessage(message, fmt.Sprintf("unable to decrease volume: %s", err))
					return
				}
			}
		default:
			{
				volume, err := strconv.Atoi(strings.TrimSpace(volumeString))

				if err != nil {
					bot.ReplyToMessage(message, fmt.Sprintf("%s is not a valid number", volumeString))
					return
				}

				if volume >= 0 && volume <= 100 {
					bot.musicPlayer.SetVolume(volume)
				} else {
					bot.ReplyToMessage(message, fmt.Sprintf("%s is not a valid volume", volumeString))
					return
				}
			}
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("Volume set to %d by %s", volume, message.Sender.NickName))
		}

		bot.ReplyToMessage(message, fmt.Sprintf("Volume set to %d", volume))
	},
}

var aboutCommand = &Command{
	Name: "about",
	Function: func(bot *MusicBot, message Message) {
		bot.ReplyToMessage(message, "go-MusicBot by Sven Wiltink: https://github.com/svenwiltink/go-MusicBot")
	},
}<commit_msg>don't create a new instance of volume (#38)<commit_after>package bot

import (
	"fmt"
	"strings"

	"github.com/svenwiltink/go-musicbot/music"
	"strconv"
)

type Command struct {
	Name       string
	MasterOnly bool
	Function   func(bot *MusicBot, message Message)
}

var helpCommand = &Command{
	Name: "help",
	Function: func(bot *MusicBot, message Message) {
		helpString := "Available commands: "
		for _, command := range bot.commands {
			helpString += command.Name + " "
		}

		bot.ReplyToMessage(message, helpString)
	},
}

var addCommand = &Command{
	Name: "add",
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 3)
		if len(words) <= 2 {
			bot.ReplyToMessage(message, "No song provided")
			return
		}

		song := &music.Song{
			Path: strings.TrimSpace(words[2]),
		}

		err := bot.musicPlayer.AddSong(song)
		if err != nil {
			bot.ReplyToMessage(message, err.Error())
		} else {
			if message.IsPrivate {
				bot.BroadcastMessage(fmt.Sprintf("%s added by %s", song.Name, message.Sender.NickName))
			}
			bot.ReplyToMessage(message, fmt.Sprintf("%s added", song.Name))
		}
	},
}

var searchAddCommand = &Command{
	Name: "search-add",
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 3)
		if len(words) <= 2 {
			bot.ReplyToMessage(message, "No song provided")
			return
		}

		songs, _ := bot.musicPlayer.Search(words[2])

		if len(songs) == 0 {
			bot.ReplyToMessage(message, "No song found")
			return
		}

		song := songs[0]
		err := bot.musicPlayer.AddSong(song)

		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Error: %v", err))
			return
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("%s added", song.Name))
		}

		bot.ReplyToMessage(message, fmt.Sprintf("%s added", song.Name))
	},
}

var nextCommand = &Command{
	Name: "next",
	Function: func(bot *MusicBot, message Message) {
		err := bot.musicPlayer.Next()
		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Could not skip song: %v", err))
		} else {
			if message.IsPrivate {
				bot.BroadcastMessage("Skipping song")
			}
			bot.ReplyToMessage(message, "Skipping song")
		}
	},
}

var pausedCommand = &Command{
	Name: "pause",
	Function: func(bot *MusicBot, message Message) {
		err := bot.musicPlayer.Pause()
		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Error: %v", err))
			return
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("%s stopped the music", message.Sender.NickName))
		}

		bot.ReplyToMessage(message, "Music paused")

	},
}

var playCommand = &Command{
	Name: "play",
	Function: func(bot *MusicBot, message Message) {
		err := bot.musicPlayer.Play()
		if err != nil {
			bot.ReplyToMessage(message, fmt.Sprintf("Error: %v", err))
			return
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("%s resumed the music", message.Sender.NickName))
		}

		bot.ReplyToMessage(message, "Music resumed")

	},
}

var currentCommand = &Command{
	Name: "current",
	Function: func(bot *MusicBot, message Message) {
		song := bot.musicPlayer.GetCurrentSong()
		if song == nil {
			bot.ReplyToMessage(message, "Nothing currently playing")
			return
		}
		bot.ReplyToMessage(message, fmt.Sprintf("Current song: %s %s", song.Artist, song.Name))
	},
}

var whiteListCommand = &Command{
	Name:       "whitelist",
	MasterOnly: true,
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 4)
		if len(words) <= 3 {
			bot.ReplyToMessage(message, "whitelist <add|remove> <name>")
			return
		}

		name := strings.TrimSpace(words[3])
		if len(name) == 0 {
			bot.ReplyToMessage(message, "whitelist <add|remove> <name>")
			return
		}

		if words[2] == "add" {
			err := bot.whitelist.Add(name)
			if err == nil {
				bot.ReplyToMessage(message, fmt.Sprintf("added %s to the whitelist", name))
			} else {
				bot.ReplyToMessage(message, fmt.Sprintf("error: %v", err))
			}
		} else if words[2] == "remove" {
			err := bot.whitelist.Remove(name)
			if err == nil {
				bot.ReplyToMessage(message, fmt.Sprintf("added %s to the whitelist", name))
			} else {
				bot.ReplyToMessage(message, fmt.Sprintf("error: %v", err))
			}
		} else {
			bot.ReplyToMessage(message, "whitelist <add|remove> <name>")
			return
		}
	},
}

var volCommand = &Command{
	Name: "vol",
	Function: func(bot *MusicBot, message Message) {
		words := strings.SplitN(message.Message, " ", 3)

		if len(words) == 2 {
			volume, err := bot.musicPlayer.GetVolume()

			if err != nil {
				bot.ReplyToMessage(message, fmt.Sprintf("unable to get volume: %v", err))
				return
			}

			bot.ReplyToMessage(message, fmt.Sprintf("Current volume: %d", volume))
			return
		}

		// init vars here so we can use them after the switch statement
		volumeString := strings.TrimSpace(words[2])
		var volume int
		var err error

		switch volumeString {
		case "++":
			{
				volume, err = bot.musicPlayer.IncreaseVolume(10)
				if err != nil {
					bot.ReplyToMessage(message, fmt.Sprintf("unable to increase volume: %s", err))
					return
				}
			}
		case "--":
			{
				volume, err = bot.musicPlayer.DecreaseVolume(10)
				if err != nil {
					bot.ReplyToMessage(message, fmt.Sprintf("unable to decrease volume: %s", err))
					return
				}
			}
		default:
			{
				volume, err = strconv.Atoi(strings.TrimSpace(volumeString))

				if err != nil {
					bot.ReplyToMessage(message, fmt.Sprintf("%s is not a valid number", volumeString))
					return
				}

				if volume >= 0 && volume <= 100 {
					bot.musicPlayer.SetVolume(volume)
				} else {
					bot.ReplyToMessage(message, fmt.Sprintf("%s is not a valid volume", volumeString))
					return
				}
			}
		}

		if message.IsPrivate {
			bot.BroadcastMessage(fmt.Sprintf("Volume set to %d by %s", volume, message.Sender.NickName))
		}

		bot.ReplyToMessage(message, fmt.Sprintf("Volume set to %d", volume))
	},
}

var aboutCommand = &Command{
	Name: "about",
	Function: func(bot *MusicBot, message Message) {
		bot.ReplyToMessage(message, "go-MusicBot by Sven Wiltink: https://github.com/svenwiltink/go-MusicBot")
	},
}<|endoftext|>


<commit_before>package ping

import (
	"fmt"
	"net"
	"time"
)

// TCPing ...
type TCPing struct {
	target *Target
	done   chan struct{}
	result *Result
}

var _ Pinger = (*TCPing)(nil)

// NewTCPing return a new TCPing
func NewTCPing() *TCPing {
	tcping := TCPing{
		done: make(chan struct{}),
	}
	return &tcping
}

// SetTarget set target for TCPing
func (tcping *TCPing) SetTarget(target *Target) {
	tcping.target = target
	if tcping.result == nil {
		tcping.result = &Result{Target: target}
	}
}

// Result return the result
func (tcping TCPing) Result() *Result {
	return tcping.result
}

// Start a tcping
func (tcping TCPing) Start() <-chan struct{} {
	go func() {
		t := time.NewTicker(tcping.target.Interval)
		defer t.Stop()
		for {
			select {
			case <-t.C:
				if tcping.result.Counter >= tcping.target.Counter && tcping.target.Counter != 0 {
					tcping.Stop()
					return
				}
				duration, remoteAddr, err := tcping.ping()
				tcping.result.Counter++

				if err != nil {
					fmt.Printf("Ping %s - failed: %s\n", tcping.target, err)
				} else {
					fmt.Printf("Ping %s(%s) - Connected - time=%s\n", tcping.target, remoteAddr, duration)

					if tcping.result.MinDuration == 0 {
						tcping.result.MinDuration = duration
					}
					if tcping.result.MaxDuration == 0 {
						tcping.result.MaxDuration = duration
					}
					tcping.result.SuccessCounter++
					if duration > tcping.result.MaxDuration {
						tcping.result.MaxDuration = duration
					} else if duration < tcping.result.MinDuration {
						tcping.result.MinDuration = duration
					}
					tcping.result.TotalDuration += duration
				}
			case <-tcping.done:
				return
			}
		}
	}()
	return tcping.done
}

// Stop the tcping
func (tcping *TCPing) Stop() {
	tcping.done <- struct{}{}
}

func (tcping TCPing) ping() (time.Duration, net.Addr, error) {
	var remoteAddr net.Addr
	duration, errIfce := timeIt(func() interface{} {
		conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", tcping.target.Host, tcping.target.Port), tcping.target.Timeout)
		if err != nil {
			return err
		}
		remoteAddr = conn.RemoteAddr()
		conn.Close()
		return nil
	})
	if errIfce != nil {
		err := errIfce.(error)
		return 0, remoteAddr, err
	}
	return time.Duration(duration), remoteAddr, nil
}
<commit_msg>Update tcp.go (#18)<commit_after>package ping

import (
	"fmt"
	"net"
	"time"
)

// TCPing ...
type TCPing struct {
	target *Target
	done   chan struct{}
	result *Result
}

var _ Pinger = (*TCPing)(nil)

// NewTCPing return a new TCPing
func NewTCPing() *TCPing {
	tcping := TCPing{
		done: make(chan struct{}),
	}
	return &tcping
}

// SetTarget set target for TCPing
func (tcping *TCPing) SetTarget(target *Target) {
	tcping.target = target
	if tcping.result == nil {
		tcping.result = &Result{Target: target}
	}
}

// Result return the result
func (tcping TCPing) Result() *Result {
	return tcping.result
}

// Start a tcping
func (tcping TCPing) Start() <-chan struct{} {
	go func() {
		// cheap workaround to not wait for Interval seconds to get the 1st ping: set short wait time and ...
		t := time.NewTicker(1)
		defer t.Stop()
		for {
			select {
			case <-t.C:
				// ... change to real Interval after the 1st run
				t.Reset(tcping.target.Interval)

				if tcping.result.Counter >= tcping.target.Counter && tcping.target.Counter != 0 {
					tcping.Stop()
					return
				}
				duration, remoteAddr, err := tcping.ping()
				tcping.result.Counter++

				if err != nil {
					fmt.Printf("Ping %s - failed: %s\n", tcping.target, err)
				} else {
					fmt.Printf("Ping %s(%s) - Connected - time=%s\n", tcping.target, remoteAddr, duration)

					if tcping.result.MinDuration == 0 {
						tcping.result.MinDuration = duration
					}
					if tcping.result.MaxDuration == 0 {
						tcping.result.MaxDuration = duration
					}
					tcping.result.SuccessCounter++
					if duration > tcping.result.MaxDuration {
						tcping.result.MaxDuration = duration
					} else if duration < tcping.result.MinDuration {
						tcping.result.MinDuration = duration
					}
					tcping.result.TotalDuration += duration
				}
			case <-tcping.done:
				return
			}
		}
	}()
	return tcping.done
}

// Stop the tcping
func (tcping *TCPing) Stop() {
	tcping.done <- struct{}{}
}

func (tcping TCPing) ping() (time.Duration, net.Addr, error) {
	var remoteAddr net.Addr
	duration, errIfce := timeIt(func() interface{} {
		conn, err := net.DialTimeout("tcp", fmt.Sprintf("%s:%d", tcping.target.Host, tcping.target.Port), tcping.target.Timeout)
		if err != nil {
			return err
		}
		remoteAddr = conn.RemoteAddr()
		conn.Close()
		return nil
	})
	if errIfce != nil {
		err := errIfce.(error)
		return 0, remoteAddr, err
	}
	return time.Duration(duration), remoteAddr, nil
}
<|endoftext|>


<commit_before>package centralserver

import (
	"distributed2048/rpc/centralrpc"
	"distributed2048/util"
	"errors"
	"fmt"
	"math"
	"net"
	"net/http"
	"net/rpc"
	"os"
	"sync"
)

const (
	ERROR_LOG bool = true
	DEBUG_LOG bool = true
)

var LOGV = util.NewLogger(DEBUG_LOG, "DEBUG", os.Stdout)
var LOGE = util.NewLogger(ERROR_LOG, "ERROR", os.Stderr)

type gameServer struct {
	info        centralrpc.Node
	clientCount int
}

type centralServer struct {
	nextGameServerID     uint32
	gameServersLock      sync.Mutex
	gameServers          map[uint32]*gameServer
	hostPortToGameServer map[string]*gameServer
	gameServersSlice     []centralrpc.Node
	numGameServers       int
}

func NewCentralServer(port, numGameServers int) (CentralServer, error) {
	if numGameServers < 1 {
		return nil, errors.New("numGameServers must be at least 1")
	}

	cs := &centralServer{
		numGameServers:   numGameServers,
		gameServers:      make(map[uint32]*gameServer),
		gameServersSlice: nil,
	}

	rpc.RegisterName("CentralServer", centralrpc.Wrap(cs))
	rpc.HandleHTTP()
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	go http.Serve(l, nil)

	return cs, nil
}

func (cs *centralServer) GetGameServerForClient(args *centralrpc.GetGameServerForClientArgs, reply *centralrpc.GetGameServerForClientReply) error {
	cs.gameServersLock.Lock()
	if len(cs.gameServers) < cs.numGameServers {
		// Not all game servers have connected to the ring, so reply with NotReady
		reply.Status = centralrpc.NotReady
	} else {
		id := cs.getGameServerIDMinClients()
		cs.gameServers[id].clientCount++
		reply.Status = centralrpc.OK
		reply.HostPort = cs.gameServers[id].info.HostPort
	}
	cs.gameServersLock.Unlock()

	return nil
}

func (cs *centralServer) RegisterGameServer(args *centralrpc.RegisterGameServerArgs, reply *centralrpc.RegisterGameServerReply) error {
	cs.gameServersLock.Lock()

	var id uint32
	if gs, exists := cs.hostPortToGameServer[args.HostPort]; !exists {

		// Get a new ID
		id = cs.nextGameServerID
		cs.nextGameServerID++

		// Get host:port
		hostport := args.HostPort

		// Add new server object to map
		gs = &gameServer{centralrpc.Node{id, hostport}, 0}
		cs.gameServers[id] = gs
		cs.hostPortToGameServer[hostport] = gs
	} else {
		id = gs.info.NodeID
	}

	// Check if all the game servers in the ring have registered. If they
	// haven't, then reply with not ready. Otherwise, reply with OK, send back
	// to the unique ID, and the list of all game servers.
	if len(cs.gameServers) < cs.numGameServers {
		reply.Status = centralrpc.NotReady
	} else {
		reply.Status = centralrpc.OK
		reply.GameServerID = id
		// Check if the game servers sliced has been cached. If it hasn't, make it.
		if cs.gameServersSlice == nil {
			cs.gameServersSlice = make([]centralrpc.Node, len(cs.gameServers))
			i := 0
			for _, node := range cs.gameServers {
				cs.gameServersSlice[i] = node.info
				i++
			}
		}
		reply.Servers = cs.gameServersSlice
	}

	cs.gameServersLock.Unlock()

	return nil
}

func (cs *centralServer) getGameServerIDMinClients() uint32 {
	// Must be called with the LOCK acquired
	min := math.MaxInt32
	var resultID uint32
	for _, gs := range cs.gameServers {
		if gs.clientCount < min {
			min = gs.clientCount
			resultID = gs.info.NodeID
		}
	}
	return resultID
}
<commit_msg>Centralserver now serves up information to game clients who want to connect at http://localhost:15430/client/<commit_after>package centralserver

import (
	"distributed2048/rpc/centralrpc"
	"distributed2048/util"
	"encoding/json"
	"errors"
	"fmt"
	"math"
	"net"
	"net/http"
	"net/rpc"
	"os"
	"sync"
)

const (
	ERROR_LOG bool = true
	DEBUG_LOG bool = true
)

var LOGV = util.NewLogger(DEBUG_LOG, "DEBUG", os.Stdout)
var LOGE = util.NewLogger(ERROR_LOG, "ERROR", os.Stderr)

type gameServer struct {
	info        centralrpc.Node
	clientCount int
}

type centralServer struct {
	nextGameServerID     uint32
	gameServersLock      sync.Mutex
	gameServers          map[uint32]*gameServer
	hostPortToGameServer map[string]*gameServer
	gameServersSlice     []centralrpc.Node
	numGameServers       int
}

func NewCentralServer(port, numGameServers int) (CentralServer, error) {
	if numGameServers < 1 {
		return nil, errors.New("numGameServers must be at least 1")
	}

	cs := &centralServer{
		numGameServers:   numGameServers,
		gameServers:      make(map[uint32]*gameServer),
		gameServersSlice: nil,
	}

	// Serve up information for the game client
	http.HandleFunc("/client/", cs.gameClientViewHandler)

	rpc.RegisterName("CentralServer", centralrpc.Wrap(cs))
	rpc.HandleHTTP()
	l, err := net.Listen("tcp", fmt.Sprintf(":%d", port))
	if err != nil {
		return nil, err
	}
	go http.Serve(l, nil)

	return cs, nil
}

func (cs *centralServer) GetGameServerForClient(args *centralrpc.GetGameServerForClientArgs, reply *centralrpc.GetGameServerForClientReply) error {
	cs.gameServersLock.Lock()
	if len(cs.gameServers) < cs.numGameServers {
		// Not all game servers have connected to the ring, so reply with NotReady
		reply.Status = centralrpc.NotReady
	} else {
		id := cs.getGameServerIDMinClients()
		cs.gameServers[id].clientCount++
		reply.Status = centralrpc.OK
		reply.HostPort = cs.gameServers[id].info.HostPort
	}
	cs.gameServersLock.Unlock()

	return nil
}

func (cs *centralServer) RegisterGameServer(args *centralrpc.RegisterGameServerArgs, reply *centralrpc.RegisterGameServerReply) error {
	cs.gameServersLock.Lock()

	var id uint32
	if gs, exists := cs.hostPortToGameServer[args.HostPort]; !exists {

		// Get a new ID
		id = cs.nextGameServerID
		cs.nextGameServerID++

		// Get host:port
		hostport := args.HostPort

		// Add new server object to map
		gs = &gameServer{centralrpc.Node{id, hostport}, 0}
		cs.gameServers[id] = gs
		cs.hostPortToGameServer[hostport] = gs
	} else {
		id = gs.info.NodeID
	}

	// Check if all the game servers in the ring have registered. If they
	// haven't, then reply with not ready. Otherwise, reply with OK, send back
	// to the unique ID, and the list of all game servers.
	if len(cs.gameServers) < cs.numGameServers {
		reply.Status = centralrpc.NotReady
	} else {
		reply.Status = centralrpc.OK
		reply.GameServerID = id
		// Check if the game servers sliced has been cached. If it hasn't, make it.
		if cs.gameServersSlice == nil {
			cs.gameServersSlice = make([]centralrpc.Node, len(cs.gameServers))
			i := 0
			for _, node := range cs.gameServers {
				cs.gameServersSlice[i] = node.info
				i++
			}
		}
		reply.Servers = cs.gameServersSlice
	}

	cs.gameServersLock.Unlock()

	return nil
}

type HttpReply struct {
	Status   string
	Hostport string
}

func (cs *centralServer) gameClientViewHandler(w http.ResponseWriter, r *http.Request) {
	reply := HttpReply{}
	cs.gameServersLock.Lock()
	if len(cs.gameServers) < cs.numGameServers {
		// Not all game servers have connected to the ring, so reply with NotReady
		reply.Status = "NotReady"
	} else {
		id := cs.getGameServerIDMinClients()
		cs.gameServers[id].clientCount++
		reply.Status = "OK"
		reply.Hostport = cs.gameServers[id].info.HostPort
	}
	cs.gameServersLock.Unlock()

	buf, err := json.Marshal(reply)
	if err != nil {
		http.Error(w, fmt.Sprintln(err), 500)
		return
	}
	fmt.Fprintf(w, "%s", string(buf))
}

func (cs *centralServer) getGameServerIDMinClients() uint32 {
	// Must be called with the LOCK acquired
	min := math.MaxInt32
	var resultID uint32
	for _, gs := range cs.gameServers {
		if gs.clientCount < min {
			min = gs.clientCount
			resultID = gs.info.NodeID
		}
	}
	return resultID
}
<|endoftext|>


<commit_before>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package band

import "errors"

var demodulationFloor = map[string]float32{
	"SF7BW125":  -7.5,
	"SF8BW125":  -10,
	"SF9BW125":  -12.5,
	"SF10BW125": -15,
	"SF11BW125": -17.5,
	"SF12BW125": -20,
	"SF7BW250":  -4.5,
}

func linkMargin(dataRate string, snr float32) float32 {
	if floor, ok := demodulationFloor[dataRate]; ok {
		return snr - floor
	}
	return 0
}

// ADRConfig contains configuration for Adaptive Data Rate
type ADRConfig struct {
	MinDataRate int
	MaxDataRate int
	MinTXPower  int
	MaxTXPower  int
}

// ErrADRUnavailable is returned when ADR is not available
var ErrADRUnavailable = errors.New("ADR Unavailable")

// ADRSettings gets the ADR settings given a dataRate, txPower, SNR and device margin
func (f *FrequencyPlan) ADRSettings(dataRate string, txPower int, snr float32, deviceMargin float32) (desiredDataRate string, desiredTxPower int, err error) {
	if f.ADR == nil {
		return dataRate, txPower, ErrADRUnavailable
	}
	margin := linkMargin(dataRate, snr) - deviceMargin
	drIdx, err := f.GetDataRateIndexFor(dataRate)
	if err != nil {
		return dataRate, txPower, err
	}
	nStep := int(margin / 3)

	// Increase the data rate with each step
	for nStep > 0 && drIdx < f.ADR.MaxDataRate {
		drIdx++
		nStep--
	}

	// Decrease the Tx power by 3 for each step, until min reached
	for nStep > 0 && txPower > f.ADR.MinTXPower {
		txPower -= 3
		nStep--
	}
	if txPower < f.ADR.MinTXPower {
		txPower = f.ADR.MinTXPower
	}

	// Increase the Tx power by 3 for each step, until max reached
	for nStep < 0 && txPower < f.ADR.MaxTXPower {
		txPower += 3
	}
	if txPower > f.ADR.MaxTXPower {
		txPower = f.ADR.MaxTXPower
	}

	desiredDataRate, err = f.GetDataRateStringForIndex(drIdx)
	if err != nil {
		return dataRate, txPower, err // This should maybe panic; it means that f.ADR is incosistent with f.DataRates
	}
	return desiredDataRate, txPower, nil
}
<commit_msg>Update adr.go<commit_after>// Copyright © 2017 The Things Network
// Use of this source code is governed by the MIT license that can be found in the LICENSE file.

package band

import "errors"

var demodulationFloor = map[string]float32{
	"SF7BW125":  -7.5,
	"SF8BW125":  -10,
	"SF9BW125":  -12.5,
	"SF10BW125": -15,
	"SF11BW125": -17.5,
	"SF12BW125": -20,
	"SF7BW250":  -4.5,
}

func linkMargin(dataRate string, snr float32) float32 {
	if floor, ok := demodulationFloor[dataRate]; ok {
		return snr - floor
	}
	return 0
}

// ADRConfig contains configuration for Adaptive Data Rate
type ADRConfig struct {
	MinDataRate int
	MaxDataRate int
	MinTXPower  int
	MaxTXPower  int
}

// ErrADRUnavailable is returned when ADR is not available
var ErrADRUnavailable = errors.New("ADR Unavailable")

// ADRSettings gets the ADR settings given a dataRate, txPower, SNR and device margin
func (f *FrequencyPlan) ADRSettings(dataRate string, txPower int, snr float32, deviceMargin float32) (desiredDataRate string, desiredTxPower int, err error) {
	if f.ADR == nil {
		return dataRate, txPower, ErrADRUnavailable
	}
	margin := linkMargin(dataRate, snr) - deviceMargin
	drIdx, err := f.GetDataRateIndexFor(dataRate)
	if err != nil {
		return dataRate, txPower, err
	}
	nStep := int(margin / 3)

	// Increase the data rate with each step
	for nStep > 0 && drIdx < f.ADR.MaxDataRate {
		drIdx++
		nStep--
	}

	// Decrease the Tx power by 3 for each step, until min reached
	for nStep > 0 && txPower > f.ADR.MinTXPower {
		txPower -= 3
		nStep--
	}
	if txPower < f.ADR.MinTXPower {
		txPower = f.ADR.MinTXPower
	}

	// Increase the Tx power by 3 for each step, until max reached
	for nStep < 0 && txPower < f.ADR.MaxTXPower {
		txPower += 3
		nStep++
	}
	if txPower > f.ADR.MaxTXPower {
		txPower = f.ADR.MaxTXPower
	}

	desiredDataRate, err = f.GetDataRateStringForIndex(drIdx)
	if err != nil {
		return dataRate, txPower, err // This should maybe panic; it means that f.ADR is incosistent with f.DataRates
	}
	return desiredDataRate, txPower, nil
}
<|endoftext|>


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

package main

import (
	"../lib/twodee"
	"fmt"
	"github.com/go-gl/mathgl/mgl32"
	"io/ioutil"
	"math"
	"time"
)

const (
	PxPerUnit = 32
)

type GameLayer struct {
	levels          map[string]string
	shake           *twodee.ContinuousAnimation
	cameraBounds    twodee.Rectangle
	camera          *twodee.Camera
	sprite          *twodee.SpriteRenderer
	batch           *twodee.BatchRenderer
	effects         *EffectsRenderer
	app             *Application
	spritesheet     *twodee.Spritesheet
	spritetexture   *twodee.Texture
	level           *Level
	shakeObserverId int
}

func NewGameLayer(winb twodee.Rectangle, app *Application) (layer *GameLayer, err error) {
	var (
		camera       *twodee.Camera
		cameraBounds = twodee.Rect(-8, -5, 8, 5)
	)
	if camera, err = twodee.NewCamera(cameraBounds, winb); err != nil {
		return
	}
	layer = &GameLayer{
		camera:       camera,
		cameraBounds: cameraBounds,
		app:          app,
		levels: map[string]string{
			"main":  "resources/main.tmx",
			"boss1": "resources/boss1.tmx",
			"boss2": "resources/boss2.tmx",
		},
	}
	layer.shakeObserverId = app.GameEventHandler.AddObserver(ShakeCamera, layer.shakeCamera)
	err = layer.Reset()
	return
}

func (l *GameLayer) Reset() (err error) {
	l.Delete()
	if l.batch, err = twodee.NewBatchRenderer(l.camera); err != nil {
		return
	}
	if l.sprite, err = twodee.NewSpriteRenderer(l.camera); err != nil {
		return
	}
	if l.effects, err = NewEffectsRenderer(512, 320, 1.0); err != nil {
		return
	}
	if err = l.loadSpritesheet(); err != nil {
		return
	}
	l.loadLevel("main")
	l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PlayMusic))
	return
}

func (l *GameLayer) loadLevel(name string) (err error) {
	var (
		path string
		ok   bool
	)
	if path, ok = l.levels[name]; !ok {
		return fmt.Errorf("Invalid level: %v", name)
	}
	if l.level, err = NewLevel(path, l.spritesheet, l.app.GameEventHandler); err != nil {
		return
	}
	l.updateCamera(1.0)
	return
}

func (l *GameLayer) Delete() {
	if l.batch != nil {
		l.batch.Delete()
		l.batch = nil
	}
	if l.sprite != nil {
		l.sprite.Delete()
		l.sprite = nil
	}
	if l.spritetexture != nil {
		l.spritetexture.Delete()
		l.spritetexture = nil
	}
	if l.effects != nil {
		l.effects.Delete()
		l.effects = nil
	}
	if l.shakeObserverId != 0 {
		l.app.GameEventHandler.RemoveObserver(ShakeCamera, l.shakeObserverId)
	}
	if l.level != nil {
		l.level.Delete()
		l.level = nil
	}
}

func (l *GameLayer) Render() {
	if l.level != nil {
		l.effects.Bind()
		l.batch.Bind()
		if err := l.batch.Draw(l.level.Background, 0, 0, 0); err != nil {
			panic(err)
		}
		l.batch.Unbind()
		l.spritetexture.Bind()
		if len(l.level.Plates) > 0 {
			l.sprite.Draw(l.level.Plates.SpriteConfigs(l.spritesheet))
		}
		if len(l.level.Props) > 0 {
			l.sprite.Draw(l.level.Props.SpriteConfigs(l.spritesheet))
		}
		l.spritetexture.Unbind()
		l.effects.Unbind()
		l.effects.Draw()
	}
}

func (l *GameLayer) Update(elapsed time.Duration) {
	if !l.checkJoy() {
		l.checkKeys()
	}
	if l.shake != nil {
		l.shake.Update(elapsed)
	}
	l.updateCamera(0.05)
	if l.level != nil {
		l.level.Update(elapsed)
		if collides, level := l.level.PortalCollides(); collides {
			l.loadLevel(level)
		}
	}
	l.effects.Color = l.level.Color
}

func (l *GameLayer) updateCamera(scale float32) {
	var (
		pPt     = l.level.Player.Pos()
		cRect   = l.camera.WorldBounds
		cWidth  = cRect.Max.X - cRect.Min.X
		cHeight = cRect.Max.Y - cRect.Min.Y
		cMidX   = cRect.Min.X + (cWidth / 2.0)
		cMidY   = cRect.Min.Y + (cHeight / 2.0)
		pVec    = mgl32.Vec2{pPt.X, pPt.Y}
		cVec    = mgl32.Vec2{cMidX, cMidY}
		diff    = pVec.Sub(cVec)
		bounds  twodee.Rectangle
		adj     mgl32.Vec2
	)
	if diff.Len() > 1 {
		adj = diff.Mul(scale)
	} else {
		adj = mgl32.Vec2{0, 0}
	}
	if l.shake != nil {
		adj[1] += l.shake.Value()
	}
	bounds = twodee.Rect(
		cRect.Min.X+adj[0],
		cRect.Min.Y+adj[1],
		cRect.Max.X+adj[0],
		cRect.Max.Y+adj[1],
	)
	l.camera.SetWorldBounds(bounds)
}

func (l *GameLayer) shakeCamera(e twodee.GETyper) {
	if l.shake == nil {
		if event, ok := e.(*ShakeEvent); ok {
			decay := twodee.SineDecayFunc(
				time.Duration(event.Millis)*time.Millisecond,
				event.Amplitude,
				event.Frequency,
				event.Decay,
				func() {
					l.shake = nil
				},
			)
			l.shake = twodee.NewContinuousAnimation(decay)
		}
	}
}

func (l *GameLayer) HandleEvent(evt twodee.Event) bool {
	switch event := evt.(type) {
	case *twodee.MouseMoveEvent:
		break
	case *twodee.MouseButtonEvent:
		break
	case *twodee.KeyEvent:
		//l.handleMovement(event)
		if event.Type == twodee.Release {
			break
		}
		switch event.Code {
		case twodee.KeyX:
			l.app.State.Exit = true
		case twodee.KeyZ:
			l.level.Player.Roll()
		case twodee.KeyM:
			if twodee.MusicIsPaused() {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(ResumeMusic))
			} else {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PauseMusic))
			}
		case twodee.Key0:
			l.loadLevel("main")
		case twodee.Key1:
			l.loadLevel("boss1")
		case twodee.Key2:
			l.loadLevel("boss2")
		}

	}
	return true
}

func (l *GameLayer) checkJoy() bool {
	var (
		events = l.app.Context.Events
	)
	if !events.JoystickPresent(twodee.Joystick1) {
		return false
	}
	var (
		axes    []float32 = events.JoystickAxes(twodee.Joystick1)
		buttons []byte    = events.JoystickButtons(twodee.Joystick1)
		x                 = float64(axes[0])
		y                 = float64(-axes[1])
	)
	if math.Abs(x) < 0.2 {
		x = 0.0
	}
	if math.Abs(y) < 0.2 {
		y = 0.0
	}
	l.level.Player.MoveX(float32(x))
	l.level.Player.MoveY(float32(y))
	if len(buttons) > 11 && buttons[11] != 0 { // Very much hardcoded to xbox controller
		l.level.Player.Roll()
	}
	return true
}

func (l *GameLayer) checkKeys() {
	var (
		events         = l.app.Context.Events
		down           = events.GetKey(twodee.KeyDown) == twodee.Press
		up             = events.GetKey(twodee.KeyUp) == twodee.Press
		left           = events.GetKey(twodee.KeyLeft) == twodee.Press
		right          = events.GetKey(twodee.KeyRight) == twodee.Press
		x      float32 = 0.0
		y      float32 = 0.0
	)
	switch {
	case down && !up:
		y = -1.0
	case up && !down:
		y = 1.0
	}
	switch {
	case left && !right:
		x = -1.0
	case right && !left:
		x = 1.0
	}
	l.level.Player.MoveX(x)
	l.level.Player.MoveY(y)
}

func (l *GameLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if l.spritesheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if l.spritetexture, err = twodee.LoadTexture(
		"resources/"+l.spritesheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}
<commit_msg>Add rendering of lines to gamelayer.<commit_after>// Copyright 2015 Pikkpoiss
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package main

import (
	"../lib/twodee"
	"fmt"
	"github.com/go-gl/mathgl/mgl32"
	"io/ioutil"
	"image/color"
	"math"
	"time"
)

const (
	PxPerUnit = 32
)

type GameLayer struct {
	levels          map[string]string
	shake           *twodee.ContinuousAnimation
	cameraBounds    twodee.Rectangle
	camera          *twodee.Camera
	sprite          *twodee.SpriteRenderer
	lines           *twodee.LinesRenderer
	batch           *twodee.BatchRenderer
	effects         *EffectsRenderer
	app             *Application
	spritesheet     *twodee.Spritesheet
	spritetexture   *twodee.Texture
	level           *Level
	shakeObserverId int
	lineSegments    []mgl32.Vec2
}

func NewGameLayer(winb twodee.Rectangle, app *Application) (layer *GameLayer, err error) {
	var (
		camera       *twodee.Camera
		cameraBounds = twodee.Rect(-8, -5, 8, 5)
	)
	if camera, err = twodee.NewCamera(cameraBounds, winb); err != nil {
		return
	}
	layer = &GameLayer{
		camera:       camera,
		cameraBounds: cameraBounds,
		app:          app,
		levels: map[string]string{
			"main":  "resources/main.tmx",
			"boss1": "resources/boss1.tmx",
			"boss2": "resources/boss2.tmx",
		},
		lineSegments: []mgl32.Vec2{mgl32.Vec2{0, 0}},
	}
	layer.shakeObserverId = app.GameEventHandler.AddObserver(ShakeCamera, layer.shakeCamera)
	err = layer.Reset()
	return
}

func (l *GameLayer) Reset() (err error) {
	l.Delete()
	if l.batch, err = twodee.NewBatchRenderer(l.camera); err != nil {
		return
	}
	if l.sprite, err = twodee.NewSpriteRenderer(l.camera); err != nil {
		return
	}
	if l.lines, err = twodee.NewLinesRenderer(l.camera); err != nil {
		return
	}
	if l.effects, err = NewEffectsRenderer(512, 320, 1.0); err != nil {
		return
	}
	if err = l.loadSpritesheet(); err != nil {
		return
	}
	l.loadLevel("main")
	l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PlayMusic))
	return
}

func (l *GameLayer) loadLevel(name string) (err error) {
	var (
		path string
		ok   bool
	)
	if path, ok = l.levels[name]; !ok {
		return fmt.Errorf("Invalid level: %v", name)
	}
	if l.level, err = NewLevel(path, l.spritesheet, l.app.GameEventHandler); err != nil {
		return
	}
	l.updateCamera(1.0)
	return
}

func (l *GameLayer) Delete() {
	if l.batch != nil {
		l.batch.Delete()
		l.batch = nil
	}
	if l.sprite != nil {
		l.sprite.Delete()
		l.sprite = nil
	}
	if l.spritetexture != nil {
		l.spritetexture.Delete()
		l.spritetexture = nil
	}
	if l.lines != nil {
		l.lines.Delete()
		l.lines = nil
	}
	if l.effects != nil {
		l.effects.Delete()
		l.effects = nil
	}
	if l.shakeObserverId != 0 {
		l.app.GameEventHandler.RemoveObserver(ShakeCamera, l.shakeObserverId)
	}
	if l.level != nil {
		l.level.Delete()
		l.level = nil
	}
}

func (l *GameLayer) Render() {
	if l.level != nil {
		l.effects.Bind()
		l.batch.Bind()
		if err := l.batch.Draw(l.level.Background, 0, 0, 0); err != nil {
			panic(err)
		}
		l.batch.Unbind()
		l.spritetexture.Bind()
		if len(l.level.Plates) > 0 {
			l.sprite.Draw(l.level.Plates.SpriteConfigs(l.spritesheet))
		}
		if len(l.level.Props) > 0 {
			l.sprite.Draw(l.level.Props.SpriteConfigs(l.spritesheet))
		}
		l.spritetexture.Unbind()
		l.effects.Unbind()
		l.effects.Draw()

		if len(l.lineSegments) > 1 {
			line := twodee.NewLineGeometry(l.lineSegments, false)
			style := &twodee.LineStyle{
				Thickness: 0.2,
				Color: color.RGBA{255, 0, 0, 128},
				Inner: 0.0,
			}
			modelview := mgl32.Ident4()
			l.lines.Bind()
			l.lines.Draw(line, modelview, style)
			l.lines.Unbind()
		}
	}
}

func (l *GameLayer) Update(elapsed time.Duration) {
	if !l.checkJoy() {
		l.checkKeys()
	}
	if l.shake != nil {
		l.shake.Update(elapsed)
	}
	l.updateCamera(0.05)
	if l.level != nil {
		l.level.Update(elapsed)
		if collides, level := l.level.PortalCollides(); collides {
			l.loadLevel(level)
		}
	}
	l.effects.Color = l.level.Color
}

func (l *GameLayer) updateCamera(scale float32) {
	var (
		pPt     = l.level.Player.Pos()
		cRect   = l.camera.WorldBounds
		cWidth  = cRect.Max.X - cRect.Min.X
		cHeight = cRect.Max.Y - cRect.Min.Y
		cMidX   = cRect.Min.X + (cWidth / 2.0)
		cMidY   = cRect.Min.Y + (cHeight / 2.0)
		pVec    = mgl32.Vec2{pPt.X, pPt.Y}
		cVec    = mgl32.Vec2{cMidX, cMidY}
		diff    = pVec.Sub(cVec)
		bounds  twodee.Rectangle
		adj     mgl32.Vec2
	)
	if diff.Len() > 1 {
		adj = diff.Mul(scale)
	} else {
		adj = mgl32.Vec2{0, 0}
	}
	if l.shake != nil {
		adj[1] += l.shake.Value()
	}
	bounds = twodee.Rect(
		cRect.Min.X+adj[0],
		cRect.Min.Y+adj[1],
		cRect.Max.X+adj[0],
		cRect.Max.Y+adj[1],
	)
	l.camera.SetWorldBounds(bounds)
}

func (l *GameLayer) shakeCamera(e twodee.GETyper) {
	if l.shake == nil {
		if event, ok := e.(*ShakeEvent); ok {
			decay := twodee.SineDecayFunc(
				time.Duration(event.Millis)*time.Millisecond,
				event.Amplitude,
				event.Frequency,
				event.Decay,
				func() {
					l.shake = nil
				},
			)
			l.shake = twodee.NewContinuousAnimation(decay)
		}
	}
}

func (l *GameLayer) HandleEvent(evt twodee.Event) bool {
	switch event := evt.(type) {
	case *twodee.MouseMoveEvent:
		break
	case *twodee.MouseButtonEvent:
		break
	case *twodee.KeyEvent:
		//l.handleMovement(event)
		if event.Type == twodee.Release {
			break
		}
		switch event.Code {
		case twodee.KeyX:
			l.app.State.Exit = true
		case twodee.KeyZ:
			l.level.Player.Roll()
		case twodee.KeyM:
			if twodee.MusicIsPaused() {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(ResumeMusic))
			} else {
				l.app.GameEventHandler.Enqueue(twodee.NewBasicGameEvent(PauseMusic))
			}
		case twodee.Key0:
			l.loadLevel("main")
		case twodee.Key1:
			l.loadLevel("boss1")
		case twodee.Key2:
			l.loadLevel("boss2")
		}

	}
	return true
}

func (l *GameLayer) checkJoy() bool {
	var (
		events = l.app.Context.Events
	)
	if !events.JoystickPresent(twodee.Joystick1) {
		return false
	}
	var (
		axes    []float32 = events.JoystickAxes(twodee.Joystick1)
		buttons []byte    = events.JoystickButtons(twodee.Joystick1)
		x                 = float64(axes[0])
		y                 = float64(-axes[1])
	)
	if math.Abs(x) < 0.2 {
		x = 0.0
	}
	if math.Abs(y) < 0.2 {
		y = 0.0
	}
	l.level.Player.MoveX(float32(x))
	l.level.Player.MoveY(float32(y))
	if len(buttons) > 11 && buttons[11] != 0 { // Very much hardcoded to xbox controller
		l.level.Player.Roll()
	}
	return true
}

func (l *GameLayer) checkKeys() {
	var (
		events         = l.app.Context.Events
		down           = events.GetKey(twodee.KeyDown) == twodee.Press
		up             = events.GetKey(twodee.KeyUp) == twodee.Press
		left           = events.GetKey(twodee.KeyLeft) == twodee.Press
		right          = events.GetKey(twodee.KeyRight) == twodee.Press
		x      float32 = 0.0
		y      float32 = 0.0
	)
	switch {
	case down && !up:
		y = -1.0
	case up && !down:
		y = 1.0
	}
	switch {
	case left && !right:
		x = -1.0
	case right && !left:
		x = 1.0
	}
	l.level.Player.MoveX(x)
	l.level.Player.MoveY(y)
}

func (l *GameLayer) loadSpritesheet() (err error) {
	var (
		data []byte
	)
	if data, err = ioutil.ReadFile("resources/spritesheet.json"); err != nil {
		return
	}
	if l.spritesheet, err = twodee.ParseTexturePackerJSONArrayString(
		string(data),
		PxPerUnit,
	); err != nil {
		return
	}
	if l.spritetexture, err = twodee.LoadTexture(
		"resources/"+l.spritesheet.TexturePath,
		twodee.Nearest,
	); err != nil {
		return
	}
	return
}
<|endoftext|>