<commit_before>package riak_explorer

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/basho-labs/riak-mesos/cepmd/cepm"
	"github.com/basho-labs/riak-mesos/process_manager"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"text/template"
)

type RiakExplorer struct {
	tempdir string
	port    int64
	pm      *process_manager.ProcessManager
}

type templateData struct {
	HTTPPort int64
	NodeName string
}

type advancedTemplateData struct {
	CEPMDPort int
}

func (re *RiakExplorer) NewRiakExplorerClient() *RiakExplorerClient {
	return NewRiakExplorerClient(fmt.Sprintf("localhost:%d", re.port))
}
func (re *RiakExplorer) TearDown() {
	re.pm.TearDown()
}

func decompress() string {
	assetPath := fmt.Sprintf("riak_explorer_%s_%s.tar.gz", runtime.GOOS, runtime.GOARCH)

	log.Info("Decompressing Riak Explorer: ", assetPath)

	asset, err := Asset(assetPath)
	if err != nil {
		log.Fatal(err)
	}
	bytesReader := bytes.NewReader(asset)

	gzReader, err := gzip.NewReader(bytesReader)

	if err != nil {
		log.Fatal("Error encountered decompressing riak explorer: ", err)
		os.Exit(1)
	}

	tr := tar.NewReader(gzReader)
	tempdir, err := ioutil.TempDir("./", "riak_explorer.")
	if err != nil {
		log.Fatal(err)
	}
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		} else if err != nil {
			log.Fatalln(err)
		}
		filename := filepath.Join(".", tempdir, hdr.Name)
		if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
			file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, os.FileMode(hdr.Mode))
			io.Copy(file, tr)
			if err != nil {
				log.Fatal(err)
			}
			if _, err := io.Copy(file, tr); err != nil {
				log.Fatal(err)
			}
			file.Close()
		} else if hdr.Typeflag == tar.TypeDir {
			err := os.Mkdir(filename, 0777)
			if err != nil {
				log.Fatalln(err)
			}
		} else if hdr.Typeflag == tar.TypeSymlink {
			if err := os.Symlink(hdr.Linkname, filename); err != nil {
				log.Fatal(err)
			}
		} else {
			log.Fatal("Experienced unknown tar file type: ", hdr.Typeflag)
		}
	}
	return tempdir
}
func (re *RiakExplorer) configure(port int64, nodename string) {
	data, err := Asset("riak_explorer.conf")
	if err != nil {
		log.Panic("Got error", err)
	}
	tmpl, err := template.New("test").Parse(string(data))

	if err != nil {
		log.Panic(err)
	}

	// Populate template data from the MesosTask
	vars := templateData{}

	vars.NodeName = nodename
	vars.HTTPPort = port
	configpath := filepath.Join(".", re.tempdir, "riak_explorer", "etc", "riak_explorer.conf")
	file, err := os.OpenFile(configpath, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0)

	if err != nil {
		log.Panic("Unable to open file: ", err)
	}

	err = tmpl.Execute(file, vars)

	if err != nil {
		log.Panic("Got error", err)
	}
}
func (re *RiakExplorer) configureAdvanced(cepmdPort int) {
	data, err := Asset("advanced.config")
	if err != nil {
		log.Panic("Got error", err)
	}
	tmpl, err := template.New("advanced").Parse(string(data))

	if err != nil {
		log.Panic(err)
	}

	// Populate template data from the MesosTask
	vars := advancedTemplateData{}
	vars.CEPMDPort = cepmdPort
	configpath := filepath.Join(".", re.tempdir, "riak_explorer", "etc", "advanced.config")
	file, err := os.OpenFile(configpath, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0)

	if err != nil {
		log.Panic("Unable to open file: ", err)
	}

	err = tmpl.Execute(file, vars)

	if err != nil {
		log.Panic("Got error", err)
	}
}

func NewRiakExplorer(port int64, nodename string, c *cepm.CEPM) (*RiakExplorer, error) {
	tempdir := decompress()
	exepath := filepath.Join(".", tempdir, "riak_explorer", "bin", "riak_explorer")

	args := []string{"console", "-noinput"}
	healthCheckFun := func() error {
		log.Info("Running healthcheck: ", port)
		_, err := NewRiakExplorerClient(fmt.Sprintf("localhost:%d", port)).Ping()
		log.Info("Healthcheck result ", err)
		return err
	}
	tearDownFun := func() {
		log.Info("Deleting all data in: ", tempdir)
		err := os.RemoveAll(tempdir)
		if err != nil {
			log.Error(err)
		}
	}
	re := &RiakExplorer{
		tempdir: tempdir,
		port:    port,
	}
	if c != nil {
		// This is gross -- we're passing "hidden" state by passing it through the unix environment variables.
		// Fix it -- we should convert the NewRiakExplorer into using a fluent pattern?
		libpath := filepath.Join(".", tempdir, "riak_explorer", "lib", "basho-patches")
		os.Mkdir(libpath, 0777)
		err := cepm.InstallInto(libpath)
		if err != nil {
			log.Panic(err)
		}
		args = append(args, "-no_epmd")
		re.configureAdvanced(c.GetPort())
	}
	re.configure(port, nodename)
	log.Debugf("Starting up Riak Explorer %v", exepath)
	var err error
	re.pm, err = process_manager.NewProcessManager(tearDownFun, exepath, args, healthCheckFun, nil)
	if err != nil {
		log.Error("Could not start Riak Explorer: ", err)
	}

	return re, err
}
<commit_msg>make riak explorer also able to chroot<commit_after>package riak_explorer

import (
	"archive/tar"
	"bytes"
	"compress/gzip"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"github.com/basho-labs/riak-mesos/cepmd/cepm"
	"github.com/basho-labs/riak-mesos/process_manager"
	"io"
	"io/ioutil"
	"os"
	"path/filepath"
	"runtime"
	"text/template"
)

type RiakExplorer struct {
	tempdir string
	port    int64
	pm      *process_manager.ProcessManager
}

type templateData struct {
	HTTPPort int64
	NodeName string
}

type advancedTemplateData struct {
	CEPMDPort int
}

func (re *RiakExplorer) NewRiakExplorerClient() *RiakExplorerClient {
	return NewRiakExplorerClient(fmt.Sprintf("localhost:%d", re.port))
}
func (re *RiakExplorer) TearDown() {
	re.pm.TearDown()
}

func decompress() string {
	assetPath := fmt.Sprintf("riak_explorer_%s_%s.tar.gz", runtime.GOOS, runtime.GOARCH)

	log.Info("Decompressing Riak Explorer: ", assetPath)

	asset, err := Asset(assetPath)
	if err != nil {
		log.Fatal(err)
	}
	bytesReader := bytes.NewReader(asset)

	gzReader, err := gzip.NewReader(bytesReader)

	if err != nil {
		log.Fatal("Error encountered decompressing riak explorer: ", err)
		os.Exit(1)
	}

	tr := tar.NewReader(gzReader)
	tempdir, err := ioutil.TempDir("./", "riak_explorer.")
	if err != nil {
		log.Fatal(err)
	}
	for {
		hdr, err := tr.Next()
		if err == io.EOF {
			// end of tar archive
			break
		} else if err != nil {
			log.Fatalln(err)
		}
		filename := filepath.Join(".", tempdir, hdr.Name)
		if hdr.Typeflag == tar.TypeReg || hdr.Typeflag == tar.TypeRegA {
			file, err := os.OpenFile(filename, os.O_WRONLY|os.O_CREATE, os.FileMode(hdr.Mode))
			io.Copy(file, tr)
			if err != nil {
				log.Fatal(err)
			}
			if _, err := io.Copy(file, tr); err != nil {
				log.Fatal(err)
			}
			file.Close()
		} else if hdr.Typeflag == tar.TypeDir {
			err := os.Mkdir(filename, 0777)
			if err != nil {
				log.Fatalln(err)
			}
		} else if hdr.Typeflag == tar.TypeSymlink {
			if err := os.Symlink(hdr.Linkname, filename); err != nil {
				log.Fatal(err)
			}
		} else {
			log.Fatal("Experienced unknown tar file type: ", hdr.Typeflag)
		}
	}
	return tempdir
}
func (re *RiakExplorer) configure(port int64, nodename string) {
	data, err := Asset("riak_explorer.conf")
	if err != nil {
		log.Panic("Got error", err)
	}
	tmpl, err := template.New("test").Parse(string(data))

	if err != nil {
		log.Panic(err)
	}

	// Populate template data from the MesosTask
	vars := templateData{}

	vars.NodeName = nodename
	vars.HTTPPort = port
	configpath := filepath.Join(".", re.tempdir, "rex_root", "riak_explorer", "etc", "riak_explorer.conf")
	file, err := os.OpenFile(configpath, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0)

	if err != nil {
		log.Panic("Unable to open file: ", err)
	}

	err = tmpl.Execute(file, vars)

	if err != nil {
		log.Panic("Got error", err)
	}
}
func (re *RiakExplorer) configureAdvanced(cepmdPort int) {
	data, err := Asset("advanced.config")
	if err != nil {
		log.Panic("Got error", err)
	}
	tmpl, err := template.New("advanced").Parse(string(data))

	if err != nil {
		log.Panic(err)
	}

	// Populate template data from the MesosTask
	vars := advancedTemplateData{}
	vars.CEPMDPort = cepmdPort
	configpath := filepath.Join(".", re.tempdir, "rex_root", "riak_explorer", "etc", "advanced.config")
	file, err := os.OpenFile(configpath, os.O_TRUNC|os.O_CREATE|os.O_RDWR, 0)

	if err != nil {
		log.Panic("Unable to open file: ", err)
	}

	err = tmpl.Execute(file, vars)

	if err != nil {
		log.Panic("Got error", err)
	}
}

func NewRiakExplorer(port int64, nodename string, c *cepm.CEPM) (*RiakExplorer, error) {
	tempdir := decompress()
	exepath := filepath.Join(".", tempdir, "rex_root", "riak_explorer", "bin", "riak_explorer")

	args := []string{"console", "-noinput"}
	healthCheckFun := func() error {
		log.Info("Running healthcheck: ", port)
		_, err := NewRiakExplorerClient(fmt.Sprintf("localhost:%d", port)).Ping()
		log.Info("Healthcheck result ", err)
		return err
	}
	tearDownFun := func() {
		log.Info("Deleting all data in: ", tempdir)
		err := os.RemoveAll(tempdir)
		if err != nil {
			log.Error(err)
		}
	}
	re := &RiakExplorer{
		tempdir: tempdir,
		port:    port,
	}
	if c != nil {
		// This is gross -- we're passing "hidden" state by passing it through the unix environment variables.
		// Fix it -- we should convert the NewRiakExplorer into using a fluent pattern?
		libpath := filepath.Join(".", tempdir, "rex_root", "riak_explorer", "lib", "basho-patches")
		os.Mkdir(libpath, 0777)
		err := cepm.InstallInto(libpath)
		if err != nil {
			log.Panic(err)
		}
		args = append(args, "-no_epmd")
		re.configureAdvanced(c.GetPort())
	}
	re.configure(port, nodename)
	log.Debugf("Starting up Riak Explorer %v", exepath)
	var err error
	chroot := filepath.Join(".", tempdir, "rex_root", "riak_explorer")
	re.pm, err = process_manager.NewProcessManager(tearDownFun, exepath, args, healthCheckFun, &chroot)
	if err != nil {
		log.Error("Could not start Riak Explorer: ", err)
	}

	return re, err
}
<|endoftext|>


