<commit_before>package main

import (
	"flag"
	"fmt"
	"testing"
	"net/http"
)

var edgeHost = flag.String("edgeHost", "www.gov.uk", "Hostname of edge")

// Should redirect from HTTP to HTTPS without hitting origin.
func TestProtocolRedirect(t *testing.T) {
	sourceUrl := fmt.Sprintf("http://%s/", *edgeHost)
	destUrl := fmt.Sprintf("https://%s/", *edgeHost)

	client := &http.Transport{}
	req, _ := http.NewRequest("GET", sourceUrl, nil)
	resp, err := client.RoundTrip(req)

	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != 301 {
		t.Errorf("Status code expected 301, got %d", resp.StatusCode)
	}
	if d := resp.Header.Get("Location"); d != destUrl {
		t.Errorf("Location header expected %s, got %s", destUrl, d)
	}

	t.Error("Not implemented test to confirm that it doesn't hit origin")
}

// Should return 403 for PURGE requests from IPs not in the whitelist.
func TestRestrictPurgeRequests(t *testing.T) {
	t.Error("Not implemented")
}

// Should create an X-Forwarded-For header containing the client's IP.
func TestHeaderCreateXFF(t *testing.T) {
	t.Error("Not implemented")
}

// Should append client's IP to existing X-Forwarded-For header.
func TestHeaderAppendXFF(t *testing.T) {
	t.Error("Not implemented")
}

// Should create a True-Client-IP header containing the client's IP
// address, discarding the value provided in the original request.
func TestHeaderUnspoofableClientIP(t *testing.T) {
	t.Error("Not implemented")
}

// Should not modify Host header from original request.
func TestHeaderHostUnmodified(t *testing.T) {
	t.Error("Not implemented")
}

// Should set a default TTL if the response doesn't set one.
func TestDefaultTTL(t *testing.T) {
	t.Error("Not implemented")
}

// Should serve stale object and not hit mirror(s) if origin is down and
// object is beyond TTL but still in cache.
func TestFailoverOriginDownServeStale(t *testing.T) {
	t.Error("Not implemented")
}

// Should serve stale object and not hit mirror(s) if origin returns a 5xx
// response and object is beyond TTL but still in cache.
func TestFailoverOrigin5xxServeStale(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to first mirror if origin is down and object is not in
// cache (active or stale).
func TestFailoverOriginDownUseFirstMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to first mirror if origin returns 5xx response and object
// is not in cache (active or stale).
func TestFailoverOrigin5xxUseFirstMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to second mirror if both origin and first mirror are
// down.
func TestFailoverOriginDownFirstMirrorDownUseSecondMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to second mirror if both origin and first mirror return
// 5xx responses.
func TestFailoverOrigin5xxFirstMirror5xxUseSecondMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should not fallback to mirror if origin returns a 5xx response with a
// No-Fallback header.
func TestFailoverNoFallbackHeader(t *testing.T) {
	t.Error("Not implemented")
}

// Should not cache a response with a Set-Cookie a header.
func TestNoCacheHeaderSetCookie(t *testing.T) {
	t.Error("Not implemented")
}

// Should not cache a response with a Cache-Control: private header.
func TestNoCacheHeaderCacheControlPrivate(t *testing.T) {
	t.Error("Not implemented")
}
<commit_msg>Add path to redirect test<commit_after>package main

import (
	"flag"
	"fmt"
	"testing"
	"net/http"
)

var edgeHost = flag.String("edgeHost", "www.gov.uk", "Hostname of edge")

// Should redirect from HTTP to HTTPS without hitting origin.
func TestProtocolRedirect(t *testing.T) {
	sourceUrl := fmt.Sprintf("http://%s/foo/bar", *edgeHost)
	destUrl := fmt.Sprintf("https://%s/foo/bar", *edgeHost)

	client := &http.Transport{}
	req, _ := http.NewRequest("GET", sourceUrl, nil)
	resp, err := client.RoundTrip(req)

	if err != nil {
		t.Fatal(err)
	}
	if resp.StatusCode != 301 {
		t.Errorf("Status code expected 301, got %d", resp.StatusCode)
	}
	if d := resp.Header.Get("Location"); d != destUrl {
		t.Errorf("Location header expected %s, got %s", destUrl, d)
	}

	t.Error("Not implemented test to confirm that it doesn't hit origin")
}

// Should return 403 for PURGE requests from IPs not in the whitelist.
func TestRestrictPurgeRequests(t *testing.T) {
	t.Error("Not implemented")
}

// Should create an X-Forwarded-For header containing the client's IP.
func TestHeaderCreateXFF(t *testing.T) {
	t.Error("Not implemented")
}

// Should append client's IP to existing X-Forwarded-For header.
func TestHeaderAppendXFF(t *testing.T) {
	t.Error("Not implemented")
}

// Should create a True-Client-IP header containing the client's IP
// address, discarding the value provided in the original request.
func TestHeaderUnspoofableClientIP(t *testing.T) {
	t.Error("Not implemented")
}

// Should not modify Host header from original request.
func TestHeaderHostUnmodified(t *testing.T) {
	t.Error("Not implemented")
}

// Should set a default TTL if the response doesn't set one.
func TestDefaultTTL(t *testing.T) {
	t.Error("Not implemented")
}

// Should serve stale object and not hit mirror(s) if origin is down and
// object is beyond TTL but still in cache.
func TestFailoverOriginDownServeStale(t *testing.T) {
	t.Error("Not implemented")
}

// Should serve stale object and not hit mirror(s) if origin returns a 5xx
// response and object is beyond TTL but still in cache.
func TestFailoverOrigin5xxServeStale(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to first mirror if origin is down and object is not in
// cache (active or stale).
func TestFailoverOriginDownUseFirstMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to first mirror if origin returns 5xx response and object
// is not in cache (active or stale).
func TestFailoverOrigin5xxUseFirstMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to second mirror if both origin and first mirror are
// down.
func TestFailoverOriginDownFirstMirrorDownUseSecondMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should fallback to second mirror if both origin and first mirror return
// 5xx responses.
func TestFailoverOrigin5xxFirstMirror5xxUseSecondMirror(t *testing.T) {
	t.Error("Not implemented")
}

// Should not fallback to mirror if origin returns a 5xx response with a
// No-Fallback header.
func TestFailoverNoFallbackHeader(t *testing.T) {
	t.Error("Not implemented")
}

// Should not cache a response with a Set-Cookie a header.
func TestNoCacheHeaderSetCookie(t *testing.T) {
	t.Error("Not implemented")
}

// Should not cache a response with a Cache-Control: private header.
func TestNoCacheHeaderCacheControlPrivate(t *testing.T) {
	t.Error("Not implemented")
}
<|endoftext|>


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

// Cgo; see gmp.go for an overview.

// TODO(rsc):
//	Emit correct line number annotations.
//	Make 6g understand the annotations.

package main

import (
	"fmt"
	"go/ast"
	"os"
	"strings"
)

func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") }

var ptrSizeMap = map[string]int64{
	"386":   4,
	"amd64": 8,
	"arm":   4,
}

var expandName = map[string]string{
	"schar":     "signed char",
	"uchar":     "unsigned char",
	"ushort":    "unsigned short",
	"uint":      "unsigned int",
	"ulong":     "unsigned long",
	"longlong":  "long long",
	"ulonglong": "unsigned long long",
}

func main() {
	args := os.Args
	if len(args) < 2 {
		usage()
		os.Exit(2)
	}

	// Find first arg that looks like a go file and assume everything before
	// that are options to pass to gcc.
	var i int
	for i = len(args) - 1; i > 0; i-- {
		if !strings.HasSuffix(args[i], ".go") {
			break
		}
	}

	i += 1

	gccOptions, goFiles := args[1:i], args[i:]

	arch := os.Getenv("GOARCH")
	if arch == "" {
		fatal("$GOARCH is not set")
	}
	ptrSize, ok := ptrSizeMap[arch]
	if !ok {
		fatal("unknown architecture %s", arch)
	}

	// Clear locale variables so gcc emits English errors [sic].
	os.Setenv("LANG", "en_US.UTF-8")
	os.Setenv("LC_ALL", "C")
	os.Setenv("LC_CTYPE", "C")

	p := new(Prog)

	p.PtrSize = ptrSize
	p.GccOptions = gccOptions
	p.Vardef = make(map[string]*Type)
	p.Funcdef = make(map[string]*FuncType)
	p.Enumdef = make(map[string]int64)
	p.Constdef = make(map[string]string)
	p.OutDefs = make(map[string]bool)

	for _, input := range goFiles {
		// Reset p.Preamble so that we don't end up with conflicting headers / defines
		p.Preamble = builtinProlog
		openProg(input, p)
		for _, cref := range p.Crefs {
			// Convert C.ulong to C.unsigned long, etc.
			if expand, ok := expandName[cref.Name]; ok {
				cref.Name = expand
			}
		}
		p.loadDebugInfo()
		for _, cref := range p.Crefs {
			switch cref.Context {
			case "const":
				// This came from a #define and we'll output it later.
				*cref.Expr = ast.NewIdent(cref.Name)
				break
			case "call":
				if !cref.TypeName {
					// Is an actual function call.
					pos := (*cref.Expr).Pos()
					*cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)}
					p.Funcdef[cref.Name] = cref.FuncType
					break
				}
				*cref.Expr = cref.Type.Go
			case "expr":
				if cref.TypeName {
					error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name)
				}
				// If the expression refers to an enumerated value, then
				// place the identifier for the value and add it to Enumdef so
				// it will be declared as a constant in the later stage.
				if cref.Type.EnumValues != nil {
					*cref.Expr = ast.NewIdent(cref.Name)
					p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name]
					break
				}
				// Reference to C variable.
				// We declare a pointer and arrange to have it filled in.
				*cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)}
				p.Vardef[cref.Name] = cref.Type
			case "type":
				if !cref.TypeName {
					error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name)
				}
				*cref.Expr = cref.Type.Go
			}
		}
		if nerrors > 0 {
			os.Exit(2)
		}
		pkg := p.Package
		if dir := os.Getenv("CGOPKGPATH"); dir != "" {
			pkg = dir + "/" + pkg
		}
		p.PackagePath = pkg
		p.writeOutput(input)
	}

	p.writeDefs()
}
<commit_msg>cgo: better error for no C symbols<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Cgo; see gmp.go for an overview.

// TODO(rsc):
//	Emit correct line number annotations.
//	Make 6g understand the annotations.

package main

import (
	"fmt"
	"go/ast"
	"os"
	"strings"
)

func usage() { fmt.Fprint(os.Stderr, "usage: cgo [compiler options] file.go ...\n") }

var ptrSizeMap = map[string]int64{
	"386":   4,
	"amd64": 8,
	"arm":   4,
}

var expandName = map[string]string{
	"schar":     "signed char",
	"uchar":     "unsigned char",
	"ushort":    "unsigned short",
	"uint":      "unsigned int",
	"ulong":     "unsigned long",
	"longlong":  "long long",
	"ulonglong": "unsigned long long",
}

