<commit_before>// Package logging implements file loggers that support log rotation.
package logging

import (
	"fmt"
	"log"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
)

// The type for logging level.
type Severity int

// The valid logging severities.
const (
	Unclassified Severity = iota
	Debug
	Info
	Warn
	Error
	Fatal
)

// NewLogger instantiates and initializes a logger for the given facility.
func NewLogger(logFacility string) *FileLogger {

	if _, ok := loggers[logFacility]; !ok {
		loggers[logFacility] = new(FileLogger)
		loggers[logFacility].init(logFacility)
	}

	return loggers[logFacility]
}

// Reopen non-destructively re-opens all log files, to support log rotation.
func Reopen() {

	for _, l := range loggers {
		l.reopen()
	}
}

// The loggers, one per facility.
var loggers map[string]*FileLogger = map[string]*FileLogger{}

// The text representations of the logging severities.
var severityText = map[Severity]string{
	Unclassified: "",
	Debug:        "DEBUG",
	Info:         "INFO",
	Warn:         "WARN",
	Error:        "ERROR",
	Fatal:        "FATAL",
}

// SeverityToText maps the severity value to a name for printing.
func SeverityToText(sev Severity) string {
	s, ok := severityText[sev]
	if !ok {
		s = "UNKNOWN"
	}
	return s
}

// TextToSeverity derives a Severity from a text string, ignoring case.
func TextToSeverity(s string) (Severity, error) {
	var sev Severity = Debug
	var err error
	switch strings.ToLower(s) {
	case "":
		sev = Unclassified
	case "unclassified":
		sev = Unclassified
	case "debug":
		sev = Debug
	case "info":
		sev = Info
	case "warn":
		sev = Warn
	case "error":
		sev = Error
	case "fatal":
		sev = Fatal
	default:
		err = fmt.Errorf(`"%s" is not a valid logging level`, s)
	}
	return sev, err
}

// FatalError is a custom error that can be distinguished in the global panic handler.
type FatalError struct {
	file     string
	line     int
	function string
	msg      string
}

// Error function makes the FatalError object conform to the error interface.
func (e *FatalError) Error() string {
	return fmt.Sprintf("FATAL %s:%d %s() %s", e.file, e.line, e.function, e.msg)
}

// The FileLogger object.
type FileLogger struct {
	m           sync.RWMutex // Serialize access to logger during log rotation
	logFacility string       // The name given to this particular logger
	logFilename string       // The name of the log file
	logLevel    Severity     // The severity threshold for generating output.
	opened      bool         // Whether the logger has been opened or not
	skipEmit    bool         // flag to permit panicing without incurring deadlock
	logFile     *os.File     // The file handle of the opened file
	logger      *log.Logger  // The logger that writes to the file
}

func (l *FileLogger) init(logFacility string) {
	l.logFacility = logFacility
}

// SetLogLevel updates the logging level threshold with imediate effect.
func (l *FileLogger) SetLogLevel(logLevel Severity) {
	l.logLevel = logLevel
	l.LogInfo("Log level set to %s", SeverityToText(logLevel))
}

// GetLogLevel returns the current logging level threshold.
func (l *FileLogger) GetLogLevel() Severity {
	return l.logLevel
}

// Open allocates resources for the logger.
func (l *FileLogger) Open(logFilename string, logLevel Severity) {

	if l.opened {
		return
	}

	l.logFilename = logFilename
	l.logLevel = logLevel

	// Open the logfile.
	l.closeAndOrOpen(1)
	l.opened = true
}

// Close releases all resources associated with the logger.
func (l *FileLogger) Close() {

	if !l.opened {
		return
	}

	// Close the logfile.
	l.closeAndOrOpen(3)
	l.opened = false
}

// LogDebug writes a time-stamped Debug message to the log file, with a mutex guard.
func (l *FileLogger) LogDebug(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Debug, format, a...)
	}
}

// LogInfo writes a time-stamped Info message to the log file, with a mutex guard.
func (l *FileLogger) LogInfo(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Info, format, a...)
	}
}

// LogWarn writes a time-stamped Warn message to the log file, with a mutex guard.
func (l *FileLogger) LogWarn(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Warn, format, a...)
	}
}

// LogError writes a time-stamped Error message to the log file, with a mutex guard.
func (l *FileLogger) LogError(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Error, format, a...)
	}
}

// LogFatal writes a time-stamped Fatal message to the log file, with a mutex guard.
func (l *FileLogger) LogFatal(format string, a ...interface{}) {

	if l.opened && !l.skipEmit {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Fatal, format, a...)
	}
	pc, file, line, _ := runtime.Caller(1)
	f := strings.Split(runtime.FuncForPC(pc).Name(), ".")
	panic(FatalError{path.Base(file), line, f[len(f)-1], fmt.Sprintf(format, a...)})
}

// reopen closes and re-opens the log file to support log rotation.
func (l *FileLogger) reopen() {

	if !l.opened {
		return
	}

	// Close and re-open the logfile.
	l.closeAndOrOpen(2)
}

// openLogfile opens the configured log file.
func (l *FileLogger) openLogfile() *os.File {

	fp, err := os.OpenFile(l.logFilename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		if l.logger != nil {
			// The log-rotation case.
			// Note that when rotating, the pre-rotation log is still open here.
			// DO NOT call the public Log() method, you will cause a deadlock.
			l.emit(Fatal, "Unable to reopen logfile '%v'. Error: '%s'", l.logFilename, err.Error())
			l.logFile.Close()
		}
		// Inability to log is a fatal error. We do not run blind.
		l.skipEmit = true // Message already emitted, just panic
		l.LogFatal("Unable to reopen logfile '%v'. Error: '%s'", l.logFilename, err.Error())
	}

	return fp
}

// rotate closes any open log files and (re-)opens them with the same name.
func (l *FileLogger) closeAndOrOpen(action int) {

	// Disable the log writer.
	l.m.Lock()
	defer l.m.Unlock()

	// Use stderr and skip messages if no log filename was specified.
	if l.logFilename == "" {
		l.logFile = os.Stderr
		l.logger = log.New(l.logFile, "", log.Ldate|log.Lmicroseconds)
		return
	}

	// DO NOT call the public Log() method, you will cause a deadlock.
	switch action {
	case 1:
		// Initial open of the log file.
		l.logFile = l.openLogfile()
		l.logger = log.New(l.logFile, "", log.Ldate|log.Lmicroseconds)
		l.emit(Info, "Log opened")
	case 2:
		// Close log file, and re-open with the same name.
		l.emit(Info, "Log closed on signal")
		l.logFile.Close()
		l.logFile = l.openLogfile()
		l.logger = log.New(l.logFile, "", log.Ldate|log.Lmicroseconds)
		l.emit(Info, "Log reopened on signal")
	case 3:
		// Close the log file.
		l.emit(Info, "Log closed")
		l.logFile.Close()
	}
}

// emit produces a log line, if the severity meets or exceeds the threshold.
func (l *FileLogger) emit(sev Severity, format string, a ...interface{}) {

	// Filter out messages that do not meet the severity threshold.
	if sev < l.logLevel {
		return
	}

	if l.logLevel == Unclassified {
		l.logger.Printf("["+l.logFacility+"] "+format, a...)
	} else {
		l.logger.Printf("["+l.logFacility+"] ["+SeverityToText(sev)+"] "+format, a...)
	}
}
<commit_msg>Eliminate logging race in SetLogLevel()<commit_after>// Package logging implements file loggers that support log rotation.
package logging

import (
	"fmt"
	"log"
	"os"
	"path"
	"runtime"
	"strings"
	"sync"
)

// The type for logging level.
type Severity int

// The valid logging severities.
const (
	Unclassified Severity = iota
	Debug
	Info
	Warn
	Error
	Fatal
)

// NewLogger instantiates and initializes a logger for the given facility.
func NewLogger(logFacility string) *FileLogger {

	if _, ok := loggers[logFacility]; !ok {
		loggers[logFacility] = new(FileLogger)
		loggers[logFacility].init(logFacility)
	}

	return loggers[logFacility]
}

// Reopen non-destructively re-opens all log files, to support log rotation.
func Reopen() {

	for _, l := range loggers {
		l.reopen()
	}
}

// The loggers, one per facility.
var loggers map[string]*FileLogger = map[string]*FileLogger{}

// The text representations of the logging severities.
var severityText = map[Severity]string{
	Unclassified: "",
	Debug:        "DEBUG",
	Info:         "INFO",
	Warn:         "WARN",
	Error:        "ERROR",
	Fatal:        "FATAL",
}

// SeverityToText maps the severity value to a name for printing.
func SeverityToText(sev Severity) string {
	s, ok := severityText[sev]
	if !ok {
		s = "UNKNOWN"
	}
	return s
}

// TextToSeverity derives a Severity from a text string, ignoring case.
func TextToSeverity(s string) (Severity, error) {
	var sev Severity = Debug
	var err error
	switch strings.ToLower(s) {
	case "":
		sev = Unclassified
	case "unclassified":
		sev = Unclassified
	case "debug":
		sev = Debug
	case "info":
		sev = Info
	case "warn":
		sev = Warn
	case "error":
		sev = Error
	case "fatal":
		sev = Fatal
	default:
		err = fmt.Errorf(`"%s" is not a valid logging level`, s)
	}
	return sev, err
}

// FatalError is a custom error that can be distinguished in the global panic handler.
type FatalError struct {
	file     string
	line     int
	function string
	msg      string
}

// Error function makes the FatalError object conform to the error interface.
func (e *FatalError) Error() string {
	return fmt.Sprintf("FATAL %s:%d %s() %s", e.file, e.line, e.function, e.msg)
}

// The FileLogger object.
type FileLogger struct {
	m           sync.RWMutex // Serialize access to logger during log rotation
	logFacility string       // The name given to this particular logger
	logFilename string       // The name of the log file
	logLevel    Severity     // The severity threshold for generating output.
	opened      bool         // Whether the logger has been opened or not
	skipEmit    bool         // flag to permit panicing without incurring deadlock
	logFile     *os.File     // The file handle of the opened file
	logger      *log.Logger  // The logger that writes to the file
}

func (l *FileLogger) init(logFacility string) {
	l.logFacility = logFacility
}

// SetLogLevel updates the logging level threshold with imediate effect.
func (l *FileLogger) SetLogLevel(logLevel Severity) {
	l.m.Lock()
	defer l.m.Unlock()
	l.logLevel = logLevel
	l.emit(Info, "Log level set to %s", SeverityToText(logLevel))
}

// GetLogLevel returns the current logging level threshold.
func (l *FileLogger) GetLogLevel() Severity {
	return l.logLevel
}

// Open allocates resources for the logger.
func (l *FileLogger) Open(logFilename string, logLevel Severity) {

	if l.opened {
		return
	}

	l.logFilename = logFilename
	l.logLevel = logLevel

	// Open the logfile.
	l.closeAndOrOpen(1)
	l.opened = true
}

// Close releases all resources associated with the logger.
func (l *FileLogger) Close() {

	if !l.opened {
		return
	}

	// Close the logfile.
	l.closeAndOrOpen(3)
	l.opened = false
}

// LogDebug writes a time-stamped Debug message to the log file, with a mutex guard.
func (l *FileLogger) LogDebug(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Debug, format, a...)
	}
}

// LogInfo writes a time-stamped Info message to the log file, with a mutex guard.
func (l *FileLogger) LogInfo(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Info, format, a...)
	}
}

// LogWarn writes a time-stamped Warn message to the log file, with a mutex guard.
func (l *FileLogger) LogWarn(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Warn, format, a...)
	}
}

// LogError writes a time-stamped Error message to the log file, with a mutex guard.
func (l *FileLogger) LogError(format string, a ...interface{}) {

	if l.opened {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Error, format, a...)
	}
}