<commit_before>// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func TestActivityService_ListStargazers(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", mediaTypeStarringPreview)
		testFormValues(t, r, values{
			"page": "2",
		})

		fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","user":{"id":1}}]`)
	})

	ctx := context.Background()
	stargazers, _, err := client.Activity.ListStargazers(ctx, "o", "r", &ListOptions{Page: 2})
	if err != nil {
		t.Errorf("Activity.ListStargazers returned error: %v", err)
	}

	want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int64(1)}}}
	if !cmp.Equal(stargazers, want) {
		t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want)
	}

	const methodName = "ListStargazers"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.ListStargazers(ctx, "\n", "\n", &ListOptions{Page: 2})
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.ListStargazers(ctx, "o", "r", &ListOptions{Page: 2})
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_ListStarred_authenticatedUser(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", "))
		fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`)
	})

	ctx := context.Background()
	repos, _, err := client.Activity.ListStarred(ctx, "", nil)
	if err != nil {
		t.Errorf("Activity.ListStarred returned error: %v", err)
	}

	want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(1)}}}
	if !cmp.Equal(repos, want) {
		t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
	}

	const methodName = "ListStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.ListStarred(ctx, "\n", nil)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.ListStarred(ctx, "", nil)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_ListStarred_specifiedUser(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", "))
		testFormValues(t, r, values{
			"sort":      "created",
			"direction": "asc",
			"page":      "2",
		})
		fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":2}}]`)
	})

	opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}}
	ctx := context.Background()
	repos, _, err := client.Activity.ListStarred(ctx, "u", opt)
	if err != nil {
		t.Errorf("Activity.ListStarred returned error: %v", err)
	}

	want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(2)}}}
	if !cmp.Equal(repos, want) {
		t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
	}

	const methodName = "ListStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.ListStarred(ctx, "\n", opt)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.ListStarred(ctx, "u", opt)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_ListStarred_invalidUser(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Activity.ListStarred(ctx, "%", nil)
	testURLParseError(t, err)
}

func TestActivityService_IsStarred_hasStar(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		w.WriteHeader(http.StatusNoContent)
	})

	ctx := context.Background()
	star, _, err := client.Activity.IsStarred(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.IsStarred returned error: %v", err)
	}
	if want := true; star != want {
		t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want)
	}

	const methodName = "IsStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.IsStarred(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.IsStarred(ctx, "o", "r")
		if got {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_IsStarred_noStar(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		w.WriteHeader(http.StatusNotFound)
	})

	ctx := context.Background()
	star, _, err := client.Activity.IsStarred(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.IsStarred returned error: %v", err)
	}
	if want := false; star != want {
		t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want)
	}

	const methodName = "IsStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.IsStarred(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.IsStarred(ctx, "o", "r")
		if got {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_IsStarred_invalidID(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Activity.IsStarred(ctx, "%", "%")
	testURLParseError(t, err)
}

func TestActivityService_Star(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")
	})

	ctx := context.Background()
	_, err := client.Activity.Star(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.Star returned error: %v", err)
	}

	const methodName = "Star"
	testBadOptions(t, methodName, func() (err error) {
		_, err = client.Activity.Star(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		return client.Activity.Star(ctx, "o", "r")
	})
}

func TestActivityService_Star_invalidID(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, err := client.Activity.Star(ctx, "%", "%")
	testURLParseError(t, err)
}

func TestActivityService_Unstar(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	ctx := context.Background()
	_, err := client.Activity.Unstar(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.Unstar returned error: %v", err)
	}

	const methodName = "Unstar"
	testBadOptions(t, methodName, func() (err error) {
		_, err = client.Activity.Unstar(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		return client.Activity.Unstar(ctx, "o", "r")
	})
}

func TestActivityService_Unstar_invalidID(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, err := client.Activity.Unstar(ctx, "%", "%")
	testURLParseError(t, err)
}
<commit_msg>Add test cases for JSON resource marshaling (#1915)<commit_after>// Copyright 2013 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"context"
	"fmt"
	"net/http"
	"strings"
	"testing"
	"time"

	"github.com/google/go-cmp/cmp"
)

func TestActivityService_ListStargazers(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/repos/o/r/stargazers", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", mediaTypeStarringPreview)
		testFormValues(t, r, values{
			"page": "2",
		})

		fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","user":{"id":1}}]`)
	})

	ctx := context.Background()
	stargazers, _, err := client.Activity.ListStargazers(ctx, "o", "r", &ListOptions{Page: 2})
	if err != nil {
		t.Errorf("Activity.ListStargazers returned error: %v", err)
	}

	want := []*Stargazer{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, User: &User{ID: Int64(1)}}}
	if !cmp.Equal(stargazers, want) {
		t.Errorf("Activity.ListStargazers returned %+v, want %+v", stargazers, want)
	}

	const methodName = "ListStargazers"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.ListStargazers(ctx, "\n", "\n", &ListOptions{Page: 2})
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.ListStargazers(ctx, "o", "r", &ListOptions{Page: 2})
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_ListStarred_authenticatedUser(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", "))
		fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":1}}]`)
	})

	ctx := context.Background()
	repos, _, err := client.Activity.ListStarred(ctx, "", nil)
	if err != nil {
		t.Errorf("Activity.ListStarred returned error: %v", err)
	}

	want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(1)}}}
	if !cmp.Equal(repos, want) {
		t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
	}

	const methodName = "ListStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.ListStarred(ctx, "\n", nil)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.ListStarred(ctx, "", nil)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_ListStarred_specifiedUser(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/users/u/starred", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		testHeader(t, r, "Accept", strings.Join([]string{mediaTypeStarringPreview, mediaTypeTopicsPreview}, ", "))
		testFormValues(t, r, values{
			"sort":      "created",
			"direction": "asc",
			"page":      "2",
		})
		fmt.Fprint(w, `[{"starred_at":"2002-02-10T15:30:00Z","repo":{"id":2}}]`)
	})

	opt := &ActivityListStarredOptions{"created", "asc", ListOptions{Page: 2}}
	ctx := context.Background()
	repos, _, err := client.Activity.ListStarred(ctx, "u", opt)
	if err != nil {
		t.Errorf("Activity.ListStarred returned error: %v", err)
	}

	want := []*StarredRepository{{StarredAt: &Timestamp{time.Date(2002, time.February, 10, 15, 30, 0, 0, time.UTC)}, Repository: &Repository{ID: Int64(2)}}}
	if !cmp.Equal(repos, want) {
		t.Errorf("Activity.ListStarred returned %+v, want %+v", repos, want)
	}

	const methodName = "ListStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.ListStarred(ctx, "\n", opt)
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.ListStarred(ctx, "u", opt)
		if got != nil {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want nil", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_ListStarred_invalidUser(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Activity.ListStarred(ctx, "%", nil)
	testURLParseError(t, err)
}

func TestActivityService_IsStarred_hasStar(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		w.WriteHeader(http.StatusNoContent)
	})

	ctx := context.Background()
	star, _, err := client.Activity.IsStarred(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.IsStarred returned error: %v", err)
	}
	if want := true; star != want {
		t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want)
	}

	const methodName = "IsStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.IsStarred(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.IsStarred(ctx, "o", "r")
		if got {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_IsStarred_noStar(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "GET")
		w.WriteHeader(http.StatusNotFound)
	})

	ctx := context.Background()
	star, _, err := client.Activity.IsStarred(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.IsStarred returned error: %v", err)
	}
	if want := false; star != want {
		t.Errorf("Activity.IsStarred returned %+v, want %+v", star, want)
	}

	const methodName = "IsStarred"
	testBadOptions(t, methodName, func() (err error) {
		_, _, err = client.Activity.IsStarred(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		got, resp, err := client.Activity.IsStarred(ctx, "o", "r")
		if got {
			t.Errorf("testNewRequestAndDoFailure %v = %#v, want false", methodName, got)
		}
		return resp, err
	})
}

func TestActivityService_IsStarred_invalidID(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, _, err := client.Activity.IsStarred(ctx, "%", "%")
	testURLParseError(t, err)
}

func TestActivityService_Star(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "PUT")
	})

	ctx := context.Background()
	_, err := client.Activity.Star(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.Star returned error: %v", err)
	}

	const methodName = "Star"
	testBadOptions(t, methodName, func() (err error) {
		_, err = client.Activity.Star(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		return client.Activity.Star(ctx, "o", "r")
	})
}

func TestActivityService_Star_invalidID(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, err := client.Activity.Star(ctx, "%", "%")
	testURLParseError(t, err)
}

func TestActivityService_Unstar(t *testing.T) {
	client, mux, _, teardown := setup()
	defer teardown()

	mux.HandleFunc("/user/starred/o/r", func(w http.ResponseWriter, r *http.Request) {
		testMethod(t, r, "DELETE")
	})

	ctx := context.Background()
	_, err := client.Activity.Unstar(ctx, "o", "r")
	if err != nil {
		t.Errorf("Activity.Unstar returned error: %v", err)
	}

	const methodName = "Unstar"
	testBadOptions(t, methodName, func() (err error) {
		_, err = client.Activity.Unstar(ctx, "\n", "\n")
		return err
	})

	testNewRequestAndDoFailure(t, methodName, client, func() (*Response, error) {
		return client.Activity.Unstar(ctx, "o", "r")
	})
}

func TestActivityService_Unstar_invalidID(t *testing.T) {
	client, _, _, teardown := setup()
	defer teardown()

	ctx := context.Background()
	_, err := client.Activity.Unstar(ctx, "%", "%")
	testURLParseError(t, err)
}

func TestStarredRepository_Marshal(t *testing.T) {
	testJSONMarshal(t, &StarredRepository{}, "{}")

	u := &StarredRepository{
		StarredAt: &Timestamp{referenceTime},
		Repository: &Repository{
			ID:   Int64(1),
			URL:  String("u"),
			Name: String("n"),
		},
	}

	want := `{
		"starred_at": ` + referenceTimeStr + `,
		"repo": {
			"id": 1,
			"url": "u",
			"name": "n"
		}
	}`

	testJSONMarshal(t, u, want)
}

func TestStargazer_Marshal(t *testing.T) {
	testJSONMarshal(t, &Stargazer{}, "{}")

	u := &Stargazer{
		StarredAt: &Timestamp{referenceTime},
		User: &User{
			Login:           String("l"),
			ID:              Int64(1),
			URL:             String("u"),
			AvatarURL:       String("a"),
			GravatarID:      String("g"),
			Name:            String("n"),
			Company:         String("c"),
			Blog:            String("b"),
			Location:        String("l"),
			Email:           String("e"),
			Hireable:        Bool(true),
			Bio:             String("b"),
			TwitterUsername: String("t"),
			PublicRepos:     Int(1),
			Followers:       Int(1),
			Following:       Int(1),
			CreatedAt:       &Timestamp{referenceTime},
			SuspendedAt:     &Timestamp{referenceTime},
		},
	}

	want := `{
		"starred_at": ` + referenceTimeStr + `,
		"user": {
			"login": "l",
			"id": 1,
			"avatar_url": "a",
			"gravatar_id": "g",
			"name": "n",
			"company": "c",
			"blog": "b",
			"location": "l",
			"email": "e",
			"hireable": true,
			"bio": "b",
			"twitter_username": "t",
			"public_repos": 1,
			"followers": 1,
			"following": 1,
			"created_at": ` + referenceTimeStr + `,
			"suspended_at": ` + referenceTimeStr + `,
			"url": "u"
		}
	}`

	testJSONMarshal(t, u, want)
}
<|endoftext|>


<commit_before>package grandcentral

import (
	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
	datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
	peer "github.com/jbenet/go-ipfs/p2p/peer"
	dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb"
	proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy"
	util "github.com/jbenet/go-ipfs/util"
	errors "github.com/jbenet/go-ipfs/util/debugerror"
)

// Server handles routing queries using a database backend
type Server struct {
	local           peer.ID
	datastore       datastore.ThreadSafeDatastore
	peerstore       peer.Peerstore
	*proxy.Loopback // so server can be injected into client
}

// NewServer creates a new GrandCentral routing Server
func NewServer(ds datastore.ThreadSafeDatastore, ps peer.Peerstore, local peer.ID) (*Server, error) {
	s := &Server{local, ds, ps, nil}
	s.Loopback = &proxy.Loopback{
		Handler: s,
		Local:   local,
	}
	return s, nil
}

// HandleLocalRequest implements the proxy.RequestHandler interface. This is
// where requests are received from the outside world.
func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Message) *dhtpb.Message {
	_, response := s.handleMessage(ctx, p, req) // ignore response peer. it's local.
	return response
}

