<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/datawire/ambassador/pkg/supervisor"
)

func (d *Daemon) interceptMessage() string {
	switch {
	case d.cluster == nil:
		return "Not connected (use 'edgectl connect' to connect to your cluster)"
	case d.trafficMgr == nil:
		return "Intercept unavailable: no traffic manager"
	case !d.trafficMgr.IsOkay():
		return "Connecting to traffic manager..."
	default:
		return ""
	}
}

// InterceptInfo tracks one intercept operation
type InterceptInfo struct {
	Name       string // Name of the intercept (user/logging)
	Namespace  string // Name in which to create the Intercept mapping
	Deployment string // Name of the deployment being intercepted
	Prefix     string // Prefix to intercept (default /)
	Patterns   map[string]string
	TargetHost string
	TargetPort int
}

// path returns the URL path for this intercept
func (ii *InterceptInfo) path() string {
	return fmt.Sprintf("intercept/%s/%s", ii.Namespace, ii.Deployment)
}

// Acquire an intercept from the traffic manager
func (ii *InterceptInfo) Acquire(_ *supervisor.Process, tm *TrafficManager) (int, error) {
	reqPatterns := make([]map[string]string, 0, len(ii.Patterns))
	for header, regex := range ii.Patterns {
		pattern := map[string]string{"name": header, "regex_match": regex}
		reqPatterns = append(reqPatterns, pattern)
	}
	request := map[string]interface{}{
		"name":     ii.Name,
		"patterns": reqPatterns,
	}
	reqData, err := json.Marshal(request)
	if err != nil {
		return 0, err
	}

	result, code, err := tm.request("POST", ii.path(), reqData)
	if err != nil {
		return 0, errors.Wrap(err, "acquire intercept")
	}
	if code == 404 {
		return 0, fmt.Errorf("deployment %q is not known to the traffic manager", ii.Deployment)
	}
	if !(200 <= code && code <= 299) {
		return 0, fmt.Errorf("acquire intercept: %s: %s", http.StatusText(code), result)
	}
	port, err := strconv.Atoi(result)
	if err != nil {
		return 0, errors.Wrapf(err, "bad port number from traffic manager: %q", result)
	}
	return port, nil
}

