<commit_before>package cbauth

import (
	"crypto/hmac"
	"crypto/sha1"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/couchbase/cbauth/cbauthimpl"
	"github.com/couchbase/cbauth/revrpc"
)

func newAuth(initPeriod time.Duration) *authImpl {
	return &authImpl{cbauthimpl.NewSVC(initPeriod, &DBStaleError{})}
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func hashPassword(password string, salt []byte) []byte {
	h := hmac.New(sha1.New, salt)
	h.Write([]byte(password))
	return h.Sum(nil)
}

func mkUser(user, password, salt string) (u cbauthimpl.User) {
	u.User = user
	u.Salt = []byte(salt)
	u.Mac = hashPassword(password, u.Salt)
	return
}

func newAuthForTest(body func(freshChan chan struct{}, timeoutBody func())) *authImpl {
	testDur := 555 * time.Hour

	wf := func(period time.Duration, ch chan struct{}, timeoutBody func()) {
		if period != testDur {
			panic(period)
		}
		body(ch, timeoutBody)
	}

	return &authImpl{cbauthimpl.NewSVCForTest(testDur, &DBStaleError{}, wf)}
}

func acc(ok bool, err error) bool {
	must(err)
	return ok
}

func assertAdmins(t *testing.T, c Creds, needAdmin, needROAdmin bool) {
	if acc(c.IsAdmin()) != needAdmin {
		t.Fatalf("admin access must be: %v", needAdmin)
	}
	roadmin := !acc(c.IsAdmin()) && c.CanReadAnyMetadata()
	if roadmin != needROAdmin {
		t.Fatalf("ro-admin access must be: %v", needROAdmin)
	}
}

type testingRoundTripper struct {
	t       *testing.T
	method  string
	url     string
	user    string
	source  string
	token   string
	tripped bool
}

func newTestingRT(t *testing.T, method, uri string) *testingRoundTripper {
	return &testingRoundTripper{
		t:      t,
		method: method,
		url:    uri,
	}
}

func (rt *testingRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) {
	if rt.tripped {
		log.Fatalf("Already tripped")
	}

	rt.tripped = true

	if req.URL.String() != rt.url {
		log.Fatalf("Bad url: %v != %v", rt.url, req.URL)
	}
	if req.Method != rt.method {
		log.Fatalf("Bad method: %s != %s", rt.method, req.Method)
	}

	statusCode := 200

	if req.Header.Get("ns-server-ui") == "yes" {
		token, err := req.Cookie("ui-auth-q")
		if err != nil || rt.token != token.Value {
			statusCode = 401
		}
	} else {
		log.Fatal("Expect to be called only with ns-server-ui=yes")
	}

	response := ""
	status := "401 Unauthorized"
	if statusCode == 200 {
		response = fmt.Sprintf(`{"user": "%s", "source": "%s"}`, rt.user, rt.source)
		status = "200 OK"
	}

	respBody := ioutil.NopCloser(strings.NewReader(response))

	return &http.Response{
		Status:        status,
		StatusCode:    statusCode,
		Proto:         "HTTP/1.0",
		ProtoMajor:    1,
		ProtoMinor:    0,
		Header:        http.Header{},
		Body:          respBody,
		ContentLength: -1,
		Trailer:       http.Header{},
		Request:       req,
	}, nil
}

func (rt *testingRoundTripper) resetTripped() {
	rt.tripped = false
}

func (rt *testingRoundTripper) assertTripped(t *testing.T, expected bool) {
	if rt.tripped != expected {
		t.Fatalf("Tripped is not expected. Have: %v, need: %v", rt.tripped, expected)
	}
}

func (rt *testingRoundTripper) setTokenAuth(user, source, token string) {
	rt.token = token
	rt.source = source
	rt.user = user
}

func TestStaleBasic(t *testing.T) {
	for _, period := range []time.Duration{1, 0} {
		a := newAuth(period)
		_, err := a.Auth("asd", "bsd")
		if _, ok := err.(*DBStaleError); !ok {
			t.Fatalf("For period: %v expect ErrStale in stale state. Got %v", period, err)
		}
	}
}

func TestStale(t *testing.T) {
	sync1 := make(chan bool)
	sync2 := make(chan bool)

	go func() {
		sync1 <- true
		sync2 <- true
		close(sync2)
	}()

	a := newAuthForTest(func(ch chan struct{}, timeoutBody func()) {
		<-sync1
		go func() {
			<-sync2
			timeoutBody()
		}()
	})

	_, err := a.Auth("a", "b")
	if _, ok := err.(*DBStaleError); !ok {
		t.Fatalf("Expect ErrStale in stale state. Got %v", err)
	}

	if _, ok := <-sync2; ok {
		t.Fatal("Some bad sync")
	}

}

func doTestStaleThenAdmin(t *testing.T, updateBeforeTimer bool) {
	timerchan := make(chan bool)
	var freshChan chan struct{}
	a := newAuthForTest(func(ch chan struct{}, timeoutBody func()) {
		freshChan = ch
		go func() {
			<-timerchan
			timeoutBody()
		}()
	})

	updatechan := make(chan bool)
	go func() {
		c := cbauthimpl.Cache{Admin: mkUser("admin", "asdasd", "nacl")}
		<-updatechan
		must(a.svc.UpdateDB(&c, nil))
		<-updatechan
	}()

	go func() {
		freshChan <- struct{}{}
		if !updateBeforeTimer {
			close(timerchan)
			return
		}

		updatechan <- true
		updatechan <- true
		close(timerchan)
	}()

	cred, err := a.Auth("admin", "asdasd")
	if updateBeforeTimer {
		must(err)
		if ok, _ := cred.IsAdmin(); !ok {
			t.Fatal("user admin must be recognised as admin")
		}
	} else {
		if _, ok := err.(*DBStaleError); !ok {
			t.Fatal("db must be stale")
		}
		updatechan <- true
		updatechan <- true
	}

	if _, ok := <-timerchan; ok {
		t.Fatal("timerchan must be closed")
	}

	cred, err = a.Auth("admin", "badpass")
	if err != ErrNoAuth {
		t.Fatalf("badpass must not work. Instead got: %v and %v", cred, err)
	}

	cred, err = a.Auth("admin", "asdasd")
	must(err)
	if ok, _ := cred.IsAdmin(); !ok {
		t.Fatal("user admin must be recognised as admin")
	}
}

func TestStaleThenAdminTimerCase(t *testing.T) {
	doTestStaleThenAdmin(t, false)
}

func TestStaleThenAdminUpdateCase(t *testing.T) {
	doTestStaleThenAdmin(t, true)
}

func mkBucket(name, pwd string) (rv cbauthimpl.Bucket) {
	rv.Name = name
	rv.Password = pwd
	return
}

func TestBucketsAuth(t *testing.T) {
	a := newAuth(0)
	must(a.svc.UpdateDB(&cbauthimpl.Cache{
		Buckets: append(cbauthimpl.Cache{}.Buckets,
			mkBucket("default", ""),
			mkBucket("foo", "bar"))}, nil))
	c, err := a.Auth("foo", "bar")
	must(err)
	if !acc(c.CanAccessBucket("foo")) {
		t.Fatal("Expect foo access with right pw to work")
	}
	if acc(c.CanAccessBucket("default")) {
		t.Fatal("Expect default access to not work when authed towards foo")
	}
	if acc(c.CanAccessBucket("unknown")) {
		t.Fatal("Expect unknown bucket access to not work")
	}
	assertAdmins(t, c, false, false)

	c, err = a.Auth("foo", "notbar")
	if err != ErrNoAuth {
		t.Fatalf("Expect wrong password auth to fail. Got: %v and %v", c, err)
	}

	c, err = a.Auth("", "")
	must(err)
	assertAdmins(t, c, false, false)
	if acc(c.CanAccessBucket("foo")) {
		t.Fatal("Expect foo access to not work under anon auth")
	}
	if !acc(c.CanAccessBucket("default")) {
		t.Fatal("Expect default access to work under anon auth")
	}

	// now somebody deletes no-password default bucket
	must(a.svc.UpdateDB(&cbauthimpl.Cache{
		Buckets: append(cbauthimpl.Cache{}.Buckets,
			mkBucket("foo", "bar"))}, nil))
	c, err = a.Auth("foo", "bar")
	must(err)
	assertAdmins(t, c, false, false)
	if !acc(c.CanAccessBucket("foo")) {
		t.Fatal("Expect foo access to work under right pw")
	}
	// and no password access should not work
	c, err = a.Auth("", "")
	if err != ErrNoAuth {
		t.Fatalf("Expect no password access to fail after deletion of default bucket. Got: %v and %v", c, err)
	}
}

func mkNode(host, user, pwd string, ports []int, local bool) (rv cbauthimpl.Node) {
	rv.Host = host
	rv.User = user
	rv.Password = pwd
	rv.Ports = ports
	rv.Local = local
	return
}

func TestServicePwd(t *testing.T) {
	a := newAuth(0)
	c := cbauthimpl.Cache{
		Nodes: append(cbauthimpl.Cache{}.Nodes,
			mkNode("beta.local", "_admin", "foobar", []int{9000, 12000}, false),
			mkNode("chi.local", "_admin", "barfoo", []int{9001, 12001}, false)),
		SpecialUser: "@component",
	}

	must(a.svc.UpdateDB(&c, nil))
	u, p, err := a.GetMemcachedServiceAuth("unknown:9000")
	if _, ok := err.(UnknownHostPortError); u != "" || p != "" || !ok {
		t.Fatal("Expect error trying to get auth for unknown service")
	}
	u, p, _ = a.GetMemcachedServiceAuth("beta.local:9000")
	if u != "_admin" || p != "foobar" {
		t.Fatalf("Expect valid creds for beta.local:9000. Got: %s:%s", u, p)
	}
	u, p, _ = a.GetMemcachedServiceAuth("chi.local:12001")
	if u != "_admin" || p != "barfoo" {
		t.Fatalf("Expect valid creds for chi.local:12001. Got: %s:%s", u, p)
	}

	u, p, _ = a.GetHTTPServiceAuth("chi.local:9001")
	if u != "@component" || p != "barfoo" {
		t.Fatalf("Expect valid http creds for chi.local:9001. Got: %s:%s", u, p)
	}
}

func overrideDefClient(c *http.Client) func() {
	var old *http.Client
	old, http.DefaultClient = http.DefaultClient, c
	return func() {
		http.DefaultClient = old
	}
}

func TestTokenAdmin(t *testing.T) {
	url := "http://127.0.0.1:9000/_auth"

	tr := newTestingRT(t, "POST", url)
	tr.setTokenAuth("Administrator", "admin", "1234567890")

	defer overrideDefClient(&http.Client{Transport: tr})()

	a := newAuth(0)
	must(a.svc.UpdateDB(&cbauthimpl.Cache{AuthCheckURL: url}, nil))

	req, err := http.NewRequest("GET", "http://q:11234/_queryStatsmaybe", nil)
	must(err)
	req.Header.Set("Cookie", "ui-auth-q=1234567890")
	req.Header.Set("ns-server-ui", "yes")

	c, err := a.AuthWebCreds(req)
	must(err)
	tr.assertTripped(t, true)

	assertAdmins(t, c, true, false)

	if c.Name() != "Administrator" {
		t.Errorf("Expect name to be Administrator")
	}

	if c.Source() != "ns_server" {
		t.Errorf("Expect source to be ns_server. Got %s", c.Source())
	}

	if !acc(c.CanAccessBucket("asdasdasdasd")) {
		t.Errorf("Expected to be able to access all buckets. Failed at asdasdasdasd")
	}
	if !acc(c.CanAccessBucket("ffee")) {
		t.Errorf("Expected to be able to access all buckets. Failed at ffee")
	}
}

func TestUnknownHostPortErrorFormatting(t *testing.T) {
	t.Log("Error: ", UnknownHostPortError("asdsd").Error())
}

func TestStaleErrorFormatting(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusUnauthorized)
	}))
	defer s.Close()

	rpcsvc := revrpc.MustService(s.URL + "/test")
	a := newAuth(10 * time.Second)
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		oldDef := revrpc.DefaultBabysitErrorPolicy.(revrpc.DefaultErrorPolicy)
		defer func() {
			revrpc.DefaultBabysitErrorPolicy = oldDef
		}()
		tmpDef := oldDef
		tmpDef.RestartsToExit = 1
		revrpc.DefaultBabysitErrorPolicy = tmpDef
		runRPCForSvc(rpcsvc, a.svc)
		wg.Done()
	}()

	_, err := a.Auth("", "")
	se, ok := err.(*DBStaleError)
	if !ok {
		t.Fatalf("Expected stale error. Got: %s", err)
	}
	errString := se.Error()
	t.Log("error string: ", errString)
	expectedString := "CBAuth database is stale: last reason: Need 200 status!. Got "
	if errString[:len(expectedString)] != expectedString {
		t.Fatalf("Expecting specific prefix of stale error. Got %s", errString)
	}
	wg.Wait()
}
<commit_msg>remove unused resetTripped()<commit_after>package cbauth