// TODO extract backend. backend can be implemented with whatever database we desire
func (s *Server) handleMessage(
	ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) {

	//  FIXME threw everything into this switch statement to get things going.
	//  Once each operation is well-defined, extract pluggable backend so any
	//  database may be used.

	var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel())
	switch req.GetType() {

	case dhtpb.Message_GET_VALUE:
		dskey := util.Key(req.GetKey()).DsKey()
		val, err := s.datastore.Get(dskey)
		if err != nil {
			log.Debug(errors.Wrap(err))
			return "", nil
		}
		rawRecord, ok := val.([]byte)
		if !ok {
			log.Debugf("datastore had non byte-slice value for %v", dskey)
			return "", nil
		}
		if err := proto.Unmarshal(rawRecord, response.Record); err != nil {
			log.Debug("failed to unmarshal dht record from datastore")
			return "", nil
		}
		// TODO before merging: if we know any providers for the requested value, return those.
		return p, response

	case dhtpb.Message_PUT_VALUE:
		// TODO before merging: verifyRecord(req.GetRecord())
		data, err := proto.Marshal(req.GetRecord())
		if err != nil {
			log.Debug(err)
			return "", nil
		}
		dskey := util.Key(req.GetKey()).DsKey()
		if err := s.datastore.Put(dskey, data); err != nil {
			log.Debug(err)
			return "", nil
		}
		return p, req // TODO before merging: verify that we should return record

	case dhtpb.Message_FIND_NODE:
		p := s.peerstore.PeerInfo(peer.ID(req.GetKey()))
		pri := []dhtpb.PeerRoutingInfo{
			dhtpb.PeerRoutingInfo{
				PeerInfo: p,
				// Connectedness: TODO
			},
		}
		response.CloserPeers = dhtpb.PeerRoutingInfosToPBPeers(pri)
		return p.ID, response

	case dhtpb.Message_ADD_PROVIDER:
		for _, provider := range req.GetProviderPeers() {
			providerID := peer.ID(provider.GetId())
			if providerID != p {
				log.Debugf("provider message came from third-party %s", p)
				continue
			}
			for _, maddr := range provider.Addresses() {
				// FIXME do we actually want to store to peerstore
				s.peerstore.AddAddr(p, maddr, peer.TempAddrTTL)
			}
		}
		var providers []dhtpb.Message_Peer
		pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()})
		if v, err := s.datastore.Get(pkey); err == nil {
			if protopeers, ok := v.([]dhtpb.Message_Peer); ok {
				providers = append(providers, protopeers...)
			}
		}
		if err := s.datastore.Put(pkey, providers); err != nil {
			log.Debug(err)
			return "", nil
		}
		return "", nil

	case dhtpb.Message_GET_PROVIDERS:
		dskey := util.Key(req.GetKey()).DsKey()
		exists, err := s.datastore.Has(dskey)
		if err == nil && exists {
			pri := []dhtpb.PeerRoutingInfo{
				dhtpb.PeerRoutingInfo{
					// Connectedness: TODO how is connectedness defined for the local node
					PeerInfo: peer.PeerInfo{ID: s.local},
				},
			}
			response.ProviderPeers = append(response.ProviderPeers, dhtpb.PeerRoutingInfosToPBPeers(pri)...)
		}
		// FIXME(btc) is this how we want to persist this data?
		pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", req.GetKey()})
		if v, err := s.datastore.Get(pkey); err == nil {
			if protopeers, ok := v.([]dhtpb.Message_Peer); ok {
				for _, p := range protopeers {
					response.ProviderPeers = append(response.ProviderPeers, &p)
				}
			}
		}
		return p, response

	case dhtpb.Message_PING:
		return p, req
	default:
	}
	return "", nil
}

var _ proxy.RequestHandler = &Server{}
var _ proxy.Proxy = &Server{}
<commit_msg>fix(gcr/s) proto marshaling bugs<commit_after>package grandcentral

import (
	"fmt"

	context "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/go.net/context"
	proto "github.com/jbenet/go-ipfs/Godeps/_workspace/src/code.google.com/p/goprotobuf/proto"
	datastore "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-datastore"
	peer "github.com/jbenet/go-ipfs/p2p/peer"
	dhtpb "github.com/jbenet/go-ipfs/routing/dht/pb"
	proxy "github.com/jbenet/go-ipfs/routing/grandcentral/proxy"
	util "github.com/jbenet/go-ipfs/util"
	errors "github.com/jbenet/go-ipfs/util/debugerror"
)

// Server handles routing queries using a database backend
type Server struct {
	local           peer.ID
	datastore       datastore.ThreadSafeDatastore
	peerstore       peer.Peerstore
	*proxy.Loopback // so server can be injected into client
}

// NewServer creates a new GrandCentral routing Server
func NewServer(ds datastore.ThreadSafeDatastore, ps peer.Peerstore, local peer.ID) (*Server, error) {
	s := &Server{local, ds, ps, nil}
	s.Loopback = &proxy.Loopback{
		Handler: s,
		Local:   local,
	}
	return s, nil
}

// HandleLocalRequest implements the proxy.RequestHandler interface. This is
// where requests are received from the outside world.
func (s *Server) HandleRequest(ctx context.Context, p peer.ID, req *dhtpb.Message) *dhtpb.Message {
	_, response := s.handleMessage(ctx, p, req) // ignore response peer. it's local.
	return response
}

// TODO extract backend. backend can be implemented with whatever database we desire
func (s *Server) handleMessage(
	ctx context.Context, p peer.ID, req *dhtpb.Message) (peer.ID, *dhtpb.Message) {

	//  FIXME threw everything into this switch statement to get things going.
	//  Once each operation is well-defined, extract pluggable backend so any
	//  database may be used.

	var response = dhtpb.NewMessage(req.GetType(), req.GetKey(), req.GetClusterLevel())
	switch req.GetType() {

	case dhtpb.Message_GET_VALUE:
		rawRecord, err := getRoutingRecord(s.datastore, util.Key(req.GetKey()))
		if err != nil {
			return "", nil
		}
		response.Record = rawRecord
		// TODO before merging: if we know any providers for the requested value, return those.
		return p, response

	case dhtpb.Message_PUT_VALUE:
		// TODO before merging: verifyRecord(req.GetRecord())
		putRoutingRecord(s.datastore, util.Key(req.GetKey()), req.GetRecord())
		return p, req // TODO before merging: verify that we should return record

	case dhtpb.Message_FIND_NODE:
		p := s.peerstore.PeerInfo(peer.ID(req.GetKey()))
		pri := []dhtpb.PeerRoutingInfo{
			dhtpb.PeerRoutingInfo{
				PeerInfo: p,
				// Connectedness: TODO
			},
		}
		response.CloserPeers = dhtpb.PeerRoutingInfosToPBPeers(pri)
		return p.ID, response

	case dhtpb.Message_ADD_PROVIDER:
		storeProvidersToPeerstore(s.peerstore, p, req.GetProviderPeers())

		if err := putRoutingProviders(s.datastore, util.Key(req.GetKey()), req.GetProviderPeers()); err != nil {
			return "", nil
		}
		return "", nil

	case dhtpb.Message_GET_PROVIDERS:
		providers, err := getRoutingProviders(s.local, s.datastore, util.Key(req.GetKey()))
		if err != nil {
			return "", nil
		}
		response.ProviderPeers = providers
		return p, response

	case dhtpb.Message_PING:
		return p, req
	default:
	}
	return "", nil
}

var _ proxy.RequestHandler = &Server{}
var _ proxy.Proxy = &Server{}

func getRoutingRecord(ds datastore.Datastore, k util.Key) (*dhtpb.Record, error) {
	dskey := k.DsKey()
	val, err := ds.Get(dskey)
	if err != nil {
		return nil, errors.Wrap(err)
	}
	recordBytes, ok := val.([]byte)
	if !ok {
		return nil, fmt.Errorf("datastore had non byte-slice value for %v", dskey)
	}
	var record dhtpb.Record
	if err := proto.Unmarshal(recordBytes, &record); err != nil {
		return nil, errors.New("failed to unmarshal dht record from datastore")
	}
	return &record, nil
}

func putRoutingRecord(ds datastore.Datastore, k util.Key, value *dhtpb.Record) error {
	data, err := proto.Marshal(value)
	if err != nil {
		return err
	}
	dskey := k.DsKey()
	// TODO namespace
	if err := ds.Put(dskey, data); err != nil {
		return err
	}
	return nil
}

func putRoutingProviders(ds datastore.Datastore, k util.Key, providers []*dhtpb.Message_Peer) error {
	pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()})
	if v, err := ds.Get(pkey); err == nil {
		if msg, ok := v.([]byte); ok {
			var protomsg dhtpb.Message
			if err := proto.Unmarshal(msg, &protomsg); err != nil {
				log.Error("failed to unmarshal routing provider record. programmer error")
			} else {
				providers = append(providers, protomsg.ProviderPeers...)
			}
		}
	}
	var protomsg dhtpb.Message
	protomsg.ProviderPeers = providers
	data, err := proto.Marshal(&protomsg)
	if err != nil {
		return err
	}
	return ds.Put(pkey, data)
}

func storeProvidersToPeerstore(ps peer.Peerstore, p peer.ID, providers []*dhtpb.Message_Peer) {
	for _, provider := range providers {
		providerID := peer.ID(provider.GetId())
		if providerID != p {
			log.Errorf("provider message came from third-party %s", p)
			continue
		}
		for _, maddr := range provider.Addresses() {
			// as a router, we want to store addresses for peers who have provided
			ps.AddAddr(p, maddr, peer.AddressTTL)
		}
	}
}

func getRoutingProviders(local peer.ID, ds datastore.Datastore, k util.Key) ([]*dhtpb.Message_Peer, error) {
	var providers []*dhtpb.Message_Peer
	exists, err := ds.Has(k.DsKey()) // TODO store values in a local datastore?
	if err == nil && exists {
		pri := []dhtpb.PeerRoutingInfo{
			dhtpb.PeerRoutingInfo{
				// Connectedness: TODO how is connectedness defined for the local node
				PeerInfo: peer.PeerInfo{ID: local},
			},
		}
		providers = append(providers, dhtpb.PeerRoutingInfosToPBPeers(pri)...)
	}

	pkey := datastore.KeyWithNamespaces([]string{"routing", "providers", k.String()}) // TODO key fmt
	if v, err := ds.Get(pkey); err == nil {
		if data, ok := v.([]byte); ok {
			var msg dhtpb.Message
			if err := proto.Unmarshal(data, &msg); err != nil {
				return nil, err
			}
			providers = append(providers, msg.GetProviderPeers()...)
		}
	}
	return providers, nil
}
<|endoftext|>


<commit_before>package lex

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
)

type Lexer struct {
	r      *bufio.Reader
	token  Token
	line   string
	lineno int
	err    error
}

func New(r io.Reader) *Lexer {
	bufr := bufio.NewReader(r)
	line, err := bufr.ReadString('\n')

	return &Lexer{
		r:      bufr,
		token:  classify(line, err),
		lineno: 1,
		line:   line,
		err:    err,
	}
}

func (l *Lexer) Token() Token {
	return l.token
}

func (l *Lexer) Line() string {
	return l.line
}

func (l *Lexer) LineNumber() int {
	return l.lineno
}

func (l *Lexer) Error() error {
	return l.err
}

func (l *Lexer) Consume() {
	line, err := l.r.ReadString('\n')
	l.token = classify(line, err)
	l.line = line
	l.lineno++
	l.err = err
}

// Consumes and returns a data element.
//
// Returns nil and an error if not currently looking at a data token or
// an I/O error occurs.
func (l *Lexer) ConsumeData() ([]byte, error) {
	if l.Token() != DataTok {
		return nil, errors.New("not on a data token")
	}

	var count int64
	_, err := fmt.Sscanf(l.line, "data %d\n", &count)
	if err != nil {
		var delim string
		_, err = fmt.Sscanf(l.line, "data <<%s\n", &delim)
		if err == nil {
			data, err := l.consumeDelimitedData(delim)
			l.Consume()
			return data, err
		}
		return nil, fmt.Errorf("invalid data byte count: %v", err)
	}

	data := make([]byte, count)

	_, err = io.ReadFull(l.r, data)
	if err != nil {
		return nil, err
		// return fmt.Errorf("could not read all of data: %v", err)
	}

	l.lineno += bytes.Count(data, []byte{'\n'})

	l.Consume()

	return data, nil
}

