<commit_before>package grab

import (
	"context"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"golang.org/x/time/rate"
)

// testRateLimiter is a naive rate limiter that limits throughput to r tokens
// per second. The total number of tokens issued is tracked as n.
type testRateLimiter struct {
	r, n int
}

func (c *testRateLimiter) WaitN(ctx context.Context, n int) (err error) {
	c.n += n
	time.Sleep(
		time.Duration(1.00 / float64(c.r) * float64(n) * float64(time.Second)))
	return
}

func TestRateLimiter(t *testing.T) {
	// download a 128 byte file, 8 bytes at a time, with a naive 512bps limiter
	// should take > 250ms
	filesize := 128
	filename := ".testRateLimiter"
	defer os.Remove(filename)

	req, err := NewRequest(filename, fmt.Sprintf("%s?size=%d", ts.URL, filesize))
	if err != nil {
		t.Fatal(err)
	}

	// ensure multiple trips to the rate limiter by downloading 8 bytes at a time
	req.BufferSize = 8

	// limit to 128bps
	lim := &testRateLimiter{r: 512}
	req.RateLimiter = lim

	resp := DefaultClient.Do(req)
	if err = resp.Err(); err != nil {
		t.Fatal(err)
	}
	testComplete(t, resp)
	if lim.n != filesize {
		t.Errorf("expected %d bytes to pass through limiter, got %d", filesize, lim.n)
	}
	if resp.Duration().Seconds() < 0.25 {
		// BUG: this test can pass if the transfer was slow for unrelated reasons
		t.Errorf("expected transfer to take >250ms, took %v", resp.Duration())
	}
}