import (
	"crypto/hmac"
	"crypto/sha1"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"strings"
	"sync"
	"testing"
	"time"

	"github.com/couchbase/cbauth/cbauthimpl"
	"github.com/couchbase/cbauth/revrpc"
)

func newAuth(initPeriod time.Duration) *authImpl {
	return &authImpl{cbauthimpl.NewSVC(initPeriod, &DBStaleError{})}
}

func must(err error) {
	if err != nil {
		panic(err)
	}
}

func hashPassword(password string, salt []byte) []byte {
	h := hmac.New(sha1.New, salt)
	h.Write([]byte(password))
	return h.Sum(nil)
}

func mkUser(user, password, salt string) (u cbauthimpl.User) {
	u.User = user
	u.Salt = []byte(salt)
	u.Mac = hashPassword(password, u.Salt)
	return
}

func newAuthForTest(body func(freshChan chan struct{}, timeoutBody func())) *authImpl {
	testDur := 555 * time.Hour

	wf := func(period time.Duration, ch chan struct{}, timeoutBody func()) {
		if period != testDur {
			panic(period)
		}
		body(ch, timeoutBody)
	}

	return &authImpl{cbauthimpl.NewSVCForTest(testDur, &DBStaleError{}, wf)}
}

func acc(ok bool, err error) bool {
	must(err)
	return ok
}

func assertAdmins(t *testing.T, c Creds, needAdmin, needROAdmin bool) {
	if acc(c.IsAdmin()) != needAdmin {
		t.Fatalf("admin access must be: %v", needAdmin)
	}
	roadmin := !acc(c.IsAdmin()) && c.CanReadAnyMetadata()
	if roadmin != needROAdmin {
		t.Fatalf("ro-admin access must be: %v", needROAdmin)
	}
}

type testingRoundTripper struct {
	t       *testing.T
	method  string
	url     string
	user    string
	source  string
	token   string
	tripped bool
}

func newTestingRT(t *testing.T, method, uri string) *testingRoundTripper {
	return &testingRoundTripper{
		t:      t,
		method: method,
		url:    uri,
	}
}

func (rt *testingRoundTripper) RoundTrip(req *http.Request) (res *http.Response, err error) {
	if rt.tripped {
		log.Fatalf("Already tripped")
	}

	rt.tripped = true

	if req.URL.String() != rt.url {
		log.Fatalf("Bad url: %v != %v", rt.url, req.URL)
	}
	if req.Method != rt.method {
		log.Fatalf("Bad method: %s != %s", rt.method, req.Method)
	}

	statusCode := 200

	if req.Header.Get("ns-server-ui") == "yes" {
		token, err := req.Cookie("ui-auth-q")
		if err != nil || rt.token != token.Value {
			statusCode = 401
		}
	} else {
		log.Fatal("Expect to be called only with ns-server-ui=yes")
	}

	response := ""
	status := "401 Unauthorized"
	if statusCode == 200 {
		response = fmt.Sprintf(`{"user": "%s", "source": "%s"}`, rt.user, rt.source)
		status = "200 OK"
	}

	respBody := ioutil.NopCloser(strings.NewReader(response))

	return &http.Response{
		Status:        status,
		StatusCode:    statusCode,
		Proto:         "HTTP/1.0",
		ProtoMajor:    1,
		ProtoMinor:    0,
		Header:        http.Header{},
		Body:          respBody,
		ContentLength: -1,
		Trailer:       http.Header{},
		Request:       req,
	}, nil
}

func (rt *testingRoundTripper) assertTripped(t *testing.T, expected bool) {
	if rt.tripped != expected {
		t.Fatalf("Tripped is not expected. Have: %v, need: %v", rt.tripped, expected)
	}
}

func (rt *testingRoundTripper) setTokenAuth(user, source, token string) {
	rt.token = token
	rt.source = source
	rt.user = user
}

func TestStaleBasic(t *testing.T) {
	for _, period := range []time.Duration{1, 0} {
		a := newAuth(period)
		_, err := a.Auth("asd", "bsd")
		if _, ok := err.(*DBStaleError); !ok {
			t.Fatalf("For period: %v expect ErrStale in stale state. Got %v", period, err)
		}
	}
}

func TestStale(t *testing.T) {
	sync1 := make(chan bool)
	sync2 := make(chan bool)

	go func() {
		sync1 <- true
		sync2 <- true
		close(sync2)
	}()

	a := newAuthForTest(func(ch chan struct{}, timeoutBody func()) {
		<-sync1
		go func() {
			<-sync2
			timeoutBody()
		}()
	})

	_, err := a.Auth("a", "b")
	if _, ok := err.(*DBStaleError); !ok {
		t.Fatalf("Expect ErrStale in stale state. Got %v", err)
	}

	if _, ok := <-sync2; ok {
		t.Fatal("Some bad sync")
	}

}

func doTestStaleThenAdmin(t *testing.T, updateBeforeTimer bool) {
	timerchan := make(chan bool)
	var freshChan chan struct{}
	a := newAuthForTest(func(ch chan struct{}, timeoutBody func()) {
		freshChan = ch
		go func() {
			<-timerchan
			timeoutBody()
		}()
	})

	updatechan := make(chan bool)
	go func() {
		c := cbauthimpl.Cache{Admin: mkUser("admin", "asdasd", "nacl")}
		<-updatechan
		must(a.svc.UpdateDB(&c, nil))
		<-updatechan
	}()

	go func() {
		freshChan <- struct{}{}
		if !updateBeforeTimer {
			close(timerchan)
			return
		}

		updatechan <- true
		updatechan <- true
		close(timerchan)
	}()

	cred, err := a.Auth("admin", "asdasd")
	if updateBeforeTimer {
		must(err)
		if ok, _ := cred.IsAdmin(); !ok {
			t.Fatal("user admin must be recognised as admin")
		}
	} else {
		if _, ok := err.(*DBStaleError); !ok {
			t.Fatal("db must be stale")
		}
		updatechan <- true
		updatechan <- true
	}

	if _, ok := <-timerchan; ok {
		t.Fatal("timerchan must be closed")
	}

	cred, err = a.Auth("admin", "badpass")
	if err != ErrNoAuth {
		t.Fatalf("badpass must not work. Instead got: %v and %v", cred, err)
	}

	cred, err = a.Auth("admin", "asdasd")
	must(err)
	if ok, _ := cred.IsAdmin(); !ok {
		t.Fatal("user admin must be recognised as admin")
	}
}

func TestStaleThenAdminTimerCase(t *testing.T) {
	doTestStaleThenAdmin(t, false)
}

func TestStaleThenAdminUpdateCase(t *testing.T) {
	doTestStaleThenAdmin(t, true)
}

func mkBucket(name, pwd string) (rv cbauthimpl.Bucket) {
	rv.Name = name
	rv.Password = pwd
	return
}

func TestBucketsAuth(t *testing.T) {
	a := newAuth(0)
	must(a.svc.UpdateDB(&cbauthimpl.Cache{
		Buckets: append(cbauthimpl.Cache{}.Buckets,
			mkBucket("default", ""),
			mkBucket("foo", "bar"))}, nil))
	c, err := a.Auth("foo", "bar")
	must(err)
	if !acc(c.CanAccessBucket("foo")) {
		t.Fatal("Expect foo access with right pw to work")
	}
	if acc(c.CanAccessBucket("default")) {
		t.Fatal("Expect default access to not work when authed towards foo")
	}
	if acc(c.CanAccessBucket("unknown")) {
		t.Fatal("Expect unknown bucket access to not work")
	}
	assertAdmins(t, c, false, false)

	c, err = a.Auth("foo", "notbar")
	if err != ErrNoAuth {
		t.Fatalf("Expect wrong password auth to fail. Got: %v and %v", c, err)
	}

	c, err = a.Auth("", "")
	must(err)
	assertAdmins(t, c, false, false)
	if acc(c.CanAccessBucket("foo")) {
		t.Fatal("Expect foo access to not work under anon auth")
	}
	if !acc(c.CanAccessBucket("default")) {
		t.Fatal("Expect default access to work under anon auth")
	}

	// now somebody deletes no-password default bucket
	must(a.svc.UpdateDB(&cbauthimpl.Cache{
		Buckets: append(cbauthimpl.Cache{}.Buckets,
			mkBucket("foo", "bar"))}, nil))
	c, err = a.Auth("foo", "bar")
	must(err)
	assertAdmins(t, c, false, false)
	if !acc(c.CanAccessBucket("foo")) {
		t.Fatal("Expect foo access to work under right pw")
	}
	// and no password access should not work
	c, err = a.Auth("", "")
	if err != ErrNoAuth {
		t.Fatalf("Expect no password access to fail after deletion of default bucket. Got: %v and %v", c, err)
	}
}

func mkNode(host, user, pwd string, ports []int, local bool) (rv cbauthimpl.Node) {
	rv.Host = host
	rv.User = user
	rv.Password = pwd
	rv.Ports = ports
	rv.Local = local
	return
}

func TestServicePwd(t *testing.T) {
	a := newAuth(0)
	c := cbauthimpl.Cache{
		Nodes: append(cbauthimpl.Cache{}.Nodes,
			mkNode("beta.local", "_admin", "foobar", []int{9000, 12000}, false),
			mkNode("chi.local", "_admin", "barfoo", []int{9001, 12001}, false)),
		SpecialUser: "@component",
	}

	must(a.svc.UpdateDB(&c, nil))
	u, p, err := a.GetMemcachedServiceAuth("unknown:9000")
	if _, ok := err.(UnknownHostPortError); u != "" || p != "" || !ok {
		t.Fatal("Expect error trying to get auth for unknown service")
	}
	u, p, _ = a.GetMemcachedServiceAuth("beta.local:9000")
	if u != "_admin" || p != "foobar" {
		t.Fatalf("Expect valid creds for beta.local:9000. Got: %s:%s", u, p)
	}
	u, p, _ = a.GetMemcachedServiceAuth("chi.local:12001")
	if u != "_admin" || p != "barfoo" {
		t.Fatalf("Expect valid creds for chi.local:12001. Got: %s:%s", u, p)
	}

	u, p, _ = a.GetHTTPServiceAuth("chi.local:9001")
	if u != "@component" || p != "barfoo" {
		t.Fatalf("Expect valid http creds for chi.local:9001. Got: %s:%s", u, p)
	}
}

func overrideDefClient(c *http.Client) func() {
	var old *http.Client
	old, http.DefaultClient = http.DefaultClient, c
	return func() {
		http.DefaultClient = old
	}
}

func TestTokenAdmin(t *testing.T) {
	url := "http://127.0.0.1:9000/_auth"

	tr := newTestingRT(t, "POST", url)
	tr.setTokenAuth("Administrator", "admin", "1234567890")

	defer overrideDefClient(&http.Client{Transport: tr})()

	a := newAuth(0)
	must(a.svc.UpdateDB(&cbauthimpl.Cache{AuthCheckURL: url}, nil))

	req, err := http.NewRequest("GET", "http://q:11234/_queryStatsmaybe", nil)
	must(err)
	req.Header.Set("Cookie", "ui-auth-q=1234567890")
	req.Header.Set("ns-server-ui", "yes")

	c, err := a.AuthWebCreds(req)
	must(err)
	tr.assertTripped(t, true)

	assertAdmins(t, c, true, false)

	if c.Name() != "Administrator" {
		t.Errorf("Expect name to be Administrator")
	}

	if c.Source() != "ns_server" {
		t.Errorf("Expect source to be ns_server. Got %s", c.Source())
	}

	if !acc(c.CanAccessBucket("asdasdasdasd")) {
		t.Errorf("Expected to be able to access all buckets. Failed at asdasdasdasd")
	}
	if !acc(c.CanAccessBucket("ffee")) {
		t.Errorf("Expected to be able to access all buckets. Failed at ffee")
	}
}