func (l *Lexer) consumeDelimitedData(delimStr string) ([]byte, error) {
	delim := make([]byte, len(delimStr)+1)
	copy(delim, delimStr)
	delim[len(delimStr)] = '\n'

	var data []byte
	for {
		line, err := l.r.ReadBytes('\n')
		if line[len(line)-1] == '\n' {
			l.lineno++
		}
		if bytes.Equal(line, delim) {
			return data, err
		}
		data = append(data, line...)
		if err != nil {
			return data, err
		}
	}
}

func classify(line string, err error) Token {
	if err != nil && len(line) == 0 {
		if err == io.EOF {
			return EOFTok
		}
		return ErrTok
	}

	if len(line) == 0 || len(line) == 1 && line[0] == '\n' {
		return EmptyTok
	}

	if line[0] == '#' {
		return CommentTok
	}

	i := strings.IndexAny(line, " \n")
	if i == -1 || line[len(line)-1] != '\n' {
		return InvalidTok
	}

	cmd := line[:i]
	tok, ok := tokenMap[cmd]
	if !ok {
		return InvalidTok
	}
	return tok
}
<commit_msg>Add fields to lexer.<commit_after>package lex

import (
	"bufio"
	"bytes"
	"errors"
	"fmt"
	"io"
	"strings"
)

type Lexer struct {
	r      *bufio.Reader
	token  Token
	line   string
	lineno int
	fields []string
	err    error
}

func New(r io.Reader) *Lexer {
	bufr := bufio.NewReader(r)
	line, err := bufr.ReadString('\n')

	return &Lexer{
		r:      bufr,
		token:  classify(line, err),
		lineno: 1,
		line:   line,
		err:    err,
	}
}

func (l *Lexer) Token() Token {
	return l.token
}

func (l *Lexer) Line() string {
	return l.line
}

func (l *Lexer) LineNumber() int {
	return l.lineno
}

func (l *Lexer) Field(i int) string {
	return l.fields[i]
}

func (l *Lexer) Fields() []string {
	return l.fields
}

func (l *Lexer) Error() error {
	return l.err
}

func (l *Lexer) Consume() {
	line, err := l.r.ReadString('\n')
	l.token = classify(line, err)
	l.line = line
	l.lineno++
	l.fields = strings.Split(line, " ")
	l.err = err
}

// Consumes and returns a data element.
//
// Returns nil and an error if not currently looking at a data token or
// an I/O error occurs.
func (l *Lexer) ConsumeData() ([]byte, error) {
	if l.Token() != DataTok {
		return nil, errors.New("not on a data token")
	}

	var count int64
	_, err := fmt.Sscanf(l.line, "data %d\n", &count)
	if err != nil {
		var delim string
		_, err = fmt.Sscanf(l.line, "data <<%s\n", &delim)
		if err == nil {
			data, err := l.consumeDelimitedData(delim)
			l.Consume()
			return data, err
		}
		return nil, fmt.Errorf("invalid data byte count: %v", err)
	}

	data := make([]byte, count)

	_, err = io.ReadFull(l.r, data)
	if err != nil {
		return nil, err
		// return fmt.Errorf("could not read all of data: %v", err)
	}

	l.lineno += bytes.Count(data, []byte{'\n'})

	l.Consume()

	return data, nil
}

func (l *Lexer) consumeDelimitedData(delimStr string) ([]byte, error) {
	delim := make([]byte, len(delimStr)+1)
	copy(delim, delimStr)
	delim[len(delimStr)] = '\n'

	var data []byte
	for {
		line, err := l.r.ReadBytes('\n')
		if line[len(line)-1] == '\n' {
			l.lineno++
		}
		if bytes.Equal(line, delim) {
			return data, err
		}
		data = append(data, line...)
		if err != nil {
			return data, err
		}
	}
}

func classify(line string, err error) Token {
	if err != nil && len(line) == 0 {
		if err == io.EOF {
			return EOFTok
		}
		return ErrTok
	}

	if len(line) == 0 || len(line) == 1 && line[0] == '\n' {
		return EmptyTok
	}

	if line[0] == '#' {
		return CommentTok
	}

	i := strings.IndexAny(line, " \n")
	if i == -1 || line[len(line)-1] != '\n' {
		return InvalidTok
	}

	cmd := line[:i]
	tok, ok := tokenMap[cmd]
	if !ok {
		return InvalidTok
	}
	return tok
}
<|endoftext|>


<commit_before>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"errors"
	"fmt"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdTeamRename struct {
	PrevName  keybase1.TeamName
	NewName   keybase1.TeamName
	SessionID int
	libkb.Contextified
}

func (v *CmdTeamRename) ParseArgv(ctx *cli.Context) (err error) {
	if len(ctx.Args()) != 2 {
		return errors.New("two team names required")
	}

	v.PrevName, err = keybase1.TeamNameFromString(ctx.Args()[0])
	if err != nil {
		return fmt.Errorf("invalid old team name: %v", err)
	}

	v.NewName, err = keybase1.TeamNameFromString(ctx.Args()[1])
	if err != nil {
		return fmt.Errorf("invalid new team name: %v", err)
	}

	return nil
}

// Most of the checks are in the teams package.
// This just covers this one presentation thing:
//
// > keybase team change-name aviato.usa.marketing aviato.america.sales
// Error! You must first rename `aviato.usa` to `aviato.america`.
func (v *CmdTeamRename) helpRenameWrongLevel() (err error) {
	if v.PrevName.Depth() != v.NewName.Depth() {
		return nil
	}
	if v.PrevName.Depth() < 3 {
		return nil
	}
	// Scan the parts of both names looking for the first difference.
	// Skip the root and the end. Because the root is not this case,
	// and the end is not a problem.
	// If it is in the middle then tell the user what to do.
	for i := 1; i < len(v.PrevName.Parts)-1; i++ {
		prevPart := v.PrevName.Parts[i]
		newPart := v.NewName.Parts[i]
		if !prevPart.Eq(newPart) {
			a1 := keybase1.TeamName{Parts: v.PrevName.Parts[:i+1]}
			a2 := keybase1.TeamName{Parts: v.NewName.Parts[:i+1]}
			return fmt.Errorf("You must first rename `%v` to `%v`", a1, a2)
		}
	}
	return nil
}

func (v *CmdTeamRename) Run() (err error) {
	cli, err := GetTeamsClient(v.G())
	if err != nil {
		return err
	}

	err = v.helpRenameWrongLevel()
	if err != nil {
		return err
	}

	return cli.TeamRename(context.TODO(), keybase1.TeamRenameArg{
		PrevName:  v.PrevName,
		NewName:   v.NewName,
		SessionID: v.SessionID,
	})
}

func newCmdTeamRename(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "rename",
		ArgumentHelp: "<team name> <new name>",
		Usage:        "Change the name of a subteam.",
		Flags:        []cli.Flag{},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(NewCmdTeamRenameRunner(g), "teamrename", c)
		},
		Description: "Rename a subteam.",
	}
}

func NewCmdTeamRenameRunner(g *libkb.GlobalContext) *CmdTeamRename {
	return &CmdTeamRename{Contextified: libkb.NewContextified(g)}
}

func (v *CmdTeamRename) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
}
<commit_msg>Fix rename too<commit_after>// Copyright 2017 Keybase, Inc. All rights reserved. Use of
// this source code is governed by the included BSD license.

package client

import (
	"errors"
	"fmt"

	"github.com/keybase/cli"
	"github.com/keybase/client/go/libcmdline"
	"github.com/keybase/client/go/libkb"
	keybase1 "github.com/keybase/client/go/protocol/keybase1"
	"golang.org/x/net/context"
)

type CmdTeamRename struct {
	PrevName  keybase1.TeamName
	NewName   keybase1.TeamName
	SessionID int
	libkb.Contextified
}

func (v *CmdTeamRename) ParseArgv(ctx *cli.Context) (err error) {
	if len(ctx.Args()) != 2 {
		return errors.New("two team names required")
	}

	v.PrevName, err = keybase1.TeamNameFromString(ctx.Args()[0])
	if err != nil {
		return fmt.Errorf("invalid old team name: %v", err)
	}

	v.NewName, err = keybase1.TeamNameFromString(ctx.Args()[1])
	if err != nil {
		return fmt.Errorf("invalid new team name: %v", err)
	}

	return nil
}

// Most of the checks are in the teams package.
// This just covers this one presentation thing:
//
// > keybase team change-name aviato.usa.marketing aviato.america.sales
// Error! You must first rename `aviato.usa` to `aviato.america`.
func (v *CmdTeamRename) helpRenameWrongLevel() (err error) {
	if v.PrevName.Depth() != v.NewName.Depth() {
		return nil
	}
	if v.PrevName.Depth() < 3 {
		return nil
	}
	// Scan the parts of both names looking for the first difference.
	// Skip the root and the end. Because the root is not this case,
	// and the end is not a problem.
	// If it is in the middle then tell the user what to do.
	for i := 1; i < len(v.PrevName.Parts)-1; i++ {
		prevPart := v.PrevName.Parts[i]
		newPart := v.NewName.Parts[i]
		if !prevPart.Eq(newPart) {
			a1 := keybase1.TeamName{Parts: v.PrevName.Parts[:i+1]}
			a2 := keybase1.TeamName{Parts: v.NewName.Parts[:i+1]}
			return fmt.Errorf("You must first rename `%v` to `%v`", a1, a2)
		}
	}
	return nil
}

func (v *CmdTeamRename) Run() (err error) {
	cli, err := GetTeamsClient(v.G())
	if err != nil {
		return err
	}

	err = v.helpRenameWrongLevel()
	if err != nil {
		return err
	}

	return cli.TeamRename(context.TODO(), keybase1.TeamRenameArg{
		PrevName:  v.PrevName,
		NewName:   v.NewName,
		SessionID: v.SessionID,
	})
}

func newCmdTeamRename(cl *libcmdline.CommandLine, g *libkb.GlobalContext) cli.Command {
	return cli.Command{
		Name:         "rename",
		ArgumentHelp: "<team name> <new name>",
		Usage:        "Change the name of a subteam.",
		Flags:        []cli.Flag{},
		Action: func(c *cli.Context) {
			cl.ChooseCommand(NewCmdTeamRenameRunner(g), "rename", c)
		},
		Description: "Rename a subteam.",
	}
}

func NewCmdTeamRenameRunner(g *libkb.GlobalContext) *CmdTeamRename {
	return &CmdTeamRename{Contextified: libkb.NewContextified(g)}
}

func (v *CmdTeamRename) GetUsage() libkb.Usage {
	return libkb.Usage{
		Config:    true,
		API:       true,
		KbKeyring: true,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/codegangsta/negroni"
	"github.com/facebookgo/stackerr"
	docker "github.com/fsouza/go-dockerclient"
	"github.com/gorilla/mux"
)

// If |useTLS| is enabled, look for the cert files here.
var defaultCertPath = os.Getenv("DOCKER_CERT_PATH")

var (
	staticPath  = flag.String("static", "./static", "serve static files located in this directory")
	dockerHost  = flag.String("docker", "tcp://192.168.59.103:2376", "address of docker daemon host")
	tlsCertPath = flag.String("tls-cert-path", defaultCertPath, "path to docker host TLS certificate")
	host        = flag.String("host", ":8080", "address:port for HTTP listener")
	useTLS      = flag.Bool("tls", true, "use Docker TLS")
)

func main() {
	flag.Parse()
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

func run() error {
	var client *docker.Client
	var err error
	if *useTLS {
		client, err = docker.NewTLSClient(
			*dockerHost,
			path.Join(*tlsCertPath, "cert.pem"),
			path.Join(*tlsCertPath, "key.pem"),
			path.Join(*tlsCertPath, "ca.pem"),
		)
		if err != nil {
			return stackerr.Wrap(err)
		}
	} else {
		client, err = docker.NewClient(*dockerHost)
		if err != nil {
			return err
		}
	}
	s := &AgentSupervisor{Client: client}
	log.Fatal(http.ListenAndServe(*host, Router(s, *staticPath)))
	return nil
}

// Router defines URL routes
func Router(s *AgentSupervisor, path string) *mux.Router {
	r := mux.NewRouter()
	WireAPIRoutes(r, s)
	WireHTMLRoutes(r, s) // after API routes

	// Finally, if none of the above routes match, delegate to the single-page
	// app's client-side router. Rewrite the path in order to load the
	// single-page app's root HTML entrypoint. The app will handle the route.
	r.NotFoundHandler = StaticPathFallback(path)
	return r
}

func StaticPathFallback(path string) http.Handler {
	return negroni.New(
		negroni.NewStatic(http.Dir(path)),
		negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			r.URL.Path = "/"
			http.FileServer(http.Dir(path)).ServeHTTP(w, r)
		})))
}