// Retain the given intercept. This likely needs to be called every
// five seconds or so.
func (ii *InterceptInfo) Retain(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("{\"port\": %d}", port))
	result, code, err := tm.request("POST", ii.path(), data)
	if err != nil {
		return errors.Wrap(err, "retain intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("retain intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// Release the given intercept.
func (ii *InterceptInfo) Release(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("%d", port))
	result, code, err := tm.request("DELETE", ii.path(), data)
	if err != nil {
		return errors.Wrap(err, "release intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("release intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// ListIntercepts lists active intercepts
func (d *Daemon) ListIntercepts(_ *supervisor.Process, out *Emitter) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	for idx, cept := range d.intercepts {
		ii := cept.ii
		out.Printf("%4d. %s\n", idx+1, ii.Name)
		out.Send(fmt.Sprintf("local_intercept.%d", idx+1), ii.Name)
		key := "local_intercept." + ii.Name
		out.Printf("      Intercepting requests to %s when\n", ii.Deployment)
		out.Send(key, ii.Deployment)
		for k, v := range ii.Patterns {
			out.Printf("      - %s: %s\n", k, v)
			out.Send(key+"."+k, v)
		}
		out.Printf("      and redirecting them to %s:%d\n", ii.TargetHost, ii.TargetPort)
		out.Send(key+".host", ii.TargetHost)
		out.Send(key+".port", ii.TargetPort)
	}
	if len(d.intercepts) == 0 {
		out.Println("No intercepts")
	}
	return nil
}

// AddIntercept adds one intercept
func (d *Daemon) AddIntercept(p *supervisor.Process, out *Emitter, ii *InterceptInfo) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}

	for _, cept := range d.intercepts {
		if cept.ii.Name == ii.Name {
			out.Printf("Intercept with name %q already exists\n", ii.Name)
			out.Send("failed", "intercept name exists")
			out.SendExit(1)
			return nil
		}
	}

	// Do we already have a namespace?
	if ii.Namespace == "" {
		// Nope. See if we have an interceptable that matches the name.

		matches := make([]InterceptInfo, 0)
		for _, deployment := range d.trafficMgr.interceptables {
			fields := strings.SplitN(deployment, "/", 2)

			appName := fields[0]
			appNamespace := d.cluster.namespace

			if len(fields) > 1 {
				appNamespace = fields[0]
				appName = fields[1]
			}

			if ii.Deployment == appName {
				// Abuse InterceptInfo rather than defining a new tuple type.
				matches = append(matches, InterceptInfo{"", appNamespace, appName, "", nil, "", 0})
			}
		}

		switch len(matches) {
		case 0:
			out.Printf("No interceptable deployment matching %s found", ii.Name)
			out.Send("failed", "no interceptable deployment matches")
			out.SendExit(1)
			return nil

		case 1:
			// Good to go.
			ii.Namespace = matches[0].Namespace
			out.Printf("Using deployment %s in namespace %s\n", ii.Name, ii.Namespace)

		default:
			out.Printf("Found more than one possible match:\n")

			for idx, match := range matches {
				out.Printf("%4d: %s in namespace %s\n", idx+1, match.Deployment, match.Namespace)
			}
			out.Send("failed", "multiple interceptable deployment matched")
			out.SendExit(1)
			return nil
		}
	}

	cept, err := MakeIntercept(p, out, d.trafficMgr, d.cluster, ii)
	if err != nil {
		out.Printf("Failed to establish intercept: %s\n", err)
		out.Send("failed", err.Error())
		out.SendExit(1)
		return nil
	}
	d.intercepts = append(d.intercepts, cept)
	out.Printf("Added intercept %q\n", ii.Name)
	return nil
}

// RemoveIntercept removes one intercept by name
func (d *Daemon) RemoveIntercept(p *supervisor.Process, out *Emitter, name string) error {
	msg := d.interceptMessage()
	for idx, cept := range d.intercepts {
		if cept.ii.Name == name {
			d.intercepts = append(d.intercepts[:idx], d.intercepts[idx+1:]...)

			out.Printf("Removed intercept %q\n", name)

			if err := cept.Close(); err != nil {
				out.Printf("Error while removing intercept: %v\n", err)
				out.Send("failed", err.Error())
				out.SendExit(1)
			}
			return nil
		}
	}
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	out.Printf("Intercept named %q not found\n", name)
	out.Send("failed", "not found")
	out.SendExit(1)
	return nil
}

// ClearIntercepts removes all intercepts
func (d *Daemon) ClearIntercepts(p *supervisor.Process) error {
	for _, cept := range d.intercepts {
		if err := cept.Close(); err != nil {
			p.Logf("Closing intercept %q: %v", cept.ii.Name, err)
		}
	}
	d.intercepts = d.intercepts[:0]
	return nil
}

// Intercept is a Resource handle that represents a live intercept
type Intercept struct {
	ii            *InterceptInfo
	tm            *TrafficManager
	cluster       *KCluster
	port          int
	crc           Resource
	mappingExists bool
	ResourceBase
}

// removeMapping drops an Intercept's mapping if needed (and possible).
func (cept *Intercept) removeMapping(p *supervisor.Process) error {
	var err error
	err = nil

	if cept.mappingExists {
		p.Logf("%v: Deleting mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
		delete := cept.cluster.GetKubectlCmd(p, "delete", "-n", cept.ii.Namespace, "mapping", fmt.Sprintf("%s-mapping", cept.ii.Name))
		err = delete.Run()
		p.Logf("%v: Deleted mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
	}

	if err != nil {
		return errors.Wrap(err, "Intercept: mapping could not be deleted")
	}

	return nil
}

type mappingMetadata struct {
	Name      string `json:"name"`
	Namespace string `json:"namespace"`
}

type mappingSpec struct {
	AmbassadorID []string          `json:"ambassador_id"`
	Prefix       string            `json:"prefix"`
	Service      string            `json:"service"`
	Headers      map[string]string `json:"headers"`
}

type interceptMapping struct {
	APIVersion string          `json:"apiVersion"`
	Kind       string          `json:"kind"`
	Metadata   mappingMetadata `json:"metadata"`
	Spec       mappingSpec     `json:"spec"`
}

// MakeIntercept acquires an intercept and returns a Resource handle
// for it
func MakeIntercept(p *supervisor.Process, out *Emitter, tm *TrafficManager, cluster *KCluster, ii *InterceptInfo) (*Intercept, error) {
	port, err := ii.Acquire(p, tm)
	if err != nil {
		return nil, err
	}

	cept := &Intercept{ii: ii, tm: tm, cluster: cluster, port: port}
	cept.mappingExists = false
	cept.doCheck = cept.check
	cept.doQuit = cept.quit
	cept.setup(p.Supervisor(), ii.Name)

	p.Logf("%s: Intercepting via port %v, using namespace %v", ii.Name, port, ii.Namespace)

	mapping := interceptMapping{
		APIVersion: "getambassador.io/v2",
		Kind:       "Mapping",
		Metadata: mappingMetadata{
			Name:      fmt.Sprintf("%s-mapping", ii.Name),
			Namespace: ii.Namespace,
		},
		Spec: mappingSpec{
			AmbassadorID: []string{fmt.Sprintf("intercept-%s", ii.Deployment)},
			Prefix:       ii.Prefix,
			Service:      fmt.Sprintf("telepresence-proxy.%s:%d", tm.namespace, port),
			Headers:      ii.Patterns,
		},
	}

	manifest, err := json.MarshalIndent(&mapping, "", "  ")
	if err != nil {
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: mapping could not be constructed")
	}

	out.Printf("%s: applying intercept mapping in namespace %s\n", ii.Name, ii.Namespace)

	apply := cluster.GetKubectlCmdNoNamespace(p, "apply", "-f", "-")
	apply.Stdin = strings.NewReader(string(manifest))
	err = apply.Run()

	if err != nil {
		p.Logf("%v: Intercept could not apply mapping: %v", ii.Name, err)
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: kubectl apply")
	}

	cept.mappingExists = true

	sshCmd := []string{
		"ssh", "-C", "-N", "telepresence@localhost",
		"-oConnectTimeout=10", "-oExitOnForwardFailure=yes",
		"-oStrictHostKeyChecking=no", "-oUserKnownHostsFile=/dev/null",
		"-p", strconv.Itoa(tm.sshPort),
		"-R", fmt.Sprintf("%d:%s:%d", cept.port, ii.TargetHost, ii.TargetPort),
	}

	p.Logf("%s: starting SSH tunnel", ii.Name)
	out.Printf("%s: starting SSH tunnel\n", ii.Name)

	ssh, err := CheckedRetryingCommand(p, ii.Name+"-ssh", sshCmd, nil, nil, 5*time.Second)
	if err != nil {
		_ = cept.Close()
		return nil, err
	}

	cept.crc = ssh

	return cept, nil
}

func (cept *Intercept) check(p *supervisor.Process) error {
	return cept.ii.Retain(p, cept.tm, cept.port)
}

func (cept *Intercept) quit(p *supervisor.Process) error {
	cept.done = true

	p.Logf("cept.Quit removing %v", cept.ii.Name)

	cept.removeMapping(p)

	p.Logf("cept.Quit removed %v", cept.ii.Name)

	if cept.crc != nil {
		_ = cept.crc.Close()
	}

	p.Logf("cept.Quit releasing %v", cept.ii.Name)

	if err := cept.ii.Release(p, cept.tm, cept.port); err != nil {
		p.Log(err)
	}

	p.Logf("cept.Quit released %v", cept.ii.Name)

	return nil
}
<commit_msg>Intercept: Use regex_headers, not headers<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"time"

	"github.com/pkg/errors"

	"github.com/datawire/ambassador/pkg/supervisor"
)

func (d *Daemon) interceptMessage() string {
	switch {
	case d.cluster == nil:
		return "Not connected (use 'edgectl connect' to connect to your cluster)"
	case d.trafficMgr == nil:
		return "Intercept unavailable: no traffic manager"
	case !d.trafficMgr.IsOkay():
		return "Connecting to traffic manager..."
	default:
		return ""
	}
}

// InterceptInfo tracks one intercept operation
type InterceptInfo struct {
	Name       string // Name of the intercept (user/logging)
	Namespace  string // Name in which to create the Intercept mapping
	Deployment string // Name of the deployment being intercepted
	Prefix     string // Prefix to intercept (default /)
	Patterns   map[string]string
	TargetHost string
	TargetPort int
}

// path returns the URL path for this intercept
func (ii *InterceptInfo) path() string {
	return fmt.Sprintf("intercept/%s/%s", ii.Namespace, ii.Deployment)
}

// Acquire an intercept from the traffic manager
func (ii *InterceptInfo) Acquire(_ *supervisor.Process, tm *TrafficManager) (int, error) {
	reqPatterns := make([]map[string]string, 0, len(ii.Patterns))
	for header, regex := range ii.Patterns {
		pattern := map[string]string{"name": header, "regex_match": regex}
		reqPatterns = append(reqPatterns, pattern)
	}
	request := map[string]interface{}{
		"name":     ii.Name,
		"patterns": reqPatterns,
	}
	reqData, err := json.Marshal(request)
	if err != nil {
		return 0, err
	}

	result, code, err := tm.request("POST", ii.path(), reqData)
	if err != nil {
		return 0, errors.Wrap(err, "acquire intercept")
	}
	if code == 404 {
		return 0, fmt.Errorf("deployment %q is not known to the traffic manager", ii.Deployment)
	}
	if !(200 <= code && code <= 299) {
		return 0, fmt.Errorf("acquire intercept: %s: %s", http.StatusText(code), result)
	}
	port, err := strconv.Atoi(result)
	if err != nil {
		return 0, errors.Wrapf(err, "bad port number from traffic manager: %q", result)
	}
	return port, nil
}

// Retain the given intercept. This likely needs to be called every
// five seconds or so.
func (ii *InterceptInfo) Retain(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("{\"port\": %d}", port))
	result, code, err := tm.request("POST", ii.path(), data)
	if err != nil {
		return errors.Wrap(err, "retain intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("retain intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// Release the given intercept.
func (ii *InterceptInfo) Release(_ *supervisor.Process, tm *TrafficManager, port int) error {
	data := []byte(fmt.Sprintf("%d", port))
	result, code, err := tm.request("DELETE", ii.path(), data)
	if err != nil {
		return errors.Wrap(err, "release intercept")
	}
	if !(200 <= code && code <= 299) {
		return fmt.Errorf("release intercept: %s: %s", http.StatusText(code), result)
	}
	return nil
}

// ListIntercepts lists active intercepts
func (d *Daemon) ListIntercepts(_ *supervisor.Process, out *Emitter) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	for idx, cept := range d.intercepts {
		ii := cept.ii
		out.Printf("%4d. %s\n", idx+1, ii.Name)
		out.Send(fmt.Sprintf("local_intercept.%d", idx+1), ii.Name)
		key := "local_intercept." + ii.Name
		out.Printf("      Intercepting requests to %s when\n", ii.Deployment)
		out.Send(key, ii.Deployment)
		for k, v := range ii.Patterns {
			out.Printf("      - %s: %s\n", k, v)
			out.Send(key+"."+k, v)
		}
		out.Printf("      and redirecting them to %s:%d\n", ii.TargetHost, ii.TargetPort)
		out.Send(key+".host", ii.TargetHost)
		out.Send(key+".port", ii.TargetPort)
	}
	if len(d.intercepts) == 0 {
		out.Println("No intercepts")
	}
	return nil
}

// AddIntercept adds one intercept
func (d *Daemon) AddIntercept(p *supervisor.Process, out *Emitter, ii *InterceptInfo) error {
	msg := d.interceptMessage()
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}

	for _, cept := range d.intercepts {
		if cept.ii.Name == ii.Name {
			out.Printf("Intercept with name %q already exists\n", ii.Name)
			out.Send("failed", "intercept name exists")
			out.SendExit(1)
			return nil
		}
	}

	// Do we already have a namespace?
	if ii.Namespace == "" {
		// Nope. See if we have an interceptable that matches the name.

		matches := make([]InterceptInfo, 0)
		for _, deployment := range d.trafficMgr.interceptables {
			fields := strings.SplitN(deployment, "/", 2)

			appName := fields[0]
			appNamespace := d.cluster.namespace

			if len(fields) > 1 {
				appNamespace = fields[0]
				appName = fields[1]
			}

			if ii.Deployment == appName {
				// Abuse InterceptInfo rather than defining a new tuple type.
				matches = append(matches, InterceptInfo{"", appNamespace, appName, "", nil, "", 0})
			}
		}

		switch len(matches) {
		case 0:
			out.Printf("No interceptable deployment matching %s found", ii.Name)
			out.Send("failed", "no interceptable deployment matches")
			out.SendExit(1)
			return nil

		case 1:
			// Good to go.
			ii.Namespace = matches[0].Namespace
			out.Printf("Using deployment %s in namespace %s\n", ii.Name, ii.Namespace)

		default:
			out.Printf("Found more than one possible match:\n")

			for idx, match := range matches {
				out.Printf("%4d: %s in namespace %s\n", idx+1, match.Deployment, match.Namespace)
			}
			out.Send("failed", "multiple interceptable deployment matched")
			out.SendExit(1)
			return nil
		}
	}

	cept, err := MakeIntercept(p, out, d.trafficMgr, d.cluster, ii)
	if err != nil {
		out.Printf("Failed to establish intercept: %s\n", err)
		out.Send("failed", err.Error())
		out.SendExit(1)
		return nil
	}
	d.intercepts = append(d.intercepts, cept)
	out.Printf("Added intercept %q\n", ii.Name)
	return nil
}

// RemoveIntercept removes one intercept by name
func (d *Daemon) RemoveIntercept(p *supervisor.Process, out *Emitter, name string) error {
	msg := d.interceptMessage()
	for idx, cept := range d.intercepts {
		if cept.ii.Name == name {
			d.intercepts = append(d.intercepts[:idx], d.intercepts[idx+1:]...)

			out.Printf("Removed intercept %q\n", name)

			if err := cept.Close(); err != nil {
				out.Printf("Error while removing intercept: %v\n", err)
				out.Send("failed", err.Error())
				out.SendExit(1)
			}
			return nil
		}
	}
	if msg != "" {
		out.Println(msg)
		out.Send("intercept", msg)
		return nil
	}
	out.Printf("Intercept named %q not found\n", name)
	out.Send("failed", "not found")
	out.SendExit(1)
	return nil
}

// ClearIntercepts removes all intercepts
func (d *Daemon) ClearIntercepts(p *supervisor.Process) error {
	for _, cept := range d.intercepts {
		if err := cept.Close(); err != nil {
			p.Logf("Closing intercept %q: %v", cept.ii.Name, err)
		}
	}
	d.intercepts = d.intercepts[:0]
	return nil
}

// Intercept is a Resource handle that represents a live intercept
type Intercept struct {
	ii            *InterceptInfo
	tm            *TrafficManager
	cluster       *KCluster
	port          int
	crc           Resource
	mappingExists bool
	ResourceBase
}

// removeMapping drops an Intercept's mapping if needed (and possible).
func (cept *Intercept) removeMapping(p *supervisor.Process) error {
	var err error
	err = nil

	if cept.mappingExists {
		p.Logf("%v: Deleting mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
		delete := cept.cluster.GetKubectlCmd(p, "delete", "-n", cept.ii.Namespace, "mapping", fmt.Sprintf("%s-mapping", cept.ii.Name))
		err = delete.Run()
		p.Logf("%v: Deleted mapping in namespace %v", cept.ii.Name, cept.ii.Namespace)
	}

	if err != nil {
		return errors.Wrap(err, "Intercept: mapping could not be deleted")
	}

	return nil
}

type mappingMetadata struct {
	Name      string `json:"name"`
	Namespace string `json:"namespace"`
}

type mappingSpec struct {
	AmbassadorID []string          `json:"ambassador_id"`
	Prefix       string            `json:"prefix"`
	Service      string            `json:"service"`
	RegexHeaders map[string]string `json:"regex_headers"`
}

type interceptMapping struct {
	APIVersion string          `json:"apiVersion"`
	Kind       string          `json:"kind"`
	Metadata   mappingMetadata `json:"metadata"`
	Spec       mappingSpec     `json:"spec"`
}

// MakeIntercept acquires an intercept and returns a Resource handle
// for it
func MakeIntercept(p *supervisor.Process, out *Emitter, tm *TrafficManager, cluster *KCluster, ii *InterceptInfo) (*Intercept, error) {
	port, err := ii.Acquire(p, tm)
	if err != nil {
		return nil, err
	}

	cept := &Intercept{ii: ii, tm: tm, cluster: cluster, port: port}
	cept.mappingExists = false
	cept.doCheck = cept.check
	cept.doQuit = cept.quit
	cept.setup(p.Supervisor(), ii.Name)

	p.Logf("%s: Intercepting via port %v, using namespace %v", ii.Name, port, ii.Namespace)

	mapping := interceptMapping{
		APIVersion: "getambassador.io/v2",
		Kind:       "Mapping",
		Metadata: mappingMetadata{
			Name:      fmt.Sprintf("%s-mapping", ii.Name),
			Namespace: ii.Namespace,
		},
		Spec: mappingSpec{
			AmbassadorID: []string{fmt.Sprintf("intercept-%s", ii.Deployment)},
			Prefix:       ii.Prefix,
			Service:      fmt.Sprintf("telepresence-proxy.%s:%d", tm.namespace, port),
			RegexHeaders: ii.Patterns,
		},
	}

	manifest, err := json.MarshalIndent(&mapping, "", "  ")
	if err != nil {
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: mapping could not be constructed")
	}

	out.Printf("%s: applying intercept mapping in namespace %s\n", ii.Name, ii.Namespace)

	apply := cluster.GetKubectlCmdNoNamespace(p, "apply", "-f", "-")
	apply.Stdin = strings.NewReader(string(manifest))
	err = apply.Run()

	if err != nil {
		p.Logf("%v: Intercept could not apply mapping: %v", ii.Name, err)
		_ = cept.Close()
		return nil, errors.Wrap(err, "Intercept: kubectl apply")
	}

	cept.mappingExists = true

	sshCmd := []string{
		"ssh", "-C", "-N", "telepresence@localhost",
		"-oConnectTimeout=10", "-oExitOnForwardFailure=yes",
		"-oStrictHostKeyChecking=no", "-oUserKnownHostsFile=/dev/null",
		"-p", strconv.Itoa(tm.sshPort),
		"-R", fmt.Sprintf("%d:%s:%d", cept.port, ii.TargetHost, ii.TargetPort),
	}

	p.Logf("%s: starting SSH tunnel", ii.Name)
	out.Printf("%s: starting SSH tunnel\n", ii.Name)

	ssh, err := CheckedRetryingCommand(p, ii.Name+"-ssh", sshCmd, nil, nil, 5*time.Second)
	if err != nil {
		_ = cept.Close()
		return nil, err
	}

	cept.crc = ssh

	return cept, nil
}

func (cept *Intercept) check(p *supervisor.Process) error {
	return cept.ii.Retain(p, cept.tm, cept.port)
}

func (cept *Intercept) quit(p *supervisor.Process) error {
	cept.done = true

	p.Logf("cept.Quit removing %v", cept.ii.Name)

	cept.removeMapping(p)

	p.Logf("cept.Quit removed %v", cept.ii.Name)

	if cept.crc != nil {
		_ = cept.crc.Close()
	}

	p.Logf("cept.Quit releasing %v", cept.ii.Name)

	if err := cept.ii.Release(p, cept.tm, cept.port); err != nil {
		p.Log(err)
	}

	p.Logf("cept.Quit released %v", cept.ii.Name)

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"sort"
	"strings"

	"github.com/sprucehealth/graphql/language/ast"
)

func generateClient(g *generator) {
	imports := []string{"github.com/sprucehealth/graphql"}
	if len(g.cfg.Resolvers) != 0 {
		imports = []string{
			"context",
			"fmt",
			"reflect",
			"",
			"github.com/sprucehealth/graphql",
			"github.com/sprucehealth/graphql/gqldecode",
			"github.com/sprucehealth/graphql/gqlerrors",
			"github.com/sprucehealth/graphql/language/parser",
			"github.com/sprucehealth/graphql/language/printer",
			"github.com/sprucehealth/graphql/language/source",
			"github.com/sprucehealth/mapstructure",
		}
	}

	g.printf("package client\n\n")
	g.printf("import (\n")
	for _, im := range imports {
		if im == "" {
			g.printf("\n")
		} else {
			g.printf("\t%q\n", im)
		}
	}
	g.printf(")\n\n")

	clientTypes := make(map[string]struct{})
	for _, t := range strings.Split(*flagClientTypes, ",") {
		clientTypes[t] = struct{}{}
	}

	var clientTypeDefs []*ast.ObjectDefinition
	for _, def := range g.doc.Definitions {
		switch def := def.(type) {
		case *ast.ObjectDefinition:
			if _, ok := clientTypes[def.Name.Value]; ok {
				clientTypeDefs = append(clientTypeDefs, def)
			}
			g.genObjectModel(def)
			g.printf("\n")
		case *ast.InterfaceDefinition:
			g.genInterfaceModel(def)
			g.printf("\n")
		case *ast.InputObjectDefinition:
			g.genInputModel(def)
			g.printf("\n")
		case *ast.UnionDefinition:
			g.genUnionModel(def)
			g.printf("\n")
		case *ast.EnumDefinition:
			g.genEnumConstants(def)
			g.printf("\n")
		}
	}

	var sigs []string
	for _, def := range clientTypeDefs {
		for _, field := range def.Fields {
			sigs = append(sigs, renderSignatureForField(g, def, field))
		}
	}
	sort.Strings(sigs)
	g.printf("type Client interface {\n")
	for _, sig := range sigs {
		g.printf(sig + "\n")
	}
	g.printf("}\n")
	g.print(`
		type client struct {
			endpoint string
			authToken string
			log Logger
		}

		type clientOption func(c *client)

		func WithClientLogger(l Logger) clientOption {
			return func(c *client) {
				c.log = l
			}
		}

		type Logger interface {
			Debugf(ctx context.Context, msg string, v ...interface{})
		}

		type nullLogger struct{}
		func (nullLogger) Debugf(ctx context.Context, msg string, v ...interface{}){}

		func New(endpoint, authToken string, opts ...clientOption) Client {
			c := &client{
				endpoint: endpoint,
				authToken: authToken,
				log: nullLogger{},
			}
			for _, o := range opts {
				o(c)
			}
			return c
		}
	`)
	for _, def := range clientTypeDefs {
		for _, field := range def.Fields {
			genClientMethodForField(g, def, field)
			g.printf("\n")
		}
	}
	genDecoderTypeMap(g)
	g.printf("\n")
	genDecoderHook(g)
	g.printf("\n")
	genQueryWrapperTypes(g)
	g.printf("\n")
	genClientDo(g)
	g.printf("\n")
	genRewriteQuery(g)
	g.printf("\n")
}

func renderSignatureForField(g *generator, d *ast.ObjectDefinition, f *ast.FieldDefinition) string {
	return fmt.Sprintf("%s%s(ctx context.Context, query string) (%s, error)", exportedName(d.Name.Value), exportedName(f.Name.Value), g.goType(f.Type, ""))
}

func genDecoderHook(g *generator) {
	g.printf("func decoderHook(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) {\n")
	g.printf("\tif from == reflect.Map && to == reflect.Interface {\n")
	g.printf("\t\tcVal := reflect.New(objectTypesByTypename[v.(map[string]interface{})[\"__typename\"].(string)])\n")
	g.printf("\t\tout := cVal.Elem().Addr().Interface()\n")
	g.printf("\t\tdecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{\n")
	g.printf("\t\t\tDecodeHook: decoderHook,\n")
	g.printf("\t\t\tResult:     out,\n")
	g.printf("\t\t})\n")
	g.printf("\t\tif err != nil {\n")
	g.printf("\t\t\treturn 0, err\n")
	g.printf("\t\t}\n")
	g.printf("\t\tif err := decoder.Decode(v.(map[string]interface{})); err != nil {\n")
	g.print("\t\t\treturn nil, fmt.Errorf(\"Error decoding %+v into %+v\", v, out)\n")
	g.printf("\t\t}\n")
	g.printf("\t\treturn out, nil\n")
	g.printf("\t}\n")
	g.printf("\treturn v, nil\n")
	g.printf("}\n")
}

func genDecoderTypeMap(g *generator) {
	g.printf("var objectTypesByTypename = map[string]reflect.Type{\n")
	for _, def := range g.doc.Definitions {
		switch def := def.(type) {
		case *ast.ObjectDefinition:
			g.printf("\"%s\": reflect.TypeOf(%s{}),\n", def.Name.Value, def.Name.Value)
		}
	}
	g.printf("}\n")
}

func genOutputTypeVar(g *generator, f *ast.FieldDefinition) {
	outType := g.goType(f.Type, "")
	if outType[0] == '*' {
		outType = outType[1:]
	}
	g.printf("\tvar out %s\n", outType)
}

func outputTypeReturn(g *generator, f *ast.FieldDefinition) string {
	outType := g.goType(f.Type, "")
	if outType[0] == '*' {
		return "&out"
	}
	return "out"
}

func genClientMethodForField(g *generator, d *ast.ObjectDefinition, f *ast.FieldDefinition) {
	g.printf("func (c *client) %s {\n", renderSignatureForField(g, d, f))
	g.printf("\tc.log.Debugf(ctx, \"%s%s\")\n", exportedName(d.Name.Value), exportedName(f.Name.Value))
	genOutputTypeVar(g, f)
	g.printf("\tif _, err := c.do(ctx, \"%s\", query, &out); err != nil {\n", unexportedName(f.Name.Value))
	g.printf("\t\treturn %s, err\n", renderDefaultReturnValue(f.Type))
	g.printf("\t}\n")
	g.printf("\treturn %s, nil", outputTypeReturn(g, f))
	g.printf("}\n")
}

func renderDefaultClientReturnValue(t ast.Type) string {
	switch t := t.(type) {
	case *ast.NonNull:
		return renderDefaultClientReturnValue(t.Type)
	case *ast.Named:
		switch t.Name.Value {
		case "ID", "String":
			return `""`
		case "Boolean":
			return "false"
		case "Float", "Int":
			return "0"
		}
	}
	return "nil"
}

func genQueryWrapperTypes(g *generator) {
	g.print(`
		type gqlRequestBody struct {
			Query string ` + "`json:\"query\"`" + `
		}

		type gqlResponse struct {
			Data   map[string]interface{}   ` + "`json:\"data\"`" + `
			Errors []map[string]interface{} ` + "`json:\"errors\"`" + `
		}
	`)
}

func genRewriteQuery(g *generator) {
	g.print(`
		func rewriteQuery(query string) (string, error) {
			qast, err := parser.Parse(parser.ParseParams{
				Source: source.New("GraphQL Query", query),
			})
			if err != nil {
				return "", err
			}
			graphql.RequestTypeNames(qast)
			return printer.Print(qast), nil
		}
	`)
}

func genClientDo(g *generator) {
	g.print(`
		func (c *client) do(ctx context.Context, dataField, query string, out interface{}) (int, error) {
			query, err := rewriteQuery(query)
			if err != nil {
				return 0, err
			}
			rb := &gqlRequestBody{Query: query}
			bBody, err := json.Marshal(rb)
			if err != nil {
				return 0, err
			}
			c.log.Debugf(ctx, "Request: %s - %s", c.endpoint, string(bBody))
			req, err := http.NewRequest(http.MethodPost, c.endpoint, bytes.NewReader(bBody))
			if err != nil {
				return 0, err
			}
			if c.authToken != "" {
				req.AddCookie(&http.Cookie{
					Name:     "at",
					Value:    c.authToken,
				})
			}
			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				return 0, err
			}
			ball, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return resp.StatusCode, fmt.Errorf("Error reading body - in response from %v: %s", req.URL.String(), err)
			}
			c.log.Debugf(ctx, "Response: %s - %s", resp.Status, ball)
			gqlResp := &gqlResponse{}
			if resp.StatusCode == http.StatusOK {
				if err := json.NewDecoder(bytes.NewReader(ball)).Decode(gqlResp); err != nil {
					return resp.StatusCode, fmt.Errorf("Error parsing body into output: %s", err)
				}
				if len(gqlResp.Errors) != 0 {
					var allErrors string
					for i, err := range gqlResp.Errors {
						allErrors += fmt.Sprintf("%d. - %+v\n", i, err)
					}
					return resp.StatusCode, errors.New(allErrors)
				}
				decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
					DecodeHook: decoderHook,
					Result:     out,
				})
				if err != nil {
					return 0, err
				}
				mData, ok := gqlResp.Data[dataField].(map[string]interface{})
				if ok {
					if err := decoder.Decode(mData); err != nil {
						return resp.StatusCode, fmt.Errorf("Error parsing body into output: %s", err)
					}
				} else {
					sData, ok := gqlResp.Data[dataField].([]interface{})
					if ok {
						if err := decoder.Decode(sData); err != nil {
							return resp.StatusCode, fmt.Errorf("Error parsing body into output: %s", err)
						}
					} else {
						return resp.StatusCode, fmt.Errorf("unhandled response data type %T %+v", gqlResp.Data[dataField], gqlResp.Data[dataField])
					}
				}

				return resp.StatusCode, nil
			}
			return resp.StatusCode, fmt.Errorf("Non 200 Response (%d) from %s: %s", resp.StatusCode, req.URL, string(ball))
		}
	`)
}
<commit_msg>add support for global request header management<commit_after>package main

import (
	"fmt"
	"sort"
	"strings"

	"github.com/sprucehealth/graphql/language/ast"
)

func generateClient(g *generator) {
	imports := []string{"github.com/sprucehealth/graphql"}
	if len(g.cfg.Resolvers) != 0 {
		imports = []string{
			"context",
			"fmt",
			"reflect",
			"",
			"github.com/sprucehealth/graphql",
			"github.com/sprucehealth/graphql/gqldecode",
			"github.com/sprucehealth/graphql/gqlerrors",
			"github.com/sprucehealth/graphql/language/parser",
			"github.com/sprucehealth/graphql/language/printer",
			"github.com/sprucehealth/graphql/language/source",
			"github.com/sprucehealth/mapstructure",
		}
	}

	g.printf("package client\n\n")
	g.printf("import (\n")
	for _, im := range imports {
		if im == "" {
			g.printf("\n")
		} else {
			g.printf("\t%q\n", im)
		}
	}
	g.printf(")\n\n")

	clientTypes := make(map[string]struct{})
	for _, t := range strings.Split(*flagClientTypes, ",") {
		clientTypes[t] = struct{}{}
	}

	var clientTypeDefs []*ast.ObjectDefinition
	for _, def := range g.doc.Definitions {
		switch def := def.(type) {
		case *ast.ObjectDefinition:
			if _, ok := clientTypes[def.Name.Value]; ok {
				clientTypeDefs = append(clientTypeDefs, def)
			}
			g.genObjectModel(def)
			g.printf("\n")
		case *ast.InterfaceDefinition:
			g.genInterfaceModel(def)
			g.printf("\n")
		case *ast.InputObjectDefinition:
			g.genInputModel(def)
			g.printf("\n")
		case *ast.UnionDefinition:
			g.genUnionModel(def)
			g.printf("\n")
		case *ast.EnumDefinition:
			g.genEnumConstants(def)
			g.printf("\n")
		}
	}

	var sigs []string
	for _, def := range clientTypeDefs {
		for _, field := range def.Fields {
			sigs = append(sigs, renderSignatureForField(g, def, field))
		}
	}
	sort.Strings(sigs)
	g.printf("type Client interface {\n")
	for _, sig := range sigs {
		g.printf(sig + "\n")
	}
	g.printf("}\n")
	g.print(`
		type client struct {
			endpoint string
			authToken string
			headers map[string][]string
			log Logger
		}

		type clientOption func(c *client)

		func WithClientLogger(l Logger) clientOption {
			return func(c *client) {
				c.log = l
			}
		}

		func WithHeaders(h map[string][]string) clientOption {
			return func(c *client) {
				c.headers = h
			}
		}

		type Logger interface {
			Debugf(ctx context.Context, msg string, v ...interface{})
		}

		type nullLogger struct{}
		func (nullLogger) Debugf(ctx context.Context, msg string, v ...interface{}){}

		func New(endpoint, authToken string, opts ...clientOption) Client {
			c := &client{
				endpoint: endpoint,
				authToken: authToken,
				headers: make(map[string][]string),
				log: nullLogger{},
			}
			for _, o := range opts {
				o(c)
			}
			return c
		}
	`)
	for _, def := range clientTypeDefs {
		for _, field := range def.Fields {
			genClientMethodForField(g, def, field)
			g.printf("\n")
		}
	}
	genDecoderTypeMap(g)
	g.printf("\n")
	genDecoderHook(g)
	g.printf("\n")
	genQueryWrapperTypes(g)
	g.printf("\n")
	genClientDo(g)
	g.printf("\n")
	genRewriteQuery(g)
	g.printf("\n")
}

func renderSignatureForField(g *generator, d *ast.ObjectDefinition, f *ast.FieldDefinition) string {
	return fmt.Sprintf("%s%s(ctx context.Context, query string) (%s, error)", exportedName(d.Name.Value), exportedName(f.Name.Value), g.goType(f.Type, ""))
}

func genDecoderHook(g *generator) {
	g.printf("func decoderHook(from reflect.Kind, to reflect.Kind, v interface{}) (interface{}, error) {\n")
	g.printf("\tif from == reflect.Map && to == reflect.Interface {\n")
	g.printf("\t\tcVal := reflect.New(objectTypesByTypename[v.(map[string]interface{})[\"__typename\"].(string)])\n")
	g.printf("\t\tout := cVal.Elem().Addr().Interface()\n")
	g.printf("\t\tdecoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{\n")
	g.printf("\t\t\tDecodeHook: decoderHook,\n")
	g.printf("\t\t\tResult:     out,\n")
	g.printf("\t\t})\n")
	g.printf("\t\tif err != nil {\n")
	g.printf("\t\t\treturn 0, err\n")
	g.printf("\t\t}\n")
	g.printf("\t\tif err := decoder.Decode(v.(map[string]interface{})); err != nil {\n")
	g.print("\t\t\treturn nil, fmt.Errorf(\"Error decoding %+v into %+v\", v, out)\n")
	g.printf("\t\t}\n")
	g.printf("\t\treturn out, nil\n")
	g.printf("\t}\n")
	g.printf("\treturn v, nil\n")
	g.printf("}\n")
}

func genDecoderTypeMap(g *generator) {
	g.printf("var objectTypesByTypename = map[string]reflect.Type{\n")
	for _, def := range g.doc.Definitions {
		switch def := def.(type) {
		case *ast.ObjectDefinition:
			g.printf("\"%s\": reflect.TypeOf(%s{}),\n", def.Name.Value, def.Name.Value)
		}
	}
	g.printf("}\n")
}

func genOutputTypeVar(g *generator, f *ast.FieldDefinition) {
	outType := g.goType(f.Type, "")
	if outType[0] == '*' {
		outType = outType[1:]
	}
	g.printf("\tvar out %s\n", outType)
}

func outputTypeReturn(g *generator, f *ast.FieldDefinition) string {
	outType := g.goType(f.Type, "")
	if outType[0] == '*' {
		return "&out"
	}
	return "out"
}

func genClientMethodForField(g *generator, d *ast.ObjectDefinition, f *ast.FieldDefinition) {
	g.printf("func (c *client) %s {\n", renderSignatureForField(g, d, f))
	g.printf("\tc.log.Debugf(ctx, \"%s%s\")\n", exportedName(d.Name.Value), exportedName(f.Name.Value))
	genOutputTypeVar(g, f)
	g.printf("\tif _, err := c.do(ctx, \"%s\", query, &out); err != nil {\n", unexportedName(f.Name.Value))
	g.printf("\t\treturn %s, err\n", renderDefaultReturnValue(f.Type))
	g.printf("\t}\n")
	g.printf("\treturn %s, nil", outputTypeReturn(g, f))
	g.printf("}\n")
}

func renderDefaultClientReturnValue(t ast.Type) string {
	switch t := t.(type) {
	case *ast.NonNull:
		return renderDefaultClientReturnValue(t.Type)
	case *ast.Named:
		switch t.Name.Value {
		case "ID", "String":
			return `""`
		case "Boolean":
			return "false"
		case "Float", "Int":
			return "0"
		}
	}
	return "nil"
}

func genQueryWrapperTypes(g *generator) {
	g.print(`
		type gqlRequestBody struct {
			Query string ` + "`json:\"query\"`" + `
		}

		type gqlResponse struct {
			Data   map[string]interface{}   ` + "`json:\"data\"`" + `
			Errors []map[string]interface{} ` + "`json:\"errors\"`" + `
		}
	`)
}

func genRewriteQuery(g *generator) {
	g.print(`
		func rewriteQuery(query string) (string, error) {
			qast, err := parser.Parse(parser.ParseParams{
				Source: source.New("GraphQL Query", query),
			})
			if err != nil {
				return "", err
			}
			graphql.RequestTypeNames(qast)
			return printer.Print(qast), nil
		}
	`)
}

func genClientDo(g *generator) {
	g.print(`
		func (c *client) do(ctx context.Context, dataField, query string, out interface{}) (int, error) {
			query, err := rewriteQuery(query)
			if err != nil {
				return 0, err
			}
			rb := &gqlRequestBody{Query: query}
			bBody, err := json.Marshal(rb)
			if err != nil {
				return 0, err
			}
			c.log.Debugf(ctx, "Request: %s - %s", c.endpoint, string(bBody))
			req, err := http.NewRequest(http.MethodPost, c.endpoint, bytes.NewReader(bBody))
			if err != nil {
				return 0, err
			}
			if c.authToken != "" {
				req.AddCookie(&http.Cookie{
					Name:     "at",
					Value:    c.authToken,
				})
			}
			for h, hvs := range c.headers {
				for _, hv := range hvs {
					req.Header.Add(h, hv)
				}
			}
			resp, err := http.DefaultClient.Do(req)
			if err != nil {
				return 0, err
			}
			ball, err := ioutil.ReadAll(resp.Body)
			if err != nil {
				return resp.StatusCode, fmt.Errorf("Error reading body - in response from %v: %s", req.URL.String(), err)
			}
			c.log.Debugf(ctx, "Response: %s - %s", resp.Status, ball)
			gqlResp := &gqlResponse{}
			if resp.StatusCode == http.StatusOK {
				if err := json.NewDecoder(bytes.NewReader(ball)).Decode(gqlResp); err != nil {
					return resp.StatusCode, fmt.Errorf("Error parsing body into output: %s", err)
				}
				if len(gqlResp.Errors) != 0 {
					var allErrors string
					for i, err := range gqlResp.Errors {
						allErrors += fmt.Sprintf("%d. - %+v\n", i, err)
					}
					return resp.StatusCode, errors.New(allErrors)
				}
				decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
					DecodeHook: decoderHook,
					Result:     out,
				})
				if err != nil {
					return 0, err
				}
				mData, ok := gqlResp.Data[dataField].(map[string]interface{})
				if ok {
					if err := decoder.Decode(mData); err != nil {
						return resp.StatusCode, fmt.Errorf("Error parsing body into output: %s", err)
					}
				} else {
					sData, ok := gqlResp.Data[dataField].([]interface{})
					if ok {
						if err := decoder.Decode(sData); err != nil {
							return resp.StatusCode, fmt.Errorf("Error parsing body into output: %s", err)
						}
					} else {
						return resp.StatusCode, fmt.Errorf("unhandled response data type %T %+v", gqlResp.Data[dataField], gqlResp.Data[dataField])
					}
				}

				return resp.StatusCode, nil
			}
			return resp.StatusCode, fmt.Errorf("Non 200 Response (%d) from %s: %s", resp.StatusCode, req.URL, string(ball))
		}
	`)
}
<|endoftext|>


<commit_before>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/names"
	"launchpad.net/gnuflag"

	actionapi "github.com/juju/juju/api/action"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/action"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

func newRunCommand() cmd.Command {
	return modelcmd.Wrap(&runCommand{})
}

// runCommand is responsible for running arbitrary commands on remote machines.
type runCommand struct {
	modelcmd.ModelCommandBase
	out      cmd.Output
	all      bool
	timeout  time.Duration
	machines []string
	services []string
	units    []string
	commands string
}

const runDoc = `
Run the commands on the specified targets.

Targets are specified using either machine ids, service names or unit
names.  At least one target specifier is needed.

Multiple values can be set for --machine, --service, and --unit by using
comma separated values.

If the target is a machine, the command is run as the "ubuntu" user on
the remote machine.

If the target is a service, the command is run on all units for that
service. For example, if there was a service "mysql" and that service
had two units, "mysql/0" and "mysql/1", then
  --service mysql
is equivalent to
  --unit mysql/0,mysql/1

Commands run for services or units are executed in a 'hook context' for
the unit.

--all is provided as a simple way to run the command on all the machines
in the model.  If you specify --all you cannot provide additional
targets.

`

func (c *runCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "run",
		Args:    "<commands>",
		Purpose: "run the commands on the remote targets specified",
		Doc:     runDoc,
	}
}

func (c *runCommand) SetFlags(f *gnuflag.FlagSet) {
	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
	f.BoolVar(&c.all, "all", false, "run the commands on all the machines")
	f.DurationVar(&c.timeout, "timeout", 5*time.Minute, "how long to wait before the remote command is considered to have failed")
	f.Var(cmd.NewStringsValue(nil, &c.machines), "machine", "one or more machine ids")
	f.Var(cmd.NewStringsValue(nil, &c.services), "service", "one or more service names")
	f.Var(cmd.NewStringsValue(nil, &c.units), "unit", "one or more unit ids")
}

func (c *runCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no commands specified")
	}
	c.commands, args = args[0], args[1:]

	if c.all {
		if len(c.machines) != 0 {
			return fmt.Errorf("You cannot specify --all and individual machines")
		}
		if len(c.services) != 0 {
			return fmt.Errorf("You cannot specify --all and individual services")
		}
		if len(c.units) != 0 {
			return fmt.Errorf("You cannot specify --all and individual units")
		}
	} else {
		if len(c.machines) == 0 && len(c.services) == 0 && len(c.units) == 0 {
			return fmt.Errorf("You must specify a target, either through --all, --machine, --service or --unit")
		}
	}

	var nameErrors []string
	for _, machineId := range c.machines {
		if !names.IsValidMachine(machineId) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid machine id", machineId))
		}
	}
	for _, service := range c.services {
		if !names.IsValidService(service) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid service name", service))
		}
	}
	for _, unit := range c.units {
		if !names.IsValidUnit(unit) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid unit name", unit))
		}
	}
	if len(nameErrors) > 0 {
		return fmt.Errorf("The following run targets are not valid:\n%s",
			strings.Join(nameErrors, "\n"))
	}

	return cmd.CheckEmpty(args)
}