func TestUnknownHostPortErrorFormatting(t *testing.T) {
	t.Log("Error: ", UnknownHostPortError("asdsd").Error())
}

func TestStaleErrorFormatting(t *testing.T) {
	s := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		w.WriteHeader(http.StatusUnauthorized)
	}))
	defer s.Close()

	rpcsvc := revrpc.MustService(s.URL + "/test")
	a := newAuth(10 * time.Second)
	wg := sync.WaitGroup{}
	wg.Add(1)
	go func() {
		oldDef := revrpc.DefaultBabysitErrorPolicy.(revrpc.DefaultErrorPolicy)
		defer func() {
			revrpc.DefaultBabysitErrorPolicy = oldDef
		}()
		tmpDef := oldDef
		tmpDef.RestartsToExit = 1
		revrpc.DefaultBabysitErrorPolicy = tmpDef
		runRPCForSvc(rpcsvc, a.svc)
		wg.Done()
	}()

	_, err := a.Auth("", "")
	se, ok := err.(*DBStaleError)
	if !ok {
		t.Fatalf("Expected stale error. Got: %s", err)
	}
	errString := se.Error()
	t.Log("error string: ", errString)
	expectedString := "CBAuth database is stale: last reason: Need 200 status!. Got "
	if errString[:len(expectedString)] != expectedString {
		t.Fatalf("Expecting specific prefix of stale error. Got %s", errString)
	}
	wg.Wait()
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/urfave/cli"
)

var ErrWrongStateFile = errors.New("fconf: wrong state file")

type WifiState struct {
	Enabled bool  `json:"enabled"`
	Configg *Wifi `json:"config"`
}

func WifiClientCMD(ctx *cli.Context) error {
	if ctx.IsSet(enableFlag) {
		return EnableWifiClient(ctx)
	}
	if ctx.IsSet(disableFlag) {
		return DisableWifi(ctx)
	}
	if ctx.IsSet(removeFlag) {
		return RemoveWifi(ctx)
	}
	if ctx.IsSet(configFlag) {
		return configWifiClient(ctx)
	}
	return nil
}

//EnableWifiClient enables wifi client. If the config flag is set, wifi is
//configured before being enabled.
func EnableWifiClient(ctx *cli.Context) error {
	if ctx.IsSet(configFlag) {
		err := configWifiClient(ctx)
		if err != nil {
			return err
		}
	}
	var i string
	if ctx.IsSet("interface") {
		i = ctx.String("interface")
	} else {
		i = ctx.Args().First()
	}
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	if w.Configg.Interface == "" {
		w.Configg.Interface = "wlan0"
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = restartService(service)
	if err != nil {
		return err
	}
	err = enableService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	w.Enabled = true
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, w.Configg.Interface), data)

}

func wifiClientState(i string) (*WifiState, error) {
	dir := os.Getenv("FCONF_CONFIGDIR")
	if dir == "" {
		dir = fconfConfigDir
	}
	b, err := ioutil.ReadFile(filepath.Join(dir,
		fmt.Sprintf(defaultWifiClientConfig, i)))
	if err != nil {
		return nil, err
	}
	w := &WifiState{}
	err = json.Unmarshal(b, w)
	if err != nil {
		return nil, err
	}
	if w.Configg == nil {
		return nil, ErrWrongStateFile
	}
	return w, nil
}

func configWifiClient(ctx *cli.Context) error {
	base := ctx.String("dir")
	name := ctx.String("name")
	src := ctx.String("config")
	if src == "" {
		return errors.New("fconf: missing configuration source file")
	}
	var b []byte
	var err error
	if src == "stdin" {
		b, err = ReadFromStdin()
		if err != nil {
			return err
		}
	} else {
		b, err = ioutil.ReadFile(src)
		if err != nil {
			return err
		}
	}
	e := Wifi{}
	err = json.Unmarshal(b, &e)
	if err != nil {
		return err
	}
	if e.Interface == "" {
		e.Interface = "wlan0"
	}
	err = checkDir(base)
	if err != nil {
		return err
	}
	if strings.Contains(name, "%s") {
		name = fmt.Sprintf(name, e.Interface)
	}
	filename := filepath.Join(base, name)
	err = CreateSystemdFile(e, filename, 0644)
	if err != nil {
		return err
	}
	fmt.Printf("successful written wifi configuration to %s \n", filename)
	path := "/etc/wpa_supplicant/"
	err = checkDir(path)
	if err != nil {
		return err
	}
	cname := "wpa_supplicant-" + e.Interface + ".conf"
	s, err := wifiConfig(e.Username, e.Password)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(path, cname), []byte(s), 0644)
	if err != nil {
		return err
	}
	state := &WifiState{Configg: &e}
	b, _ = json.Marshal(state)
	fmt.Printf("successful written wifi connection  configuration to %s \n", filepath.Join(path, cname))
	ctx.Set("interface", e.Interface)
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, e.Interface), b)
}

func wifiConfig(username, password string) (string, error) {
	cmd := "/usr/bin/wpa_passphrase"
	firstLine := "ctrl_interface=/run/wpa_supplicant_fconf"
	o, err := exec.Command(cmd, username, password).Output()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s \n \n%s\n", firstLine, string(o)), nil
}

func DisableWifi(ctx *cli.Context) error {
	var i string
	if ctx.IsSet("interface") {
		i = ctx.String("interface")
	} else {
		i = ctx.Args().First()
	}
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = disableService(service)
	if err != nil {
		return err
	}
	err = stopService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	//w.Enabled = false
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, i), data)
}

func RemoveWifi(ctx *cli.Context) error {
	var i string
	if ctx.IsSet("interface") {
		i = ctx.String("interface")
	} else {
		i = ctx.Args().First()
	}
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}

	// remove systemd file
	unit := filepath.Join(networkBase, wirelessService)
	err = removeFile(unit)
	if err != nil {
		return err
	}
	path := "/etc/wpa_supplicant/"
	cname := "wpa_supplicant-" + w.Configg.Interface + ".conf"

	// remove client connection
	err = removeFile(filepath.Join(path, cname))
	if err != nil {
		return err
	}
	err = DisableWifi(ctx)
	if err != nil {
		return err
	}

	// Remove any interface settings
	err = FlushInterface(w.Configg.Interface)
	if err != nil {
		return err
	}

	// remove the state file
	stateFile := filepath.Join(stateDir(),
		fmt.Sprintf(defaultWifiClientConfig, i))
	return removeFile(stateFile)
}
<commit_msg>wifi: Fix getting interface<commit_after>package main

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"github.com/urfave/cli"
)

var ErrWrongStateFile = errors.New("fconf: wrong state file")

type WifiState struct {
	Enabled bool  `json:"enabled"`
	Configg *Wifi `json:"config"`
}

func WifiClientCMD(ctx *cli.Context) error {
	if ctx.IsSet(enableFlag) {
		return EnableWifiClient(ctx)
	}
	if ctx.IsSet(disableFlag) {
		return DisableWifi(ctx)
	}
	if ctx.IsSet(removeFlag) {
		return RemoveWifi(ctx)
	}
	if ctx.IsSet(configFlag) {
		return configWifiClient(ctx)
	}
	return nil
}

//EnableWifiClient enables wifi client. If the config flag is set, wifi is
//configured before being enabled.
func EnableWifiClient(ctx *cli.Context) error {
	if ctx.IsSet(configFlag) {
		err := configWifiClient(ctx)
		if err != nil {
			return err
		}
	}
	var i string
	if ctx.IsSet("interface") {
		i = ctx.String("interface")
	} else {
		i = ctx.Args().First()
	}
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	if w.Configg.Interface == "" {
		w.Configg.Interface = "wlan0"
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = restartService(service)
	if err != nil {
		return err
	}
	err = enableService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	w.Enabled = true
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, w.Configg.Interface), data)

}

func wifiClientState(i string) (*WifiState, error) {
	dir := os.Getenv("FCONF_CONFIGDIR")
	if dir == "" {
		dir = fconfConfigDir
	}
	b, err := ioutil.ReadFile(filepath.Join(dir,
		fmt.Sprintf(defaultWifiClientConfig, i)))
	if err != nil {
		return nil, err
	}
	w := &WifiState{}
	err = json.Unmarshal(b, w)
	if err != nil {
		return nil, err
	}
	if w.Configg == nil {
		return nil, ErrWrongStateFile
	}
	return w, nil
}

func configWifiClient(ctx *cli.Context) error {
	base := ctx.String("dir")
	name := ctx.String("name")
	src := ctx.String("config")
	if src == "" {
		return errors.New("fconf: missing configuration source file")
	}
	var b []byte
	var err error
	if src == "stdin" {
		b, err = ReadFromStdin()
		if err != nil {
			return err
		}
	} else {
		b, err = ioutil.ReadFile(src)
		if err != nil {
			return err
		}
	}
	e := Wifi{}
	err = json.Unmarshal(b, &e)
	if err != nil {
		return err
	}
	if e.Interface == "" {
		e.Interface = "wlan0"
	}
	err = checkDir(base)
	if err != nil {
		return err
	}
	if strings.Contains(name, "%s") {
		name = fmt.Sprintf(name, e.Interface)
	}
	filename := filepath.Join(base, name)
	err = CreateSystemdFile(e, filename, 0644)
	if err != nil {
		return err
	}
	fmt.Printf("successful written wifi configuration to %s \n", filename)
	path := "/etc/wpa_supplicant/"
	err = checkDir(path)
	if err != nil {
		return err
	}
	cname := "wpa_supplicant-" + e.Interface + ".conf"
	s, err := wifiConfig(e.Username, e.Password)
	if err != nil {
		return err
	}
	err = ioutil.WriteFile(filepath.Join(path, cname), []byte(s), 0644)
	if err != nil {
		return err
	}
	state := &WifiState{Configg: &e}
	b, _ = json.Marshal(state)
	fmt.Printf("successful written wifi connection  configuration to %s \n", filepath.Join(path, cname))
	ctx.GlobalSet("interface", e.Interface)
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, e.Interface), b)
}

func wifiConfig(username, password string) (string, error) {
	cmd := "/usr/bin/wpa_passphrase"
	firstLine := "ctrl_interface=/run/wpa_supplicant_fconf"
	o, err := exec.Command(cmd, username, password).Output()
	if err != nil {
		return "", err
	}
	return fmt.Sprintf("%s \n \n%s\n", firstLine, string(o)), nil
}

func DisableWifi(ctx *cli.Context) error {
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}
	service := "wpa_supplicant@" + w.Configg.Interface
	err = disableService(service)
	if err != nil {
		return err
	}
	err = stopService(service)
	if err != nil {
		return err
	}
	err = restartService("systemd-networkd")
	if err != nil {
		return err
	}
	//w.Enabled = false
	data, err := json.Marshal(w)
	if err != nil {
		return err
	}
	return keepState(
		fmt.Sprintf(defaultWifiClientConfig, i), data)
}

func RemoveWifi(ctx *cli.Context) error {
	i := getInterface(ctx)
	if i == "" {
		return errors.New("missing interface, you must specify interface")
	}
	w, err := wifiClientState(i)
	if err != nil {
		return err
	}

	// remove systemd file
	unit := filepath.Join(networkBase, wirelessService)
	err = removeFile(unit)
	if err != nil {
		return err
	}
	path := "/etc/wpa_supplicant/"
	cname := "wpa_supplicant-" + w.Configg.Interface + ".conf"

	// remove client connection
	err = removeFile(filepath.Join(path, cname))
	if err != nil {
		return err
	}
	err = DisableWifi(ctx)
	if err != nil {
		return err
	}

	// Remove any interface settings
	err = FlushInterface(w.Configg.Interface)
	if err != nil {
		return err
	}

	// remove the state file
	stateFile := filepath.Join(stateDir(),
		fmt.Sprintf(defaultWifiClientConfig, i))
	return removeFile(stateFile)
}
<|endoftext|>


<commit_before>package toml

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

func TestParse(t *testing.T) {

	inputs := []string{
		`     #foo`,
		`#foo`,
		`#`,
		"\n\n\n",
		"#one\n   # two   \n",
		`a = false`,
		`abc = false`,
		`  abc  = false  # foo`,
		`'abc' = false`,
		`"foo bar" = false`,
		`"hello\tworld" = false`,
		`"hello \u1234 foo" = false`,
		`a.b.c = false`,
		`a."b".c = true`,
	}

	for i, data := range inputs {
		t.Run(fmt.Sprintf("example %d", i), func(t *testing.T) {
			fmt.Printf("input:\n\t`%s`\n", data)
			doc, err := Parse([]byte(data))
			assert.NoError(t, err)
			fmt.Println(doc)
		})
	}
}
<commit_msg>Check for allocs<commit_after>package toml

