<commit_before>// Copyright 2011 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.

// Package build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree.  The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code.  The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path.  That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
//	GOPATH=/home/user/gocode
//
//	/home/user/gocode/
//	    src/
//	        foo/
//	            bar/               (go code in package bar)
//	                x.go
//	            quux/              (go code in package main)
//	                y.go
//	    bin/
//	        quux                   (installed command)
//	    pkg/
//	        linux_amd64/
//	            foo/
//	                bar.a          (installed package object)
//
// Build Constraints
//
// A build constraint is a line comment beginning with the directive +build
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
//	// +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
//	(linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
//	// +build linux darwin
//	// +build 386
//
// corresponds to the boolean formula:
//
//	(linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
//	- the target operating system, as spelled by runtime.GOOS
//	- the target architecture, as spelled by runtime.GOARCH
//	- the compiler being used, either "gc" or "gccgo"
//	- "cgo", if ctxt.CgoEnabled is true
//	- "go1.1", from Go version 1.1 onward
//	- any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches *_GOOS, *_GOARCH, or *_GOOS_GOARCH for any known operating
// system and architecture values, then the file is considered to have an implicit
// build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
//	// +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
//	// +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
//	// +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
<commit_msg>go/build: document GOOS.go also has implicit GOOS build constraint<commit_after>// Copyright 2011 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.

// Package build gathers information about Go packages.
//
// Go Path
//
// The Go path is a list of directory trees containing Go source code.
// It is consulted to resolve imports that cannot be found in the standard
// Go tree.  The default path is the value of the GOPATH environment
// variable, interpreted as a path list appropriate to the operating system
// (on Unix, the variable is a colon-separated string;
// on Windows, a semicolon-separated string;
// on Plan 9, a list).
//
// Each directory listed in the Go path must have a prescribed structure:
//
// The src/ directory holds source code.  The path below 'src' determines
// the import path or executable name.
//
// The pkg/ directory holds installed package objects.
// As in the Go tree, each target operating system and
// architecture pair has its own subdirectory of pkg
// (pkg/GOOS_GOARCH).
//
// If DIR is a directory listed in the Go path, a package with
// source in DIR/src/foo/bar can be imported as "foo/bar" and
// has its compiled form installed to "DIR/pkg/GOOS_GOARCH/foo/bar.a"
// (or, for gccgo, "DIR/pkg/gccgo/foo/libbar.a").
//
// The bin/ directory holds compiled commands.
// Each command is named for its source directory, but only
// using the final element, not the entire path.  That is, the
// command with source in DIR/src/foo/quux is installed into
// DIR/bin/quux, not DIR/bin/foo/quux.  The foo/ is stripped
// so that you can add DIR/bin to your PATH to get at the
// installed commands.
//
// Here's an example directory layout:
//
//	GOPATH=/home/user/gocode
//
//	/home/user/gocode/
//	    src/
//	        foo/
//	            bar/               (go code in package bar)
//	                x.go
//	            quux/              (go code in package main)
//	                y.go
//	    bin/
//	        quux                   (installed command)
//	    pkg/
//	        linux_amd64/
//	            foo/
//	                bar.a          (installed package object)
//
// Build Constraints
//
// A build constraint is a line comment beginning with the directive +build
// that lists the conditions under which a file should be included in the package.
// Constraints may appear in any kind of source file (not just Go), but
// they must appear near the top of the file, preceded
// only by blank lines and other line comments.
//
// To distinguish build constraints from package documentation, a series of
// build constraints must be followed by a blank line.
//
// A build constraint is evaluated as the OR of space-separated options;
// each option evaluates as the AND of its comma-separated terms;
// and each term is an alphanumeric word or, preceded by !, its negation.
// That is, the build constraint:
//
//	// +build linux,386 darwin,!cgo
//
// corresponds to the boolean formula:
//
//	(linux AND 386) OR (darwin AND (NOT cgo))
//
// A file may have multiple build constraints. The overall constraint is the AND
// of the individual constraints. That is, the build constraints:
//
//	// +build linux darwin
//	// +build 386
//
// corresponds to the boolean formula:
//
//	(linux OR darwin) AND 386
//
// During a particular build, the following words are satisfied:
//
//	- the target operating system, as spelled by runtime.GOOS
//	- the target architecture, as spelled by runtime.GOARCH
//	- the compiler being used, either "gc" or "gccgo"
//	- "cgo", if ctxt.CgoEnabled is true
//	- "go1.1", from Go version 1.1 onward
//	- any additional words listed in ctxt.BuildTags
//
// If a file's name, after stripping the extension and a possible _test suffix,
// matches any of the following patterns:
//	*_GOOS
// 	*_GOARCH
// 	*_GOOS_GOARCH
// (example: source_windows_amd64.go) or the literals:
//	GOOS
// 	GOARCH
// (example: windows.go) where GOOS and GOARCH represent any known operating
// system and architecture values respectively, then the file is considered to
// have an implicit build constraint requiring those terms.
//
// To keep a file from being considered for the build:
//
//	// +build ignore
//
// (any other unsatisfied word will work as well, but ``ignore'' is conventional.)
//
// To build a file only when using cgo, and only on Linux and OS X:
//
//	// +build linux,cgo darwin,cgo
//
// Such a file is usually paired with another file implementing the
// default functionality for other systems, which in this case would
// carry the constraint:
//
//	// +build !linux,!darwin !cgo
//
// Naming a file dns_windows.go will cause it to be included only when
// building the package for Windows; similarly, math_386.s will be included
// only when building the package for 32-bit x86.
//
package build
<|endoftext|>


<commit_before>// Copyright 2010 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.

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io/ioutil"
	"os"
	"testing"
)

const (
	testFile       = "testdata/file"
	testFileLength = 11
)

var ServeFileRangeTests = []struct {
	start, end int
	r          string
	code       int
}{
	{0, testFileLength, "", StatusOK},
	{0, 5, "0-4", StatusPartialContent},
	{2, testFileLength, "2-", StatusPartialContent},
	{testFileLength - 5, testFileLength, "-5", StatusPartialContent},
	{3, 8, "3-7", StatusPartialContent},
	{0, 0, "20-", StatusRequestedRangeNotSatisfiable},
}

func TestServeFile(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()

	var err os.Error

	file, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatal("reading file:", err)
	}

	// set up the Request (re-used for all tests)
	var req Request
	req.Header = make(Header)
	if req.URL, err = ParseURL(ts.URL); err != nil {
		t.Fatal("ParseURL:", err)
	}
	req.Method = "GET"

	// straight GET
	_, body := getBody(t, req)
	if !equal(body, file) {
		t.Fatalf("body mismatch: got %q, want %q", body, file)
	}

	// Range tests
	for _, rt := range ServeFileRangeTests {
		req.Header.Set("Range", "bytes="+rt.r)
		if rt.r == "" {
			req.Header["Range"] = nil
		}
		r, body := getBody(t, req)
		if r.StatusCode != rt.code {
			t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
		}
		if rt.code == StatusRequestedRangeNotSatisfiable {
			continue
		}
		h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
		if rt.r == "" {
			h = ""
		}
		cr := r.Header.Get("Content-Range")
		if cr != h {
			t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
		}
		if !equal(body, file[rt.start:rt.end]) {
			t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
		}
	}
}

type testFileSystem struct {
	open func(name string) (File, os.Error)
}

func (fs *testFileSystem) Open(name string) (File, os.Error) {
	return fs.open(name)
}

func TestFileServerCleans(t *testing.T) {
	ch := make(chan string, 1)
	fs := FileServer(&testFileSystem{func(name string) (File, os.Error) {
		ch <- name
		return nil, os.ENOENT
	}})
	tests := []struct {
		reqPath, openArg string
	}{
		{"/foo.txt", "/foo.txt"},
		{"//foo.txt", "/foo.txt"},
		{"/../foo.txt", "/foo.txt"},
	}
	req, _ := NewRequest("GET", "http://example.com", nil)
	for n, test := range tests {
		rec := httptest.NewRecorder()
		req.URL.Path = test.reqPath
		fs.ServeHTTP(rec, req)
		if got := <-ch; got != test.openArg {
			t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
		}
	}
}

func TestDirJoin(t *testing.T) {
	wfi, err := os.Stat("/etc/hosts")
	if err != nil {
		t.Logf("skipping test; no /etc/hosts file")
		return
	}
	test := func(d Dir, name string) {
		f, err := d.Open(name)
		if err != nil {
			t.Fatalf("open of %s: %v", name, err)
		}
		defer f.Close()
		gfi, err := f.Stat()
		if err != nil {
			t.Fatalf("stat of %s: %v", err)
		}
		if gfi.Ino != wfi.Ino {
			t.Errorf("%s got different inode")
		}
	}
	test(Dir("/etc/"), "/hosts")
	test(Dir("/etc/"), "hosts")
	test(Dir("/etc/"), "../../../../hosts")
	test(Dir("/etc"), "/hosts")
	test(Dir("/etc"), "hosts")
	test(Dir("/etc"), "../../../../hosts")

	// Not really directories, but since we use this trick in
	// ServeFile, test it:
	test(Dir("/etc/hosts"), "")
	test(Dir("/etc/hosts"), "/")
	test(Dir("/etc/hosts"), "../")
}

func TestServeFileContentType(t *testing.T) {
	const ctype = "icecream/chocolate"
	override := false
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		if override {
			w.Header().Set("Content-Type", ctype)
		}
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	get := func(want string) {
		resp, err := Get(ts.URL)
		if err != nil {
			t.Fatal(err)
		}
		if h := resp.Header.Get("Content-Type"); h != want {
			t.Errorf("Content-Type mismatch: got %d, want %d", h, want)
		}
	}
	get("text/plain; charset=utf-8")
	override = true
	get(ctype)
}

func TestServeFileMimeType(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/style.css")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	want := "text/css"
	if h := resp.Header.Get("Content-Type"); h != want {
		t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
	}
}

func TestServeFileWithContentEncoding(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		w.Header().Set("Content-Encoding", "foo")
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if g, e := resp.ContentLength, int64(-1); g != e {
		t.Errorf("Content-Length mismatch: got %q, want %q", g, e)
	}
}

func getBody(t *testing.T, req Request) (*Response, []byte) {
	r, err := DefaultClient.Do(&req)
	if err != nil {
		t.Fatal(req.URL.String(), "send:", err)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal("reading Body:", err)
	}
	return r, b
}