func main() {
	args := os.Args
	if len(args) < 2 {
		usage()
		os.Exit(2)
	}

	// Find first arg that looks like a go file and assume everything before
	// that are options to pass to gcc.
	var i int
	for i = len(args) - 1; i > 0; i-- {
		if !strings.HasSuffix(args[i], ".go") {
			break
		}
	}

	i += 1

	gccOptions, goFiles := args[1:i], args[i:]

	arch := os.Getenv("GOARCH")
	if arch == "" {
		fatal("$GOARCH is not set")
	}
	ptrSize, ok := ptrSizeMap[arch]
	if !ok {
		fatal("unknown architecture %s", arch)
	}

	// Clear locale variables so gcc emits English errors [sic].
	os.Setenv("LANG", "en_US.UTF-8")
	os.Setenv("LC_ALL", "C")
	os.Setenv("LC_CTYPE", "C")

	p := new(Prog)

	p.PtrSize = ptrSize
	p.GccOptions = gccOptions
	p.Vardef = make(map[string]*Type)
	p.Funcdef = make(map[string]*FuncType)
	p.Enumdef = make(map[string]int64)
	p.Constdef = make(map[string]string)
	p.OutDefs = make(map[string]bool)

	for _, input := range goFiles {
		// Reset p.Preamble so that we don't end up with conflicting headers / defines
		p.Preamble = builtinProlog
		openProg(input, p)

		if len(p.Vardef) == 0 && len(p.Funcdef) == 0 && len(p.Enumdef) == 0 && len(p.Constdef) == 0 {
			fatal("no C symbols were used in cgo file " + input)
		}

		for _, cref := range p.Crefs {
			// Convert C.ulong to C.unsigned long, etc.
			if expand, ok := expandName[cref.Name]; ok {
				cref.Name = expand
			}
		}
		p.loadDebugInfo()
		for _, cref := range p.Crefs {
			switch cref.Context {
			case "const":
				// This came from a #define and we'll output it later.
				*cref.Expr = ast.NewIdent(cref.Name)
				break
			case "call":
				if !cref.TypeName {
					// Is an actual function call.
					pos := (*cref.Expr).Pos()
					*cref.Expr = &ast.Ident{Position: pos, Obj: ast.NewObj(ast.Err, pos, "_C_"+cref.Name)}
					p.Funcdef[cref.Name] = cref.FuncType
					break
				}
				*cref.Expr = cref.Type.Go
			case "expr":
				if cref.TypeName {
					error((*cref.Expr).Pos(), "type C.%s used as expression", cref.Name)
				}
				// If the expression refers to an enumerated value, then
				// place the identifier for the value and add it to Enumdef so
				// it will be declared as a constant in the later stage.
				if cref.Type.EnumValues != nil {
					*cref.Expr = ast.NewIdent(cref.Name)
					p.Enumdef[cref.Name] = cref.Type.EnumValues[cref.Name]
					break
				}
				// Reference to C variable.
				// We declare a pointer and arrange to have it filled in.
				*cref.Expr = &ast.StarExpr{X: ast.NewIdent("_C_" + cref.Name)}
				p.Vardef[cref.Name] = cref.Type
			case "type":
				if !cref.TypeName {
					error((*cref.Expr).Pos(), "expression C.%s used as type", cref.Name)
				}
				*cref.Expr = cref.Type.Go
			}
		}
		if nerrors > 0 {
			os.Exit(2)
		}
		pkg := p.Package
		if dir := os.Getenv("CGOPKGPATH"); dir != "" {
			pkg = dir + "/" + pkg
		}
		p.PackagePath = pkg
		p.writeOutput(input)
	}

	p.writeDefs()
}
<|endoftext|>


<commit_before>package fs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"syscall"

	git "github.com/libgit2/git2go"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
)

type treeFS struct {
	repo *git.Repository
	opts GitFSOptions
}

type GitFSOptions struct {
	Lazy    bool
	Disk    bool
	TempDir string
}

// NewTreeFS creates a git Tree FS. The treeish should resolve to tree SHA1.
func NewTreeFSRoot(repo *git.Repository, treeish string, opts *GitFSOptions) (nodefs.Node, error) {
	obj, err := repo.RevparseSingle(treeish)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	var treeId *git.Oid
	switch obj.Type() {
	case git.ObjectCommit:
		commit, err := repo.LookupCommit(obj.Id())
		if err != nil {
			return nil, err
		}
		treeId = commit.TreeId()
	case git.ObjectTree:
		treeId = obj.Id()
	default:
		return nil, fmt.Errorf("gitfs: unsupported object type %d", obj.Type())
	}

	if opts == nil {
		opts = &GitFSOptions{
			Lazy: true,
			Disk: false,
		}
	}
	if opts.TempDir == "" {
		opts.TempDir, err = ioutil.TempDir("", "gitfs")
		if err != nil {
			return nil, err
		}
	}

	t := &treeFS{
		repo: repo,
		opts: *opts,
	}
	root := t.newDirNode(treeId)
	return root, nil
}

func (t *treeFS) onMount(root *dirNode) {
	tree, err := t.repo.LookupTree(root.id)
	if err != nil {
		panic(err)
	}
	defer tree.Free()
	if root.Inode() == nil {
		panic("nil?")
	}
	t.recurse(tree, root)
	if err != nil {
		panic(err)
	}
}

type mutableLink struct {
	nodefs.Node
	content []byte
}

func (n *mutableLink) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *mutableLink) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.content, fuse.OK
}

type gitNode struct {
	fs *treeFS
	id *git.Oid
	nodefs.Node
}

type dirNode struct {
	gitNode
}

func (n *dirNode) OnMount(conn *nodefs.FileSystemConnector) {
	n.fs.onMount(n)
}

func (n *dirNode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
	l := &mutableLink{nodefs.NewDefaultNode(), []byte(content)}
	return n.Inode().NewChild(name, false, l), fuse.OK
}

func (n *dirNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
	ch := n.Inode().GetChild(name)
	if ch == nil {
		return fuse.ENOENT
	}

	if _, ok := ch.Node().(*mutableLink); !ok {
		return fuse.EPERM
	}

	n.Inode().RmChild(name)
	return fuse.OK
}

type blobNode struct {
	gitNode
	mode git.Filemode
	size uint64
}

type linkNode struct {
	gitNode
	target []byte
}

func (n *linkNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *linkNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.target, fuse.OK
}

func (n *blobNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	ctor := n.LoadMemory
	if n.fs.opts.Disk {
		ctor = n.LoadDisk
	}

	if !n.fs.opts.Lazy {
		f, err := ctor()
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		return f, fuse.OK
	}

	return &lazyBlobFile{
		ctor: ctor,
		node: n,
	}, fuse.OK
}

func (n *blobNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = uint32(n.mode)
	out.Size = uint64(n.size)
	return fuse.OK
}

func (t *treeFS) newLinkNode(id *git.Oid) (nodefs.Node, error) {
	n := &linkNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}

	blob, err := t.repo.LookupBlob(id)
	if err != nil {
		return nil, err
	}
	defer blob.Free()
	n.target = append([]byte{}, blob.Contents()...)
	return n, nil
}

func (n *blobNode) LoadMemory() (nodefs.File, error) {
	blob, err := n.fs.repo.LookupBlob(n.id)
	if err != nil {
		return nil, err
	}
	return &memoryFile{
		File: nodefs.NewDefaultFile(),
		blob: blob,
	}, nil
}

type lazyBlobFile struct {
	mu sync.Mutex
	nodefs.File
	ctor func() (nodefs.File, error)
	node *blobNode
}

func (f *lazyBlobFile) SetInode(n *nodefs.Inode) {
}

func (f *lazyBlobFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File == nil {
		g, err := f.ctor()
		if err != nil {
			log.Printf("opening blob for %s: %v", f.node.id.String(), err)
			return nil, fuse.EIO
		}
		f.File = g
	}
	return f.File.Read(dest, off)
}

func (f *lazyBlobFile) Flush() fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		return f.File.Flush()
	}
	return fuse.OK
}

func (f *lazyBlobFile) Release() {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		f.File.Release()
	}
}

type memoryFile struct {
	nodefs.File
	blob *git.Blob
}

func (f *memoryFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	b := f.blob.Contents()
	end := off + int64(len(dest))
	if end > int64(len(b)) {
		end = int64(len(b))
	}
	return fuse.ReadResultData(b[off:end]), fuse.OK
}

func (f *memoryFile) Release() {
	f.blob.Free()
}

func (n *blobNode) LoadDisk() (nodefs.File, error) {
	p := filepath.Join(n.fs.opts.TempDir, n.id.String())
	if _, err := os.Lstat(p); os.IsNotExist(err) {
		blob, err := n.fs.repo.LookupBlob(n.id)
		if err != nil {
			return nil, err
		}
		defer blob.Free()

		// TODO - atomic, use content store to share content.
		if err := ioutil.WriteFile(p, blob.Contents(), 0644); err != nil {
			return nil, err
		}
	}
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}

	return nodefs.NewLoopbackFile(f), nil
}

func (t *treeFS) newBlobNode(id *git.Oid, mode git.Filemode) (nodefs.Node, error) {
	n := &blobNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	odb, err := t.repo.Odb()
	if err != nil {
		return nil, err
	}
	defer odb.Free()
	obj, err := odb.Read(id)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	n.size = obj.Len()
	n.mode = mode
	return n, nil
}

func (t *treeFS) newDirNode(id *git.Oid) nodefs.Node {
	n := &dirNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	return n
}

func (t *treeFS) recurse(tree *git.Tree, n *dirNode) error {
	for i := uint64(0); ; i++ {
		e := tree.EntryByIndex(i)
		if e == nil {
			break
		}
		isdir := e.Filemode&syscall.S_IFDIR != 0
		var chNode nodefs.Node
		if isdir {
			chNode = t.newDirNode(e.Id)
		} else if e.Filemode&^07777 == syscall.S_IFLNK {
			l, err := t.newLinkNode(e.Id)
			if err != nil {
				return err
			}
			chNode = l
		} else if e.Filemode&^07777 == syscall.S_IFREG {
			b, err := t.newBlobNode(e.Id, e.Filemode)
			if err != nil {
				return err
			}
			chNode = b
		} else {
			panic(e)
		}
		n.Inode().NewChild(e.Name, isdir, chNode)

		if isdir {
			tree, err := t.repo.LookupTree(chNode.(*dirNode).id)
			if err != nil {
				return err
			}

			if err := t.recurse(tree, chNode.(*dirNode)); err != nil {
				return nil
			}
		}
	}
	return nil
}
<commit_msg>fs: simplify recurse()<commit_after>package fs

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"sync"
	"syscall"

	git "github.com/libgit2/git2go"

	"github.com/hanwen/go-fuse/fuse"
	"github.com/hanwen/go-fuse/fuse/nodefs"
)

type treeFS struct {
	repo *git.Repository
	opts GitFSOptions
}

type GitFSOptions struct {
	Lazy    bool
	Disk    bool
	TempDir string
}

// NewTreeFS creates a git Tree FS. The treeish should resolve to tree SHA1.
func NewTreeFSRoot(repo *git.Repository, treeish string, opts *GitFSOptions) (nodefs.Node, error) {
	obj, err := repo.RevparseSingle(treeish)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	var treeId *git.Oid
	switch obj.Type() {
	case git.ObjectCommit:
		commit, err := repo.LookupCommit(obj.Id())
		if err != nil {
			return nil, err
		}
		treeId = commit.TreeId()
	case git.ObjectTree:
		treeId = obj.Id()
	default:
		return nil, fmt.Errorf("gitfs: unsupported object type %d", obj.Type())
	}

	if opts == nil {
		opts = &GitFSOptions{
			Lazy: true,
			Disk: false,
		}
	}
	if opts.TempDir == "" {
		opts.TempDir, err = ioutil.TempDir("", "gitfs")
		if err != nil {
			return nil, err
		}
	}

	t := &treeFS{
		repo: repo,
		opts: *opts,
	}
	root := t.newDirNode(treeId)
	return root, nil
}

func (t *treeFS) onMount(root *dirNode) {
	tree, err := t.repo.LookupTree(root.id)
	if err != nil {
		panic(err)
	}
	defer tree.Free()
	if root.Inode() == nil {
		panic("nil?")
	}
	t.recurse(tree, root)
	if err != nil {
		panic(err)
	}
}

type mutableLink struct {
	nodefs.Node
	content []byte
}