import (
	"fmt"
	"testing"

	"github.com/stretchr/testify/assert"
)

var inputs = []string{
	`     #foo`,
	`#foo`,
	`#`,
	"\n\n\n",
	"#one\n   # two   \n",
	`a = false`,
	`abc = false`,
	`  abc  = false  # foo`,
	`'abc' = false`,
	`"foo bar" = false`,
	`"hello\tworld" = false`,
	`"hello \u1234 foo" = false`,
	`a.b.c = false`,
	`a."b".c = true`,
}

func TestParse(t *testing.T) {
	for i, data := range inputs {
		t.Run(fmt.Sprintf("example %d", i), func(t *testing.T) {
			fmt.Printf("input:\n\t`%s`\n", data)
			doc, err := Parse([]byte(data))
			assert.NoError(t, err)
			fmt.Println(doc)
		})
	}
}

type noopBuilder struct {
}

func (n noopBuilder) Whitespace(b []byte) {
}

func (n noopBuilder) Comment(b []byte) {
}

func (n noopBuilder) UnquotedKey(b []byte) {
}

func (n noopBuilder) LiteralString(b []byte) {
}

func (n noopBuilder) BasicString(b []byte) {
}

func (n noopBuilder) Dot(b []byte) {
}

func (n noopBuilder) Boolean(b []byte) {
}

func (n noopBuilder) Equal(b []byte) {
}

func BenchmarkParseAll(b *testing.B) {
	b.ReportAllocs()

	for i := 0; i < b.N; i++ {
		for _, data := range inputs {
			builder := noopBuilder{}
			p := parser{builder: &builder, data: []byte(data)}
			err := p.parse()
			if err != nil {
				b.Fatalf("error: %s", err)
			}
		}
	}
}
<|endoftext|>


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

package sql

import (
	"errors"
	"fmt"

	"github.com/cockroachdb/cockroach/client"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/sql/parser"
	"github.com/cockroachdb/cockroach/sql/sqlbase"
	"github.com/pkg/errors"
)

// TablesByID maps table IDs to looked up descriptors.
type TablesByID map[sqlbase.ID]*sqlbase.TableDescriptor

// FKCheck indicates a kind of FK check (delete, insert, or both).
type FKCheck int

const (
	// CheckDeletes checks if rows reference a changed value.
	CheckDeletes FKCheck = iota
	// CheckInserts checks if a new/changed value references an existing row.
	CheckInserts
	// CheckUpdates checks all references (CheckDeletes+CheckInserts).
	CheckUpdates
)

// TablesNeededForFKs calculates the IDs of the additional TableDescriptors that
// will be needed for FK checking delete and/or insert operations on `table`.
//
// NB: the returned map's values are *not* set -- higher level calling code, eg
// in planner, should fill the map's values by acquiring leases. This function
// is essentially just returning a slice of IDs, but the empty map can be filled
// in place and reused, avoiding a second allocation.
func TablesNeededForFKs(table *sqlbase.TableDescriptor, usage FKCheck) TablesByID {
	var ret TablesByID
	for _, idx := range table.AllNonDropIndexes() {
		if usage != CheckDeletes && idx.ForeignKey != nil {
			if ret == nil {
				ret = make(TablesByID)
			}
			ret[idx.ForeignKey.Table] = nil
		}
		if usage != CheckInserts {
			for _, idx := range table.AllNonDropIndexes() {
				for _, ref := range idx.ReferencedBy {
					if ret == nil {
						ret = make(TablesByID)
					}
					ret[ref.Table] = nil
				}
			}
		}
	}
	return ret
}

type fkInsertHelper map[sqlbase.IndexID][]baseFKHelper

var errSkipUnsedFK = errors.New("no columns involved in FK included in writer")

func makeFKInsertHelper(
	txn *client.Txn, table *sqlbase.TableDescriptor, otherTables TablesByID, colMap map[sqlbase.ColumnID]int,
) (fkInsertHelper, error) {
	var fks fkInsertHelper
	for _, idx := range table.AllNonDropIndexes() {
		if idx.ForeignKey != nil {
			fk, err := makeBaseFKHelper(txn, otherTables, idx, idx.ForeignKey, colMap)
			if err == errSkipUnsedFK {
				continue
			}
			if err != nil {
				return fks, err
			}
			if fks == nil {
				fks = make(fkInsertHelper)
			}
			fks[idx.ID] = append(fks[idx.ID], fk)
		}
	}
	return fks, nil
}

func (fks fkInsertHelper) checkAll(row parser.DTuple) error {
	for idx := range fks {
		if err := fks.checkIdx(idx, row); err != nil {
			return err
		}
	}
	return nil
}

func (fks fkInsertHelper) checkIdx(idx sqlbase.IndexID, row parser.DTuple) error {
	for _, fk := range fks[idx] {
		nulls := true
		for i := range fk.searchIdx.ColumnIDs {
			found, ok := fk.ids[fk.searchIdx.ColumnIDs[i]]
			if !ok {
				panic("fk ids missing column id")
			}
			nulls = nulls && row[found] == parser.DNull
		}
		if nulls {
			continue
		}

		found, err := fk.check(row)
		if err != nil {
			return err
		}
		if found == nil {
			fkValues := make(parser.DTuple, len(fk.searchIdx.ColumnIDs))
			for i := range fk.searchIdx.ColumnIDs {
				fkValues[i] = row[fk.ids[fk.searchIdx.ColumnIDs[i]]]
			}
			return fmt.Errorf("foreign key violation: value %s not found in %s@%s %s", fkValues, fk.searchTable.Name, fk.searchIdx.Name, fk.searchIdx.ColumnNames)
		}
	}
	return nil
}

type fkDeleteHelper map[sqlbase.IndexID][]baseFKHelper

func makeFKDeleteHelper(
	txn *client.Txn, table *sqlbase.TableDescriptor, otherTables TablesByID, colMap map[sqlbase.ColumnID]int,
) (fkDeleteHelper, error) {
	var fks fkDeleteHelper
	for _, idx := range table.AllNonDropIndexes() {
		for _, ref := range idx.ReferencedBy {
			fk, err := makeBaseFKHelper(txn, otherTables, idx, ref, colMap)
			if err == errSkipUnsedFK {
				continue
			}
			if err != nil {
				return fks, err
			}
			if fks == nil {
				fks = make(fkDeleteHelper)
			}
			fks[idx.ID] = append(fks[idx.ID], fk)
		}
	}
	return fks, nil
}

func (fks fkDeleteHelper) checkAll(row parser.DTuple) error {
	for idx := range fks {
		if err := fks.checkIdx(idx, row); err != nil {
			return err
		}
	}
	return nil
}

func (fks fkDeleteHelper) checkIdx(idx sqlbase.IndexID, row parser.DTuple) error {
	for _, fk := range fks[idx] {
		found, err := fk.check(row)
		if err != nil {
			return err
		}
		if found != nil {
			fkValues := make(parser.DTuple, len(fk.searchIdx.ColumnIDs))
			for i := range fk.searchIdx.ColumnIDs {
				fkValues[i] = row[fk.ids[fk.searchIdx.ColumnIDs[i]]]
			}
			return fmt.Errorf("foreign key violation: value(s) %v in columns %s referenced in table %q",
				fkValues, fk.writeIdx.ColumnNames, fk.searchTable.Name)
		}
	}
	return nil
}

type fkUpdateHelper struct {
	inbound  fkDeleteHelper // Check old values are not referenced.
	outbound fkInsertHelper // Check rows referenced by new values still exist.
}

func makeFKUpdateHelper(
	txn *client.Txn, table *sqlbase.TableDescriptor, otherTables TablesByID, colMap map[sqlbase.ColumnID]int,
) (fkUpdateHelper, error) {
	ret := fkUpdateHelper{}
	var err error
	if ret.inbound, err = makeFKDeleteHelper(txn, table, otherTables, colMap); err != nil {
		return ret, err
	}
	ret.outbound, err = makeFKInsertHelper(txn, table, otherTables, colMap)
	return ret, err
}

func (fks fkUpdateHelper) checkIdx(idx sqlbase.IndexID, oldValues, newValues parser.DTuple) error {
	if err := fks.inbound.checkIdx(idx, oldValues); err != nil {
		return err
	}
	return fks.outbound.checkIdx(idx, newValues)
}

type baseFKHelper struct {
	txn          *client.Txn
	rf           sqlbase.RowFetcher
	searchTable  *sqlbase.TableDescriptor // the table being searched (for err msg)
	searchIdx    *sqlbase.IndexDescriptor // the index that must (not) contain a value
	writeIdx     sqlbase.IndexDescriptor  // the index we want to modify
	searchPrefix []byte                   // prefix of keys in searchIdx
	ids          map[sqlbase.ColumnID]int // col IDs
}

func makeBaseFKHelper(
	txn *client.Txn,
	otherTables TablesByID,
	writeIdx sqlbase.IndexDescriptor,
	ref *sqlbase.TableAndIndexID,
	colMap map[sqlbase.ColumnID]int, // col ids (for idx being written) to row offset.
) (baseFKHelper, error) {
	b := baseFKHelper{txn: txn, writeIdx: writeIdx, searchPrefix: ref.IndexKeyPrefix()}
	searchTable, ok := otherTables[ref.Table]
	if !ok {
		return b, errors.Errorf("referenced table %d not in provided table map %+v", ref.Table, otherTables)
	}
	b.searchTable = searchTable
	searchIdx, err := searchTable.FindIndexByID(ref.Index)
	if err != nil {
		return b, err
	}
	b.searchIdx = searchIdx
	ids := colIDtoRowIndexFromCols(searchTable.Columns)
	needed := make([]bool, len(ids))
	for _, i := range searchIdx.ColumnIDs {
		needed[ids[i]] = true
	}
	isSecondary := searchTable.PrimaryIndex.ID != searchIdx.ID
	err = b.rf.Init(searchTable, ids, searchIdx, false, isSecondary, searchTable.Columns, needed)
	if err != nil {
		return b, err
	}

	b.ids = make(map[sqlbase.ColumnID]int, len(writeIdx.ColumnIDs))
	nulls := true
	for i := range writeIdx.ColumnIDs {
		if found, ok := colMap[writeIdx.ColumnIDs[i]]; ok {
			b.ids[searchIdx.ColumnIDs[i]] = found
			nulls = false
		}
	}
	if nulls {
		return b, errSkipUnsedFK
	}
	return b, nil
}

// TODO(dt): Batch checks of many rows.
func (f baseFKHelper) check(values parser.DTuple) (parser.DTuple, error) {
	keyBytes, _, err := sqlbase.EncodeIndexKey(f.searchIdx, f.ids, values, f.searchPrefix)
	if err != nil {
		return nil, err
	}
	key := roachpb.Key(keyBytes)

	spans := sqlbase.Spans{sqlbase.Span{Start: key, End: key.PrefixEnd()}}
	if err := f.rf.StartScan(f.txn, spans, 1); err != nil {
		return nil, err
	}
	return f.rf.NextRow()
}
<commit_msg>sql: fix double import of 'errors' from ships-passing merge<commit_after>// Copyright 2016 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

package sql

import (
	"fmt"

	"github.com/cockroachdb/cockroach/client"
	"github.com/cockroachdb/cockroach/roachpb"
	"github.com/cockroachdb/cockroach/sql/parser"
	"github.com/cockroachdb/cockroach/sql/sqlbase"
	"github.com/pkg/errors"
)

// TablesByID maps table IDs to looked up descriptors.
type TablesByID map[sqlbase.ID]*sqlbase.TableDescriptor

// FKCheck indicates a kind of FK check (delete, insert, or both).
type FKCheck int

const (
	// CheckDeletes checks if rows reference a changed value.
	CheckDeletes FKCheck = iota
	// CheckInserts checks if a new/changed value references an existing row.
	CheckInserts
	// CheckUpdates checks all references (CheckDeletes+CheckInserts).
	CheckUpdates
)