// LogFatal writes a time-stamped Fatal message to the log file, with a mutex guard.
func (l *FileLogger) LogFatal(format string, a ...interface{}) {

	if l.opened && !l.skipEmit {
		l.m.RLock()
		defer l.m.RUnlock()
		l.emit(Fatal, format, a...)
	}
	pc, file, line, _ := runtime.Caller(1)
	f := strings.Split(runtime.FuncForPC(pc).Name(), ".")
	panic(FatalError{path.Base(file), line, f[len(f)-1], fmt.Sprintf(format, a...)})
}

// reopen closes and re-opens the log file to support log rotation.
func (l *FileLogger) reopen() {

	if !l.opened {
		return
	}

	// Close and re-open the logfile.
	l.closeAndOrOpen(2)
}

// openLogfile opens the configured log file.
func (l *FileLogger) openLogfile() *os.File {

	fp, err := os.OpenFile(l.logFilename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
	if err != nil {
		if l.logger != nil {
			// The log-rotation case.
			// Note that when rotating, the pre-rotation log is still open here.
			// DO NOT call the public Log() method, you will cause a deadlock.
			l.emit(Fatal, "Unable to reopen logfile '%v'. Error: '%s'", l.logFilename, err.Error())
			l.logFile.Close()
		}
		// Inability to log is a fatal error. We do not run blind.
		l.skipEmit = true // Message already emitted, just panic
		l.LogFatal("Unable to reopen logfile '%v'. Error: '%s'", l.logFilename, err.Error())
	}

	return fp
}

// rotate closes any open log files and (re-)opens them with the same name.
func (l *FileLogger) closeAndOrOpen(action int) {

	// Disable the log writer.
	l.m.Lock()
	defer l.m.Unlock()

	// Use stderr and skip messages if no log filename was specified.
	if l.logFilename == "" {
		l.logFile = os.Stderr
		l.logger = log.New(l.logFile, "", log.Ldate|log.Lmicroseconds)
		return
	}

	// DO NOT call the public Log() method, you will cause a deadlock.
	switch action {
	case 1:
		// Initial open of the log file.
		l.logFile = l.openLogfile()
		l.logger = log.New(l.logFile, "", log.Ldate|log.Lmicroseconds)
		l.emit(Info, "Log opened")
	case 2:
		// Close log file, and re-open with the same name.
		l.emit(Info, "Log closed on signal")
		l.logFile.Close()
		l.logFile = l.openLogfile()
		l.logger = log.New(l.logFile, "", log.Ldate|log.Lmicroseconds)
		l.emit(Info, "Log reopened on signal")
	case 3:
		// Close the log file.
		l.emit(Info, "Log closed")
		l.logFile.Close()
	}
}

// emit produces a log line, if the severity meets or exceeds the threshold.
func (l *FileLogger) emit(sev Severity, format string, a ...interface{}) {

	// Filter out messages that do not meet the severity threshold.
	if sev < l.logLevel {
		return
	}

	if l.logLevel == Unclassified {
		l.logger.Printf("["+l.logFacility+"] "+format, a...)
	} else {
		l.logger.Printf("["+l.logFacility+"] ["+SeverityToText(sev)+"] "+format, a...)
	}
}
<|endoftext|>


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

import (
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	kubeApi "k8s.io/kubernetes/pkg/api"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

const (
	urlCommandFlag       = "url"
	namespaceCommandFlag = "namespace"
	exposeURLAnnotation  = "fabric8.io/exposeUrl"
)

// NewCmdService looks up the external service address and opens the URL
// Credits: https://github.com/kubernetes/minikube/blob/v0.9.0/cmd/minikube/cmd/service.go
func NewCmdService(f *cmdutil.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "service",
		Short: "Opens the specified Kubernetes service in your browser",
		Long:  `Opens the specified Kubernetes service in your browser`,

		Run: func(cmd *cobra.Command, args []string) {
			c, _ := client.NewClient(f)

			ns := cmd.Flags().Lookup(namespaceCommandFlag).Value.String()
			if ns == "" {
				ns, _, _ = f.DefaultNamespace()
			}
			printURL := cmd.Flags().Lookup(urlCommandFlag).Value.String() == "true"
			openService(ns, args[0], c, printURL)
		},
	}
	cmd.PersistentFlags().StringP(namespaceCommandFlag, "n", "default", "The service namespace")
	cmd.PersistentFlags().BoolP(urlCommandFlag, "u", false, "Display the kubernetes service exposed URL in the CLI instead of opening it in the default browser")
	return cmd
}

func openService(ns string, serviceName string, c *k8sclient.Client, printURL bool) {
	if err := RetryAfter(40, func() error { return CheckService(ns, serviceName, c) }, 10*time.Second); err != nil {
		util.Errorf("Could not find finalized endpoint being pointed to by %s: %v", serviceName, err)
		os.Exit(1)
	}

	svcs, err := c.Services(ns).List(kubeApi.ListOptions{})
	if err != nil {
		util.Errorf("No services found %v\n", err)
	}
	found := false
	for _, service := range svcs.Items {
		if serviceName == service.Name {

			url := service.ObjectMeta.Annotations[exposeURLAnnotation]

			if printURL {
				util.Successf("%s\n", url)
			} else {
				util.Successf("Opening URL %s\n", url)
				browser.OpenURL(url)
			}
			found = true
			break
		}
	}
	if !found {
		util.Errorf("No service %s in namespace %s\n", serviceName, ns)
	}
}

// CheckService waits for the specified service to be ready by returning an error until the service is up
// The check is done by polling the endpoint associated with the service and when the endpoint exists, returning no error->service-online
// Credits: https://github.com/kubernetes/minikube/blob/v0.9.0/cmd/minikube/cmd/service.go#L89
func CheckService(ns string, service string, c *k8sclient.Client) error {
	svc, err := c.Services(ns).Get(service)
	if err != nil {
		return err
	}
	url := svc.ObjectMeta.Annotations[exposeURLAnnotation]
	if url == "" {
		util.Infof("Waiting, external URL for %s service is not ready yet... \n", service)
		return errors.New("")
	}
	endpoints := c.Endpoints(ns)
	if endpoints == nil {
		util.Errorf("No endpoints found in namespace %s\n", ns)
	}
	endpoint, err := endpoints.Get(service)
	if err != nil {
		util.Errorf("No endpoints found for service %s\n", service)
		return err
	}
	return CheckEndpointReady(endpoint)
}

//CheckEndpointReady checks that the kubernetes endpoint is ready
// Credits: https://github.com/kubernetes/minikube/blob/v0.9.0/cmd/minikube/cmd/service.go#L101
func CheckEndpointReady(endpoint *kubeApi.Endpoints) error {
	if len(endpoint.Subsets) == 0 {
		fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n")
		return fmt.Errorf("Endpoint for service is not ready yet\n")
	}
	for _, subset := range endpoint.Subsets {
		if len(subset.NotReadyAddresses) != 0 {
			fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n")
			return fmt.Errorf("Endpoint for service is not ready yet\n")
		}
	}
	return nil
}

func Retry(attempts int, callback func() error) (err error) {
	return RetryAfter(attempts, callback, 0)
}

func RetryAfter(attempts int, callback func() error, d time.Duration) (err error) {
	m := MultiError{}
	for i := 0; i < attempts; i++ {
		err = callback()
		if err == nil {
			return nil
		}
		m.Collect(err)
		time.Sleep(d)
	}
	return m.ToError()
}

type MultiError struct {
	Errors []error
}

func (m *MultiError) Collect(err error) {
	if err != nil {
		m.Errors = append(m.Errors, err)
	}
}

func (m MultiError) ToError() error {
	if len(m.Errors) == 0 {
		return nil
	}

	errStrings := []string{}
	for _, err := range m.Errors {
		errStrings = append(errStrings, err.Error())
	}
	return fmt.Errorf(strings.Join(errStrings, "\n"))
}
<commit_msg>clean up summary outpout<commit_after>/**
 * Copyright (C) 2015 Red Hat, Inc.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package cmds

import (
	"errors"
	"fmt"
	"os"
	"strings"
	"time"

	"github.com/fabric8io/gofabric8/client"
	"github.com/fabric8io/gofabric8/util"
	"github.com/pkg/browser"
	"github.com/spf13/cobra"
	kubeApi "k8s.io/kubernetes/pkg/api"
	k8sclient "k8s.io/kubernetes/pkg/client/unversioned"
	cmdutil "k8s.io/kubernetes/pkg/kubectl/cmd/util"
)

const (
	urlCommandFlag       = "url"
	namespaceCommandFlag = "namespace"
	exposeURLAnnotation  = "fabric8.io/exposeUrl"
)

// NewCmdService looks up the external service address and opens the URL
// Credits: https://github.com/kubernetes/minikube/blob/v0.9.0/cmd/minikube/cmd/service.go
func NewCmdService(f *cmdutil.Factory) *cobra.Command {
	cmd := &cobra.Command{
		Use:   "service",
		Short: "Opens the specified Kubernetes service in your browser",
		Long:  `Opens the specified Kubernetes service in your browser`,

		Run: func(cmd *cobra.Command, args []string) {
			c, _ := client.NewClient(f)

			ns := cmd.Flags().Lookup(namespaceCommandFlag).Value.String()
			if ns == "" {
				ns, _, _ = f.DefaultNamespace()
			}
			printURL := cmd.Flags().Lookup(urlCommandFlag).Value.String() == "true"
			openService(ns, args[0], c, printURL)
		},
	}
	cmd.PersistentFlags().StringP(namespaceCommandFlag, "n", "default", "The service namespace")
	cmd.PersistentFlags().BoolP(urlCommandFlag, "u", false, "Display the kubernetes service exposed URL in the CLI instead of opening it in the default browser")
	return cmd
}

func openService(ns string, serviceName string, c *k8sclient.Client, printURL bool) {
	if err := RetryAfter(40, func() error { return CheckService(ns, serviceName, c) }, 10*time.Second); err != nil {
		util.Errorf("Could not find finalized endpoint being pointed to by %s: %v", serviceName, err)
		os.Exit(1)
	}

	svcs, err := c.Services(ns).List(kubeApi.ListOptions{})
	if err != nil {
		util.Errorf("No services found %v\n", err)
	}
	found := false
	for _, service := range svcs.Items {
		if serviceName == service.Name {

			url := service.ObjectMeta.Annotations[exposeURLAnnotation]

			if printURL {
				util.Successf("%s\n", url)
			} else {
				util.Successf("Opening URL %s\n", url)
				browser.OpenURL(url)
			}
			found = true
			break
		}
	}
	if !found {
		util.Errorf("No service %s in namespace %s\n", serviceName, ns)
	}
}

// CheckService waits for the specified service to be ready by returning an error until the service is up
// The check is done by polling the endpoint associated with the service and when the endpoint exists, returning no error->service-online
// Credits: https://github.com/kubernetes/minikube/blob/v0.9.0/cmd/minikube/cmd/service.go#L89
func CheckService(ns string, service string, c *k8sclient.Client) error {
	svc, err := c.Services(ns).Get(service)
	if err != nil {
		return err
	}
	url := svc.ObjectMeta.Annotations[exposeURLAnnotation]
	if url == "" {
		util.Info(".")
		return errors.New("")
	}
	endpoints := c.Endpoints(ns)
	if endpoints == nil {
		util.Errorf("No endpoints found in namespace %s\n", ns)
	}
	endpoint, err := endpoints.Get(service)
	if err != nil {
		util.Errorf("No endpoints found for service %s\n", service)
		return err
	}
	return CheckEndpointReady(endpoint)
}

//CheckEndpointReady checks that the kubernetes endpoint is ready
// Credits: https://github.com/kubernetes/minikube/blob/v0.9.0/cmd/minikube/cmd/service.go#L101
func CheckEndpointReady(endpoint *kubeApi.Endpoints) error {
	if len(endpoint.Subsets) == 0 {
		fmt.Fprintf(os.Stderr, ".")
		return fmt.Errorf("Endpoint for service is not ready yet\n")
	}
	for _, subset := range endpoint.Subsets {
		if len(subset.NotReadyAddresses) != 0 {
			fmt.Fprintf(os.Stderr, "Waiting, endpoint for service is not ready yet...\n")
			return fmt.Errorf("Endpoint for service is not ready yet\n")
		}
	}
	return nil
}

func Retry(attempts int, callback func() error) (err error) {
	return RetryAfter(attempts, callback, 0)
}

func RetryAfter(attempts int, callback func() error, d time.Duration) (err error) {
	m := MultiError{}
	for i := 0; i < attempts; i++ {
		err = callback()
		if err == nil {
			return nil
		}
		m.Collect(err)
		time.Sleep(d)
	}
	return m.ToError()
}

type MultiError struct {
	Errors []error
}

func (m *MultiError) Collect(err error) {
	if err != nil {
		m.Errors = append(m.Errors, err)
	}
}

func (m MultiError) ToError() error {
	if len(m.Errors) == 0 {
		return nil
	}

	errStrings := []string{}
	for _, err := range m.Errors {
		errStrings = append(errStrings, err.Error())
	}
	return fmt.Errorf(strings.Join(errStrings, "\n"))
}
<|endoftext|>


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

// A command that consumes the output of a `comeback verify` run (--fast mode
// is okay), assuming that the roots were all backup jobs of interest. Any
// score that is in the bucket but not represented in the verify output is
// cloned to a garbage/ prefix in the bucket, and deleted from the blobs/
// prefix.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/verify"
	"github.com/jacobsa/comeback/wiring"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

var cmdGC = &Command{
	Name: "gc",
}

var fInput = cmdGC.Flags.String(
	"input",
	"",
	"Path to a file containing the output of a verify run.")

func init() {
	cmdGC.Run = runGC // Break flag-related dependency loop.
}

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

// Parse the supplied input line, returning a list of all scores mentioned.
func parseInputLine(
	line []byte) (scores []blob.Score, err error) {
	// We expect space-separate components.
	components := bytes.Split(line, []byte{' '})
	if len(components) < 2 {
		err = fmt.Errorf(
			"Expected at least two components, got %d.",
			len(components))

		return
	}

	// The first should be the timestmap.
	_, err = time.Parse(time.RFC3339, string(components[0]))
	if err != nil {
		err = fmt.Errorf("time.Parse(%q): %v", components[0], err)
		return
	}

	// The rest are node names understood by package verify.
	for i := 1; i < len(components); i++ {
		node := string(components[i])

		var score blob.Score
		_, score, err = verify.ParseNodeName(node)
		if err != nil {
			err = fmt.Errorf("ParseNodeName(%q): %v", node, err)
			return
		}

		scores = append(scores, score)
	}

	return
}

// Parse the verify output, returning a list of all scores encountered.
func parseInput(
	r io.Reader) (scores []blob.Score, err error) {
	reader := bufio.NewReader(r)

	for {
		// Find the next line. EOF with no data means we are done; otherwise ignore
		// EOF in case the file doesn't end with a newline.
		var line []byte
		line, err = reader.ReadBytes('\n')
		if err == io.EOF {
			if len(line) == 0 {
				break
			}

			err = nil
		}

		// Propagate other errors.
		if err != nil {
			err = fmt.Errorf("ReadBytes: %v", err)
			return
		}

		// Parse the line.
		var lineScores []blob.Score
		lineScores, err = parseInputLine(line)
		if err != nil {
			err = fmt.Errorf("parseInputLine(%q): %v", line, err)
			return
		}

		scores = append(scores, lineScores...)
	}

	return
}

// Filter out scores that are in the list of non-garbage accessible scores,
// passing on only garbage.
func filterToGarbage(
	ctx context.Context,
	accessible []blob.Score,
	allScores <-chan blob.Score,
	garbageScores chan<- blob.Score) (err error) {
	// Create a map indexing the accessible scores.
	accessibleMap := make(map[blob.Score]struct{})
	for _, score := range accessible {
		accessibleMap[score] = struct{}{}
	}

	// Process each score.
	for score := range allScores {
		// Is this score accessible?
		if _, ok := accessibleMap[score]; ok {
			continue
		}

		// Send it down the garbage chute.
		select {
		case <-ctx.Done():
			err = ctx.Err()
			return

		case garbageScores <- score:
		}
	}

	return
}

// Clone garbage blobs into a new location. Pass on the names of the source
// objects that were cloned.
func cloneGarbage(
	ctx context.Context,
	bucket gcs.Bucket,
	garbageScores <-chan blob.Score,
	garbageObjects chan<- string) (err error) {
	b := syncutil.NewBundle(ctx)

	const parallelism = 128
	for i := 0; i < parallelism; i++ {
		b.Add(func(ctx context.Context) (err error) {
			// Process each score.
			for score := range garbageScores {
				srcName := wiring.BlobObjectNamePrefix + score.Hex()

				// Clone the object.
				req := &gcs.CopyObjectRequest{
					SrcName: srcName,
					DstName: fmt.Sprintf("garbage/%s", score.Hex()),
				}

				_, err = bucket.CopyObject(ctx, req)
				if err != nil {
					err = fmt.Errorf("CopyObject: %v", err)
					return
				}

				// Write out the name of the object to be deleted.
				select {
				case <-ctx.Done():
					err = ctx.Err()

				case garbageObjects <- srcName:
				}
			}

			return
		})
	}

	err = b.Join()
	return
}

// Delete all objects whose name come in on the supplied channel.
func deleteObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	names <-chan string) (err error) {
	b := syncutil.NewBundle(ctx)

	const parallelism = 128
	for i := 0; i < parallelism; i++ {
		b.Add(func(ctx context.Context) (err error) {
			for name := range names {
				err = bucket.DeleteObject(ctx, name)
				if err != nil {
					err = fmt.Errorf("DeleteObject(%q): %v", name, err)
					return
				}
			}

			return
		})
	}

	err = b.Join()
	return
}

////////////////////////////////////////////////////////////////////////
// GC
////////////////////////////////////////////////////////////////////////

func runGC(args []string) {
	// Allow parallelism.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Die on error.
	var err error
	defer func() {
		if err != nil {
			log.Fatalln(err)
		}
	}()

	// Grab dependencies.
	bucket := getBucket()

	// Open the input file.
	if *fInput == "" {
		err = fmt.Errorf("You must set --input.")
		return
	}

	inputFile, err := os.Open(*fInput)
	if err != nil {
		err = fmt.Errorf("Open: %v", err)
		return
	}

	// Parse it.
	accessibleScores, err := parseInput(inputFile)
	inputFile.Close()

	if err != nil {
		err = fmt.Errorf("parseInput: %v", err)
		return
	}

	b := syncutil.NewBundle(context.Background())

	// List all extant scores into a channel.
	allScores := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(allScores)
		err = blob.ListScores(ctx, bucket, wiring.BlobObjectNamePrefix, allScores)
		if err != nil {
			err = fmt.Errorf("ListScores: %v", err)
			return
		}

		return
	})

	// Count the total number of scores, periodically printing status updates.
	var allScoresCount uint64
	var garbageScoresCount uint64

	allScoresAfterCounting := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(allScoresAfterCounting)
		ticker := time.Tick(2 * time.Second)

		for score := range allScores {
			allScoresCount++

			// Print a status update?
			select {
			case <-ticker:
				g := atomic.LoadUint64(&garbageScoresCount)
				log.Printf("%d scores seen; %d garbage so far.", allScoresCount, g)

			default:
			}

			// Pass on the score.
			select {
			case <-ctx.Done():
				err = ctx.Err()
				return

			case allScoresAfterCounting <- score:
			}
		}

		return
	})

	// Filter to garbage scores.
	garbageScores := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(garbageScores)
		err = filterToGarbage(
			ctx,
			accessibleScores,
			allScoresAfterCounting,
			garbageScores)

		if err != nil {
			err = fmt.Errorf("filterToGarbage: %v", err)
			return
		}

		return
	})

	// Count the number of garbage scores.
	garbageScoresAfterCounting := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(garbageScoresAfterCounting)
		for score := range garbageScores {
			atomic.AddUint64(&garbageScoresCount, 1)

			select {
			case <-ctx.Done():
				err = ctx.Err()
				return

			case garbageScoresAfterCounting <- score:
			}
		}

		return
	})

	// Clone garbage blobs into a backup location.
	toDelete := make(chan string, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(toDelete)
		err = cloneGarbage(
			ctx,
			bucket,
			garbageScoresAfterCounting,
			toDelete)
		if err != nil {
			err = fmt.Errorf("cloneGarbage: %v", err)
			return
		}

		return
	})

	// Delete the original objects.
	b.Add(func(ctx context.Context) (err error) {
		err = deleteObjects(ctx, bucket, toDelete)
		if err != nil {
			err = fmt.Errorf("deleteObjects: %v", err)
			return
		}

		return
	})

	err = b.Join()
	if err != nil {
		return
	}

	// Print a summary.
	log.Printf(
		"Deleted %d objects out of %d total.",
		garbageScoresCount,
		allScoresCount)
}
<commit_msg>Fixed two parsing bugs.<commit_after>// Copyright 2015 Aaron Jacobs. All Rights Reserved.
// Author: aaronjjacobs@gmail.com (Aaron Jacobs)
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// A command that consumes the output of a `comeback verify` run (--fast mode
// is okay), assuming that the roots were all backup jobs of interest. Any
// score that is in the bucket but not represented in the verify output is
// cloned to a garbage/ prefix in the bucket, and deleted from the blobs/
// prefix.

package main

import (
	"bufio"
	"bytes"
	"fmt"
	"io"
	"log"
	"os"
	"runtime"
	"sync/atomic"
	"time"

	"github.com/jacobsa/comeback/blob"
	"github.com/jacobsa/comeback/verify"
	"github.com/jacobsa/comeback/wiring"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/syncutil"
	"golang.org/x/net/context"
)

var cmdGC = &Command{
	Name: "gc",
}

var fInput = cmdGC.Flags.String(
	"input",
	"",
	"Path to a file containing the output of a verify run.")

func init() {
	cmdGC.Run = runGC // Break flag-related dependency loop.
}

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

// Parse the supplied input line, returning a list of all scores mentioned.
func parseInputLine(
	line []byte) (scores []blob.Score, err error) {
	// We expect space-separate components.
	components := bytes.Split(line, []byte{' '})
	if len(components) < 2 {
		err = fmt.Errorf(
			"Expected at least two components, got %d.",
			len(components))

		return
	}

	// The first should be the timestmap.
	_, err = time.Parse(time.RFC3339, string(components[0]))
	if err != nil {
		err = fmt.Errorf("time.Parse(%q): %v", components[0], err)
		return
	}

	// The rest are node names understood by package verify.
	for i := 1; i < len(components); i++ {
		node := string(components[i])

		var score blob.Score
		_, score, err = verify.ParseNodeName(node)
		if err != nil {
			err = fmt.Errorf("ParseNodeName(%q): %v", node, err)
			return
		}

		scores = append(scores, score)
	}

	return
}

// Parse the verify output, returning a list of all scores encountered.
func parseInput(
	r io.Reader) (scores []blob.Score, err error) {
	reader := bufio.NewReader(r)

	for {
		// Find the next line. EOF with no data means we are done; otherwise ignore
		// EOF in case the file doesn't end with a newline.
		var line []byte
		line, err = reader.ReadBytes('\n')
		if err == io.EOF {
			err = nil
			if len(line) == 0 {
				break
			}
		}

		// Propagate other errors.
		if err != nil {
			err = fmt.Errorf("ReadBytes: %v", err)
			return
		}

		// Trim the delimiter.
		line = line[:len(line)-1]

		// Parse the line.
		var lineScores []blob.Score
		lineScores, err = parseInputLine(line)
		if err != nil {
			err = fmt.Errorf("parseInputLine(%q): %v", line, err)
			return
		}

		scores = append(scores, lineScores...)
	}

	return
}

// Filter out scores that are in the list of non-garbage accessible scores,
// passing on only garbage.
func filterToGarbage(
	ctx context.Context,
	accessible []blob.Score,
	allScores <-chan blob.Score,
	garbageScores chan<- blob.Score) (err error) {
	// Create a map indexing the accessible scores.
	accessibleMap := make(map[blob.Score]struct{})
	for _, score := range accessible {
		accessibleMap[score] = struct{}{}
	}

	// Process each score.
	for score := range allScores {
		// Is this score accessible?
		if _, ok := accessibleMap[score]; ok {
			continue
		}

		// Send it down the garbage chute.
		select {
		case <-ctx.Done():
			err = ctx.Err()
			return

		case garbageScores <- score:
		}
	}

	return
}

// Clone garbage blobs into a new location. Pass on the names of the source
// objects that were cloned.
func cloneGarbage(
	ctx context.Context,
	bucket gcs.Bucket,
	garbageScores <-chan blob.Score,
	garbageObjects chan<- string) (err error) {
	b := syncutil.NewBundle(ctx)

	const parallelism = 128
	for i := 0; i < parallelism; i++ {
		b.Add(func(ctx context.Context) (err error) {
			// Process each score.
			for score := range garbageScores {
				srcName := wiring.BlobObjectNamePrefix + score.Hex()

				// Clone the object.
				req := &gcs.CopyObjectRequest{
					SrcName: srcName,
					DstName: fmt.Sprintf("garbage/%s", score.Hex()),
				}

				_, err = bucket.CopyObject(ctx, req)
				if err != nil {
					err = fmt.Errorf("CopyObject: %v", err)
					return
				}

				// Write out the name of the object to be deleted.
				select {
				case <-ctx.Done():
					err = ctx.Err()

				case garbageObjects <- srcName:
				}
			}

			return
		})
	}

	err = b.Join()
	return
}

// Delete all objects whose name come in on the supplied channel.
func deleteObjects(
	ctx context.Context,
	bucket gcs.Bucket,
	names <-chan string) (err error) {
	b := syncutil.NewBundle(ctx)

	const parallelism = 128
	for i := 0; i < parallelism; i++ {
		b.Add(func(ctx context.Context) (err error) {
			for name := range names {
				err = bucket.DeleteObject(ctx, name)
				if err != nil {
					err = fmt.Errorf("DeleteObject(%q): %v", name, err)
					return
				}
			}

			return
		})
	}

	err = b.Join()
	return
}

////////////////////////////////////////////////////////////////////////
// GC
////////////////////////////////////////////////////////////////////////

func runGC(args []string) {
	// Allow parallelism.
	runtime.GOMAXPROCS(runtime.NumCPU())

	// Die on error.
	var err error
	defer func() {
		if err != nil {
			log.Fatalln(err)
		}
	}()

	// Grab dependencies.
	bucket := getBucket()

	// Open the input file.
	if *fInput == "" {
		err = fmt.Errorf("You must set --input.")
		return
	}

	inputFile, err := os.Open(*fInput)
	if err != nil {
		err = fmt.Errorf("Open: %v", err)
		return
	}

	// Parse it.
	accessibleScores, err := parseInput(inputFile)
	inputFile.Close()

	if err != nil {
		err = fmt.Errorf("parseInput: %v", err)
		return
	}

	b := syncutil.NewBundle(context.Background())

	// List all extant scores into a channel.
	allScores := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(allScores)
		err = blob.ListScores(ctx, bucket, wiring.BlobObjectNamePrefix, allScores)
		if err != nil {
			err = fmt.Errorf("ListScores: %v", err)
			return
		}

		return
	})

	// Count the total number of scores, periodically printing status updates.
	var allScoresCount uint64
	var garbageScoresCount uint64

	allScoresAfterCounting := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(allScoresAfterCounting)
		ticker := time.Tick(2 * time.Second)

		for score := range allScores {
			allScoresCount++

			// Print a status update?
			select {
			case <-ticker:
				g := atomic.LoadUint64(&garbageScoresCount)
				log.Printf("%d scores seen; %d garbage so far.", allScoresCount, g)

			default:
			}

			// Pass on the score.
			select {
			case <-ctx.Done():
				err = ctx.Err()
				return

			case allScoresAfterCounting <- score:
			}
		}

		return
	})

	// Filter to garbage scores.
	garbageScores := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(garbageScores)
		err = filterToGarbage(
			ctx,
			accessibleScores,
			allScoresAfterCounting,
			garbageScores)

		if err != nil {
			err = fmt.Errorf("filterToGarbage: %v", err)
			return
		}

		return
	})

	// Count the number of garbage scores.
	garbageScoresAfterCounting := make(chan blob.Score, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(garbageScoresAfterCounting)
		for score := range garbageScores {
			atomic.AddUint64(&garbageScoresCount, 1)

			select {
			case <-ctx.Done():
				err = ctx.Err()
				return

			case garbageScoresAfterCounting <- score:
			}
		}

		return
	})

	// Clone garbage blobs into a backup location.
	toDelete := make(chan string, 100)
	b.Add(func(ctx context.Context) (err error) {
		defer close(toDelete)
		err = cloneGarbage(
			ctx,
			bucket,
			garbageScoresAfterCounting,
			toDelete)
		if err != nil {
			err = fmt.Errorf("cloneGarbage: %v", err)
			return
		}

		return
	})

	// Delete the original objects.
	b.Add(func(ctx context.Context) (err error) {
		err = deleteObjects(ctx, bucket, toDelete)
		if err != nil {
			err = fmt.Errorf("deleteObjects: %v", err)
			return
		}

		return
	})

	err = b.Join()
	if err != nil {
		return
	}

	// Print a summary.
	log.Printf(
		"Deleted %d objects out of %d total.",
		garbageScoresCount,
		allScoresCount)
}
<|endoftext|>


<commit_before>package lib

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
)

// IP on Vultr
type IP struct {
	ID         string `json:"SUBID,string"`
	RegionID   int    `json:"DCID,string"`
	IPType     string `json:"ip_type"`
	Subnet     string `json:"subnet"`
	SubnetSize int    `json:"subnet_size"`
	Label      string `json:"label"`
	AttachedTo string `json:"attached_SUBID,string"`
}

// Implements json.Unmarshaller on IP.
// This is needed because the Vultr API is inconsistent in it's JSON responses.
// Some fields can change type, from JSON number to JSON string and vice-versa.
func (i *IP) UnmarshalJSON(data []byte) (err error) {
	if i == nil {
		*i = IP{}
	}

	var fields map[string]interface{}
	if err := json.Unmarshal(data, &fields); err != nil {
		return err
	}

	value := fmt.Sprintf("%v", fields["SUBID"])
	if len(value) == 0 || value == "<nil>" || value == "0" {
		i.ID = ""
	} else {
		id, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		i.ID = strconv.FormatFloat(id, 'f', -1, 64)
	}

	value = fmt.Sprintf("%v", fields["DCID"])
	if len(value) == 0 || value == "<nil>" {
		value = "0"
	}
	region, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return err
	}
	i.RegionID = int(region)

	value = fmt.Sprintf("%v", fields["attached_SUBID"])
	if len(value) == 0 || value == "<nil>" || value == "0" || value == "false" {
		i.AttachedTo = ""
	} else {
		attached, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		i.AttachedTo = strconv.FormatFloat(attached, 'f', -1, 64)
	}

	value = fmt.Sprintf("%v", fields["subnet_size"])
	if len(value) == 0 || value == "<nil>" {
		value = "0"
	}
	size, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return err
	}
	i.SubnetSize = int(size)

	i.IPType = fmt.Sprintf("%v", fields["ip_type"])
	i.Subnet = fmt.Sprintf("%v", fields["subnet"])
	i.Label = fmt.Sprintf("%v", fields["label"])

	return
}

func (c *Client) ListReservedIP() ([]IP, error) {
	var ipMap map[string]IP

	err := c.get(`reservedip/list`, &ipMap)
	if err != nil {
		return nil, err
	}

	ips := make([]IP, 0)
	for _, ip := range ipMap {
		ips = append(ips, ip)
	}
	return ips, nil
}

func (c *Client) CreateReservedIP(regionID int, ipType string, label string) (string, error) {
	values := url.Values{
		"DCID":    {fmt.Sprintf("%v", regionID)},
		"ip_type": {ipType},
	}
	if len(label) > 0 {
		values.Add("label", label)
	}

	result := IP{}
	err := c.post(`reservedip/create`, values, &result)
	if err != nil {
		return "", err
	}
	return result.ID, nil
}

func (c *Client) DestroyReservedIP(id string) error {
	values := url.Values{
		"SUBID": {id},
	}
	return c.post(`reservedip/destroy`, values, nil)
}

func (c *Client) AttachReservedIP(ip string, serverId string) error {
	values := url.Values{
		"ip_address":   {ip},
		"attach_SUBID": {serverId},
	}
	return c.post(`reservedip/attach`, values, nil)
}

func (c *Client) ConvertReservedIP(serverId string, ip string) (string, error) {
	values := url.Values{
		"SUBID":      {serverId},
		"ip_address": {ip},
	}

	result := IP{}
	err := c.post(`reservedip/convert`, values, &result)
	if err != nil {
		return "", err
	}
	return result.ID, err
}

func (c *Client) DetachReservedIP(serverId string, ip string) error {
	values := url.Values{
		"ip_address":   {ip},
		"detach_SUBID": {serverId},
	}
	return c.post(`reservedip/detach`, values, nil)
}
<commit_msg>Add GetReservedIP()<commit_after>package lib

import (
	"encoding/json"
	"fmt"
	"net/url"
	"strconv"
)

// IP on Vultr
type IP struct {
	ID         string `json:"SUBID,string"`
	RegionID   int    `json:"DCID,string"`
	IPType     string `json:"ip_type"`
	Subnet     string `json:"subnet"`
	SubnetSize int    `json:"subnet_size"`
	Label      string `json:"label"`
	AttachedTo string `json:"attached_SUBID,string"`
}

// Implements json.Unmarshaller on IP.
// This is needed because the Vultr API is inconsistent in it's JSON responses.
// Some fields can change type, from JSON number to JSON string and vice-versa.
func (i *IP) UnmarshalJSON(data []byte) (err error) {
	if i == nil {
		*i = IP{}
	}

	var fields map[string]interface{}
	if err := json.Unmarshal(data, &fields); err != nil {
		return err
	}

	value := fmt.Sprintf("%v", fields["SUBID"])
	if len(value) == 0 || value == "<nil>" || value == "0" {
		i.ID = ""
	} else {
		id, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		i.ID = strconv.FormatFloat(id, 'f', -1, 64)
	}

	value = fmt.Sprintf("%v", fields["DCID"])
	if len(value) == 0 || value == "<nil>" {
		value = "0"
	}
	region, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return err
	}
	i.RegionID = int(region)

	value = fmt.Sprintf("%v", fields["attached_SUBID"])
	if len(value) == 0 || value == "<nil>" || value == "0" || value == "false" {
		i.AttachedTo = ""
	} else {
		attached, err := strconv.ParseFloat(value, 64)
		if err != nil {
			return err
		}
		i.AttachedTo = strconv.FormatFloat(attached, 'f', -1, 64)
	}

	value = fmt.Sprintf("%v", fields["subnet_size"])
	if len(value) == 0 || value == "<nil>" {
		value = "0"
	}
	size, err := strconv.ParseInt(value, 10, 64)
	if err != nil {
		return err
	}
	i.SubnetSize = int(size)

	i.IPType = fmt.Sprintf("%v", fields["ip_type"])
	i.Subnet = fmt.Sprintf("%v", fields["subnet"])
	i.Label = fmt.Sprintf("%v", fields["label"])

	return
}

func (c *Client) ListReservedIP() ([]IP, error) {
	var ipMap map[string]IP

	err := c.get(`reservedip/list`, &ipMap)
	if err != nil {
		return nil, err
	}

	ips := make([]IP, 0)
	for _, ip := range ipMap {
		ips = append(ips, ip)
	}
	return ips, nil
}

func (c *Client) GetReservedIP(id string) (IP, error) {
	var ipMap map[string]IP

	err := c.get(`reservedip/list`, &ipMap)
	if err != nil {
		return IP{}, err
	}
	if ip, ok := ipMap[id]; ok {
		return ip, nil
	}
	return IP{}, fmt.Errorf("IP with id %v not found.", id)
}

func (c *Client) CreateReservedIP(regionID int, ipType string, label string) (string, error) {
	values := url.Values{
		"DCID":    {fmt.Sprintf("%v", regionID)},
		"ip_type": {ipType},
	}
	if len(label) > 0 {
		values.Add("label", label)
	}

	result := IP{}
	err := c.post(`reservedip/create`, values, &result)
	if err != nil {
		return "", err
	}
	return result.ID, nil
}

func (c *Client) DestroyReservedIP(id string) error {
	values := url.Values{
		"SUBID": {id},
	}
	return c.post(`reservedip/destroy`, values, nil)
}

func (c *Client) AttachReservedIP(ip string, serverId string) error {
	values := url.Values{
		"ip_address":   {ip},
		"attach_SUBID": {serverId},
	}
	return c.post(`reservedip/attach`, values, nil)
}

func (c *Client) ConvertReservedIP(serverId string, ip string) (string, error) {
	values := url.Values{
		"SUBID":      {serverId},
		"ip_address": {ip},
	}

	result := IP{}
	err := c.post(`reservedip/convert`, values, &result)
	if err != nil {
		return "", err
	}
	return result.ID, err
}

func (c *Client) DetachReservedIP(serverId string, ip string) error {
	values := url.Values{
		"ip_address":   {ip},
		"detach_SUBID": {serverId},
	}
	return c.post(`reservedip/detach`, values, nil)
}
<|endoftext|>


<commit_before>package sarah

import (
	"golang.org/x/net/context"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
)

type DummyCommand struct {
	IdentifierValue  string
	ExecuteFunc      func(context.Context, Input) (*CommandResponse, error)
	InputExampleFunc func() string
	MatchFunc        func(string) bool
}

func (command *DummyCommand) Identifier() string {
	return command.IdentifierValue
}

func (command *DummyCommand) Execute(ctx context.Context, input Input) (*CommandResponse, error) {
	return command.ExecuteFunc(ctx, input)
}

func (command *DummyCommand) InputExample() string {
	return command.InputExampleFunc()
}

func (command *DummyCommand) Match(str string) bool {
	return command.MatchFunc(str)
}

func TestNewCommandBuilder(t *testing.T) {
	builder := NewCommandBuilder()
	if builder == nil {
		t.Fatal("NewCommandBuilder returned nil.")
	}
}

func TestCommandBuilder_ConfigurableFunc(t *testing.T) {
	wrappedFncCalled := false
	config := &struct{}{}
	fnc := func(_ context.Context, _ Input, passedConfig CommandConfig) (*CommandResponse, error) {
		wrappedFncCalled = true
		if passedConfig != config {
			t.Errorf("Passed config is not the expected one: %#v", passedConfig)
		}
		return nil, nil
	}

	builder := &CommandBuilder{}
	builder.ConfigurableFunc(config, fnc)
	if builder.config != config {
		t.Error("Passed config struct is not set.")
	}

	builder.commandFunc(context.TODO(), &DummyInput{}, config)
	if wrappedFncCalled == false {
		t.Error("Provided func was not properlly wrapped in builder.")
	}
}

func TestCommandBuilder_Func(t *testing.T) {
	wrappedFncCalled := false
	builder := &CommandBuilder{}
	fnc := func(_ context.Context, _ Input) (*CommandResponse, error) {
		wrappedFncCalled = true
		return nil, nil
	}

	builder.Func(fnc)
	builder.commandFunc(context.TODO(), &DummyInput{})
	if wrappedFncCalled == false {
		t.Error("Provided func was not properlly wrapped in builder.")
	}
}

func TestCommandBuilder_Identifier(t *testing.T) {
	builder := &CommandBuilder{}
	id := "FOO"
	builder.Identifier(id)

	if builder.identifier != id {
		t.Error("Provided identifier is not set.")
	}
}

func TestCommandBuilder_InputExample(t *testing.T) {
	builder := &CommandBuilder{}
	example := ".echo foo"
	builder.InputExample(example)

	if builder.example != example {
		t.Error("Provided example is not set.")
	}
}

func TestCommandBuilder_MatchPattern(t *testing.T) {
	builder := &CommandBuilder{}
	pattern := regexp.MustCompile(`^\.echo`)
	builder.MatchPattern(pattern)

	if builder.matchPattern != pattern {
		t.Error("Provided match pattern is not set.")
	}
}

func TestCommandBuilder_Build(t *testing.T) {
	builder := &CommandBuilder{}
	if _, err := builder.Build("/path/"); err == nil {
		t.Error("expected error not given.")
	} else if err != ErrCommandInsufficientArgument {
		t.Errorf("expected error not given. %#v", err)
	}

	matchPattern := regexp.MustCompile(`^\.echo`)
	builder.Identifier("dummy").
		MatchPattern(matchPattern).
		InputExample(".echo knock knock")

	// When corresponding configuration file is not found, then manually set schedule must stay.
	dummyToken := "dummy"
	config := &struct {
		Token string `yaml:"token"`
	}{
		Token: dummyToken,
	}
	builder.ConfigurableFunc(config, func(_ context.Context, input Input, passedConfig CommandConfig) (*CommandResponse, error) {
		return &CommandResponse{
			Content: StripMessage(matchPattern, input.Message()),
		}, nil
	})

	command, err := builder.Build(filepath.Join("unknown", "path", "foo"))
	if err != nil {
		t.Fatal("Error on command construction with no config file.")
	}
	if config.Token != dummyToken {
		t.Errorf("Config value changed: %s.", config.Token)
	}

	command, err = builder.Build(filepath.Join("testdata", "commandbuilder"))
	if err != nil {
		t.Errorf("something is wrong with command construction. %#v", err)
	}

	if command == nil {
		t.Fatal("Built command is not returned.")
	}

	if _, ok := command.(*simpleCommand); !ok {
		t.Fatalf("Returned command is not type of *simpleCommand: %T.", command)
	}

	if config.Token != "foobar" {
		t.Error("Configuration is not read from testdata/commandbuilder/dummy.yaml file.")
	}
}

func TestCommandBuilder_MustBuild(t *testing.T) {
	builder := &CommandBuilder{}
	builder.Identifier("dummy").
		MatchPattern(regexp.MustCompile(`^\.echo`)).
		InputExample(".echo knock knock")

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Error("Expected panic did not occur.")
			}
		}()
		builder.MustBuild()
	}()

	builder.Func(func(_ context.Context, input Input) (*CommandResponse, error) {
		return nil, nil
	})
	command := builder.MustBuild()
	if command.Identifier() != builder.identifier {
		t.Error("Provided identifier is not set.")
	}
}

func TestNewCommands(t *testing.T) {
	commands := NewCommands()
	if commands == nil {
		t.Error("Not properly initialized.")
	}
}

func TestCommands_FindFirstMatched(t *testing.T) {
	commands := &Commands{}
	matchedCommand := commands.FindFirstMatched("echo")
	if matchedCommand != nil {
		t.Fatalf("Something is returned while nothing other than nil may returned: %#v.", matchedCommand)
	}

	irrelevantCommand := &DummyCommand{}
	irrelevantCommand.MatchFunc = func(msg string) bool {
		return false
	}
	echoCommand := &DummyCommand{}
	echoCommand.MatchFunc = func(msg string) bool {
		return strings.HasPrefix(msg, "echo")
	}
	echoCommand.ExecuteFunc = func(_ context.Context, _ Input) (*CommandResponse, error) {
		return &CommandResponse{Content: ""}, nil
	}
	irrelevantCommand2 := &DummyCommand{}
	irrelevantCommand2.MatchFunc = func(msg string) bool {
		return false
	}
	commands = &Commands{irrelevantCommand, echoCommand, irrelevantCommand2}

	matchedCommand = commands.FindFirstMatched("echo")
	if matchedCommand == nil {
		t.Fatal("Expected command is not found.")
	}

	if matchedCommand != echoCommand {
		t.Fatalf("Expected command instance not returned: %#v.", matchedCommand)
	}
}

func TestCommands_ExecuteFirstMatched(t *testing.T) {
	commands := &Commands{}

	input := &DummyInput{}
	input.MessageValue = "echo foo"
	response, err := commands.ExecuteFirstMatched(context.TODO(), input)
	if err != nil {
		t.Error("Error is returned on non matching case.")
	}
	if response != nil {
		t.Error("Response should be nil on non matching case.")
	}

	echoCommand := &DummyCommand{}
	echoCommand.MatchFunc = func(msg string) bool {
		return strings.HasPrefix(msg, "echo")
	}
	echoCommand.ExecuteFunc = func(_ context.Context, _ Input) (*CommandResponse, error) {
		return &CommandResponse{Content: ""}, nil
	}
	commands = &Commands{echoCommand}
	response, err = commands.ExecuteFirstMatched(context.TODO(), input)
	if err != nil {
		t.Errorf("Unexpected error on command execution: %#v.", err)
		return
	}

	if response == nil {
		t.Error("Response expected, but was not returned.")
		return
	}

	switch v := response.Content.(type) {
	case string:
	//OK
	default:
		t.Errorf("Expected string, but was %#v.", v)
	}
}

func TestCommands_Append(t *testing.T) {
	commands := &Commands{}

	command := &DummyCommand{}
	commands.Append(command)
	if len(*commands) == 0 {
		t.Fatal("Provided command was not appended.")
	}

	if (*commands)[0] != command {
		t.Fatalf("Appended command is not the one provided: %#v", (*commands)[0])
	}
}

func TestCommands_Helps(t *testing.T) {
	cmd := &DummyCommand{
		IdentifierValue: "id",
		InputExampleFunc: func() string {
			return "example"
		},
	}
	commands := &Commands{cmd}

	helps := commands.Helps()
	if len(*helps) != 1 {
		t.Fatalf("Expectnig one help to be given, but was %d.", len(*helps))
	}
	if (*helps)[0].Identifier != cmd.IdentifierValue {
		t.Errorf("Expected ID was not returned: %s.", (*helps)[0].Identifier)
	}
	if (*helps)[0].InputExample != cmd.InputExampleFunc() {
		t.Errorf("Expected example was not returned: %s.", (*helps)[0].InputExample)
	}
}

func TestSimpleCommand_Identifier(t *testing.T) {
	id := "bar"
	command := simpleCommand{identifier: id}

	if command.Identifier() != id {
		t.Errorf("Stored identifier is not returned: %s.", command.Identifier())
	}
}

func TestSimpleCommand_InputExample(t *testing.T) {
	example := "example foo"
	command := simpleCommand{example: example}

	if command.InputExample() != example {
		t.Errorf("Stored example is not returned: %s.", command.Identifier())
	}
}

func TestSimpleCommand_Match(t *testing.T) {
	pattern := regexp.MustCompile(`^\.echo`)
	command := simpleCommand{matchPattern: pattern}

	if command.Match(".echo foo") == false {
		t.Error("Expected match result is not returned.")
	}
}

func TestSimpleCommand_Execute(t *testing.T) {
	wrappedFncCalled := false
	command := simpleCommand{
		config: &struct{}{},
		commandFunc: func(ctx context.Context, input Input, cfg ...CommandConfig) (*CommandResponse, error) {
			wrappedFncCalled = true
			return nil, nil
		},
	}

	input := &DummyInput{}
	_, err := command.Execute(context.TODO(), input)
	if err != nil {
		t.Errorf("Error is returned: %s", err.Error())
	}
	if wrappedFncCalled == false {
		t.Error("Wrapped function is not called.")
	}
}

func TestStripMessage(t *testing.T) {
	pattern := regexp.MustCompile(`^\.echo`)
	stripped := StripMessage(pattern, ".echo foo bar")

	if stripped != "foo bar" {
		t.Errorf("Unexpected return value: %s.", stripped)
	}
}
<commit_msg>Add missing tests for Commands.Append<commit_after>package sarah

import (
	"golang.org/x/net/context"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
)

type DummyCommand struct {
	IdentifierValue  string
	ExecuteFunc      func(context.Context, Input) (*CommandResponse, error)
	InputExampleFunc func() string
	MatchFunc        func(string) bool
}

func (command *DummyCommand) Identifier() string {
	return command.IdentifierValue
}

func (command *DummyCommand) Execute(ctx context.Context, input Input) (*CommandResponse, error) {
	return command.ExecuteFunc(ctx, input)
}

func (command *DummyCommand) InputExample() string {
	return command.InputExampleFunc()
}

func (command *DummyCommand) Match(str string) bool {
	return command.MatchFunc(str)
}

func TestNewCommandBuilder(t *testing.T) {
	builder := NewCommandBuilder()
	if builder == nil {
		t.Fatal("NewCommandBuilder returned nil.")
	}
}

func TestCommandBuilder_ConfigurableFunc(t *testing.T) {
	wrappedFncCalled := false
	config := &struct{}{}
	fnc := func(_ context.Context, _ Input, passedConfig CommandConfig) (*CommandResponse, error) {
		wrappedFncCalled = true
		if passedConfig != config {
			t.Errorf("Passed config is not the expected one: %#v", passedConfig)
		}
		return nil, nil
	}

	builder := &CommandBuilder{}
	builder.ConfigurableFunc(config, fnc)
	if builder.config != config {
		t.Error("Passed config struct is not set.")
	}

	builder.commandFunc(context.TODO(), &DummyInput{}, config)
	if wrappedFncCalled == false {
		t.Error("Provided func was not properlly wrapped in builder.")
	}
}

func TestCommandBuilder_Func(t *testing.T) {
	wrappedFncCalled := false
	builder := &CommandBuilder{}
	fnc := func(_ context.Context, _ Input) (*CommandResponse, error) {
		wrappedFncCalled = true
		return nil, nil
	}

	builder.Func(fnc)
	builder.commandFunc(context.TODO(), &DummyInput{})
	if wrappedFncCalled == false {
		t.Error("Provided func was not properlly wrapped in builder.")
	}
}

func TestCommandBuilder_Identifier(t *testing.T) {
	builder := &CommandBuilder{}
	id := "FOO"
	builder.Identifier(id)

	if builder.identifier != id {
		t.Error("Provided identifier is not set.")
	}
}

func TestCommandBuilder_InputExample(t *testing.T) {
	builder := &CommandBuilder{}
	example := ".echo foo"
	builder.InputExample(example)

	if builder.example != example {
		t.Error("Provided example is not set.")
	}
}

func TestCommandBuilder_MatchPattern(t *testing.T) {
	builder := &CommandBuilder{}
	pattern := regexp.MustCompile(`^\.echo`)
	builder.MatchPattern(pattern)

	if builder.matchPattern != pattern {
		t.Error("Provided match pattern is not set.")
	}
}

func TestCommandBuilder_Build(t *testing.T) {
	builder := &CommandBuilder{}
	if _, err := builder.Build("/path/"); err == nil {
		t.Error("expected error not given.")
	} else if err != ErrCommandInsufficientArgument {
		t.Errorf("expected error not given. %#v", err)
	}

	matchPattern := regexp.MustCompile(`^\.echo`)
	builder.Identifier("dummy").
		MatchPattern(matchPattern).
		InputExample(".echo knock knock")

	// When corresponding configuration file is not found, then manually set schedule must stay.
	dummyToken := "dummy"
	config := &struct {
		Token string `yaml:"token"`
	}{
		Token: dummyToken,
	}
	builder.ConfigurableFunc(config, func(_ context.Context, input Input, passedConfig CommandConfig) (*CommandResponse, error) {
		return &CommandResponse{
			Content: StripMessage(matchPattern, input.Message()),
		}, nil
	})

	command, err := builder.Build(filepath.Join("unknown", "path", "foo"))
	if err != nil {
		t.Fatal("Error on command construction with no config file.")
	}
	if config.Token != dummyToken {
		t.Errorf("Config value changed: %s.", config.Token)
	}

	command, err = builder.Build(filepath.Join("testdata", "commandbuilder"))
	if err != nil {
		t.Errorf("something is wrong with command construction. %#v", err)
	}

	if command == nil {
		t.Fatal("Built command is not returned.")
	}

	if _, ok := command.(*simpleCommand); !ok {
		t.Fatalf("Returned command is not type of *simpleCommand: %T.", command)
	}

	if config.Token != "foobar" {
		t.Error("Configuration is not read from testdata/commandbuilder/dummy.yaml file.")
	}
}

func TestCommandBuilder_MustBuild(t *testing.T) {
	builder := &CommandBuilder{}
	builder.Identifier("dummy").
		MatchPattern(regexp.MustCompile(`^\.echo`)).
		InputExample(".echo knock knock")

	func() {
		defer func() {
			if r := recover(); r == nil {
				t.Error("Expected panic did not occur.")
			}
		}()
		builder.MustBuild()
	}()

	builder.Func(func(_ context.Context, input Input) (*CommandResponse, error) {
		return nil, nil
	})
	command := builder.MustBuild()
	if command.Identifier() != builder.identifier {
		t.Error("Provided identifier is not set.")
	}
}

func TestNewCommands(t *testing.T) {
	commands := NewCommands()
	if commands == nil {
		t.Error("Not properly initialized.")
	}
}

func TestCommands_FindFirstMatched(t *testing.T) {
	commands := &Commands{}
	matchedCommand := commands.FindFirstMatched("echo")
	if matchedCommand != nil {
		t.Fatalf("Something is returned while nothing other than nil may returned: %#v.", matchedCommand)
	}

	irrelevantCommand := &DummyCommand{}
	irrelevantCommand.MatchFunc = func(msg string) bool {
		return false
	}
	echoCommand := &DummyCommand{}
	echoCommand.MatchFunc = func(msg string) bool {
		return strings.HasPrefix(msg, "echo")
	}
	echoCommand.ExecuteFunc = func(_ context.Context, _ Input) (*CommandResponse, error) {
		return &CommandResponse{Content: ""}, nil
	}
	irrelevantCommand2 := &DummyCommand{}
	irrelevantCommand2.MatchFunc = func(msg string) bool {
		return false
	}
	commands = &Commands{irrelevantCommand, echoCommand, irrelevantCommand2}

	matchedCommand = commands.FindFirstMatched("echo")
	if matchedCommand == nil {
		t.Fatal("Expected command is not found.")
	}

	if matchedCommand != echoCommand {
		t.Fatalf("Expected command instance not returned: %#v.", matchedCommand)
	}
}

func TestCommands_ExecuteFirstMatched(t *testing.T) {
	commands := &Commands{}

	input := &DummyInput{}
	input.MessageValue = "echo foo"
	response, err := commands.ExecuteFirstMatched(context.TODO(), input)
	if err != nil {
		t.Error("Error is returned on non matching case.")
	}
	if response != nil {
		t.Error("Response should be nil on non matching case.")
	}

	echoCommand := &DummyCommand{}
	echoCommand.MatchFunc = func(msg string) bool {
		return strings.HasPrefix(msg, "echo")
	}
	echoCommand.ExecuteFunc = func(_ context.Context, _ Input) (*CommandResponse, error) {
		return &CommandResponse{Content: ""}, nil
	}
	commands = &Commands{echoCommand}
	response, err = commands.ExecuteFirstMatched(context.TODO(), input)
	if err != nil {
		t.Errorf("Unexpected error on command execution: %#v.", err)
		return
	}

	if response == nil {
		t.Error("Response expected, but was not returned.")
		return
	}

	switch v := response.Content.(type) {
	case string:
	//OK
	default:
		t.Errorf("Expected string, but was %#v.", v)
	}
}

func TestCommands_Append(t *testing.T) {
	commands := &Commands{}

	command := &DummyCommand{
		IdentifierValue: "first",
	}

	// First operation
	commands.Append(command)
	if len(*commands) == 0 {
		t.Fatal("Provided command was not appended.")
	}

	if (*commands)[0] != command {
		t.Fatalf("Appended command is not the one provided: %#v", (*commands)[0])
	}

	// Second operation with same command
	commands.Append(command)
	if len(*commands) != 1 {
		t.Fatalf("Expected only one command to stay, but was: %d.", len(*commands))
	}

	// Third operation with different command
	anotherCommand := &DummyCommand{
		IdentifierValue: "second",
	}
	commands.Append(anotherCommand)
	if len(*commands) != 2 {
		t.Fatalf("Expected 2 commands to stay, but was: %d.", len(*commands))
	}
}

func TestCommands_Helps(t *testing.T) {
	cmd := &DummyCommand{
		IdentifierValue: "id",
		InputExampleFunc: func() string {
			return "example"
		},
	}
	commands := &Commands{cmd}

	helps := commands.Helps()
	if len(*helps) != 1 {
		t.Fatalf("Expectnig one help to be given, but was %d.", len(*helps))
	}
	if (*helps)[0].Identifier != cmd.IdentifierValue {
		t.Errorf("Expected ID was not returned: %s.", (*helps)[0].Identifier)
	}
	if (*helps)[0].InputExample != cmd.InputExampleFunc() {
		t.Errorf("Expected example was not returned: %s.", (*helps)[0].InputExample)
	}
}

func TestSimpleCommand_Identifier(t *testing.T) {
	id := "bar"
	command := simpleCommand{identifier: id}

	if command.Identifier() != id {
		t.Errorf("Stored identifier is not returned: %s.", command.Identifier())
	}
}

func TestSimpleCommand_InputExample(t *testing.T) {
	example := "example foo"
	command := simpleCommand{example: example}

	if command.InputExample() != example {
		t.Errorf("Stored example is not returned: %s.", command.Identifier())
	}
}

func TestSimpleCommand_Match(t *testing.T) {
	pattern := regexp.MustCompile(`^\.echo`)
	command := simpleCommand{matchPattern: pattern}

	if command.Match(".echo foo") == false {
		t.Error("Expected match result is not returned.")
	}
}

func TestSimpleCommand_Execute(t *testing.T) {
	wrappedFncCalled := false
	command := simpleCommand{
		config: &struct{}{},
		commandFunc: func(ctx context.Context, input Input, cfg ...CommandConfig) (*CommandResponse, error) {
			wrappedFncCalled = true
			return nil, nil
		},
	}

	input := &DummyInput{}
	_, err := command.Execute(context.TODO(), input)
	if err != nil {
		t.Errorf("Error is returned: %s", err.Error())
	}
	if wrappedFncCalled == false {
		t.Error("Wrapped function is not called.")
	}
}

func TestStripMessage(t *testing.T) {
	pattern := regexp.MustCompile(`^\.echo`)
	stripped := StripMessage(pattern, ".echo foo bar")

	if stripped != "foo bar" {
		t.Errorf("Unexpected return value: %s.", stripped)
	}
}
<|endoftext|>


<commit_before>package clw11

/*
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif
*/
import "C"

type (
	CommandQueue           C.cl_command_queue
	CommandQueueProperties C.cl_command_queue_properties
)

// Bitfield.
const (
	QueueOutOfOrderExecModeEnable CommandQueueProperties = C.CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE
	QueueProfilingEnable          CommandQueueProperties = C.CL_QUEUE_PROFILING_ENABLE
)

// Create a command-queue on a specific device.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateCommandQueue.html
func CreateCommandQueue(context Context, device DeviceID, properties CommandQueueProperties) (CommandQueue, error) {
	var err C.cl_int
	result := C.clCreateCommandQueue(context, device, C.cl_command_queue_properties(properties), &err)
	return CommandQueue(result), toError(err)
}

// Issues all previously queued OpenCL commands in a command-queue to the device
// associated with the command-queue.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFlush.html
func Flush(cq CommandQueue) error {
	return toError(C.clFlush(cq))
}

// Blocks until all previously queued OpenCL commands in a command-queue are
// issued to the associated device and have completed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFinish.html
func Finish(cq CommandQueue) error {
	return toError(C.clFinish(cq))
}

// Increments the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clRetainCommandQueue.html
func RetainCommandQueue(command_queue CommandQueue) error {
	return toError(C.clRetainCommandQueue(command_queue))
}

// Decrements the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clReleaseCommandQueue.html
func ReleaseCommandQueue(command_queue CommandQueue) error {
	return toError(C.clReleaseCommandQueue(command_queue))
}
<commit_msg>Added remaining command queue functions.<commit_after>package clw11

/*
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif
*/
import "C"

type (
	CommandQueue           C.cl_command_queue
	CommandQueueProperties C.cl_command_queue_properties
)

// Bitfield.
const (
	QueueOutOfOrderExecModeEnable CommandQueueProperties = C.CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE
	QueueProfilingEnable          CommandQueueProperties = C.CL_QUEUE_PROFILING_ENABLE
)

// Create a command-queue on a specific device.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateCommandQueue.html
func CreateCommandQueue(context Context, device DeviceID, properties CommandQueueProperties) (CommandQueue, error) {
	var err C.cl_int
	result := C.clCreateCommandQueue(context, device, C.cl_command_queue_properties(properties), &err)
	return CommandQueue(result), toError(err)
}

// Issues all previously queued OpenCL commands in a command-queue to the device
// associated with the command-queue.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFlush.html
func Flush(cq CommandQueue) error {
	return toError(C.clFlush(cq))
}

// Blocks until all previously queued OpenCL commands in a command-queue are
// issued to the associated device and have completed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFinish.html
func Finish(cq CommandQueue) error {
	return toError(C.clFinish(cq))
}

// Increments the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clRetainCommandQueue.html
func RetainCommandQueue(command_queue CommandQueue) error {
	return toError(C.clRetainCommandQueue(command_queue))
}

// Decrements the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clReleaseCommandQueue.html
func ReleaseCommandQueue(command_queue CommandQueue) error {
	return toError(C.clReleaseCommandQueue(command_queue))
}

// Enqueues a marker command.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueMarker.html
func EnqueueMarker(command_queue CommandQueue, event *Event) error {
	return toError(C.clEnqueueMarker(command_queue, (*C.cl_event)(event)))
}

// Enqueues a wait for a specific event or a list of events to complete before
// any future commands queued in the command-queue are executed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueWaitForEvents.html
func EnqueueWaitForEvents(command_queue CommandQueue, wait_list []Event) error {
	event_list, num_events := toEventList(wait_list)
	return toError(C.clEnqueueWaitForEvents(command_queue, num_events, event_list))
}

// A synchronization point that enqueues a barrier operation.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueBarrier.html
func EnqueueBarrier(command_queue CommandQueue) error {
	return toError(C.clEnqueueBarrier(command_queue))
}
<|endoftext|>


<commit_before>package clw11

/*
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif
*/
import "C"

type (
	CommandQueue           C.cl_command_queue
	CommandQueueProperties C.cl_command_queue_properties
)

// Bitfield.
const (
	QueueOutOfOrderExecModeEnable CommandQueueProperties = C.CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE
	QueueProfilingEnable          CommandQueueProperties = C.CL_QUEUE_PROFILING_ENABLE
)

// Create a command-queue on a specific device.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateCommandQueue.html
func CreateCommandQueue(context Context, device DeviceID, properties CommandQueueProperties) (CommandQueue, error) {
	var err C.cl_int
	result := C.clCreateCommandQueue(context, device, C.cl_command_queue_properties(properties), &err)
	return CommandQueue(result), toError(err)
}

// Issues all previously queued OpenCL commands in a command-queue to the device
// associated with the command-queue.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFlush.html
func Flush(cq CommandQueue) error {
	return toError(C.clFlush(cq))
}

// Blocks until all previously queued OpenCL commands in a command-queue are
// issued to the associated device and have completed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFinish.html
func Finish(cq CommandQueue) error {
	return toError(C.clFinish(cq))
}

// Increments the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clRetainCommandQueue.html
func RetainCommandQueue(command_queue CommandQueue) error {
	return toError(C.clRetainCommandQueue(command_queue))
}

// Decrements the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clReleaseCommandQueue.html
func ReleaseCommandQueue(command_queue CommandQueue) error {
	return toError(C.clReleaseCommandQueue(command_queue))
}

// Enqueues a marker command.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueMarker.html
func EnqueueMarker(command_queue CommandQueue, event *Event) error {
	return toError(C.clEnqueueMarker(command_queue, (*C.cl_event)(event)))
}

// Enqueues a wait for a specific event or a list of events to complete before
// any future commands queued in the command-queue are executed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueWaitForEvents.html
func EnqueueWaitForEvents(command_queue CommandQueue, wait_list []Event) error {
	event_list, num_events := toEventList(wait_list)
	return toError(C.clEnqueueWaitForEvents(command_queue, num_events, event_list))
}

// A synchronization point that enqueues a barrier operation.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueBarrier.html
func EnqueueBarrier(command_queue CommandQueue) error {
	return toError(C.clEnqueueBarrier(command_queue))
}
<commit_msg>Added deprecated function and a little reordering.<commit_after>package clw11

/*
#define CL_USE_DEPRECATED_OPENCL_1_1_APIS
#ifdef __APPLE__
#include "OpenCL/opencl.h"
#else
#include "CL/opencl.h"
#endif
*/
import "C"

type (
	CommandQueue           C.cl_command_queue
	CommandQueueProperties C.cl_command_queue_properties
)

// Bitfield.
const (
	QueueOutOfOrderExecModeEnable CommandQueueProperties = C.CL_QUEUE_OUT_OF_ORDER_EXEC_MODE_ENABLE
	QueueProfilingEnable          CommandQueueProperties = C.CL_QUEUE_PROFILING_ENABLE
)

// Create a command-queue on a specific device.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clCreateCommandQueue.html
func CreateCommandQueue(context Context, device DeviceID, properties CommandQueueProperties) (CommandQueue, error) {
	var err C.cl_int
	result := C.clCreateCommandQueue(context, device, C.cl_command_queue_properties(properties), &err)
	return CommandQueue(result), toError(err)
}

// Increments the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clRetainCommandQueue.html
func RetainCommandQueue(command_queue CommandQueue) error {
	return toError(C.clRetainCommandQueue(command_queue))
}

// Decrements the command_queue reference count.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clReleaseCommandQueue.html
func ReleaseCommandQueue(command_queue CommandQueue) error {
	return toError(C.clReleaseCommandQueue(command_queue))
}

// // Enable or disable the properties of a command-queue.
// // http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clSetCommandQueueProperty.html
// func SetCommandQueueProperty(command_queue CommandQueue, properties CommandQueueProperties, enable Bool,
// 	old_properties *CommandQueueProperties) error {
// 	return toError(C.clSetCommandQueueProperty(command_queue, properties, C.cl_bool(enable),
// 		(*C.cl_command_queue_properties)(old_properties)))
// }

// Enqueues a marker command.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueMarker.html
func EnqueueMarker(command_queue CommandQueue, event *Event) error {
	return toError(C.clEnqueueMarker(command_queue, (*C.cl_event)(event)))
}

// Enqueues a wait for a specific event or a list of events to complete before
// any future commands queued in the command-queue are executed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueWaitForEvents.html
func EnqueueWaitForEvents(command_queue CommandQueue, wait_list []Event) error {
	event_list, num_events := toEventList(wait_list)
	return toError(C.clEnqueueWaitForEvents(command_queue, num_events, event_list))
}

// A synchronization point that enqueues a barrier operation.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clEnqueueBarrier.html
func EnqueueBarrier(command_queue CommandQueue) error {
	return toError(C.clEnqueueBarrier(command_queue))
}

// Issues all previously queued OpenCL commands in a command-queue to the device
// associated with the command-queue.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFlush.html
func Flush(cq CommandQueue) error {
	return toError(C.clFlush(cq))
}

// Blocks until all previously queued OpenCL commands in a command-queue are
// issued to the associated device and have completed.
// http://www.khronos.org/registry/cl/sdk/1.1/docs/man/xhtml/clFinish.html
func Finish(cq CommandQueue) error {
	return toError(C.clFinish(cq))
}
<|endoftext|>


<commit_before>package commands

import (
	"github.com/ipfs-search/ipfs-search/crawler"
	"github.com/ipfs-search/ipfs-search/queue"
)

// AddHash queues a single IPFS hash for indexing
func AddHash(hash string) error {
	config, err := getConfig()
	if err != nil {
		return err
	}

	conn, err := queue.NewConnection(config.Factory.AMQPURL)
	if err != nil {
		return err
	}
	defer conn.Close()

	ch, err := conn.NewChannel()
	if err != nil {
		return err
	}

	queue, err := ch.NewQueue("hashes")
	if err != nil {
		return err
	}

	err = queue.Publish(&crawler.Args{
		Hash: hash,
	})

	return err
}
<commit_msg>Simplify add command.<commit_after>package commands

import (
	"github.com/ipfs-search/ipfs-search/crawler"
	"github.com/ipfs-search/ipfs-search/queue"
)

// AddHash queues a single IPFS hash for indexing
func AddHash(hash string) error {
	config, err := getConfig()
	if err != nil {
		return err
	}

	conn, err := queue.NewConnection(config.Factory.AMQPURL)
	if err != nil {
		return err
	}
	defer conn.Close()

	queue, err := conn.NewChannelQueue("hashes")
	if err != nil {
		return err
	}

	err = queue.Publish(&crawler.Args{
		Hash: hash,
	})

	return err
}
<|endoftext|>


<commit_before>package commands

import (
	"fmt"

	"github.com/ambientsound/gompd/mpd"
	"github.com/ambientsound/pms/input/lexer"
	"github.com/ambientsound/pms/message"
	"github.com/ambientsound/pms/song"
	"github.com/ambientsound/pms/songlist"
)

// Add adds songs to MPD's queue.
type Add struct {
	api      API
	song     *song.Song
	songlist songlist.Songlist
}

func NewAdd(api API) Command {
	return &Add{
		api: api,
	}
}

func (cmd *Add) Reset() {
	cmd.song = nil
	cmd.songlist = nil
}

func (cmd *Add) Execute(t lexer.Token) error {
	var err error

	switch t.Class {
	case lexer.TokenIdentifier:
		if cmd.song != nil {
			return fmt.Errorf("Cannot add multiple paths on the same command line.")
		}
		cmd.song = song.New()
		cmd.song.SetTags(mpd.Attrs{
			"file": t.String(),
		})

	case lexer.TokenEnd:
		songlistWidget := cmd.api.SonglistWidget()
		queue := cmd.api.Queue()

		switch {
		case cmd.song == nil:
			selection := songlistWidget.Selection()
			if selection.Len() == 0 {
				return fmt.Errorf("No selection, cannot add without any parameters.")
			}
			err = queue.AddList(selection)
			if err != nil {
				break
			}
			songlistWidget.ClearSelection()
			songlistWidget.MoveCursor(1)
			len := selection.Len()
			if len == 1 {
				song := selection.Songs()[0]
				cmd.api.Message(message.Format("Added to queue: %s", song.StringTags["file"]))
			} else {
				cmd.api.Message(message.Format("Added %d songs to queue.", len))
			}

		default:
			err = queue.Add(cmd.song)
			if err == nil {
				cmd.api.Message(message.Format("Added to queue: %s", cmd.song.StringTags["file"]))
			}
		}

	default:
		return fmt.Errorf("Unknown input '%s', expected END", string(t.Runes))
	}

	return err
}
<commit_msg>Remove obsolete code<commit_after>package commands

import (
	"fmt"

	"github.com/ambientsound/gompd/mpd"
	"github.com/ambientsound/pms/input/lexer"
	"github.com/ambientsound/pms/message"
	"github.com/ambientsound/pms/song"
	"github.com/ambientsound/pms/songlist"
)

// Add adds songs to MPD's queue.
type Add struct {
	api      API
	song     *song.Song
	songlist songlist.Songlist
}

func NewAdd(api API) Command {
	return &Add{
		api: api,
	}
}

func (cmd *Add) Execute(t lexer.Token) error {
	var err error

	switch t.Class {
	case lexer.TokenIdentifier:
		if cmd.song != nil {
			return fmt.Errorf("Cannot add multiple paths on the same command line.")
		}
		cmd.song = song.New()
		cmd.song.SetTags(mpd.Attrs{
			"file": t.String(),
		})

	case lexer.TokenEnd:
		songlistWidget := cmd.api.SonglistWidget()
		queue := cmd.api.Queue()

		switch {
		case cmd.song == nil:
			selection := songlistWidget.Selection()
			if selection.Len() == 0 {
				return fmt.Errorf("No selection, cannot add without any parameters.")
			}
			err = queue.AddList(selection)
			if err != nil {
				break
			}
			songlistWidget.ClearSelection()
			songlistWidget.MoveCursor(1)
			len := selection.Len()
			if len == 1 {
				song := selection.Songs()[0]
				cmd.api.Message(message.Format("Added to queue: %s", song.StringTags["file"]))
			} else {
				cmd.api.Message(message.Format("Added %d songs to queue.", len))
			}

		default:
			err = queue.Add(cmd.song)
			if err == nil {
				cmd.api.Message(message.Format("Added to queue: %s", cmd.song.StringTags["file"]))
			}
		}

	default:
		return fmt.Errorf("Unknown input '%s', expected END", string(t.Runes))
	}

	return err
}
<|endoftext|>


<commit_before>package commands

import (
	"context"
	"os"
	"strings"

	"../shell"
)

func array2hash(args []string) ([]string, map[string]string) {
	hash := map[string]string{}
	for i, arg1 := range args {
		equalPos := strings.IndexRune(arg1, '=')
		if equalPos < 0 {
			return args[i:], hash
		}
		key := arg1[:equalPos]
		val := arg1[equalPos+1:]
		hash[key] = val
	}
	return []string{}, hash
}

func cmd_env(ctx context.Context, cmd *shell.Cmd) (int, error) {
	args, hash := array2hash(cmd.Args[1:])
	if len(args) <= 0 {
		return 0, nil
	}
	backup := map[string]string{}
	for key, val := range hash {
		backup[key] = os.Getenv(key)
		os.Setenv(key, val)
	}

	var rc int
	subCmd, err := cmd.Clone()
	if err == nil {
		subCmd.Args = args
		rc, err = subCmd.SpawnvpContext(ctx)
	} else {
		rc = -1
	}

	for key, val := range backup {
		os.Setenv(key, val)
	}
	return rc, err
}
<commit_msg>env: with no arguments, echo all environments same as set.<commit_after>package commands

import (
	"context"
	"fmt"
	"os"
	"strings"

	"../shell"
)

func array2hash(args []string) ([]string, map[string]string) {
	hash := map[string]string{}
	for i, arg1 := range args {
		equalPos := strings.IndexRune(arg1, '=')
		if equalPos < 0 {
			return args[i:], hash
		}
		key := arg1[:equalPos]
		val := arg1[equalPos+1:]
		hash[key] = val
	}
	return []string{}, hash
}

func cmd_env(ctx context.Context, cmd *shell.Cmd) (int, error) {
	args, hash := array2hash(cmd.Args[1:])
	if len(args) <= 0 {
		for _, val := range os.Environ() {
			fmt.Fprintln(cmd.Stdout, val)
		}
		return 0, nil
	}
	backup := map[string]string{}
	for key, val := range hash {
		backup[key] = os.Getenv(key)
		os.Setenv(key, val)
	}

	var rc int
	subCmd, err := cmd.Clone()
	if err == nil {
		subCmd.Args = args
		rc, err = subCmd.SpawnvpContext(ctx)
	} else {
		rc = -1
	}

	for key, val := range backup {
		os.Setenv(key, val)
	}
	return rc, err
}
<|endoftext|>


<commit_before>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package commands

import (
	"strconv"
	"strings"

	"github.com/bogem/nehm/client"
	"github.com/bogem/nehm/config"
	"github.com/bogem/nehm/track"
	"github.com/bogem/nehm/tracksprocessor"
	"github.com/bogem/nehm/ui"
	"github.com/spf13/cobra"
)

var (
	getCommand = &cobra.Command{
		Use:     "get ([number] or [url])",
		Short:   "download either inputed count of likes or track from entered url, set tags (and add to your iTunes library)",
		Aliases: []string{"g"},
		Run:     getTracks,
	}
)

func init() {
	addCommonFlags(getCommand)
	addOffsetFlag(getCommand)
	addPermalinkFlag(getCommand)
}

func getTracks(cmd *cobra.Command, args []string) {
	initializeConfig(cmd)

	tp := tracksprocessor.NewConfiguredTracksProcessor()

	var arg string
	if len(args) == 0 {
		arg = "1"
	} else {
		arg = args[0]
	}

	var downloadTracks []track.Track
	if isSoundCloudURL(arg) {
		downloadTracks = getTrackFromURL(arg)
	} else if num, err := strconv.Atoi(arg); err == nil {
		downloadTracks, err = getLastTracks(uint(num))
		if err != nil {
			handleError(err)
		}
	} else {
		ui.Term("You've entered invalid argument. Run 'nehm get --help' for usage.", nil)
	}

	tp.ProcessAll(downloadTracks)
}

func getLastTracks(count uint) ([]track.Track, error) {
	uid := client.UID(config.Get("permalink"))
	return client.Favorites(count, offset, uid)
}

func isSoundCloudURL(url string) bool {
	return strings.Contains(url, "soundcloud.com")
}

func getTrackFromURL(url string) []track.Track {
	return client.TrackFromURI(url)
}

func handleError(err error) {
	switch {
	case strings.Contains(err.Error(), "403"):
		ui.Term("You're not allowed to see these tracks", nil)
	case strings.Contains(err.Error(), "404"):
		ui.Term("There are no tracks", nil)
	}
}
<commit_msg>Edit description of get command<commit_after>// Copyright 2016 Albert Nigmatzianov. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package commands

import (
	"strconv"
	"strings"

	"github.com/bogem/nehm/client"
	"github.com/bogem/nehm/config"
	"github.com/bogem/nehm/track"
	"github.com/bogem/nehm/tracksprocessor"
	"github.com/bogem/nehm/ui"
	"github.com/spf13/cobra"
)

var (
	getCommand = &cobra.Command{
		Use:     "get [number or url]",
		Short:   "download either inputed count of likes or track from entered url, set tags (and add to your iTunes library)",
		Aliases: []string{"g"},
		Run:     getTracks,
	}
)

func init() {
	addCommonFlags(getCommand)
	addOffsetFlag(getCommand)
	addPermalinkFlag(getCommand)
}

func getTracks(cmd *cobra.Command, args []string) {
	initializeConfig(cmd)

	tp := tracksprocessor.NewConfiguredTracksProcessor()

	var arg string
	if len(args) == 0 {
		arg = "1"
	} else {
		arg = args[0]
	}

	var downloadTracks []track.Track
	if isSoundCloudURL(arg) {
		downloadTracks = getTrackFromURL(arg)
	} else if num, err := strconv.Atoi(arg); err == nil {
		downloadTracks, err = getLastTracks(uint(num))
		if err != nil {
			handleError(err)
		}
	} else {
		ui.Term("You've entered invalid argument. Run 'nehm get --help' for usage.", nil)
	}

	tp.ProcessAll(downloadTracks)
}

func getLastTracks(count uint) ([]track.Track, error) {
	uid := client.UID(config.Get("permalink"))
	return client.Favorites(count, offset, uid)
}

func isSoundCloudURL(url string) bool {
	return strings.Contains(url, "soundcloud.com")
}

func getTrackFromURL(url string) []track.Track {
	return client.TrackFromURI(url)
}

func handleError(err error) {
	switch {
	case strings.Contains(err.Error(), "403"):
		ui.Term("You're not allowed to see these tracks", nil)
	case strings.Contains(err.Error(), "404"):
		ui.Term("There are no tracks", nil)
	}
}
<|endoftext|>


<commit_before>package common

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

// GetOrCreateEventID uses grpc metadata context to set an event id
// the metadata context is then sent over the wire - gRPC calls
// and available to other services
func DEPRECATEDGetOrCreateEventID(ctx context.Context) (string, context.Context) {
	// get metadata from context
	md, ok := metadata.FromContext(ctx)
	if !ok {
		md = metadata.New(map[string]string{})
	}
	e, ok := md["event"]
	if !ok {
		// append new evt id
		md["event"] = append(md["event"], RandID("evt_", 16))
	}
	ctx = metadata.NewContext(ctx, md)
	e, _ = md["event"]
	// log.Printf("GetOrCreateEventID md:%v", md)
	// log.Printf("GetOrCreateEventID ctx:%v", ctx)

	return e[0], ctx
}

// GetOrCreateEventID returns eid and context with eid in context metadata
func GetOrCreateEventID(ctx context.Context) (string, context.Context) {
	// get metadata from context
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		md = metadata.New(map[string]string{})
	}
	// NOTE: modification should be made to copies of the returned MD.
	md = md.Copy()
	// verify if eid exists, if not generate new eid
	_, ok = md["eid"]
	if !ok {
		md = metadata.Join(md, metadata.Pairs("eid", RandID("", 16)))
	}
	ctx = metadata.NewOutgoingContext(ctx, md)
	return md["eid"][0], ctx
}
<commit_msg>remove deprecated function<commit_after>package common

import (
	"golang.org/x/net/context"
	"google.golang.org/grpc/metadata"
)

// GetOrCreateEventID returns eid and context with eid in context metadata
func GetOrCreateEventID(ctx context.Context) (string, context.Context) {
	// get metadata from context
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		md = metadata.New(map[string]string{})
	}
	// NOTE: modification should be made to copies of the returned MD.
	md = md.Copy()
	// verify if eid exists, if not generate new eid
	_, ok = md["eid"]
	if !ok {
		md = metadata.Join(md, metadata.Pairs("eid", RandID("", 16)))
	}
	ctx = metadata.NewOutgoingContext(ctx, md)
	return md["eid"][0], ctx
}
<|endoftext|>