var templateHome = `

fxx >>= ai

	<form action=/api/v0/validate method=GET>

	validate your AI... Enter the name of the Docker image:
		<input type=text name=image>
		</input>
	</form>

`

func WireHTMLRoutes(r *mux.Router, s *AgentSupervisor) {

	r.HandleFunc("/html", func(w http.ResponseWriter, r *http.Request) {
		tmpl, err := template.New("name").Parse(templateHome)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		// Error checking elided
		if err := tmpl.Execute(w, nil); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	})
}

func WireAPIRoutes(r *mux.Router, s *AgentSupervisor) {

	apiV0 := r.PathPrefix("/api/v0").Subrouter()

	// TODO Build an agent from a GitHub repo
	// TODO Run Game between two running agents

	agents := apiV0.Path("/agents").Subrouter()
	agents.Methods("GET").HandlerFunc(ListAgentsHandler(s)) // list all available agents

	// pull an agent from Dockerhub
	dockerhub := apiV0.Path("/pull/dockerhub").Subrouter()
	dockerhub.Methods("GET").HandlerFunc(PullDockerHubAgentHandler(s)) // FIXME make this a POST

	// TODO List running agents with parameter
	apiV0.Path("/running").HandlerFunc(ListActiveAgentsHandler(s))

	// checks that the agent implements the right protocol
	apiV0.Path("/validate").HandlerFunc(ValidateAgentHandler(s))

	apiV0.Path("/image").HandlerFunc(ShowAgentInfoHandler(s))

	// If no API routes matched, but path had API prefix, return 404.
	apiV0.Path("/{rest:.*}").HandlerFunc(http.NotFound)
}

// PullDockerHubAgentHandler pulls the Docker image named |image| from
// DockerHub.
func PullDockerHubAgentHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.FormValue("image") == "" {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		if err := s.Client.PullImage(docker.PullImageOptions{
			OutputStream: w,
			Registry:     "https://index.docker.io",
			Repository:   r.FormValue("image"),
			Tag:          "latest",
		}, docker.AuthConfiguration{}); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
		}
	}
}

// ListAgentsHandler lists AI agents.
func ListAgentsHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		images, err := s.Client.ListImages(docker.ListImagesOptions{
			All: false,
		})
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			_, err := io.WriteString(w, err.Error())
			if err != nil {
				log.Println("error writing err: %s", err)
			}
			return
		}
		for _, img := range images {
			_, err := fmt.Fprintln(w, fmt.Sprintln(append([]string{img.ID, "\t"}, img.RepoTags...)))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				_, err := io.WriteString(w, err.Error())
				if err != nil {
					log.Println("error writing err: %s", err)
				}
			}
		}
	}
}

// ListActiveAgentsHandler lists agents that are currently running.
func ListActiveAgentsHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		containers, err := s.Client.ListContainers(docker.ListContainersOptions{})
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		for _, ps := range containers {
			fmt.Fprintln(w, fmt.Sprintf("%+v\n", ps))
		}
	}
}

// ValidateAgentHandler ensures that the Agent image responds to the PING
// command.
func ValidateAgentHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer log.Println("returned")

		// client gives the |image| as a URL parameter
		if r.FormValue("image") == "" {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintln(w, "`image` parameter is required")
			return
		}
		image := r.FormValue("image")

		if err := s.ValidateImage(image); err != nil {
			fmt.Fprintf(w, "image %s is not valid. error: %s", image, err)
			return
		}
		fmt.Fprintf(w, "image %s is valid", image)
	}
}

type AgentInfo struct {
	Owner  string
	Taunts []string
}

// ShowImageHandler shows information about an AI Agent
func ShowAgentInfoHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		image, err := s.Client.InspectImage("jbenet/go-ipfs")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(image.Config.ExposedPorts)
	}
}

// AgentSupervisor manages AI agents running in Docker containers
type AgentSupervisor struct {
	Client *docker.Client
}

func (s *AgentSupervisor) ValidateImage(image string) error {

	// run the container in a two-phase process. First, create the
	// container.
	container, err := s.Client.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Image: image, // the only required argument
		},
	})
	if err != nil {
		return fmt.Errorf("error creating container:", err.Error())
	}

	hc := &docker.HostConfig{
		PublishAllPorts: true,
	}

	// run the created container
	if err := s.Client.StartContainer(container.ID, hc); err != nil {
		return fmt.Errorf("error starting container: %s", err.Error())
	}

	// ensure the proper port is exposed
	info, err := s.Client.InspectContainer(container.ID)
	if err != nil {
		return fmt.Errorf("error inspecting container: %s", err.Error())
	}

	mappings, ok := info.NetworkSettings.Ports[docker.Port("3423/tcp")]
	if !ok {
		return fmt.Errorf(
			"container must expose port 3423/tcp. Found: %+v",
			info.NetworkSettings.Ports)
	}
	if len(mappings) != 1 {
		return fmt.Errorf(
			"error. expected one port mapping. found: %+v",
			info.NetworkSettings.Ports)
	}
	ip, port := mappings[0].HostIP, mappings[0].HostPort

	backoffConfig := backoff.NewExponentialBackOff()
	backoffConfig.InitialInterval = time.Second
	backoffConfig.MaxInterval = 10
	backoffConfig.MaxElapsedTime = 10 * time.Second
	err = backoff.Retry(func() error {
		resp, err := http.Get(fmt.Sprintf("http://%s:%s/ping", ip, port))
		if err != nil {
			log.Println("error pinging agent. found:", err)
			// TODO handle err
			return err
		}
		defer resp.Body.Close()
		var agentInfo AgentInfo
		if err := json.NewDecoder(resp.Body).Decode(&agentInfo); err != nil {
			return err
		}
		if agentInfo.Owner == "btc" {
			log.Println("yay!")
		}
		return nil
	}, backoffConfig)

	const kStopContainerTimeout = 5 // seconds
	if err := s.Client.StopContainer(container.ID, kStopContainerTimeout); err != nil {
		return err
	}
	return nil
}
<commit_msg>misc(httpd) add helpful log statement when server starts<commit_after>package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"html/template"
	"io"
	"log"
	"net/http"
	"os"
	"path"
	"time"

	"github.com/cenkalti/backoff"
	"github.com/codegangsta/negroni"
	"github.com/facebookgo/stackerr"
	docker "github.com/fsouza/go-dockerclient"
	"github.com/gorilla/mux"
)

// If |useTLS| is enabled, look for the cert files here.
var defaultCertPath = os.Getenv("DOCKER_CERT_PATH")

var (
	staticPath  = flag.String("static", "./static", "serve static files located in this directory")
	dockerHost  = flag.String("docker", "tcp://192.168.59.103:2376", "address of docker daemon host")
	tlsCertPath = flag.String("tls-cert-path", defaultCertPath, "path to docker host TLS certificate")
	host        = flag.String("host", ":8080", "address:port for HTTP listener")
	useTLS      = flag.Bool("tls", true, "use Docker TLS")
)

func main() {
	flag.Parse()
	if err := run(); err != nil {
		log.Fatal(err)
	}
}

func run() error {
	var client *docker.Client
	var err error
	if *useTLS {
		client, err = docker.NewTLSClient(
			*dockerHost,
			path.Join(*tlsCertPath, "cert.pem"),
			path.Join(*tlsCertPath, "key.pem"),
			path.Join(*tlsCertPath, "ca.pem"),
		)
		if err != nil {
			return stackerr.Wrap(err)
		}
	} else {
		client, err = docker.NewClient(*dockerHost)
		if err != nil {
			return err
		}
	}
	s := &AgentSupervisor{Client: client}
	log.Printf("listening at %s", *host)
	log.Fatal(http.ListenAndServe(*host, Router(s, *staticPath)))
	return nil
}

// Router defines URL routes
func Router(s *AgentSupervisor, path string) *mux.Router {
	r := mux.NewRouter()
	WireAPIRoutes(r, s)
	WireHTMLRoutes(r, s) // after API routes

	// Finally, if none of the above routes match, delegate to the single-page
	// app's client-side router. Rewrite the path in order to load the
	// single-page app's root HTML entrypoint. The app will handle the route.
	r.NotFoundHandler = StaticPathFallback(path)
	return r
}

func StaticPathFallback(path string) http.Handler {
	return negroni.New(
		negroni.NewStatic(http.Dir(path)),
		negroni.Wrap(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
			r.URL.Path = "/"
			http.FileServer(http.Dir(path)).ServeHTTP(w, r)
		})))
}

var templateHome = `

fxx >>= ai

	<form action=/api/v0/validate method=GET>

	validate your AI... Enter the name of the Docker image:
		<input type=text name=image>
		</input>
	</form>

`

func WireHTMLRoutes(r *mux.Router, s *AgentSupervisor) {

	r.HandleFunc("/html", func(w http.ResponseWriter, r *http.Request) {
		tmpl, err := template.New("name").Parse(templateHome)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		// Error checking elided
		if err := tmpl.Execute(w, nil); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	})
}

func WireAPIRoutes(r *mux.Router, s *AgentSupervisor) {

	apiV0 := r.PathPrefix("/api/v0").Subrouter()

	// TODO Build an agent from a GitHub repo
	// TODO Run Game between two running agents

	agents := apiV0.Path("/agents").Subrouter()
	agents.Methods("GET").HandlerFunc(ListAgentsHandler(s)) // list all available agents

	// pull an agent from Dockerhub
	dockerhub := apiV0.Path("/pull/dockerhub").Subrouter()
	dockerhub.Methods("GET").HandlerFunc(PullDockerHubAgentHandler(s)) // FIXME make this a POST

	// TODO List running agents with parameter
	apiV0.Path("/running").HandlerFunc(ListActiveAgentsHandler(s))

	// checks that the agent implements the right protocol
	apiV0.Path("/validate").HandlerFunc(ValidateAgentHandler(s))

	apiV0.Path("/image").HandlerFunc(ShowAgentInfoHandler(s))

	// If no API routes matched, but path had API prefix, return 404.
	apiV0.Path("/{rest:.*}").HandlerFunc(http.NotFound)
}

// PullDockerHubAgentHandler pulls the Docker image named |image| from
// DockerHub.
func PullDockerHubAgentHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		if r.FormValue("image") == "" {
			w.WriteHeader(http.StatusBadRequest)
			return
		}
		if err := s.Client.PullImage(docker.PullImageOptions{
			OutputStream: w,
			Registry:     "https://index.docker.io",
			Repository:   r.FormValue("image"),
			Tag:          "latest",
		}, docker.AuthConfiguration{}); err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			fmt.Fprintln(w, err)
		}
	}
}

// ListAgentsHandler lists AI agents.
func ListAgentsHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		images, err := s.Client.ListImages(docker.ListImagesOptions{
			All: false,
		})
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			_, err := io.WriteString(w, err.Error())
			if err != nil {
				log.Println("error writing err: %s", err)
			}
			return
		}
		for _, img := range images {
			_, err := fmt.Fprintln(w, fmt.Sprintln(append([]string{img.ID, "\t"}, img.RepoTags...)))
			if err != nil {
				w.WriteHeader(http.StatusInternalServerError)
				_, err := io.WriteString(w, err.Error())
				if err != nil {
					log.Println("error writing err: %s", err)
				}
			}
		}
	}
}