func ExampleRequest_RateLimiter() {
	req, _ := NewRequest("", "http://www.golang-book.com/public/pdf/gobook.pdf")

	// Attach a rate limiter, using the token bucket implementation from
	// golang.org/x/time/rate. Limit to 1Mbps with burst up to 2Mbps.
	req.RateLimiter = rate.NewLimiter(1048576, 1048576)

	resp := DefaultClient.Do(req)
	if err := resp.Err(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Fixed typos<commit_after>package grab

import (
	"context"
	"fmt"
	"log"
	"os"
	"testing"
	"time"

	"golang.org/x/time/rate"
)

// testRateLimiter is a naive rate limiter that limits throughput to r tokens
// per second. The total number of tokens issued is tracked as n.
type testRateLimiter struct {
	r, n int
}

func (c *testRateLimiter) WaitN(ctx context.Context, n int) (err error) {
	c.n += n
	time.Sleep(
		time.Duration(1.00 / float64(c.r) * float64(n) * float64(time.Second)))
	return
}

func TestRateLimiter(t *testing.T) {
	// download a 128 byte file, 8 bytes at a time, with a naive 512bps limiter
	// should take > 250ms
	filesize := 128
	filename := ".testRateLimiter"
	defer os.Remove(filename)

	req, err := NewRequest(filename, fmt.Sprintf("%s?size=%d", ts.URL, filesize))
	if err != nil {
		t.Fatal(err)
	}

	// ensure multiple trips to the rate limiter by downloading 8 bytes at a time
	req.BufferSize = 8

	// limit to 512bps
	lim := &testRateLimiter{r: 512}
	req.RateLimiter = lim

	resp := DefaultClient.Do(req)
	if err = resp.Err(); err != nil {
		t.Fatal(err)
	}
	testComplete(t, resp)
	if lim.n != filesize {
		t.Errorf("expected %d bytes to pass through limiter, got %d", filesize, lim.n)
	}
	if resp.Duration().Seconds() < 0.25 {
		// BUG: this test can pass if the transfer was slow for unrelated reasons
		t.Errorf("expected transfer to take >250ms, took %v", resp.Duration())
	}
}

func ExampleRateLimiter() {
	req, _ := NewRequest("", "http://www.golang-book.com/public/pdf/gobook.pdf")

	// Attach a rate limiter, using the token bucket implementation from
	// golang.org/x/time/rate. Limit to 1Mbps with burst up to 2Mbps.
	req.RateLimiter = rate.NewLimiter(1048576, 2097152)

	resp := DefaultClient.Do(req)
	if err := resp.Err(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2015 The Kubernetes 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 kubectl

import (
	"reflect"
	"testing"

	"k8s.io/kubernetes/pkg/api"
)

func TestNamespaceGenerate(t *testing.T) {
	tests := []struct {
		params    map[string]interface{}
		expected  *api.Namespace
		expectErr bool
		index     int
	}{
		{
			params: map[string]interface{}{
				"name": "foo",
			},
			expected: &api.Namespace{
				ObjectMeta: api.ObjectMeta{
					Name: "foo",
				},
			},
			expectErr: false,
		},
		{
			params:    map[string]interface{}{},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"name": 1,
			},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"name": nil,
			},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"name_wrong_key": "some_value",
			},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"NAME": "some_value",
			},
			expectErr: true,
		},
	}
	generator := NamespaceGeneratorV1{}
	for index, test := range tests {
		obj, err := generator.Generate(test.params)
		switch {
		case test.expectErr && err != nil:
			continue // loop, since there's no output to check
		case test.expectErr && err == nil:
			t.Errorf("%v: expected error and didn't get one", index)
			continue // loop, no expected output object
		case !test.expectErr && err != nil:
			t.Errorf("%v: expected error and didn't get one", index)
			continue // loop, no output object
		case !test.expectErr && err == nil:
			// do nothing and drop through
		}
		if !reflect.DeepEqual(obj.(*api.Namespace), test.expected) {
			t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Namespace))
		}
	}
}
<commit_msg>Fix namespace_test error message<commit_after>/*
Copyright 2015 The Kubernetes 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 kubectl

import (
	"reflect"
	"testing"

	"k8s.io/kubernetes/pkg/api"
)

func TestNamespaceGenerate(t *testing.T) {
	tests := []struct {
		params    map[string]interface{}
		expected  *api.Namespace
		expectErr bool
		index     int
	}{
		{
			params: map[string]interface{}{
				"name": "foo",
			},
			expected: &api.Namespace{
				ObjectMeta: api.ObjectMeta{
					Name: "foo",
				},
			},
			expectErr: false,
		},
		{
			params:    map[string]interface{}{},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"name": 1,
			},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"name": nil,
			},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"name_wrong_key": "some_value",
			},
			expectErr: true,
		},
		{
			params: map[string]interface{}{
				"NAME": "some_value",
			},
			expectErr: true,
		},
	}
	generator := NamespaceGeneratorV1{}
	for index, test := range tests {
		obj, err := generator.Generate(test.params)
		switch {
		case test.expectErr && err != nil:
			continue // loop, since there's no output to check
		case test.expectErr && err == nil:
			t.Errorf("%v: expected error and didn't get one", index)
			continue // loop, no expected output object
		case !test.expectErr && err != nil:
			t.Errorf("%v: unexpected error %v", index, err)
			continue // loop, no output object
		case !test.expectErr && err == nil:
			// do nothing and drop through
		}
		if !reflect.DeepEqual(obj.(*api.Namespace), test.expected) {
			t.Errorf("\nexpected:\n%#v\nsaw:\n%#v", test.expected, obj.(*api.Namespace))
		}
	}
}
<|endoftext|>


<commit_before>package terminal

import (
	"bytes"
	"errors"
	"fmt"
	"os/exec"
	"strings"
)

const (
	sessionPrefix     = "koding"
	defaultScreenPath = "/usr/bin/screen"
)

type Command struct {
	// Name is used for starting the terminal instance, it's the program path
	// usually
	Name string

	// Args is passed to the program name
	Args []string

	// Session id used for reconnections, used by screen or tmux
	Session string
}

var (
	ErrNoSession      = errors.New("ErrNoSession")
	ErrInvalidSession = errors.New("ErrInvalidSession")
)

// newCmd returns a new command instance that is used to start the terminal.
// The command line is created differently based on the incoming mode.
func newCommand(mode, session, username string) (*Command, error) {
	// let's assume by default its Screen
	name := defaultScreenPath
	args := []string{"-e^Bb", "-s", "/bin/bash", "-S"}

	switch mode {
	case "shared", "resume":
		if session == "" {
			return nil, errors.New("session is needed for 'shared' or 'resume' mode")
		}

		if !sessionExists(session, username) {
			return nil, ErrNoSession
		}

		args = append(args, sessionPrefix+"."+session)
		if mode == "shared" {
			args = append(args, "-x") // multiuser mode
		} else if mode == "resume" {
			args = append(args, "-raAd") // resume
		}
	case "noscreen":
		name = "/bin/bash"
		args = []string{}
	case "create":
		session = randomString()
		args = append(args, sessionPrefix+"."+session)
	default:
		return nil, fmt.Errorf("mode '%s' is unknown. Valid modes are:  [shared|noscreen|resume|create]", mode)
	}

	c := &Command{
		Name:    name,
		Args:    args,
		Session: session,
	}

	return c, nil
}

// screenSessions returns a list of sessions that belongs to the given
// username.  The sessions are in the form of ["k7sdjv12344", "askIj12sas12",
// ...]
// TODO: socket directory is different under darwin, it will not work probably
func screenSessions(username string) []string {
	// Do not include dead sessions in our result
	exec.Command(defaultScreenPath, "-wipe").Run()

	// We need to use ls here, because /var/run/screen mount is only
	// visible from inside of container. Errors are ignored.
	out, _ := exec.Command("ls", "/var/run/screen/S-"+username).Output()
	shellOut := string(bytes.TrimSpace(out))
	if shellOut == "" {
		return []string{}
	}

	names := strings.Split(shellOut, "\n")
	sessions := make([]string, len(names))

	prefix := sessionPrefix + "."
	for i, name := range names {
		segments := strings.SplitN(name, ".", 2)
		sessions[i] = strings.TrimPrefix(segments[1], prefix)
	}

	return sessions
}

// screenExists checks whether the given session exists in the running list of
// screen sessions.
func sessionExists(session, username string) bool {
	for _, s := range screenSessions(username) {
		if s == session {
			return true
		}
	}

	return false
}

// killSession kills the given SessionID
func killSession(session string) error {
	out, err := exec.Command(defaultScreenPath, "-X", "-S", sessionPrefix+"."+session, "kill").Output()
	if err != nil {
		return commandError("screen kill failed", err, out)
	}

	return nil
}

func commandError(message string, err error, out []byte) error {
	return fmt.Errorf("%s\n%s\n%s", message, err.Error(), string(out))
}
<commit_msg>klient: support users default shell<commit_after>package terminal

import (
	"bytes"
	"errors"
	"fmt"
	"log"
	"os/exec"
	"strings"

	"github.com/willdonnelly/passwd"
)

const (
	sessionPrefix     = "koding"
	defaultScreenPath = "/usr/bin/screen"
)

type Command struct {
	// Name is used for starting the terminal instance, it's the program path
	// usually
	Name string

	// Args is passed to the program name
	Args []string

	// Session id used for reconnections, used by screen or tmux
	Session string
}

var (
	ErrNoSession      = errors.New("ErrNoSession")
	ErrInvalidSession = errors.New("ErrInvalidSession")
)

func getDefaultShell(username string) string {
	fallbackShell := "/bin/bash"
	entries, err := passwd.Parse()
	if err != nil {
		log.Println("terminal: couldn't get default shell ", err)
		return fallbackShell
	}

	user, ok := entries[username]
	if !ok {
		log.Printf("terminal: no entry for username '%s'", username)
		return fallbackShell
	}

	if user.Shell == "" {
		log.Println("terminal: shell entry is empty")
		return fallbackShell
	}

	return user.Shell
}

// newCmd returns a new command instance that is used to start the terminal.
// The command line is created differently based on the incoming mode.
func newCommand(mode, session, username string) (*Command, error) {
	// let's assume by default its Screen
	name := defaultScreenPath
	defaultShell := getDefaultShell(username)
	args := []string{"-e^Bb", "-s", defaultShell, "-S"}

	switch mode {
	case "shared", "resume":
		if session == "" {
			return nil, errors.New("session is needed for 'shared' or 'resume' mode")
		}

		if !sessionExists(session, username) {
			return nil, ErrNoSession
		}

		args = append(args, sessionPrefix+"."+session)
		if mode == "shared" {
			args = append(args, "-x") // multiuser mode
		} else if mode == "resume" {
			args = append(args, "-raAd") // resume
		}
	case "noscreen":
		name = defaultShell
		args = []string{}
	case "create":
		session = randomString()
		args = append(args, sessionPrefix+"."+session)
	default:
		return nil, fmt.Errorf("mode '%s' is unknown. Valid modes are:  [shared|noscreen|resume|create]", mode)
	}

	c := &Command{
		Name:    name,
		Args:    args,
		Session: session,
	}

	return c, nil
}

// screenSessions returns a list of sessions that belongs to the given
// username.  The sessions are in the form of ["k7sdjv12344", "askIj12sas12",
// ...]
// TODO: socket directory is different under darwin, it will not work probably
func screenSessions(username string) []string {
	// Do not include dead sessions in our result
	exec.Command(defaultScreenPath, "-wipe").Run()

	// We need to use ls here, because /var/run/screen mount is only
	// visible from inside of container. Errors are ignored.
	out, _ := exec.Command("ls", "/var/run/screen/S-"+username).Output()
	shellOut := string(bytes.TrimSpace(out))
	if shellOut == "" {
		return []string{}
	}

	names := strings.Split(shellOut, "\n")
	sessions := make([]string, len(names))

	prefix := sessionPrefix + "."
	for i, name := range names {
		segments := strings.SplitN(name, ".", 2)
		sessions[i] = strings.TrimPrefix(segments[1], prefix)
	}

	return sessions
}

// screenExists checks whether the given session exists in the running list of
// screen sessions.
func sessionExists(session, username string) bool {
	for _, s := range screenSessions(username) {
		if s == session {
			return true
		}
	}

	return false
}

// killSession kills the given SessionID
func killSession(session string) error {
	out, err := exec.Command(defaultScreenPath, "-X", "-S", sessionPrefix+"."+session, "kill").Output()
	if err != nil {
		return commandError("screen kill failed", err, out)
	}

	return nil
}

func commandError(message string, err error, out []byte) error {
	return fmt.Errorf("%s\n%s\n%s", message, err.Error(), string(out))
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes 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 container

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/containerd/continuity"
	"github.com/pkg/errors"
	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

// The container state machine in the CRI plugin:
//
//                         +              +
//                         |              |
//                         | Create       | Load
//                         |              |
//                    +----v----+         |
//                    |         |         |
//                    | CREATED <---------+-----------+
//                    |         |         |           |
//                    +----+-----         |           |
//                         |              |           |
//                         | Start        |           |
//                         |              |           |
//                    +----v----+         |           |
//   Exec    +--------+         |         |           |
//  Attach   |        | RUNNING <---------+           |
// LogReopen +-------->         |         |           |
//                    +----+----+         |           |
//                         |              |           |
//                         | Stop/Exit    |           |
//                         |              |           |
//                    +----v----+         |           |
//                    |         <---------+      +----v----+
//                    |  EXITED |                |         |
//                    |         <----------------+ UNKNOWN |
//                    +----+----+       Stop     |         |
//                         |                     +---------+
//                         | Remove
//                         v
//                      DELETED

// statusVersion is current version of container status.
const statusVersion = "v1" // nolint

// versionedStatus is the internal used versioned container status.
// nolint
type versionedStatus struct {
	// Version indicates the version of the versioned container status.
	Version string
	Status
}

// Status is the status of a container.
type Status struct {
	// Pid is the init process id of the container.
	Pid uint32
	// CreatedAt is the created timestamp.
	CreatedAt int64
	// StartedAt is the started timestamp.
	StartedAt int64
	// FinishedAt is the finished timestamp.
	FinishedAt int64
	// ExitCode is the container exit code.
	ExitCode int32
	// CamelCase string explaining why container is in its current state.
	Reason string
	// Human-readable message indicating details about why container is in its
	// current state.
	Message string
	// Starting indicates that the container is in starting state.
	// This field doesn't need to be checkpointed.
	// TODO(now): Add unit test.
	Starting bool `json:"-"`
	// Removing indicates that the container is in removing state.
	// This field doesn't need to be checkpointed.
	Removing bool `json:"-"`
}

// State returns current state of the container based on the container status.
func (s Status) State() runtime.ContainerState {
	if s.FinishedAt != 0 {
		return runtime.ContainerState_CONTAINER_EXITED
	}
	if s.StartedAt != 0 {
		return runtime.ContainerState_CONTAINER_RUNNING
	}
	if s.CreatedAt != 0 {
		return runtime.ContainerState_CONTAINER_CREATED
	}
	return runtime.ContainerState_CONTAINER_UNKNOWN
}

// encode encodes Status into bytes in json format.
func (s *Status) encode() ([]byte, error) {
	return json.Marshal(&versionedStatus{
		Version: statusVersion,
		Status:  *s,
	})
}

// decode decodes Status from bytes.
func (s *Status) decode(data []byte) error {
	versioned := &versionedStatus{}
	if err := json.Unmarshal(data, versioned); err != nil {
		return err
	}
	// Handle old version after upgrade.
	switch versioned.Version {
	case statusVersion:
		*s = versioned.Status
		return nil
	}
	return errors.New("unsupported version")
}

// UpdateFunc is function used to update the container status. If there
// is an error, the update will be rolled back.
type UpdateFunc func(Status) (Status, error)

// StatusStorage manages the container status with a storage backend.
type StatusStorage interface {
	// Get a container status.
	Get() Status
	// UpdateSync updates the container status and the on disk checkpoint.
	// Note that the update MUST be applied in one transaction.
	UpdateSync(UpdateFunc) error
	// Update the container status. Note that the update MUST be applied
	// in one transaction.
	Update(UpdateFunc) error
	// Delete the container status.
	// Note:
	// * Delete should be idempotent.
	// * The status must be deleted in one trasaction.
	Delete() error
}

// StoreStatus creates the storage containing the passed in container status with the
// specified id.
// The status MUST be created in one transaction.
func StoreStatus(root, id string, status Status) (StatusStorage, error) {
	data, err := status.encode()
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode status")
	}
	path := filepath.Join(root, "status")
	if err := continuity.AtomicWriteFile(path, data, 0600); err != nil {
		return nil, errors.Wrapf(err, "failed to checkpoint status to %q", path)
	}
	return &statusStorage{
		path:   path,
		status: status,
	}, nil
}

// LoadStatus loads container status from checkpoint. There shouldn't be threads
// writing to the file during loading.
func LoadStatus(root, id string) (Status, error) {
	path := filepath.Join(root, "status")
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return Status{}, errors.Wrapf(err, "failed to read status from %q", path)
	}
	var status Status
	if err := status.decode(data); err != nil {
		return Status{}, errors.Wrapf(err, "failed to decode status %q", data)
	}
	return status, nil
}

type statusStorage struct {
	sync.RWMutex
	path   string
	status Status
}

// Get a copy of container status.
func (s *statusStorage) Get() Status {
	s.RLock()
	defer s.RUnlock()
	return s.status
}

// UpdateSync updates the container status and the on disk checkpoint.
func (s *statusStorage) UpdateSync(u UpdateFunc) error {
	s.Lock()
	defer s.Unlock()
	newStatus, err := u(s.status)
	if err != nil {
		return err
	}
	data, err := newStatus.encode()
	if err != nil {
		return errors.Wrap(err, "failed to encode status")
	}
	if err := continuity.AtomicWriteFile(s.path, data, 0600); err != nil {
		return errors.Wrapf(err, "failed to checkpoint status to %q", s.path)
	}
	s.status = newStatus
	return nil
}

// Update the container status.
func (s *statusStorage) Update(u UpdateFunc) error {
	s.Lock()
	defer s.Unlock()
	newStatus, err := u(s.status)
	if err != nil {
		return err
	}
	s.status = newStatus
	return nil
}

// Delete deletes the container status from disk atomically.
func (s *statusStorage) Delete() error {
	temp := filepath.Dir(s.path) + ".del-" + filepath.Base(s.path)
	if err := os.Rename(s.path, temp); err != nil && !os.IsNotExist(err) {
		return err
	}
	return os.RemoveAll(temp)
}
<commit_msg>Remove an unused TODO.<commit_after>/*
Copyright 2017 The Kubernetes 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 container

import (
	"encoding/json"
	"io/ioutil"
	"os"
	"path/filepath"
	"sync"

	"github.com/containerd/continuity"
	"github.com/pkg/errors"
	runtime "k8s.io/kubernetes/pkg/kubelet/apis/cri/runtime/v1alpha2"
)

// The container state machine in the CRI plugin:
//
//                         +              +
//                         |              |
//                         | Create       | Load
//                         |              |
//                    +----v----+         |
//                    |         |         |
//                    | CREATED <---------+-----------+
//                    |         |         |           |
//                    +----+-----         |           |
//                         |              |           |
//                         | Start        |           |
//                         |              |           |
//                    +----v----+         |           |
//   Exec    +--------+         |         |           |
//  Attach   |        | RUNNING <---------+           |
// LogReopen +-------->         |         |           |
//                    +----+----+         |           |
//                         |              |           |
//                         | Stop/Exit    |           |
//                         |              |           |
//                    +----v----+         |           |
//                    |         <---------+      +----v----+
//                    |  EXITED |                |         |
//                    |         <----------------+ UNKNOWN |
//                    +----+----+       Stop     |         |
//                         |                     +---------+
//                         | Remove
//                         v
//                      DELETED

// statusVersion is current version of container status.
const statusVersion = "v1" // nolint

// versionedStatus is the internal used versioned container status.
// nolint
type versionedStatus struct {
	// Version indicates the version of the versioned container status.
	Version string
	Status
}

// Status is the status of a container.
type Status struct {
	// Pid is the init process id of the container.
	Pid uint32
	// CreatedAt is the created timestamp.
	CreatedAt int64
	// StartedAt is the started timestamp.
	StartedAt int64
	// FinishedAt is the finished timestamp.
	FinishedAt int64
	// ExitCode is the container exit code.
	ExitCode int32
	// CamelCase string explaining why container is in its current state.
	Reason string
	// Human-readable message indicating details about why container is in its
	// current state.
	Message string
	// Starting indicates that the container is in starting state.
	// This field doesn't need to be checkpointed.
	Starting bool `json:"-"`
	// Removing indicates that the container is in removing state.
	// This field doesn't need to be checkpointed.
	Removing bool `json:"-"`
}

// State returns current state of the container based on the container status.
func (s Status) State() runtime.ContainerState {
	if s.FinishedAt != 0 {
		return runtime.ContainerState_CONTAINER_EXITED
	}
	if s.StartedAt != 0 {
		return runtime.ContainerState_CONTAINER_RUNNING
	}
	if s.CreatedAt != 0 {
		return runtime.ContainerState_CONTAINER_CREATED
	}
	return runtime.ContainerState_CONTAINER_UNKNOWN
}

// encode encodes Status into bytes in json format.
func (s *Status) encode() ([]byte, error) {
	return json.Marshal(&versionedStatus{
		Version: statusVersion,
		Status:  *s,
	})
}

// decode decodes Status from bytes.
func (s *Status) decode(data []byte) error {
	versioned := &versionedStatus{}
	if err := json.Unmarshal(data, versioned); err != nil {
		return err
	}
	// Handle old version after upgrade.
	switch versioned.Version {
	case statusVersion:
		*s = versioned.Status
		return nil
	}
	return errors.New("unsupported version")
}

// UpdateFunc is function used to update the container status. If there
// is an error, the update will be rolled back.
type UpdateFunc func(Status) (Status, error)

// StatusStorage manages the container status with a storage backend.
type StatusStorage interface {
	// Get a container status.
	Get() Status
	// UpdateSync updates the container status and the on disk checkpoint.
	// Note that the update MUST be applied in one transaction.
	UpdateSync(UpdateFunc) error
	// Update the container status. Note that the update MUST be applied
	// in one transaction.
	Update(UpdateFunc) error
	// Delete the container status.
	// Note:
	// * Delete should be idempotent.
	// * The status must be deleted in one trasaction.
	Delete() error
}

// StoreStatus creates the storage containing the passed in container status with the
// specified id.
// The status MUST be created in one transaction.
func StoreStatus(root, id string, status Status) (StatusStorage, error) {
	data, err := status.encode()
	if err != nil {
		return nil, errors.Wrap(err, "failed to encode status")
	}
	path := filepath.Join(root, "status")
	if err := continuity.AtomicWriteFile(path, data, 0600); err != nil {
		return nil, errors.Wrapf(err, "failed to checkpoint status to %q", path)
	}
	return &statusStorage{
		path:   path,
		status: status,
	}, nil
}

// LoadStatus loads container status from checkpoint. There shouldn't be threads
// writing to the file during loading.
func LoadStatus(root, id string) (Status, error) {
	path := filepath.Join(root, "status")
	data, err := ioutil.ReadFile(path)
	if err != nil {
		return Status{}, errors.Wrapf(err, "failed to read status from %q", path)
	}
	var status Status
	if err := status.decode(data); err != nil {
		return Status{}, errors.Wrapf(err, "failed to decode status %q", data)
	}
	return status, nil
}

type statusStorage struct {
	sync.RWMutex
	path   string
	status Status
}

// Get a copy of container status.
func (s *statusStorage) Get() Status {
	s.RLock()
	defer s.RUnlock()
	return s.status
}

// UpdateSync updates the container status and the on disk checkpoint.
func (s *statusStorage) UpdateSync(u UpdateFunc) error {
	s.Lock()
	defer s.Unlock()
	newStatus, err := u(s.status)
	if err != nil {
		return err
	}
	data, err := newStatus.encode()
	if err != nil {
		return errors.Wrap(err, "failed to encode status")
	}
	if err := continuity.AtomicWriteFile(s.path, data, 0600); err != nil {
		return errors.Wrapf(err, "failed to checkpoint status to %q", s.path)
	}
	s.status = newStatus
	return nil
}

// Update the container status.
func (s *statusStorage) Update(u UpdateFunc) error {
	s.Lock()
	defer s.Unlock()
	newStatus, err := u(s.status)
	if err != nil {
		return err
	}
	s.status = newStatus
	return nil
}

// Delete deletes the container status from disk atomically.
func (s *statusStorage) Delete() error {
	temp := filepath.Dir(s.path) + ".del-" + filepath.Base(s.path)
	if err := os.Rename(s.path, temp); err != nil && !os.IsNotExist(err) {
		return err
	}
	return os.RemoveAll(temp)
}
<|endoftext|>


<commit_before>package terse

import (
	"bytes"
	"html/template"
	"testing"

	"github.com/acsellers/multitemplate"
)

func TestParse(t *testing.T) {
	for _, test := range parseTests {
		tmpl := multitemplate.New("terse").Funcs(test.Funcs)
		var e error
		if len(test.Sources) == 0 {
			tmpl, e = tmpl.Parse("parse", test.Content, "terse")
			if e != nil {
				t.Logf("In test %s\n", test.Name)
				t.Error("Parse Error:", e)
				continue
			}
		} else {
			for tn, tc := range test.Sources {
				tmpl, e = tmpl.Parse(tn, tc, "terse")
				if e != nil {
					t.Logf("In test %s\n", test.Name)
					t.Error("Parse Error:", e)
					continue
				}
			}
		}
		b := &bytes.Buffer{}
		if test.Template == "" {
			e = tmpl.Execute(b, test.Data)
		} else {
			e = tmpl.ExecuteTemplate(b, test.Template, test.Data)
		}
		if e != nil {
			t.Logf("In test %s\n", test.Name)
			t.Error("Execute Error:", e)
		}
		if b.String() != test.Expected {
			t.Logf("In test %s\n", test.Name)
			t.Errorf("Result Error, Expected:`%s`\nReceived:`%s`", test.Expected, b.String())
		}
	}
}

var parseTests = []parseTest{
	parseTest{
		Name: "Blank Template",
	},
	parseTest{
		Name:     "Doctype Template Blank",
		Content:  "!!",
		Expected: "<!DOCTYPE html>",
	},
	parseTest{
		Name:     "Doctype Template Named",
		Content:  "!! Strict",
		Expected: `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">`,
	},
	parseTest{
		Name:     "Text in template",
		Content:  "blah blah",
		Expected: "blah blah",
	},
	parseTest{
		Name:     "Text in template",
		Content:  "blah blah\nnerr",
		Expected: "blah blah\nnerr",
	},
	parseTest{
		Name:     "Text in template",
		Content:  "bleh\n  wat",
		Expected: "bleh\n  wat",
	},
	parseTest{
		Name:     "Comment in template",
		Content:  "blah\n// don't show",
		Expected: "blah",
	},
	parseTest{
		Name:     "Nested Comment",
		Content:  "blah\n  // don't show",
		Expected: "blah",
	},
	parseTest{
		Name:     "Triple Nested Text",
		Content:  "first\n  second\n    third\n  fourth",
		Expected: "first\n  second\n    third\n  fourth",
	},
	parseTest{
		Name:     "Quadruple Nested Text",
		Content:  "First\n  Second\n    Third\n      Fourth",
		Expected: "First\n  Second\n    Third\n      Fourth",
	},
	parseTest{
		Name:     "If Statement",
		Content:  "?true\n  show",
		Expected: "show",
	},
	parseTest{
		Name:     "If/Else Statement (False)",
		Content:  "?false\n  no\n!?\n  yes",
		Expected: "yes",
	},
	parseTest{
		Name:     "If/Else Statement (True)",
		Content:  "?true\n  yes\n!?\n  no",
		Expected: "yes",
	},
	parseTest{
		Name:     "Range Statement (1 item)",
		Content:  "&.\n  wat",
		Expected: "\nwat",
		Data:     []string{"1"},
	},
	parseTest{
		Name:     "Range Statement (2 items)",
		Content:  "&.\n  wat",
		Expected: "\nwat\nwat",
		Data:     []string{"1", "2"},
	},
	parseTest{
		Name:     "Range Statement (0 items)",
		Content:  "&.\n  wat",
		Expected: "",
		Data:     []string{},
	},
	parseTest{
		Name:     "Range/Else Statement (0 items)",
		Content:  "&.\n  wat\n!&\n  no",
		Expected: "no",
		Data:     []string{},
	},
	parseTest{
		Name:     "Range/Else Statement (2 items)",
		Content:  "&.\n  wat\n!&\n  no",
		Expected: "\nwat\nwat",
		Data:     []string{"1", "2"},
	},
	parseTest{
		Name:     "Range Statement",
		Content:  "&.:$element:$index\n  li= $element",
		Expected: "\n
a\n
\n
i\n
\n
b\n
",
		Data:     []string{"a", "i", "b"},
	},
	parseTest{
		Name:     "Verbatim Statement",
		Content:  "/ $9@(#*$now",
		Expected: "$9@(#*$now",
	},
	parseTest{
		Name:     "Verbatim Statement with Nested lines",
		Content:  "/ $now\n  ?wat\n    do",
		Expected: "$now\n  ?wat\n    do",
	},
	parseTest{
		Name:     "Simple Exec",
		Content:  "= print 123",
		Expected: "123",
	},
	parseTest{
		Name:     "Continued Exec",
		Content:  "= print\n  /= 123\n  wat",
		Expected: "123\n  wat",
	},
	parseTest{
		Name:     "Auto-Close Exec",
		Content:  "= div \n  blah",
		Expected: "
\n  blah\n
",
		Funcs: template.FuncMap{
			"div": func() template.HTML {
				return "
"
			},
			"end_div": func() template.HTML {
				return "
"
			},
		},
	},
	parseTest{
		Name: "Auto-Close Exec w/ Args",
		Content: `= tag "div"
  blah`,
		Expected: "
\n  blah\n
",
		Funcs: template.FuncMap{
			"tag": func(s string) template.HTML {
				return "<" + template.HTML(s) + ">"
			},
			"end_tag": func(s string) template.HTML {
				return "</" + template.HTML(s) + ">"
			},
		},
	},
	parseTest{
		Name: "Child Template",
		Sources: map[string]string{
			"main":  "[block]\n  1234",
			"child": "child\n  4321",
		},
		Template: "child",
		Expected: "child\n  4321",
	},
	parseTest{
		Name: "Block",
		Sources: map[string]string{
			"main":  "[block]\n  12345",
			"child": "child\n  54321",
		},
		Template: "main",
		Expected: "12345",
	},
	parseTest{
		Name: "Inherited Block",
		Sources: map[string]string{
			"main":  "[block]\n  12345",
			"child": "@@main\n[block]\n  54321",
		},
		Template: "child",
		Expected: "\n54321",
	},
	parseTest{
		Name: "Define Block",
		Sources: map[string]string{
			"main":  "[block]\n  12345\n678",
			"child": "@@main\n^block]\n  54321",
		},
		Template: "child",
		Expected: "\n54321\n678",
	},
	parseTest{
		Name: "Yield Block",
		Sources: map[string]string{
			"main":  "@block\n123",
			"child": "@@main\n^block]\n  54321",
		},
		Template: "child",
		Expected: "\n54321\n123",
	},
	parseTest{
		Name:     "Fake Filter Block",
		Content:  ":wat",
		Expected: ":wat",
	},
	parseTest{
		Name:     "Fake Nested Filter Block",
		Content:  ":wat\n  two",
		Expected: ":wat\n  two",
	},
	parseTest{
		Name:     "Nested Filter Block",
		Content:  ":plain\n  two",
		Expected: "two",
	},
	parseTest{
		Name:     "Nested Filter Block",
		Content:  ":plain\n  two",
		Expected: "two",
	},
	parseTest{
		Name:     "Empty tag",
		Content:  "img",
		Expected: "",
	},
	parseTest{
		Name:     "Empty tag",
		Content:  "span\n  name",
		Expected: "name",
	},
	parseTest{
		Name:     "Class div",
		Content:  ".wat\n  here",
		Expected: "
here
",
	},
	parseTest{
		Name:     "Passthrough HTML",
		Content:  "h1\n  no",
		Expected: "
no
",
	},
	parseTest{
		Name:     "Tag with content",
		Content:  "h1 Here",
		Expected: "
Here
",
	},
	parseTest{
		Name:     "Interpolated content",
		Content:  "Welcome {{ . }}",
		Expected: "Welcome Gopher",
		Data:     "Gopher",
	},
	parseTest{
		Name:     "With line",
		Content:  ">.Username\n  = .",
		Expected: "Gopher",
		Data:     map[string]string{"Username": "Gopher"},
	},
	parseTest{
		Name:     "With line using variable",
		Content:  ">.Username:$name\n  = $name",
		Expected: "Andrew",
		Data:     map[string]string{"Username": "Andrew"},
	},
	parseTest{
		Name:     "With line",
		Content:  ">.NotExist\n  = .\n!>\n  Other animal",
		Expected: "Other animal",
		Data:     map[string]string{"Username": "Gopher"},
	},
	parseTest{
		Name:     "Tag with attribute",
		Content:  `input type="checkbox"`,
		Expected: `<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with attribute and single quotes",
		Content:  `input type='checkbox'`,
		Expected: `<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with dot attribute",
		Content:  `input type=.Type`,
		Expected: `<input type="checkbox" />`,
		Data:     map[string]string{"Type": "checkbox"},
	},
	parseTest{
		Name: "Tag with variable attribute",
		Content: `= $t := "checkbox"
input type=$t`,
		Expected: `
<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with parentheses attribute",
		Content:  `input type=(print "checkbox")`,
		Expected: `<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with function attribute",
		Content:  `input type=checker`,
		Expected: `<input type="checkbox" />`,
		Funcs: template.FuncMap{
			"checker": func() string {
				return "checkbox"
			},
		},
	},
	parseTest{
		Name:     "If with trailing content",
		Content:  "?true\n  Yes\ntrailer",
		Expected: "Yes\ntrailer",
	},
	parseTest{
		Name:     "Collapsed Tags",
		Content:  "table > tr > %td\n  I'm different",
		Expected: "

I'm different

",
	},
	parseTest{
		Name:     "Totem Pole Tags",
		Content:  "table > tr > td",
		Expected: "

",
	},
	parseTest{
		Name:     "Collapsed tags with content on line",
		Content:  "table > tr > td Hello",
		Expected: "

Hello

",
	},
	parseTest{
		Name:     "Nested If/Else Statements",
		Content:  "?true\n  ?false\n    1\n  !?\n    2\n!?\n  3",
		Expected: "2",
	},
	parseTest{
		Name: "Nested Else things",
		Content: `?true
  >func1
    = .
    news
  !>
    bad
    wherever
!?
  unknown
  iops`,
		Expected: "asdf\nnews",
		Funcs: template.FuncMap{
			"func1": func() string {
				return "asdf"
			},
		},
	},
	parseTest{
		Name:     "Percentage Sign",
		Content:  "%",
		Expected: "%",
	},
	parseTest{
		Name:     "Blank Yield",
		Content:  "@",
		Expected: "",
	},
	parseTest{
		Name:     "Equals sign",
		Content:  "=",
		Expected: "=",
	},
	parseTest{
		Name:     "Single slash",
		Content:  "/",
		Expected: "/",
	},
	parseTest{
		Name:     "Blank comment",
		Content:  "//",
		Expected: "",
	},
	parseTest{
		Name:     "Cont, but actually verbatim",
		Content:  "/=",
		Expected: "=",
	},
	parseTest{
		Name:     "Malformed Block 1",
		Content:  "[name",
		Expected: "[name",
	},
	parseTest{
		Name:     "Malformed Block 2",
		Content:  "[",
		Expected: "[",
	},
	parseTest{
		Name:     "Malformed Block 2",
		Content:  "[]",
		Expected: "[]",
	},
}

type parseTest struct {
	Name     string
	Content  string
	Sources  map[string]string
	Expected string
	Template string
	Funcs    template.FuncMap
	Data     interface{}
}
<commit_msg>Add more block tests<commit_after>package terse

import (
	"bytes"
	"html/template"
	"testing"

	"github.com/acsellers/multitemplate"
)

func TestParse(t *testing.T) {
	for _, test := range parseTests {
		tmpl := multitemplate.New("terse").Funcs(test.Funcs)
		var e error
		if len(test.Sources) == 0 {
			tmpl, e = tmpl.Parse("parse", test.Content, "terse")
			if e != nil {
				t.Logf("In test %s\n", test.Name)
				t.Error("Parse Error:", e)
				continue
			}
		} else {
			for tn, tc := range test.Sources {
				tmpl, e = tmpl.Parse(tn, tc, "terse")
				if e != nil {
					t.Logf("In test %s\n", test.Name)
					t.Error("Parse Error:", e)
					continue
				}
			}
		}
		b := &bytes.Buffer{}
		if test.Template == "" {
			e = tmpl.Execute(b, test.Data)
		} else {
			e = tmpl.ExecuteTemplate(b, test.Template, test.Data)
		}
		if e != nil {
			t.Logf("In test %s\n", test.Name)
			t.Error("Execute Error:", e)
		}
		if b.String() != test.Expected {
			t.Logf("In test %s\n", test.Name)
			t.Errorf("Result Error, Expected:`%s`\nReceived:`%s`", test.Expected, b.String())
		}
	}
}

var parseTests = []parseTest{
	parseTest{
		Name: "Blank Template",
	},
	parseTest{
		Name:     "Doctype Template Blank",
		Content:  "!!",
		Expected: "<!DOCTYPE html>",
	},
	parseTest{
		Name:     "Doctype Template Named",
		Content:  "!! Strict",
		Expected: `<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">`,
	},
	parseTest{
		Name:     "Text in template",
		Content:  "blah blah",
		Expected: "blah blah",
	},
	parseTest{
		Name:     "Text in template",
		Content:  "blah blah\nnerr",
		Expected: "blah blah\nnerr",
	},
	parseTest{
		Name:     "Text in template",
		Content:  "bleh\n  wat",
		Expected: "bleh\n  wat",
	},
	parseTest{
		Name:     "Comment in template",
		Content:  "blah\n// don't show",
		Expected: "blah",
	},
	parseTest{
		Name:     "Nested Comment",
		Content:  "blah\n  // don't show",
		Expected: "blah",
	},
	parseTest{
		Name:     "Triple Nested Text",
		Content:  "first\n  second\n    third\n  fourth",
		Expected: "first\n  second\n    third\n  fourth",
	},
	parseTest{
		Name:     "Quadruple Nested Text",
		Content:  "First\n  Second\n    Third\n      Fourth",
		Expected: "First\n  Second\n    Third\n      Fourth",
	},
	parseTest{
		Name:     "If Statement",
		Content:  "?true\n  show",
		Expected: "show",
	},
	parseTest{
		Name:     "If/Else Statement (False)",
		Content:  "?false\n  no\n!?\n  yes",
		Expected: "yes",
	},
	parseTest{
		Name:     "If/Else Statement (True)",
		Content:  "?true\n  yes\n!?\n  no",
		Expected: "yes",
	},
	parseTest{
		Name:     "Range Statement (1 item)",
		Content:  "&.\n  wat",
		Expected: "\nwat",
		Data:     []string{"1"},
	},
	parseTest{
		Name:     "Range Statement (2 items)",
		Content:  "&.\n  wat",
		Expected: "\nwat\nwat",
		Data:     []string{"1", "2"},
	},
	parseTest{
		Name:     "Range Statement (0 items)",
		Content:  "&.\n  wat",
		Expected: "",
		Data:     []string{},
	},
	parseTest{
		Name:     "Range/Else Statement (0 items)",
		Content:  "&.\n  wat\n!&\n  no",
		Expected: "no",
		Data:     []string{},
	},
	parseTest{
		Name:     "Range/Else Statement (2 items)",
		Content:  "&.\n  wat\n!&\n  no",
		Expected: "\nwat\nwat",
		Data:     []string{"1", "2"},
	},
	parseTest{
		Name:     "Range Statement",
		Content:  "&.:$element:$index\n  li= $element",
		Expected: "\n
a\n
\n
i\n
\n
b\n
",
		Data:     []string{"a", "i", "b"},
	},
	parseTest{
		Name:     "Verbatim Statement",
		Content:  "/ $9@(#*$now",
		Expected: "$9@(#*$now",
	},
	parseTest{
		Name:     "Verbatim Statement with Nested lines",
		Content:  "/ $now\n  ?wat\n    do",
		Expected: "$now\n  ?wat\n    do",
	},
	parseTest{
		Name:     "Simple Exec",
		Content:  "= print 123",
		Expected: "123",
	},
	parseTest{
		Name:     "Continued Exec",
		Content:  "= print\n  /= 123\n  wat",
		Expected: "123\n  wat",
	},
	parseTest{
		Name:     "Auto-Close Exec",
		Content:  "= div \n  blah",
		Expected: "
\n  blah\n
",
		Funcs: template.FuncMap{
			"div": func() template.HTML {
				return "
"
			},
			"end_div": func() template.HTML {
				return "
"
			},
		},
	},
	parseTest{
		Name: "Auto-Close Exec w/ Args",
		Content: `= tag "div"
  blah`,
		Expected: "
\n  blah\n
",
		Funcs: template.FuncMap{
			"tag": func(s string) template.HTML {
				return "<" + template.HTML(s) + ">"
			},
			"end_tag": func(s string) template.HTML {
				return "</" + template.HTML(s) + ">"
			},
		},
	},
	parseTest{
		Name: "Child Template",
		Sources: map[string]string{
			"main":  "[block]\n  1234",
			"child": "child\n  4321",
		},
		Template: "child",
		Expected: "child\n  4321",
	},
	parseTest{
		Name: "Block",
		Sources: map[string]string{
			"main":  "[block]\n  12345",
			"child": "child\n  54321",
		},
		Template: "main",
		Expected: "12345",
	},
	parseTest{
		Name: "Inherited Block",
		Sources: map[string]string{
			"main":  "[block]\n  12345",
			"child": "@@main\n[block]\n  54321",
		},
		Template: "child",
		Expected: "\n54321",
	},
	parseTest{
		Name: "Define Block",
		Sources: map[string]string{
			"main":  "[block]\n  12345\n678",
			"child": "@@main\n^block]\n  54321",
		},
		Template: "child",
		Expected: "\n54321\n678",
	},
	parseTest{
		Name: "Yield Block",
		Sources: map[string]string{
			"main":  "@block\n123",
			"child": "@@main\n^block]\n  54321",
		},
		Template: "child",
		Expected: "\n54321\n123",
	},
	parseTest{
		Name:     "Fake Filter Block",
		Content:  ":wat",
		Expected: ":wat",
	},
	parseTest{
		Name:     "Fake Nested Filter Block",
		Content:  ":wat\n  two",
		Expected: ":wat\n  two",
	},
	parseTest{
		Name:     "Nested Filter Block",
		Content:  ":plain\n  two",
		Expected: "two",
	},
	parseTest{
		Name:     "Nested Filter Block",
		Content:  ":plain\n  two",
		Expected: "two",
	},
	parseTest{
		Name:     "Empty tag",
		Content:  "img",
		Expected: "",
	},
	parseTest{
		Name:     "Empty tag",
		Content:  "span\n  name",
		Expected: "name",
	},
	parseTest{
		Name:     "Class div",
		Content:  ".wat\n  here",
		Expected: "
here
",
	},
	parseTest{
		Name:     "Passthrough HTML",
		Content:  "h1\n  no",
		Expected: "
no
",
	},
	parseTest{
		Name:     "Tag with content",
		Content:  "h1 Here",
		Expected: "
Here
",
	},
	parseTest{
		Name:     "Interpolated content",
		Content:  "Welcome {{ . }}",
		Expected: "Welcome Gopher",
		Data:     "Gopher",
	},
	parseTest{
		Name:     "With line",
		Content:  ">.Username\n  = .",
		Expected: "Gopher",
		Data:     map[string]string{"Username": "Gopher"},
	},
	parseTest{
		Name:     "With line using variable",
		Content:  ">.Username:$name\n  = $name",
		Expected: "Andrew",
		Data:     map[string]string{"Username": "Andrew"},
	},
	parseTest{
		Name:     "With line",
		Content:  ">.NotExist\n  = .\n!>\n  Other animal",
		Expected: "Other animal",
		Data:     map[string]string{"Username": "Gopher"},
	},
	parseTest{
		Name:     "Tag with attribute",
		Content:  `input type="checkbox"`,
		Expected: `<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with attribute and single quotes",
		Content:  `input type='checkbox'`,
		Expected: `<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with dot attribute",
		Content:  `input type=.Type`,
		Expected: `<input type="checkbox" />`,
		Data:     map[string]string{"Type": "checkbox"},
	},
	parseTest{
		Name: "Tag with variable attribute",
		Content: `= $t := "checkbox"
input type=$t`,
		Expected: `
<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with parentheses attribute",
		Content:  `input type=(print "checkbox")`,
		Expected: `<input type="checkbox" />`,
	},
	parseTest{
		Name:     "Tag with function attribute",
		Content:  `input type=checker`,
		Expected: `<input type="checkbox" />`,
		Funcs: template.FuncMap{
			"checker": func() string {
				return "checkbox"
			},
		},
	},
	parseTest{
		Name:     "If with trailing content",
		Content:  "?true\n  Yes\ntrailer",
		Expected: "Yes\ntrailer",
	},
	parseTest{
		Name:     "Collapsed Tags",
		Content:  "table > tr > %td\n  I'm different",
		Expected: "

I'm different

",
	},
	parseTest{
		Name:     "Totem Pole Tags",
		Content:  "table > tr > td",
		Expected: "

",
	},
	parseTest{
		Name:     "Collapsed tags with content on line",
		Content:  "table > tr > td Hello",
		Expected: "

Hello

",
	},
	parseTest{
		Name:     "Nested If/Else Statements",
		Content:  "?true\n  ?false\n    1\n  !?\n    2\n!?\n  3",
		Expected: "2",
	},
	parseTest{
		Name: "Nested Else things",
		Content: `?true
  >func1
    = .
    news
  !>
    bad
    wherever
!?
  unknown
  iops`,
		Expected: "asdf\nnews",
		Funcs: template.FuncMap{
			"func1": func() string {
				return "asdf"
			},
		},
	},
	parseTest{
		Name:     "Percentage Sign",
		Content:  "%",
		Expected: "%",
	},
	parseTest{
		Name:     "Blank Yield",
		Content:  "@",
		Expected: "",
	},
	parseTest{
		Name:     "Equals sign",
		Content:  "=",
		Expected: "=",
	},
	parseTest{
		Name:     "Single slash",
		Content:  "/",
		Expected: "/",
	},
	parseTest{
		Name:     "Blank comment",
		Content:  "//",
		Expected: "",
	},
	parseTest{
		Name:     "Cont, but actually verbatim",
		Content:  "/=",
		Expected: "=",
	},
	parseTest{
		Name:     "Malformed Block 1",
		Content:  "[name",
		Expected: "[name",
	},
	parseTest{
		Name:     "Malformed Block 2",
		Content:  "[",
		Expected: "[",
	},
	parseTest{
		Name:     "Malformed Block 3",
		Content:  "[]",
		Expected: "[]",
	},
	parseTest{
		Name:     "Malformed Exec Block 1",
		Content:  "$name",
		Expected: "$name",
	},
	parseTest{
		Name:     "Malformed Exec Block 2",
		Content:  "$",
		Expected: "$",
	},
	parseTest{
		Name:     "Malformed Exec Block 3",
		Content:  "$]",
		Expected: "$]",
	},
	parseTest{
		Name:     "Malformed Define Block 1",
		Content:  "^name",
		Expected: "^name",
	},
	parseTest{
		Name:     "Malformed Define Block 2",
		Content:  "^",
		Expected: "^",
	},
	parseTest{
		Name:     "Malformed Define Block 3",
		Content:  "^]",
		Expected: "^]",
	},
}

type parseTest struct {
	Name     string
	Content  string
	Sources  map[string]string
	Expected string
	Template string
	Funcs    template.FuncMap
	Data     interface{}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"

	"github.com/spf13/cobra"
)

/*
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <lxc/lxccontainer.h>
#include <lxc/version.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define VERSION_AT_LEAST(major, minor, micro)							\
	((LXC_DEVEL == 1) || (!(major > LXC_VERSION_MAJOR ||					\
	major == LXC_VERSION_MAJOR && minor > LXC_VERSION_MINOR ||				\
	major == LXC_VERSION_MAJOR && minor == LXC_VERSION_MINOR && micro > LXC_VERSION_MICRO)))

extern char* advance_arg(bool required);
extern void error(char *msg);
extern void attach_userns(int pid);
extern int dosetns(int pid, char *nstype);

int mkdir_p(const char *dir, mode_t mode)
{
	const char *tmp = dir;
	const char *orig = dir;
	char *makeme;

	do {
		dir = tmp + strspn(tmp, "/");
		tmp = dir + strcspn(dir, "/");
		makeme = strndup(orig, dir - orig);
		if (*makeme) {
			if (mkdir(makeme, mode) && errno != EEXIST) {
				fprintf(stderr, "failed to create directory '%s': %s\n", makeme, strerror(errno));
				free(makeme);
				return -1;
			}
		}
		free(makeme);
	} while(tmp != dir);

	return 0;
}

void ensure_dir(char *dest) {
	struct stat sb;
	if (stat(dest, &sb) == 0) {
		if ((sb.st_mode & S_IFMT) == S_IFDIR)
			return;
		if (unlink(dest) < 0) {
			fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno));
			_exit(1);
		}
	}
	if (mkdir(dest, 0755) < 0) {
		fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno));
		_exit(1);
	}
}

void ensure_file(char *dest) {
	struct stat sb;
	int fd;

	if (stat(dest, &sb) == 0) {
		if ((sb.st_mode & S_IFMT) != S_IFDIR)
			return;
		if (rmdir(dest) < 0) {
			fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno));
			_exit(1);
		}
	}

	fd = creat(dest, 0755);
	if (fd < 0) {
		fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno));
		_exit(1);
	}
	close(fd);
}

void create(char *src, char *dest) {
	char *dirdup;
	char *destdirname;

	struct stat sb;
	if (stat(src, &sb) < 0) {
		fprintf(stderr, "source %s does not exist\n", src);
		_exit(1);
	}

	dirdup = strdup(dest);
	if (!dirdup)
		_exit(1);

	destdirname = dirname(dirdup);

	if (mkdir_p(destdirname, 0755) < 0) {
		fprintf(stderr, "failed to create path: %s\n", destdirname);
		free(dirdup);
		_exit(1);
	}
	free(dirdup);

	switch (sb.st_mode & S_IFMT) {
	case S_IFDIR:
		ensure_dir(dest);
		return;
	default:
		ensure_file(dest);
		return;
	}
}

void do_lxd_forkmount(pid_t pid) {
	char *src, *dest, *opts;

	attach_userns(pid);

	if (dosetns(pid, "mnt") < 0) {
		fprintf(stderr, "Failed setns to container mount namespace: %s\n", strerror(errno));
		_exit(1);
	}

	src = advance_arg(true);
	dest = advance_arg(true);

	create(src, dest);

	if (access(src, F_OK) < 0) {
		fprintf(stderr, "Mount source doesn't exist: %s\n", strerror(errno));
		_exit(1);
	}

	if (access(dest, F_OK) < 0) {
		fprintf(stderr, "Mount destination doesn't exist: %s\n", strerror(errno));
		_exit(1);
	}

	// Here, we always move recursively, because we sometimes allow
	// recursive mounts. If the mount has no kids then it doesn't matter,
	// but if it does, we want to move those too.
	if (mount(src, dest, "none", MS_MOVE | MS_REC, NULL) < 0) {
		fprintf(stderr, "Failed mounting %s onto %s: %s\n", src, dest, strerror(errno));
		_exit(1);
	}

	_exit(0);
}

void do_lxd_forkumount(pid_t pid) {
	int ret;
	char *path = NULL;

	ret = dosetns(pid, "mnt");
	if (ret < 0) {
		fprintf(stderr, "Failed to setns to container mount namespace: %s\n", strerror(errno));
		_exit(1);
	}

	path = advance_arg(true);

	ret = umount2(path, MNT_DETACH);
	if (ret < 0) {
		// - ENOENT: The user must have unmounted and removed the path.
		// - EINVAL: The user must have unmounted. Other explanations
		//           for EINVAL do not apply.
		if (errno == ENOENT || errno == EINVAL)
			_exit(0);

		fprintf(stderr, "Error unmounting %s: %s\n", path, strerror(errno));
		_exit(1);
	}

	_exit(0);
}

#if VERSION_AT_LEAST(3, 1, 0)
static int lxc_safe_ulong(const char *numstr, unsigned long *converted)
{
	char *err = NULL;
	unsigned long int uli;

	while (isspace(*numstr))
		numstr++;

	if (*numstr == '-')
		return -EINVAL;

	errno = 0;
	uli = strtoul(numstr, &err, 0);
	if (errno == ERANGE && uli == ULONG_MAX)
		return -ERANGE;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	*converted = uli;
	return 0;
}
#endif

void do_lxc_forkmount()
{
#if VERSION_AT_LEAST(3, 1, 0)
	int ret;
	char *config, *flags, *fstype, *lxcpath, *name, *source, *target;
	struct lxc_container *c;
	struct lxc_mount mnt = {0};
	unsigned long mntflags = 0;

	name = advance_arg(true);
	lxcpath = advance_arg(true);
	config = advance_arg(true);
	source = advance_arg(true);
	target = advance_arg(true);
	fstype = advance_arg(true);
	flags = advance_arg(true);

	fprintf(stderr, "name:    %s\n", name);
	fprintf(stderr, "lxcpath: %s\n", lxcpath);
	fprintf(stderr, "config:  %s\n", config);
	fprintf(stderr, "source:  %s\n", source);
	fprintf(stderr, "target:  %s\n", target);
	fprintf(stderr, "fstype:  %s\n", fstype);
	fprintf(stderr, "flags:   %s\n", flags);

	c = lxc_container_new(name, lxcpath);
	if (!c)
		_exit(1);

	c->clear_config(c);

	if (!c->load_config(c, config)) {
		lxc_container_put(c);
		_exit(1);
	}

	ret = lxc_safe_ulong(flags, &mntflags);
	if (ret < 0) {
		lxc_container_put(c);
		_exit(1);
	}

	ret = c->mount(c, source, target, fstype, mntflags, NULL, &mnt);
	lxc_container_put(c);
	if (ret < 0)
		_exit(1);

	_exit(0);
#endif
}

void do_lxc_forkumount()
{
#if VERSION_AT_LEAST(3, 1, 0)
	int ret;
	char *config, *lxcpath, *name, *target;
	struct lxc_container *c;
	struct lxc_mount mnt = {0};

	name = advance_arg(true);
	lxcpath = advance_arg(true);
	config = advance_arg(true);
	target = advance_arg(true);

	c = lxc_container_new(name, lxcpath);
	if (!c)
		_exit(1);

	c->clear_config(c);

	if (!c->load_config(c, config)) {
		lxc_container_put(c);
		_exit(1);
	}

	ret = c->umount(c, target, MNT_DETACH, &mnt);
	lxc_container_put(c);
	if (ret < 0)
		_exit(1);
#endif
}

void forkmount() {
	char *cur = NULL;

	char *command = NULL;
	char *rootfs = NULL;
	pid_t pid = 0;

	// Get the subcommand
	command = advance_arg(false);
	if (command == NULL || (strcmp(command, "--help") == 0 || strcmp(command, "--version") == 0 || strcmp(command, "-h") == 0)) {
		return;
	}

	// Check that we're root
	if (geteuid() != 0) {
		fprintf(stderr, "Error: forkmount requires root privileges\n");
		_exit(1);
	}

	// Call the subcommands
	if (strcmp(command, "lxd-mount") == 0) {
		// Get the pid
		cur = advance_arg(false);
		if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) {
			return;
		}
		pid = atoi(cur);

		do_lxd_forkmount(pid);
	} else if (strcmp(command, "lxc-mount") == 0) {
		do_lxc_forkmount();
	} else if (strcmp(command, "lxd-umount") == 0) {
		// Get the pid
		cur = advance_arg(false);
		if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) {
			return;
		}
		pid = atoi(cur);

		do_lxd_forkumount(pid);
	} else if (strcmp(command, "lxc-umount") == 0) {
		do_lxc_forkumount();
	}
}
*/
// #cgo CFLAGS: -std=gnu11 -Wvla
// #cgo LDFLAGS: -llxc
// #cgo pkg-config: lxc
import "C"