func equal(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
<commit_msg>fix build from e904b6784768 breakage<commit_after>// Copyright 2010 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.

package http_test

import (
	"fmt"
	. "http"
	"http/httptest"
	"io/ioutil"
	"os"
	"testing"
)

const (
	testFile       = "testdata/file"
	testFileLength = 11
)

var ServeFileRangeTests = []struct {
	start, end int
	r          string
	code       int
}{
	{0, testFileLength, "", StatusOK},
	{0, 5, "0-4", StatusPartialContent},
	{2, testFileLength, "2-", StatusPartialContent},
	{testFileLength - 5, testFileLength, "-5", StatusPartialContent},
	{3, 8, "3-7", StatusPartialContent},
	{0, 0, "20-", StatusRequestedRangeNotSatisfiable},
}

func TestServeFile(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()

	var err os.Error

	file, err := ioutil.ReadFile(testFile)
	if err != nil {
		t.Fatal("reading file:", err)
	}

	// set up the Request (re-used for all tests)
	var req Request
	req.Header = make(Header)
	if req.URL, err = ParseURL(ts.URL); err != nil {
		t.Fatal("ParseURL:", err)
	}
	req.Method = "GET"

	// straight GET
	_, body := getBody(t, req)
	if !equal(body, file) {
		t.Fatalf("body mismatch: got %q, want %q", body, file)
	}

	// Range tests
	for _, rt := range ServeFileRangeTests {
		req.Header.Set("Range", "bytes="+rt.r)
		if rt.r == "" {
			req.Header["Range"] = nil
		}
		r, body := getBody(t, req)
		if r.StatusCode != rt.code {
			t.Errorf("range=%q: StatusCode=%d, want %d", rt.r, r.StatusCode, rt.code)
		}
		if rt.code == StatusRequestedRangeNotSatisfiable {
			continue
		}
		h := fmt.Sprintf("bytes %d-%d/%d", rt.start, rt.end-1, testFileLength)
		if rt.r == "" {
			h = ""
		}
		cr := r.Header.Get("Content-Range")
		if cr != h {
			t.Errorf("header mismatch: range=%q: got %q, want %q", rt.r, cr, h)
		}
		if !equal(body, file[rt.start:rt.end]) {
			t.Errorf("body mismatch: range=%q: got %q, want %q", rt.r, body, file[rt.start:rt.end])
		}
	}
}

type testFileSystem struct {
	open func(name string) (File, os.Error)
}

func (fs *testFileSystem) Open(name string) (File, os.Error) {
	return fs.open(name)
}

func TestFileServerCleans(t *testing.T) {
	ch := make(chan string, 1)
	fs := FileServer(&testFileSystem{func(name string) (File, os.Error) {
		ch <- name
		return nil, os.ENOENT
	}})
	tests := []struct {
		reqPath, openArg string
	}{
		{"/foo.txt", "/foo.txt"},
		{"//foo.txt", "/foo.txt"},
		{"/../foo.txt", "/foo.txt"},
	}
	req, _ := NewRequest("GET", "http://example.com", nil)
	for n, test := range tests {
		rec := httptest.NewRecorder()
		req.URL.Path = test.reqPath
		fs.ServeHTTP(rec, req)
		if got := <-ch; got != test.openArg {
			t.Errorf("test %d: got %q, want %q", n, got, test.openArg)
		}
	}
}

func TestDirJoin(t *testing.T) {
	wfi, err := os.Stat("/etc/hosts")
	if err != nil {
		t.Logf("skipping test; no /etc/hosts file")
		return
	}
	test := func(d Dir, name string) {
		f, err := d.Open(name)
		if err != nil {
			t.Fatalf("open of %s: %v", name, err)
		}
		defer f.Close()
		gfi, err := f.Stat()
		if err != nil {
			t.Fatalf("stat of %s: %v", err)
		}
		if gfi.Ino != wfi.Ino {
			t.Errorf("%s got different inode")
		}
	}
	test(Dir("/etc/"), "/hosts")
	test(Dir("/etc/"), "hosts")
	test(Dir("/etc/"), "../../../../hosts")
	test(Dir("/etc"), "/hosts")
	test(Dir("/etc"), "hosts")
	test(Dir("/etc"), "../../../../hosts")

	// Not really directories, but since we use this trick in
	// ServeFile, test it:
	test(Dir("/etc/hosts"), "")
	test(Dir("/etc/hosts"), "/")
	test(Dir("/etc/hosts"), "../")
}

func TestServeFileContentType(t *testing.T) {
	const ctype = "icecream/chocolate"
	override := false
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		if override {
			w.Header().Set("Content-Type", ctype)
		}
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	get := func(want string) {
		resp, err := Get(ts.URL)
		if err != nil {
			t.Fatal(err)
		}
		if h := resp.Header.Get("Content-Type"); h != want {
			t.Errorf("Content-Type mismatch: got %d, want %d", h, want)
		}
	}
	get("text/plain; charset=utf-8")
	override = true
	get(ctype)
}

func TestServeFileMimeType(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		ServeFile(w, r, "testdata/style.css")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	want := "text/css; charset=utf-8"
	if h := resp.Header.Get("Content-Type"); h != want {
		t.Errorf("Content-Type mismatch: got %q, want %q", h, want)
	}
}

func TestServeFileWithContentEncoding(t *testing.T) {
	ts := httptest.NewServer(HandlerFunc(func(w ResponseWriter, r *Request) {
		w.Header().Set("Content-Encoding", "foo")
		ServeFile(w, r, "testdata/file")
	}))
	defer ts.Close()
	resp, err := Get(ts.URL)
	if err != nil {
		t.Fatal(err)
	}
	if g, e := resp.ContentLength, int64(-1); g != e {
		t.Errorf("Content-Length mismatch: got %q, want %q", g, e)
	}
}

func getBody(t *testing.T, req Request) (*Response, []byte) {
	r, err := DefaultClient.Do(&req)
	if err != nil {
		t.Fatal(req.URL.String(), "send:", err)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		t.Fatal("reading Body:", err)
	}
	return r, b
}

func equal(a, b []byte) bool {
	if len(a) != len(b) {
		return false
	}
	for i := range a {
		if a[i] != b[i] {
			return false
		}
	}
	return true
}
<|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.

// Waiting for FDs via epoll(7).

package net

import (
	"os"
	"syscall"
)

const (
	readFlags  = syscall.EPOLLIN | syscall.EPOLLRDHUP
	writeFlags = syscall.EPOLLOUT
)

type pollster struct {
	epfd int

	// Events we're already waiting for
	// Must hold pollServer lock
	events map[int]uint32
}

func newpollster() (p *pollster, err os.Error) {
	p = new(pollster)
	var e int

	// The arg to epoll_create is a hint to the kernel
	// about the number of FDs we will care about.
	// We don't know.
	if p.epfd, e = syscall.EpollCreate(16); e != 0 {
		return nil, os.NewSyscallError("epoll_create", e)
	}
	p.events = make(map[int]uint32)
	return p, nil
}

func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
	// pollServer is locked.

	var ev syscall.EpollEvent
	var already bool
	ev.Fd = int32(fd)
	ev.Events, already = p.events[fd]
	if !repeat {
		ev.Events |= syscall.EPOLLONESHOT
	}
	if mode == 'r' {
		ev.Events |= readFlags
	} else {
		ev.Events |= writeFlags
	}

	var op int
	if already {
		op = syscall.EPOLL_CTL_MOD
	} else {
		op = syscall.EPOLL_CTL_ADD
	}
	if e := syscall.EpollCtl(p.epfd, op, fd, &ev); e != 0 {
		return os.NewSyscallError("epoll_ctl", e)
	}
	p.events[fd] = ev.Events
	return nil
}

func (p *pollster) StopWaiting(fd int, bits uint) {
	// pollServer is locked.

	events, already := p.events[fd]
	if !already {
		print("Epoll unexpected fd=", fd, "\n")
		return
	}

	// If syscall.EPOLLONESHOT is not set, the wait
	// is a repeating wait, so don't change it.
	if events&syscall.EPOLLONESHOT == 0 {
		return
	}

	// Disable the given bits.
	// If we're still waiting for other events, modify the fd
	// event in the kernel.  Otherwise, delete it.
	events &= ^uint32(bits)
	if int32(events)&^syscall.EPOLLONESHOT != 0 {
		var ev syscall.EpollEvent
		ev.Fd = int32(fd)
		ev.Events = events
		if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &ev); e != 0 {
			print("Epoll modify fd=", fd, ": ", os.Errno(e).String(), "\n")
		}
		p.events[fd] = events
	} else {
		if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
			print("Epoll delete fd=", fd, ": ", os.Errno(e).String(), "\n")
		}
		p.events[fd] = 0, false
	}
}

func (p *pollster) DelFD(fd int, mode int) {
	// pollServer is locked.

	if mode == 'r' {
		p.StopWaiting(fd, readFlags)
	} else {
		p.StopWaiting(fd, writeFlags)
	}
}

func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) {
	s.Unlock()

	// Get an event.
	var evarray [1]syscall.EpollEvent
	ev := &evarray[0]
	var msec int = -1
	if nsec > 0 {
		msec = int((nsec + 1e6 - 1) / 1e6)
	}
	n, e := syscall.EpollWait(p.epfd, evarray[0:], msec)
	for e == syscall.EAGAIN || e == syscall.EINTR {
		n, e = syscall.EpollWait(p.epfd, evarray[0:], msec)
	}

	s.Lock()

	if e != 0 {
		return -1, 0, os.NewSyscallError("epoll_wait", e)
	}
	if n == 0 {
		return -1, 0, nil
	}
	fd = int(ev.Fd)

	if ev.Events&writeFlags != 0 {
		p.StopWaiting(fd, writeFlags)
		return fd, 'w', nil
	}
	if ev.Events&readFlags != 0 {
		p.StopWaiting(fd, readFlags)
		return fd, 'r', nil
	}

	// Other events are error conditions - wake whoever is waiting.
	events, _ := p.events[fd]
	if events&writeFlags != 0 {
		p.StopWaiting(fd, writeFlags)
		return fd, 'w', nil
	}
	p.StopWaiting(fd, readFlags)
	return fd, 'r', nil
}

func (p *pollster) Close() os.Error {
	return os.NewSyscallError("close", syscall.Close(p.epfd))
}
<commit_msg>net: Use preallocated buffer for epoll.<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.

// Waiting for FDs via epoll(7).

package net

import (
	"os"
	"syscall"
)

const (
	readFlags  = syscall.EPOLLIN | syscall.EPOLLRDHUP
	writeFlags = syscall.EPOLLOUT
)

type pollster struct {
	epfd int

	// Events we're already waiting for
	// Must hold pollServer lock
	events map[int]uint32

	// An event buffer for EpollWait.
	// Used without a lock, may only be used by WaitFD.
	waitEventBuf [10]syscall.EpollEvent
	waitEvents   []syscall.EpollEvent

	// An event buffer for EpollCtl, to avoid a malloc.
	// Must hold pollServer lock.
	ctlEvent syscall.EpollEvent
}

func newpollster() (p *pollster, err os.Error) {
	p = new(pollster)
	var e int

	// The arg to epoll_create is a hint to the kernel
	// about the number of FDs we will care about.
	// We don't know, and since 2.6.8 the kernel ignores it anyhow.
	if p.epfd, e = syscall.EpollCreate(16); e != 0 {
		return nil, os.NewSyscallError("epoll_create", e)
	}
	p.events = make(map[int]uint32)
	return p, nil
}

func (p *pollster) AddFD(fd int, mode int, repeat bool) os.Error {
	// pollServer is locked.

	var already bool
	p.ctlEvent.Fd = int32(fd)
	p.ctlEvent.Events, already = p.events[fd]
	if !repeat {
		p.ctlEvent.Events |= syscall.EPOLLONESHOT
	}
	if mode == 'r' {
		p.ctlEvent.Events |= readFlags
	} else {
		p.ctlEvent.Events |= writeFlags
	}

	var op int
	if already {
		op = syscall.EPOLL_CTL_MOD
	} else {
		op = syscall.EPOLL_CTL_ADD
	}
	if e := syscall.EpollCtl(p.epfd, op, fd, &p.ctlEvent); e != 0 {
		return os.NewSyscallError("epoll_ctl", e)
	}
	p.events[fd] = p.ctlEvent.Events
	return nil
}