// TablesNeededForFKs calculates the IDs of the additional TableDescriptors that
// will be needed for FK checking delete and/or insert operations on `table`.
//
// NB: the returned map's values are *not* set -- higher level calling code, eg
// in planner, should fill the map's values by acquiring leases. This function
// is essentially just returning a slice of IDs, but the empty map can be filled
// in place and reused, avoiding a second allocation.
func TablesNeededForFKs(table *sqlbase.TableDescriptor, usage FKCheck) TablesByID {
	var ret TablesByID
	for _, idx := range table.AllNonDropIndexes() {
		if usage != CheckDeletes && idx.ForeignKey != nil {
			if ret == nil {
				ret = make(TablesByID)
			}
			ret[idx.ForeignKey.Table] = nil
		}
		if usage != CheckInserts {
			for _, idx := range table.AllNonDropIndexes() {
				for _, ref := range idx.ReferencedBy {
					if ret == nil {
						ret = make(TablesByID)
					}
					ret[ref.Table] = nil
				}
			}
		}
	}
	return ret
}

type fkInsertHelper map[sqlbase.IndexID][]baseFKHelper

var errSkipUnsedFK = errors.New("no columns involved in FK included in writer")

func makeFKInsertHelper(
	txn *client.Txn, table *sqlbase.TableDescriptor, otherTables TablesByID, colMap map[sqlbase.ColumnID]int,
) (fkInsertHelper, error) {
	var fks fkInsertHelper
	for _, idx := range table.AllNonDropIndexes() {
		if idx.ForeignKey != nil {
			fk, err := makeBaseFKHelper(txn, otherTables, idx, idx.ForeignKey, colMap)
			if err == errSkipUnsedFK {
				continue
			}
			if err != nil {
				return fks, err
			}
			if fks == nil {
				fks = make(fkInsertHelper)
			}
			fks[idx.ID] = append(fks[idx.ID], fk)
		}
	}
	return fks, nil
}

func (fks fkInsertHelper) checkAll(row parser.DTuple) error {
	for idx := range fks {
		if err := fks.checkIdx(idx, row); err != nil {
			return err
		}
	}
	return nil
}

func (fks fkInsertHelper) checkIdx(idx sqlbase.IndexID, row parser.DTuple) error {
	for _, fk := range fks[idx] {
		nulls := true
		for i := range fk.searchIdx.ColumnIDs {
			found, ok := fk.ids[fk.searchIdx.ColumnIDs[i]]
			if !ok {
				panic("fk ids missing column id")
			}
			nulls = nulls && row[found] == parser.DNull
		}
		if nulls {
			continue
		}

		found, err := fk.check(row)
		if err != nil {
			return err
		}
		if found == nil {
			fkValues := make(parser.DTuple, len(fk.searchIdx.ColumnIDs))
			for i := range fk.searchIdx.ColumnIDs {
				fkValues[i] = row[fk.ids[fk.searchIdx.ColumnIDs[i]]]
			}
			return fmt.Errorf("foreign key violation: value %s not found in %s@%s %s", fkValues, fk.searchTable.Name, fk.searchIdx.Name, fk.searchIdx.ColumnNames)
		}
	}
	return nil
}

type fkDeleteHelper map[sqlbase.IndexID][]baseFKHelper

func makeFKDeleteHelper(
	txn *client.Txn, table *sqlbase.TableDescriptor, otherTables TablesByID, colMap map[sqlbase.ColumnID]int,
) (fkDeleteHelper, error) {
	var fks fkDeleteHelper
	for _, idx := range table.AllNonDropIndexes() {
		for _, ref := range idx.ReferencedBy {
			fk, err := makeBaseFKHelper(txn, otherTables, idx, ref, colMap)
			if err == errSkipUnsedFK {
				continue
			}
			if err != nil {
				return fks, err
			}
			if fks == nil {
				fks = make(fkDeleteHelper)
			}
			fks[idx.ID] = append(fks[idx.ID], fk)
		}
	}
	return fks, nil
}

func (fks fkDeleteHelper) checkAll(row parser.DTuple) error {
	for idx := range fks {
		if err := fks.checkIdx(idx, row); err != nil {
			return err
		}
	}
	return nil
}

func (fks fkDeleteHelper) checkIdx(idx sqlbase.IndexID, row parser.DTuple) error {
	for _, fk := range fks[idx] {
		found, err := fk.check(row)
		if err != nil {
			return err
		}
		if found != nil {
			fkValues := make(parser.DTuple, len(fk.searchIdx.ColumnIDs))
			for i := range fk.searchIdx.ColumnIDs {
				fkValues[i] = row[fk.ids[fk.searchIdx.ColumnIDs[i]]]
			}
			return fmt.Errorf("foreign key violation: value(s) %v in columns %s referenced in table %q",
				fkValues, fk.writeIdx.ColumnNames, fk.searchTable.Name)
		}
	}
	return nil
}

type fkUpdateHelper struct {
	inbound  fkDeleteHelper // Check old values are not referenced.
	outbound fkInsertHelper // Check rows referenced by new values still exist.
}

func makeFKUpdateHelper(
	txn *client.Txn, table *sqlbase.TableDescriptor, otherTables TablesByID, colMap map[sqlbase.ColumnID]int,
) (fkUpdateHelper, error) {
	ret := fkUpdateHelper{}
	var err error
	if ret.inbound, err = makeFKDeleteHelper(txn, table, otherTables, colMap); err != nil {
		return ret, err
	}
	ret.outbound, err = makeFKInsertHelper(txn, table, otherTables, colMap)
	return ret, err
}

func (fks fkUpdateHelper) checkIdx(idx sqlbase.IndexID, oldValues, newValues parser.DTuple) error {
	if err := fks.inbound.checkIdx(idx, oldValues); err != nil {
		return err
	}
	return fks.outbound.checkIdx(idx, newValues)
}

type baseFKHelper struct {
	txn          *client.Txn
	rf           sqlbase.RowFetcher
	searchTable  *sqlbase.TableDescriptor // the table being searched (for err msg)
	searchIdx    *sqlbase.IndexDescriptor // the index that must (not) contain a value
	writeIdx     sqlbase.IndexDescriptor  // the index we want to modify
	searchPrefix []byte                   // prefix of keys in searchIdx
	ids          map[sqlbase.ColumnID]int // col IDs
}

func makeBaseFKHelper(
	txn *client.Txn,
	otherTables TablesByID,
	writeIdx sqlbase.IndexDescriptor,
	ref *sqlbase.TableAndIndexID,
	colMap map[sqlbase.ColumnID]int, // col ids (for idx being written) to row offset.
) (baseFKHelper, error) {
	b := baseFKHelper{txn: txn, writeIdx: writeIdx, searchPrefix: ref.IndexKeyPrefix()}
	searchTable, ok := otherTables[ref.Table]
	if !ok {
		return b, errors.Errorf("referenced table %d not in provided table map %+v", ref.Table, otherTables)
	}
	b.searchTable = searchTable
	searchIdx, err := searchTable.FindIndexByID(ref.Index)
	if err != nil {
		return b, err
	}
	b.searchIdx = searchIdx
	ids := colIDtoRowIndexFromCols(searchTable.Columns)
	needed := make([]bool, len(ids))
	for _, i := range searchIdx.ColumnIDs {
		needed[ids[i]] = true
	}
	isSecondary := searchTable.PrimaryIndex.ID != searchIdx.ID
	err = b.rf.Init(searchTable, ids, searchIdx, false, isSecondary, searchTable.Columns, needed)
	if err != nil {
		return b, err
	}

	b.ids = make(map[sqlbase.ColumnID]int, len(writeIdx.ColumnIDs))
	nulls := true
	for i := range writeIdx.ColumnIDs {
		if found, ok := colMap[writeIdx.ColumnIDs[i]]; ok {
			b.ids[searchIdx.ColumnIDs[i]] = found
			nulls = false
		}
	}
	if nulls {
		return b, errSkipUnsedFK
	}
	return b, nil
}

// TODO(dt): Batch checks of many rows.
func (f baseFKHelper) check(values parser.DTuple) (parser.DTuple, error) {
	keyBytes, _, err := sqlbase.EncodeIndexKey(f.searchIdx, f.ids, values, f.searchPrefix)
	if err != nil {
		return nil, err
	}
	key := roachpb.Key(keyBytes)

	spans := sqlbase.Spans{sqlbase.Span{Start: key, End: key.PrefixEnd()}}
	if err := f.rf.StartScan(f.txn, spans, 1); err != nil {
		return nil, err
	}
	return f.rf.NextRow()
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

type testOption struct{
	IsVerbose bool
	IsDocker bool
}

type testCommandInfo struct{
	Command string
	Func func(testOption) int
	NeedGlide bool
	NeedYarn bool
	Description string
}

var testCommands = []*testCommandInfo{
	&testCommandInfo{
		Command: "sunit",
		Func: runGoUnitTest,
		NeedGlide: true,
		NeedYarn: false,
		Description: "Run goapp test",
	},
	&testCommandInfo{
		Command: "cunit",
		Func: runAngularUnitTest,
		NeedGlide: false,
		NeedYarn: true,
		Description: "Run ng test",
	},
	&testCommandInfo{
		Command: "e2e",
		Func: runE2ETest,
		NeedGlide: true,
		NeedYarn: true,
		Description: "Run ng e2e",
	},
}

func main() {
	// os.Args[0] points to the compiled binary
	_, arg0, _, _ := runtime.Caller(0)
	parser := flag.NewFlagSet(arg0, flag.ExitOnError)
	parser.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [Options] [Commands]:\n", arg0)
		fmt.Fprintf(os.Stderr, "Options:\n")
		parser.PrintDefaults()
		fmt.Fprintf(os.Stderr, "Commands:\n")
		for _, c := range(testCommands){
			fmt.Fprintf(os.Stderr, "  %s: %s\n", c.Command, c.Description)
		}
	}
	option := testOption{}
	parser.BoolVar(&option.IsVerbose, "v", false, "Verbose output")
	parser.BoolVar(&option.IsVerbose, "verbose", false, "Verbose output")
	parser.BoolVar(&option.IsDocker, "docker", false, "Specify when running in docker")
	noSetup := parser.Bool("nosetup", false, "Skip calling glide install or yarn")
	parser.Parse(os.Args[1:])
	commands := parser.Args()
	if len(commands) <= 0 {
		for _, c := range(testCommands){
			commands = append(commands, c.Command)
		}
	}

	var needGlide bool
	var needYarn bool
	commandsToRun := []*testCommandInfo{}
	commandMap := map[string]*testCommandInfo{}
	for _, c := range(testCommands){
		commandMap[c.Command] = c
	}
	for _, command := range(commands) {
		c, ok := commandMap[command]
		if !ok {
			fmt.Fprintf(os.Stderr, "Unknown Command: %s\n", command)
			flag.Usage()
			os.Exit(2)
		}
		commandsToRun = append(commandsToRun, c)
		needGlide = needGlide || c.NeedGlide
		needYarn = needYarn || c.NeedYarn
	}

	// locate the project directory
	// this file should be located at projectdir/tool/
	projectDir := filepath.Clean(filepath.Join(filepath.Dir(arg0), ".."))
	if err := os.Chdir(projectDir); err != nil {
		panic(err)
	}

	if needGlide && !*noSetup {
		if ret := runCommand("glide", "install"); ret != 0 {
			os.Exit(ret)
		}
	}
	if needYarn && !*noSetup {
		func(){
			popd := pushd("client")
			defer popd()
			if ret := runCommand("yarn"); ret != 0 {
				os.Exit(ret)
			}
		}()
	}
	for _, c := range(commandsToRun){
		if ret := c.Func(option); ret != 0 {
			os.Exit(ret)
		}
	}
	os.Exit(0)
}

func runCommand(name string, args... string) int {
	fmt.Fprintf(os.Stderr, "# %v %v\n", name, strings.Join(args, " "))
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			s := exitErr.Sys()
			if waitStatus, ok := s.(*syscall.WaitStatus); ok {
				return waitStatus.ExitStatus()
			}
		}
		panic(err)
	}
	return 0
}

func shutdownServer(cmd *exec.Cmd) bool {
	if resp, err := http.Get("http://localhost:8000/quit"); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to stop server: %v", err)
		return false
	} else if resp.StatusCode != http.StatusOK {
		fmt.Fprintf(os.Stderr, "Failed to stop server: %v", resp)
		return false
	}
	return true
}

func pushd(dir string) func() {
	orig, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if err := os.Chdir(dir); err != nil {
		panic(err)
	}
	return func() {
		if err := os.Chdir(orig); err != nil {
			panic(err)
		}
	}
}

func runGoUnitTest(option testOption) int {
	args := []string{
		"test",
		"./server",
		"./testutil",
	}
	if option.IsVerbose {
		args = append(args, "-v")
	}
	return runCommand("goapp", args...)
}