type cmdForkmount struct {
	global *cmdGlobal
}

func (c *cmdForkmount) Command() *cobra.Command {
	// Main subcommand
	cmd := &cobra.Command{}
	cmd.Use = "forkmount"
	cmd.Short = "Perform mount operations"
	cmd.Long = `Description:
  Perform mount operations

  This set of internal commands are used for all container mount
  operations.
`
	cmd.Hidden = true

	// mount
	cmdMount := &cobra.Command{}
	cmdMount.Use = "mount <PID> <source> <destination>"
	cmdMount.Args = cobra.ExactArgs(3)
	cmdMount.RunE = c.Run
	cmd.AddCommand(cmdMount)

	// umount
	cmdUmount := &cobra.Command{}
	cmdUmount.Use = "umount <PID> <path>"
	cmdUmount.Args = cobra.ExactArgs(2)
	cmdUmount.RunE = c.Run
	cmd.AddCommand(cmdUmount)

	return cmd
}

func (c *cmdForkmount) Run(cmd *cobra.Command, args []string) error {
	return fmt.Errorf("This command should have been intercepted in cgo")
}
<commit_msg>lxd/main_forkmount: Remove debug statements<commit_after>package main

import (
	"fmt"

	"github.com/spf13/cobra"
)

/*
#define _GNU_SOURCE
#include <ctype.h>
#include <errno.h>
#include <fcntl.h>
#include <libgen.h>
#include <limits.h>
#include <lxc/lxccontainer.h>
#include <lxc/version.h>
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mount.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>

#define VERSION_AT_LEAST(major, minor, micro)							\
	((LXC_DEVEL == 1) || (!(major > LXC_VERSION_MAJOR ||					\
	major == LXC_VERSION_MAJOR && minor > LXC_VERSION_MINOR ||				\
	major == LXC_VERSION_MAJOR && minor == LXC_VERSION_MINOR && micro > LXC_VERSION_MICRO)))

extern char* advance_arg(bool required);
extern void error(char *msg);
extern void attach_userns(int pid);
extern int dosetns(int pid, char *nstype);

int mkdir_p(const char *dir, mode_t mode)
{
	const char *tmp = dir;
	const char *orig = dir;
	char *makeme;

	do {
		dir = tmp + strspn(tmp, "/");
		tmp = dir + strcspn(dir, "/");
		makeme = strndup(orig, dir - orig);
		if (*makeme) {
			if (mkdir(makeme, mode) && errno != EEXIST) {
				fprintf(stderr, "failed to create directory '%s': %s\n", makeme, strerror(errno));
				free(makeme);
				return -1;
			}
		}
		free(makeme);
	} while(tmp != dir);

	return 0;
}

void ensure_dir(char *dest) {
	struct stat sb;
	if (stat(dest, &sb) == 0) {
		if ((sb.st_mode & S_IFMT) == S_IFDIR)
			return;
		if (unlink(dest) < 0) {
			fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno));
			_exit(1);
		}
	}
	if (mkdir(dest, 0755) < 0) {
		fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno));
		_exit(1);
	}
}

void ensure_file(char *dest) {
	struct stat sb;
	int fd;

	if (stat(dest, &sb) == 0) {
		if ((sb.st_mode & S_IFMT) != S_IFDIR)
			return;
		if (rmdir(dest) < 0) {
			fprintf(stderr, "Failed to remove old %s: %s\n", dest, strerror(errno));
			_exit(1);
		}
	}

	fd = creat(dest, 0755);
	if (fd < 0) {
		fprintf(stderr, "Failed to mkdir %s: %s\n", dest, strerror(errno));
		_exit(1);
	}
	close(fd);
}

void create(char *src, char *dest) {
	char *dirdup;
	char *destdirname;

	struct stat sb;
	if (stat(src, &sb) < 0) {
		fprintf(stderr, "source %s does not exist\n", src);
		_exit(1);
	}

	dirdup = strdup(dest);
	if (!dirdup)
		_exit(1);

	destdirname = dirname(dirdup);

	if (mkdir_p(destdirname, 0755) < 0) {
		fprintf(stderr, "failed to create path: %s\n", destdirname);
		free(dirdup);
		_exit(1);
	}
	free(dirdup);

	switch (sb.st_mode & S_IFMT) {
	case S_IFDIR:
		ensure_dir(dest);
		return;
	default:
		ensure_file(dest);
		return;
	}
}

void do_lxd_forkmount(pid_t pid) {
	char *src, *dest, *opts;

	attach_userns(pid);

	if (dosetns(pid, "mnt") < 0) {
		fprintf(stderr, "Failed setns to container mount namespace: %s\n", strerror(errno));
		_exit(1);
	}

	src = advance_arg(true);
	dest = advance_arg(true);

	create(src, dest);

	if (access(src, F_OK) < 0) {
		fprintf(stderr, "Mount source doesn't exist: %s\n", strerror(errno));
		_exit(1);
	}

	if (access(dest, F_OK) < 0) {
		fprintf(stderr, "Mount destination doesn't exist: %s\n", strerror(errno));
		_exit(1);
	}

	// Here, we always move recursively, because we sometimes allow
	// recursive mounts. If the mount has no kids then it doesn't matter,
	// but if it does, we want to move those too.
	if (mount(src, dest, "none", MS_MOVE | MS_REC, NULL) < 0) {
		fprintf(stderr, "Failed mounting %s onto %s: %s\n", src, dest, strerror(errno));
		_exit(1);
	}

	_exit(0);
}

void do_lxd_forkumount(pid_t pid) {
	int ret;
	char *path = NULL;

	ret = dosetns(pid, "mnt");
	if (ret < 0) {
		fprintf(stderr, "Failed to setns to container mount namespace: %s\n", strerror(errno));
		_exit(1);
	}

	path = advance_arg(true);

	ret = umount2(path, MNT_DETACH);
	if (ret < 0) {
		// - ENOENT: The user must have unmounted and removed the path.
		// - EINVAL: The user must have unmounted. Other explanations
		//           for EINVAL do not apply.
		if (errno == ENOENT || errno == EINVAL)
			_exit(0);

		fprintf(stderr, "Error unmounting %s: %s\n", path, strerror(errno));
		_exit(1);
	}

	_exit(0);
}

#if VERSION_AT_LEAST(3, 1, 0)
static int lxc_safe_ulong(const char *numstr, unsigned long *converted)
{
	char *err = NULL;
	unsigned long int uli;

	while (isspace(*numstr))
		numstr++;

	if (*numstr == '-')
		return -EINVAL;

	errno = 0;
	uli = strtoul(numstr, &err, 0);
	if (errno == ERANGE && uli == ULONG_MAX)
		return -ERANGE;

	if (err == numstr || *err != '\0')
		return -EINVAL;

	*converted = uli;
	return 0;
}
#endif

void do_lxc_forkmount()
{
#if VERSION_AT_LEAST(3, 1, 0)
	int ret;
	char *config, *flags, *fstype, *lxcpath, *name, *source, *target;
	struct lxc_container *c;
	struct lxc_mount mnt = {0};
	unsigned long mntflags = 0;

	name = advance_arg(true);
	lxcpath = advance_arg(true);
	config = advance_arg(true);
	source = advance_arg(true);
	target = advance_arg(true);
	fstype = advance_arg(true);
	flags = advance_arg(true);

	c = lxc_container_new(name, lxcpath);
	if (!c)
		_exit(1);

	c->clear_config(c);

	if (!c->load_config(c, config)) {
		lxc_container_put(c);
		_exit(1);
	}

	ret = lxc_safe_ulong(flags, &mntflags);
	if (ret < 0) {
		lxc_container_put(c);
		_exit(1);
	}

	ret = c->mount(c, source, target, fstype, mntflags, NULL, &mnt);
	lxc_container_put(c);
	if (ret < 0)
		_exit(1);

	_exit(0);
#endif
}

void do_lxc_forkumount()
{
#if VERSION_AT_LEAST(3, 1, 0)
	int ret;
	char *config, *lxcpath, *name, *target;
	struct lxc_container *c;
	struct lxc_mount mnt = {0};

	name = advance_arg(true);
	lxcpath = advance_arg(true);
	config = advance_arg(true);
	target = advance_arg(true);

	c = lxc_container_new(name, lxcpath);
	if (!c)
		_exit(1);

	c->clear_config(c);

	if (!c->load_config(c, config)) {
		lxc_container_put(c);
		_exit(1);
	}

	ret = c->umount(c, target, MNT_DETACH, &mnt);
	lxc_container_put(c);
	if (ret < 0)
		_exit(1);
#endif
}

void forkmount() {
	char *cur = NULL;

	char *command = NULL;
	char *rootfs = NULL;
	pid_t pid = 0;

	// Get the subcommand
	command = advance_arg(false);
	if (command == NULL || (strcmp(command, "--help") == 0 || strcmp(command, "--version") == 0 || strcmp(command, "-h") == 0)) {
		return;
	}

	// Check that we're root
	if (geteuid() != 0) {
		fprintf(stderr, "Error: forkmount requires root privileges\n");
		_exit(1);
	}

	// Call the subcommands
	if (strcmp(command, "lxd-mount") == 0) {
		// Get the pid
		cur = advance_arg(false);
		if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) {
			return;
		}
		pid = atoi(cur);

		do_lxd_forkmount(pid);
	} else if (strcmp(command, "lxc-mount") == 0) {
		do_lxc_forkmount();
	} else if (strcmp(command, "lxd-umount") == 0) {
		// Get the pid
		cur = advance_arg(false);
		if (cur == NULL || (strcmp(cur, "--help") == 0 || strcmp(cur, "--version") == 0 || strcmp(cur, "-h") == 0)) {
			return;
		}
		pid = atoi(cur);

		do_lxd_forkumount(pid);
	} else if (strcmp(command, "lxc-umount") == 0) {
		do_lxc_forkumount();
	}
}
*/
// #cgo CFLAGS: -std=gnu11 -Wvla
// #cgo LDFLAGS: -llxc
// #cgo pkg-config: lxc
import "C"