func (n *mutableLink) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *mutableLink) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.content, fuse.OK
}

type gitNode struct {
	fs *treeFS
	id *git.Oid
	nodefs.Node
}

type dirNode struct {
	gitNode
}

func (n *dirNode) OnMount(conn *nodefs.FileSystemConnector) {
	n.fs.onMount(n)
}

func (n *dirNode) Symlink(name string, content string, context *fuse.Context) (*nodefs.Inode, fuse.Status) {
	l := &mutableLink{nodefs.NewDefaultNode(), []byte(content)}
	return n.Inode().NewChild(name, false, l), fuse.OK
}

func (n *dirNode) Unlink(name string, context *fuse.Context) (code fuse.Status) {
	ch := n.Inode().GetChild(name)
	if ch == nil {
		return fuse.ENOENT
	}

	if _, ok := ch.Node().(*mutableLink); !ok {
		return fuse.EPERM
	}

	n.Inode().RmChild(name)
	return fuse.OK
}

type blobNode struct {
	gitNode
	mode git.Filemode
	size uint64
}

type linkNode struct {
	gitNode
	target []byte
}

func (n *linkNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = fuse.S_IFLNK
	return fuse.OK
}

func (n *linkNode) Readlink(c *fuse.Context) ([]byte, fuse.Status) {
	return n.target, fuse.OK
}

func (n *blobNode) Open(flags uint32, context *fuse.Context) (file nodefs.File, code fuse.Status) {
	if flags&fuse.O_ANYWRITE != 0 {
		return nil, fuse.EPERM
	}

	ctor := n.LoadMemory
	if n.fs.opts.Disk {
		ctor = n.LoadDisk
	}

	if !n.fs.opts.Lazy {
		f, err := ctor()
		if err != nil {
			return nil, fuse.ToStatus(err)
		}
		return f, fuse.OK
	}

	return &lazyBlobFile{
		ctor: ctor,
		node: n,
	}, fuse.OK
}

func (n *blobNode) GetAttr(out *fuse.Attr, file nodefs.File, context *fuse.Context) (code fuse.Status) {
	out.Mode = uint32(n.mode)
	out.Size = uint64(n.size)
	return fuse.OK
}

func (t *treeFS) newLinkNode(id *git.Oid) (nodefs.Node, error) {
	n := &linkNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}

	blob, err := t.repo.LookupBlob(id)
	if err != nil {
		return nil, err
	}
	defer blob.Free()
	n.target = append([]byte{}, blob.Contents()...)
	return n, nil
}

func (n *blobNode) LoadMemory() (nodefs.File, error) {
	blob, err := n.fs.repo.LookupBlob(n.id)
	if err != nil {
		return nil, err
	}
	return &memoryFile{
		File: nodefs.NewDefaultFile(),
		blob: blob,
	}, nil
}

type lazyBlobFile struct {
	mu sync.Mutex
	nodefs.File
	ctor func() (nodefs.File, error)
	node *blobNode
}

func (f *lazyBlobFile) SetInode(n *nodefs.Inode) {
}

func (f *lazyBlobFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File == nil {
		g, err := f.ctor()
		if err != nil {
			log.Printf("opening blob for %s: %v", f.node.id.String(), err)
			return nil, fuse.EIO
		}
		f.File = g
	}
	return f.File.Read(dest, off)
}

func (f *lazyBlobFile) Flush() fuse.Status {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		return f.File.Flush()
	}
	return fuse.OK
}

func (f *lazyBlobFile) Release() {
	f.mu.Lock()
	defer f.mu.Unlock()
	if f.File != nil {
		f.File.Release()
	}
}

type memoryFile struct {
	nodefs.File
	blob *git.Blob
}

func (f *memoryFile) Read(dest []byte, off int64) (fuse.ReadResult, fuse.Status) {
	b := f.blob.Contents()
	end := off + int64(len(dest))
	if end > int64(len(b)) {
		end = int64(len(b))
	}
	return fuse.ReadResultData(b[off:end]), fuse.OK
}

func (f *memoryFile) Release() {
	f.blob.Free()
}

func (n *blobNode) LoadDisk() (nodefs.File, error) {
	p := filepath.Join(n.fs.opts.TempDir, n.id.String())
	if _, err := os.Lstat(p); os.IsNotExist(err) {
		blob, err := n.fs.repo.LookupBlob(n.id)
		if err != nil {
			return nil, err
		}
		defer blob.Free()

		// TODO - atomic, use content store to share content.
		if err := ioutil.WriteFile(p, blob.Contents(), 0644); err != nil {
			return nil, err
		}
	}
	f, err := os.Open(p)
	if err != nil {
		return nil, err
	}

	return nodefs.NewLoopbackFile(f), nil
}

func (t *treeFS) newBlobNode(id *git.Oid, mode git.Filemode) (nodefs.Node, error) {
	n := &blobNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	odb, err := t.repo.Odb()
	if err != nil {
		return nil, err
	}
	defer odb.Free()
	obj, err := odb.Read(id)
	if err != nil {
		return nil, err
	}
	defer obj.Free()

	n.size = obj.Len()
	n.mode = mode
	return n, nil
}

func (t *treeFS) newDirNode(id *git.Oid) nodefs.Node {
	n := &dirNode{
		gitNode: gitNode{
			fs:   t,
			id:   id.Copy(),
			Node: nodefs.NewDefaultNode(),
		},
	}
	return n
}

func (t *treeFS) recurse(tree *git.Tree, n nodefs.Node) error {
	for i := uint64(0); ; i++ {
		e := tree.EntryByIndex(i)
		if e == nil {
			break
		}
		isdir := e.Filemode&syscall.S_IFDIR != 0
		var chNode nodefs.Node
		if isdir {
			chNode = t.newDirNode(e.Id)
		} else if e.Filemode&^07777 == syscall.S_IFLNK {
			l, err := t.newLinkNode(e.Id)
			if err != nil {
				return err
			}
			chNode = l
		} else if e.Filemode&^07777 == syscall.S_IFREG {
			b, err := t.newBlobNode(e.Id, e.Filemode)
			if err != nil {
				return err
			}
			chNode = b
		} else {
			panic(e)
		}
		n.Inode().NewChild(e.Name, isdir, chNode)
		if isdir {
			tree, err := t.repo.LookupTree(e.Id)
			if err != nil {
				return err
			}

			if err := t.recurse(tree, chNode); err != nil {
				return nil
			}
		}
	}
	return nil
}
<|endoftext|>


<commit_before>/*
   Copyright 2014 GoPivotal (UK) Limited.

   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.
*/

/*
	The kernel package encapsulates the operating system features required
	to create a container.
*/
package kernel

/*
	A resource controller virtualises a specific type of resource.
*/
type ResourceController interface {
	Init() error
}
<commit_msg>improve doc<commit_after>/*
   Copyright 2014 GoPivotal (UK) Limited.

   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.
*/

/*
	The kernel package encapsulates the operating system features required
	to create a container.
*/
package kernel

/*
	A resource controller provides containment for a specific type of resource.
*/
type ResourceController interface {
	Init() error
}
<|endoftext|>


<commit_before>// Package memdb provides an in-memory database that supports transactions
// and MVCC.
package memdb

import (
	"sync"
	"sync/atomic"
	"unsafe"

	"github.com/hashicorp/go-immutable-radix"
)

// MemDB is an in-memory database providing Atomicity, Consistency, and
// Isolation from ACID. MemDB doesn't provide Durability since it is an
// in-memory database.
//
// MemDB provides a table abstraction to store objects (rows) with multiple
// indexes based on inserted values. The database makes use of immutable radix
// trees to provide transactions and MVCC.
//
// Objects inserted into MemDB are not copied. It is **extremely important**
// that objects are not modified in-place after they are inserted since they
// are stored directly in MemDB. It remains unsafe to modify existing values
// even after they've been deleted from MemDB since there may still be older
// snapshots of the DB being read from other goroutines.
type MemDB struct {
	schema  *DBSchema
	root    unsafe.Pointer // *iradix.Tree underneath
	primary bool

	// There can only be a single writer at once
	writer sync.Mutex
}

// NewMemDB creates a new MemDB with the given schema.
func NewMemDB(schema *DBSchema) (*MemDB, error) {
	// Validate the schema
	if err := schema.Validate(); err != nil {
		return nil, err
	}

	// Create the MemDB
	db := &MemDB{
		schema:  schema,
		root:    unsafe.Pointer(iradix.New()),
		primary: true,
	}
	if err := db.initialize(); err != nil {
		return nil, err
	}

	return db, nil
}

// getRoot is used to do an atomic load of the root pointer
func (db *MemDB) getRoot() *iradix.Tree {
	root := (*iradix.Tree)(atomic.LoadPointer(&db.root))
	return root
}

// Txn is used to start a new transaction in either read or write mode.
// There can only be a single concurrent writer, but any number of readers.
func (db *MemDB) Txn(write bool) *Txn {
	if write {
		db.writer.Lock()
	}
	txn := &Txn{
		db:      db,
		write:   write,
		rootTxn: db.getRoot().Txn(),
	}
	return txn
}

// Snapshot is used to capture a point-in-time snapshot  of the database that
// will not be affected by any write operations to the existing DB.
//
// If MemDB is storing reference-based values (pointers, maps, slices, etc.),
// the Snapshot will not deep copy those values. Therefore, it is still unsafe
// to modify any inserted values in either DB.
func (db *MemDB) Snapshot() *MemDB {
	clone := &MemDB{
		schema:  db.schema,
		root:    unsafe.Pointer(db.getRoot()),
		primary: false,
	}
	return clone
}

// initialize is used to setup the DB for use after creation. This should
// be called only once after allocating a MemDB.
func (db *MemDB) initialize() error {
	root := db.getRoot()
	for tName, tableSchema := range db.schema.Tables {
		for iName := range tableSchema.Indexes {
			index := iradix.New()
			path := indexPath(tName, iName)
			root, _, _ = root.Insert(path, index)
		}
	}
	db.root = unsafe.Pointer(root)
	return nil
}

// indexPath returns the path from the root to the given table index
func indexPath(table, index string) []byte {
	return []byte(table + "." + index)
}
<commit_msg>Update memdb.go<commit_after>// Package memdb provides an in-memory database that supports transactions
// and MVCC.
package memdb

import (
	"sync"
	"sync/atomic"
	"unsafe"

	"github.com/hashicorp/go-immutable-radix"
)

// MemDB is an in-memory database providing Atomicity, Consistency, and
// Isolation from ACID. MemDB doesn't provide Durability since it is an
// in-memory database.
//
// MemDB provides a table abstraction to store objects (rows) with multiple
// indexes based on inserted values. The database makes use of immutable radix
// trees to provide transactions and MVCC.
//
// Objects inserted into MemDB are not copied. It is **extremely important**
// that objects are not modified in-place after they are inserted since they
// are stored directly in MemDB. It remains unsafe to modify inserted objects
// even after they've been deleted from MemDB since there may still be older
// snapshots of the DB being read from other goroutines.
type MemDB struct {
	schema  *DBSchema
	root    unsafe.Pointer // *iradix.Tree underneath
	primary bool

	// There can only be a single writer at once
	writer sync.Mutex
}

// NewMemDB creates a new MemDB with the given schema.
func NewMemDB(schema *DBSchema) (*MemDB, error) {
	// Validate the schema
	if err := schema.Validate(); err != nil {
		return nil, err
	}

	// Create the MemDB
	db := &MemDB{
		schema:  schema,
		root:    unsafe.Pointer(iradix.New()),
		primary: true,
	}
	if err := db.initialize(); err != nil {
		return nil, err
	}

	return db, nil
}