func runAngularUnitTest(option testOption) int {
	popd := pushd("client")
	defer popd()

	args := []string {
		"run",
		"test",
		"--",
		"--single-run",
		"true",
	}
	if option.IsDocker {
		args = append(args, "--config", "karma-docker.conf.js")
	}

	return runCommand("yarn", args...)
}

func runE2ETest(option testOption) int {
	serverStdout, err := os.OpenFile(
		"server.stdout.log",
		os.O_RDWR|os.O_CREATE|os.O_APPEND,
		0644,
	)
	if err != nil {
		panic(err)
	}
	defer serverStdout.Close()
	serverStderr, err := os.OpenFile(
		"server.stderr.log",
		os.O_RDWR|os.O_CREATE|os.O_APPEND,
		0644,
	)
	if err != nil {
		panic(err)
	}
	defer serverStderr.Close()

	python, err := exec.LookPath("python")
	if err != nil {
		panic(err)
	}
	devAppServer, err := exec.LookPath("dev_appserver.py")
	if err != nil {
		panic(err)
	}
	args := []string {
		devAppServer,
		"--enable_watching_go_path=false",
		"--clear_datastore=true",
		"--datastore_consistency_policy=consistent",
		"--automatic_restart=false",
		"--watcher_ignore_re=.*",
		"--skip_sdk_update_check=true",
		"server/app.yaml",
	}

	fmt.Fprintf(os.Stderr, "# %v %v\n", python, strings.Join(args, " "))
	cmd := exec.Command(python, args...)
	cmd.Stdout = serverStdout
	cmd.Stderr = serverStderr
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	defer shutdownServer(cmd)

	url := "http://localhost:8080/"
	seconds := 30
	fmt.Fprintf(os.Stderr, "Wait for server gets ready for %v seconds: %v\n", seconds, url)
	limit := time.Now().Add(time.Duration(seconds) * time.Second)
	client := &http.Client {
		Timeout: time.Duration(seconds) * time.Second,
	}
	for true {
		if time.Since(limit) > 0 {
			panic(fmt.Sprintf("Server didnot get ready after %v seconds", seconds))
		}
		if _, err := client.Get(url); err == nil {
			break
		}
		time.Sleep(time.Duration(100) * time.Millisecond)
	}

	popd := pushd("client")
	defer popd()

	e2eArgs := []string {
		"run",
		"e2e",
		"--",
		"--serve",
		"true",
		"--port",
		"4200",
	}
	if option.IsDocker {
		e2eArgs = append(e2eArgs, "--config", "protractor-docker.conf.js")
	}

	return runCommand("yarn", e2eArgs...)
}
<commit_msg>Add --require_indexes=true, this improves the performance of datastore enormously<commit_after>package main

import (
	"flag"
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"strings"
	"syscall"
	"time"
)

type testOption struct{
	IsVerbose bool
	IsDocker bool
}

type testCommandInfo struct{
	Command string
	Func func(testOption) int
	NeedGlide bool
	NeedYarn bool
	Description string
}

var testCommands = []*testCommandInfo{
	&testCommandInfo{
		Command: "sunit",
		Func: runGoUnitTest,
		NeedGlide: true,
		NeedYarn: false,
		Description: "Run goapp test",
	},
	&testCommandInfo{
		Command: "cunit",
		Func: runAngularUnitTest,
		NeedGlide: false,
		NeedYarn: true,
		Description: "Run ng test",
	},
	&testCommandInfo{
		Command: "e2e",
		Func: runE2ETest,
		NeedGlide: true,
		NeedYarn: true,
		Description: "Run ng e2e",
	},
}

func main() {
	// os.Args[0] points to the compiled binary
	_, arg0, _, _ := runtime.Caller(0)
	parser := flag.NewFlagSet(arg0, flag.ExitOnError)
	parser.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [Options] [Commands]:\n", arg0)
		fmt.Fprintf(os.Stderr, "Options:\n")
		parser.PrintDefaults()
		fmt.Fprintf(os.Stderr, "Commands:\n")
		for _, c := range(testCommands){
			fmt.Fprintf(os.Stderr, "  %s: %s\n", c.Command, c.Description)
		}
	}
	option := testOption{}
	parser.BoolVar(&option.IsVerbose, "v", false, "Verbose output")
	parser.BoolVar(&option.IsVerbose, "verbose", false, "Verbose output")
	parser.BoolVar(&option.IsDocker, "docker", false, "Specify when running in docker")
	noSetup := parser.Bool("nosetup", false, "Skip calling glide install or yarn")
	parser.Parse(os.Args[1:])
	commands := parser.Args()
	if len(commands) <= 0 {
		for _, c := range(testCommands){
			commands = append(commands, c.Command)
		}
	}

	var needGlide bool
	var needYarn bool
	commandsToRun := []*testCommandInfo{}
	commandMap := map[string]*testCommandInfo{}
	for _, c := range(testCommands){
		commandMap[c.Command] = c
	}
	for _, command := range(commands) {
		c, ok := commandMap[command]
		if !ok {
			fmt.Fprintf(os.Stderr, "Unknown Command: %s\n", command)
			flag.Usage()
			os.Exit(2)
		}
		commandsToRun = append(commandsToRun, c)
		needGlide = needGlide || c.NeedGlide
		needYarn = needYarn || c.NeedYarn
	}

	// locate the project directory
	// this file should be located at projectdir/tool/
	projectDir := filepath.Clean(filepath.Join(filepath.Dir(arg0), ".."))
	if err := os.Chdir(projectDir); err != nil {
		panic(err)
	}

	if needGlide && !*noSetup {
		if ret := runCommand("glide", "install"); ret != 0 {
			os.Exit(ret)
		}
	}
	if needYarn && !*noSetup {
		func(){
			popd := pushd("client")
			defer popd()
			if ret := runCommand("yarn"); ret != 0 {
				os.Exit(ret)
			}
		}()
	}
	for _, c := range(commandsToRun){
		if ret := c.Func(option); ret != 0 {
			os.Exit(ret)
		}
	}
	os.Exit(0)
}

func runCommand(name string, args... string) int {
	fmt.Fprintf(os.Stderr, "# %v %v\n", name, strings.Join(args, " "))
	cmd := exec.Command(name, args...)
	cmd.Stdout = os.Stdout
	cmd.Stderr = os.Stderr
	if err := cmd.Run(); err != nil {
		if exitErr, ok := err.(*exec.ExitError); ok {
			s := exitErr.Sys()
			if waitStatus, ok := s.(*syscall.WaitStatus); ok {
				return waitStatus.ExitStatus()
			}
		}
		panic(err)
	}
	return 0
}

func shutdownServer(cmd *exec.Cmd) bool {
	if resp, err := http.Get("http://localhost:8000/quit"); err != nil {
		fmt.Fprintf(os.Stderr, "Failed to stop server: %v", err)
		return false
	} else if resp.StatusCode != http.StatusOK {
		fmt.Fprintf(os.Stderr, "Failed to stop server: %v", resp)
		return false
	}
	return true
}

func pushd(dir string) func() {
	orig, err := os.Getwd()
	if err != nil {
		panic(err)
	}
	if err := os.Chdir(dir); err != nil {
		panic(err)
	}
	return func() {
		if err := os.Chdir(orig); err != nil {
			panic(err)
		}
	}
}

func runGoUnitTest(option testOption) int {
	args := []string{
		"test",
		"./server",
		"./testutil",
	}
	if option.IsVerbose {
		args = append(args, "-v")
	}
	return runCommand("goapp", args...)
}

func runAngularUnitTest(option testOption) int {
	popd := pushd("client")
	defer popd()

	args := []string {
		"run",
		"test",
		"--",
		"--single-run",
		"true",
	}
	if option.IsDocker {
		args = append(args, "--config", "karma-docker.conf.js")
	}

	return runCommand("yarn", args...)
}

func runE2ETest(option testOption) int {
	serverStdout, err := os.OpenFile(
		"server.stdout.log",
		os.O_RDWR|os.O_CREATE|os.O_APPEND,
		0644,
	)
	if err != nil {
		panic(err)
	}
	defer serverStdout.Close()
	serverStderr, err := os.OpenFile(
		"server.stderr.log",
		os.O_RDWR|os.O_CREATE|os.O_APPEND,
		0644,
	)
	if err != nil {
		panic(err)
	}
	defer serverStderr.Close()

	python, err := exec.LookPath("python")
	if err != nil {
		panic(err)
	}
	devAppServer, err := exec.LookPath("dev_appserver.py")
	if err != nil {
		panic(err)
	}
	args := []string {
		devAppServer,
		"--require_indexes=true",
		"--enable_watching_go_path=false",
		"--clear_datastore=true",
		"--datastore_consistency_policy=consistent",
		"--automatic_restart=false",
		"--watcher_ignore_re=.*",
		"--skip_sdk_update_check=true",
		"server/app.yaml",
	}

	fmt.Fprintf(os.Stderr, "# %v %v\n", python, strings.Join(args, " "))
	cmd := exec.Command(python, args...)
	cmd.Stdout = serverStdout
	cmd.Stderr = serverStderr
	if err := cmd.Start(); err != nil {
		panic(err)
	}
	defer shutdownServer(cmd)

	url := "http://localhost:8080/"
	seconds := 30
	fmt.Fprintf(os.Stderr, "Wait for server gets ready for %v seconds: %v\n", seconds, url)
	limit := time.Now().Add(time.Duration(seconds) * time.Second)
	client := &http.Client {
		Timeout: time.Duration(seconds) * time.Second,
	}
	for true {
		if time.Since(limit) > 0 {
			panic(fmt.Sprintf("Server didnot get ready after %v seconds", seconds))
		}
		if _, err := client.Get(url); err == nil {
			break
		}
		time.Sleep(time.Duration(100) * time.Millisecond)
	}

	popd := pushd("client")
	defer popd()

	e2eArgs := []string {
		"run",
		"e2e",
		"--",
		"--serve",
		"true",
		"--port",
		"4200",
	}
	if option.IsDocker {
		e2eArgs = append(e2eArgs, "--config", "protractor-docker.conf.js")
	}

	return runCommand("yarn", e2eArgs...)
}
<|endoftext|>


<commit_before>package confobject

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

func sliceFromStrings(value interface{}, type_ interface{}) (vs interface{}, err error) {
	switch value.(type) {
	case string:
		sv := value.(string)
		strs := strings.Split(sv, ",")
		switch type_.(type) {
		case string:
			return strs, err
		case int:
			var ivs []int
			for _, s := range strs {
				var iv int64
				iv, err = strconv.ParseInt(s, 10, 64)
				if err != nil {
					err = fmt.Errorf("Cannot parse %v", s)
					return
				}
				ivs = append(ivs, int(iv))
			}
			return ivs, err
		case float64:
			var fvs []float64
			for _, s := range strs {
				var fv float64
				fv, err = strconv.ParseFloat(s, 64)
				if err != nil {
					err = fmt.Errorf("Cannot parse %v", s)
					return
				}
				fvs = append(fvs, fv)
			}
			return fvs, err
		default:
			err = fmt.Errorf("Cannot parse %v", value)
			return
		}
	case []string:
		// var values interface{}
		for _, sval := range value.([]string) {
			val, err := sliceFromStrings(sval, type_)
			if err != nil {
				return vs, err
			}
			// vs = append(vs, val)

			if reflect.TypeOf(vs) == nil {
				vs = val
			} else {
				vs = reflect.AppendSlice(
					reflect.ValueOf(vs),
					reflect.ValueOf(val),
				).Interface()
			}

		}
	default:
		err = fmt.Errorf("Cannot parse %v", value)
	}
	return
}

func boolFromInterface(value interface{}) (v bool, err error) {
	if reflect.ValueOf(value).Kind() == reflect.Slice {
		v, err = boolFromInterface(reflect.ValueOf(value).Index(0).Interface())
		return
	}
	switch value.(type) {
	case string:
		bs := strings.ToLower(value.(string))
		switch bs {
		case "t", "true":
			v = true
		case "f", "false":
			v = false
		default:
			err = fmt.Errorf("Invalid string representation of a bool: %v", bs)
		}
	default:
		err = fmt.Errorf("Cannot parse %v", value)
	}
	return
}