// ConvertActionResults takes the results from the api and creates a map
// suitable for format converstion to YAML or JSON.
func ConvertActionResults(result params.ActionResult, query actionQuery) map[string]interface{} {
	values := make(map[string]interface{})
	values[query.receiver.receiverType] = query.receiver.tag.Id()
	if result.Error != nil {
		values["Error"] = result.Error.Error()
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Tag != query.actionTag.String() {
		values["Error"] = fmt.Sprintf("expected action tag %q, got %q", query.actionTag.String(), result.Action.Tag)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Receiver != query.receiver.tag.String() {
		values["Error"] = fmt.Sprintf("expected action receiver %q, got %q", query.receiver.tag.String(), result.Action.Receiver)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Message != "" {
		values["Message"] = result.Message
	}
	// We always want to have a string for stdout, but only show stderr,
	// code and error if they are there.
	if res, ok := result.Output["Stdout"].(string); ok {
		values["Stdout"] = res
		if res, ok := result.Output["StdoutEncoding"].(string); ok && res != "" {
			values["Stdout.encoding"] = res
		}
	} else {
		values["Stdout"] = ""
	}
	if res, ok := result.Output["Stderr"].(string); ok && res != "" {
		values["Stderr"] = res
		if res, ok := result.Output["StderrEncoding"].(string); ok && res != "" {
			values["Stderr.encoding"] = res
		}
	}
	if res, ok := result.Output["Code"].(string); ok {
		code, err := strconv.Atoi(res)
		if err == nil && code != 0 {
			values["ReturnCode"] = code
		}
	}
	return values
}

func (c *runCommand) Run(ctx *cmd.Context) error {
	client, err := getRunAPIClient(c)
	if err != nil {
		return err
	}
	defer client.Close()

	var runResults []params.ActionResult
	if c.all {
		runResults, err = client.RunOnAllMachines(c.commands, c.timeout)
	} else {
		params := params.RunParams{
			Commands: c.commands,
			Timeout:  c.timeout,
			Machines: c.machines,
			Services: c.services,
			Units:    c.units,
		}
		runResults, err = client.Run(params)
	}

	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	actionsToQuery := []actionQuery{}
	for _, result := range runResults {
		if result.Error != nil {
			fmt.Fprintf(ctx.GetStderr(), "couldn't queue one action: %v", result.Error)
			continue
		}
		actionTag, err := names.ParseActionTag(result.Action.Tag)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action tag %v for receiver %v", result.Action.Tag, result.Action.Receiver)
			continue
		}

		receiverTag, err := names.ActionReceiverFromTag(result.Action.Receiver)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action receiver tag %v for action %v", result.Action.Receiver, result.Action.Tag)
			continue
		}
		var receiverType string
		switch receiverTag.(type) {
		case names.UnitTag:
			receiverType = "UnitId"
		case names.MachineTag:
			receiverType = "MachineId"
		default:
			receiverType = "ReceiverId"
		}
		actionsToQuery = append(actionsToQuery, actionQuery{
			actionTag: actionTag,
			receiver: actionReceiver{
				receiverType: receiverType,
				tag:          receiverTag,
			}})
	}

	if len(actionsToQuery) == 0 {
		return errors.New("no actions were successfully enqueued, aborting")
	}

	values := []interface{}{}
	for len(actionsToQuery) > 0 {
		actionResults, err := client.Actions(entities(actionsToQuery))
		if err != nil {
			return errors.Trace(err)
		}

		newActionsToQuery := []actionQuery{}
		for i, result := range actionResults.Results {
			if result.Error == nil {
				switch result.Status {
				case params.ActionRunning, params.ActionPending:
					newActionsToQuery = append(newActionsToQuery, actionsToQuery[i])
					continue
				}
			}

			values = append(values, ConvertActionResults(result, actionsToQuery[i]))
		}

		actionsToQuery = newActionsToQuery

		// TODO: use a watcher instead of sleeping
		// this should be easier once we implement action grouping
		<-afterFunc(1 * time.Second)
	}

	// If we are just dealing with one result, AND we are using the smart
	// format, then pretend we were running it locally.
	if len(values) == 1 && c.out.Name() == "smart" {
		result, ok := values[0].(map[string]interface{})
		if !ok {
			return errors.New("couldn't read action output")
		}
		if res, ok := result["Error"].(string); ok {
			return errors.New(res)
		}
		ctx.Stdout.Write(formatOutput(result, "Stdout"))
		ctx.Stderr.Write(formatOutput(result, "Stderr"))
		if code, ok := result["ReturnCode"].(int); ok && code != 0 {
			return cmd.NewRcPassthroughError(code)
		}
		// Message should always contain only errors.
		if res, ok := result["Message"].(string); ok && res != "" {
			ctx.Stderr.Write([]byte(res))
		}

		return nil
	}

	return c.out.Write(ctx, values)
}

type actionReceiver struct {
	receiverType string
	tag          names.Tag
}

type actionQuery struct {
	receiver  actionReceiver
	actionTag names.ActionTag
}

// RunClient exposes the capabilities required by the CLI
type RunClient interface {
	action.APIClient
	RunOnAllMachines(commands string, timeout time.Duration) ([]params.ActionResult, error)
	Run(params.RunParams) ([]params.ActionResult, error)
}

// In order to be able to easily mock out the API side for testing,
// the API client is retrieved using a function.
var getRunAPIClient = func(c *runCommand) (RunClient, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return actionapi.NewClient(root), errors.Trace(err)
}

// getActionResult abstracts over the action CLI function that we use here to fetch results
var getActionResult = func(c RunClient, actionId string, wait *time.Timer) (params.ActionResult, error) {
	return action.GetActionResult(c, actionId, wait)
}

var afterFunc = func(d time.Duration) <-chan time.Time {
	return time.After(d)
}

// entities is a convenience constructor for params.Entities.
func entities(actions []actionQuery) params.Entities {
	entities := params.Entities{
		Entities: make([]params.Entity, len(actions)),
	}
	for i, action := range actions {
		entities.Entities[i].Tag = action.actionTag.String()
	}
	return entities
}

func formatOutput(results map[string]interface{}, key string) []byte {
	res, ok := results[key].(string)
	if !ok {
		return []byte("")
	}
	if enc, ok := results[key+".encoding"].(string); ok && enc != "" {
		switch enc {
		case "base64":
			decoded, err := base64.StdEncoding.DecodeString(res)
			if err != nil {
				return []byte("expected b64 encoded string, got " + res)
			}
			return decoded
		default:
			return []byte(res)
		}
	}
	return []byte(res)
}
<commit_msg>run: convert end lines from windows so they get rendered properly<commit_after>// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package commands

import (
	"encoding/base64"
	"fmt"
	"strconv"
	"strings"
	"time"

	"github.com/juju/cmd"
	"github.com/juju/errors"
	"github.com/juju/names"
	"launchpad.net/gnuflag"

	actionapi "github.com/juju/juju/api/action"
	"github.com/juju/juju/apiserver/params"
	"github.com/juju/juju/cmd/juju/action"
	"github.com/juju/juju/cmd/juju/block"
	"github.com/juju/juju/cmd/modelcmd"
)

func newRunCommand() cmd.Command {
	return modelcmd.Wrap(&runCommand{})
}

// runCommand is responsible for running arbitrary commands on remote machines.
type runCommand struct {
	modelcmd.ModelCommandBase
	out      cmd.Output
	all      bool
	timeout  time.Duration
	machines []string
	services []string
	units    []string
	commands string
}

const runDoc = `
Run the commands on the specified targets.

Targets are specified using either machine ids, service names or unit
names.  At least one target specifier is needed.

Multiple values can be set for --machine, --service, and --unit by using
comma separated values.

If the target is a machine, the command is run as the "ubuntu" user on
the remote machine.

If the target is a service, the command is run on all units for that
service. For example, if there was a service "mysql" and that service
had two units, "mysql/0" and "mysql/1", then
  --service mysql
is equivalent to
  --unit mysql/0,mysql/1

Commands run for services or units are executed in a 'hook context' for
the unit.

--all is provided as a simple way to run the command on all the machines
in the model.  If you specify --all you cannot provide additional
targets.

`

func (c *runCommand) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "run",
		Args:    "<commands>",
		Purpose: "run the commands on the remote targets specified",
		Doc:     runDoc,
	}
}