// ListActiveAgentsHandler lists agents that are currently running.
func ListActiveAgentsHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		containers, err := s.Client.ListContainers(docker.ListContainersOptions{})
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		for _, ps := range containers {
			fmt.Fprintln(w, fmt.Sprintf("%+v\n", ps))
		}
	}
}

// ValidateAgentHandler ensures that the Agent image responds to the PING
// command.
func ValidateAgentHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		defer log.Println("returned")

		// client gives the |image| as a URL parameter
		if r.FormValue("image") == "" {
			w.WriteHeader(http.StatusBadRequest)
			fmt.Fprintln(w, "`image` parameter is required")
			return
		}
		image := r.FormValue("image")

		if err := s.ValidateImage(image); err != nil {
			fmt.Fprintf(w, "image %s is not valid. error: %s", image, err)
			return
		}
		fmt.Fprintf(w, "image %s is valid", image)
	}
}

type AgentInfo struct {
	Owner  string
	Taunts []string
}

// ShowImageHandler shows information about an AI Agent
func ShowAgentInfoHandler(s *AgentSupervisor) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		image, err := s.Client.InspectImage("jbenet/go-ipfs")
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
		json.NewEncoder(w).Encode(image.Config.ExposedPorts)
	}
}

// AgentSupervisor manages AI agents running in Docker containers
type AgentSupervisor struct {
	Client *docker.Client
}

func (s *AgentSupervisor) ValidateImage(image string) error {

	// run the container in a two-phase process. First, create the
	// container.
	container, err := s.Client.CreateContainer(docker.CreateContainerOptions{
		Config: &docker.Config{
			Image: image, // the only required argument
		},
	})
	if err != nil {
		return fmt.Errorf("error creating container:", err.Error())
	}

	hc := &docker.HostConfig{
		PublishAllPorts: true,
	}

	// run the created container
	if err := s.Client.StartContainer(container.ID, hc); err != nil {
		return fmt.Errorf("error starting container: %s", err.Error())
	}

	// ensure the proper port is exposed
	info, err := s.Client.InspectContainer(container.ID)
	if err != nil {
		return fmt.Errorf("error inspecting container: %s", err.Error())
	}

	mappings, ok := info.NetworkSettings.Ports[docker.Port("3423/tcp")]
	if !ok {
		return fmt.Errorf(
			"container must expose port 3423/tcp. Found: %+v",
			info.NetworkSettings.Ports)
	}
	if len(mappings) != 1 {
		return fmt.Errorf(
			"error. expected one port mapping. found: %+v",
			info.NetworkSettings.Ports)
	}
	ip, port := mappings[0].HostIP, mappings[0].HostPort

	backoffConfig := backoff.NewExponentialBackOff()
	backoffConfig.InitialInterval = time.Second
	backoffConfig.MaxInterval = 10
	backoffConfig.MaxElapsedTime = 10 * time.Second
	err = backoff.Retry(func() error {
		resp, err := http.Get(fmt.Sprintf("http://%s:%s/ping", ip, port))
		if err != nil {
			log.Println("error pinging agent. found:", err)
			// TODO handle err
			return err
		}
		defer resp.Body.Close()
		var agentInfo AgentInfo
		if err := json.NewDecoder(resp.Body).Decode(&agentInfo); err != nil {
			return err
		}
		if agentInfo.Owner == "btc" {
			log.Println("yay!")
		}
		return nil
	}, backoffConfig)

	const kStopContainerTimeout = 5 // seconds
	if err := s.Client.StopContainer(container.ID, kStopContainerTimeout); err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>// Copyright 2018 ETH Zurich
//
// 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 snetproxy implements transparent logic for reconnecting to the
// dispatcher.
//
// This is done via two types: ProxyConn, a wrapper around snet.Conn that
// supports transparent reconnects, and ProxyNetwork, a wrapper around
// snet.Network that provides some helper functions for initializing ProxyConns
// (e.g., creating callbacks and checking that addresses stay the same).
// Callers can opt to use the helper ProxyNetwork, or manage reconnection logic
// themselves by using ProxyConn directly.
package snetproxy
<commit_msg>snetproxy: Add disclaimer (#2755)<commit_after>// Copyright 2018 ETH Zurich
//
// 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 snetproxy implements transparent logic for reconnecting to the
// dispatcher.
//
// This is done via two types: ProxyConn, a wrapper around snet.Conn that
// supports transparent reconnects, and ProxyNetwork, a wrapper around
// snet.Network that provides some helper functions for initializing ProxyConns
// (e.g., creating callbacks and checking that addresses stay the same).
// Callers can opt to use the helper ProxyNetwork, or manage reconnection logic
// themselves by using ProxyConn directly.
//
// ProxyConn currently sets a new timer for every IO operation.
package snetproxy
<|endoftext|>


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

package control

import (
	"fmt"
	"os"
	"strconv"
	"time"

	"github.com/scionproto/scion/go/lib/topology"
)

const (
	envDisable = "SCION_EXPERIMENTAL_BFD_DISABLE"
	envMult    = "SCION_EXPERIMENTAL_BFD_DETECT_MULT"
	envMinTx   = "SCION_EXPERIMENTAL_BFD_DESIRED_MIN_TX"
	envMinRx   = "SCION_EXPERIMENTAL_BFD_REQUIRED_MIN_RX"
)

// BFD is the configuration for the BFD sessions.
type BFD topology.BFD

// XXX(sgmonroy) note that env values only affect defaults, which in turn are only used
// if there were no BFD related settings in the topology.
func withDefaults(cfg BFD) BFD {
	// If default is disable, BFD is globally disabled.
	if bfdDefaults.Disable {
		cfg.Disable = true
	}
	if cfg.DetectMult == 0 {
		cfg.DetectMult = bfdDefaults.DetectMult
	}
	if cfg.DesiredMinTxInterval == 0 {
		cfg.DesiredMinTxInterval = bfdDefaults.DesiredMinTxInterval
	}
	if cfg.RequiredMinRxInterval == 0 {
		cfg.RequiredMinRxInterval = bfdDefaults.RequiredMinRxInterval
	}
	return cfg
}

var (
	bfdDefaults = BFD{
		DetectMult:            3,
		DesiredMinTxInterval:  200 * time.Millisecond,
		RequiredMinRxInterval: 200 * time.Millisecond,
		// Disable indicates if BFD is disabled globally.
		Disable: false,
	}
)

func init() {
	if val := os.Getenv(envDisable); val != "" {
		if disabled, err := strconv.ParseBool(val); err == nil {
			bfdDefaults.Disable = disabled
			fmt.Fprintf(os.Stderr, "%s=%v\n", envDisable, disabled)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envDisable, err)
		}
	}
	if val := os.Getenv(envMult); val != "" {
		if p, err := strconv.ParseUint(val, 10, 8); err == nil {
			bfdDefaults.DetectMult = uint8(p)
			fmt.Fprintf(os.Stderr, "%s=%v\n", envMult, bfdDefaults.DetectMult)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envMult, err)
		}
	}
	if val := os.Getenv(envMinTx); val != "" {
		if d, err := time.ParseDuration(val); err == nil {
			bfdDefaults.DesiredMinTxInterval = d
			fmt.Fprintf(os.Stderr, "%s=%v\n", envMinTx, bfdDefaults.DesiredMinTxInterval)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envMinTx, err)
		}
	}
	if val := os.Getenv(envMinRx); val != "" {
		if d, err := time.ParseDuration(val); err == nil {
			bfdDefaults.RequiredMinRxInterval = d
			fmt.Fprintf(os.Stderr, "%s=%v\n", envMinRx, bfdDefaults.RequiredMinRxInterval)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envMinRx, err)
		}
	}
}
<commit_msg>Workaround for issue scionproto/4019. (#89)<commit_after>// Copyright 2020 Anapaya Systems
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"fmt"
	"os"
	"strconv"
	"time"

	"github.com/scionproto/scion/go/lib/topology"
)

const (
	envDisable = "SCION_EXPERIMENTAL_BFD_DISABLE"
	envMult    = "SCION_EXPERIMENTAL_BFD_DETECT_MULT"
	envMinTx   = "SCION_EXPERIMENTAL_BFD_DESIRED_MIN_TX"
	envMinRx   = "SCION_EXPERIMENTAL_BFD_REQUIRED_MIN_RX"
)

// BFD is the configuration for the BFD sessions.
type BFD topology.BFD

// XXX(sgmonroy) note that env values only affect defaults, which in turn are only used
// if there were no BFD related settings in the topology.
func withDefaults(cfg BFD) BFD {
	// If default is disable, BFD is globally disabled.
	if bfdDefaults.Disable {
		cfg.Disable = true
	}
	if cfg.DetectMult == 0 {
		cfg.DetectMult = bfdDefaults.DetectMult
	}
	if cfg.DesiredMinTxInterval == 0 {
		cfg.DesiredMinTxInterval = bfdDefaults.DesiredMinTxInterval
	}
	if cfg.RequiredMinRxInterval == 0 {
		cfg.RequiredMinRxInterval = bfdDefaults.RequiredMinRxInterval
	}
	return cfg
}

var (
	bfdDefaults = BFD{
		// XXX(juagargi) patching the default value of DetectMult from 3 to 100 to
		// workaround a problem where BFD packets are dropped when the router is under heavy
		// load (issue #4019). Once it's solved, rollback to a value of 3
		DetectMult:            100,
		DesiredMinTxInterval:  200 * time.Millisecond,
		RequiredMinRxInterval: 200 * time.Millisecond,
		// Disable indicates if BFD is disabled globally.
		Disable: false,
	}
)

func init() {
	if val := os.Getenv(envDisable); val != "" {
		if disabled, err := strconv.ParseBool(val); err == nil {
			bfdDefaults.Disable = disabled
			fmt.Fprintf(os.Stderr, "%s=%v\n", envDisable, disabled)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envDisable, err)
		}
	}
	if val := os.Getenv(envMult); val != "" {
		if p, err := strconv.ParseUint(val, 10, 8); err == nil {
			bfdDefaults.DetectMult = uint8(p)
			fmt.Fprintf(os.Stderr, "%s=%v\n", envMult, bfdDefaults.DetectMult)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envMult, err)
		}
	}
	if val := os.Getenv(envMinTx); val != "" {
		if d, err := time.ParseDuration(val); err == nil {
			bfdDefaults.DesiredMinTxInterval = d
			fmt.Fprintf(os.Stderr, "%s=%v\n", envMinTx, bfdDefaults.DesiredMinTxInterval)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envMinTx, err)
		}
	}
	if val := os.Getenv(envMinRx); val != "" {
		if d, err := time.ParseDuration(val); err == nil {
			bfdDefaults.RequiredMinRxInterval = d
			fmt.Fprintf(os.Stderr, "%s=%v\n", envMinRx, bfdDefaults.RequiredMinRxInterval)
		} else {
			fmt.Fprintf(os.Stderr, "Error parsing %s: %v\n", envMinRx, err)
		}
	}
}
<|endoftext|>


<commit_before>package core

import (
	"io"
	"os"
	"time"
	"unicode"
	"unicode/utf8"

	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"

	"github.com/atotto/clipboard"
	"github.com/mibk/syd/ui"
	"github.com/mibk/syd/ui/term"
)

type Text struct {
	ctx  cmdContext
	text *term.Text
	buf  Buffer

	origin    int64
	q0, q1    int64
	selEnd    *int64
	timestamp time.Time
}

func newText(ctx cmdContext, buf Buffer, tt *term.Text) *Text {
	t := &Text{
		ctx:  ctx,
		buf:  buf,
		text: tt,
	}
	tt.OnMouseEvent(t.handleMouse)
	tt.OnKeyEvent(t.handleKeyEvent)
	return t
}

func (t *Text) loadText() {
	t.text.Select(int(t.q0-t.origin), int(t.q1-t.origin))

	for p := t.origin; ; p++ {
		r, _, err := t.buf.ReadRuneAt(p)
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		if err := t.text.WriteRune(r); err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}

	}
}