func intFromInterface(value interface{}) (v int64, err error) {
	if reflect.ValueOf(value).Kind() == reflect.Slice {
		// fmt.Println("intFromInterface got slice", reflect.ValueOf(value).Index(0).Interface())
		v, err = intFromInterface(reflect.ValueOf(value).Index(0).Interface())
		return
	}
	switch value.(type) {
	case int:
		v = int64(value.(int))
	case int8:
		v = int64(value.(int8))
	case int16:
		v = int64(value.(int16))
	case int32:
		v = int64(value.(int32))
	case string:
		v, err = strconv.ParseInt(value.(string), 10, 64)
	default:
		err = fmt.Errorf("Cannot parse %v", value)
	}

	return
}

func floatFromInterface(value interface{}) (v float64, err error) {
	if reflect.ValueOf(value).Kind() == reflect.Slice {
		// fmt.Println("floatFromInterface got slice", reflect.ValueOf(value).Index(0).Interface())
		v, err = floatFromInterface(reflect.ValueOf(value).Index(0).Interface())
		return
	}
	switch value.(type) {
	case float32:
		v = float64(value.(float32))
	case string:
		v, err = strconv.ParseFloat(value.(string), 64)
	default:
		err = fmt.Errorf("Cannot parse %v", value)
	}

	return
}
<commit_msg>add uint interface>int transformation<commit_after>package confobject

import (
	"fmt"
	"reflect"
	"strconv"
	"strings"
)

func sliceFromStrings(value interface{}, type_ interface{}) (vs interface{}, err error) {
	switch value.(type) {
	case string:
		sv := value.(string)
		strs := strings.Split(sv, ",")
		switch type_.(type) {
		case string:
			return strs, err
		case int:
			var ivs []int
			for _, s := range strs {
				var iv int64
				iv, err = strconv.ParseInt(s, 10, 64)
				if err != nil {
					err = fmt.Errorf("Cannot parse %v (int)", s)
					return
				}
				ivs = append(ivs, int(iv))
			}
			return ivs, err
		case float64:
			var fvs []float64
			for _, s := range strs {
				var fv float64
				fv, err = strconv.ParseFloat(s, 64)
				if err != nil {
					err = fmt.Errorf("Cannot parse %v (float64)", s)
					return
				}
				fvs = append(fvs, fv)
			}
			return fvs, err
		default:
			err = fmt.Errorf("Cannot parse %v (%v)", value, reflect.ValueOf(value).Kind())
			return
		}
	case []string:
		// var values interface{}
		for _, sval := range value.([]string) {
			val, err := sliceFromStrings(sval, type_)
			if err != nil {
				return vs, err
			}
			// vs = append(vs, val)

			if reflect.TypeOf(vs) == nil {
				vs = val
			} else {
				vs = reflect.AppendSlice(
					reflect.ValueOf(vs),
					reflect.ValueOf(val),
				).Interface()
			}

		}
	default:
		err = fmt.Errorf("Cannot parse %v (%v)", value, reflect.ValueOf(value).Kind())
	}
	return
}

func boolFromInterface(value interface{}) (v bool, err error) {
	if reflect.ValueOf(value).Kind() == reflect.Slice {
		v, err = boolFromInterface(reflect.ValueOf(value).Index(0).Interface())
		return
	}
	switch value.(type) {
	case string:
		bs := strings.ToLower(value.(string))
		switch bs {
		case "t", "true":
			v = true
		case "f", "false":
			v = false
		default:
			err = fmt.Errorf("Invalid string representation of a bool: %v", bs)
		}
	default:
		err = fmt.Errorf("Cannot parse %v", value)
	}
	return
}

func intFromInterface(value interface{}) (v int64, err error) {
	if reflect.ValueOf(value).Kind() == reflect.Slice {
		// fmt.Println("intFromInterface got slice", reflect.ValueOf(value).Index(0).Interface())
		v, err = intFromInterface(reflect.ValueOf(value).Index(0).Interface())
		return
	}
	switch value.(type) {
	case int:
		v = int64(value.(int))
	case int8:
		v = int64(value.(int8))
	case uint8:
		v = int64(value.(uint8))
	case int16:
		v = int64(value.(int16))
	case uint16:
		v = int64(value.(uint16))
	case int32:
		v = int64(value.(int32))
	case uint32:
		v = int64(value.(uint32))
	case int64:
		v = value.(int64)
	case uint64:
		v = int64(value.(uint64))
	case string:
		v, err = strconv.ParseInt(value.(string), 10, 64)
	default:
		err = fmt.Errorf("Cannot parse %v (%v)", value, reflect.ValueOf(value).Kind())
	}

	return
}

func floatFromInterface(value interface{}) (v float64, err error) {
	if reflect.ValueOf(value).Kind() == reflect.Slice {
		// fmt.Println("floatFromInterface got slice", reflect.ValueOf(value).Index(0).Interface())
		v, err = floatFromInterface(reflect.ValueOf(value).Index(0).Interface())
		return
	}
	switch value.(type) {
	case float32:
		v = float64(value.(float32))
	case string:
		v, err = strconv.ParseFloat(value.(string), 64)
	default:
		err = fmt.Errorf("Cannot parse %v", value)
	}

	return
}
<|endoftext|>


<commit_before>package typhon

// import (
// 	"bytes"
// 	"encoding/json"
// 	"errors"
// 	"fmt"
// 	"io"
// 	"io/ioutil"
// 	"net"
// 	"net/http"
// 	"os"
// 	"strconv"
// 	"sync"
// 	"time"
//
// 	"github.com/facebookgo/httpcontrol"
// 	log "github.com/mondough/slog"
// 	"github.com/mondough/terrors"
// 	terrorp "github.com/mondough/terrors/proto"
// 	"golang.org/x/net/context"
// )
//
// type errReader struct {
// 	error
// }
//
// func (e errReader) Read(_ []byte) (int, error) {
// 	return 0, e
// }
//
// type Transport struct {
// 	listenerM sync.RWMutex       // protects listener
// 	listener  *net.TCPListener   // initialised on first Listen()
// 	servicesM sync.RWMutex       // protects services
// 	services  map[string]Service // maps service names to services
// 	client    *http.Client       // immutable (no mutex needed)
// 	serverM   sync.Mutex         // protects server
// 	server    *http.Server       // initialised on first Listen()
// 	Send      Service
// }
//
// // A single HTTP client is used among all transports
// var sharedHttpClient = &http.Client{
// 	Timeout: time.Hour,
// 	Transport: &httpcontrol.Transport{
// 		Proxy:                 http.ProxyFromEnvironment,
// 		DisableKeepAlives:     false,
// 		DisableCompression:    false,
// 		MaxIdleConnsPerHost:   10,
// 		DialTimeout:           10 * time.Second,
// 		DialKeepAlive:         10 * time.Minute,
// 		ResponseHeaderTimeout: time.Minute,
// 		RequestTimeout:        time.Hour,
// 		RetryAfterTimeout:     false,
// 		MaxTries:              3}}
//
// func NetworkTransport() *Transport {
// 	sharedTransportOnce.Do(func() {
// 		sharedTransport = &Transport{
// 			services: make(map[string]Service, 1),
// 			client:   sharedHttpClient}
// 		sharedTransport.Send = sharedTransport.send
// 	})
// 	return sharedTransport
// }
//
// func (t *Transport) Listen(name string, svc Service) error {
// 	l, err := t.ensureListening()
// 	if err != nil {
// 		return err
// 	}
//
// 	t.servicesM.Lock()
// 	t.services[name] = svc
// 	t.servicesM.Unlock()
//
// 	t.serverM.Lock()
// 	if t.server == nil {
// 		t.server = &http.Server{
// 			Handler:        t,
// 			MaxHeaderBytes: http.DefaultMaxHeaderBytes}
// 		go func() {
// 			err := t.server.Serve(l)
// 			log.Info(nil, "[Typhon:http:networkTransport] Server exited with %v", err)
// 		}()
// 	}
// 	t.serverM.Unlock()
// 	return nil
// }
//
// func (t *Transport) RemoteAddr() net.Addr {
// 	t.listenerM.RLock()
// 	defer t.listenerM.RUnlock()
// 	if t.listener != nil {
// 		return t.listener.Addr()
// 	}
// 	return nil
// }
//
// func (t *Transport) Unlisten(name string) {
// 	// @TODO: Draining
// 	t.servicesM.Lock()
// 	delete(t.services, name)
// 	t.servicesM.Unlock()
// }
//
// func (t *Transport) Close(timeout time.Duration) {
// 	t.listenerM.Lock()
// 	defer t.listenerM.Unlock()
// 	if t.listener != nil {
// 		t.servicesM.RLock()
// 		svcNames := make([]string, 0, len(t.services))
// 		for k, _ := range t.services {
// 			svcNames = append(svcNames, k)
// 		}
// 		t.servicesM.RUnlock()
// 		for _, svc := range svcNames {
// 			t.Unlisten(svc)
// 		}
// 		addr := t.listener.Addr()
// 		t.listener.Close()
// 		t.listener = nil
// 		log.Info(nil, "[Typhon:http:networkTransport] Stopped listening on %v", addr)
// 	}
// }
//
// func (t *Transport) send(req Request) Response {
// 	httpRsp, err := t.client.Do(&(req.Request))
// 	ret := Response{
// 		Error: err}
// 	if httpRsp != nil {
// 		defer httpRsp.Body.Close()
// 		ret.Response = httpRsp
// 		// Read the entire response body here so we can make sure the request is Close()d
// 		// @TODO: Provide a hook to allow the client to get a streaming body?
// 		payload := new(bytes.Buffer)
// 		_, err := io.Copy(payload, httpRsp.Body)
// 		if err != nil {
// 			ret.Response.Body = ioutil.NopCloser(errReader{err})
// 		} else {
// 			ret.Response.Body = ioutil.NopCloser(payload)
//
// 			// Errors should set an error on the response; in the
// 			if ret.StatusCode >= 400 && ret.StatusCode < 600 {
// 				if ret.Header.Get("Terror") == "1" {
// 					terrp := &terrorp.Error{}
// 					if err := json.Unmarshal(payload.Bytes(), terrp); err == nil {
// 						ret.Error = terrors.Unmarshal(terrp)
// 					} else {
//
// 						ret.Error = errors.New(string(payload.Bytes()))
// 					}
// 				}
// 			}
// 		}
// 	}
// 	return ret
// }
//
// func (t *Transport) ensureListening() (net.Listener, error) {
// 	// Determine on which address to listen, choosing in order one of:
// 	// 1. LISTEN_ADDR environment variable
// 	// 2. PORT variable (listening on all interfaces)
// 	// 3. Random, available port
// 	addr := ":0"
// 	if addr_ := os.Getenv("LISTEN_ADDR"); addr_ != "" {
// 		addr = addr_
// 	} else if port, err := strconv.Atoi(os.Getenv("PORT")); err == nil && port >= 0 {
// 		addr = fmt.Sprintf(":%d", port)
// 	}
//
// 	t.listenerM.Lock()
// 	defer t.listenerM.Unlock()
// 	if t.listener == nil {
// 		tcpAddr, err := net.ResolveTCPAddr("tcp", addr)
// 		if err != nil {
// 			return nil, terrors.Wrap(err, nil)
// 		}
//
// 		// @TODO: Keep alives
// 		l, err := net.ListenTCP("tcp", tcpAddr)
// 		if err != nil {
// 			return nil, terrors.Wrap(err, nil)
// 		}
// 		log.Info(nil, "[Typhon:http:networkTransport] Listening on %v", l.Addr())
// 		t.listener = l
// 	}
// 	return t.listener, nil
// }
//
// func (t *Transport) ServeHTTP(rw http.ResponseWriter, httpReq *http.Request) {
// 	req := Request{
// 		Request: *httpReq,
// 		Context: context.Background()} // @TODO: Proper context
// 	rsp := Response{}
//
// 	t.servicesM.RLock()
// 	svc, ok := t.services[req.Host]
// 	t.servicesM.RUnlock()
// 	if ok {
// 		rsp = svc(req)
// 	} else {
// 		rsp.Error = terrors.InternalService("unhandled_service", fmt.Sprintf("Unhandled service %s", req.Host), nil)
// 	}
//
// 	rsp.ctx = req
// 	if rsp.Response == nil { // Error responses often won't have a HTTP response
// 		rsp.Response = newHttpResponse(req)
// 	}
//
// 	// If the response is an error, serialise it
// 	if rsp.Error != nil {
// 		terr := terrors.Wrap(rsp.Error, nil).(*terrors.Error)
// 		rsp.StatusCode = terr2StatusCode(terr.Code)
// 		rsp.Encode(terrors.Marshal(terr))
// 		rsp.Header.Set("Terror", "1")
// 	}
//
// 	// Write the response out to the wire
// 	for k, v := range rsp.Header {
// 		rw.Header()[k] = v
// 	}
// 	rw.WriteHeader(rsp.StatusCode)
// 	if rsp.Body != nil {
// 		defer rsp.Body.Close()
// 		if _, err := io.Copy(rw, rsp.Body); err != nil {
// 			log.Error(req, "[Typhon:http:networkTransport] Error copying response body: %v", err)
// 		}
// 	}
// }
<commit_msg>Remove transport 🔫<commit_after><|endoftext|>