func (p *pollster) StopWaiting(fd int, bits uint) {
	// pollServer is locked.

	events, already := p.events[fd]
	if !already {
		print("Epoll unexpected fd=", fd, "\n")
		return
	}

	// If syscall.EPOLLONESHOT is not set, the wait
	// is a repeating wait, so don't change it.
	if events&syscall.EPOLLONESHOT == 0 {
		return
	}

	// Disable the given bits.
	// If we're still waiting for other events, modify the fd
	// event in the kernel.  Otherwise, delete it.
	events &= ^uint32(bits)
	if int32(events)&^syscall.EPOLLONESHOT != 0 {
		p.ctlEvent.Fd = int32(fd)
		p.ctlEvent.Events = events
		if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_MOD, fd, &p.ctlEvent); e != 0 {
			print("Epoll modify fd=", fd, ": ", os.Errno(e).String(), "\n")
		}
		p.events[fd] = events
	} else {
		if e := syscall.EpollCtl(p.epfd, syscall.EPOLL_CTL_DEL, fd, nil); e != 0 {
			print("Epoll delete fd=", fd, ": ", os.Errno(e).String(), "\n")
		}
		p.events[fd] = 0, false
	}
}

func (p *pollster) DelFD(fd int, mode int) {
	// pollServer is locked.

	if mode == 'r' {
		p.StopWaiting(fd, readFlags)
	} else {
		p.StopWaiting(fd, writeFlags)
	}
}

func (p *pollster) WaitFD(s *pollServer, nsec int64) (fd int, mode int, err os.Error) {
	for len(p.waitEvents) == 0 {
		var msec int = -1
		if nsec > 0 {
			msec = int((nsec + 1e6 - 1) / 1e6)
		}

		s.Unlock()
		n, e := syscall.EpollWait(p.epfd, p.waitEventBuf[0:], msec)
		s.Lock()

		if e != 0 {
			if e == syscall.EAGAIN || e == syscall.EINTR {
				continue
			}
			return -1, 0, os.NewSyscallError("epoll_wait", e)
		}
		if n == 0 {
			return -1, 0, nil
		}
		p.waitEvents = p.waitEventBuf[0:n]
	}

	ev := &p.waitEvents[0]
	p.waitEvents = p.waitEvents[1:]

	fd = int(ev.Fd)

	if ev.Events&writeFlags != 0 {
		p.StopWaiting(fd, writeFlags)
		return fd, 'w', nil
	}
	if ev.Events&readFlags != 0 {
		p.StopWaiting(fd, readFlags)
		return fd, 'r', nil
	}

	// Other events are error conditions - wake whoever is waiting.
	events, _ := p.events[fd]
	if events&writeFlags != 0 {
		p.StopWaiting(fd, writeFlags)
		return fd, 'w', nil
	}
	p.StopWaiting(fd, readFlags)
	return fd, 'r', nil
}

func (p *pollster) Close() os.Error {
	return os.NewSyscallError("close", syscall.Close(p.epfd))
}
<|endoftext|>


<commit_before>// Package imdb provides easy access to publicly available data on IMDB
package imdb
<commit_msg>format documentation<commit_after>// Package imdb provides easy access to publicly available data on IMDB.
// Items are accessed by their IMDB ID, and all getter methods called
// on them are lazy (an http request will be made only when data is needed,
// and this will happen only once). There is also a convenience AllData()
// method, which fetches all available data at once.
package imdb
<|endoftext|>


<commit_before>// Copyright 2016 The Serviced 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 service

import (
	"github.com/control-center/serviced/datastore"
	"github.com/control-center/serviced/health"
	"github.com/control-center/serviced/validation"
)

// a lightweight Service object with enough data to support status polling even if frequent
type ServiceHealth struct {
	ID           string
	Name         string
	PoolID       string
	Instances    int
	DesiredState int
	HealthChecks map[string]health.HealthCheck
	datastore.VersionedEntity
}

// Validation for Service ServiceDetails entity
func (sh *ServiceHealth) ValidEntity() error {
	violations := validation.NewValidationError()
	violations.Add(validation.NotEmpty("ID", sh.ID))
	violations.Add(validation.NotEmpty("Name", sh.Name))
	violations.Add(validation.NotEmpty("PoolID", sh.PoolID))

	if len(violations.Errors) > 0 {
		return violations
	}

	return nil
}

func BuildServiceHealth(svc Service) *ServiceHealth {
	sh := &ServiceHealth{
		ID:           svc.ID,
		Name:         svc.Name,
		PoolID:       svc.PoolID,
		Instances:    svc.Instances,
		DesiredState: svc.DesiredState,
		HealthChecks: make(map[string]health.HealthCheck),
	}

	sh.HealthChecks = make(map[string]health.HealthCheck)
	for key, value := range svc.HealthChecks {
		sh.HealthChecks[key] = value
	}

	return sh
}
<commit_msg>Code review changes<commit_after>// Copyright 2016 The Serviced 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 service

import (
	"github.com/control-center/serviced/datastore"
	"github.com/control-center/serviced/health"
	"github.com/control-center/serviced/validation"
)

// a lightweight Service object with enough data to support status polling even if frequent
type ServiceHealth struct {
	ID           string
	Name         string
	PoolID       string
	Instances    int
	DesiredState int
	HealthChecks map[string]health.HealthCheck
	datastore.VersionedEntity
}

// Validation for Service ServiceDetails entity
func (sh *ServiceHealth) ValidEntity() error {
	violations := validation.NewValidationError()
	violations.Add(validation.NotEmpty("ID", sh.ID))
	violations.Add(validation.NotEmpty("Name", sh.Name))
	violations.Add(validation.NotEmpty("PoolID", sh.PoolID))

	if len(violations.Errors) > 0 {
		return violations
	}

	return nil
}

func BuildServiceHealth(svc Service) *ServiceHealth {
	sh := &ServiceHealth{
		ID:           svc.ID,
		Name:         svc.Name,
		PoolID:       svc.PoolID,
		Instances:    svc.Instances,
		DesiredState: svc.DesiredState,
		HealthChecks: make(map[string]health.HealthCheck),
	}

	for key, value := range svc.HealthChecks {
		sh.HealthChecks[key] = value
	}

	return sh
}
<|endoftext|>


<commit_before>package space_test

import (
	"github.com/cloudfoundry/cli/cf/api/spacequotas/spacequotasfakes"
	"github.com/cloudfoundry/cli/cf/commandregistry"
	//"github.com/cloudfoundry/cli/cf/configuration/coreconfig"

	"github.com/cloudfoundry/cli/plugin/models"
	//testcmd "github.com/cloudfoundry/cli/testhelpers/commands"
	//testreq "github.com/cloudfoundry/cli/testhelpers/requirements"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/commands/space"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/requirements/requirementsfakes"
	"github.com/cloudfoundry/cli/flags"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
)