func (c *runCommand) SetFlags(f *gnuflag.FlagSet) {
	c.out.AddFlags(f, "smart", cmd.DefaultFormatters)
	f.BoolVar(&c.all, "all", false, "run the commands on all the machines")
	f.DurationVar(&c.timeout, "timeout", 5*time.Minute, "how long to wait before the remote command is considered to have failed")
	f.Var(cmd.NewStringsValue(nil, &c.machines), "machine", "one or more machine ids")
	f.Var(cmd.NewStringsValue(nil, &c.services), "service", "one or more service names")
	f.Var(cmd.NewStringsValue(nil, &c.units), "unit", "one or more unit ids")
}

func (c *runCommand) Init(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("no commands specified")
	}
	c.commands, args = args[0], args[1:]

	if c.all {
		if len(c.machines) != 0 {
			return fmt.Errorf("You cannot specify --all and individual machines")
		}
		if len(c.services) != 0 {
			return fmt.Errorf("You cannot specify --all and individual services")
		}
		if len(c.units) != 0 {
			return fmt.Errorf("You cannot specify --all and individual units")
		}
	} else {
		if len(c.machines) == 0 && len(c.services) == 0 && len(c.units) == 0 {
			return fmt.Errorf("You must specify a target, either through --all, --machine, --service or --unit")
		}
	}

	var nameErrors []string
	for _, machineId := range c.machines {
		if !names.IsValidMachine(machineId) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid machine id", machineId))
		}
	}
	for _, service := range c.services {
		if !names.IsValidService(service) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid service name", service))
		}
	}
	for _, unit := range c.units {
		if !names.IsValidUnit(unit) {
			nameErrors = append(nameErrors, fmt.Sprintf("  %q is not a valid unit name", unit))
		}
	}
	if len(nameErrors) > 0 {
		return fmt.Errorf("The following run targets are not valid:\n%s",
			strings.Join(nameErrors, "\n"))
	}

	return cmd.CheckEmpty(args)
}