<commit_before>/*
Package xkcd allows access to metadata for xkcd comics.
*/
package xkcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	currentURL  = "http://xkcd.com/info.0.json"
	templateURL = "http://xkcd.com/%v/info.0.json"
)

// Comic is a struct that contains infomation about a xkcd comic.
type Comic struct {
	Num       int    `json:"num"`
	Title     string `json:"title"`
	SafeTitle string `json:"safe_title"`

	Img string `json:"img"`
	Alt string `json:"alt"`

	Year  string `json:"year"`
	Month string `json:"month"`
	Day   string `json:"day"`

	News       string `json:"news"`
	Link       string `json:"link"`
	Transcript string `json:"transcript"`
}

// Get returns the information about the xkcd comic number `n'.
func Get(n int) (*Comic, error) {
	url := fmt.Sprintf(templateURL, n)
	return getByURL(url)
}

// GetCurrent returns information for the newest xkcd comic.
func GetCurrent() (*Comic, error) {
	return getByURL(currentURL)
}

// getByURL returns infomation downloaded from `url'.
func getByURL(url string) (*Comic, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, errors.New(resp.Status)
	}

	dec := json.NewDecoder(resp.Body)

	c := new(Comic)
	err = dec.Decode(c)
	return c, err
}

// ByteSlice returns the comic as a []byte in json format.
func (c *Comic) ByteSlice() []byte {
	buf, err := json.Marshal(*c)
	if err != nil {
		return nil
	}
	return buf
}

// String returns the comic as a string encoded in json format.
func (c *Comic) String() string {
	return string(c.ByteSlice())
}
<commit_msg>Revert "Added ByteSlice() to Comic."<commit_after>/*
Package xkcd allows access to metadata for xkcd comics.
*/
package xkcd

import (
	"encoding/json"
	"errors"
	"fmt"
	"net/http"
)

const (
	currentURL  = "http://xkcd.com/info.0.json"
	templateURL = "http://xkcd.com/%v/info.0.json"
)

// Comic is a struct that contains infomation about a xkcd comic.
type Comic struct {
	Num       int    `json:"num"`
	Title     string `json:"title"`
	SafeTitle string `json:"safe_title"`

	Img string `json:"img"`
	Alt string `json:"alt"`

	Year  string `json:"year"`
	Month string `json:"month"`
	Day   string `json:"day"`

	News       string `json:"news"`
	Link       string `json:"link"`
	Transcript string `json:"transcript"`
}

// Get returns the information about the xkcd comic number `n'.
func Get(n int) (*Comic, error) {
	url := fmt.Sprintf(templateURL, n)
	return getByURL(url)
}

// GetCurrent returns information for the newest xkcd comic.
func GetCurrent() (*Comic, error) {
	return getByURL(currentURL)
}

// getByURL returns infomation downloaded from `url'.
func getByURL(url string) (*Comic, error) {
	resp, err := http.Get(url)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()

	if resp.StatusCode >= 400 {
		return nil, errors.New(resp.Status)
	}

	dec := json.NewDecoder(resp.Body)

	c := new(Comic)
	err = dec.Decode(c)
	return c, err
}

// String returns the comic as a string encoded in json format.
func (c *Comic) String() string {
	b, err := json.Marshal(*c)
	if err != nil {
		return ""
	}
	return string(b)
}
<|endoftext|>


<commit_before>package chain

import (
	"encoding/json"
	"errors"
	"fmt"
	"gochain/config"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Chain : the shape of what is returned
// should have a name corresponding to the file it's stored in
// and the actual chain consisting of links
type Chain struct {
	Name       string
	ChainLinks []link
	MetaData   chainMetaData
}

type link struct {
	date   string
	symbol rune
}

type chainMetaData struct {
	Description  string
	CreationDate string
}

// PrintChain : prints this chain
func (chain *Chain) PrintChain(name string) error {
	c := config.Configuration{}
	c.GetConfiguration()
	chainPath := filepath.Join(c.ChainDirectory, "Chains", name+".chain")
	file, err := os.Open(chainPath)
	if err != nil {
		return err
	}

	decoder := json.NewDecoder(file)
	decodeErr := decoder.Decode(chain)
	if decodeErr != nil {
		return decodeErr
	}

	// TODO break this into it's own method(s)
	// provide flags for different printing types
	// ie: basic, detailed, limits
	for _, c := range chain.ChainLinks {
		fmt.Printf("[%c]", c.symbol)
	}

	return nil
}

// GetChain : returns a chain with a given name
func (chain *Chain) GetChain(name string) (myErr error) {
	c := config.Configuration{}
	c.GetConfiguration()
	chainPath := filepath.Join(c.ChainDirectory, "Chains", name+".chain")
	file, err := os.Open(chainPath)
	if err != nil {
		return err
	}

	decoder := json.NewDecoder(file)
	decodeErr := decoder.Decode(chain)
	if decodeErr != nil {
		return decodeErr
	}

	return
}

// CreateChain : create a chain with a given name
// will create a chain directory if none exists
// directory will default to user's home directory
// or if the ~/gochain.json exists will use the
// defaultDirectory value from there
func CreateChain(name string) error {
	chain := Chain{}
	chain.Name = name
	createChainDir()
	_, err := createChain(name)

	return err
}

func createChainDir() error {
	config := config.Configuration{}
	config.GetConfiguration()

	dirPath := filepath.Join(config.ChainDirectory, "Chains")
	if exist, err := objExists(dirPath); !exist {
		if err == nil {
			os.MkdirAll(dirPath, os.ModePerm)
		} else {
			return err
		}
	} else {
		return errors.New("directory creation failed")
	}

	return nil
}

func objExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func createChain(name string) (bool, error) {
	config := config.Configuration{}
	config.GetConfiguration()
	chainPath := filepath.Join(config.ChainDirectory, "Chains", name+".chain")
	if exist, existErr := objExists(chainPath); !exist {
		if existErr == nil {
			newChainFile, err := os.Create(chainPath)
			if err != nil {
				return false, err
			}
			chainSkeleton, err := getNewFileLayout(name)
			newChainFile.Write(chainSkeleton)
			newChainFile.Sync()
			newChainFile.Close()
		} else {
			return false, existErr
		}
	} else {
		return false, errors.New("a chain with name already exists")
	}

	return true, nil
}

func getNewFileLayout(name string) ([]byte, error) {
	l := make([]link, 1, 1)
	t := time.Now()
	l[0].date = t.String()
	l[0].symbol = ' '

	chain := Chain{
		name,
		l,
		chainMetaData{
			"This is my new chain!",
			t.String(),
		},
	}
	jm, err := json.Marshal(chain)
	if err != nil {
		return nil, err
	}

	return jm, nil
}

// GetAllChains : returns list of all chains in chain dir
func GetAllChains() (list []string, readError error) {
	config := config.Configuration{}
	config.GetConfiguration()
	chainPath := filepath.Join(config.ChainDirectory, "Chains")
	files, err := ioutil.ReadDir(chainPath)
	if err != nil {
		return nil, err
	}
	fileNames := make([]string, len(files), len(files))
	for i, f := range files {
		basename := f.Name()
		name := strings.TrimSuffix(basename, filepath.Ext(basename))
		fileNames[i] = name
	}

	return fileNames, nil
}

// CreateLink : creates link on current chain
func (chain *Chain) CreateLink(name string) error {
	chain.GetChain(name)
	t := time.Now()
	chain.ChainLinks = append(chain.ChainLinks, link{t.String(), 'X'})

	return nil
}

func (chain *Chain) WriteChainToFile(name string) {
	// TODO write the updated chain back to file
}
<commit_msg>Terminate string<commit_after>package chain

import (
	"encoding/json"
	"errors"
	"fmt"
	"gochain/config"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"
	"time"
)

// Chain : the shape of what is returned
// should have a name corresponding to the file it's stored in
// and the actual chain consisting of links
type Chain struct {
	Name       string
	ChainLinks []link
	MetaData   chainMetaData
}

type link struct {
	date   string
	symbol rune
}

type chainMetaData struct {
	Description  string
	CreationDate string
}

// PrintChain : prints this chain
func (chain *Chain) PrintChain(name string) error {
	c := config.Configuration{}
	c.GetConfiguration()
	chainPath := filepath.Join(c.ChainDirectory, "Chains", name+".chain")
	file, err := os.Open(chainPath)
	if err != nil {
		return err
	}

	decoder := json.NewDecoder(file)
	decodeErr := decoder.Decode(chain)
	if decodeErr != nil {
		return decodeErr
	}

	// TODO break this into it's own method(s)
	// provide flags for different printing types
	// ie: basic, detailed, limits
	for _, c := range chain.ChainLinks {
		fmt.Printf("[%c]\n", c.symbol)
	}

	return nil
}

// GetChain : returns a chain with a given name
func (chain *Chain) GetChain(name string) (myErr error) {
	c := config.Configuration{}
	c.GetConfiguration()
	chainPath := filepath.Join(c.ChainDirectory, "Chains", name+".chain")
	file, err := os.Open(chainPath)
	if err != nil {
		return err
	}

	decoder := json.NewDecoder(file)
	decodeErr := decoder.Decode(chain)
	if decodeErr != nil {
		return decodeErr
	}

	return
}

// CreateChain : create a chain with a given name
// will create a chain directory if none exists
// directory will default to user's home directory
// or if the ~/gochain.json exists will use the
// defaultDirectory value from there
func CreateChain(name string) error {
	chain := Chain{}
	chain.Name = name
	createChainDir()
	_, err := createChain(name)

	return err
}

func createChainDir() error {
	config := config.Configuration{}
	config.GetConfiguration()

	dirPath := filepath.Join(config.ChainDirectory, "Chains")
	if exist, err := objExists(dirPath); !exist {
		if err == nil {
			os.MkdirAll(dirPath, os.ModePerm)
		} else {
			return err
		}
	} else {
		return errors.New("directory creation failed")
	}

	return nil
}

func objExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return true, err
}

func createChain(name string) (bool, error) {
	config := config.Configuration{}
	config.GetConfiguration()
	chainPath := filepath.Join(config.ChainDirectory, "Chains", name+".chain")
	if exist, existErr := objExists(chainPath); !exist {
		if existErr == nil {
			newChainFile, err := os.Create(chainPath)
			if err != nil {
				return false, err
			}
			chainSkeleton, err := getNewFileLayout(name)
			newChainFile.Write(chainSkeleton)
			newChainFile.Sync()
			newChainFile.Close()
		} else {
			return false, existErr
		}
	} else {
		return false, errors.New("a chain with name already exists")
	}

	return true, nil
}

func getNewFileLayout(name string) ([]byte, error) {
	l := make([]link, 1, 1)
	t := time.Now()
	l[0].date = t.String()
	l[0].symbol = ' '

	chain := Chain{
		name,
		l,
		chainMetaData{
			"This is my new chain!",
			t.String(),
		},
	}
	jm, err := json.Marshal(chain)
	if err != nil {
		return nil, err
	}

	return jm, nil
}

// GetAllChains : returns list of all chains in chain dir
func GetAllChains() (list []string, readError error) {
	config := config.Configuration{}
	config.GetConfiguration()
	chainPath := filepath.Join(config.ChainDirectory, "Chains")
	files, err := ioutil.ReadDir(chainPath)
	if err != nil {
		return nil, err
	}
	fileNames := make([]string, len(files), len(files))
	for i, f := range files {
		basename := f.Name()
		name := strings.TrimSuffix(basename, filepath.Ext(basename))
		fileNames[i] = name
	}

	return fileNames, nil
}

// CreateLink : creates link on current chain
func (chain *Chain) CreateLink(name string) error {
	chain.GetChain(name)
	t := time.Now()
	chain.ChainLinks = append(chain.ChainLinks, link{t.String(), 'X'})

	return nil
}

func (chain *Chain) WriteChainToFile(name string) {
	// TODO write the updated chain back to file
}
<|endoftext|>