var _ = Describe("space command", func() {
	var (
		ui               *testterm.FakeUI
		loginReq         *requirementsfakes.FakeRequirement
		targetedOrgReq   *requirementsfakes.FakeTargetedOrgRequirement
		reqFactory       *requirementsfakes.FakeFactory
		deps             commandregistry.Dependency
		cmd              space.ShowSpace
		flagContext      flags.FlagContext
		getSpaceModel    *plugin_models.GetSpace_Model
		spaceRequirement *requirementsfakes.FakeSpaceRequirement
		quotaRepo        *spacequotasfakes.FakeSpaceQuotaRepository
	)

	BeforeEach(func() {
		ui = new(testterm.FakeUI)
		quotaRepo = new(spacequotasfakes.FakeSpaceQuotaRepository)
		repoLocator := api.RepositoryLocator{}
		repoLocator = repoLocator.SetSpaceQuotaRepository(quotaRepo)
		getSpaceModel = new(plugin_models.GetSpace_Model)

		deps = commandregistry.Dependency{
			UI:          ui,
			Config:      testconfig.NewRepositoryWithDefaults(),
			RepoLocator: repoLocator,
			PluginModels: &commandregistry.PluginModels{
				Space: getSpaceModel,
			},
		}

		reqFactory = new(requirementsfakes.FakeFactory)

		loginReq = new(requirementsfakes.FakeRequirement)
		loginReq.ExecuteReturns(nil)
		reqFactory.NewLoginRequirementReturns(loginReq)

		targetedOrgReq = new(requirementsfakes.FakeTargetedOrgRequirement)
		targetedOrgReq.ExecuteReturns(nil)
		reqFactory.NewTargetedOrgRequirementReturns(targetedOrgReq)

		spaceRequirement = new(requirementsfakes.FakeSpaceRequirement)
		spaceRequirement.ExecuteReturns(nil)
		reqFactory.NewSpaceRequirementReturns(spaceRequirement)

		cmd = space.ShowSpace{}
		flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
		cmd.SetDependency(deps, false)
	})

	Describe("Requirements", func() {
		Context("when the wrong number of args are provided", func() {
			It("fails with no args", func() {
				flagContext.Parse()
				Expect(func() { cmd.Requirements(reqFactory, flagContext) }).To(Panic())
				Expect(ui.Outputs).To(ContainSubstrings(
					[]string{"FAILED"},
					[]string{"Incorrect Usage. Requires an argument"},
				))
			})
		})

		Context("when provided exactly one arg", func() {
			var actualRequirements []requirements.Requirement

			Context("when no flags are provided", func() {
				BeforeEach(func() {
					flagContext.Parse("my-space")
					actualRequirements = cmd.Requirements(reqFactory, flagContext)
				})

				It("returns a login requirement", func() {
					Expect(reqFactory.NewLoginRequirementCallCount()).To(Equal(1))
					Expect(actualRequirements).To(ContainElement(loginReq))
				})

				It("returns a targeted org requirement", func() {
					Expect(reqFactory.NewTargetedOrgRequirementCallCount()).To(Equal(1))
					Expect(actualRequirements).To(ContainElement(targetedOrgReq))
				})
			})
		})
	})

	Describe("Execute", func() {
		var (
			space      models.Space
			spaceQuota models.SpaceQuota
			executeErr error
		)

		BeforeEach(func() {
			org := models.OrganizationFields{
				Name: "my-org",
				GUID: "my-org-guid",
			}

			app := models.ApplicationFields{
				Name: "app1",
				GUID: "app1-guid",
			}

			apps := []models.ApplicationFields{app}

			domain := models.DomainFields{
				Name: "domain1",
				GUID: "domain1-guid",
			}

			domains := []models.DomainFields{domain}

			serviceInstance := models.ServiceInstanceFields{
				Name: "service1",
				GUID: "service1-guid",
			}
			services := []models.ServiceInstanceFields{serviceInstance}

			securityGroup1 := models.SecurityGroupFields{Name: "Nacho Security", Rules: []map[string]interface{}{
				{"protocol": "all", "destination": "0.0.0.0-9.255.255.255", "log": true, "IntTest": 1000},
			}}
			securityGroup2 := models.SecurityGroupFields{Name: "Nacho Prime", Rules: []map[string]interface{}{
				{"protocol": "udp", "ports": "8080-9090", "destination": "198.41.191.47/1"},
			}}
			securityGroups := []models.SecurityGroupFields{securityGroup1, securityGroup2}

			space = models.Space{
				SpaceFields: models.SpaceFields{
					Name: "whose-space-is-it-anyway",
					GUID: "whose-space-is-it-anyway-guid",
				},
				Organization:     org,
				Applications:     apps,
				Domains:          domains,
				ServiceInstances: services,
				SecurityGroups:   securityGroups,
				SpaceQuotaGUID:   "runaway-guid",
			}

			spaceRequirement.GetSpaceReturns(space)

			spaceQuota = models.SpaceQuota{
				Name:                    "runaway",
				GUID:                    "runaway-guid",
				MemoryLimit:             102400,
				InstanceMemoryLimit:     -1,
				RoutesLimit:             111,
				ServicesLimit:           222,
				NonBasicServicesAllowed: false,
				AppInstanceLimit:        7,
				ReservedRoutePortsLimit: "7",
			}

			quotaRepo.FindByGUIDReturns(spaceQuota, nil)
		})

		JustBeforeEach(func() {
			executeErr = cmd.Execute(flagContext)
		})

		Context("when logged in and an org is targeted", func() {
			BeforeEach(func() {
				err := flagContext.Parse("my-space")
				Expect(err).NotTo(HaveOccurred())
				cmd.Requirements(reqFactory, flagContext)
			})

			Context("when the guid flag is passed", func() {
				BeforeEach(func() {
					err := flagContext.Parse("my-space", "--guid")
					Expect(err).NotTo(HaveOccurred())
				})

				It("shows only the space guid", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"whose-space-is-it-anyway-guid"},
					))

					Expect(ui.Outputs).ToNot(ContainSubstrings(
						[]string{"Getting info for space", "whose-space-is-it-anyway", "my-org", "my-user"},
					))
				})
			})

			Context("when the security-group-rules flag is passed", func() {
				BeforeEach(func() {
					err := flagContext.Parse("my-space", "--security-group-rules")
					Expect(err).NotTo(HaveOccurred())
				})
				It("it shows space information and security group rules", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"Getting rules for the security group", "Nacho Security"},
						[]string{"protocol", "all"},
						[]string{"destination", "0.0.0.0-9.255.255.255"},
						[]string{"Getting rules for the security group", "Nacho Prime"},
						[]string{"protocol", "udp"},
						[]string{"log", "true"},
						[]string{"IntTest", "1000"},
						[]string{"ports", "8080-9090"},
						[]string{"destination", "198.41.191.47/1"},
					))
				})
			})

			Context("when the space has a space quota", func() {
				It("shows information about the given space", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"Getting info for space", "whose-space-is-it-anyway", "my-org", "my-user"},
						[]string{"OK"},
						[]string{"whose-space-is-it-anyway"},
						[]string{"Org", "my-org"},
						[]string{"Apps", "app1"},
						[]string{"Domains", "domain1"},
						[]string{"Services", "service1"},
						[]string{"Security Groups", "Nacho Security", "Nacho Prime"},
						[]string{"Space Quota", "runaway (100G memory limit, unlimited instance memory limit, 111 routes, 222 services, paid services disallowed, 7 app instance limit, 7 route ports)"},
					))
				})

				Context("when the route ports limit is -1", func() {
					BeforeEach(func() {
						spaceQuota.ReservedRoutePortsLimit = "-1"
						quotaRepo.FindByGUIDReturns(spaceQuota, nil)
					})

					It("displays unlimited as the route ports limit", func() {
						Expect(executeErr).NotTo(HaveOccurred())
						Expect(ui.Outputs).To(ContainSubstrings(
							[]string{"unlimited route ports"},
						))
					})
				})

				Context("when the reserved route ports field is not provided by the CC API", func() {
					BeforeEach(func() {
						spaceQuota.ReservedRoutePortsLimit = ""
						quotaRepo.FindByGUIDReturns(spaceQuota, nil)
					})

					It("should not display route ports", func() {
						Expect(executeErr).NotTo(HaveOccurred())
						Expect(ui.Outputs).NotTo(ContainSubstrings(
							[]string{"route ports"},
						))
					})
				})

				Context("when the app instance limit is -1", func() {
					BeforeEach(func() {
						spaceQuota.AppInstanceLimit = -1
						quotaRepo.FindByGUIDReturns(spaceQuota, nil)
					})

					It("displays unlimited as the app instance limit", func() {
						Expect(executeErr).NotTo(HaveOccurred())
						Expect(ui.Outputs).To(ContainSubstrings(
							[]string{"unlimited app instance limit"},
						))
					})
				})
			})

			Context("when the space does not have a space quota", func() {
				BeforeEach(func() {
					space.SpaceQuotaGUID = ""
					spaceRequirement.GetSpaceReturns(space)
				})

				It("shows information without a space quota", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(quotaRepo.FindByGUIDCallCount()).To(Equal(0))
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"Getting info for space", "whose-space-is-it-anyway", "my-org", "my-user"},
						[]string{"OK"},
						[]string{"whose-space-is-it-anyway"},
						[]string{"Org", "my-org"},
						[]string{"Apps", "app1"},
						[]string{"Domains", "domain1"},
						[]string{"Services", "service1"},
						[]string{"Security Groups", "Nacho Security", "Nacho Prime"},
						[]string{"Space Quota"},
					))
				})
			})

			Context("When called as a plugin", func() {
				BeforeEach(func() {
					cmd.SetDependency(deps, true)
				})

				It("Fills in the PluginModel", func() {
					Expect(executeErr).NotTo(HaveOccurred())

					Expect(getSpaceModel.Name).To(Equal("whose-space-is-it-anyway"))
					Expect(getSpaceModel.Guid).To(Equal("whose-space-is-it-anyway-guid"))

					Expect(getSpaceModel.Organization.Name).To(Equal("my-org"))
					Expect(getSpaceModel.Organization.Guid).To(Equal("my-org-guid"))

					Expect(getSpaceModel.Applications).To(HaveLen(1))
					Expect(getSpaceModel.Applications[0].Name).To(Equal("app1"))
					Expect(getSpaceModel.Applications[0].Guid).To(Equal("app1-guid"))

					Expect(getSpaceModel.Domains).To(HaveLen(1))
					Expect(getSpaceModel.Domains[0].Name).To(Equal("domain1"))
					Expect(getSpaceModel.Domains[0].Guid).To(Equal("domain1-guid"))

					Expect(getSpaceModel.ServiceInstances).To(HaveLen(1))
					Expect(getSpaceModel.ServiceInstances[0].Name).To(Equal("service1"))
					Expect(getSpaceModel.ServiceInstances[0].Guid).To(Equal("service1-guid"))

					Expect(getSpaceModel.SecurityGroups).To(HaveLen(2))
					Expect(getSpaceModel.SecurityGroups[0].Name).To(Equal("Nacho Security"))
					Expect(getSpaceModel.SecurityGroups[0].Rules).To(HaveLen(1))
					Expect(getSpaceModel.SecurityGroups[0].Rules[0]).To(HaveLen(4))
					val := getSpaceModel.SecurityGroups[0].Rules[0]["protocol"]
					Expect(val).To(Equal("all"))
					val = getSpaceModel.SecurityGroups[0].Rules[0]["destination"]
					Expect(val).To(Equal("0.0.0.0-9.255.255.255"))

					Expect(getSpaceModel.SecurityGroups[1].Name).To(Equal("Nacho Prime"))
					Expect(getSpaceModel.SecurityGroups[1].Rules).To(HaveLen(1))
					Expect(getSpaceModel.SecurityGroups[1].Rules[0]).To(HaveLen(3))
					val = getSpaceModel.SecurityGroups[1].Rules[0]["protocol"]
					Expect(val).To(Equal("udp"))
					val = getSpaceModel.SecurityGroups[1].Rules[0]["destination"]
					Expect(val).To(Equal("198.41.191.47/1"))
					val = getSpaceModel.SecurityGroups[1].Rules[0]["ports"]
					Expect(val).To(Equal("8080-9090"))

					Expect(getSpaceModel.SpaceQuota.Name).To(Equal("runaway"))
					Expect(getSpaceModel.SpaceQuota.Guid).To(Equal("runaway-guid"))
					Expect(getSpaceModel.SpaceQuota.MemoryLimit).To(Equal(int64(102400)))
					Expect(getSpaceModel.SpaceQuota.InstanceMemoryLimit).To(Equal(int64(-1)))
					Expect(getSpaceModel.SpaceQuota.RoutesLimit).To(Equal(111))
					Expect(getSpaceModel.SpaceQuota.ServicesLimit).To(Equal(222))
					Expect(getSpaceModel.SpaceQuota.NonBasicServicesAllowed).To(BeFalse())
				})
			})
		})
	})
})
<commit_msg>Add space requirement test and remove imports<commit_after>package space_test

import (
	"github.com/cloudfoundry/cli/cf/api/spacequotas/spacequotasfakes"
	"github.com/cloudfoundry/cli/cf/commandregistry"

	"github.com/cloudfoundry/cli/plugin/models"
	testconfig "github.com/cloudfoundry/cli/testhelpers/configuration"
	testterm "github.com/cloudfoundry/cli/testhelpers/terminal"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"github.com/cloudfoundry/cli/cf/api"
	"github.com/cloudfoundry/cli/cf/commands/space"
	"github.com/cloudfoundry/cli/cf/models"
	"github.com/cloudfoundry/cli/cf/requirements"
	"github.com/cloudfoundry/cli/cf/requirements/requirementsfakes"
	"github.com/cloudfoundry/cli/flags"
	. "github.com/cloudfoundry/cli/testhelpers/matchers"
)