// ConvertActionResults takes the results from the api and creates a map
// suitable for format converstion to YAML or JSON.
func ConvertActionResults(result params.ActionResult, query actionQuery) map[string]interface{} {
	values := make(map[string]interface{})
	values[query.receiver.receiverType] = query.receiver.tag.Id()
	if result.Error != nil {
		values["Error"] = result.Error.Error()
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Tag != query.actionTag.String() {
		values["Error"] = fmt.Sprintf("expected action tag %q, got %q", query.actionTag.String(), result.Action.Tag)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Action.Receiver != query.receiver.tag.String() {
		values["Error"] = fmt.Sprintf("expected action receiver %q, got %q", query.receiver.tag.String(), result.Action.Receiver)
		values["Action"] = query.actionTag.Id()
		return values
	}
	if result.Message != "" {
		values["Message"] = result.Message
	}
	// We always want to have a string for stdout, but only show stderr,
	// code and error if they are there.
	if res, ok := result.Output["Stdout"].(string); ok {
		values["Stdout"] = strings.Replace(res, "\r\n", "\n", -1)
		if res, ok := result.Output["StdoutEncoding"].(string); ok && res != "" {
			values["Stdout.encoding"] = res
		}
	} else {
		values["Stdout"] = ""
	}
	if res, ok := result.Output["Stderr"].(string); ok && res != "" {
		values["Stderr"] = strings.Replace(res, "\r\n", "\n", -1)
		if res, ok := result.Output["StderrEncoding"].(string); ok && res != "" {
			values["Stderr.encoding"] = res
		}
	}
	if res, ok := result.Output["Code"].(string); ok {
		code, err := strconv.Atoi(res)
		if err == nil && code != 0 {
			values["ReturnCode"] = code
		}
	}
	return values
}

func (c *runCommand) Run(ctx *cmd.Context) error {
	client, err := getRunAPIClient(c)
	if err != nil {
		return err
	}
	defer client.Close()

	var runResults []params.ActionResult
	if c.all {
		runResults, err = client.RunOnAllMachines(c.commands, c.timeout)
	} else {
		params := params.RunParams{
			Commands: c.commands,
			Timeout:  c.timeout,
			Machines: c.machines,
			Services: c.services,
			Units:    c.units,
		}
		runResults, err = client.Run(params)
	}

	if err != nil {
		return block.ProcessBlockedError(err, block.BlockChange)
	}

	actionsToQuery := []actionQuery{}
	for _, result := range runResults {
		if result.Error != nil {
			fmt.Fprintf(ctx.GetStderr(), "couldn't queue one action: %v", result.Error)
			continue
		}
		actionTag, err := names.ParseActionTag(result.Action.Tag)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action tag %v for receiver %v", result.Action.Tag, result.Action.Receiver)
			continue
		}

		receiverTag, err := names.ActionReceiverFromTag(result.Action.Receiver)
		if err != nil {
			fmt.Fprintf(ctx.GetStderr(), "got invalid action receiver tag %v for action %v", result.Action.Receiver, result.Action.Tag)
			continue
		}
		var receiverType string
		switch receiverTag.(type) {
		case names.UnitTag:
			receiverType = "UnitId"
		case names.MachineTag:
			receiverType = "MachineId"
		default:
			receiverType = "ReceiverId"
		}
		actionsToQuery = append(actionsToQuery, actionQuery{
			actionTag: actionTag,
			receiver: actionReceiver{
				receiverType: receiverType,
				tag:          receiverTag,
			}})
	}

	if len(actionsToQuery) == 0 {
		return errors.New("no actions were successfully enqueued, aborting")
	}

	values := []interface{}{}
	for len(actionsToQuery) > 0 {
		actionResults, err := client.Actions(entities(actionsToQuery))
		if err != nil {
			return errors.Trace(err)
		}

		newActionsToQuery := []actionQuery{}
		for i, result := range actionResults.Results {
			if result.Error == nil {
				switch result.Status {
				case params.ActionRunning, params.ActionPending:
					newActionsToQuery = append(newActionsToQuery, actionsToQuery[i])
					continue
				}
			}

			values = append(values, ConvertActionResults(result, actionsToQuery[i]))
		}

		actionsToQuery = newActionsToQuery

		// TODO: use a watcher instead of sleeping
		// this should be easier once we implement action grouping
		<-afterFunc(1 * time.Second)
	}

	// If we are just dealing with one result, AND we are using the smart
	// format, then pretend we were running it locally.
	if len(values) == 1 && c.out.Name() == "smart" {
		result, ok := values[0].(map[string]interface{})
		if !ok {
			return errors.New("couldn't read action output")
		}
		if res, ok := result["Error"].(string); ok {
			return errors.New(res)
		}
		ctx.Stdout.Write(formatOutput(result, "Stdout"))
		ctx.Stderr.Write(formatOutput(result, "Stderr"))
		if code, ok := result["ReturnCode"].(int); ok && code != 0 {
			return cmd.NewRcPassthroughError(code)
		}
		// Message should always contain only errors.
		if res, ok := result["Message"].(string); ok && res != "" {
			ctx.Stderr.Write([]byte(res))
		}

		return nil
	}

	return c.out.Write(ctx, values)
}

type actionReceiver struct {
	receiverType string
	tag          names.Tag
}

type actionQuery struct {
	receiver  actionReceiver
	actionTag names.ActionTag
}

// RunClient exposes the capabilities required by the CLI
type RunClient interface {
	action.APIClient
	RunOnAllMachines(commands string, timeout time.Duration) ([]params.ActionResult, error)
	Run(params.RunParams) ([]params.ActionResult, error)
}

// In order to be able to easily mock out the API side for testing,
// the API client is retrieved using a function.
var getRunAPIClient = func(c *runCommand) (RunClient, error) {
	root, err := c.NewAPIRoot()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return actionapi.NewClient(root), errors.Trace(err)
}

// getActionResult abstracts over the action CLI function that we use here to fetch results
var getActionResult = func(c RunClient, actionId string, wait *time.Timer) (params.ActionResult, error) {
	return action.GetActionResult(c, actionId, wait)
}

var afterFunc = func(d time.Duration) <-chan time.Time {
	return time.After(d)
}

// entities is a convenience constructor for params.Entities.
func entities(actions []actionQuery) params.Entities {
	entities := params.Entities{
		Entities: make([]params.Entity, len(actions)),
	}
	for i, action := range actions {
		entities.Entities[i].Tag = action.actionTag.String()
	}
	return entities
}

func formatOutput(results map[string]interface{}, key string) []byte {
	res, ok := results[key].(string)
	if !ok {
		return []byte("")
	}
	if enc, ok := results[key+".encoding"].(string); ok && enc != "" {
		switch enc {
		case "base64":
			decoded, err := base64.StdEncoding.DecodeString(res)
			if err != nil {
				return []byte("expected b64 encoded string, got " + res)
			}
			return decoded
		default:
			return []byte(res)
		}
	}
	return []byte(res)
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/siddontang/ledisdb/server"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

var configFile = flag.String("config", "/etc/ledis.json", "ledisdb config file")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()

	if len(*configFile) == 0 {
		panic("must use a config file")
	}

	cfg, err := server.NewConfigWithFile(*configFile)
	if err != nil {
		panic(err)
	}

	var app *server.App
	app, err = server.NewApp(cfg)
	if err != nil {
		panic(err)
	}

	sc := make(chan os.Signal, 1)
	signal.Notify(sc,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)

	go func() {
		<-sc

		app.Close()
	}()

	app.Run()
}
<commit_msg>use println instead of panic<commit_after>package main

import (
	"flag"
	"github.com/siddontang/ledisdb/server"
	"os"
	"os/signal"
	"runtime"
	"syscall"
)

var configFile = flag.String("config", "/etc/ledis.json", "ledisdb config file")

func main() {
	runtime.GOMAXPROCS(runtime.NumCPU())

	flag.Parse()

	if len(*configFile) == 0 {
		println("must use a config file")
		return
	}

	cfg, err := server.NewConfigWithFile(*configFile)
	if err != nil {
		println(err.Error())
		return
	}

	var app *server.App
	app, err = server.NewApp(cfg)
	if err != nil {
		println(err.Error())
		return
	}

	sc := make(chan os.Signal, 1)
	signal.Notify(sc,
		syscall.SIGHUP,
		syscall.SIGINT,
		syscall.SIGTERM,
		syscall.SIGQUIT)

	go func() {
		<-sc

		app.Close()
	}()

	app.Run()
}
<|endoftext|>


<commit_before>// Package uasurfer provides fast and reliable abstraction
// of HTTP User-Agent strings. The philosophy is to identify
// technologies that holds >1% market share, and to avoid
// expending resources and accuracy on guessing at esoteric UA
// strings.
package uasurfer

import "strings"

//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go

// DeviceType (int) returns a constant.
type DeviceType int

// A complete list of supported devices in the
// form of constants.
const (
	DeviceUnknown DeviceType = iota
	DeviceComputer
	DeviceTablet
	DevicePhone
	DeviceConsole
	DeviceWearable
	DeviceTV
)

// BrowserName (int) returns a constant.
type BrowserName int

// A complete list of supported web browsers in the
// form of constants.
const (
	BrowserUnknown BrowserName = iota
	BrowserChrome
	BrowserIE
	BrowserSafari
	BrowserFirefox
	BrowserAndroid
	BrowserOpera
	BrowserBlackberry
	BrowserUCBrowser
	BrowserSilk
	BrowserNokia
	BrowserNetFront
	BrowserQQ
	BrowserMaxthon
	BrowserSogouExplorer
	BrowserSpotify
	BrowserBot
)

// OSName (int) returns a constant.
type OSName int

// A complete list of supported OSes in the
// form of constants. For handling particular versions
// of operating systems (e.g. Windows 2000), see
// the README.md file.
const (
	OSUnknown OSName = iota
	OSWindowsPhone
	OSWindows
	OSMacOSX
	OSiOS
	OSAndroid
	OSBlackberry
	OSChromeOS
	OSKindle
	OSWebOS
	OSLinux
	OSPlaystation
	OSXbox
	OSNintendo
	OSBot
)

// Platform (int) returns a constant.
type Platform int

// A complete list of supported platforms in the
// form of constants. Many OSes report their
// true platform, such as Android OS being Linux
// platform.
const (
	PlatformUnknown Platform = iota
	PlatformWindows
	PlatformMac
	PlatformLinux
	PlatformiPad
	PlatformiPhone
	PlatformiPod
	PlatformBlackberry
	PlatformWindowsPhone
	PlatformPlaystation
	PlatformXbox
	PlatformNintendo
	PlatformBot
)

type Version struct {
	Major int
	Minor int
	Patch int
}

func (v Version) Less(c Version) bool {
	if v.Major < c.Major {
		return true
	}

	if v.Major > c.Major {
		return false
	}

	if v.Minor < c.Minor {
		return true
	}

	if v.Minor > c.Minor {
		return false
	}

	return v.Patch < c.Patch
}

type UserAgent struct {
	Browser    Browser
	OS         OS
	DeviceType DeviceType
}

type Browser struct {
	Name    BrowserName
	Version Version
}

type OS struct {
	Platform Platform
	Name     OSName
	Version  Version
}

// Reset resets the UserAgent to it's zero value
func (ua *UserAgent) Reset() {
	ua.Browser = Browser{}
	ua.OS = OS{}
	ua.DeviceType = DeviceUnknown
}

// Parse accepts a raw user agent (string) and returns the UserAgent.
func Parse(ua string) *UserAgent {
	dest := new(UserAgent)
	parse(ua, dest)
	return dest
}

// ParseUserAgent is the same as Parse, but populates the supplied UserAgent.
// It is the caller's responsibility to call Reset() on the UserAgent before
// passing it to this function.
func ParseUserAgent(ua string, dest *UserAgent) {
	parse(ua, dest)
}

func parse(ua string, dest *UserAgent) {
	ua = strings.ToLower(ua)
	switch {
	case len(ua) == 0:
		dest.OS.Platform = PlatformUnknown
		dest.OS.Name = OSUnknown
		dest.Browser.Name = BrowserUnknown
		dest.DeviceType = DeviceUnknown

	// stop on on first case returning true
	case dest.evalOS(ua):
	case dest.evalBrowserName(ua):
	default:
		dest.evalBrowserVersion(ua)
		dest.evalDevice(ua)
	}
}
<commit_msg>Fast path for ascii user agents shorter than 1024 characters. (#26)<commit_after>// Package uasurfer provides fast and reliable abstraction
// of HTTP User-Agent strings. The philosophy is to identify
// technologies that holds >1% market share, and to avoid
// expending resources and accuracy on guessing at esoteric UA
// strings.
package uasurfer

import "strings"

//go:generate stringer -type=DeviceType,BrowserName,OSName,Platform -output=const_string.go

// DeviceType (int) returns a constant.
type DeviceType int

// A complete list of supported devices in the
// form of constants.
const (
	DeviceUnknown DeviceType = iota
	DeviceComputer
	DeviceTablet
	DevicePhone
	DeviceConsole
	DeviceWearable
	DeviceTV
)

// BrowserName (int) returns a constant.
type BrowserName int

// A complete list of supported web browsers in the
// form of constants.
const (
	BrowserUnknown BrowserName = iota
	BrowserChrome
	BrowserIE
	BrowserSafari
	BrowserFirefox
	BrowserAndroid
	BrowserOpera
	BrowserBlackberry
	BrowserUCBrowser
	BrowserSilk
	BrowserNokia
	BrowserNetFront
	BrowserQQ
	BrowserMaxthon
	BrowserSogouExplorer
	BrowserSpotify
	BrowserBot
)

// OSName (int) returns a constant.
type OSName int

// A complete list of supported OSes in the
// form of constants. For handling particular versions
// of operating systems (e.g. Windows 2000), see
// the README.md file.
const (
	OSUnknown OSName = iota
	OSWindowsPhone
	OSWindows
	OSMacOSX
	OSiOS
	OSAndroid
	OSBlackberry
	OSChromeOS
	OSKindle
	OSWebOS
	OSLinux
	OSPlaystation
	OSXbox
	OSNintendo
	OSBot
)

// Platform (int) returns a constant.
type Platform int

// A complete list of supported platforms in the
// form of constants. Many OSes report their
// true platform, such as Android OS being Linux
// platform.
const (
	PlatformUnknown Platform = iota
	PlatformWindows
	PlatformMac
	PlatformLinux
	PlatformiPad
	PlatformiPhone
	PlatformiPod
	PlatformBlackberry
	PlatformWindowsPhone
	PlatformPlaystation
	PlatformXbox
	PlatformNintendo
	PlatformBot
)

type Version struct {
	Major int
	Minor int
	Patch int
}

func (v Version) Less(c Version) bool {
	if v.Major < c.Major {
		return true
	}

	if v.Major > c.Major {
		return false
	}

	if v.Minor < c.Minor {
		return true
	}

	if v.Minor > c.Minor {
		return false
	}

	return v.Patch < c.Patch
}

type UserAgent struct {
	Browser    Browser
	OS         OS
	DeviceType DeviceType
}

type Browser struct {
	Name    BrowserName
	Version Version
}

type OS struct {
	Platform Platform
	Name     OSName
	Version  Version
}

// Reset resets the UserAgent to it's zero value
func (ua *UserAgent) Reset() {
	ua.Browser = Browser{}
	ua.OS = OS{}
	ua.DeviceType = DeviceUnknown
}

// Parse accepts a raw user agent (string) and returns the UserAgent.
func Parse(ua string) *UserAgent {
	dest := new(UserAgent)
	parse(ua, dest)
	return dest
}

// ParseUserAgent is the same as Parse, but populates the supplied UserAgent.
// It is the caller's responsibility to call Reset() on the UserAgent before
// passing it to this function.
func ParseUserAgent(ua string, dest *UserAgent) {
	parse(ua, dest)
}

func parse(ua string, dest *UserAgent) {
	ua = normalise(ua)
	switch {
	case len(ua) == 0:
		dest.OS.Platform = PlatformUnknown
		dest.OS.Name = OSUnknown
		dest.Browser.Name = BrowserUnknown
		dest.DeviceType = DeviceUnknown

	// stop on on first case returning true
	case dest.evalOS(ua):
	case dest.evalBrowserName(ua):
	default:
		dest.evalBrowserVersion(ua)
		dest.evalDevice(ua)
	}
}

// normalise normalises the user supplied agent string so that
// we can more easily parse it.
func normalise(ua string) string {
	if len(ua) <= 1024 {
		var buf [1024]byte
		ascii := copyLower(buf[:len(ua)], ua)
		if !ascii {
			// Fall back for non ascii characters
			return strings.ToLower(ua)
		}
		return string(buf[:len(ua)])
	}
	// Fallback for unusually long strings
	return strings.ToLower(ua)
}

// copyLower copies a lowercase version of s to b. It assumes s contains only single byte characters
// and will panic if b is nil or is not long enough to contain all the bytes from s.
// It returns early with false if any characters were non ascii.
func copyLower(b []byte, s string) bool {
	for j := 0; j < len(s); j++ {
		c := s[j]
		if c > 127 {
			return false
		}

		if 'A' <= c && c <= 'Z' {
			c += 'a' - 'A'
		}

		b[j] = c
	}
	return true
}
<|endoftext|>


<commit_before>package main

import (
	"io/ioutil"
	"net/http"
	"os/exec"
	"strings"
	"sync"
	"time"
	"unicode"

	"fmt"

	"github.com/datawire/teleproxy/pkg/supervisor"
	rpc "github.com/gorilla/rpc/v2"
	"github.com/gorilla/rpc/v2/json2"
)

// getRPCServer returns an RPC server that locks around every call
func getRPCServer(p *supervisor.Process) *rpc.Server {
	serverLock := new(sync.Mutex)

	rpcServer := rpc.NewServer()
	rpcServer.RegisterCodec(json2.NewCodec(), "application/json")
	rpcServer.RegisterBeforeFunc(func(_ *rpc.RequestInfo) {
		serverLock.Lock()
	})
	rpcServer.RegisterAfterFunc(func(i *rpc.RequestInfo) {
		p.Logf("RPC call method=%s err=%v", i.Method, i.Error)
		serverLock.Unlock()
	})
	return rpcServer
}

func checkNetOverride() error {
	// Check by doing the equivalent of curl http://teleproxy/api/tables/
	// It's okay to create a new client each time because we don't want to
	// reuse connections.
	client := http.Client{Timeout: 3 * time.Second}
	res, err := client.Get(fmt.Sprintf(
		"http://teleproxy%d.cachebust.telepresence.io/api/tables",
		time.Now().Unix(),
	))
	if err != nil {
		return err
	}
	_, err = ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return err
	}
	return nil
}

// DaemonService is the RPC Service used to export Playpen Daemon functionality
// to the client
type DaemonService struct {
	p              *supervisor.Process
	network        Resource
	cluster        Resource
	bridge         Resource
	intercepts     []Resource
	interceptables []string
}

// MakeDaemonService creates a DaemonService object
func MakeDaemonService(p *supervisor.Process) (*DaemonService, error) {
	teleproxy := "/Users/ark3/datawire/bin/pp-teleproxy-darwin-amd64"
	netOverride, err := CheckedRetryingCommand(
		p,
		"netOverride",
		[]string{teleproxy, "-mode", "intercept"},
		&RunAsInfo{},
		checkNetOverride,
	)
	if err != nil {
		return nil, err
	}
	return &DaemonService{
		p:       p,
		network: netOverride,
	}, nil
}

// Status reports the current status of the daemon
func (d *DaemonService) Status(_ *http.Request, _ *EmptyArgs, reply *StringReply) error {
	res := new(strings.Builder)
	defer func() { reply.Message = res.String() }()

	if !d.network.IsOkay() {
		fmt.Fprintln(res, "Network overrides NOT established")
	}
	if d.cluster == nil {
		fmt.Fprintln(res, "Not connected")
		return nil
	}
	if d.cluster.IsOkay() {
		fmt.Fprintln(res, "Connected")
	} else {
		fmt.Fprintln(res, "Attempting to reconnect...")
	}
	fmt.Fprintf(res, "  Context:       %s (%s)\n", "{context}", "{server}")
	if d.bridge != nil && d.bridge.IsOkay() {
		fmt.Fprintln(res, "  Proxy:         ON (networking to the cluster is enabled)")
	} else {
		fmt.Fprintln(res, "  Proxy:         OFF (attempting to connect...)")
	}
	fmt.Fprintf(res, "  Interceptable: %d deployments\n", len(d.interceptables))
	fmt.Fprintf(res, "  Intercepts:    ? total, %d local\n", len(d.intercepts))
	return nil
}

// Connect the daemon to a cluster
func (d *DaemonService) Connect(_ *http.Request, args *ConnectArgs, reply *StringReply) error {
	cmdArgs := make([]string, 0, 3+len(args.KArgs))
	cmdArgs = append(cmdArgs, "kubectl", "get", "po")
	cmdArgs = append(cmdArgs, args.KArgs...)
	cmd := args.RAI.Command(d.p, cmdArgs...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		_, ok := err.(*exec.ExitError)
		if !ok {
			return err
		}
	}
	reply.Message = strings.TrimRightFunc(string(output), unicode.IsSpace)
	return nil
}

// Disconnect from the connected cluster
func (d *DaemonService) Disconnect(_ *http.Request, _ *EmptyArgs, reply *StringReply) error {
	reply.Message = "Not connected"
	return nil
}
<commit_msg>Factor out rstrip<commit_after>package main

import (
	"io/ioutil"
	"net/http"
	"os/exec"
	"strings"
	"sync"
	"time"
	"unicode"

	"fmt"

	"github.com/datawire/teleproxy/pkg/supervisor"
	rpc "github.com/gorilla/rpc/v2"
	"github.com/gorilla/rpc/v2/json2"
)

// TrimRightSpace returns a slice of the string s, with all trailing white space
// removed, as defined by Unicode.
func TrimRightSpace(s string) string {
	return strings.TrimRightFunc(s, unicode.IsSpace)
}

// getRPCServer returns an RPC server that locks around every call
func getRPCServer(p *supervisor.Process) *rpc.Server {
	serverLock := new(sync.Mutex)

	rpcServer := rpc.NewServer()
	rpcServer.RegisterCodec(json2.NewCodec(), "application/json")
	rpcServer.RegisterBeforeFunc(func(_ *rpc.RequestInfo) {
		serverLock.Lock()
	})
	rpcServer.RegisterAfterFunc(func(i *rpc.RequestInfo) {
		p.Logf("RPC call method=%s err=%v", i.Method, i.Error)
		serverLock.Unlock()
	})
	return rpcServer
}

func checkNetOverride() error {
	// Check by doing the equivalent of curl http://teleproxy/api/tables/
	// It's okay to create a new client each time because we don't want to
	// reuse connections.
	client := http.Client{Timeout: 3 * time.Second}
	res, err := client.Get(fmt.Sprintf(
		"http://teleproxy%d.cachebust.telepresence.io/api/tables",
		time.Now().Unix(),
	))
	if err != nil {
		return err
	}
	_, err = ioutil.ReadAll(res.Body)
	res.Body.Close()
	if err != nil {
		return err
	}
	return nil
}

// DaemonService is the RPC Service used to export Playpen Daemon functionality
// to the client
type DaemonService struct {
	p              *supervisor.Process
	network        Resource
	cluster        Resource
	bridge         Resource
	intercepts     []Resource
	interceptables []string
}

// MakeDaemonService creates a DaemonService object
func MakeDaemonService(p *supervisor.Process) (*DaemonService, error) {
	teleproxy := "/Users/ark3/datawire/bin/pp-teleproxy-darwin-amd64"
	netOverride, err := CheckedRetryingCommand(
		p,
		"netOverride",
		[]string{teleproxy, "-mode", "intercept"},
		&RunAsInfo{},
		checkNetOverride,
	)
	if err != nil {
		return nil, err
	}
	return &DaemonService{
		p:       p,
		network: netOverride,
	}, nil
}

// Status reports the current status of the daemon
func (d *DaemonService) Status(_ *http.Request, _ *EmptyArgs, reply *StringReply) error {
	res := new(strings.Builder)
	defer func() { reply.Message = TrimRightSpace(res.String()) }()

	if !d.network.IsOkay() {
		fmt.Fprintln(res, "Network overrides NOT established")
	}
	if d.cluster == nil {
		fmt.Fprintln(res, "Not connected")
		return nil
	}
	if d.cluster.IsOkay() {
		fmt.Fprintln(res, "Connected")
	} else {
		fmt.Fprintln(res, "Attempting to reconnect...")
	}
	fmt.Fprintf(res, "  Context:       %s (%s)\n", "{context}", "{server}")
	if d.bridge != nil && d.bridge.IsOkay() {
		fmt.Fprintln(res, "  Proxy:         ON (networking to the cluster is enabled)")
	} else {
		fmt.Fprintln(res, "  Proxy:         OFF (attempting to connect...)")
	}
	fmt.Fprintf(res, "  Interceptable: %d deployments\n", len(d.interceptables))
	fmt.Fprintf(res, "  Intercepts:    ? total, %d local\n", len(d.intercepts))
	return nil
}

// Connect the daemon to a cluster
func (d *DaemonService) Connect(_ *http.Request, args *ConnectArgs, reply *StringReply) error {
	cmdArgs := make([]string, 0, 3+len(args.KArgs))
	cmdArgs = append(cmdArgs, "kubectl", "get", "po")
	cmdArgs = append(cmdArgs, args.KArgs...)
	cmd := args.RAI.Command(d.p, cmdArgs...)
	output, err := cmd.CombinedOutput()
	if err != nil {
		_, ok := err.(*exec.ExitError)
		if !ok {
			return err
		}
	}
	reply.Message = TrimRightSpace(string(output))
	return nil
}

// Disconnect from the connected cluster
func (d *DaemonService) Disconnect(_ *http.Request, _ *EmptyArgs, reply *StringReply) error {
	reply.Message = "Not connected"
	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"encoding/json"
	"fmt"
	"log"
	"net/http"
	"os"
)

func main() {
	apiKey := os.Getenv("BHT_APIKEY")
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		word := s.Text()
		counter := 0
		response, err := http.Get("http://words.bighugelabs.com/api/2/" + apiKey + "/" + word + "/json")
		if err != nil {
			log.Fatalln("Failed when looking for synonyms for \""+word+"\"", err)
		}
		var data map[string]interface{}
		if json.NewDecoder(response.Body).Decode(&data) == nil {
			for _, valuesMap := range data {
				for _, values := range valuesMap.(map[string]interface{}) {
					for _, value := range values.([]interface{}) {
						fmt.Println(value)
						counter++
					}
				}
			}
		}
		if counter == 0 {
			log.Fatalln("Couldn't find any synonyms for \"" + word + "\"")
		}
	}
}
<commit_msg>implemented new thesaurus package<commit_after>package main