type cmdForkmount struct {
	global *cmdGlobal
}

func (c *cmdForkmount) Command() *cobra.Command {
	// Main subcommand
	cmd := &cobra.Command{}
	cmd.Use = "forkmount"
	cmd.Short = "Perform mount operations"
	cmd.Long = `Description:
  Perform mount operations

  This set of internal commands are used for all container mount
  operations.
`
	cmd.Hidden = true

	// mount
	cmdMount := &cobra.Command{}
	cmdMount.Use = "mount <PID> <source> <destination>"
	cmdMount.Args = cobra.ExactArgs(3)
	cmdMount.RunE = c.Run
	cmd.AddCommand(cmdMount)

	// umount
	cmdUmount := &cobra.Command{}
	cmdUmount.Use = "umount <PID> <path>"
	cmdUmount.Args = cobra.ExactArgs(2)
	cmdUmount.RunE = c.Run
	cmd.AddCommand(cmdUmount)

	return cmd
}

func (c *cmdForkmount) Run(cmd *cobra.Command, args []string) error {
	return fmt.Errorf("This command should have been intercepted in cgo")
}
<|endoftext|>


<commit_before>package consul

import (
	"fmt"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/serf"
	"log"
	"math/rand"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"
)

// Interface is used to provide either a Client or Server,
// both of which can be used to perform certain common
// Consul methods
type Interface interface {
	RPC(method string, args interface{}, reply interface{}) error
	LANMembers() []serf.Member
}