var _ = Describe("space command", func() {
	var (
		ui               *testterm.FakeUI
		loginReq         *requirementsfakes.FakeRequirement
		targetedOrgReq   *requirementsfakes.FakeTargetedOrgRequirement
		reqFactory       *requirementsfakes.FakeFactory
		deps             commandregistry.Dependency
		cmd              space.ShowSpace
		flagContext      flags.FlagContext
		getSpaceModel    *plugin_models.GetSpace_Model
		spaceRequirement *requirementsfakes.FakeSpaceRequirement
		quotaRepo        *spacequotasfakes.FakeSpaceQuotaRepository
	)

	BeforeEach(func() {
		ui = new(testterm.FakeUI)
		quotaRepo = new(spacequotasfakes.FakeSpaceQuotaRepository)
		repoLocator := api.RepositoryLocator{}
		repoLocator = repoLocator.SetSpaceQuotaRepository(quotaRepo)
		getSpaceModel = new(plugin_models.GetSpace_Model)

		deps = commandregistry.Dependency{
			UI:          ui,
			Config:      testconfig.NewRepositoryWithDefaults(),
			RepoLocator: repoLocator,
			PluginModels: &commandregistry.PluginModels{
				Space: getSpaceModel,
			},
		}

		reqFactory = new(requirementsfakes.FakeFactory)

		loginReq = new(requirementsfakes.FakeRequirement)
		loginReq.ExecuteReturns(nil)
		reqFactory.NewLoginRequirementReturns(loginReq)

		targetedOrgReq = new(requirementsfakes.FakeTargetedOrgRequirement)
		targetedOrgReq.ExecuteReturns(nil)
		reqFactory.NewTargetedOrgRequirementReturns(targetedOrgReq)

		spaceRequirement = new(requirementsfakes.FakeSpaceRequirement)
		spaceRequirement.ExecuteReturns(nil)
		reqFactory.NewSpaceRequirementReturns(spaceRequirement)

		cmd = space.ShowSpace{}
		flagContext = flags.NewFlagContext(cmd.MetaData().Flags)
		cmd.SetDependency(deps, false)
	})

	Describe("Requirements", func() {
		Context("when the wrong number of args are provided", func() {
			It("fails with no args", func() {
				flagContext.Parse()
				Expect(func() { cmd.Requirements(reqFactory, flagContext) }).To(Panic())
				Expect(ui.Outputs).To(ContainSubstrings(
					[]string{"FAILED"},
					[]string{"Incorrect Usage. Requires an argument"},
				))
			})
		})

		Context("when provided exactly one arg", func() {
			var actualRequirements []requirements.Requirement

			Context("when no flags are provided", func() {
				BeforeEach(func() {
					flagContext.Parse("my-space")
					actualRequirements = cmd.Requirements(reqFactory, flagContext)
				})

				It("returns a login requirement", func() {
					Expect(reqFactory.NewLoginRequirementCallCount()).To(Equal(1))
					Expect(actualRequirements).To(ContainElement(loginReq))
				})

				It("returns a targeted org requirement", func() {
					Expect(reqFactory.NewTargetedOrgRequirementCallCount()).To(Equal(1))
					Expect(actualRequirements).To(ContainElement(targetedOrgReq))
				})

				It("returns a space requirement", func() {
					Expect(reqFactory.NewSpaceRequirementCallCount()).To(Equal(1))
					Expect(actualRequirements).To(ContainElement(spaceRequirement))
				})
			})
		})
	})

	Describe("Execute", func() {
		var (
			space      models.Space
			spaceQuota models.SpaceQuota
			executeErr error
		)

		BeforeEach(func() {
			org := models.OrganizationFields{
				Name: "my-org",
				GUID: "my-org-guid",
			}

			app := models.ApplicationFields{
				Name: "app1",
				GUID: "app1-guid",
			}

			apps := []models.ApplicationFields{app}

			domain := models.DomainFields{
				Name: "domain1",
				GUID: "domain1-guid",
			}

			domains := []models.DomainFields{domain}

			serviceInstance := models.ServiceInstanceFields{
				Name: "service1",
				GUID: "service1-guid",
			}
			services := []models.ServiceInstanceFields{serviceInstance}

			securityGroup1 := models.SecurityGroupFields{Name: "Nacho Security", Rules: []map[string]interface{}{
				{"protocol": "all", "destination": "0.0.0.0-9.255.255.255", "log": true, "IntTest": 1000},
			}}
			securityGroup2 := models.SecurityGroupFields{Name: "Nacho Prime", Rules: []map[string]interface{}{
				{"protocol": "udp", "ports": "8080-9090", "destination": "198.41.191.47/1"},
			}}
			securityGroups := []models.SecurityGroupFields{securityGroup1, securityGroup2}

			space = models.Space{
				SpaceFields: models.SpaceFields{
					Name: "whose-space-is-it-anyway",
					GUID: "whose-space-is-it-anyway-guid",
				},
				Organization:     org,
				Applications:     apps,
				Domains:          domains,
				ServiceInstances: services,
				SecurityGroups:   securityGroups,
				SpaceQuotaGUID:   "runaway-guid",
			}

			spaceRequirement.GetSpaceReturns(space)

			spaceQuota = models.SpaceQuota{
				Name:                    "runaway",
				GUID:                    "runaway-guid",
				MemoryLimit:             102400,
				InstanceMemoryLimit:     -1,
				RoutesLimit:             111,
				ServicesLimit:           222,
				NonBasicServicesAllowed: false,
				AppInstanceLimit:        7,
				ReservedRoutePortsLimit: "7",
			}

			quotaRepo.FindByGUIDReturns(spaceQuota, nil)
		})

		JustBeforeEach(func() {
			executeErr = cmd.Execute(flagContext)
		})

		Context("when logged in and an org is targeted", func() {
			BeforeEach(func() {
				err := flagContext.Parse("my-space")
				Expect(err).NotTo(HaveOccurred())
				cmd.Requirements(reqFactory, flagContext)
			})

			Context("when the guid flag is passed", func() {
				BeforeEach(func() {
					err := flagContext.Parse("my-space", "--guid")
					Expect(err).NotTo(HaveOccurred())
				})

				It("shows only the space guid", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"whose-space-is-it-anyway-guid"},
					))

					Expect(ui.Outputs).ToNot(ContainSubstrings(
						[]string{"Getting info for space", "whose-space-is-it-anyway", "my-org", "my-user"},
					))
				})
			})

			Context("when the security-group-rules flag is passed", func() {
				BeforeEach(func() {
					err := flagContext.Parse("my-space", "--security-group-rules")
					Expect(err).NotTo(HaveOccurred())
				})
				It("it shows space information and security group rules", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"Getting rules for the security group", "Nacho Security"},
						[]string{"protocol", "all"},
						[]string{"destination", "0.0.0.0-9.255.255.255"},
						[]string{"Getting rules for the security group", "Nacho Prime"},
						[]string{"protocol", "udp"},
						[]string{"log", "true"},
						[]string{"IntTest", "1000"},
						[]string{"ports", "8080-9090"},
						[]string{"destination", "198.41.191.47/1"},
					))
				})
			})

			Context("when the space has a space quota", func() {
				It("shows information about the given space", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"Getting info for space", "whose-space-is-it-anyway", "my-org", "my-user"},
						[]string{"OK"},
						[]string{"whose-space-is-it-anyway"},
						[]string{"Org", "my-org"},
						[]string{"Apps", "app1"},
						[]string{"Domains", "domain1"},
						[]string{"Services", "service1"},
						[]string{"Security Groups", "Nacho Security", "Nacho Prime"},
						[]string{"Space Quota", "runaway (100G memory limit, unlimited instance memory limit, 111 routes, 222 services, paid services disallowed, 7 app instance limit, 7 route ports)"},
					))
				})

				Context("when the route ports limit is -1", func() {
					BeforeEach(func() {
						spaceQuota.ReservedRoutePortsLimit = "-1"
						quotaRepo.FindByGUIDReturns(spaceQuota, nil)
					})

					It("displays unlimited as the route ports limit", func() {
						Expect(executeErr).NotTo(HaveOccurred())
						Expect(ui.Outputs).To(ContainSubstrings(
							[]string{"unlimited route ports"},
						))
					})
				})

				Context("when the reserved route ports field is not provided by the CC API", func() {
					BeforeEach(func() {
						spaceQuota.ReservedRoutePortsLimit = ""
						quotaRepo.FindByGUIDReturns(spaceQuota, nil)
					})

					It("should not display route ports", func() {
						Expect(executeErr).NotTo(HaveOccurred())
						Expect(ui.Outputs).NotTo(ContainSubstrings(
							[]string{"route ports"},
						))
					})
				})

				Context("when the app instance limit is -1", func() {
					BeforeEach(func() {
						spaceQuota.AppInstanceLimit = -1
						quotaRepo.FindByGUIDReturns(spaceQuota, nil)
					})

					It("displays unlimited as the app instance limit", func() {
						Expect(executeErr).NotTo(HaveOccurred())
						Expect(ui.Outputs).To(ContainSubstrings(
							[]string{"unlimited app instance limit"},
						))
					})
				})
			})

			Context("when the space does not have a space quota", func() {
				BeforeEach(func() {
					space.SpaceQuotaGUID = ""
					spaceRequirement.GetSpaceReturns(space)
				})

				It("shows information without a space quota", func() {
					Expect(executeErr).NotTo(HaveOccurred())
					Expect(quotaRepo.FindByGUIDCallCount()).To(Equal(0))
					Expect(ui.Outputs).To(ContainSubstrings(
						[]string{"Getting info for space", "whose-space-is-it-anyway", "my-org", "my-user"},
						[]string{"OK"},
						[]string{"whose-space-is-it-anyway"},
						[]string{"Org", "my-org"},
						[]string{"Apps", "app1"},
						[]string{"Domains", "domain1"},
						[]string{"Services", "service1"},
						[]string{"Security Groups", "Nacho Security", "Nacho Prime"},
						[]string{"Space Quota"},
					))
				})
			})

			Context("When called as a plugin", func() {
				BeforeEach(func() {
					cmd.SetDependency(deps, true)
				})

				It("Fills in the PluginModel", func() {
					Expect(executeErr).NotTo(HaveOccurred())

					Expect(getSpaceModel.Name).To(Equal("whose-space-is-it-anyway"))
					Expect(getSpaceModel.Guid).To(Equal("whose-space-is-it-anyway-guid"))

					Expect(getSpaceModel.Organization.Name).To(Equal("my-org"))
					Expect(getSpaceModel.Organization.Guid).To(Equal("my-org-guid"))

					Expect(getSpaceModel.Applications).To(HaveLen(1))
					Expect(getSpaceModel.Applications[0].Name).To(Equal("app1"))
					Expect(getSpaceModel.Applications[0].Guid).To(Equal("app1-guid"))

					Expect(getSpaceModel.Domains).To(HaveLen(1))
					Expect(getSpaceModel.Domains[0].Name).To(Equal("domain1"))
					Expect(getSpaceModel.Domains[0].Guid).To(Equal("domain1-guid"))

					Expect(getSpaceModel.ServiceInstances).To(HaveLen(1))
					Expect(getSpaceModel.ServiceInstances[0].Name).To(Equal("service1"))
					Expect(getSpaceModel.ServiceInstances[0].Guid).To(Equal("service1-guid"))

					Expect(getSpaceModel.SecurityGroups).To(HaveLen(2))
					Expect(getSpaceModel.SecurityGroups[0].Name).To(Equal("Nacho Security"))
					Expect(getSpaceModel.SecurityGroups[0].Rules).To(HaveLen(1))
					Expect(getSpaceModel.SecurityGroups[0].Rules[0]).To(HaveLen(4))
					val := getSpaceModel.SecurityGroups[0].Rules[0]["protocol"]
					Expect(val).To(Equal("all"))
					val = getSpaceModel.SecurityGroups[0].Rules[0]["destination"]
					Expect(val).To(Equal("0.0.0.0-9.255.255.255"))

					Expect(getSpaceModel.SecurityGroups[1].Name).To(Equal("Nacho Prime"))
					Expect(getSpaceModel.SecurityGroups[1].Rules).To(HaveLen(1))
					Expect(getSpaceModel.SecurityGroups[1].Rules[0]).To(HaveLen(3))
					val = getSpaceModel.SecurityGroups[1].Rules[0]["protocol"]
					Expect(val).To(Equal("udp"))
					val = getSpaceModel.SecurityGroups[1].Rules[0]["destination"]
					Expect(val).To(Equal("198.41.191.47/1"))
					val = getSpaceModel.SecurityGroups[1].Rules[0]["ports"]
					Expect(val).To(Equal("8080-9090"))

					Expect(getSpaceModel.SpaceQuota.Name).To(Equal("runaway"))
					Expect(getSpaceModel.SpaceQuota.Guid).To(Equal("runaway-guid"))
					Expect(getSpaceModel.SpaceQuota.MemoryLimit).To(Equal(int64(102400)))
					Expect(getSpaceModel.SpaceQuota.InstanceMemoryLimit).To(Equal(int64(-1)))
					Expect(getSpaceModel.SpaceQuota.RoutesLimit).To(Equal(111))
					Expect(getSpaceModel.SpaceQuota.ServicesLimit).To(Equal(222))
					Expect(getSpaceModel.SpaceQuota.NonBasicServicesAllowed).To(BeFalse())
				})
			})
		})
	})
})
<|endoftext|>


<commit_before>package mesh