import (
	"bufio"
	"fmt"

	"log"
	"os"

	"github.com/matryer/goblueprints/chapter4/thesaurus"
)

func main() {
	apiKey := os.Getenv("BHT_APIKEY")
	thesaurus := &thesaurus.BigHugh{APIKey: apiKey}
	s := bufio.NewScanner(os.Stdin)
	for s.Scan() {
		word := s.Text()
		syns, err := thesaurus.Synonyms(word)
		if err != nil {
			log.Fatalln("Failed when looking for synonyms for \""+word+"\"", err)
		}
		if len(syns) == 0 {
			log.Fatalln("Couldn't find any synonyms for \"" + word + "\"")
		}
		for _, syn := range syns {
			fmt.Println(syn)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/restic/restic"
	"github.com/restic/restic/backend"
	"golang.org/x/crypto/ssh/terminal"
)

type CmdBackup struct {
	Parent string `short:"p" long:"parent"    description:"use this parent snapshot (default: not set)"`
}

func init() {
	_, err := parser.AddCommand("backup",
		"save file/directory",
		"The backup command creates a snapshot of a file or directory",
		&CmdBackup{})
	if err != nil {
		panic(err)
	}
}

func format_bytes(c uint64) string {
	b := float64(c)

	switch {
	case c > 1<<40:
		return fmt.Sprintf("%.3f TiB", b/(1<<40))
	case c > 1<<30:
		return fmt.Sprintf("%.3f GiB", b/(1<<30))
	case c > 1<<20:
		return fmt.Sprintf("%.3f MiB", b/(1<<20))
	case c > 1<<10:
		return fmt.Sprintf("%.3f KiB", b/(1<<10))
	default:
		return fmt.Sprintf("%dB", c)
	}
}

func format_seconds(sec uint64) string {
	hours := sec / 3600
	sec -= hours * 3600
	min := sec / 60
	sec -= min * 60
	if hours > 0 {
		return fmt.Sprintf("%d:%02d:%02d", hours, min, sec)
	}

	return fmt.Sprintf("%d:%02d", min, sec)
}

func format_duration(d time.Duration) string {
	sec := uint64(d / time.Second)
	return format_seconds(sec)
}

func print_tree2(indent int, t *restic.Tree) {
	for _, node := range t.Nodes {
		if node.Tree() != nil {
			fmt.Printf("%s%s/\n", strings.Repeat("  ", indent), node.Name)
			print_tree2(indent+1, node.Tree())
		} else {
			fmt.Printf("%s%s\n", strings.Repeat("  ", indent), node.Name)
		}
	}
}

func (cmd CmdBackup) Usage() string {
	return "DIR/FILE [snapshot-ID]"
}

func newScanProgress() *restic.Progress {
	if !terminal.IsTerminal(int(os.Stdout.Fd())) {
		return nil
	}

	p := restic.NewProgress(time.Second)
	p.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
		fmt.Printf("\x1b[2K\r[%s] %d directories, %d files, %s", format_duration(d), s.Dirs, s.Files, format_bytes(s.Bytes))
	}
	p.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
		fmt.Printf("\nDone in %s\n", format_duration(d))
	}

	return p
}