func (t *Text) Origin() int64 { return t.origin }

func (t *Text) SetOrigin(org int64) { t.origin = org }

func (t *Text) Selected() (q0, q1 int64) { return t.q0, t.q1 }

func (t *Text) SelectionToString(q0, q1 int64) string {
	s := make([]rune, 0, q1-q0)
	for p := q0; p < q1; p++ {
		s = append(s, t.readRuneAt(p))
	}
	return string(s)
}

func (t *Text) Select(q0, q1 int64) {
	if q0 < 0 || q1 < q0 {
		return
	}
	t.q0, t.q1 = q0, q1
	if t.q1 > t.origin+int64(t.text.Frame().Nchars()) {
		oldOrg := t.origin
		t.origin += int64(t.text.Frame().CharsUntilXY(0, 3))
		t.loadText()
		if t.q1 > t.origin+int64(t.text.Frame().Nchars()) {
			// There's no more content, get back.
			t.origin = oldOrg
			t.q1--
			if t.q0 > t.q1 {
				t.q0 = t.q1
			}
			t.loadText()
		}
	}
	t.checkVisibility()
}

func (t *Text) Insert(s string) {
	if t.q0 != t.q1 {
		t.buf.Delete(t.q0, t.q1)
	}
	t.buf.Insert(t.q0, s)
	q := t.q0 + int64(utf8.RuneCountInString(s))
	t.q0, t.q1 = q, q
	t.text.Frame().SetWantCol(ui.ColQ1)
	t.checkVisibility()
}

func (t *Text) DeleteSel() {
	t.buf.Delete(t.q0, t.q1)
	t.q1 = t.q0
	t.checkVisibility()
}

func (t *Text) checkVisibility() {
	if t.q0 < t.origin || t.q0 > t.origin+int64(t.text.Frame().Nchars())+1 {
		t.origin = t.PrevNewLine(t.q0, 3)
	}
}

func (t *Text) PrevNewLine(p int64, n int) int64 {
	for ; n > 0; n-- {
		// Shorten long lines. After 128 characters call it a line anyway.
		for i := 0; i < 128 && p > 0; i++ {
			p--
			if p == 0 {
				return 0
			}
			r, _, err := t.buf.ReadRuneAt(p - 1)
			if err != nil {
				panic(err)
			}
			if r == '\n' {
				break
			}
		}
	}
	return p
}

func (t *Text) readRuneAt(off int64) rune {
	r, _, err := t.buf.ReadRuneAt(off)
	if err == io.EOF {
		return EOF
	} else if err != nil {
		panic(err)
	}
	return r
}

func (t *Text) handleMouse(p int, ev mouse.Event) {
	q := t.origin + int64(p)
	switch ev.Direction {
	case mouse.DirPress:
		if ev.Button == mouse.ButtonMiddle {
			var q0, q1 int64
			if q >= t.q0 && q < t.q1 {
				q0, q1 = t.q0, t.q1
			} else {
				q0, q1 = t.selectPath(q)
			}
			cmd := t.SelectionToString(q0, q1)
			execute(t.ctx, cmd)
			return
		} else if ev.Button == mouse.ButtonRight {
			path := t.SelectionToString(t.selectPath(q))
			// TODO: Don't require being in a column context. Just
			// open the file in the most recent column (using similar
			// heuristic as in Acme).
			if col, ok := t.ctx.column(); ok {
				if _, ok := col.ed.wins[path]; ok {
					return
				}
				if _, err := os.Stat(path); err == nil {
					col.NewWindowFile(path)
				}
			}
			return
		}

		if time.Since(t.timestamp) < 300*time.Millisecond {
			t.Select(t.dblclick(q))
			t.selEnd = nil
			return
		}
		t.q0, t.q1 = q, q
		t.selEnd = &t.q1
		t.timestamp = time.Now()
		// TODO: Get rid of SetWantCol.
		t.text.Frame().SetWantCol(ui.ColQ0)
	case mouse.DirRelease:
		t.selEnd = nil
	case mouse.DirNone:
		if t.selEnd == nil {
			return
		}
		*t.selEnd = q
		if t.q0 > t.q1 {
			t.q0, t.q1 = t.q1, t.q0
			if t.selEnd == &t.q0 {
				t.selEnd = &t.q1
			} else {
				t.selEnd = &t.q0
			}
		}
	case mouse.DirStep:
		switch ev.Button {
		case mouse.ButtonWheelUp:
			t.ScrollUp(3)
		case mouse.ButtonWheelDown:
			t.ScrollDown(3)
		}
	}
}

func (t *Text) dblclick(q int64) (q0, q1 int64) {
	return t.spread(q, isAlphaNumeric)
}

func (t *Text) selectPath(q int64) (q0, q1 int64) {
	return t.spread(q, isPath)
}

func (t *Text) spread(q int64, fn func(rune) bool) (q0, q1 int64) {
	q0, q1 = q, q
	for q0 > 0 {
		r := t.readRuneAt(q0 - 1)
		if !fn(r) {
			break
		}
		q0--
	}
	for {
		r := t.readRuneAt(q1)
		if !fn(r) {
			break
		}
		q1++
	}
	return
}

func isAlphaNumeric(r rune) bool {
	return unicode.IsLetter(r) || unicode.IsDigit(r)
}

func isPath(r rune) bool { return !unicode.IsSpace(r) && r != EOF }

func (t *Text) handleKeyEvent(ev key.Event) {
	switch {
	case ev.Rune == ui.KeyEnter:
		q0, _ := t.Selected()
		p := t.PrevNewLine(q0, 1)

		var indent []rune
		for ; ; p++ {
			r := t.readRuneAt(p)
			if r != ' ' && r != '\t' {
				break
			}
			indent = append(indent, r)
		}
		t.Insert("\n" + string(indent))
	case ev.Rune == ui.KeyBackspace:
		q0, q1 := t.Selected()
		if q0 == q1 {
			t.Select(q0-1, q1)
		}
		t.DeleteSel()
	case ev.Rune == ui.KeyDelete:
		q0, q1 := t.Selected()
		if q0 == q1 {
			t.Select(q0, q1+1)
		}
		t.DeleteSel()
	case ev.Rune == ui.KeyEscape:
		t.DeleteSel()
	case ev.Rune == ui.KeyLeft:
		left(t)
	case ev.Rune == ui.KeyRight:
		right(t)

	case ev.Rune == ui.KeyUp:
		up(t)
	case ev.Rune == ui.KeyDown:
		down(t)

	case (ev.Rune == 'c' || ev.Rune == 'x') && ev.Modifiers&key.ModControl != 0:
		err := clipboard.WriteAll(t.SelectionToString(t.Selected()))
		if err != nil {
			panic(err)
		}
		if ev.Rune == 'x' {
			t.DeleteSel()
		}
	case ev.Rune == 'v' && ev.Modifiers&key.ModControl != 0:
		s, err := clipboard.ReadAll()
		if err != nil {
			panic(err)
		}
		t.Insert(s)
	default:
		t.Insert(string(ev.Rune))
	}
}

// TODO: Remove these.

func (t *Text) ScrollUp(nlines int) {
	t.SetOrigin(t.PrevNewLine(t.Origin(), nlines))
}

func (t *Text) ScrollDown(nlines int) {
	t.SetOrigin(t.Origin() + int64(t.text.Frame().CharsUntilXY(0, nlines)))
}

// TODO: Is this the right place for these?

func left(t *Text) {
	q0, _ := t.Selected()
	t.Select(q0-1, q0-1)
	t.text.Frame().SetWantCol(ui.ColQ0)
}

func right(t *Text) {
	_, q1 := t.Selected()
	t.Select(q1+1, q1+1)
	t.text.Frame().SetWantCol(ui.ColQ1)
}

func up(t *Text) {
	_, line1 := t.text.Frame().SelectionLines()
	q := findQ(t, line1-1)
	t.Select(q, q)
}

func down(t *Text) {
	_, line1 := t.text.Frame().SelectionLines()
	q := findQ(t, line1+1)
	t.Select(q, q)
}

func findQ(t *Text, line int) int64 {
	if line < 0 {
		t.SetOrigin(t.PrevNewLine(t.Origin(), -line))
		t.loadText()
		line = 0
	} else if line > t.text.Frame().Lines()-1 {
		_, h := t.text.Size()
		if t.text.Frame().Lines() == h {
			i := line - t.text.Frame().Lines() + 1
			oldOrg := t.Origin()
			l := t.text.Frame().Lines()
			t.SetOrigin(oldOrg + int64(t.text.Frame().CharsUntilXY(0, i)))
			t.loadText()
			if t.text.Frame().Lines() < l {
				t.SetOrigin(oldOrg)
				t.loadText()
			}
		}
		line = t.text.Frame().Lines() - 1
	}
	q := t.Origin()
	return q + int64(t.text.Frame().CharsUntilXY(t.text.Frame().WantCol(), line))
}
<commit_msg>core: Fix inclusion of \0 when searching for paths (e.g. middle click)<commit_after>package core

import (
	"io"
	"os"
	"time"
	"unicode"
	"unicode/utf8"

	"golang.org/x/mobile/event/key"
	"golang.org/x/mobile/event/mouse"

	"github.com/atotto/clipboard"
	"github.com/mibk/syd/ui"
	"github.com/mibk/syd/ui/term"
)

type Text struct {
	ctx  cmdContext
	text *term.Text
	buf  Buffer

	origin    int64
	q0, q1    int64
	selEnd    *int64
	timestamp time.Time
}

func newText(ctx cmdContext, buf Buffer, tt *term.Text) *Text {
	t := &Text{
		ctx:  ctx,
		buf:  buf,
		text: tt,
	}
	tt.OnMouseEvent(t.handleMouse)
	tt.OnKeyEvent(t.handleKeyEvent)
	return t
}

func (t *Text) loadText() {
	t.text.Select(int(t.q0-t.origin), int(t.q1-t.origin))

	for p := t.origin; ; p++ {
		r, _, err := t.buf.ReadRuneAt(p)
		if err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}
		if err := t.text.WriteRune(r); err == io.EOF {
			break
		} else if err != nil {
			panic(err)
		}

	}
}

func (t *Text) Origin() int64 { return t.origin }

func (t *Text) SetOrigin(org int64) { t.origin = org }

func (t *Text) Selected() (q0, q1 int64) { return t.q0, t.q1 }

func (t *Text) SelectionToString(q0, q1 int64) string {
	s := make([]rune, 0, q1-q0)
	for p := q0; p < q1; p++ {
		s = append(s, t.readRuneAt(p))
	}
	return string(s)
}

func (t *Text) Select(q0, q1 int64) {
	if q0 < 0 || q1 < q0 {
		return
	}
	t.q0, t.q1 = q0, q1
	if t.q1 > t.origin+int64(t.text.Frame().Nchars()) {
		oldOrg := t.origin
		t.origin += int64(t.text.Frame().CharsUntilXY(0, 3))
		t.loadText()
		if t.q1 > t.origin+int64(t.text.Frame().Nchars()) {
			// There's no more content, get back.
			t.origin = oldOrg
			t.q1--
			if t.q0 > t.q1 {
				t.q0 = t.q1
			}
			t.loadText()
		}
	}
	t.checkVisibility()
}

func (t *Text) Insert(s string) {
	if t.q0 != t.q1 {
		t.buf.Delete(t.q0, t.q1)
	}
	t.buf.Insert(t.q0, s)
	q := t.q0 + int64(utf8.RuneCountInString(s))
	t.q0, t.q1 = q, q
	t.text.Frame().SetWantCol(ui.ColQ1)
	t.checkVisibility()
}

func (t *Text) DeleteSel() {
	t.buf.Delete(t.q0, t.q1)
	t.q1 = t.q0
	t.checkVisibility()
}

func (t *Text) checkVisibility() {
	if t.q0 < t.origin || t.q0 > t.origin+int64(t.text.Frame().Nchars())+1 {
		t.origin = t.PrevNewLine(t.q0, 3)
	}
}