// getRoot is used to do an atomic load of the root pointer
func (db *MemDB) getRoot() *iradix.Tree {
	root := (*iradix.Tree)(atomic.LoadPointer(&db.root))
	return root
}

// Txn is used to start a new transaction in either read or write mode.
// There can only be a single concurrent writer, but any number of readers.
func (db *MemDB) Txn(write bool) *Txn {
	if write {
		db.writer.Lock()
	}
	txn := &Txn{
		db:      db,
		write:   write,
		rootTxn: db.getRoot().Txn(),
	}
	return txn
}

// Snapshot is used to capture a point-in-time snapshot  of the database that
// will not be affected by any write operations to the existing DB.
//
// If MemDB is storing reference-based values (pointers, maps, slices, etc.),
// the Snapshot will not deep copy those values. Therefore, it is still unsafe
// to modify any inserted values in either DB.
func (db *MemDB) Snapshot() *MemDB {
	clone := &MemDB{
		schema:  db.schema,
		root:    unsafe.Pointer(db.getRoot()),
		primary: false,
	}
	return clone
}

// initialize is used to setup the DB for use after creation. This should
// be called only once after allocating a MemDB.
func (db *MemDB) initialize() error {
	root := db.getRoot()
	for tName, tableSchema := range db.schema.Tables {
		for iName := range tableSchema.Indexes {
			index := iradix.New()
			path := indexPath(tName, iName)
			root, _, _ = root.Insert(path, index)
		}
	}
	db.root = unsafe.Pointer(root)
	return nil
}

// indexPath returns the path from the root to the given table index
func indexPath(table, index string) []byte {
	return []byte(table + "." + index)
}
<|endoftext|>


<commit_before>package gerrittest

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"time"

	"github.com/opalmer/gerrittest/internal"
	"github.com/pkg/errors"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/config"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

var (
	// DefaultTempName is used as the prefix or suffix of temporary files
	// and folders.
	DefaultTempName = "gerrittest-"

	// ErrRepositoryNotInitialized may be returned by any operation that
	// requires a fully setup Repository struct.
	ErrRepositoryNotInitialized = errors.New("The repository is not initialized")
)

// Repository is used to store information about an interact
// with a git repository.
type Repository struct {
	Path   string
	Repo   *git.Repository
	User   string // Defaults to 'admin' in Init()
	Email  string // Defaults to '<User>@localhost' in Init()
	Branch string // Defaults to 'master' in Init()
}

func (r *Repository) writeConfig() error {
	if r.Repo == nil {
		return ErrRepositoryNotInitialized
	}
	cfg, err := r.Repo.Config()
	if err != nil {
		return err
	}
	data, err := cfg.Marshal()
	if err != nil {
		return err
	}
	return ioutil.WriteFile(filepath.Join(r.Path, ".git", "config"), data, 0600)
}

// CreateRemoteFromSpec adds a new remote based on the provided spec.
func (r *Repository) CreateRemoteFromSpec(service *ServiceSpec, remoteName string, project string) error {
	_, err := r.Repo.CreateRemote(&config.RemoteConfig{
		Name: remoteName,
		URL: fmt.Sprintf(
			"ssh://%s@%s:%d/%s", service.Admin.Login,
			service.SSH.Address, service.SSH.Public, project),
		Fetch: []config.RefSpec{"+refs/heads/*:refs/remotes/origin/*"},
	})
	if err != nil {
		return err
	}
	return r.writeConfig()
}

func (r Repository) setDefaults() error {
	if r.Path == "" {
		path, err := ioutil.TempDir("", DefaultTempName)
		if err != nil {
			return err
		}
		r.Path = path
	}

	// Configure defaults.
	if r.User == "" {
		r.User = "admin"
	}
	if r.Email == "" {
		r.Email = fmt.Sprintf("%s@localhost", r.User)
	}
	if r.Branch == "" {
		r.Branch = "master"
	}
	return nil
}

// Init initializes the git repository. If the repository was setup without
// a path then a temp path will be used. Note, this may make modifications to
// an existing repository.
func (r *Repository) Init() error {
	if r.Repo != nil {
		return nil
	}

	if err := r.setDefaults(); err != nil {
		return err
	}

	// Create the repository.
	if _, err := os.Stat(filepath.Join(r.Path, ".git")); os.IsNotExist(err) {
		repo, err := git.PlainInit(r.Path, false)
		if err != nil {
			return err
		}
		r.Repo = repo

		// Open an existing repository.
	} else {
		repo, err := git.PlainOpen(r.Path)
		if err != nil {
			return err
		}
		r.Repo = repo
	}

	// Drop the commit hook on disk.
	if err := os.MkdirAll(filepath.Join(r.Path, ".git", "hooks"), 0700); err != nil {
		return err
	}
	if err := ioutil.WriteFile(
		filepath.Join(r.Path, ".git", "hooks", "commit-msg"),
		internal.MustAsset("internal/commit-msg"), 0700); err != nil {
		return err
	}

	// Add user/email to the config and write it to disk.
	cfg, err := r.Repo.Config()
	if err != nil {
		return err
	}
	cfg.Raw = cfg.Raw.AddOption("core", "", "user", r.User)
	cfg.Raw = cfg.Raw.AddOption("core", "", "email", r.Email)
	return r.writeConfig()
}

// Add adds a path to the repository. The path must be relative to the root of
// the repository
func (r *Repository) Add(path string) error {
	if r.Repo == nil {
		return ErrRepositoryNotInitialized
	}

	tree, err := r.Repo.Worktree()
	if err != nil {
		return err
	}
	_, err = tree.Add(path)
	return err
}

// Commit will add a new commit to the repository with the
// given message.
func (r *Repository) Commit(message string) error {
	if r.Repo == nil {
		return ErrRepositoryNotInitialized
	}

	tree, err := r.Repo.Worktree()
	if err != nil {
		return err
	}
	author := &object.Signature{
		Name:  r.User,
		Email: r.Email,
		When:  time.Now(),
	}
	_, err = tree.Commit(message, &git.CommitOptions{
		All:       false,
		Author:    author,
		Committer: author,
	})
	return err
}

// Remove will remove the entire repository from disk, useful for temporary
// repositories. This cannot be reversed.
func (r *Repository) Remove() error {
	if r.Path == "" {
		return ErrRepositoryNotInitialized
	}
	return os.RemoveAll(r.Path)
}
<commit_msg>travis lint fix<commit_after>package gerrittest

import (
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"

	"time"

	"github.com/opalmer/gerrittest/internal"
	"github.com/pkg/errors"
	"gopkg.in/src-d/go-git.v4"
	"gopkg.in/src-d/go-git.v4/config"
	"gopkg.in/src-d/go-git.v4/plumbing/object"
)

var (
	// DefaultTempName is used as the prefix or suffix of temporary files
	// and folders.
	DefaultTempName = "gerrittest-"

	// ErrRepositoryNotInitialized may be returned by any operation that
	// requires a fully setup Repository struct.
	ErrRepositoryNotInitialized = errors.New("The repository is not initialized")
)

// Repository is used to store information about an interact
// with a git repository.
type Repository struct {
	Path   string
	Repo   *git.Repository
	User   string // Defaults to 'admin' in Init()
	Email  string // Defaults to '<User>@localhost' in Init()
	Branch string // Defaults to 'master' in Init()
}

func (r *Repository) writeConfig() error {
	if r.Repo == nil {
		return ErrRepositoryNotInitialized
	}
	cfg, err := r.Repo.Config()
	if err != nil {
		return err
	}
	data, err := cfg.Marshal()
	if err != nil {
		return err
	}
	return ioutil.WriteFile(filepath.Join(r.Path, ".git", "config"), data, 0600)
}

// CreateRemoteFromSpec adds a new remote based on the provided spec.
// nolint: unused,gosimple,unconvert,varcheck
func (r *Repository) CreateRemoteFromSpec(service *ServiceSpec, remoteName string, project string) error {
	_, err := r.Repo.CreateRemote(&config.RemoteConfig{
		Name: remoteName,
		URL: fmt.Sprintf(
			"ssh://%s@%s:%d/%s", service.Admin.Login,
			service.SSH.Address, service.SSH.Public, project),
		Fetch: []config.RefSpec{"+refs/heads/*:refs/remotes/origin/*"},
	})
	if err != nil {
		return err
	}
	return r.writeConfig()
}

func (r Repository) setDefaults() error {
	if r.Path == "" {
		path, err := ioutil.TempDir("", DefaultTempName)
		if err != nil {
			return err
		}
		r.Path = path
	}

	// Configure defaults.
	if r.User == "" {
		r.User = "admin"
	}
	if r.Email == "" {
		r.Email = fmt.Sprintf("%s@localhost", r.User)
	}
	if r.Branch == "" {
		r.Branch = "master"
	}
	return nil
}

// Init initializes the git repository. If the repository was setup without
// a path then a temp path will be used. Note, this may make modifications to
// an existing repository.
func (r *Repository) Init() error {
	if r.Repo != nil {
		return nil
	}

	if err := r.setDefaults(); err != nil {
		return err
	}

	// Create the repository.
	if _, err := os.Stat(filepath.Join(r.Path, ".git")); os.IsNotExist(err) {
		repo, err := git.PlainInit(r.Path, false)
		if err != nil {
			return err
		}
		r.Repo = repo

		// Open an existing repository.
	} else {
		repo, err := git.PlainOpen(r.Path)
		if err != nil {
			return err
		}
		r.Repo = repo
	}

	// Drop the commit hook on disk.
	if err := os.MkdirAll(filepath.Join(r.Path, ".git", "hooks"), 0700); err != nil {
		return err
	}
	if err := ioutil.WriteFile(
		filepath.Join(r.Path, ".git", "hooks", "commit-msg"),
		internal.MustAsset("internal/commit-msg"), 0700); err != nil {
		return err
	}

	// Add user/email to the config and write it to disk.
	cfg, err := r.Repo.Config()
	if err != nil {
		return err
	}
	cfg.Raw = cfg.Raw.AddOption("core", "", "user", r.User)
	cfg.Raw = cfg.Raw.AddOption("core", "", "email", r.Email)
	return r.writeConfig()
}

// Add adds a path to the repository. The path must be relative to the root of
// the repository
func (r *Repository) Add(path string) error {
	if r.Repo == nil {
		return ErrRepositoryNotInitialized
	}

	tree, err := r.Repo.Worktree()
	if err != nil {
		return err
	}
	_, err = tree.Add(path)
	return err
}

// Commit will add a new commit to the repository with the
// given message.
func (r *Repository) Commit(message string) error {
	if r.Repo == nil {
		return ErrRepositoryNotInitialized
	}

	tree, err := r.Repo.Worktree()
	if err != nil {
		return err
	}
	author := &object.Signature{
		Name:  r.User,
		Email: r.Email,
		When:  time.Now(),
	}
	_, err = tree.Commit(message, &git.CommitOptions{
		All:       false,
		Author:    author,
		Committer: author,
	})
	return err
}

// Remove will remove the entire repository from disk, useful for temporary
// repositories. This cannot be reversed.
func (r *Repository) Remove() error {
	if r.Path == "" {
		return ErrRepositoryNotInitialized
	}
	return os.RemoveAll(r.Path)
}
<|endoftext|>


<commit_before>package gerrittest

import (
	"bytes"
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"fmt"

	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/opalmer/gerrittest/internal"
)

var (
	// CommandTimeout is the default amount of time to wait
	// for a git command to finish.
	CommandTimeout = time.Second * 60

	// DefaultDirectoryMode is used when creating parent directories
	// for files.
	DefaultDirectoryMode os.FileMode = 0700

	// DefaultFileMode is returned by FileInput.GetMode if no mode
	// has been set.
	DefaultFileMode os.FileMode = 0600
)