// Client is Consul client which uses RPC to communicate with the
// services for service discovery, health checking, and DC forwarding.
type Client struct {
	config *Config

	// Connection pool to consul servers
	connPool *ConnPool

	// consuls tracks the locally known servers
	consuls    []net.Addr
	consulLock sync.RWMutex

	// eventCh is used to receive events from the
	// serf cluster in the datacenter
	eventCh chan serf.Event

	// Logger uses the provided LogOutput
	logger *log.Logger

	// serf is the Serf cluster maintained inside the DC
	// which contains all the DC nodes
	serf *serf.Serf

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewClient is used to construct a new Consul client from the
// configuration, potentially returning an error
func NewClient(config *Config) (*Client, error) {
	// Check for a data directory!
	if config.DataDir == "" {
		return nil, fmt.Errorf("Config must provide a DataDir")
	}

	// Ensure we have a log output
	if config.LogOutput == nil {
		config.LogOutput = os.Stderr
	}

	// Create a logger
	logger := log.New(config.LogOutput, "", log.LstdFlags)

	// Create server
	c := &Client{
		config:     config,
		connPool:   NewPool(8, 30*time.Second),
		eventCh:    make(chan serf.Event, 256),
		logger:     logger,
		shutdownCh: make(chan struct{}),
	}

	// Start the Serf listeners to prevent a deadlock
	go c.lanEventHandler()

	// Initialize the lan Serf
	var err error
	c.serf, err = c.setupSerf(config.SerfLANConfig,
		c.eventCh, serfLANSnapshot)
	if err != nil {
		c.Shutdown()
		return nil, fmt.Errorf("Failed to start lan serf: %v", err)
	}
	return c, nil
}

// setupSerf is used to setup and initialize a Serf
func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (*serf.Serf, error) {
	conf.Init()
	conf.NodeName = c.config.NodeName
	conf.Tags["role"] = "node"
	conf.Tags["dc"] = c.config.Datacenter
	conf.MemberlistConfig.LogOutput = c.config.LogOutput
	conf.LogOutput = c.config.LogOutput
	conf.EventCh = ch
	conf.SnapshotPath = filepath.Join(c.config.DataDir, path)
	if err := ensurePath(conf.SnapshotPath, false); err != nil {
		return nil, err
	}
	return serf.Create(conf)
}

// Shutdown is used to shutdown the client
func (c *Client) Shutdown() error {
	c.logger.Printf("[INFO] consul: shutting down client")
	c.shutdownLock.Lock()
	defer c.shutdownLock.Unlock()

	if c.shutdown {
		return nil
	}

	c.shutdown = true
	close(c.shutdownCh)

	if c.serf != nil {
		c.serf.Shutdown()
	}

	// Close the connection pool
	c.connPool.Shutdown()
	return nil
}

// Leave is used to prepare for a graceful shutdown
func (c *Client) Leave() error {
	c.logger.Printf("[INFO] consul: client starting leave")

	// Leave the LAN pool
	if c.serf != nil {
		if err := c.serf.Leave(); err != nil {
			c.logger.Printf("[ERR] consul: Failed to leave LAN Serf cluster: %v", err)
		}
	}
	return nil
}

// JoinLAN is used to have Consul client join the inner-DC pool
// The target address should be another node inside the DC
// listening on the Serf LAN address
func (c *Client) JoinLAN(addrs []string) (int, error) {
	return c.serf.Join(addrs, false)
}

// LANMembers is used to return the members of the LAN cluster
func (c *Client) LANMembers() []serf.Member {
	return c.serf.Members()
}

// RemoveFailedNode is used to remove a failed node from the cluster
func (c *Client) RemoveFailedNode(node string) error {
	return c.serf.RemoveFailedNode(node)
}

// lanEventHandler is used to handle events from the lan Serf cluster
func (c *Client) lanEventHandler() {
	for {
		select {
		case e := <-c.eventCh:
			switch e.EventType() {
			case serf.EventMemberJoin:
				c.nodeJoin(e.(serf.MemberEvent))
			case serf.EventMemberLeave:
				fallthrough
			case serf.EventMemberFailed:
				c.nodeFail(e.(serf.MemberEvent))
			case serf.EventUser:
			default:
				c.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-c.shutdownCh:
			return
		}
	}
}

// nodeJoin is used to handle join events on the serf cluster
func (c *Client) nodeJoin(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		if parts.Datacenter != c.config.Datacenter {
			c.logger.Printf("[WARN] consul: server %s for datacenter %s has joined wrong cluster",
				m.Name, parts.Datacenter)
			continue
		}

		var addr net.Addr = &net.TCPAddr{IP: m.Addr, Port: parts.Port}
		c.logger.Printf("[INFO] consul: adding server for datacenter: %s, addr: %s", parts.Datacenter, addr)

		// Check if this server is known
		found := false
		c.consulLock.Lock()
		for _, c := range c.consuls {
			if c.String() == addr.String() {
				found = true
				break
			}
		}

		// Add to the list if not known
		if !found {
			c.consuls = append(c.consuls, addr)
		}
		c.consulLock.Unlock()
	}
}

// nodeFail is used to handle fail events on the serf cluster
func (c *Client) nodeFail(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		var addr net.Addr = &net.TCPAddr{IP: m.Addr, Port: parts.Port}
		c.logger.Printf("[INFO] consul: removing server for datacenter: %s, addr: %s", parts.Datacenter, addr)

		// Remove the server if known
		c.consulLock.Lock()
		n := len(c.consuls)
		for i := 0; i < n; i++ {
			if c.consuls[i].String() == addr.String() {
				c.consuls[i], c.consuls[n-1] = c.consuls[n-1], nil
				c.consuls = c.consuls[:n-1]
				break
			}
		}
		c.consulLock.Unlock()
	}
}