func (t *Text) PrevNewLine(p int64, n int) int64 {
	for ; n > 0; n-- {
		// Shorten long lines. After 128 characters call it a line anyway.
		for i := 0; i < 128 && p > 0; i++ {
			p--
			if p == 0 {
				return 0
			}
			r, _, err := t.buf.ReadRuneAt(p - 1)
			if err != nil {
				panic(err)
			}
			if r == '\n' {
				break
			}
		}
	}
	return p
}

func (t *Text) readRuneAt(off int64) rune {
	r, _, err := t.buf.ReadRuneAt(off)
	if err == io.EOF {
		return EOF
	} else if err != nil {
		panic(err)
	}
	return r
}

func (t *Text) handleMouse(p int, ev mouse.Event) {
	q := t.origin + int64(p)
	switch ev.Direction {
	case mouse.DirPress:
		if ev.Button == mouse.ButtonMiddle {
			var q0, q1 int64
			if q >= t.q0 && q < t.q1 {
				q0, q1 = t.q0, t.q1
			} else {
				q0, q1 = t.selectPath(q)
			}
			cmd := t.SelectionToString(q0, q1)
			execute(t.ctx, cmd)
			return
		} else if ev.Button == mouse.ButtonRight {
			path := t.SelectionToString(t.selectPath(q))
			// TODO: Don't require being in a column context. Just
			// open the file in the most recent column (using similar
			// heuristic as in Acme).
			if col, ok := t.ctx.column(); ok {
				if _, ok := col.ed.wins[path]; ok {
					return
				}
				if _, err := os.Stat(path); err == nil {
					col.NewWindowFile(path)
				}
			}
			return
		}

		if time.Since(t.timestamp) < 300*time.Millisecond {
			t.Select(t.dblclick(q))
			t.selEnd = nil
			return
		}
		t.q0, t.q1 = q, q
		t.selEnd = &t.q1
		t.timestamp = time.Now()
		// TODO: Get rid of SetWantCol.
		t.text.Frame().SetWantCol(ui.ColQ0)
	case mouse.DirRelease:
		t.selEnd = nil
	case mouse.DirNone:
		if t.selEnd == nil {
			return
		}
		*t.selEnd = q
		if t.q0 > t.q1 {
			t.q0, t.q1 = t.q1, t.q0
			if t.selEnd == &t.q0 {
				t.selEnd = &t.q1
			} else {
				t.selEnd = &t.q0
			}
		}
	case mouse.DirStep:
		switch ev.Button {
		case mouse.ButtonWheelUp:
			t.ScrollUp(3)
		case mouse.ButtonWheelDown:
			t.ScrollDown(3)
		}
	}
}

func (t *Text) dblclick(q int64) (q0, q1 int64) {
	return t.spread(q, isAlphaNumeric)
}

func (t *Text) selectPath(q int64) (q0, q1 int64) {
	return t.spread(q, isPath)
}

func (t *Text) spread(q int64, fn func(rune) bool) (q0, q1 int64) {
	q0, q1 = q, q
	for q0 > 0 {
		r := t.readRuneAt(q0 - 1)
		if !fn(r) {
			break
		}
		q0--
	}
	for {
		r := t.readRuneAt(q1)
		if !fn(r) {
			break
		}
		q1++
	}
	return
}

func isAlphaNumeric(r rune) bool {
	return unicode.IsLetter(r) || unicode.IsDigit(r)
}

func isPath(r rune) bool { return !unicode.IsSpace(r) && r != EOF && r != 0 }

func (t *Text) handleKeyEvent(ev key.Event) {
	switch {
	case ev.Rune == ui.KeyEnter:
		q0, _ := t.Selected()
		p := t.PrevNewLine(q0, 1)

		var indent []rune
		for ; ; p++ {
			r := t.readRuneAt(p)
			if r != ' ' && r != '\t' {
				break
			}
			indent = append(indent, r)
		}
		t.Insert("\n" + string(indent))
	case ev.Rune == ui.KeyBackspace:
		q0, q1 := t.Selected()
		if q0 == q1 {
			t.Select(q0-1, q1)
		}
		t.DeleteSel()
	case ev.Rune == ui.KeyDelete:
		q0, q1 := t.Selected()
		if q0 == q1 {
			t.Select(q0, q1+1)
		}
		t.DeleteSel()
	case ev.Rune == ui.KeyEscape:
		t.DeleteSel()
	case ev.Rune == ui.KeyLeft:
		left(t)
	case ev.Rune == ui.KeyRight:
		right(t)

	case ev.Rune == ui.KeyUp:
		up(t)
	case ev.Rune == ui.KeyDown:
		down(t)

	case (ev.Rune == 'c' || ev.Rune == 'x') && ev.Modifiers&key.ModControl != 0:
		err := clipboard.WriteAll(t.SelectionToString(t.Selected()))
		if err != nil {
			panic(err)
		}
		if ev.Rune == 'x' {
			t.DeleteSel()
		}
	case ev.Rune == 'v' && ev.Modifiers&key.ModControl != 0:
		s, err := clipboard.ReadAll()
		if err != nil {
			panic(err)
		}
		t.Insert(s)
	default:
		t.Insert(string(ev.Rune))
	}
}

// TODO: Remove these.

func (t *Text) ScrollUp(nlines int) {
	t.SetOrigin(t.PrevNewLine(t.Origin(), nlines))
}

func (t *Text) ScrollDown(nlines int) {
	t.SetOrigin(t.Origin() + int64(t.text.Frame().CharsUntilXY(0, nlines)))
}

// TODO: Is this the right place for these?

func left(t *Text) {
	q0, _ := t.Selected()
	t.Select(q0-1, q0-1)
	t.text.Frame().SetWantCol(ui.ColQ0)
}

func right(t *Text) {
	_, q1 := t.Selected()
	t.Select(q1+1, q1+1)
	t.text.Frame().SetWantCol(ui.ColQ1)
}

func up(t *Text) {
	_, line1 := t.text.Frame().SelectionLines()
	q := findQ(t, line1-1)
	t.Select(q, q)
}

func down(t *Text) {
	_, line1 := t.text.Frame().SelectionLines()
	q := findQ(t, line1+1)
	t.Select(q, q)
}

func findQ(t *Text, line int) int64 {
	if line < 0 {
		t.SetOrigin(t.PrevNewLine(t.Origin(), -line))
		t.loadText()
		line = 0
	} else if line > t.text.Frame().Lines()-1 {
		_, h := t.text.Size()
		if t.text.Frame().Lines() == h {
			i := line - t.text.Frame().Lines() + 1
			oldOrg := t.Origin()
			l := t.text.Frame().Lines()
			t.SetOrigin(oldOrg + int64(t.text.Frame().CharsUntilXY(0, i)))
			t.loadText()
			if t.text.Frame().Lines() < l {
				t.SetOrigin(oldOrg)
				t.loadText()
			}
		}
		line = t.text.Frame().Lines() - 1
	}
	q := t.Origin()
	return q + int64(t.text.Frame().CharsUntilXY(t.text.Frame().WantCol(), line))
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/checkstyle"
)

var config = flag.String("config", "", "config json file")

var checker checkstyle.Checker

type Ignore struct {
	Files []string `json:"ignore"`
}

var ignore Ignore

var normalProblems []*checkstyle.Problem
var fatalProblems []*checkstyle.Problem

func main() {
	flag.Parse()

	files := flag.Args()

	if config == nil {
		log.Fatalln("No config")
	}
	conf, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatalf("Open config %v fail %v\n", *config, err)
	}

	err = json.Unmarshal(conf, &ignore)
	if err != nil {
		log.Fatalf("Parse config %v fail \n", *config, err)
	}
	checker, err = checkstyle.New(conf)
	if err != nil {
		log.Fatalf("New checker fail %v\n", err)
	}

	for _, v := range files {
		if isDir(v) {
			checkDir(v)
		} else {
			checkFile(v)
		}
	}

	if len(normalProblems) != 0 {
		log.Printf(" ========= There are %d normal problems ========= \n", len(normalProblems))
		printProblems(normalProblems)
	}

	if len(fatalProblems) != 0 {
		log.Printf(" ========= There are %d fatal problems ========= \n", len(fatalProblems))
		printProblems(fatalProblems)
		os.Exit(1)
	}
}

func printProblems(ps []*checkstyle.Problem) {
	for _, p := range ps {
		log.Printf("%v: %s\n", p.Position, p.Description)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func checkFile(fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("Read File Fail %v %v\n", fileName, err)
	}

	ps, err := checker.Check(fileName, file)
	if err != nil {
		log.Fatalf("Parse File Fail %v %v\n", fileName, err)
	}

	for i, p := range ps {
		if checker.IsFatal(&p) {
			fatalProblems = append(fatalProblems, &ps[i])
		} else {
			normalProblems = append(normalProblems, &ps[i])
		}
	}
}

func isIgnore(fileName string) bool {
	for _, v := range ignore.Files {
		if ok, _ := filepath.Match(v, fileName); ok {
			return true
		}
	}
	return false
}

func checkDir(dir string) {
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err == nil && !info.IsDir() && strings.HasSuffix(path, ".go") && !isIgnore(path) {
			checkFile(path)
		}
		return err
	})
}
<commit_msg>checkstyle dir * pattern<commit_after>package main

import (
	"encoding/json"
	"flag"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"

	"github.com/qiniu/checkstyle"
)

var config = flag.String("config", "", "config json file")

var checker checkstyle.Checker

type Ignore struct {
	Files []string `json:"ignore"`
}

var ignore Ignore

var normalProblems []*checkstyle.Problem
var fatalProblems []*checkstyle.Problem

func main() {
	flag.Parse()

	files := flag.Args()

	if config == nil {
		log.Fatalln("No config")
	}
	conf, err := ioutil.ReadFile(*config)
	if err != nil {
		log.Fatalf("Open config %v fail %v\n", *config, err)
	}

	err = json.Unmarshal(conf, &ignore)
	if err != nil {
		log.Fatalf("Parse config %v fail \n", *config, err)
	}
	checker, err = checkstyle.New(conf)
	if err != nil {
		log.Fatalf("New checker fail %v\n", err)
	}

	for _, v := range files {
		if isDir(v) {
			checkDir(v)
		} else {
			checkFile(v)
		}
	}

	if len(normalProblems) != 0 {
		log.Printf(" ========= There are %d normal problems ========= \n", len(normalProblems))
		printProblems(normalProblems)
	}

	if len(fatalProblems) != 0 {
		log.Printf(" ========= There are %d fatal problems ========= \n", len(fatalProblems))
		printProblems(fatalProblems)
		os.Exit(1)
	}
}

func printProblems(ps []*checkstyle.Problem) {
	for _, p := range ps {
		log.Printf("%v: %s\n", p.Position, p.Description)
	}
}

func isDir(filename string) bool {
	fi, err := os.Stat(filename)
	return err == nil && fi.IsDir()
}

func checkFile(fileName string) {
	file, err := ioutil.ReadFile(fileName)
	if err != nil {
		log.Fatalf("Read File Fail %v %v\n", fileName, err)
	}

	ps, err := checker.Check(fileName, file)
	if err != nil {
		log.Fatalf("Parse File Fail %v %v\n", fileName, err)
	}

	for i, p := range ps {
		if checker.IsFatal(&p) {
			fatalProblems = append(fatalProblems, &ps[i])
		} else {
			normalProblems = append(normalProblems, &ps[i])
		}
	}
}

func isIgnore(fileName string) bool {
	for _, v := range ignore.Files {
		if ok, _ := filepath.Match(v, fileName); ok {
			return true
		}
	}
	return false
}

func isIgnoreDir(dir string) bool {
	for _, v := range ignore.Files {
		if ok, _ := filepath.Match(v, dir); ok {
			return true
		}
	}
	return false
}

func checkDir(dir string) {
	if isIgnoreDir(dir) {
		return
	}
	filepath.Walk(dir, func(path string, info os.FileInfo, err error) error {
		if err == nil && info.IsDir() && isIgnoreDir(path) {
			return filepath.SkipDir
		}
		if err == nil && !info.IsDir() && strings.HasSuffix(path, ".go") && !isIgnoreFile(path) {
			checkFile(path)
		}
		return err
	})
}
<|endoftext|>