import (
	"fmt"
	"io/ioutil"
	"log"
	"sync"
	"testing"

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

// TODO test gossip unicast; atm we only test topology gossip and
// surrogates, neither of which employ unicast.

type mockGossipConnection struct {
	remoteConnection
	dest    *Router
	senders *gossipSenders
	start   chan struct{}
}

var _ gossipConnection = &mockGossipConnection{}

func newTestRouter(t *testing.T, name string) *Router {
	peerName, _ := PeerNameFromString(name)
	router, err := NewRouter(Config{}, peerName, "nick", nil, log.New(ioutil.Discard, "", 0))
	require.NoError(t, err)
	router.Start()
	return router
}

func (conn *mockGossipConnection) breakTie(dupConn ourConnection) connectionTieBreak {
	return tieBreakTied
}

func (conn *mockGossipConnection) shutdown(err error) {
}

func (conn *mockGossipConnection) logf(format string, args ...interface{}) {
	format = "->[" + conn.remoteTCPAddr + "|" + conn.remote.String() + "]: " + format
	if len(format) == 0 || format[len(format)-1] != '\n' {
		format += "\n"
	}
	fmt.Printf(format, args...)
}

func (conn *mockGossipConnection) SendProtocolMsg(pm protocolMsg) error {
	<-conn.start
	return conn.dest.handleGossip(pm.tag, pm.msg)
}

func (conn *mockGossipConnection) gossipSenders() *gossipSenders {
	return conn.senders
}

func (conn *mockGossipConnection) Start() {
	close(conn.start)
}

func sendPendingGossip(routers ...*Router) {
	// Loop until all routers report they didn't send anything
	for sentSomething := true; sentSomething; {
		sentSomething = false
		for _, router := range routers {
			sentSomething = router.sendPendingGossip() || sentSomething
		}
	}
}

func sendPendingTopologyUpdates(routers ...*Router) {
	for _, router := range routers {
		router.Ourself.broadcastPendingTopologyUpdates()
	}
}

func addTestGossipConnection(r1, r2 *Router) {
	c1 := r1.newTestGossipConnection(r2)
	c2 := r2.newTestGossipConnection(r1)
	c1.Start()
	c2.Start()
}

func (router *Router) newTestGossipConnection(r *Router) *mockGossipConnection {
	to := r.Ourself.Peer
	toPeer := newPeer(to.Name, to.NickName, to.UID, 0, to.ShortID)
	toPeer = router.Peers.fetchWithDefault(toPeer) // Has side-effect of incrementing refcount

	conn := &mockGossipConnection{
		remoteConnection: *newRemoteConnection(router.Ourself.Peer, toPeer, "", false, true),
		dest:             r,
		start:            make(chan struct{}),
	}
	conn.senders = newGossipSenders(conn, make(chan struct{}))
	router.Ourself.handleAddConnection(conn, false)
	router.Ourself.handleConnectionEstablished(conn)
	return conn
}

func (router *Router) DeleteTestGossipConnection(r *Router) {
	toName := r.Ourself.Peer.Name
	conn, _ := router.Ourself.ConnectionTo(toName)
	router.Peers.dereference(conn.Remote())
	router.Ourself.handleDeleteConnection(conn.(ourConnection))
}

// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
	peer := newPeerFrom(router.Ourself.Peer)
	connections := make(map[PeerName]Connection)
	for _, r := range routers {
		p := newPeerFrom(r.Ourself.Peer)
		connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
	}
	peer.Version = router.Ourself.Peer.Version
	peer.connections = connections
	return peer
}

// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
	router.Peers.RLock()
	checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
	router.Peers.RUnlock()
}

func flushAndCheckTopology(t *testing.T, routers []*Router, wantedPeers ...*Peer) {
	sendPendingTopologyUpdates(routers...)
	sendPendingGossip(routers...)
	for _, r := range routers {
		checkTopology(t, r, wantedPeers...)
	}
}

func TestGossipTopology(t *testing.T) {
	// Create some peers that will talk to each other
	r1 := newTestRouter(t, "01:00:00:01:00:00")
	r2 := newTestRouter(t, "02:00:00:02:00:00")
	r3 := newTestRouter(t, "03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	// Check state when they have no connections
	checkTopology(t, r1, r1.tp())
	checkTopology(t, r2, r2.tp())

	// Now try adding some connections
	addTestGossipConnection(r1, r2)
	sendPendingGossip(r1, r2)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))

	addTestGossipConnection(r2, r3)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	addTestGossipConnection(r3, r1)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))

	// Drop the connection from 2 to 3
	r2.DeleteTestGossipConnection(r3)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))

	// Drop the connection from 1 to 3
	r1.DeleteTestGossipConnection(r3)
	sendPendingTopologyUpdates(routers...)
	sendPendingGossip(r1, r2, r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
	// r3 still thinks r1 has a connection to it
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
}

func TestGossipSurrogate(t *testing.T) {
	// create the topology r1 <-> r2 <-> r3
	r1 := newTestRouter(t, "01:00:00:01:00:00")
	r2 := newTestRouter(t, "02:00:00:02:00:00")
	r3 := newTestRouter(t, "03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	addTestGossipConnection(r1, r2)
	addTestGossipConnection(r3, r2)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	// create a gossiper at either end, but not the middle
	g1 := newTestGossiper()
	g3 := newTestGossiper()
	s1, err := r1.NewGossip("Test", g1)
	require.NoError(t, err)
	s3, err := r3.NewGossip("Test", g3)
	require.NoError(t, err)

	// broadcast a message from each end, check it reaches the other
	broadcast(s1, 1)
	broadcast(s3, 2)
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 2)
	g3.checkHas(t, 1)

	// check that each end gets their message back through periodic
	// gossip
	r1.sendAllGossip()
	r3.sendAllGossip()
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 1, 2)
	g3.checkHas(t, 1, 2)
}

type testGossiper struct {
	sync.RWMutex
	state map[byte]struct{}
}

func newTestGossiper() *testGossiper {
	return &testGossiper{state: make(map[byte]struct{})}
}

func (g *testGossiper) OnGossipUnicast(sender PeerName, msg []byte) error {
	return nil
}

func (g *testGossiper) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	for _, v := range update {
		g.state[v] = struct{}{}
	}
	return newSurrogateGossipData(update), nil
}

func (g *testGossiper) Gossip() GossipData {
	g.RLock()
	defer g.RUnlock()
	state := make([]byte, len(g.state))
	for v := range g.state {
		state = append(state, v)
	}
	return newSurrogateGossipData(state)
}

func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	var delta []byte
	for _, v := range update {
		if _, found := g.state[v]; !found {
			delta = append(delta, v)
			g.state[v] = struct{}{}
		}
	}
	if len(delta) == 0 {
		return nil, nil
	}
	return newSurrogateGossipData(delta), nil
}

func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
	g.RLock()
	defer g.RUnlock()
	for _, v := range vs {
		if _, found := g.state[v]; !found {
			require.FailNow(t, fmt.Sprintf("%d is missing", v))
		}
	}
}

func broadcast(s Gossip, v byte) {
	s.GossipBroadcast(newSurrogateGossipData([]byte{v}))
}
<commit_msg>check pendingTopologyUpdate flag to send topology updates<commit_after>package mesh

import (
	"fmt"
	"io/ioutil"
	"log"
	"sync"
	"testing"

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

// TODO test gossip unicast; atm we only test topology gossip and
// surrogates, neither of which employ unicast.

type mockGossipConnection struct {
	remoteConnection
	dest    *Router
	senders *gossipSenders
	start   chan struct{}
}

var _ gossipConnection = &mockGossipConnection{}

func newTestRouter(t *testing.T, name string) *Router {
	peerName, _ := PeerNameFromString(name)
	router, err := NewRouter(Config{}, peerName, "nick", nil, log.New(ioutil.Discard, "", 0))
	require.NoError(t, err)
	router.Start()
	return router
}

func (conn *mockGossipConnection) breakTie(dupConn ourConnection) connectionTieBreak {
	return tieBreakTied
}

func (conn *mockGossipConnection) shutdown(err error) {
}

func (conn *mockGossipConnection) logf(format string, args ...interface{}) {
	format = "->[" + conn.remoteTCPAddr + "|" + conn.remote.String() + "]: " + format
	if len(format) == 0 || format[len(format)-1] != '\n' {
		format += "\n"
	}
	fmt.Printf(format, args...)
}

func (conn *mockGossipConnection) SendProtocolMsg(pm protocolMsg) error {
	<-conn.start
	return conn.dest.handleGossip(pm.tag, pm.msg)
}

func (conn *mockGossipConnection) gossipSenders() *gossipSenders {
	return conn.senders
}

func (conn *mockGossipConnection) Start() {
	close(conn.start)
}

func sendPendingGossip(routers ...*Router) {
	// Loop until all routers report they didn't send anything
	for sentSomething := true; sentSomething; {
		sentSomething = false
		for _, router := range routers {
			sentSomething = router.sendPendingGossip() || sentSomething
		}
	}
}

func sendPendingTopologyUpdates(routers ...*Router) {
	for _, router := range routers {
		router.Ourself.Lock()
		pendingUpdate := router.Ourself.pendingTopologyUpdate
		router.Ourself.Unlock()
		if pendingUpdate {
			router.Ourself.broadcastPendingTopologyUpdates()
		}
	}
}

func addTestGossipConnection(r1, r2 *Router) {
	c1 := r1.newTestGossipConnection(r2)
	c2 := r2.newTestGossipConnection(r1)
	c1.Start()
	c2.Start()
}

func (router *Router) newTestGossipConnection(r *Router) *mockGossipConnection {
	to := r.Ourself.Peer
	toPeer := newPeer(to.Name, to.NickName, to.UID, 0, to.ShortID)
	toPeer = router.Peers.fetchWithDefault(toPeer) // Has side-effect of incrementing refcount

	conn := &mockGossipConnection{
		remoteConnection: *newRemoteConnection(router.Ourself.Peer, toPeer, "", false, true),
		dest:             r,
		start:            make(chan struct{}),
	}
	conn.senders = newGossipSenders(conn, make(chan struct{}))
	router.Ourself.handleAddConnection(conn, false)
	router.Ourself.handleConnectionEstablished(conn)
	return conn
}

func (router *Router) DeleteTestGossipConnection(r *Router) {
	toName := r.Ourself.Peer.Name
	conn, _ := router.Ourself.ConnectionTo(toName)
	router.Peers.dereference(conn.Remote())
	router.Ourself.handleDeleteConnection(conn.(ourConnection))
}

// Create a Peer representing the receiver router, with connections to
// the routers supplied as arguments, carrying across all UID and
// version information.
func (router *Router) tp(routers ...*Router) *Peer {
	peer := newPeerFrom(router.Ourself.Peer)
	connections := make(map[PeerName]Connection)
	for _, r := range routers {
		p := newPeerFrom(r.Ourself.Peer)
		connections[r.Ourself.Peer.Name] = newMockConnection(peer, p)
	}
	peer.Version = router.Ourself.Peer.Version
	peer.connections = connections
	return peer
}

// Check that the topology of router matches the peers and all of their connections
func checkTopology(t *testing.T, router *Router, wantedPeers ...*Peer) {
	router.Peers.RLock()
	checkTopologyPeers(t, true, router.Peers.allPeers(), wantedPeers...)
	router.Peers.RUnlock()
}

func flushAndCheckTopology(t *testing.T, routers []*Router, wantedPeers ...*Peer) {
	sendPendingTopologyUpdates(routers...)
	sendPendingGossip(routers...)
	for _, r := range routers {
		checkTopology(t, r, wantedPeers...)
	}
}

func TestGossipTopology(t *testing.T) {
	// Create some peers that will talk to each other
	r1 := newTestRouter(t, "01:00:00:01:00:00")
	r2 := newTestRouter(t, "02:00:00:02:00:00")
	r3 := newTestRouter(t, "03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	// Check state when they have no connections
	checkTopology(t, r1, r1.tp())
	checkTopology(t, r2, r2.tp())

	// Now try adding some connections
	addTestGossipConnection(r1, r2)
	sendPendingGossip(r1, r2)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))

	addTestGossipConnection(r2, r3)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	addTestGossipConnection(r3, r1)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1, r3), r3.tp(r1, r2))

	// Drop the connection from 2 to 3
	r2.DeleteTestGossipConnection(r3)
	flushAndCheckTopology(t, routers, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))

	// Drop the connection from 1 to 3
	r1.DeleteTestGossipConnection(r3)
	sendPendingTopologyUpdates(routers...)
	sendPendingGossip(r1, r2, r3)
	checkTopology(t, r1, r1.tp(r2), r2.tp(r1))
	checkTopology(t, r2, r1.tp(r2), r2.tp(r1))
	// r3 still thinks r1 has a connection to it
	checkTopology(t, r3, r1.tp(r2, r3), r2.tp(r1), r3.tp(r1, r2))
}