// RPC is used to forward an RPC call to a consul server, or fail if no servers
func (c *Client) RPC(method string, args interface{}, reply interface{}) error {
	// Bail if we can't find any servers
	c.consulLock.RLock()
	if len(c.consuls) == 0 {
		c.consulLock.RUnlock()
		return structs.ErrNoServers
	}

	// Select a random addr
	offset := rand.Int31() % int32(len(c.consuls))
	server := c.consuls[offset]
	c.consulLock.RUnlock()

	// Forward to remote Consul
	return c.connPool.RPC(server, method, args, reply)
}
<commit_msg>consul: client re-uses the last connection if error free<commit_after>package consul

import (
	"fmt"
	"github.com/hashicorp/consul/consul/structs"
	"github.com/hashicorp/serf/serf"
	"log"
	"math/rand"
	"net"
	"os"
	"path/filepath"
	"sync"
	"time"
)

var (
	clientRPCCache = 30 * time.Second
)

// Interface is used to provide either a Client or Server,
// both of which can be used to perform certain common
// Consul methods
type Interface interface {
	RPC(method string, args interface{}, reply interface{}) error
	LANMembers() []serf.Member
}

// Client is Consul client which uses RPC to communicate with the
// services for service discovery, health checking, and DC forwarding.
type Client struct {
	config *Config

	// Connection pool to consul servers
	connPool *ConnPool

	// consuls tracks the locally known servers
	consuls    []net.Addr
	consulLock sync.RWMutex

	// eventCh is used to receive events from the
	// serf cluster in the datacenter
	eventCh chan serf.Event

	// lastServer is the last server we made an RPC call to,
	// this is used to re-use the last connection
	lastServer  net.Addr
	lastRPCTime time.Time

	// Logger uses the provided LogOutput
	logger *log.Logger

	// serf is the Serf cluster maintained inside the DC
	// which contains all the DC nodes
	serf *serf.Serf

	shutdown     bool
	shutdownCh   chan struct{}
	shutdownLock sync.Mutex
}