func newArchiveProgress(todo restic.Stat) *restic.Progress {
	if !terminal.IsTerminal(int(os.Stdout.Fd())) {
		return nil
	}

	archiveProgress := restic.NewProgress(time.Second)

	var bps, eta uint64
	itemsTodo := todo.Files + todo.Dirs

	archiveProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
		sec := uint64(d / time.Second)
		if todo.Bytes > 0 && sec > 0 && ticker {
			bps = s.Bytes / sec
			if s.Bytes >= todo.Bytes {
				eta = 0
			} else if bps > 0 {
				eta = (todo.Bytes - s.Bytes) / bps
			}
		}

		itemsDone := s.Files + s.Dirs
		percent := float64(s.Bytes) / float64(todo.Bytes) * 100
		if percent > 100 {
			percent = 100
		}

		fmt.Printf("\x1b[2K\r[%s] %3.2f%%  %s/s  %s / %s  %d / %d items  ETA %s",
			format_duration(d),
			percent,
			format_bytes(bps),
			format_bytes(s.Bytes), format_bytes(todo.Bytes),
			itemsDone, itemsTodo,
			format_seconds(eta))
	}

	archiveProgress.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
		sec := uint64(d / time.Second)
		fmt.Printf("\nduration: %s, %.2fMiB/s\n",
			format_duration(d),
			float64(todo.Bytes)/float64(sec)/(1<<20))
	}

	return archiveProgress
}