func TestGossipSurrogate(t *testing.T) {
	// create the topology r1 <-> r2 <-> r3
	r1 := newTestRouter(t, "01:00:00:01:00:00")
	r2 := newTestRouter(t, "02:00:00:02:00:00")
	r3 := newTestRouter(t, "03:00:00:03:00:00")
	routers := []*Router{r1, r2, r3}
	addTestGossipConnection(r1, r2)
	addTestGossipConnection(r3, r2)
	flushAndCheckTopology(t, routers, r1.tp(r2), r2.tp(r1, r3), r3.tp(r2))

	// create a gossiper at either end, but not the middle
	g1 := newTestGossiper()
	g3 := newTestGossiper()
	s1, err := r1.NewGossip("Test", g1)
	require.NoError(t, err)
	s3, err := r3.NewGossip("Test", g3)
	require.NoError(t, err)

	// broadcast a message from each end, check it reaches the other
	broadcast(s1, 1)
	broadcast(s3, 2)
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 2)
	g3.checkHas(t, 1)

	// check that each end gets their message back through periodic
	// gossip
	r1.sendAllGossip()
	r3.sendAllGossip()
	sendPendingGossip(r1, r2, r3)
	g1.checkHas(t, 1, 2)
	g3.checkHas(t, 1, 2)
}

type testGossiper struct {
	sync.RWMutex
	state map[byte]struct{}
}

func newTestGossiper() *testGossiper {
	return &testGossiper{state: make(map[byte]struct{})}
}

func (g *testGossiper) OnGossipUnicast(sender PeerName, msg []byte) error {
	return nil
}

func (g *testGossiper) OnGossipBroadcast(_ PeerName, update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	for _, v := range update {
		g.state[v] = struct{}{}
	}
	return newSurrogateGossipData(update), nil
}

func (g *testGossiper) Gossip() GossipData {
	g.RLock()
	defer g.RUnlock()
	state := make([]byte, len(g.state))
	for v := range g.state {
		state = append(state, v)
	}
	return newSurrogateGossipData(state)
}

func (g *testGossiper) OnGossip(update []byte) (GossipData, error) {
	g.Lock()
	defer g.Unlock()
	var delta []byte
	for _, v := range update {
		if _, found := g.state[v]; !found {
			delta = append(delta, v)
			g.state[v] = struct{}{}
		}
	}
	if len(delta) == 0 {
		return nil, nil
	}
	return newSurrogateGossipData(delta), nil
}

func (g *testGossiper) checkHas(t *testing.T, vs ...byte) {
	g.RLock()
	defer g.RUnlock()
	for _, v := range vs {
		if _, found := g.state[v]; !found {
			require.FailNow(t, fmt.Sprintf("%d is missing", v))
		}
	}
}

func broadcast(s Gossip, v byte) {
	s.GossipBroadcast(newSurrogateGossipData([]byte{v}))
}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"io/ioutil"

	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/worker"

	yaml "gopkg.in/yaml.v2"
)

// Schema returns the configuration file schema
func Schema() schematypes.Schema {
	transformations := []string{}
	for name := range Providers() {
		transformations = append(transformations, name)
	}
	s := schematypes.Object{
		MetaData: schematypes.MetaData{
			Title:       "Worker Configuration",
			Description: `Initial configuration and transformations to run.`,
		},
		Properties: schematypes.Properties{
			"transforms": schematypes.Array{
				MetaData: schematypes.MetaData{
					Title:       "Configuration Transformations",
					Description: "Ordered list of transformations to run on the config.",
				},
				Items: schematypes.StringEnum{
					Options: transformations,
				},
			},
			"config": worker.ConfigSchema(),
		},
		Required: []string{"config"},
	}
	return s
}

// Load configuration from YAML config object.
func Load(data []byte) (map[string]interface{}, error) {
	// Parse config file
	var config interface{}
	err := yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse YAML config, error: %s", err)
	}
	// This fixes obscurities in yaml.Unmarshal where it generates
	// map[interface{}]interface{} instead of map[string]interface{}
	// credits: https://github.com/go-yaml/yaml/issues/139#issuecomment-220072190
	config = convertToMapStr(config)

	// Extract transforms and config
	c, ok := config.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Expected top-level config vlaue to be an object")
	}
	result, ok := c["config"].(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Expected 'config' property to be an object")
	}

	// Apply transforms
	if _, ok := c["transforms"]; ok {
		providers := Providers()
		for _, t := range c["transforms"].([]string) {
			provider, ok := providers[t]
			if !ok {
				return nil, fmt.Errorf("Unknown config transformation: %s", t)
			}
			if err := provider.Transform(result); err != nil {
				return nil, fmt.Errorf("Config transformation: %s failed error: %s",
					t, err)
			}
		}
	}

	// Filter out keys that aren't in the config schema...
	// This way extra keys can be used to provide options for the
	// transformations, like "secrets" which will use the secretsBaseUrl if
	// present in the configuration.
	worker.ConfigSchema().Filter(result)

	// Validate against worker schema
	if err := worker.ConfigSchema().Validate(result); err != nil {
		return nil, err
	}

	return result, nil
}

// LoadFromFile will load configuration options from a YAML file and validate
// against the config file schema, returning an error message explaining what
// went wrong if unsuccessful.
func LoadFromFile(filename string) (interface{}, error) {
	// Read config file
	configFile, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("Failed to read config file: '%s' error: %s\n",
			filename, err)
	}

	return Load(configFile)
}

func convertToMapStr(val interface{}) interface{} {
	switch val := val.(type) {
	case []interface{}:
		r := make([]interface{}, len(val))
		for i, v := range val {
			r[i] = convertToMapStr(v)
		}
		return r
	case map[interface{}]interface{}:
		r := make(map[string]interface{})
		for k, v := range val {
			s, ok := k.(string)
			if !ok {
				s = fmt.Sprintf("%v", k)
			}
			r[s] = convertToMapStr(v)
		}
		return r
	default:
		return val
	}
}
<commit_msg>Fixed typo<commit_after>package config

import (
	"fmt"
	"io/ioutil"

	schematypes "github.com/taskcluster/go-schematypes"
	"github.com/taskcluster/taskcluster-worker/worker"

	yaml "gopkg.in/yaml.v2"
)

// Schema returns the configuration file schema
func Schema() schematypes.Schema {
	transformations := []string{}
	for name := range Providers() {
		transformations = append(transformations, name)
	}
	s := schematypes.Object{
		MetaData: schematypes.MetaData{
			Title:       "Worker Configuration",
			Description: `Initial configuration and transformations to run.`,
		},
		Properties: schematypes.Properties{
			"transforms": schematypes.Array{
				MetaData: schematypes.MetaData{
					Title:       "Configuration Transformations",
					Description: "Ordered list of transformations to run on the config.",
				},
				Items: schematypes.StringEnum{
					Options: transformations,
				},
			},
			"config": worker.ConfigSchema(),
		},
		Required: []string{"config"},
	}
	return s
}

// Load configuration from YAML config object.
func Load(data []byte) (map[string]interface{}, error) {
	// Parse config file
	var config interface{}
	err := yaml.Unmarshal(data, &config)
	if err != nil {
		return nil, fmt.Errorf("Failed to parse YAML config, error: %s", err)
	}
	// This fixes obscurities in yaml.Unmarshal where it generates
	// map[interface{}]interface{} instead of map[string]interface{}
	// credits: https://github.com/go-yaml/yaml/issues/139#issuecomment-220072190
	config = convertToMapStr(config)

	// Extract transforms and config
	c, ok := config.(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Expected top-level config value to be an object")
	}
	result, ok := c["config"].(map[string]interface{})
	if !ok {
		return nil, fmt.Errorf("Expected 'config' property to be an object")
	}

	// Apply transforms
	if _, ok := c["transforms"]; ok {
		providers := Providers()
		for _, t := range c["transforms"].([]string) {
			provider, ok := providers[t]
			if !ok {
				return nil, fmt.Errorf("Unknown config transformation: %s", t)
			}
			if err := provider.Transform(result); err != nil {
				return nil, fmt.Errorf("Config transformation: %s failed error: %s",
					t, err)
			}
		}
	}

	// Filter out keys that aren't in the config schema...
	// This way extra keys can be used to provide options for the
	// transformations, like "secrets" which will use the secretsBaseUrl if
	// present in the configuration.
	worker.ConfigSchema().Filter(result)

	// Validate against worker schema
	if err := worker.ConfigSchema().Validate(result); err != nil {
		return nil, err
	}

	return result, nil
}

// LoadFromFile will load configuration options from a YAML file and validate
// against the config file schema, returning an error message explaining what
// went wrong if unsuccessful.
func LoadFromFile(filename string) (interface{}, error) {
	// Read config file
	configFile, err := ioutil.ReadFile(filename)
	if err != nil {
		return nil, fmt.Errorf("Failed to read config file: '%s' error: %s\n",
			filename, err)
	}

	return Load(configFile)
}

func convertToMapStr(val interface{}) interface{} {
	switch val := val.(type) {
	case []interface{}:
		r := make([]interface{}, len(val))
		for i, v := range val {
			r[i] = convertToMapStr(v)
		}
		return r
	case map[interface{}]interface{}:
		r := make(map[string]interface{})
		for k, v := range val {
			s, ok := k.(string)
			if !ok {
				s = fmt.Sprintf("%v", k)
			}
			r[s] = convertToMapStr(v)
		}
		return r
	default:
		return val
	}
}
<|endoftext|>


<commit_before>// Copyright 2013 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.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"code.google.com/p/go.tools/godoc/static"
	"code.google.com/p/go.tools/present"
)

var (
	uiContent      []byte
	lessons        = make(map[string][]byte)
	lessonNotFound = fmt.Errorf("lesson not found")
)

// initTour loads tour.article and the relevant HTML templates from the given
// tour root, and renders the template to the tourContent global variable.
func initTour(root, transport string) error {
	// Make sure playground is enabled before rendering.
	present.PlayEnabled = true

	// Set up templates.
	action := filepath.Join(root, "template", "action.tmpl")
	tmpl, err := present.Template().ParseFiles(action)
	if err != nil {
		return fmt.Errorf("parse templates: %v", err)
	}

	// Init lessons.
	contentPath := filepath.Join(root, "content")
	if err := initLessons(tmpl, contentPath); err != nil {
		return fmt.Errorf("init lessons: %v", err)
	}

	// Init UI
	index := filepath.Join(root, "template", "index.tmpl")
	ui, err := template.ParseFiles(index)
	if err != nil {
		return fmt.Errorf("parse index.tmpl: %v", err)
	}
	buf := new(bytes.Buffer)

	data := struct {
		SocketAddr string
		Transport  template.JS
	}{socketAddr(), template.JS(transport)}

	if err := ui.Execute(buf, data); err != nil {
		return fmt.Errorf("render UI: %v", err)
	}
	uiContent = buf.Bytes()

	return initScript(root)
}

// initLessonss finds all the lessons in the passed directory, renders them,
// using the given template and saves the content in the lessons map.
func initLessons(tmpl *template.Template, content string) error {
	dir, err := os.Open(content)
	if err != nil {
		return err
	}
	files, err := dir.Readdirnames(0)
	if err != nil {
		return err
	}
	for _, f := range files {
		if filepath.Ext(f) != ".article" {
			continue
		}
		content, err := parseLesson(tmpl, filepath.Join(content, f))
		if err != nil {
			return fmt.Errorf("parsing %v: %v", f, err)
		}
		name := strings.TrimSuffix(f, ".article")
		lessons[name] = content
	}
	return nil
}

// File defines the JSON form of a code file in a page.
type File struct {
	Name    string
	Content string
}