// NewClient is used to construct a new Consul client from the
// configuration, potentially returning an error
func NewClient(config *Config) (*Client, error) {
	// Check for a data directory!
	if config.DataDir == "" {
		return nil, fmt.Errorf("Config must provide a DataDir")
	}

	// Ensure we have a log output
	if config.LogOutput == nil {
		config.LogOutput = os.Stderr
	}

	// Create a logger
	logger := log.New(config.LogOutput, "", log.LstdFlags)

	// Create server
	c := &Client{
		config:     config,
		connPool:   NewPool(8, clientRPCCache),
		eventCh:    make(chan serf.Event, 256),
		logger:     logger,
		shutdownCh: make(chan struct{}),
	}

	// Start the Serf listeners to prevent a deadlock
	go c.lanEventHandler()

	// Initialize the lan Serf
	var err error
	c.serf, err = c.setupSerf(config.SerfLANConfig,
		c.eventCh, serfLANSnapshot)
	if err != nil {
		c.Shutdown()
		return nil, fmt.Errorf("Failed to start lan serf: %v", err)
	}
	return c, nil
}

// setupSerf is used to setup and initialize a Serf
func (c *Client) setupSerf(conf *serf.Config, ch chan serf.Event, path string) (*serf.Serf, error) {
	conf.Init()
	conf.NodeName = c.config.NodeName
	conf.Tags["role"] = "node"
	conf.Tags["dc"] = c.config.Datacenter
	conf.MemberlistConfig.LogOutput = c.config.LogOutput
	conf.LogOutput = c.config.LogOutput
	conf.EventCh = ch
	conf.SnapshotPath = filepath.Join(c.config.DataDir, path)
	if err := ensurePath(conf.SnapshotPath, false); err != nil {
		return nil, err
	}
	return serf.Create(conf)
}

// Shutdown is used to shutdown the client
func (c *Client) Shutdown() error {
	c.logger.Printf("[INFO] consul: shutting down client")
	c.shutdownLock.Lock()
	defer c.shutdownLock.Unlock()

	if c.shutdown {
		return nil
	}

	c.shutdown = true
	close(c.shutdownCh)

	if c.serf != nil {
		c.serf.Shutdown()
	}

	// Close the connection pool
	c.connPool.Shutdown()
	return nil
}

// Leave is used to prepare for a graceful shutdown
func (c *Client) Leave() error {
	c.logger.Printf("[INFO] consul: client starting leave")

	// Leave the LAN pool
	if c.serf != nil {
		if err := c.serf.Leave(); err != nil {
			c.logger.Printf("[ERR] consul: Failed to leave LAN Serf cluster: %v", err)
		}
	}
	return nil
}

// JoinLAN is used to have Consul client join the inner-DC pool
// The target address should be another node inside the DC
// listening on the Serf LAN address
func (c *Client) JoinLAN(addrs []string) (int, error) {
	return c.serf.Join(addrs, false)
}

// LANMembers is used to return the members of the LAN cluster
func (c *Client) LANMembers() []serf.Member {
	return c.serf.Members()
}

// RemoveFailedNode is used to remove a failed node from the cluster
func (c *Client) RemoveFailedNode(node string) error {
	return c.serf.RemoveFailedNode(node)
}

// lanEventHandler is used to handle events from the lan Serf cluster
func (c *Client) lanEventHandler() {
	for {
		select {
		case e := <-c.eventCh:
			switch e.EventType() {
			case serf.EventMemberJoin:
				c.nodeJoin(e.(serf.MemberEvent))
			case serf.EventMemberLeave:
				fallthrough
			case serf.EventMemberFailed:
				c.nodeFail(e.(serf.MemberEvent))
			case serf.EventUser:
			default:
				c.logger.Printf("[WARN] consul: unhandled LAN Serf Event: %#v", e)
			}
		case <-c.shutdownCh:
			return
		}
	}
}

// nodeJoin is used to handle join events on the serf cluster
func (c *Client) nodeJoin(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		if parts.Datacenter != c.config.Datacenter {
			c.logger.Printf("[WARN] consul: server %s for datacenter %s has joined wrong cluster",
				m.Name, parts.Datacenter)
			continue
		}

		var addr net.Addr = &net.TCPAddr{IP: m.Addr, Port: parts.Port}
		c.logger.Printf("[INFO] consul: adding server for datacenter: %s, addr: %s", parts.Datacenter, addr)

		// Check if this server is known
		found := false
		c.consulLock.Lock()
		for _, c := range c.consuls {
			if c.String() == addr.String() {
				found = true
				break
			}
		}

		// Add to the list if not known
		if !found {
			c.consuls = append(c.consuls, addr)
		}
		c.consulLock.Unlock()
	}
}

// nodeFail is used to handle fail events on the serf cluster
func (c *Client) nodeFail(me serf.MemberEvent) {
	for _, m := range me.Members {
		ok, parts := isConsulServer(m)
		if !ok {
			continue
		}
		var addr net.Addr = &net.TCPAddr{IP: m.Addr, Port: parts.Port}
		c.logger.Printf("[INFO] consul: removing server for datacenter: %s, addr: %s", parts.Datacenter, addr)

		// Remove the server if known
		c.consulLock.Lock()
		n := len(c.consuls)
		for i := 0; i < n; i++ {
			if c.consuls[i].String() == addr.String() {
				c.consuls[i], c.consuls[n-1] = c.consuls[n-1], nil
				c.consuls = c.consuls[:n-1]
				break
			}
		}
		c.consulLock.Unlock()
	}
}

// RPC is used to forward an RPC call to a consul server, or fail if no servers
func (c *Client) RPC(method string, args interface{}, reply interface{}) error {
	// Check the last rpc time
	var server net.Addr
	if time.Now().Sub(c.lastRPCTime) < clientRPCCache {
		server = c.lastServer
		if server != nil {
			goto TRY_RPC
		}
	}

	// Bail if we can't find any servers
	c.consulLock.RLock()
	if len(c.consuls) == 0 {
		c.consulLock.RUnlock()
		return structs.ErrNoServers
	}

	// Select a random addr
	server = c.consuls[rand.Int31()%int32(len(c.consuls))]
	c.consulLock.RUnlock()

	// Forward to remote Consul
TRY_RPC:
	if err := c.connPool.RPC(server, method, args, reply); err != nil {
		c.lastServer = nil
		c.lastRPCTime = time.Time{}
		return err
	}

	// Cache the last server
	c.lastServer = server
	c.lastRPCTime = time.Now()
	return nil
}
<|endoftext|>


<commit_before>package geo

import (
	"testing"

	kdgeo "github.com/kellydunn/golang-geo"
	"github.com/paulsmith/gogeos/geos"
)

func BenchmarkPolygonGeosPaulSmit(b *testing.B) {
	// Setup
	polygon, err := geos.NewPolygon([]geos.Coord{
		{0, 1, 0},
		{1, 2, 0},
		{2, 1, 0},
		{2, 0, 0},
		{1, -1, 0},
		{0, 0, 0},
		{0, 1, 0},
	})
	if err != nil {
		b.Fatal(err)
	}
	point, err := geos.NewPoint(geos.Coord{X: 1, Y: 0, Z: 0})
	if err != nil {
		b.Fatal(err)
	}

	b.ResetTimer()

	// Test
	for i := 0; i < b.N; i++ {
		contains, err := polygon.Contains(point)
		if err != nil {
			b.Fatal(err)
		}
		if !contains {
			b.Fatal("not contains")
		}
	}
}

func BenchmarkPolygonGeoKellyDunn(b *testing.B) {
	// Setup
	polygon := kdgeo.NewPolygon([]*kdgeo.Point{
		kdgeo.NewPoint(0, 1),
		kdgeo.NewPoint(1, 2),
		kdgeo.NewPoint(2, 1),
		kdgeo.NewPoint(2, 0),
		kdgeo.NewPoint(1, -1),
		kdgeo.NewPoint(0, 0),
		kdgeo.NewPoint(0, 1),
	})
	point := kdgeo.NewPoint(1, 0)

	b.ResetTimer()

	// Test
	for i := 0; i < b.N; i++ {
		if contains := polygon.Contains(point); !contains {
			b.Fatal("not contains")
		}
	}
}

// TODO: make gdal usable

// const WGS84 = `
// GEOGCS["WGS 84",
//     DATUM["WGS_1984",
//         SPHEROID["WGS 84",6378137,298.257223563,
//             AUTHORITY["EPSG","7030"]],
//         AUTHORITY["EPSG","6326"]],
//     PRIMEM["Greenwich",0,
//         AUTHORITY["EPSG","8901"]],
//     UNIT["degree",0.01745329251994328,
//         AUTHORITY["EPSG","9122"]],
//     AUTHORITY["EPSG","4326"]]
// `

// func BenchmarkPolygonSorahanGdal(b *testing.B) {
// 	// Setup
// 	sref := gdal.CreateSpatialReference(WGS84)
// 	polygon, err := gdal.CreateFromWKT("POLYGON(0 1, 1 2, 2 1, 2 0, 1 -1, 0 0, 0 1)", sref)
// 	if err != nil {
// 		b.Fatal(err)
// 	}
// 	point, err := gdal.CreateFromWKT("POINT(1 0)", sref)
// 	if err != nil {
// 		b.Fatal(err)
// 	}

// 	b.ResetTimer()