func (cmd CmdBackup) Execute(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage())
	}

	target := make([]string, 0, len(args))
	for _, d := range args {
		if a, err := filepath.Abs(d); err == nil {
			d = a
		}
		target = append(target, d)
	}

	s, err := OpenRepo()
	if err != nil {
		return err
	}

	var parentSnapshotID backend.ID

	if cmd.Parent != "" {
		parentSnapshotID, err = s.FindSnapshot(cmd.Parent)
		if err != nil {
			return fmt.Errorf("invalid id %q: %v", cmd.Parent, err)
		}

		fmt.Printf("found parent snapshot %v\n", parentSnapshotID)
	}

	fmt.Printf("scan %v\n", target)

	stat, err := restic.Scan(target, newScanProgress())

	// TODO: add filter
	// arch.Filter = func(dir string, fi os.FileInfo) bool {
	// 	return true
	// }

	arch, err := restic.NewArchiver(s)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}

	arch.Error = func(dir string, fi os.FileInfo, err error) error {
		// TODO: make ignoring errors configurable
		fmt.Fprintf(os.Stderr, "\x1b[2K\rerror for %s: %v\n", dir, err)
		return nil
	}

	fmt.Printf("loading blobs\n")
	err = arch.Preload()
	if err != nil {
		return err
	}

	_, id, err := arch.Snapshot(newArchiveProgress(stat), target, parentSnapshotID)
	if err != nil {
		return err
	}

	plen, err := s.PrefixLength(backend.Snapshot)
	if err != nil {
		return err
	}

	fmt.Printf("snapshot %s saved\n", id[:plen])

	return nil
}
<commit_msg>Shorten status output if terminal is small<commit_after>package main

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"time"

	"github.com/restic/restic"
	"github.com/restic/restic/backend"
	"golang.org/x/crypto/ssh/terminal"
)

type CmdBackup struct {
	Parent string `short:"p" long:"parent"    description:"use this parent snapshot (default: not set)"`
}

func init() {
	_, err := parser.AddCommand("backup",
		"save file/directory",
		"The backup command creates a snapshot of a file or directory",
		&CmdBackup{})
	if err != nil {
		panic(err)
	}
}

func format_bytes(c uint64) string {
	b := float64(c)

	switch {
	case c > 1<<40:
		return fmt.Sprintf("%.3f TiB", b/(1<<40))
	case c > 1<<30:
		return fmt.Sprintf("%.3f GiB", b/(1<<30))
	case c > 1<<20:
		return fmt.Sprintf("%.3f MiB", b/(1<<20))
	case c > 1<<10:
		return fmt.Sprintf("%.3f KiB", b/(1<<10))
	default:
		return fmt.Sprintf("%dB", c)
	}
}

func format_seconds(sec uint64) string {
	hours := sec / 3600
	sec -= hours * 3600
	min := sec / 60
	sec -= min * 60
	if hours > 0 {
		return fmt.Sprintf("%d:%02d:%02d", hours, min, sec)
	}

	return fmt.Sprintf("%d:%02d", min, sec)
}

func format_duration(d time.Duration) string {
	sec := uint64(d / time.Second)
	return format_seconds(sec)
}

func print_tree2(indent int, t *restic.Tree) {
	for _, node := range t.Nodes {
		if node.Tree() != nil {
			fmt.Printf("%s%s/\n", strings.Repeat("  ", indent), node.Name)
			print_tree2(indent+1, node.Tree())
		} else {
			fmt.Printf("%s%s\n", strings.Repeat("  ", indent), node.Name)
		}
	}
}

func (cmd CmdBackup) Usage() string {
	return "DIR/FILE [snapshot-ID]"
}

func newScanProgress() *restic.Progress {
	if !terminal.IsTerminal(int(os.Stdout.Fd())) {
		return nil
	}

	p := restic.NewProgress(time.Second)
	p.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
		fmt.Printf("\x1b[2K\r[%s] %d directories, %d files, %s", format_duration(d), s.Dirs, s.Files, format_bytes(s.Bytes))
	}
	p.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
		fmt.Printf("\nDone in %s\n", format_duration(d))
	}

	return p
}

func newArchiveProgress(todo restic.Stat) *restic.Progress {
	if !terminal.IsTerminal(int(os.Stdout.Fd())) {
		return nil
	}

	archiveProgress := restic.NewProgress(time.Second)

	var bps, eta uint64
	itemsTodo := todo.Files + todo.Dirs

	archiveProgress.OnUpdate = func(s restic.Stat, d time.Duration, ticker bool) {
		sec := uint64(d / time.Second)
		if todo.Bytes > 0 && sec > 0 && ticker {
			bps = s.Bytes / sec
			if s.Bytes >= todo.Bytes {
				eta = 0
			} else if bps > 0 {
				eta = (todo.Bytes - s.Bytes) / bps
			}
		}

		itemsDone := s.Files + s.Dirs
		percent := float64(s.Bytes) / float64(todo.Bytes) * 100
		if percent > 100 {
			percent = 100
		}

		status1 := fmt.Sprintf("[%s] %3.2f%%  %s/s  %s / %s  %d / %d items  ",
			format_duration(d),
			percent,
			format_bytes(bps),
			format_bytes(s.Bytes), format_bytes(todo.Bytes),
			itemsDone, itemsTodo)
		status2 := fmt.Sprintf("ETA %s ", format_seconds(eta))

		w, _, err := terminal.GetSize(int(os.Stdout.Fd()))
		if err == nil {
			if len(status1)+len(status2) > w {
				max := w - len(status2) - 4
				status1 = status1[:max] + "... "
			}
		}

		fmt.Printf("\x1b[2K\r%s%s", status1, status2)
	}

	archiveProgress.OnDone = func(s restic.Stat, d time.Duration, ticker bool) {
		sec := uint64(d / time.Second)
		fmt.Printf("\nduration: %s, %.2fMiB/s\n",
			format_duration(d),
			float64(todo.Bytes)/float64(sec)/(1<<20))
	}

	return archiveProgress
}

func (cmd CmdBackup) Execute(args []string) error {
	if len(args) == 0 {
		return fmt.Errorf("wrong number of parameters, Usage: %s", cmd.Usage())
	}

	target := make([]string, 0, len(args))
	for _, d := range args {
		if a, err := filepath.Abs(d); err == nil {
			d = a
		}
		target = append(target, d)
	}

	s, err := OpenRepo()
	if err != nil {
		return err
	}

	var parentSnapshotID backend.ID

	if cmd.Parent != "" {
		parentSnapshotID, err = s.FindSnapshot(cmd.Parent)
		if err != nil {
			return fmt.Errorf("invalid id %q: %v", cmd.Parent, err)
		}

		fmt.Printf("found parent snapshot %v\n", parentSnapshotID)
	}

	fmt.Printf("scan %v\n", target)

	stat, err := restic.Scan(target, newScanProgress())

	// TODO: add filter
	// arch.Filter = func(dir string, fi os.FileInfo) bool {
	// 	return true
	// }

	arch, err := restic.NewArchiver(s)
	if err != nil {
		fmt.Fprintf(os.Stderr, "err: %v\n", err)
	}

	arch.Error = func(dir string, fi os.FileInfo, err error) error {
		// TODO: make ignoring errors configurable
		fmt.Fprintf(os.Stderr, "\x1b[2K\rerror for %s: %v\n", dir, err)
		return nil
	}

	fmt.Printf("loading blobs\n")
	err = arch.Preload()
	if err != nil {
		return err
	}

	_, id, err := arch.Snapshot(newArchiveProgress(stat), target, parentSnapshotID)
	if err != nil {
		return err
	}

	plen, err := s.PrefixLength(backend.Snapshot)
	if err != nil {
		return err
	}

	fmt.Printf("snapshot %s saved\n", id[:plen])

	return nil
}
<|endoftext|>