// FileInput defines all the information necessary to add a single file to
// a repository.
type FileInput struct {
	Path    string
	Content []byte
	Mode    os.FileMode
}

// GetMode returns the mode of the file. If no mode has been set `0600` will
// be returned.
func (f *FileInput) GetMode() os.FileMode {
	if f.Mode == 0 {
		return DefaultFileMode
	}
	return f.Mode
}

// Repository is a basic wrapper for a git repository. This does not
// fully implement all git commands, just enough to work with
// a repository on disk for the purposes of gerrittest.
type Repository struct {
	Root       string
	Git        string
	mtx        *sync.Mutex
	log        *log.Entry
	sshCommand string
}

// Run will run git with the provided args to completion then return the
// results. The working directory will be changed to the root of the repository
// prior to running and be reset on exit.
func (r *Repository) Run(args []string) (string, string, error) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	logger := r.log.WithFields(log.Fields{
		"action": "run",
		"args":   args,
	})
	ctx, cancel := context.WithTimeout(context.Background(), CommandTimeout)
	defer cancel()

	cwd, err := os.Getwd()
	if err != nil {
		return "", "", err
	}
	if err := os.Chdir(r.Root); err != nil {
		return "", "", err
	}

	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}
	cmd := exec.CommandContext(ctx, r.Git, args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	// Depending on the version of Git either the config command in
	// ConfigureFromSpec() will work or one of the below will.
	cmd.Env = append(
		cmd.Env, fmt.Sprintf("GIT_SSH_COMMAND=%s", r.sshCommand))
	cmd.Env = append(
		cmd.Env, fmt.Sprintf("GIT_SSH=%s", r.sshCommand))

	start := time.Now()
	err = cmd.Run()
	duration := time.Since(start)
	outstr := stdout.String()
	errstr := stderr.String()
	logger = logger.WithFields(log.Fields{
		"duration": duration,
		"stdout":   outstr,
		"stderr":   errstr,
	})

	if err != nil {
		logger.Warn()
		defer os.Chdir(cwd)
		return outstr, errstr, err
	}

	logger.Debug()
	return outstr, errstr, os.Chdir(cwd)
}

// Init will create the repository if it does not exist. This function
// does nothing if the path on disk already appears to be a repository.
func (r *Repository) Init() error {
	if _, err := os.Stat(r.Root); os.IsNotExist(err) {
		if err := os.MkdirAll(r.Root, DefaultDirectoryMode); err != nil {
			return err
		}
	}

	r.log.WithFields(log.Fields{
		"action": "init",
		"path":   r.Root,
	}).Debug()
	_, _, err := r.Run([]string{"init", "--quiet", r.Root})
	config := map[string]string{
		"user.name":  "admin",
		"user.email": "admin@localhost",
	}
	if err := r.ConfigureValues(config); err != nil {
		return err
	}

	hook, err := internal.Asset("internal/commit-msg")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(
		filepath.Join(r.Root, ".git", "hooks", "commit-msg"), hook, 0700)
}

// Destroy removes the repository from the local disk.
func (r *Repository) Destroy() error {
	r.log.WithFields(log.Fields{
		"action": "destroy",
		"path":   r.Root,
	}).Debug()
	return os.RemoveAll(r.Root)
}

// Add adds a single file to the repository.
func (r *Repository) Add(input *FileInput) error {
	r.log.WithFields(log.Fields{
		"action": "add",
		"path":   input.Path,
	}).Debug()
	_, _, err := r.Run([]string{"add", input.Path})
	return err
}

// AddFile performs multiple steps in a single command:
//  - Write a file to disk using the given path. The path itself should
//    be relative to the repository root. Any parent paths will be automatically
//    created.
//  - Set the permissions of the file.
//  - Add the file to the git repository.
func (r *Repository) AddFile(input *FileInput) error {
	logger := r.log.WithFields(log.Fields{
		"action": "add-file",
		"path":   input.Path,
	})

	absolute := filepath.Join(r.Root, input.Path)
	logger = logger.WithField("phase", "mkdir")
	if err := os.MkdirAll(filepath.Dir(absolute), DefaultDirectoryMode); err != nil {
		logger.WithError(err).Warn()
		return err
	}

	logger = logger.WithField("phase", "write")
	if err := ioutil.WriteFile(absolute, input.Content, input.GetMode()); err != nil {
		logger.WithError(err).Warn()
		return err
	}
	return r.Add(input)
}

// Commit will commit any pending changes with the provided message.
func (r *Repository) Commit(message string) error {
	r.log.WithFields(log.Fields{
		"action":  "commit",
		"message": message,
	}).Debug()
	_, _, err := r.Run([]string{"commit", "-m", message, "--quiet"})
	return err
}

// Amend amends the current commit without changing the commit message.
func (r *Repository) Amend() error {
	r.log.WithFields(log.Fields{
		"action": "amend",
	}).Debug()
	_, _, err := r.Run(
		[]string{"commit", "--quiet", "--amend", "--no-edit", "--allow-empty"})

	return err
}

// Push will call 'git push HEAD:refs/for/<branch>'. Note, this will
// return an error if you have not already configured the repository. If no
// branch is provided 'master' will be used.
func (r *Repository) Push(branch string) error {
	if branch == "" {
		branch = "master"
	}
	r.log.WithFields(log.Fields{
		"action": "amend",
	}).Debug()
	_, _, err := r.Run(
		[]string{"push", "origin", fmt.Sprintf("HEAD:refs/for/%s", branch)})
	return err
}

// ConfigureValues runs 'git config' using the given map.
func (r *Repository) ConfigureValues(values map[string]string) error {
	logger := r.log.WithFields(log.Fields{
		"action": "config",
	})
	for key, value := range values {
		logger.WithFields(log.Fields{
			"action": "config",
			"key":    key,
			"value":  value,
		}).Debug()
		if _, _, err := r.Run(
			[]string{"config", "--local", key, value}); err != nil {
			logger.WithError(err).Error()
			return err
		}
	}
	return nil
}

// ConfigureFromSpec will configure the git repository to point at
func (r *Repository) ConfigureFromSpec(service *ServiceSpec, project string, branch string) error {
	r.mtx.Lock()
	r.sshCommand = fmt.Sprintf(
		"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i %s",
		service.Admin.PrivateKey)
	config := map[string]string{
		"remote.origin.url": fmt.Sprintf(
			"ssh://%s@%s:%d/%s", service.Admin.Login,
			service.SSH.Address, service.SSH.Public, project),
		"remote.origin.fetch":                   "+refs/heads/*:refs/remotes/origin/*",
		fmt.Sprintf("branch.%s.remote", branch): "origin",
		fmt.Sprintf("branch.%s.merge", branch):  fmt.Sprintf("refs/heads/%s", branch),
		"core.sshCommand":                       r.sshCommand,
	}
	r.mtx.Unlock()
	return r.ConfigureValues(config)
}

// NewRepository creates and returns a *Repository struct. If root is defined
// as "" then a temporary will be created.
func NewRepository(root string) (*Repository, error) {
	git, err := exec.LookPath("git")
	if err != nil {
		return nil, err
	}
	if root == "" {
		path, err := ioutil.TempDir("", "gerrittest-")
		if err != nil {
			return nil, err
		}
		root = path
	}

	repo := &Repository{
		Root: root,
		Git:  git,
		mtx:  &sync.Mutex{},
		log:  log.WithField("cmp", "repo"),
	}
	return repo, repo.Init()
}
<commit_msg>whitespace fix<commit_after>package gerrittest

import (
	"bytes"
	"context"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"time"

	"fmt"

	"sync"

	log "github.com/Sirupsen/logrus"
	"github.com/opalmer/gerrittest/internal"
)

var (
	// CommandTimeout is the default amount of time to wait
	// for a git command to finish.
	CommandTimeout = time.Second * 60

	// DefaultDirectoryMode is used when creating parent directories
	// for files.
	DefaultDirectoryMode os.FileMode = 0700

	// DefaultFileMode is returned by FileInput.GetMode if no mode
	// has been set.
	DefaultFileMode os.FileMode = 0600
)

// FileInput defines all the information necessary to add a single file to
// a repository.
type FileInput struct {
	Path    string
	Content []byte
	Mode    os.FileMode
}

// GetMode returns the mode of the file. If no mode has been set `0600` will
// be returned.
func (f *FileInput) GetMode() os.FileMode {
	if f.Mode == 0 {
		return DefaultFileMode
	}
	return f.Mode
}

// Repository is a basic wrapper for a git repository. This does not
// fully implement all git commands, just enough to work with
// a repository on disk for the purposes of gerrittest.
type Repository struct {
	Root       string
	Git        string
	mtx        *sync.Mutex
	log        *log.Entry
	sshCommand string
}

// Run will run git with the provided args to completion then return the
// results. The working directory will be changed to the root of the repository
// prior to running and be reset on exit.
func (r *Repository) Run(args []string) (string, string, error) {
	r.mtx.Lock()
	defer r.mtx.Unlock()
	logger := r.log.WithFields(log.Fields{
		"action": "run",
		"args":   args,
	})
	ctx, cancel := context.WithTimeout(context.Background(), CommandTimeout)
	defer cancel()

	cwd, err := os.Getwd()
	if err != nil {
		return "", "", err
	}
	if err := os.Chdir(r.Root); err != nil {
		return "", "", err
	}

	stdout := &bytes.Buffer{}
	stderr := &bytes.Buffer{}
	cmd := exec.CommandContext(ctx, r.Git, args...)
	cmd.Stdout = stdout
	cmd.Stderr = stderr

	// Depending on the version of Git either the config command in
	// ConfigureFromSpec() will work or one of the below will.
	cmd.Env = append(
		cmd.Env, fmt.Sprintf("GIT_SSH_COMMAND=%s", r.sshCommand))
	cmd.Env = append(
		cmd.Env, fmt.Sprintf("GIT_SSH=%s", r.sshCommand))

	start := time.Now()
	err = cmd.Run()
	duration := time.Since(start)
	outstr := stdout.String()
	errstr := stderr.String()
	logger = logger.WithFields(log.Fields{
		"duration": duration,
		"stdout":   outstr,
		"stderr":   errstr,
	})

	if err != nil {
		logger.Warn()
		defer os.Chdir(cwd)
		return outstr, errstr, err
	}

	logger.Debug()
	return outstr, errstr, os.Chdir(cwd)
}

// Init will create the repository if it does not exist. This function
// does nothing if the path on disk already appears to be a repository.
func (r *Repository) Init() error {
	if _, err := os.Stat(r.Root); os.IsNotExist(err) {
		if err := os.MkdirAll(r.Root, DefaultDirectoryMode); err != nil {
			return err
		}
	}

	r.log.WithFields(log.Fields{
		"action": "init",
		"path":   r.Root,
	}).Debug()
	_, _, err := r.Run([]string{"init", "--quiet", r.Root})
	config := map[string]string{
		"user.name":  "admin",
		"user.email": "admin@localhost",
	}
	if err := r.ConfigureValues(config); err != nil {
		return err
	}

	hook, err := internal.Asset("internal/commit-msg")
	if err != nil {
		return err
	}
	return ioutil.WriteFile(
		filepath.Join(r.Root, ".git", "hooks", "commit-msg"), hook, 0700)
}

// Destroy removes the repository from the local disk.
func (r *Repository) Destroy() error {
	r.log.WithFields(log.Fields{
		"action": "destroy",
		"path":   r.Root,
	}).Debug()
	return os.RemoveAll(r.Root)
}

// Add adds a single file to the repository.
func (r *Repository) Add(input *FileInput) error {
	r.log.WithFields(log.Fields{
		"action": "add",
		"path":   input.Path,
	}).Debug()
	_, _, err := r.Run([]string{"add", input.Path})
	return err
}