// 	// Test
// 	for i := 0; i < b.N; i++ {
// 		if contains := polygon.Contains(point); !contains {
// 			b.Fatal("not contains")
// 		}
// 	}
// }

func BenchmarkPolygonBrian(b *testing.B) {
	// Setup
	polygon := Polygon([][][2]float64{
		{
			{0, 1},
			{1, 2},
			{2, 1},
			{2, 0},
			{1, -1},
			{0, 0},
			{0, 1},
		},
	})
	point := Point{1, 0}

	b.ResetTimer()

	// Test
	for i := 0; i < b.N; i++ {
		if contains := polygon.Contains(point); !contains {
			b.Fatal("not contains")
		}
	}
}

func BenchmarkCircleContains(b *testing.B) {
	// Setup
	circle := Circle{
		Coordinates: [2]float64{-100, 22},
		Radius:      1300,
	}
	point := Point{-100.00001, 22}

	b.ResetTimer()

	// Test haversine distance calculation.
	b.Run("haversine", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if contains := circle.ContainsHaversine(point); !contains {
				b.Fatal("circle does not contain point")
			}
		}
	})

	b.Run("spherical law of cosines", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if contains := circle.ContainsSLC(point); !contains {
				b.Fatal("circle does not contain point")
			}
		}
	})

	b.Run("equirectangular", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if contains := circle.ContainsEquirectangular(point); !contains {
				b.Fatal("circle does not contain point")
			}
		}
	})
}
<commit_msg>comment out geos benchmark<commit_after>package geo

import (
	"testing"

	kdgeo "github.com/kellydunn/golang-geo"
	// "github.com/paulsmith/gogeos/geos"
)

// This test is troublesome in CI as it depends on a C library.
// func BenchmarkPolygonGeosPaulSmit(b *testing.B) {
// 	// Setup
// 	polygon, err := geos.NewPolygon([]geos.Coord{
// 		{0, 1, 0},
// 		{1, 2, 0},
// 		{2, 1, 0},
// 		{2, 0, 0},
// 		{1, -1, 0},
// 		{0, 0, 0},
// 		{0, 1, 0},
// 	})
// 	if err != nil {
// 		b.Fatal(err)
// 	}
// 	point, err := geos.NewPoint(geos.Coord{X: 1, Y: 0, Z: 0})
// 	if err != nil {
// 		b.Fatal(err)
// 	}

// 	b.ResetTimer()

// 	// Test
// 	for i := 0; i < b.N; i++ {
// 		contains, err := polygon.Contains(point)
// 		if err != nil {
// 			b.Fatal(err)
// 		}
// 		if !contains {
// 			b.Fatal("not contains")
// 		}
// 	}
// }

func BenchmarkPolygonGeoKellyDunn(b *testing.B) {
	// Setup
	polygon := kdgeo.NewPolygon([]*kdgeo.Point{
		kdgeo.NewPoint(0, 1),
		kdgeo.NewPoint(1, 2),
		kdgeo.NewPoint(2, 1),
		kdgeo.NewPoint(2, 0),
		kdgeo.NewPoint(1, -1),
		kdgeo.NewPoint(0, 0),
		kdgeo.NewPoint(0, 1),
	})
	point := kdgeo.NewPoint(1, 0)

	b.ResetTimer()

	// Test
	for i := 0; i < b.N; i++ {
		if contains := polygon.Contains(point); !contains {
			b.Fatal("not contains")
		}
	}
}

// TODO: make gdal usable

// const WGS84 = `
// GEOGCS["WGS 84",
//     DATUM["WGS_1984",
//         SPHEROID["WGS 84",6378137,298.257223563,
//             AUTHORITY["EPSG","7030"]],
//         AUTHORITY["EPSG","6326"]],
//     PRIMEM["Greenwich",0,
//         AUTHORITY["EPSG","8901"]],
//     UNIT["degree",0.01745329251994328,
//         AUTHORITY["EPSG","9122"]],
//     AUTHORITY["EPSG","4326"]]
// `

// func BenchmarkPolygonSorahanGdal(b *testing.B) {
// 	// Setup
// 	sref := gdal.CreateSpatialReference(WGS84)
// 	polygon, err := gdal.CreateFromWKT("POLYGON(0 1, 1 2, 2 1, 2 0, 1 -1, 0 0, 0 1)", sref)
// 	if err != nil {
// 		b.Fatal(err)
// 	}
// 	point, err := gdal.CreateFromWKT("POINT(1 0)", sref)
// 	if err != nil {
// 		b.Fatal(err)
// 	}

// 	b.ResetTimer()

// 	// Test
// 	for i := 0; i < b.N; i++ {
// 		if contains := polygon.Contains(point); !contains {
// 			b.Fatal("not contains")
// 		}
// 	}
// }

func BenchmarkPolygonBrian(b *testing.B) {
	// Setup
	polygon := Polygon([][][2]float64{
		{
			{0, 1},
			{1, 2},
			{2, 1},
			{2, 0},
			{1, -1},
			{0, 0},
			{0, 1},
		},
	})
	point := Point{1, 0}

	b.ResetTimer()

	// Test
	for i := 0; i < b.N; i++ {
		if contains := polygon.Contains(point); !contains {
			b.Fatal("not contains")
		}
	}
}

func BenchmarkCircleContains(b *testing.B) {
	// Setup
	circle := Circle{
		Coordinates: [2]float64{-100, 22},
		Radius:      1300,
	}
	point := Point{-100.00001, 22}

	b.ResetTimer()

	// Test haversine distance calculation.
	b.Run("haversine", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if contains := circle.ContainsHaversine(point); !contains {
				b.Fatal("circle does not contain point")
			}
		}
	})

	b.Run("spherical law of cosines", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if contains := circle.ContainsSLC(point); !contains {
				b.Fatal("circle does not contain point")
			}
		}
	})

	b.Run("equirectangular", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			if contains := circle.ContainsEquirectangular(point); !contains {
				b.Fatal("circle does not contain point")
			}
		}
	})
}
<|endoftext|>


<commit_before>package recorder

import (
	"io/ioutil"
	"os"
	"log"
	"net/http"
	"net/url"

	"github.com/dnaeon/vcr/cassette"
)

// Recorder states
const (
	ModeRecording = iota
	ModeReplaying
)

type Recorder struct {
	// Operating mode of the recorder
	mode int

	// HTTP server used to mock requests
	server *httptest.Server

	// Cassette used by the recorder
	cassette cassette.Cassette

 	// Proxy function that can be used by client transports
	ProxyFunc func(*http.Request) (*url.URL, error)

	// Default transport that can be used by clients to inject
	Transport *http.Transport
}

// Proxies client requests to their original destination
func requestHandler(r *http.Request, c *cassette.Cassette, mode int) (*cassette.Interaction, error) {
	// Return interaction from cassette if in replay mode
	if mode == ModeReplaying {
		return c.Get(r)
	}

	// Else, perform client request to their original
	// destination and record interactions
	client := &http.Client{}
	req, err := http.NewRequest(r.Method, r.URL.String(), r.Body)
	if err != nil {
		return nil, err
	}

	req.Header = r.Header
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	// Record the interaction and add it to the cassette
	reqBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Fatal(err)
	}

	// Add interaction to cassette
	interaction := cassette.Interaction{
		Request: cassette.Request{
			Body:    string(reqBody),
			Headers: req.Header,
			URL:     req.URL.String(),
			Method:  req.Method,
		},
		Response: cassette.Response{
			Body:    string(respBody),
			Headers: resp.Header,
			Status:  resp.Status,
			Code:    resp.StatusCode,
		},
	}
	c.Add(interaction)

	return interaction, nil
}

// Creates a new recorder
func NewRecorder(cassetteName string) *Recorder {
	c := cassette.NewCassette(cassetteName)
	if os.Stat(cassetteName); os.IsNotExist(err) {
		mode := ModeRecording
	} else {
		mode := ModeReplaying
	}

	// Handler for client requests
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Pass cassette to handler for recording and replaying of interactions
		interaction, err := requestHandler(r, c, mode)
		if err != nil {
			log.Fatal(err)
		}

		w.WriteHeader(interaction.Response.Code)
		fmt.Fprintln(w, interaction.Response.Body)
	})

	// HTTP server used to mock requests
	server := httptest.NewUnstartedServer(handler)

	// A proxy function which routes all requests through our HTTP server
	// Can be used by clients to inject into their own transports
	proxyFunc := func(*http.Request) (*url.URL, error) {
		return url.Parse(server.URL)
	}

	// A transport which can be used by clients to inject
	transport := &http.Transport{
		Proxy: proxyFunc,
	}

	r := &Recorder{
		mode:      mode,
		server:    server,
		cassette:  c,
		ProxyFunc: proxyFunc,
		Transport: transport,
	}

	return r
}

// Starts the recorder
func (r *Recorder) Start() error {
	// Load cassette data if in replay mode
	if r.mode == ModeReplaying {
		if err := r.cassette.Load(); err != nil {
			return err
		}
	}

	// Start HTTP server to mock request
	r.server.Start()

	return nil
}

// Stops the recorder
func (r *Recorder) Stop() error {
	r.server.Close()

	// Save cassette if in recording mode
	if r.mode == ModeRecording {
		if err := r.cassette.Save(); err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Return error if not able to read response body<commit_after>package recorder

import (
	"io/ioutil"
	"os"
	"log"
	"net/http"
	"net/url"

	"github.com/dnaeon/vcr/cassette"
)

// Recorder states
const (
	ModeRecording = iota
	ModeReplaying
)

type Recorder struct {
	// Operating mode of the recorder
	mode int

	// HTTP server used to mock requests
	server *httptest.Server

	// Cassette used by the recorder
	cassette cassette.Cassette

 	// Proxy function that can be used by client transports
	ProxyFunc func(*http.Request) (*url.URL, error)

	// Default transport that can be used by clients to inject
	Transport *http.Transport
}

// Proxies client requests to their original destination
func requestHandler(r *http.Request, c *cassette.Cassette, mode int) (*cassette.Interaction, error) {
	// Return interaction from cassette if in replay mode
	if mode == ModeReplaying {
		return c.Get(r)
	}

	// Else, perform client request to their original
	// destination and record interactions
	client := &http.Client{}
	req, err := http.NewRequest(r.Method, r.URL.String(), r.Body)
	if err != nil {
		return nil, err
	}

	req.Header = r.Header
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}

	// Record the interaction and add it to the cassette
	reqBody, err := ioutil.ReadAll(req.Body)
	if err != nil {
		return nil, err
	}

	respBody, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	// Add interaction to cassette
	interaction := cassette.Interaction{
		Request: cassette.Request{
			Body:    string(reqBody),
			Headers: req.Header,
			URL:     req.URL.String(),
			Method:  req.Method,
		},
		Response: cassette.Response{
			Body:    string(respBody),
			Headers: resp.Header,
			Status:  resp.Status,
			Code:    resp.StatusCode,
		},
	}
	c.Add(interaction)

	return interaction, nil
}

// Creates a new recorder
func NewRecorder(cassetteName string) *Recorder {
	c := cassette.NewCassette(cassetteName)
	if os.Stat(cassetteName); os.IsNotExist(err) {
		mode := ModeRecording
	} else {
		mode := ModeReplaying
	}

	// Handler for client requests
	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		// Pass cassette to handler for recording and replaying of interactions
		interaction, err := requestHandler(r, c, mode)
		if err != nil {
			log.Fatal(err)
		}

		w.WriteHeader(interaction.Response.Code)
		fmt.Fprintln(w, interaction.Response.Body)
	})

	// HTTP server used to mock requests
	server := httptest.NewUnstartedServer(handler)

	// A proxy function which routes all requests through our HTTP server
	// Can be used by clients to inject into their own transports
	proxyFunc := func(*http.Request) (*url.URL, error) {
		return url.Parse(server.URL)
	}

	// A transport which can be used by clients to inject
	transport := &http.Transport{
		Proxy: proxyFunc,
	}

	r := &Recorder{
		mode:      mode,
		server:    server,
		cassette:  c,
		ProxyFunc: proxyFunc,
		Transport: transport,
	}

	return r
}

// Starts the recorder
func (r *Recorder) Start() error {
	// Load cassette data if in replay mode
	if r.mode == ModeReplaying {
		if err := r.cassette.Load(); err != nil {
			return err
		}
	}

	// Start HTTP server to mock request
	r.server.Start()

	return nil
}

// Stops the recorder
func (r *Recorder) Stop() error {
	r.server.Close()

	// Save cassette if in recording mode
	if r.mode == ModeRecording {
		if err := r.cassette.Save(); err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>