// Page defines the JSON form of a tour lesson page.
type Page struct {
	Title   string
	Content string
	Files   []File
}

// Lesson defines the JSON form of a tour lesson.
type Lesson struct {
	Title       string
	Description string
	Pages       []Page
}

// parseLesson parses and returns a lesson content given its name and
// the template to render it.
func parseLesson(tmpl *template.Template, path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	doc, err := present.Parse(prepContent(f), path, 0)
	if err != nil {
		return nil, err
	}

	lesson := Lesson{
		doc.Title,
		doc.Subtitle,
		make([]Page, len(doc.Sections)),
	}

	for i, sec := range doc.Sections {
		p := &lesson.Pages[i]
		w := new(bytes.Buffer)
		if err := sec.Render(w, tmpl); err != nil {
			return nil, fmt.Errorf("render section: %v", err)
		}
		p.Title = sec.Title
		p.Content = w.String()
		codes := findPlayCode(sec)
		p.Files = make([]File, len(codes))
		for i, c := range codes {
			f := &p.Files[i]
			f.Name = c.FileName
			f.Content = string(c.Raw)
		}
	}

	w := new(bytes.Buffer)
	if err := json.NewEncoder(w).Encode(lesson); err != nil {
		return nil, fmt.Errorf("encode lesson: %v", err)
	}
	return w.Bytes(), nil
}

// findPlayCode returns a slide with all the Code elements in the given
// Elem with Play set to true.
func findPlayCode(e present.Elem) []*present.Code {
	var r []*present.Code
	switch v := e.(type) {
	case present.Code:
		if v.Play {
			r = append(r, &v)
		}
	case present.Section:
		for _, s := range v.Elem {
			r = append(r, findPlayCode(s)...)
		}
	}
	return r
}

// writeLesson writes the tour content to the provided Writer.
func writeLesson(name string, w io.Writer) error {
	if uiContent == nil {
		panic("writeLesson called before successful initTour")
	}
	if len(name) == 0 {
		return writeAllLessons(w)
	}
	l, ok := lessons[name]
	if !ok {
		return lessonNotFound
	}
	_, err := w.Write(l)
	return err
}

func writeAllLessons(w io.Writer) error {
	if _, err := fmt.Fprint(w, "{"); err != nil {
		return err
	}
	nLessons := len(lessons)
	for k, v := range lessons {
		if _, err := fmt.Fprintf(w, "%q:%s", k, v); err != nil {
			return err
		}
		nLessons--
		if nLessons != 0 {
			if _, err := fmt.Fprint(w, ","); err != nil {
				return err
			}
		}
	}
	_, err := fmt.Fprint(w, "}")
	return err
}

// renderUI writes the tour UI to the provided Writer.
func renderUI(w io.Writer) error {
	if uiContent == nil {
		panic("renderUI called before successful initTour")
	}
	_, err := w.Write(uiContent)
	return err
}

// nocode returns true if the provided Section contains
// no Code elements with Play enabled.
func nocode(s present.Section) bool {
	for _, e := range s.Elem {
		if c, ok := e.(present.Code); ok && c.Play {
			return false
		}
	}
	return true
}

// initScript concatenates all the javascript files needed to render
// the tour UI and serves the result on /script.js.
func initScript(root string) error {
	modTime := time.Now()
	b := new(bytes.Buffer)

	content, ok := static.Files["playground.js"]
	if !ok {
		return fmt.Errorf("playground.js not found in static files")
	}
	b.WriteString(content)

	// Keep this list in dependency order
	files := []string{
		"static/lib/jquery.min.js",
		"static/lib/jquery-ui.min.js",
		"static/lib/angular.min.js",
		"static/lib/codemirror/lib/codemirror.js",
		"static/lib/codemirror/addon/edit/matchbrackets.js",
		"static/lib/codemirror/mode/go/go.js",
		"static/lib/angular-ui.min.js",
		"static/js/app.js",
		"static/js/controllers.js",
		"static/js/directives.js",
		"static/js/services.js",
		"static/js/values.js",
	}

	for _, file := range files {
		f, err := ioutil.ReadFile(filepath.Join(root, file))
		if err != nil {
			return fmt.Errorf("couldn't open %v", file, err)
		}
		_, err = b.Write(f)
		if err != nil {
			return fmt.Errorf("error concatenating %v", file, err)
		}
	}

	http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-type", "application/javascript")
		// Set expiration time in one week.
		w.Header().Set("Cache-control", "max-age=604800")
		http.ServeContent(w, r, "", modTime, bytes.NewReader(b.Bytes()))
	})

	return nil
}
<commit_msg>go-tour: fix a couple of bad format strings<commit_after>// Copyright 2013 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.

package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"html/template"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path/filepath"
	"strings"
	"time"

	"code.google.com/p/go.tools/godoc/static"
	"code.google.com/p/go.tools/present"
)

var (
	uiContent      []byte
	lessons        = make(map[string][]byte)
	lessonNotFound = fmt.Errorf("lesson not found")
)

// initTour loads tour.article and the relevant HTML templates from the given
// tour root, and renders the template to the tourContent global variable.
func initTour(root, transport string) error {
	// Make sure playground is enabled before rendering.
	present.PlayEnabled = true

	// Set up templates.
	action := filepath.Join(root, "template", "action.tmpl")
	tmpl, err := present.Template().ParseFiles(action)
	if err != nil {
		return fmt.Errorf("parse templates: %v", err)
	}

	// Init lessons.
	contentPath := filepath.Join(root, "content")
	if err := initLessons(tmpl, contentPath); err != nil {
		return fmt.Errorf("init lessons: %v", err)
	}

	// Init UI
	index := filepath.Join(root, "template", "index.tmpl")
	ui, err := template.ParseFiles(index)
	if err != nil {
		return fmt.Errorf("parse index.tmpl: %v", err)
	}
	buf := new(bytes.Buffer)

	data := struct {
		SocketAddr string
		Transport  template.JS
	}{socketAddr(), template.JS(transport)}

	if err := ui.Execute(buf, data); err != nil {
		return fmt.Errorf("render UI: %v", err)
	}
	uiContent = buf.Bytes()

	return initScript(root)
}

// initLessonss finds all the lessons in the passed directory, renders them,
// using the given template and saves the content in the lessons map.
func initLessons(tmpl *template.Template, content string) error {
	dir, err := os.Open(content)
	if err != nil {
		return err
	}
	files, err := dir.Readdirnames(0)
	if err != nil {
		return err
	}
	for _, f := range files {
		if filepath.Ext(f) != ".article" {
			continue
		}
		content, err := parseLesson(tmpl, filepath.Join(content, f))
		if err != nil {
			return fmt.Errorf("parsing %v: %v", f, err)
		}
		name := strings.TrimSuffix(f, ".article")
		lessons[name] = content
	}
	return nil
}

// File defines the JSON form of a code file in a page.
type File struct {
	Name    string
	Content string
}

// Page defines the JSON form of a tour lesson page.
type Page struct {
	Title   string
	Content string
	Files   []File
}

// Lesson defines the JSON form of a tour lesson.
type Lesson struct {
	Title       string
	Description string
	Pages       []Page
}

// parseLesson parses and returns a lesson content given its name and
// the template to render it.
func parseLesson(tmpl *template.Template, path string) ([]byte, error) {
	f, err := os.Open(path)
	if err != nil {
		return nil, err
	}
	defer f.Close()
	doc, err := present.Parse(prepContent(f), path, 0)
	if err != nil {
		return nil, err
	}

	lesson := Lesson{
		doc.Title,
		doc.Subtitle,
		make([]Page, len(doc.Sections)),
	}

	for i, sec := range doc.Sections {
		p := &lesson.Pages[i]
		w := new(bytes.Buffer)
		if err := sec.Render(w, tmpl); err != nil {
			return nil, fmt.Errorf("render section: %v", err)
		}
		p.Title = sec.Title
		p.Content = w.String()
		codes := findPlayCode(sec)
		p.Files = make([]File, len(codes))
		for i, c := range codes {
			f := &p.Files[i]
			f.Name = c.FileName
			f.Content = string(c.Raw)
		}
	}

	w := new(bytes.Buffer)
	if err := json.NewEncoder(w).Encode(lesson); err != nil {
		return nil, fmt.Errorf("encode lesson: %v", err)
	}
	return w.Bytes(), nil
}

// findPlayCode returns a slide with all the Code elements in the given
// Elem with Play set to true.
func findPlayCode(e present.Elem) []*present.Code {
	var r []*present.Code
	switch v := e.(type) {
	case present.Code:
		if v.Play {
			r = append(r, &v)
		}
	case present.Section:
		for _, s := range v.Elem {
			r = append(r, findPlayCode(s)...)
		}
	}
	return r
}

// writeLesson writes the tour content to the provided Writer.
func writeLesson(name string, w io.Writer) error {
	if uiContent == nil {
		panic("writeLesson called before successful initTour")
	}
	if len(name) == 0 {
		return writeAllLessons(w)
	}
	l, ok := lessons[name]
	if !ok {
		return lessonNotFound
	}
	_, err := w.Write(l)
	return err
}

func writeAllLessons(w io.Writer) error {
	if _, err := fmt.Fprint(w, "{"); err != nil {
		return err
	}
	nLessons := len(lessons)
	for k, v := range lessons {
		if _, err := fmt.Fprintf(w, "%q:%s", k, v); err != nil {
			return err
		}
		nLessons--
		if nLessons != 0 {
			if _, err := fmt.Fprint(w, ","); err != nil {
				return err
			}
		}
	}
	_, err := fmt.Fprint(w, "}")
	return err
}

// renderUI writes the tour UI to the provided Writer.
func renderUI(w io.Writer) error {
	if uiContent == nil {
		panic("renderUI called before successful initTour")
	}
	_, err := w.Write(uiContent)
	return err
}

// nocode returns true if the provided Section contains
// no Code elements with Play enabled.
func nocode(s present.Section) bool {
	for _, e := range s.Elem {
		if c, ok := e.(present.Code); ok && c.Play {
			return false
		}
	}
	return true
}

// initScript concatenates all the javascript files needed to render
// the tour UI and serves the result on /script.js.
func initScript(root string) error {
	modTime := time.Now()
	b := new(bytes.Buffer)

	content, ok := static.Files["playground.js"]
	if !ok {
		return fmt.Errorf("playground.js not found in static files")
	}
	b.WriteString(content)

	// Keep this list in dependency order
	files := []string{
		"static/lib/jquery.min.js",
		"static/lib/jquery-ui.min.js",
		"static/lib/angular.min.js",
		"static/lib/codemirror/lib/codemirror.js",
		"static/lib/codemirror/addon/edit/matchbrackets.js",
		"static/lib/codemirror/mode/go/go.js",
		"static/lib/angular-ui.min.js",
		"static/js/app.js",
		"static/js/controllers.js",
		"static/js/directives.js",
		"static/js/services.js",
		"static/js/values.js",
	}

	for _, file := range files {
		f, err := ioutil.ReadFile(filepath.Join(root, file))
		if err != nil {
			return fmt.Errorf("couldn't open %v: %v", file, err)
		}
		_, err = b.Write(f)
		if err != nil {
			return fmt.Errorf("error concatenating %v: %v", file, err)
		}
	}

	http.HandleFunc("/script.js", func(w http.ResponseWriter, r *http.Request) {
		w.Header().Set("Content-type", "application/javascript")
		// Set expiration time in one week.
		w.Header().Set("Cache-control", "max-age=604800")
		http.ServeContent(w, r, "", modTime, bytes.NewReader(b.Bytes()))
	})

	return nil
}
<|endoftext|>