// AddFile performs multiple steps in a single command:
//  - Write a file to disk using the given path. The path itself should
//    be relative to the repository root. Any parent paths will be automatically
//    created.
//  - Set the permissions of the file.
//  - Add the file to the git repository.
func (r *Repository) AddFile(input *FileInput) error {
	logger := r.log.WithFields(log.Fields{
		"action": "add-file",
		"path":   input.Path,
	})

	absolute := filepath.Join(r.Root, input.Path)
	logger = logger.WithField("phase", "mkdir")
	if err := os.MkdirAll(filepath.Dir(absolute), DefaultDirectoryMode); err != nil {
		logger.WithError(err).Warn()
		return err
	}

	logger = logger.WithField("phase", "write")
	if err := ioutil.WriteFile(absolute, input.Content, input.GetMode()); err != nil {
		logger.WithError(err).Warn()
		return err
	}
	return r.Add(input)
}

// Commit will commit any pending changes with the provided message.
func (r *Repository) Commit(message string) error {
	r.log.WithFields(log.Fields{
		"action":  "commit",
		"message": message,
	}).Debug()
	_, _, err := r.Run([]string{"commit", "-m", message, "--quiet"})
	return err
}

// Amend amends the current commit without changing the commit message.
func (r *Repository) Amend() error {
	r.log.WithFields(log.Fields{
		"action": "amend",
	}).Debug()
	_, _, err := r.Run(
		[]string{"commit", "--quiet", "--amend", "--no-edit", "--allow-empty"})
	return err
}

// Push will call 'git push HEAD:refs/for/<branch>'. Note, this will
// return an error if you have not already configured the repository. If no
// branch is provided 'master' will be used.
func (r *Repository) Push(branch string) error {
	if branch == "" {
		branch = "master"
	}
	r.log.WithFields(log.Fields{
		"action": "amend",
	}).Debug()
	_, _, err := r.Run(
		[]string{"push", "origin", fmt.Sprintf("HEAD:refs/for/%s", branch)})
	return err
}

// ConfigureValues runs 'git config' using the given map.
func (r *Repository) ConfigureValues(values map[string]string) error {
	logger := r.log.WithFields(log.Fields{
		"action": "config",
	})
	for key, value := range values {
		logger.WithFields(log.Fields{
			"action": "config",
			"key":    key,
			"value":  value,
		}).Debug()
		if _, _, err := r.Run(
			[]string{"config", "--local", key, value}); err != nil {
			logger.WithError(err).Error()
			return err
		}
	}
	return nil
}

// ConfigureFromSpec will configure the git repository to point at
func (r *Repository) ConfigureFromSpec(service *ServiceSpec, project string, branch string) error {
	r.mtx.Lock()
	r.sshCommand = fmt.Sprintf(
		"ssh -o UserKnownHostsFile=/dev/null -o StrictHostKeyChecking=no -i %s",
		service.Admin.PrivateKey)
	config := map[string]string{
		"remote.origin.url": fmt.Sprintf(
			"ssh://%s@%s:%d/%s", service.Admin.Login,
			service.SSH.Address, service.SSH.Public, project),
		"remote.origin.fetch":                   "+refs/heads/*:refs/remotes/origin/*",
		fmt.Sprintf("branch.%s.remote", branch): "origin",
		fmt.Sprintf("branch.%s.merge", branch):  fmt.Sprintf("refs/heads/%s", branch),
		"core.sshCommand":                       r.sshCommand,
	}
	r.mtx.Unlock()
	return r.ConfigureValues(config)
}

// NewRepository creates and returns a *Repository struct. If root is defined
// as "" then a temporary will be created.
func NewRepository(root string) (*Repository, error) {
	git, err := exec.LookPath("git")
	if err != nil {
		return nil, err
	}
	if root == "" {
		path, err := ioutil.TempDir("", "gerrittest-")
		if err != nil {
			return nil, err
		}
		root = path
	}

	repo := &Repository{
		Root: root,
		Git:  git,
		mtx:  &sync.Mutex{},
		log:  log.WithField("cmp", "repo"),
	}
	return repo, repo.Init()
}
<|endoftext|>


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

package state_test

import (
	"fmt"

	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/network"
	"github.com/juju/juju/state"
)

type IPAddressSuite struct {
	ConnSuite
}

var _ = gc.Suite(&IPAddressSuite{})

func (s *IPAddressSuite) assertAddress(
	c *gc.C,
	ipAddr *state.IPAddress,
	addr network.Address,
	ipState state.AddressState,
	machineId, ifaceId, subnetId string,
) {
	c.Assert(ipAddr, gc.NotNil)
	c.Assert(ipAddr.MachineId(), gc.Equals, machineId)
	c.Assert(ipAddr.InterfaceId(), gc.Equals, ifaceId)
	c.Assert(ipAddr.SubnetId(), gc.Equals, subnetId)
	c.Assert(ipAddr.Value(), gc.Equals, addr.Value)
	c.Assert(ipAddr.Type(), gc.Equals, addr.Type)
	c.Assert(ipAddr.Scope(), gc.Equals, addr.Scope)
	c.Assert(ipAddr.State(), gc.Equals, ipState)
	c.Assert(ipAddr.Address(), jc.DeepEquals, addr)
	c.Assert(ipAddr.String(), gc.Equals, addr.String())
}

func (s *IPAddressSuite) TestAddIPAddress(c *gc.C) {
	for i, test := range []string{"0.1.2.3", "2001:db8::1"} {
		c.Logf("test %d: %q", i, test)
		addr := network.NewAddress(test, network.ScopePublic)
		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
		c.Assert(err, jc.ErrorIsNil)
		s.assertAddress(c, ipAddr, addr, state.AddressStateUnknown, "", "", "foobar")
		c.Assert(ipAddr.Life(), gc.Equals, state.Alive)

		// verify the address was stored in the state
		ipAddr, err = s.State.IPAddress(test)
		c.Assert(err, jc.ErrorIsNil)
		s.assertAddress(c, ipAddr, addr, state.AddressStateUnknown, "", "", "foobar")
	}
}

func (s *IPAddressSuite) TestAddIPAddressInvalid(c *gc.C) {
	addr := network.Address{Value: "foo"}
	_, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.Satisfies, errors.IsNotValid)
	c.Assert(err, gc.ErrorMatches, `cannot add IP address "foo": address not valid`)
}

func (s *IPAddressSuite) TestAddIPAddressAlreadyExists(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	_, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)
	_, err = s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
	c.Assert(err, gc.ErrorMatches,
		`cannot add IP address "public:0.1.2.3": address already exists`,
	)
}

func (s *IPAddressSuite) TestIPAddressNotFound(c *gc.C) {
	_, err := s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
	c.Assert(err, gc.ErrorMatches, `IP address "0.1.2.3" not found`)
}

func (s *IPAddressSuite) TestEnsureDeadRemove(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)

	// should not be able to remove an Alive IP address
	c.Assert(ipAddr.Life(), gc.Equals, state.Alive)
	err = ipAddr.Remove()
	msg := fmt.Sprintf("cannot remove IP address %q: IP address is not dead", ipAddr)
	c.Assert(err, gc.ErrorMatches, msg)

	err = ipAddr.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)

	// EnsureDead twice should not be an error
	err = ipAddr.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)

	err = ipAddr.Remove()
	c.Assert(err, jc.ErrorIsNil)

	// Remove twice is also fine.
	err = ipAddr.Remove()
	c.Assert(err, jc.ErrorIsNil)

	_, err = s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
	c.Assert(err, gc.ErrorMatches, `IP address "0.1.2.3" not found`)
}

func (s *IPAddressSuite) TestAddressStateString(c *gc.C) {
	for i, test := range []struct {
		ipState state.AddressState
		expect  string
	}{{
		state.AddressStateUnknown,
		"<unknown>",
	}, {
		state.AddressStateAllocated,
		"allocated",
	}, {
		state.AddressStateUnavailable,
		"unavailable",
	}} {
		c.Logf("test %d: %q -> %q", i, test.ipState, test.expect)
		c.Check(test.ipState.String(), gc.Equals, test.expect)
	}
}

func (s *IPAddressSuite) TestSetState(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)

	for i, test := range []struct {
		initial, changeTo state.AddressState
		err               string
	}{{
		initial:  state.AddressStateUnknown,
		changeTo: state.AddressStateUnknown,
	}, {
		initial:  state.AddressStateUnknown,
		changeTo: state.AddressStateAllocated,
	}, {
		initial:  state.AddressStateUnknown,
		changeTo: state.AddressStateUnavailable,
	}, {
		initial:  state.AddressStateAllocated,
		changeTo: state.AddressStateAllocated,
	}, {
		initial:  state.AddressStateUnavailable,
		changeTo: state.AddressStateUnavailable,
	}, {
		initial:  state.AddressStateAllocated,
		changeTo: state.AddressStateUnknown,
		err: `cannot set IP address "public:0.1.2.3" to state "<unknown>": ` +
			`transition from "allocated" not valid`,
	}, {
		initial:  state.AddressStateUnavailable,
		changeTo: state.AddressStateUnknown,
		err: `cannot set IP address "public:0.1.2.3" to state "<unknown>": ` +
			`transition from "unavailable" not valid`,
	}, {
		initial:  state.AddressStateAllocated,
		changeTo: state.AddressStateUnavailable,
		err: `cannot set IP address "public:0.1.2.3" to state "unavailable": ` +
			`transition from "allocated" not valid`,
	}, {
		initial:  state.AddressStateUnavailable,
		changeTo: state.AddressStateAllocated,
		err: `cannot set IP address "public:0.1.2.3" to state "allocated": ` +
			`transition from "unavailable" not valid`,
	}} {
		c.Logf("test %d: %q -> %q ok:%v", i, test.initial, test.changeTo, test.err == "")
		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
		c.Check(err, jc.ErrorIsNil)

		// Initially, all addresses have AddressStateUnknown.
		c.Assert(ipAddr.State(), gc.Equals, state.AddressStateUnknown)

		if test.initial != state.AddressStateUnknown {
			err = ipAddr.SetState(test.initial)
			c.Check(err, jc.ErrorIsNil)
		}
		err = ipAddr.SetState(test.changeTo)
		if test.err != "" {
			c.Check(err, gc.ErrorMatches, test.err)
			c.Check(err, jc.Satisfies, errors.IsNotValid)
			c.Check(ipAddr.EnsureDead(), jc.ErrorIsNil)
			c.Check(ipAddr.Remove(), jc.ErrorIsNil)
			continue
		}
		c.Check(err, jc.ErrorIsNil)
		c.Check(ipAddr.State(), gc.Equals, test.changeTo)
		c.Check(ipAddr.EnsureDead(), jc.ErrorIsNil)
		c.Check(ipAddr.Remove(), jc.ErrorIsNil)
	}
}

func (s *IPAddressSuite) TestAllocateTo(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(ipAddr.State(), gc.Equals, state.AddressStateUnknown)
	c.Assert(ipAddr.MachineId(), gc.Equals, "")
	c.Assert(ipAddr.InterfaceId(), gc.Equals, "")

	err = ipAddr.AllocateTo("wibble", "wobble")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(ipAddr.State(), gc.Equals, state.AddressStateAllocated)
	c.Assert(ipAddr.MachineId(), gc.Equals, "wibble")
	c.Assert(ipAddr.InterfaceId(), gc.Equals, "wobble")

	freshCopy, err := s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(freshCopy.State(), gc.Equals, state.AddressStateAllocated)
	c.Assert(freshCopy.MachineId(), gc.Equals, "wibble")
	c.Assert(freshCopy.InterfaceId(), gc.Equals, "wobble")

	// allocating twice should fail.
	err = ipAddr.AllocateTo("m", "i")
	c.Assert(err, gc.ErrorMatches,
		`cannot allocate IP address "public:0.1.2.3" to machine "m", interface "i": `+
			`already allocated or unavailable`,
	)
}

func (s *IPAddressSuite) TestAddress(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(ipAddr.Address(), jc.DeepEquals, addr)

}

func (s *IPAddressSuite) TestAllocatedIPAddresses(c *gc.C) {
	addresses := [][]string{
		{"0.1.2.3", "wibble"},
		{"0.1.2.4", "wibble"},
		{"0.1.2.5", "wobble"},
	}
	for _, details := range addresses {
		addr := network.NewAddress(details[0], network.ScopePublic)
		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
		c.Assert(err, jc.ErrorIsNil)
		err = ipAddr.AllocateTo(details[1], "wobble")
		c.Assert(err, jc.ErrorIsNil)
	}
	result, err := s.State.AllocatedIPAddresses("wibble")
	c.Assert(err, jc.ErrorIsNil)
	addr1, err := s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.ErrorIsNil)
	addr2, err := s.State.IPAddress("0.1.2.4")
	c.Assert(err, jc.ErrorIsNil)
	expected := []*state.IPAddress{addr1, addr2}
	c.Assert(result, jc.SameContents, expected)

}
<commit_msg>Comment update from review<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package state_test

import (
	"fmt"

	"github.com/juju/errors"
	jc "github.com/juju/testing/checkers"
	gc "gopkg.in/check.v1"

	"github.com/juju/juju/network"
	"github.com/juju/juju/state"
)

type IPAddressSuite struct {
	ConnSuite
}

var _ = gc.Suite(&IPAddressSuite{})

func (s *IPAddressSuite) assertAddress(
	c *gc.C,
	ipAddr *state.IPAddress,
	addr network.Address,
	ipState state.AddressState,
	machineId, ifaceId, subnetId string,
) {
	c.Assert(ipAddr, gc.NotNil)
	c.Assert(ipAddr.MachineId(), gc.Equals, machineId)
	c.Assert(ipAddr.InterfaceId(), gc.Equals, ifaceId)
	c.Assert(ipAddr.SubnetId(), gc.Equals, subnetId)
	c.Assert(ipAddr.Value(), gc.Equals, addr.Value)
	c.Assert(ipAddr.Type(), gc.Equals, addr.Type)
	c.Assert(ipAddr.Scope(), gc.Equals, addr.Scope)
	c.Assert(ipAddr.State(), gc.Equals, ipState)
	c.Assert(ipAddr.Address(), jc.DeepEquals, addr)
	c.Assert(ipAddr.String(), gc.Equals, addr.String())
}

func (s *IPAddressSuite) TestAddIPAddress(c *gc.C) {
	for i, test := range []string{"0.1.2.3", "2001:db8::1"} {
		c.Logf("test %d: %q", i, test)
		addr := network.NewAddress(test, network.ScopePublic)
		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
		c.Assert(err, jc.ErrorIsNil)
		s.assertAddress(c, ipAddr, addr, state.AddressStateUnknown, "", "", "foobar")
		c.Assert(ipAddr.Life(), gc.Equals, state.Alive)

		// verify the address was stored in the state
		ipAddr, err = s.State.IPAddress(test)
		c.Assert(err, jc.ErrorIsNil)
		s.assertAddress(c, ipAddr, addr, state.AddressStateUnknown, "", "", "foobar")
	}
}

func (s *IPAddressSuite) TestAddIPAddressInvalid(c *gc.C) {
	addr := network.Address{Value: "foo"}
	_, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.Satisfies, errors.IsNotValid)
	c.Assert(err, gc.ErrorMatches, `cannot add IP address "foo": address not valid`)
}

func (s *IPAddressSuite) TestAddIPAddressAlreadyExists(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	_, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)
	_, err = s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.Satisfies, errors.IsAlreadyExists)
	c.Assert(err, gc.ErrorMatches,
		`cannot add IP address "public:0.1.2.3": address already exists`,
	)
}

func (s *IPAddressSuite) TestIPAddressNotFound(c *gc.C) {
	_, err := s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
	c.Assert(err, gc.ErrorMatches, `IP address "0.1.2.3" not found`)
}

func (s *IPAddressSuite) TestEnsureDeadRemove(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)

	// Should not be able to remove an Alive IP address.
	c.Assert(ipAddr.Life(), gc.Equals, state.Alive)
	err = ipAddr.Remove()
	msg := fmt.Sprintf("cannot remove IP address %q: IP address is not dead", ipAddr)
	c.Assert(err, gc.ErrorMatches, msg)

	err = ipAddr.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)

	// EnsureDead twice should not be an error
	err = ipAddr.EnsureDead()
	c.Assert(err, jc.ErrorIsNil)

	err = ipAddr.Remove()
	c.Assert(err, jc.ErrorIsNil)

	// Remove twice is also fine.
	err = ipAddr.Remove()
	c.Assert(err, jc.ErrorIsNil)

	_, err = s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.Satisfies, errors.IsNotFound)
	c.Assert(err, gc.ErrorMatches, `IP address "0.1.2.3" not found`)
}

func (s *IPAddressSuite) TestAddressStateString(c *gc.C) {
	for i, test := range []struct {
		ipState state.AddressState
		expect  string
	}{{
		state.AddressStateUnknown,
		"<unknown>",
	}, {
		state.AddressStateAllocated,
		"allocated",
	}, {
		state.AddressStateUnavailable,
		"unavailable",
	}} {
		c.Logf("test %d: %q -> %q", i, test.ipState, test.expect)
		c.Check(test.ipState.String(), gc.Equals, test.expect)
	}
}

func (s *IPAddressSuite) TestSetState(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)

	for i, test := range []struct {
		initial, changeTo state.AddressState
		err               string
	}{{
		initial:  state.AddressStateUnknown,
		changeTo: state.AddressStateUnknown,
	}, {
		initial:  state.AddressStateUnknown,
		changeTo: state.AddressStateAllocated,
	}, {
		initial:  state.AddressStateUnknown,
		changeTo: state.AddressStateUnavailable,
	}, {
		initial:  state.AddressStateAllocated,
		changeTo: state.AddressStateAllocated,
	}, {
		initial:  state.AddressStateUnavailable,
		changeTo: state.AddressStateUnavailable,
	}, {
		initial:  state.AddressStateAllocated,
		changeTo: state.AddressStateUnknown,
		err: `cannot set IP address "public:0.1.2.3" to state "<unknown>": ` +
			`transition from "allocated" not valid`,
	}, {
		initial:  state.AddressStateUnavailable,
		changeTo: state.AddressStateUnknown,
		err: `cannot set IP address "public:0.1.2.3" to state "<unknown>": ` +
			`transition from "unavailable" not valid`,
	}, {
		initial:  state.AddressStateAllocated,
		changeTo: state.AddressStateUnavailable,
		err: `cannot set IP address "public:0.1.2.3" to state "unavailable": ` +
			`transition from "allocated" not valid`,
	}, {
		initial:  state.AddressStateUnavailable,
		changeTo: state.AddressStateAllocated,
		err: `cannot set IP address "public:0.1.2.3" to state "allocated": ` +
			`transition from "unavailable" not valid`,
	}} {
		c.Logf("test %d: %q -> %q ok:%v", i, test.initial, test.changeTo, test.err == "")
		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
		c.Check(err, jc.ErrorIsNil)

		// Initially, all addresses have AddressStateUnknown.
		c.Assert(ipAddr.State(), gc.Equals, state.AddressStateUnknown)

		if test.initial != state.AddressStateUnknown {
			err = ipAddr.SetState(test.initial)
			c.Check(err, jc.ErrorIsNil)
		}
		err = ipAddr.SetState(test.changeTo)
		if test.err != "" {
			c.Check(err, gc.ErrorMatches, test.err)
			c.Check(err, jc.Satisfies, errors.IsNotValid)
			c.Check(ipAddr.EnsureDead(), jc.ErrorIsNil)
			c.Check(ipAddr.Remove(), jc.ErrorIsNil)
			continue
		}
		c.Check(err, jc.ErrorIsNil)
		c.Check(ipAddr.State(), gc.Equals, test.changeTo)
		c.Check(ipAddr.EnsureDead(), jc.ErrorIsNil)
		c.Check(ipAddr.Remove(), jc.ErrorIsNil)
	}
}

func (s *IPAddressSuite) TestAllocateTo(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(ipAddr.State(), gc.Equals, state.AddressStateUnknown)
	c.Assert(ipAddr.MachineId(), gc.Equals, "")
	c.Assert(ipAddr.InterfaceId(), gc.Equals, "")

	err = ipAddr.AllocateTo("wibble", "wobble")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(ipAddr.State(), gc.Equals, state.AddressStateAllocated)
	c.Assert(ipAddr.MachineId(), gc.Equals, "wibble")
	c.Assert(ipAddr.InterfaceId(), gc.Equals, "wobble")

	freshCopy, err := s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(freshCopy.State(), gc.Equals, state.AddressStateAllocated)
	c.Assert(freshCopy.MachineId(), gc.Equals, "wibble")
	c.Assert(freshCopy.InterfaceId(), gc.Equals, "wobble")

	// allocating twice should fail.
	err = ipAddr.AllocateTo("m", "i")
	c.Assert(err, gc.ErrorMatches,
		`cannot allocate IP address "public:0.1.2.3" to machine "m", interface "i": `+
			`already allocated or unavailable`,
	)
}

func (s *IPAddressSuite) TestAddress(c *gc.C) {
	addr := network.NewAddress("0.1.2.3", network.ScopePublic)
	ipAddr, err := s.State.AddIPAddress(addr, "foobar")
	c.Assert(err, jc.ErrorIsNil)
	c.Assert(ipAddr.Address(), jc.DeepEquals, addr)

}

func (s *IPAddressSuite) TestAllocatedIPAddresses(c *gc.C) {
	addresses := [][]string{
		{"0.1.2.3", "wibble"},
		{"0.1.2.4", "wibble"},
		{"0.1.2.5", "wobble"},
	}
	for _, details := range addresses {
		addr := network.NewAddress(details[0], network.ScopePublic)
		ipAddr, err := s.State.AddIPAddress(addr, "foobar")
		c.Assert(err, jc.ErrorIsNil)
		err = ipAddr.AllocateTo(details[1], "wobble")
		c.Assert(err, jc.ErrorIsNil)
	}
	result, err := s.State.AllocatedIPAddresses("wibble")
	c.Assert(err, jc.ErrorIsNil)
	addr1, err := s.State.IPAddress("0.1.2.3")
	c.Assert(err, jc.ErrorIsNil)
	addr2, err := s.State.IPAddress("0.1.2.4")
	c.Assert(err, jc.ErrorIsNil)
	expected := []*state.IPAddress{addr1, addr2}
	c.Assert(result, jc.SameContents, expected)

}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	krpty "github.com/kr/pty"
	"os"
	"os/exec"
	"time"
)

func KissAttach(port string, mtu int) (pty *os.File, err error) {
	pty, tty, err := krpty.Open()
	if err != nil {
		return nil, err
	}

	time.Sleep(100 * time.Millisecond)

	args := []string{tty.Name(), port, "-l"}
	if mtu > 0 {
		args = append(args, fmt.Sprintf("-m %d", mtu))
	}

	c := exec.Command("kissattach", args...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr

	err = c.Start()
	if err != nil {
		pty.Close()
		return nil, err
	}

	// Wait for kissattach to open tty
	time.Sleep(2 * time.Second)

	err = tty.Close()
	return pty, err
}
<commit_msg>Wait for kissattach to daemonize instead of sleeping. This also cleans up the reaped process (defunct).<commit_after>package main

import (
	"fmt"
	krpty "github.com/kr/pty"
	"os"
	"os/exec"
)

func KissAttach(port string, mtu int) (pty *os.File, err error) {
	pty, tty, err := krpty.Open()
	if err != nil {
		return nil, err
	}

	args := []string{tty.Name(), port, "-l"}
	if mtu > 0 {
		args = append(args, fmt.Sprintf("-m %d", mtu))
	}

	c := exec.Command("kissattach", args...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stderr

	err = c.Start()
	if err != nil {
		pty.Close()
		return nil, err
	}

	// Wait for kissattach to daemonize
	if err = c.Wait(); err != nil {
		pty.Close()
		return nil, err
	}

	return pty, tty.Close()
}
<|endoftext|>


<commit_before>package index

import (
	"appengine"

	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"sort"
	"time"

	"firego"
)

type Client struct {
	ReqId   string `json:"reqId"`
	ReqTime int64  `json:"reqTime"`
	Comment string `json:"comment"`
}

type Delete struct {
	ReqId string `json:"reqId"`
}

type ClientArray []Client

func (s ClientArray) Len() int {
	return len(s)
}
func (s ClientArray) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s ClientArray) Less(i, j int) bool {
	return s[i].ReqTime >= s[j].ReqTime
}

func init() {
	http.HandleFunc("/insert", handleInsert)
	http.HandleFunc("/remove", handleRemove)
	http.HandleFunc("/delete", handleDelete)
	http.HandleFunc("/list", handleList)
	http.HandleFunc("/table", handleTable)
}

func status(w http.ResponseWriter, reqId string, status int) {
	if reqId == "" {
		reqId = "not provided"
	}
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status":%d, "reqId" : "%s"}`, status, reqId)
}

func statusNoMessage(w http.ResponseWriter, status int) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status":%d}`, status)
}

func response(w http.ResponseWriter, reqId string, clients []Client) {
	if reqId == "" {
		reqId = "not provided"
	}
	json, _ := json.Marshal(clients)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status":200, "reqId" : "%s", "result" : %s}`, reqId, string(json))
}

func loadClients(r *http.Request, ch chan ClientArray) {
	f := firego.NewGAE(appengine.NewContext(r), DB)
	f.Auth(AUTH)
	var values map[string]Client
	if e := f.Value(&values); e == nil {
		list := make(ClientArray, 0)
		for _, v := range values {
			list = append(list, v)
		}
		sort.Sort(list)
		ch <- list
	} else {
		ch <- nil
	}
}

func showList(w http.ResponseWriter, r *http.Request, reqId string) {
	ch := make(chan ClientArray)
	go loadClients(r, ch)
	list := <-ch
	if list != nil {
		response(w, reqId, list)
	} else {
		status(w, reqId, 500)
	}
}

func handleInsert(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		if e := json.Unmarshal(bys, &client); e == nil {
			f := firego.NewGAE(appengine.NewContext(r), DB)
			f.Auth(AUTH)
			childFB := f.Child(client.ReqId)
			if e := childFB.Set(client); e == nil {
				if _, e := f.Push(nil); e == nil { //Like java: fb.child("xxxx").setValue(obj).push()
					status(w, client.ReqId, 200)
				} else {
					s := fmt.Sprintf("%v", e)
					status(w, s, 500)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		} else {
			s := fmt.Sprintf("%v", e)
			status(w, s, 500)
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleList(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bytes, e := ioutil.ReadAll(r.Body); e == nil {
		if string(bytes) == "" {
			showList(w, r, "reqId is empty, it might be sent from desktop.")
		} else {
			if e := json.Unmarshal(bytes, &client); e == nil {
				showList(w, r, client.ReqId)
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleRemove(w http.ResponseWriter, r *http.Request) {
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		clientID := string(bys)
		if clientID == "" {
			status(w, "Please give client-ID on post-body.", 500)
		} else {
			var whichItemToRemove string = clientID
			//Delete client which the key(from DB).
			if whichItemToRemove != "" {
				f := firego.NewGAE(appengine.NewContext(r), DB)
				f.Auth(AUTH)
				if err := f.Child(whichItemToRemove).Remove(); err != nil {
					status(w, fmt.Sprintf("%v", err), 500)
				} else {
					status(w, whichItemToRemove, 200)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleDelete(w http.ResponseWriter, r *http.Request) {
	del := Delete{}
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		if e := json.Unmarshal(bys, &del); e == nil {
			//Delete client which the key(from DB).
			if del.ReqId != "" {
				f := firego.NewGAE(appengine.NewContext(r), DB)
				f.Auth(AUTH)
				if err := f.Child(del.ReqId).Remove(); err != nil {
					status(w, fmt.Sprintf("%v", err), 500)
				} else {
					status(w, del.ReqId, 200)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		} else {
			s := fmt.Sprintf("%v", e)
			status(w, s, 500)
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

var tableTep = template.Must(template.ParseFiles("table.html"))

func (c Client) RequestTime() (datetime string) {
	tz := "CET"
	location, _ := time.LoadLocation(tz)
	datetime = time.Unix(c.ReqTime/1000, 0).In(location).Format("Mon Jan _2 15:04:05 2006")
	return
}

func showTable(w http.ResponseWriter, r *http.Request, reqId string) {
	ch := make(chan ClientArray)
	go loadClients(r, ch)
	list := <-ch
	if list != nil {
		tableTep.Execute(w, list)
	} else {
		status(w, reqId, 500)
	}
}

func handleTable(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bytes, e := ioutil.ReadAll(r.Body); e == nil {
		if string(bytes) == "" {
			showTable(w, r, "reqId is empty, it might be sent from desktop.")
		} else {
			if e := json.Unmarshal(bytes, &client); e == nil {
				showTable(w, r, client.ReqId)
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}
<commit_msg>Add update<commit_after>package index

import (
	"appengine"

	"encoding/json"
	"fmt"
	"html/template"
	"io/ioutil"
	"net/http"
	"sort"
	"time"

	"firego"
)

type Client struct {
	ReqId   string `json:"reqId"`
	ReqTime int64  `json:"reqTime"`
	Comment string `json:"comment"`
}

type Delete struct {
	ReqId string `json:"reqId"`
}

type ClientArray []Client

func (s ClientArray) Len() int {
	return len(s)
}
func (s ClientArray) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}
func (s ClientArray) Less(i, j int) bool {
	return s[i].ReqTime >= s[j].ReqTime
}

func init() {
	http.HandleFunc("/insert", handleInsert)
	http.HandleFunc("/remove", handleRemove)
	http.HandleFunc("/delete", handleDelete)
	http.HandleFunc("/update", handleUpdate)
	http.HandleFunc("/list", handleList)
	http.HandleFunc("/table", handleTable)
}

func status(w http.ResponseWriter, reqId string, status int) {
	if reqId == "" {
		reqId = "not provided"
	}
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status":%d, "reqId" : "%s"}`, status, reqId)
}

func statusNoMessage(w http.ResponseWriter, status int) {
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status":%d}`, status)
}

func response(w http.ResponseWriter, reqId string, clients []Client) {
	if reqId == "" {
		reqId = "not provided"
	}
	json, _ := json.Marshal(clients)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, `{"status":200, "reqId" : "%s", "result" : %s}`, reqId, string(json))
}

func loadClients(r *http.Request, ch chan ClientArray) {
	f := firego.NewGAE(appengine.NewContext(r), DB)
	f.Auth(AUTH)
	var values map[string]Client
	if e := f.Value(&values); e == nil {
		list := make(ClientArray, 0)
		for _, v := range values {
			list = append(list, v)
		}
		sort.Sort(list)
		ch <- list
	} else {
		ch <- nil
	}
}

func showList(w http.ResponseWriter, r *http.Request, reqId string) {
	ch := make(chan ClientArray)
	go loadClients(r, ch)
	list := <-ch
	if list != nil {
		response(w, reqId, list)
	} else {
		status(w, reqId, 500)
	}
}

func handleInsert(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		if e := json.Unmarshal(bys, &client); e == nil {
			f := firego.NewGAE(appengine.NewContext(r), DB)
			f.Auth(AUTH)
			childFB := f.Child(client.ReqId)
			if e := childFB.Set(client); e == nil {
				if _, e := f.Push(nil); e == nil { //Like java: fb.child("xxxx").setValue(obj).push()
					status(w, client.ReqId, 200)
				} else {
					s := fmt.Sprintf("%v", e)
					status(w, s, 500)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		} else {
			s := fmt.Sprintf("%v", e)
			status(w, s, 500)
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleList(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bytes, e := ioutil.ReadAll(r.Body); e == nil {
		if string(bytes) == "" {
			showList(w, r, "reqId is empty, it might be sent from desktop.")
		} else {
			if e := json.Unmarshal(bytes, &client); e == nil {
				showList(w, r, client.ReqId)
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleRemove(w http.ResponseWriter, r *http.Request) {
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		clientID := string(bys)
		if clientID == "" {
			status(w, "Please give client-ID on post-body.", 500)
		} else {
			var whichItemToRemove string = clientID
			//Delete client which the key(from DB).
			if whichItemToRemove != "" {
				f := firego.NewGAE(appengine.NewContext(r), DB)
				f.Auth(AUTH)
				if err := f.Child(whichItemToRemove).Remove(); err != nil {
					status(w, fmt.Sprintf("%v", err), 500)
				} else {
					status(w, whichItemToRemove, 200)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleDelete(w http.ResponseWriter, r *http.Request) {
	del := Delete{}
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		if e := json.Unmarshal(bys, &del); e == nil {
			//Delete client which the key(from DB).
			if del.ReqId != "" {
				f := firego.NewGAE(appengine.NewContext(r), DB)
				f.Auth(AUTH)
				if err := f.Child(del.ReqId).Remove(); err != nil {
					status(w, fmt.Sprintf("%v", err), 500)
				} else {
					status(w, del.ReqId, 200)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		} else {
			s := fmt.Sprintf("%v", e)
			status(w, s, 500)
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

func handleUpdate(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bys, e := ioutil.ReadAll(r.Body); e == nil {
		if e := json.Unmarshal(bys, &client); e == nil {
			//Delete client which the key(from DB).
			if client.ReqId != "" {
				f := firego.NewGAE(appengine.NewContext(r), DB)
				f.Auth(AUTH)
				childFB := f.Child(client.ReqId)
				if e := childFB.Set(client); e != nil {
					status(w, fmt.Sprintf("%v", e), 500)
				} else {
					status(w, client.ReqId, 200)
				}
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		} else {
			s := fmt.Sprintf("%v", e)
			status(w, s, 500)
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}

var tableTep = template.Must(template.ParseFiles("table.html"))

func (c Client) RequestTime() (datetime string) {
	tz := "CET"
	location, _ := time.LoadLocation(tz)
	datetime = time.Unix(c.ReqTime/1000, 0).In(location).Format("Mon Jan _2 15:04:05 2006")
	return
}

func showTable(w http.ResponseWriter, r *http.Request, reqId string) {
	ch := make(chan ClientArray)
	go loadClients(r, ch)
	list := <-ch
	if list != nil {
		tableTep.Execute(w, list)
	} else {
		status(w, reqId, 500)
	}
}

func handleTable(w http.ResponseWriter, r *http.Request) {
	client := Client{}
	if bytes, e := ioutil.ReadAll(r.Body); e == nil {
		if string(bytes) == "" {
			showTable(w, r, "reqId is empty, it might be sent from desktop.")
		} else {
			if e := json.Unmarshal(bytes, &client); e == nil {
				showTable(w, r, client.ReqId)
			} else {
				s := fmt.Sprintf("%v", e)
				status(w, s, 500)
			}
		}
	} else {
		s := fmt.Sprintf("%v", e)
		status(w, s, 500)
	}
}
<|endoftext|>