<commit_before>package functional

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/coreos/fleet/functional/platform"
)

// Start three pairs of services, asserting each pair land on the same
// machine due to the X-ConditionMachineOf options in the unit files.
func TestScheduleConditionMachineOf(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	// Start with a simple three-node cluster
	if err := platform.CreateNClusterMembers(cluster, 3, platform.MachineConfig{}); err != nil {
		t.Fatal(err)
	}
	machines, err := waitForNMachines(3)
	if err != nil {
		t.Fatal(err)
	}

	// Ensure we can SSH into each machine using fleetctl
	for _, machine := range machines {
		if _, _, err := fleetctl("--strict-host-key-checking=false", "ssh", machine, "uptime"); err != nil {
			t.Errorf("Unable to SSH into fleet machine: %v", err)
		}
	}

	// Start the 3 pairs of services
	for i := 0; i < 3; i++ {
		ping := fmt.Sprintf("fixtures/units/ping.%d.service", i)
		pong := fmt.Sprintf("fixtures/units/pong.%d.service", i)
		_, _, err := fleetctl("start", "--no-block", ping, pong)
		if err != nil {
			t.Errorf("Failed starting units: %v", err)
		}
	}

	// All 6 services should be visible immediately and become ACTIVE
	// shortly thereafter
	stdout, _, err := fleetctl("list-units", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-units: %v", err)
	}
	units := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(units) != 6 {
		t.Fatalf("Did not find six units in cluster: \n%s", stdout)
	}
	states, err := waitForNActiveUnits(6)
	if err != nil {
		t.Fatal(err)
	}

	for i := 0; i < 3; i++ {
		ping := fmt.Sprintf("ping.%d.service", i)
		pingState, ok := states[ping]
		if !ok {
			t.Errorf("Failed to find state for %s", ping)
			continue
		}

		pong := fmt.Sprintf("pong.%d.service", i)
		pongState, ok := states[pong]
		if !ok {
			t.Errorf("Failed to find state for %s", pong)
			continue
		}

		if len(pingState.Machine) == 0 {
			t.Errorf("Unit %s is not reporting machine", ping)
		}

		if len(pongState.Machine) == 0 {
			t.Errorf("Unit %s is not reporting machine", pong)
		}

		if pingState.Machine != pongState.Machine {
			t.Errorf("Units %s and %s are not on same machine", ping, pong)
		}
	}

	// Ensure a pair of units migrate together when their host goes down
	mach := states["ping.1.service"].Machine
	if _, _, err = fleetctl("--strict-host-key-checking=false", "ssh", mach, "sudo", "systemctl", "stop", "fleet"); err != nil {
		t.Fatal(err)
	}
	if _, err := waitForNMachines(2); err != nil {
		t.Fatal(err)
	}
	states, err = waitForNActiveUnits(6)
	if err != nil {
		t.Fatal(err)
	}

	newPingMach := states["ping.1.service"].Machine
	if mach == newPingMach {
		t.Fatalf("Unit ping.1.service did not appear to migrate")
	}

	newPongMach := states["pong.1.service"].Machine
	if newPingMach != newPongMach {
		t.Errorf("Unit pong.1.service did not migrate with ping.1.service")
	}
}

// Start 5 services that conflict with one another. Assert that only
// 3 of the 5 are started.
func TestScheduleConflicts(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	// Start with a simple three-node cluster
	if err := platform.CreateNClusterMembers(cluster, 3, platform.MachineConfig{}); err != nil {
		t.Fatal(err)
	}
	machines, err := waitForNMachines(3)
	if err != nil {
		t.Fatal(err)
	}

	// Ensure we can SSH into each machine using fleetctl
	for _, machine := range machines {
		if _, _, err := fleetctl("--strict-host-key-checking=false", "ssh", machine, "uptime"); err != nil {
			t.Errorf("Unable to SSH into fleet machine: %v", err)
		}
	}

	// submit 5 conflicting pairs of services
	for i := 0; i < 5; i++ {
		unit := fmt.Sprintf("fixtures/units/conflict.%d.service", i)
		_, _, err := fleetctl("submit", unit)
		if err != nil {
			t.Errorf("Failed submitting unit %s: %v", unit, err)
		}
	}

	// starting the first 3 should be fine
	for i := 0; i < 3; i++ {
		unit := fmt.Sprintf("fixtures/units/conflict.%d.service", i)
		_, _, err := fleetctl("start", unit)
		if err != nil {
			t.Errorf("Failed starting unit %s: %v", unit, err)
		}
	}

	// starting the last 2 should fail?
	for i := 3; i < 5; i++ {
		unit := fmt.Sprintf("fixtures/units/conflict.%d.service", i)
		_, _, err := fleetctl("start", unit)
		if err == nil {
			t.Errorf("Expected nonzero exit code while starting unscheduleable unit %s: %v", unit, err)
		}
	}

	// All 5 services should be visible immediately and 3 should become
	// ACTIVE shortly thereafter
	stdout, _, err := fleetctl("list-units", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-units: %v", err)
	}
	units := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(units) != 5 {
		t.Fatalf("Did not find five units in cluster: \n%s", stdout)
	}
	states, err := waitForNActiveUnits(3)
	if err != nil {
		t.Fatal(err)
	}

	machineSet := make(map[string]bool)

	for unit, unitState := range states {
		if len(unitState.Machine) == 0 {
			t.Errorf("Unit %s is not reporting machine", unit)
		}

		machineSet[unitState.Machine] = true
	}

	if len(machineSet) != 3 {
		t.Errorf("3 active units not running on 3 unique machines")
	}
}

// Ensure units can be scheduled directly to a given machine using the
// X-ConditionMachineBootID unit option.
func TestScheduleConditionMachineBootID(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	// Start with a simple three-node cluster
	if err := platform.CreateNClusterMembers(cluster, 3, platform.MachineConfig{}); err != nil {
		t.Fatal(err)
	}
	machines, err := waitForNMachines(3)
	if err != nil {
		t.Fatal(err)
	}

	// Start 3 units that are each scheduled to one of our machines
	schedule := make(map[string]string)
	for _, machine := range machines {
		contents := `
[Service]
ExecStart=/bin/bash -c "while true; do echo Hello, World!; sleep 1; done"

[X-Fleet]
X-ConditionMachineBootID=%s
`
		unitFile, err := tempUnit(fmt.Sprintf(contents, machine))
		if err != nil {
			t.Fatalf("Failed creating temporary unit: %v", err)
		}
		defer os.Remove(unitFile)

		_, _, err = fleetctl("start", unitFile)
		if err != nil {
			t.Fatalf("Failed starting unit file %s: %v", unitFile, err)
		}

		unit := filepath.Base(unitFile)
		schedule[unit] = machine
	}

	// Block until our three units have been started
	states, err := waitForNActiveUnits(3)
	if err != nil {
		t.Fatal(err)
	}

	for unit, unitState := range states {
		if unitState.Machine != schedule[unit] {
			t.Errorf("Unit %s was scheduled to %s, expected %s", unit, unitState.Machine, schedule[unit])
		}
	}
}
<commit_msg>fix(test): fix/simplify TestConflictsWith<commit_after>package functional

import (
	"fmt"
	"os"
	"path/filepath"
	"strings"
	"testing"

	"github.com/coreos/fleet/functional/platform"
)

// Start three pairs of services, asserting each pair land on the same
// machine due to the X-ConditionMachineOf options in the unit files.
func TestScheduleConditionMachineOf(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	// Start with a simple three-node cluster
	if err := platform.CreateNClusterMembers(cluster, 3, platform.MachineConfig{}); err != nil {
		t.Fatal(err)
	}
	machines, err := waitForNMachines(3)
	if err != nil {
		t.Fatal(err)
	}

	// Ensure we can SSH into each machine using fleetctl
	for _, machine := range machines {
		if _, _, err := fleetctl("--strict-host-key-checking=false", "ssh", machine, "uptime"); err != nil {
			t.Errorf("Unable to SSH into fleet machine: %v", err)
		}
	}

	// Start the 3 pairs of services
	for i := 0; i < 3; i++ {
		ping := fmt.Sprintf("fixtures/units/ping.%d.service", i)
		pong := fmt.Sprintf("fixtures/units/pong.%d.service", i)
		_, _, err := fleetctl("start", "--no-block", ping, pong)
		if err != nil {
			t.Errorf("Failed starting units: %v", err)
		}
	}

	// All 6 services should be visible immediately and become ACTIVE
	// shortly thereafter
	stdout, _, err := fleetctl("list-units", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-units: %v", err)
	}
	units := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(units) != 6 {
		t.Fatalf("Did not find six units in cluster: \n%s", stdout)
	}
	states, err := waitForNActiveUnits(6)
	if err != nil {
		t.Fatal(err)
	}

	for i := 0; i < 3; i++ {
		ping := fmt.Sprintf("ping.%d.service", i)
		pingState, ok := states[ping]
		if !ok {
			t.Errorf("Failed to find state for %s", ping)
			continue
		}

		pong := fmt.Sprintf("pong.%d.service", i)
		pongState, ok := states[pong]
		if !ok {
			t.Errorf("Failed to find state for %s", pong)
			continue
		}

		if len(pingState.Machine) == 0 {
			t.Errorf("Unit %s is not reporting machine", ping)
		}

		if len(pongState.Machine) == 0 {
			t.Errorf("Unit %s is not reporting machine", pong)
		}

		if pingState.Machine != pongState.Machine {
			t.Errorf("Units %s and %s are not on same machine", ping, pong)
		}
	}

	// Ensure a pair of units migrate together when their host goes down
	mach := states["ping.1.service"].Machine
	if _, _, err = fleetctl("--strict-host-key-checking=false", "ssh", mach, "sudo", "systemctl", "stop", "fleet"); err != nil {
		t.Fatal(err)
	}
	if _, err := waitForNMachines(2); err != nil {
		t.Fatal(err)
	}
	states, err = waitForNActiveUnits(6)
	if err != nil {
		t.Fatal(err)
	}

	newPingMach := states["ping.1.service"].Machine
	if mach == newPingMach {
		t.Fatalf("Unit ping.1.service did not appear to migrate")
	}

	newPongMach := states["pong.1.service"].Machine
	if newPingMach != newPongMach {
		t.Errorf("Unit pong.1.service did not migrate with ping.1.service")
	}
}

// Start 5 services that conflict with one another. Assert that only
// 3 of the 5 are started.
func TestScheduleConflicts(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	// Start with a simple three-node cluster
	if err := platform.CreateNClusterMembers(cluster, 3, platform.MachineConfig{}); err != nil {
		t.Fatal(err)
	}
	machines, err := waitForNMachines(3)
	if err != nil {
		t.Fatal(err)
	}

	// Ensure we can SSH into each machine using fleetctl
	for _, machine := range machines {
		if _, _, err := fleetctl("--strict-host-key-checking=false", "ssh", machine, "uptime"); err != nil {
			t.Errorf("Unable to SSH into fleet machine: %v", err)
		}
	}

	for i := 0; i < 5; i++ {
		unit := fmt.Sprintf("fixtures/units/conflict.%d.service", i)
		_, _, err := fleetctl("start", "--no-block", unit)
		if err != nil {
			t.Errorf("Failed starting unit %s: %v", unit, err)
		}
	}

	// All 5 services should be visible immediately and 3 should become
	// ACTIVE shortly thereafter
	stdout, _, err := fleetctl("list-units", "--no-legend")
	if err != nil {
		t.Fatalf("Failed to run list-units: %v", err)
	}
	units := strings.Split(strings.TrimSpace(stdout), "\n")
	if len(units) != 5 {
		t.Fatalf("Did not find five units in cluster: \n%s", stdout)
	}
	states, err := waitForNActiveUnits(3)
	if err != nil {
		t.Fatal(err)
	}

	machineSet := make(map[string]bool)

	for unit, unitState := range states {
		if len(unitState.Machine) == 0 {
			t.Errorf("Unit %s is not reporting machine", unit)
		}

		machineSet[unitState.Machine] = true
	}

	if len(machineSet) != 3 {
		t.Errorf("3 active units not running on 3 unique machines")
	}
}

// Ensure units can be scheduled directly to a given machine using the
// X-ConditionMachineBootID unit option.
func TestScheduleConditionMachineBootID(t *testing.T) {
	cluster, err := platform.NewNspawnCluster("smoke")
	if err != nil {
		t.Fatal(err)
	}
	defer cluster.Destroy()

	// Start with a simple three-node cluster
	if err := platform.CreateNClusterMembers(cluster, 3, platform.MachineConfig{}); err != nil {
		t.Fatal(err)
	}
	machines, err := waitForNMachines(3)
	if err != nil {
		t.Fatal(err)
	}

	// Start 3 units that are each scheduled to one of our machines
	schedule := make(map[string]string)
	for _, machine := range machines {
		contents := `
[Service]
ExecStart=/bin/bash -c "while true; do echo Hello, World!; sleep 1; done"

[X-Fleet]
X-ConditionMachineBootID=%s
`
		unitFile, err := tempUnit(fmt.Sprintf(contents, machine))
		if err != nil {
			t.Fatalf("Failed creating temporary unit: %v", err)
		}
		defer os.Remove(unitFile)

		_, _, err = fleetctl("start", unitFile)
		if err != nil {
			t.Fatalf("Failed starting unit file %s: %v", unitFile, err)
		}

		unit := filepath.Base(unitFile)
		schedule[unit] = machine
	}

	// Block until our three units have been started
	states, err := waitForNActiveUnits(3)
	if err != nil {
		t.Fatal(err)
	}

	for unit, unitState := range states {
		if unitState.Machine != schedule[unit] {
			t.Errorf("Unit %s was scheduled to %s, expected %s", unit, unitState.Machine, schedule[unit])
		}
	}
}
<|endoftext|>


<commit_before>package godbg

import (
	"fmt"
	"os"
	"testing"
	. "github.com/smartystreets/goconvey/convey"
)

func TestProject(t *testing.T) {
	Convey("Test buffers", t, func() {

		Convey("By Default, equals to std", func() {
			So(Out(), ShouldEqual, os.Stdout)
			So(Err(), ShouldEqual, os.Stderr)
		})
		Convey("When set to buffer, no longer equals to std", func() {
			SetBuffers(nil)
			So(Out(), ShouldNotEqual, os.Stdout)
			So(Err(), ShouldNotEqual, os.Stderr)
		})
		Convey("By Default, a new pdbg instance buffer equals to std", func() {
			apdbg := NewPdbg()
			So(apdbg.Out(), ShouldEqual, os.Stdout)
			So(apdbg.Err(), ShouldEqual, os.Stderr)
		})
		Convey("By Default, a new pdbg instance set to buffer writes no longer equals to std", func() {
			apdbg := NewPdbg(SetBuffers)
			So(apdbg.Out(), ShouldNotEqual, os.Stdout)
			So(apdbg.Err(), ShouldNotEqual, os.Stderr)
		})
		Convey("Test custom buffer on global pdbg", func() {
			pdbg.bout = nil
			pdbg.sout = nil
			pdbg.berr = nil
			pdbg.serr = nil
			fmt.Fprintln(Out(), "test0 content0")
			So(OutString(), ShouldEqual, ``)
			fmt.Fprintln(Err(), "err0 content0")
			So(ErrString(), ShouldEqual, ``)
			SetBuffers(nil)
			fmt.Fprintln(Out(), "test content")
			fmt.Fprintln(Err(), "err1 cerr")
			fmt.Fprintln(Err(), "err2 cerr2")
			fmt.Fprint(Out(), "test2 content2")
			So(OutString(), ShouldEqual, `test content
test2 content2`)
			So(ErrString(), ShouldEqual, `err1 cerr
err2 cerr2
`)
		})

		Convey("Test custom buffer reset on global pdbg", func() {
			SetBuffers(nil)
			fmt.Fprint(Out(), "test content")
			So(OutString(), ShouldEqual, `test content`)
			fmt.Fprint(Err(), "err1 cerr")
			So(ErrString(), ShouldEqual, `err1 cerr`)
			ResetIOs()
			fmt.Fprint(Out(), "test2 content2")
			So(OutString(), ShouldEqual, `test2 content2`)
			fmt.Fprint(Err(), "err2 cerr2")
			So(ErrString(), ShouldEqual, `err2 cerr2`)
		})

		Convey("Test custom buffer on custom pdbg", func() {
			apdbg := NewPdbg(SetBuffers)
			fmt.Fprintln(apdbg.Out(), "test content")
			fmt.Fprintln(apdbg.Err(), "err1 cerr")
			fmt.Fprintln(apdbg.Err(), "err2 cerr2")
			fmt.Fprint(apdbg.Out(), "test2 content2")
			So(apdbg.OutString(), ShouldEqual, `test content
test2 content2`)
			So(apdbg.ErrString(), ShouldEqual, `err1 cerr
err2 cerr2
`)
		})
		Convey("Test custom buffer reset on custom pdbg", func() {
			apdbg := NewPdbg(SetBuffers)
			fmt.Fprint(apdbg.Out(), "test content")
			So(apdbg.OutString(), ShouldEqual, `test content`)
			fmt.Fprint(apdbg.Err(), "err1 cerr")
			So(apdbg.ErrString(), ShouldEqual, `err1 cerr`)
			apdbg.ResetIOs()
			fmt.Fprint(apdbg.Out(), "test2 content2")
			So(apdbg.OutString(), ShouldEqual, `test2 content2`)
			fmt.Fprint(apdbg.Err(), "err2 cerr2")
			So(apdbg.ErrString(), ShouldEqual, `err2 cerr2`)
		})
	})

	Convey("Test pdbg print functions", t, func() {
		Convey("Test pdbg print with global instance", func() {
			SetBuffers(nil)
			Pdbgf("test")
			So(ErrString(), ShouldEqual, `[func.009:81]
  test
`)
		})
	})
}
<commit_msg>Start testing embedded calls<commit_after>package godbg

import (
	"fmt"
	"os"
	"testing"
	. "github.com/smartystreets/goconvey/convey"
)

func TestProject(t *testing.T) {
	Convey("Test buffers", t, func() {

		Convey("By Default, equals to std", func() {
			So(Out(), ShouldEqual, os.Stdout)
			So(Err(), ShouldEqual, os.Stderr)
		})
		Convey("When set to buffer, no longer equals to std", func() {
			SetBuffers(nil)
			So(Out(), ShouldNotEqual, os.Stdout)
			So(Err(), ShouldNotEqual, os.Stderr)
		})
		Convey("By Default, a new pdbg instance buffer equals to std", func() {
			apdbg := NewPdbg()
			So(apdbg.Out(), ShouldEqual, os.Stdout)
			So(apdbg.Err(), ShouldEqual, os.Stderr)
		})
		Convey("By Default, a new pdbg instance set to buffer writes no longer equals to std", func() {
			apdbg := NewPdbg(SetBuffers)
			So(apdbg.Out(), ShouldNotEqual, os.Stdout)
			So(apdbg.Err(), ShouldNotEqual, os.Stderr)
		})
		Convey("Test custom buffer on global pdbg", func() {
			pdbg.bout = nil
			pdbg.sout = nil
			pdbg.berr = nil
			pdbg.serr = nil
			fmt.Fprintln(Out(), "test0 content0")
			So(OutString(), ShouldEqual, ``)
			fmt.Fprintln(Err(), "err0 content0")
			So(ErrString(), ShouldEqual, ``)
			SetBuffers(nil)
			fmt.Fprintln(Out(), "test content")
			fmt.Fprintln(Err(), "err1 cerr")
			fmt.Fprintln(Err(), "err2 cerr2")
			fmt.Fprint(Out(), "test2 content2")
			So(OutString(), ShouldEqual, `test content
test2 content2`)
			So(ErrString(), ShouldEqual, `err1 cerr
err2 cerr2
`)
		})

		Convey("Test custom buffer reset on global pdbg", func() {
			SetBuffers(nil)
			fmt.Fprint(Out(), "test content")
			So(OutString(), ShouldEqual, `test content`)
			fmt.Fprint(Err(), "err1 cerr")
			So(ErrString(), ShouldEqual, `err1 cerr`)
			ResetIOs()
			fmt.Fprint(Out(), "test2 content2")
			So(OutString(), ShouldEqual, `test2 content2`)
			fmt.Fprint(Err(), "err2 cerr2")
			So(ErrString(), ShouldEqual, `err2 cerr2`)
		})

		Convey("Test custom buffer on custom pdbg", func() {
			apdbg := NewPdbg(SetBuffers)
			fmt.Fprintln(apdbg.Out(), "test content")
			fmt.Fprintln(apdbg.Err(), "err1 cerr")
			fmt.Fprintln(apdbg.Err(), "err2 cerr2")
			fmt.Fprint(apdbg.Out(), "test2 content2")
			So(apdbg.OutString(), ShouldEqual, `test content
test2 content2`)
			So(apdbg.ErrString(), ShouldEqual, `err1 cerr
err2 cerr2
`)
		})
		Convey("Test custom buffer reset on custom pdbg", func() {
			apdbg := NewPdbg(SetBuffers)
			fmt.Fprint(apdbg.Out(), "test content")
			So(apdbg.OutString(), ShouldEqual, `test content`)
			fmt.Fprint(apdbg.Err(), "err1 cerr")
			So(apdbg.ErrString(), ShouldEqual, `err1 cerr`)
			apdbg.ResetIOs()
			fmt.Fprint(apdbg.Out(), "test2 content2")
			So(apdbg.OutString(), ShouldEqual, `test2 content2`)
			fmt.Fprint(apdbg.Err(), "err2 cerr2")
			So(apdbg.ErrString(), ShouldEqual, `err2 cerr2`)
		})
	})

	Convey("Test pdbg print functions", t, func() {
		Convey("Test pdbg print with global instance", func() {
			SetBuffers(nil)
			Pdbgf("test")
			So(ErrString(), ShouldEqual, `[func.009:95]
  test
`)
			ResetIOs()
			prbgtest()
			So(ErrString(), ShouldEqual, `[func.009:81]
  test
`)
		})
	})
}

func prbgtest() {
	Pdbgf("prbgtest content")
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"strings"

	"github.com/beatgammit/stein"
	"github.com/wsxiaoys/terminal"
)

const (
	timeFormat = "2006-01-02 at 15:04:05"
)

func newKegParser() *stein.Parser {
	return &stein.Parser{
		&kegHandler{
			currentLevel: -1,
		},
	}
}

type kegHandler struct {
	*stein.DefaultHandler
	currentLevel int
	afterCase    bool
}

func (h *kegHandler) getIndentation(level int) string {
	indentation := ""
	for i := 0; i < level; i++ {
		indentation += "    "
	}
	return indentation
}

func (h *kegHandler) HandleSuite(s *stein.Suite) {
	if *quiet {
		return
	}
	fmt.Printf("Test suite started on %s\n", s.Start.Local().Format(timeFormat))
	if s.Count > 0 {
		fmt.Printf("Running %d tests...\n\n", s.Count)
	}
}

func (h *kegHandler) HandleCase(c *stein.Case) {
	if *quiet {
		return
	}
	clearLine()
	fmt.Printf("%s%s\n", h.getIndentation(h.currentLevel), c.Label)
	h.currentLevel = c.Level
}

func (h *kegHandler) HandleTest(t *stein.Test) {
	if *quiet {
		return
	}
	status := t.Status
	if strings.ToLower(status) == "omit" {
		status = "skip"
	}
	clearLine()
	indent := h.getIndentation(h.currentLevel + 1)
	var testLine string
	var postIndent = ""
	for len(status)+len(postIndent) < longestStatus {
		postIndent += " "
	}
	testLine = fmt.Sprintf("%s%s%s ... %s", indent, status, postIndent, t.Label)
	printColor(statusColors[status], testLine)
	if t.Exception != nil {
		for i := 0; i < longestStatus+len(" ... "); i++ {
			indent += " "
		}

		printColor(statusColors[status], fmt.Sprintf("\n%sError: %s", indent+"  ", t.Exception.Message))
		if t.Exception.File != "" {
			str := fmt.Sprintf("\n%s%s", indent, t.Exception.File)
			if t.Exception.Line > 0 {
				str += fmt.Sprintf(":%d", t.Exception.Line)
			}
			printColor(statusColors["note"], str)
		}
	}
	if *format == "onlyfail" {
		if status == "fail" || status == "error" {
			fmt.Print("\n")
		} else {
			fmt.Print("\r")
		}
	} else {
		fmt.Print("\n")
	}
}

func (h *kegHandler) HandleFinal(t *stein.Tally) {
	if *quiet {
		return
	}
	clearLine()
	fmt.Printf("\nResults: %d pass, %d fail, %d error, %d skip\n",
		t.Counts.Pass, t.Counts.Fail, t.Counts.Error, t.Counts.Omit)
}

func clearLine() {
	if *format == "onlyfail" {
		terminal.Stdout.ClearLine()
	}
}

var statusColors = map[string]string{
	"pass":  "g",
	"skip":  "y",
	"fail":  "r",
	"error": "m",
	"todo":  "b",
	"note":  "w",
}

var longestStatus int

func init() {
	for status := range statusColors {
		if len(status) > longestStatus {
			longestStatus = len(status)
		}
	}
}

func printColor(color, text string) {
	if nocolor {
		terminal.Stdout.Print(text)
		return
	}
	terminal.Stdout.Color(color).Print(text).Reset()
}
<commit_msg>Indent file and line number same as error message<commit_after>package main

import (
	"fmt"
	"strings"

	"github.com/beatgammit/stein"
	"github.com/wsxiaoys/terminal"
)

const (
	timeFormat = "2006-01-02 at 15:04:05"
)

func newKegParser() *stein.Parser {
	return &stein.Parser{
		&kegHandler{
			currentLevel: -1,
		},
	}
}

type kegHandler struct {
	*stein.DefaultHandler
	currentLevel int
	afterCase    bool
}

func (h *kegHandler) getIndentation(level int) string {
	indentation := ""
	for i := 0; i < level; i++ {
		indentation += "    "
	}
	return indentation
}

func (h *kegHandler) HandleSuite(s *stein.Suite) {
	if *quiet {
		return
	}
	fmt.Printf("Test suite started on %s\n", s.Start.Local().Format(timeFormat))
	if s.Count > 0 {
		fmt.Printf("Running %d tests...\n\n", s.Count)
	}
}

func (h *kegHandler) HandleCase(c *stein.Case) {
	if *quiet {
		return
	}
	clearLine()
	fmt.Printf("%s%s\n", h.getIndentation(h.currentLevel), c.Label)
	h.currentLevel = c.Level
}

func (h *kegHandler) HandleTest(t *stein.Test) {
	if *quiet {
		return
	}
	status := t.Status
	if strings.ToLower(status) == "omit" {
		status = "skip"
	}
	clearLine()
	indent := h.getIndentation(h.currentLevel + 1)
	var testLine string
	var postIndent = ""
	for len(status)+len(postIndent) < longestStatus {
		postIndent += " "
	}
	testLine = fmt.Sprintf("%s%s%s ... %s", indent, status, postIndent, t.Label)
	printColor(statusColors[status], testLine)
	if t.Exception != nil {
		for i := 0; i < longestStatus+len(" ... "); i++ {
			indent += " "
		}

		printColor(statusColors[status], fmt.Sprintf("\n%sError: %s", indent+"  ", t.Exception.Message))
		if t.Exception.File != "" {
			str := fmt.Sprintf("\n%s%s", indent+"  ", t.Exception.File)
			if t.Exception.Line > 0 {
				str += fmt.Sprintf(":%d", t.Exception.Line)
			}
			printColor(statusColors["note"], str)
		}
	}
	if *format == "onlyfail" {
		if status == "fail" || status == "error" {
			fmt.Print("\n")
		} else {
			fmt.Print("\r")
		}
	} else {
		fmt.Print("\n")
	}
}

func (h *kegHandler) HandleFinal(t *stein.Tally) {
	if *quiet {
		return
	}
	clearLine()
	fmt.Printf("\nResults: %d pass, %d fail, %d error, %d skip\n",
		t.Counts.Pass, t.Counts.Fail, t.Counts.Error, t.Counts.Omit)
}

func clearLine() {
	if *format == "onlyfail" {
		terminal.Stdout.ClearLine()
	}
}

var statusColors = map[string]string{
	"pass":  "g",
	"skip":  "y",
	"fail":  "r",
	"error": "m",
	"todo":  "b",
	"note":  "w",
}

var longestStatus int

func init() {
	for status := range statusColors {
		if len(status) > longestStatus {
			longestStatus = len(status)
		}
	}
}

func printColor(color, text string) {
	if nocolor {
		terminal.Stdout.Print(text)
		return
	}
	terminal.Stdout.Color(color).Print(text).Reset()
}
<|endoftext|>


<commit_before>package main

import (

	"bytes"

	"github.com/goadesign/goa"
	"github.com/perigee/terrant/app"
	"github.com/fsouza/go-dockerclient"

)

// ResourceController implements the resource resource.
type ResourceController struct {
	*goa.Controller
}

// NewResourceController creates a resource controller.
func NewResourceController(service *goa.Service) *ResourceController {
	return &ResourceController{Controller: service.NewController("ResourceController")}
}

// Create runs the create action.
func (c *ResourceController) Create(ctx *app.CreateResourceContext) error {
	// ResourceController_Create: start_implement

	// Put your logic here
	id := ctx.ResourceID

	ch := make(chan string)
	endpoint := "unix:///var/run/docker.sock"

	client, err := docker.NewClient(endpoint)

	if err != nil {
	   panic(err)
	}

	go func(id string) {

	   containers, err := client.ListContainers(docker.ListContainersOptions{All: false})

	   if err != nil {
	      panic(err)
	   }

	   var buffer bytes.Buffer

	   for _, cont := range containers {
	      buffer.WriteString(cont.ID)
	      buffer.WriteString("---")
	   }

	   ch <- buffer.String()

	}(id)

	return ctx.OK([]byte(<-ch))

	// ResourceController_Create: end_implement
	return nil
}
<commit_msg>start created contaienr<commit_after>package main

import (
	"github.com/goadesign/goa"
	"github.com/perigee/terrant/app"
	"github.com/fsouza/go-dockerclient"

)

// ResourceController implements the resource resource.
type ResourceController struct {
	*goa.Controller
}

// NewResourceController creates a resource controller.
func NewResourceController(service *goa.Service) *ResourceController {
	return &ResourceController{Controller: service.NewController("ResourceController")}
}

// Create runs the create action.
func (c *ResourceController) Create(ctx *app.CreateResourceContext) error {
	// ResourceController_Create: start_implement

	// Put your logic here
	id := ctx.ResourceID

	ch := make(chan string)
	endpoint := "unix:///var/run/docker.sock"

	client, err := docker.NewClient(endpoint)

	if err != nil {
	   panic(err)
	}

	go func(id string) {

	   //containers, err := client.ListContainers(docker.ListContainersOptions{All: false})

	   config := new(docker.Config)
	   config.Image = id

	   container, err := client.CreateContainer(docker.CreateContainerOptions{Name:"haha", Config: config})

	   if err != nil {
	      panic(err)
	   }

	   if err := client.StartContainer(container.ID, new(docker.HostConfig)); err  != nil {
	      panic(err)
	   }

	   ch <- container.ID

	}(id)

	return ctx.OK([]byte(<-ch))

	// ResourceController_Create: end_implement
	return nil
}
<|endoftext|>


<commit_before>package fire

import (
	"net/http"
	"reflect"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/manyminds/api2go"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// An Action describes the currently called action on the API.
type Action int

// All the available actions.
const (
	FindAll Action = iota
	FindOne
	Create
	Update
	Delete
)

// A Context provides useful contextual information to callbacks.
type Context struct {
	// The action that has been called.
	Action Action

	// The Model that will be saved during Create and Update.
	Model Model

	// The query that will be used during FindAll, FindOne, Update and Delete.
	//
	// Note: On FindOne, Update and Delete, the "_id" key is already set to the
	// document ID. On FindAll all ordinary filters and relationship filters are
	// also already present.
	Query bson.M

	// The sorting that will be used during FindAll.
	//
	// Note: This list will already include allowed sorting parameters passed
	// in the query parameters.
	Sorting []string

	// The db used to query.
	DB *mgo.Database

	// The underlying gin.context.
	GinContext *gin.Context

	// The underlying api2go.Request
	API2GoReq *api2go.Request
}

// TODO: Use intermediate ResourceDefinition to hide api2go specifics?

// A Resource provides an interface to a model.
//
// Note: Resources must not be modified after adding to an Endpoint.
type Resource struct {
	// The model that this resource should provide (e.g. &Foo{}).
	Model Model

	// The Authorizer is run on all actions. Will return an Unauthorized status
	// if an user error is returned.
	Authorizer Callback

	// The Validator is run to validate Create, Update and Delete actions. Will
	// return a Bad Request status if an user error is returned.
	Validator Callback

	adapter  *adapter
	endpoint *Endpoint
}

/* api2go interface */

// InitializeObject implements the api2go.ObjectInitializer interface.
func (r *Resource) InitializeObject(obj interface{}) {
	// initialize model
	Init(obj.(Model))
}

// FindAll implements the api2go.FindAll interface.
func (r *Resource) FindAll(req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(FindAll, &req)
	ctx.Query = bson.M{}

	// set self referencing and to one relationship filters
	r.setRelationshipFilters(ctx)

	// add filters
	for _, attr := range r.Model.getBase().attributesByTag("filterable") {
		if values, ok := ctx.API2GoReq.QueryParams["filter["+attr.jsonName+"]"]; ok {
			ctx.Query[attr.bsonName] = bson.M{"$in": values}
		}
	}

	// add sorting
	if fields, ok := req.QueryParams["sort"]; ok {
		for _, field := range fields {
			for _, attr := range r.Model.getBase().attributesByTag("sortable") {
				if field == attr.bsonName || field == "-"+attr.bsonName {
					ctx.Sorting = append(ctx.Sorting, field)
				}
			}
		}
	}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// prepare slice
	pointer := newSlicePointer(r.Model)

	// query db
	err := r.endpoint.db.C(r.Model.Collection()).Find(ctx.Query).Sort(ctx.Sorting...).All(pointer)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while retrieving resources", http.StatusInternalServerError)
	}

	// get content from pointer
	slice := sliceContent(pointer)

	// initialize each model
	s := reflect.ValueOf(slice)
	for i := 0; i < s.Len(); i++ {
		Init(s.Index(i).Interface().(Model))
	}

	return &response{Data: slice}, nil
}

// FindOne implements a part of the api2go.CRUD interface.
func (r *Resource) FindOne(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(FindOne, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// prepare object
	obj := newStructPointer(r.Model)

	// query db
	err := r.endpoint.db.C(r.Model.Collection()).Find(ctx.Query).One(obj)
	if err == mgo.ErrNotFound {
		return nil, api2go.NewHTTPError(err, "resource not found", http.StatusNotFound)
	} else if err != nil {
		return nil, api2go.NewHTTPError(err, "error while retrieving resource", http.StatusInternalServerError)
	}

	// initialize model
	model := Init(obj.(Model))

	return &response{Data: model}, nil
}

// Create implements a part of the api2go.CRUD interface.
func (r *Resource) Create(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Create, &req)
	ctx.Model = obj.(Model)

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// validate model
	err := ctx.Model.Validate(true)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, *err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Collection()).Insert(ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while saving resource", http.StatusInternalServerError)
	}

	return &response{Data: ctx.Model, Code: http.StatusCreated}, nil
}

// Update implements a part of the api2go.CRUD interface.
func (r *Resource) Update(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Update, &req)
	ctx.Model = obj.(Model)
	ctx.Query = bson.M{"_id": ctx.Model.ID()}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// validate model
	err := ctx.Model.Validate(false)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, *err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Collection()).Update(ctx.Query, ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while updating resource", http.StatusInternalServerError)
	}

	return &response{Data: ctx.Model}, nil
}

// Delete implements a part of the api2go.CRUD interface.
func (r *Resource) Delete(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(Delete, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, *err
	}

	// query db
	err := r.endpoint.db.C(r.Model.Collection()).Remove(ctx.Query)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while deleting resource", http.StatusInternalServerError)
	}

	return &response{Code: http.StatusNoContent}, nil
}

func (r *Resource) buildContext(act Action, req *api2go.Request) *Context {
	return &Context{
		Action:     act,
		DB:         r.endpoint.db,
		GinContext: r.adapter.getContext(req),
		API2GoReq:  req,
	}
}

func (r *Resource) setRelationshipFilters(ctx *Context) {
	for param, values := range ctx.API2GoReq.QueryParams {
		// remove *Name params as not needed
		if strings.HasSuffix(param, "Name") {
			delete(ctx.API2GoReq.QueryParams, param)
		}

		// handle *ID params
		if strings.HasSuffix(param, "ID") {
			// remove param in any case
			delete(ctx.API2GoReq.QueryParams, param)

			// get plural name
			pluralName := strings.Replace(param, "ID", "", 1)

			// get singular name and continue if not existing
			singularName, ok := r.endpoint.nameMap[pluralName]
			if !ok {
				continue
			}

			// check if self referencing
			if singularName == r.Model.getBase().singularName {
				ctx.Query["_id"] = bson.M{"$in": stringsToIDs(values)}
			}

			// add to one relationship filters
			for _, rel := range r.Model.getBase().toOneRelationships {
				if rel.name == singularName {
					ctx.Query[rel.bsonName] = bson.M{"$in": stringsToIDs(values)}
				}
			}
		}
	}
}

func (r *Resource) runCallback(cb Callback, ctx *Context, errorStatus int) *api2go.HTTPError {
	// check if callback is available
	if cb != nil {
		err := cb(ctx)
		if isFatal(err) {
			// return system error
			httpErr := api2go.NewHTTPError(err, "internal server error", http.StatusInternalServerError)
			return &httpErr
		}
		if err != nil {
			// return user error
			httpErr := api2go.NewHTTPError(nil, err.Error(), errorStatus)
			return &httpErr
		}
	}

	return nil
}
<commit_msg>not necessary<commit_after>package fire

import (
	"net/http"
	"reflect"
	"strings"

	"github.com/gin-gonic/gin"
	"github.com/manyminds/api2go"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

// An Action describes the currently called action on the API.
type Action int

// All the available actions.
const (
	FindAll Action = iota
	FindOne
	Create
	Update
	Delete
)

// A Context provides useful contextual information to callbacks.
type Context struct {
	// The action that has been called.
	Action Action

	// The Model that will be saved during Create and Update.
	Model Model

	// The query that will be used during FindAll, FindOne, Update and Delete.
	//
	// Note: On FindOne, Update and Delete, the "_id" key is already set to the
	// document ID. On FindAll all ordinary filters and relationship filters are
	// also already present.
	Query bson.M

	// The sorting that will be used during FindAll.
	//
	// Note: This list will already include allowed sorting parameters passed
	// in the query parameters.
	Sorting []string

	// The db used to query.
	DB *mgo.Database

	// The underlying gin.context.
	GinContext *gin.Context

	// The underlying api2go.Request
	API2GoReq *api2go.Request
}

// A Resource provides an interface to a model.
//
// Note: Resources must not be modified after adding to an Endpoint.
type Resource struct {
	// The model that this resource should provide (e.g. &Foo{}).
	Model Model

	// The Authorizer is run on all actions. Will return an Unauthorized status
	// if an user error is returned.
	Authorizer Callback

	// The Validator is run to validate Create, Update and Delete actions. Will
	// return a Bad Request status if an user error is returned.
	Validator Callback

	adapter  *adapter
	endpoint *Endpoint
}

/* api2go interface */

// InitializeObject implements the api2go.ObjectInitializer interface.
func (r *Resource) InitializeObject(obj interface{}) {
	// initialize model
	Init(obj.(Model))
}

// FindAll implements the api2go.FindAll interface.
func (r *Resource) FindAll(req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(FindAll, &req)
	ctx.Query = bson.M{}

	// set self referencing and to one relationship filters
	r.setRelationshipFilters(ctx)

	// add filters
	for _, attr := range r.Model.getBase().attributesByTag("filterable") {
		if values, ok := ctx.API2GoReq.QueryParams["filter["+attr.jsonName+"]"]; ok {
			ctx.Query[attr.bsonName] = bson.M{"$in": values}
		}
	}

	// add sorting
	if fields, ok := req.QueryParams["sort"]; ok {
		for _, field := range fields {
			for _, attr := range r.Model.getBase().attributesByTag("sortable") {
				if field == attr.bsonName || field == "-"+attr.bsonName {
					ctx.Sorting = append(ctx.Sorting, field)
				}
			}
		}
	}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// prepare slice
	pointer := newSlicePointer(r.Model)

	// query db
	err := r.endpoint.db.C(r.Model.Collection()).Find(ctx.Query).Sort(ctx.Sorting...).All(pointer)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while retrieving resources", http.StatusInternalServerError)
	}

	// get content from pointer
	slice := sliceContent(pointer)

	// initialize each model
	s := reflect.ValueOf(slice)
	for i := 0; i < s.Len(); i++ {
		Init(s.Index(i).Interface().(Model))
	}

	return &response{Data: slice}, nil
}

// FindOne implements a part of the api2go.CRUD interface.
func (r *Resource) FindOne(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(FindOne, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// prepare object
	obj := newStructPointer(r.Model)

	// query db
	err := r.endpoint.db.C(r.Model.Collection()).Find(ctx.Query).One(obj)
	if err == mgo.ErrNotFound {
		return nil, api2go.NewHTTPError(err, "resource not found", http.StatusNotFound)
	} else if err != nil {
		return nil, api2go.NewHTTPError(err, "error while retrieving resource", http.StatusInternalServerError)
	}

	// initialize model
	model := Init(obj.(Model))

	return &response{Data: model}, nil
}

// Create implements a part of the api2go.CRUD interface.
func (r *Resource) Create(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Create, &req)
	ctx.Model = obj.(Model)

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// validate model
	err := ctx.Model.Validate(true)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, *err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Collection()).Insert(ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while saving resource", http.StatusInternalServerError)
	}

	return &response{Data: ctx.Model, Code: http.StatusCreated}, nil
}

// Update implements a part of the api2go.CRUD interface.
func (r *Resource) Update(obj interface{}, req api2go.Request) (api2go.Responder, error) {
	// build context
	ctx := r.buildContext(Update, &req)
	ctx.Model = obj.(Model)
	ctx.Query = bson.M{"_id": ctx.Model.ID()}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// validate model
	err := ctx.Model.Validate(false)
	if err != nil {
		return nil, api2go.NewHTTPError(nil, err.Error(), http.StatusBadRequest)
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, *err
	}

	// query db
	err = r.endpoint.db.C(r.Model.Collection()).Update(ctx.Query, ctx.Model)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while updating resource", http.StatusInternalServerError)
	}

	return &response{Data: ctx.Model}, nil
}

// Delete implements a part of the api2go.CRUD interface.
func (r *Resource) Delete(id string, req api2go.Request) (api2go.Responder, error) {
	// validate id
	if !bson.IsObjectIdHex(id) {
		return nil, api2go.NewHTTPError(nil, "invalid id", http.StatusBadRequest)
	}

	// build context
	ctx := r.buildContext(Delete, &req)
	ctx.Query = bson.M{"_id": bson.ObjectIdHex(id)}

	// run authorizer if available
	if err := r.runCallback(r.Authorizer, ctx, http.StatusUnauthorized); err != nil {
		return nil, *err
	}

	// run validator if available
	if err := r.runCallback(r.Validator, ctx, http.StatusBadRequest); err != nil {
		return nil, *err
	}

	// query db
	err := r.endpoint.db.C(r.Model.Collection()).Remove(ctx.Query)
	if err != nil {
		return nil, api2go.NewHTTPError(err, "error while deleting resource", http.StatusInternalServerError)
	}

	return &response{Code: http.StatusNoContent}, nil
}

func (r *Resource) buildContext(act Action, req *api2go.Request) *Context {
	return &Context{
		Action:     act,
		DB:         r.endpoint.db,
		GinContext: r.adapter.getContext(req),
		API2GoReq:  req,
	}
}

func (r *Resource) setRelationshipFilters(ctx *Context) {
	for param, values := range ctx.API2GoReq.QueryParams {
		// remove *Name params as not needed
		if strings.HasSuffix(param, "Name") {
			delete(ctx.API2GoReq.QueryParams, param)
		}

		// handle *ID params
		if strings.HasSuffix(param, "ID") {
			// remove param in any case
			delete(ctx.API2GoReq.QueryParams, param)

			// get plural name
			pluralName := strings.Replace(param, "ID", "", 1)

			// get singular name and continue if not existing
			singularName, ok := r.endpoint.nameMap[pluralName]
			if !ok {
				continue
			}

			// check if self referencing
			if singularName == r.Model.getBase().singularName {
				ctx.Query["_id"] = bson.M{"$in": stringsToIDs(values)}
			}

			// add to one relationship filters
			for _, rel := range r.Model.getBase().toOneRelationships {
				if rel.name == singularName {
					ctx.Query[rel.bsonName] = bson.M{"$in": stringsToIDs(values)}
				}
			}
		}
	}
}

func (r *Resource) runCallback(cb Callback, ctx *Context, errorStatus int) *api2go.HTTPError {
	// check if callback is available
	if cb != nil {
		err := cb(ctx)
		if isFatal(err) {
			// return system error
			httpErr := api2go.NewHTTPError(err, "internal server error", http.StatusInternalServerError)
			return &httpErr
		}
		if err != nil {
			// return user error
			httpErr := api2go.NewHTTPError(nil, err.Error(), errorStatus)
			return &httpErr
		}
	}

	return nil
}
<|endoftext|>


<commit_before>package httpexpect

import (
	"bytes"
	"encoding/json"
	"github.com/ajg/form"
	"io/ioutil"
	"mime"
	"net/http"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// StatusRange is enum for response status ranges.
type StatusRange int

const (
	// Status1xx defines "Informational" status codes.
	Status1xx StatusRange = 100

	// Status2xx defines "Success" status codes.
	Status2xx StatusRange = 200

	// Status3xx defines "Redirection" status codes.
	Status3xx StatusRange = 300

	// Status4xx defines "Client Error" status codes.
	Status4xx StatusRange = 400

	// Status5xx defines "Server Error" status codes.
	Status5xx StatusRange = 500
)

// Response provides methods to inspect attached http.Response object.
type Response struct {
	chain   chain
	resp    *http.Response
	content []byte
	cookies []*http.Cookie
	time    time.Duration
}

// NewResponse returns a new Response given a reporter used to report
// failures and http.Response to be inspected.
//
// Both reporter and response should not be nil. If response is nil,
// failure is reported.
//
// If duration is given, it defines response time to be reported by
// response.Duration().
func NewResponse(
	reporter Reporter, response *http.Response, duration ...time.Duration) *Response {
	var dr time.Duration
	if len(duration) > 0 {
		dr = duration[0]
	}
	return makeResponse(makeChain(reporter), response, dr)
}

func makeResponse(chain chain, response *http.Response, duration time.Duration) *Response {
	var content []byte
	var cookies []*http.Cookie
	if response != nil {
		content = getContent(&chain, response)
		cookies = response.Cookies()
	} else {
		chain.fail("expected non-nil response")
	}
	return &Response{
		chain:   chain,
		resp:    response,
		content: content,
		cookies: cookies,
		time:    duration,
	}
}

func getContent(chain *chain, resp *http.Response) []byte {
	if resp.Body == nil {
		return []byte{}
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		chain.fail(err.Error())
		return nil
	}

	return content
}

// Raw returns underlying http.Response object.
// This is the value originally passed to NewResponse.
func (r *Response) Raw() *http.Response {
	return r.resp
}

// Duration returns a new Number object that may be used to inspect
// response time, in nanoseconds.
//
// Response time is a time interval starting just before request is sent
// and ending right after response is received, retrieved from monotonic
// clock source.
//
// Example:
//  resp := NewResponse(t, response, time.Duration(10000000))
//  resp.Duration().Equal(10 * time.Millisecond)
func (r *Response) Duration() *Number {
	return &Number{r.chain, float64(r.time)}
}

// Status succeedes if response contains given status code.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Status(http.StatusOK)
func (r *Response) Status(status int) *Response {
	if r.chain.failed() {
		return r
	}
	r.checkEqual("status", statusCodeText(status), statusCodeText(r.resp.StatusCode))
	return r
}

// StatusRange succeedes if response status belongs to given range.
//
// Supported ranges:
//  - Status1xx - Informational
//  - Status2xx - Success
//  - Status3xx - Redirection
//  - Status4xx - Client Error
//  - Status5xx - Server Error
//
// See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.StatusRange(Status2xx)
func (r *Response) StatusRange(rn StatusRange) *Response {
	if r.chain.failed() {
		return r
	}

	status := statusCodeText(r.resp.StatusCode)

	actual := statusRangeText(r.resp.StatusCode)
	expected := statusRangeText(int(rn))

	if actual == "" || actual != expected {
		if actual == "" {
			r.chain.fail("\nexpected status from range:\n %q\n\nbut got:\n %q",
				expected, status)
		} else {
			r.chain.fail(
				"\nexpected status from range:\n %q\n\nbut got:\n %q (%q)",
				expected, actual, status)
		}
	}

	return r
}

func statusCodeText(code int) string {
	if s := http.StatusText(code); s != "" {
		return strconv.Itoa(code) + " " + s
	}
	return strconv.Itoa(code)
}

func statusRangeText(code int) string {
	switch {
	case code >= 100 && code < 200:
		return "1xx Informational"
	case code >= 200 && code < 300:
		return "2xx Success"
	case code >= 300 && code < 400:
		return "3xx Redirection"
	case code >= 400 && code < 500:
		return "4xx Client Error"
	case code >= 500 && code < 600:
		return "5xx Server Error"
	default:
		return ""
	}
}

// Headers returns a new Object that may be used to inspect header map.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Headers().Value("Content-Type").String().Equal("application-json")
func (r *Response) Headers() *Object {
	var value map[string]interface{}
	if !r.chain.failed() {
		value, _ = canonMap(&r.chain, r.resp.Header)
	}
	return &Object{r.chain, value}
}

// Header returns a new String object that may be used to inspect given header.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Header("Content-Type").Equal("application-json")
//  resp.Header("Date").DateTime().Le(time.Now())
func (r *Response) Header(header string) *String {
	value := ""
	if !r.chain.failed() {
		value = r.resp.Header.Get(header)
	}
	return &String{r.chain, value}
}

// Cookies returns a new Array object with all available cookie names.
// Returned Array contains a String value for every cookie name.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Cookies().Contains("session")
func (r *Response) Cookies() *Array {
	if r.chain.failed() {
		return &Array{r.chain, nil}
	}
	names := []interface{}{}
	for _, c := range r.cookies {
		names = append(names, c.Name)
	}
	return &Array{r.chain, names}
}

// Cookie returns a new Cookie object that may be used to inspect given cookie.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Cookie("session").Domain().Equal("example.com")
func (r *Response) Cookie(name string) *Cookie {
	if r.chain.failed() {
		return &Cookie{r.chain, nil}
	}
	names := []string{}
	for _, c := range r.cookies {
		if c.Name == name {
			return &Cookie{r.chain, c}
		}
		names = append(names, c.Name)
	}
	r.chain.fail("\nexpected response with cookie:\n %q\n\nbut got only cookies:\n%s",
		name, dumpValue(names))
	return &Cookie{r.chain, nil}
}

// Body returns a new String object that may be used to inspect response body.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Body().NotEmpty()
//  resp.Body().Length().Equal(100)
func (r *Response) Body() *String {
	return &String{r.chain, string(r.content)}
}

// NoContent succeedes if response contains empty Content-Type header and
// empty body.
func (r *Response) NoContent() *Response {
	if r.chain.failed() {
		return r
	}

	contentType := r.resp.Header.Get("Content-Type")

	r.checkEqual("\"Content-Type\" header", "", contentType)
	r.checkEqual("body", "", string(r.content))

	return r
}

// ContentType succeedes if response contains Content-Type header with given
// media type and charset.
//
// If charset is omitted, and mediaType is non-empty, Content-Type header
// should contain empty or utf-8 charset.
//
// If charset is omitted, and mediaType is also empty, Content-Type header
// should contain no charset.
func (r *Response) ContentType(mediaType string, charset ...string) *Response {
	r.checkContentType(mediaType, charset...)
	return r
}

// TransferEncoding succeedes if response contains given Transfer-Encoding list.
// Common values are empty, "chunked" and "identity".
func (r *Response) TransferEncoding(encoding ...string) *Response {
	if r.chain.failed() {
		return r
	}
	r.checkEqual("\"Transfer-Encoding\" header", encoding, r.resp.TransferEncoding)
	return r
}

// Text returns a new String object that may be used to inspect response body.
//
// Text succeedes if response contains "text/plain" Content-Type header
// with empty or "utf-8" charset.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Text().Equal("hello, world!")
func (r *Response) Text() *String {
	var content string

	if !r.chain.failed() && r.checkContentType("text/plain") {
		content = string(r.content)
	}

	return &String{r.chain, content}
}

// Form returns a new Object that may be used to inspect form contents
// of response.
//
// Form succeedes if response contains "application/x-www-form-urlencoded"
// Content-Type header and if form may be decoded from response body.
// Decoding is performed using https://github.com/ajg/form.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Form().Value("foo").Equal("bar")
func (r *Response) Form() *Object {
	object := r.getForm()
	return &Object{r.chain, object}
}

func (r *Response) getForm() map[string]interface{} {
	if r.chain.failed() {
		return nil
	}

	if !r.checkContentType("application/x-www-form-urlencoded", "") {
		return nil
	}

	decoder := form.NewDecoder(bytes.NewReader(r.content))

	var object map[string]interface{}
	if err := decoder.Decode(&object); err != nil {
		r.chain.fail(err.Error())
		return nil
	}

	return object
}

// JSON returns a new Value object that may be used to inspect JSON contents
// of response.
//
// JSON succeedes if response contains "application/json" Content-Type header
// with empty or "utf-8" charset and if JSON may be decoded from response body.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.JSON().Array().Elements("foo", "bar")
func (r *Response) JSON() *Value {
	value := r.getJSON()
	return &Value{r.chain, value}
}

func (r *Response) getJSON() interface{} {
	if r.chain.failed() {
		return nil
	}

	if !r.checkContentType("application/json") {
		return nil
	}

	var value interface{}
	if err := json.Unmarshal(r.content, &value); err != nil {
		r.chain.fail(err.Error())
		return nil
	}

	return value
}

// JSONP returns a new Value object that may be used to inspect JSONP contents
// of response.
//
// JSONP succeedes if response contains "application/javascript" Content-Type
// header with empty or "utf-8" charset and reponse body of the following form:
//  callback(<valid json>);
// or:
//  callback(<valid json>)
//
// Whitespaces are allowed.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.JSONP("myCallback").Array().Elements("foo", "bar")
func (r *Response) JSONP(callback string) *Value {
	value := r.getJSONP(callback)
	return &Value{r.chain, value}
}

var (
	jsonp = regexp.MustCompile(`^\s*([^\s(]+)\s*\((.*)\)\s*;*\s*$`)
)

func (r *Response) getJSONP(callback string) interface{} {
	if r.chain.failed() {
		return nil
	}

	if !r.checkContentType("application/javascript") {
		return nil
	}

	m := jsonp.FindSubmatch(r.content)
	if len(m) != 3 || string(m[1]) != callback {
		r.chain.fail(
			"\nexpected JSONP body in form of:\n \"%s(<valid json>)\"\n\nbut got:\n %q\n",
			callback,
			string(r.content))
		return nil
	}

	var value interface{}
	if err := json.Unmarshal(m[2], &value); err != nil {
		r.chain.fail(err.Error())
		return nil
	}

	return value
}

func (r *Response) checkContentType(expectedType string, expectedCharset ...string) bool {
	if r.chain.failed() {
		return false
	}

	contentType := r.resp.Header.Get("Content-Type")

	if expectedType == "" && len(expectedCharset) == 0 {
		if contentType == "" {
			return true
		}
	}

	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		r.chain.fail("\ngot invalid \"Content-Type\" header %q", contentType)
		return false
	}

	if mediaType != expectedType {
		r.chain.fail(
			"\nexpected \"Content-Type\" header with %q media type,"+
				"\nbut got %q", expectedType, mediaType)
		return false
	}

	charset := params["charset"]

	if len(expectedCharset) == 0 {
		if charset != "" && !strings.EqualFold(charset, "utf-8") {
			r.chain.fail(
				"\nexpected \"Content-Type\" header with \"utf-8\" or empty charset,"+
					"\nbut got %q", charset)
			return false
		}
	} else {
		if !strings.EqualFold(charset, expectedCharset[0]) {
			r.chain.fail(
				"\nexpected \"Content-Type\" header with %q charset,"+
					"\nbut got %q", expectedCharset[0], charset)
			return false
		}
	}

	return true
}

func (r *Response) checkEqual(what string, expected, actual interface{}) {
	if !reflect.DeepEqual(expected, actual) {
		r.chain.fail("\nexpected %s equal to:\n%s\n\nbut got:\n%s", what,
			dumpValue(expected), dumpValue(actual))
	}
}
<commit_msg>Clarify Cookie documentation (#18)<commit_after>package httpexpect

import (
	"bytes"
	"encoding/json"
	"github.com/ajg/form"
	"io/ioutil"
	"mime"
	"net/http"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
)

// StatusRange is enum for response status ranges.
type StatusRange int

const (
	// Status1xx defines "Informational" status codes.
	Status1xx StatusRange = 100

	// Status2xx defines "Success" status codes.
	Status2xx StatusRange = 200

	// Status3xx defines "Redirection" status codes.
	Status3xx StatusRange = 300

	// Status4xx defines "Client Error" status codes.
	Status4xx StatusRange = 400

	// Status5xx defines "Server Error" status codes.
	Status5xx StatusRange = 500
)

// Response provides methods to inspect attached http.Response object.
type Response struct {
	chain   chain
	resp    *http.Response
	content []byte
	cookies []*http.Cookie
	time    time.Duration
}

// NewResponse returns a new Response given a reporter used to report
// failures and http.Response to be inspected.
//
// Both reporter and response should not be nil. If response is nil,
// failure is reported.
//
// If duration is given, it defines response time to be reported by
// response.Duration().
func NewResponse(
	reporter Reporter, response *http.Response, duration ...time.Duration) *Response {
	var dr time.Duration
	if len(duration) > 0 {
		dr = duration[0]
	}
	return makeResponse(makeChain(reporter), response, dr)
}

func makeResponse(chain chain, response *http.Response, duration time.Duration) *Response {
	var content []byte
	var cookies []*http.Cookie
	if response != nil {
		content = getContent(&chain, response)
		cookies = response.Cookies()
	} else {
		chain.fail("expected non-nil response")
	}
	return &Response{
		chain:   chain,
		resp:    response,
		content: content,
		cookies: cookies,
		time:    duration,
	}
}

func getContent(chain *chain, resp *http.Response) []byte {
	if resp.Body == nil {
		return []byte{}
	}

	content, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		chain.fail(err.Error())
		return nil
	}

	return content
}

// Raw returns underlying http.Response object.
// This is the value originally passed to NewResponse.
func (r *Response) Raw() *http.Response {
	return r.resp
}

// Duration returns a new Number object that may be used to inspect
// response time, in nanoseconds.
//
// Response time is a time interval starting just before request is sent
// and ending right after response is received, retrieved from monotonic
// clock source.
//
// Example:
//  resp := NewResponse(t, response, time.Duration(10000000))
//  resp.Duration().Equal(10 * time.Millisecond)
func (r *Response) Duration() *Number {
	return &Number{r.chain, float64(r.time)}
}

// Status succeedes if response contains given status code.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Status(http.StatusOK)
func (r *Response) Status(status int) *Response {
	if r.chain.failed() {
		return r
	}
	r.checkEqual("status", statusCodeText(status), statusCodeText(r.resp.StatusCode))
	return r
}

// StatusRange succeedes if response status belongs to given range.
//
// Supported ranges:
//  - Status1xx - Informational
//  - Status2xx - Success
//  - Status3xx - Redirection
//  - Status4xx - Client Error
//  - Status5xx - Server Error
//
// See https://en.wikipedia.org/wiki/List_of_HTTP_status_codes.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.StatusRange(Status2xx)
func (r *Response) StatusRange(rn StatusRange) *Response {
	if r.chain.failed() {
		return r
	}

	status := statusCodeText(r.resp.StatusCode)

	actual := statusRangeText(r.resp.StatusCode)
	expected := statusRangeText(int(rn))

	if actual == "" || actual != expected {
		if actual == "" {
			r.chain.fail("\nexpected status from range:\n %q\n\nbut got:\n %q",
				expected, status)
		} else {
			r.chain.fail(
				"\nexpected status from range:\n %q\n\nbut got:\n %q (%q)",
				expected, actual, status)
		}
	}

	return r
}

func statusCodeText(code int) string {
	if s := http.StatusText(code); s != "" {
		return strconv.Itoa(code) + " " + s
	}
	return strconv.Itoa(code)
}

func statusRangeText(code int) string {
	switch {
	case code >= 100 && code < 200:
		return "1xx Informational"
	case code >= 200 && code < 300:
		return "2xx Success"
	case code >= 300 && code < 400:
		return "3xx Redirection"
	case code >= 400 && code < 500:
		return "4xx Client Error"
	case code >= 500 && code < 600:
		return "5xx Server Error"
	default:
		return ""
	}
}

// Headers returns a new Object that may be used to inspect header map.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Headers().Value("Content-Type").String().Equal("application-json")
func (r *Response) Headers() *Object {
	var value map[string]interface{}
	if !r.chain.failed() {
		value, _ = canonMap(&r.chain, r.resp.Header)
	}
	return &Object{r.chain, value}
}

// Header returns a new String object that may be used to inspect given header.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Header("Content-Type").Equal("application-json")
//  resp.Header("Date").DateTime().Le(time.Now())
func (r *Response) Header(header string) *String {
	value := ""
	if !r.chain.failed() {
		value = r.resp.Header.Get(header)
	}
	return &String{r.chain, value}
}

// Cookies returns a new Array object with all cookie names set by this response.
// Returned Array contains a String value for every cookie name.
//
// Note that this returns only cookies set by Set-Cookie headers of this response.
// It doesn't return session cookies from previous responses, which may be stored
// in a cookie jar.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Cookies().Contains("session")
func (r *Response) Cookies() *Array {
	if r.chain.failed() {
		return &Array{r.chain, nil}
	}
	names := []interface{}{}
	for _, c := range r.cookies {
		names = append(names, c.Name)
	}
	return &Array{r.chain, names}
}

// Cookie returns a new Cookie object that may be used to inspect given cookie
// set by this response.
//
// Note that this returns only cookies set by Set-Cookie headers of this response.
// It doesn't return session cookies from previous responses, which may be stored
// in a cookie jar.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Cookie("session").Domain().Equal("example.com")
func (r *Response) Cookie(name string) *Cookie {
	if r.chain.failed() {
		return &Cookie{r.chain, nil}
	}
	names := []string{}
	for _, c := range r.cookies {
		if c.Name == name {
			return &Cookie{r.chain, c}
		}
		names = append(names, c.Name)
	}
	r.chain.fail("\nexpected response with cookie:\n %q\n\nbut got only cookies:\n%s",
		name, dumpValue(names))
	return &Cookie{r.chain, nil}
}

// Body returns a new String object that may be used to inspect response body.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Body().NotEmpty()
//  resp.Body().Length().Equal(100)
func (r *Response) Body() *String {
	return &String{r.chain, string(r.content)}
}

// NoContent succeedes if response contains empty Content-Type header and
// empty body.
func (r *Response) NoContent() *Response {
	if r.chain.failed() {
		return r
	}

	contentType := r.resp.Header.Get("Content-Type")

	r.checkEqual("\"Content-Type\" header", "", contentType)
	r.checkEqual("body", "", string(r.content))

	return r
}

// ContentType succeedes if response contains Content-Type header with given
// media type and charset.
//
// If charset is omitted, and mediaType is non-empty, Content-Type header
// should contain empty or utf-8 charset.
//
// If charset is omitted, and mediaType is also empty, Content-Type header
// should contain no charset.
func (r *Response) ContentType(mediaType string, charset ...string) *Response {
	r.checkContentType(mediaType, charset...)
	return r
}

// TransferEncoding succeedes if response contains given Transfer-Encoding list.
// Common values are empty, "chunked" and "identity".
func (r *Response) TransferEncoding(encoding ...string) *Response {
	if r.chain.failed() {
		return r
	}
	r.checkEqual("\"Transfer-Encoding\" header", encoding, r.resp.TransferEncoding)
	return r
}

// Text returns a new String object that may be used to inspect response body.
//
// Text succeedes if response contains "text/plain" Content-Type header
// with empty or "utf-8" charset.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Text().Equal("hello, world!")
func (r *Response) Text() *String {
	var content string

	if !r.chain.failed() && r.checkContentType("text/plain") {
		content = string(r.content)
	}

	return &String{r.chain, content}
}

// Form returns a new Object that may be used to inspect form contents
// of response.
//
// Form succeedes if response contains "application/x-www-form-urlencoded"
// Content-Type header and if form may be decoded from response body.
// Decoding is performed using https://github.com/ajg/form.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.Form().Value("foo").Equal("bar")
func (r *Response) Form() *Object {
	object := r.getForm()
	return &Object{r.chain, object}
}

func (r *Response) getForm() map[string]interface{} {
	if r.chain.failed() {
		return nil
	}

	if !r.checkContentType("application/x-www-form-urlencoded", "") {
		return nil
	}

	decoder := form.NewDecoder(bytes.NewReader(r.content))

	var object map[string]interface{}
	if err := decoder.Decode(&object); err != nil {
		r.chain.fail(err.Error())
		return nil
	}

	return object
}

// JSON returns a new Value object that may be used to inspect JSON contents
// of response.
//
// JSON succeedes if response contains "application/json" Content-Type header
// with empty or "utf-8" charset and if JSON may be decoded from response body.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.JSON().Array().Elements("foo", "bar")
func (r *Response) JSON() *Value {
	value := r.getJSON()
	return &Value{r.chain, value}
}

func (r *Response) getJSON() interface{} {
	if r.chain.failed() {
		return nil
	}

	if !r.checkContentType("application/json") {
		return nil
	}

	var value interface{}
	if err := json.Unmarshal(r.content, &value); err != nil {
		r.chain.fail(err.Error())
		return nil
	}

	return value
}

// JSONP returns a new Value object that may be used to inspect JSONP contents
// of response.
//
// JSONP succeedes if response contains "application/javascript" Content-Type
// header with empty or "utf-8" charset and reponse body of the following form:
//  callback(<valid json>);
// or:
//  callback(<valid json>)
//
// Whitespaces are allowed.
//
// Example:
//  resp := NewResponse(t, response)
//  resp.JSONP("myCallback").Array().Elements("foo", "bar")
func (r *Response) JSONP(callback string) *Value {
	value := r.getJSONP(callback)
	return &Value{r.chain, value}
}

var (
	jsonp = regexp.MustCompile(`^\s*([^\s(]+)\s*\((.*)\)\s*;*\s*$`)
)

func (r *Response) getJSONP(callback string) interface{} {
	if r.chain.failed() {
		return nil
	}

	if !r.checkContentType("application/javascript") {
		return nil
	}

	m := jsonp.FindSubmatch(r.content)
	if len(m) != 3 || string(m[1]) != callback {
		r.chain.fail(
			"\nexpected JSONP body in form of:\n \"%s(<valid json>)\"\n\nbut got:\n %q\n",
			callback,
			string(r.content))
		return nil
	}

	var value interface{}
	if err := json.Unmarshal(m[2], &value); err != nil {
		r.chain.fail(err.Error())
		return nil
	}

	return value
}

func (r *Response) checkContentType(expectedType string, expectedCharset ...string) bool {
	if r.chain.failed() {
		return false
	}

	contentType := r.resp.Header.Get("Content-Type")

	if expectedType == "" && len(expectedCharset) == 0 {
		if contentType == "" {
			return true
		}
	}

	mediaType, params, err := mime.ParseMediaType(contentType)
	if err != nil {
		r.chain.fail("\ngot invalid \"Content-Type\" header %q", contentType)
		return false
	}

	if mediaType != expectedType {
		r.chain.fail(
			"\nexpected \"Content-Type\" header with %q media type,"+
				"\nbut got %q", expectedType, mediaType)
		return false
	}

	charset := params["charset"]

	if len(expectedCharset) == 0 {
		if charset != "" && !strings.EqualFold(charset, "utf-8") {
			r.chain.fail(
				"\nexpected \"Content-Type\" header with \"utf-8\" or empty charset,"+
					"\nbut got %q", charset)
			return false
		}
	} else {
		if !strings.EqualFold(charset, expectedCharset[0]) {
			r.chain.fail(
				"\nexpected \"Content-Type\" header with %q charset,"+
					"\nbut got %q", expectedCharset[0], charset)
			return false
		}
	}

	return true
}

func (r *Response) checkEqual(what string, expected, actual interface{}) {
	if !reflect.DeepEqual(expected, actual) {
		r.chain.fail("\nexpected %s equal to:\n%s\n\nbut got:\n%s", what,
			dumpValue(expected), dumpValue(actual))
	}
}
<|endoftext|>


<commit_before>// common.go
//
// This file implements the configuration part for when you need the API
// key to modify things in the Atlas configuration and manage measurements.

package atlas

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
)

const (
	apiEndpoint = "https://atlas.ripe.net/api/v2"
)

// GetVersion returns the API wrapper version
func GetVersion() string {
	return ourVersion
}

// getPageNum returns the value of the page= parameter
func getPageNum(url string) (page string) {
	re := regexp.MustCompile(`page=(\d+)`)
	if m := re.FindStringSubmatch(url); len(m) >= 1 {
		return m[1]
	}
	return ""
}

// AddQueryParameters adds query parameters to the URL.
func AddQueryParameters(baseURL string, queryParams map[string]string) string {
	baseURL += "?"
	params := url.Values{}
	for key, value := range queryParams {
		params.Add(key, value)
	}
	return baseURL + params.Encode()
}

// prepareRequest insert all pre-defined stuff
func (c *Client) prepareRequest(method, what string, opts map[string]string) (req *http.Request) {
	var endPoint string

	// This is a hack to fetch direct urls for results
	if method == "FETCH" {
		endPoint = what
		method = "GET"
	} else {
		endPoint = fmt.Sprintf("%s/%s/", apiEndpoint, what)
	}

	key, ok := c.HasAPIKey()
	// Insert key
	if ok {
		opts["key"] = key
	}

	c.mergeGlobalOptions(opts)
	c.verbose("Options:\n%v", opts)
	baseURL := AddQueryParameters(endPoint, opts)

	req, err := http.NewRequest(method, baseURL, nil)
	if err != nil {
		c.log.Printf("error parsing %s: %v", baseURL, err)
		return &http.Request{}
	}

	// We need these when we POST
	if method == "POST" {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Accept", "application/json")
	}

	return
}

// client.handleAPIResponsese check status code & errors from the API
func (c *Client) handleAPIResponsese(r *http.Response) (err error) {
	if r == nil {
		return fmt.Errorf("error: r is nil")
	}

	// Everything is fine
	if r.StatusCode == 0 {
		return nil
	}

	// Everything is fine too
	if r.StatusCode >= 200 && r.StatusCode <= 299 {
		return nil
	}

	// Check this condition
	if r.StatusCode >= 300 && r.StatusCode <= 399 {
		var aerr APIError

		body, _ := ioutil.ReadAll(r.Body)
		defer r.Body.Close()

		err = json.Unmarshal(body, &aerr)
		if err != nil {
			c.log.Printf("Error handling error: %s - %v", r.Body, err)
		}

		c.log.Printf("Info 3XX status: %d code: %d - r:%v\n",
			aerr.Error.Status,
			aerr.Error.Code,
			aerr.Error.Detail)
		return nil
	}

	// EVerything else is an error
	var aerr APIError

	body, _ := ioutil.ReadAll(r.Body)
	defer r.Body.Close()

	err = json.Unmarshal(body, &aerr)
	if err != nil {
		c.log.Printf("Error handling error: %s - %v", r.Body, err)
	}

	err = fmt.Errorf("status: %d code: %d - r:%s\nerrors: %v",
		aerr.Error.Status,
		aerr.Error.Code,
		aerr.Error.Detail,
		aerr.Error.Errors)
	return
}
<commit_msg>Add addAPIKey() to be called whenever needed. Not all call want it.<commit_after>// common.go
//
// This file implements the configuration part for when you need the API
// key to modify things in the Atlas configuration and manage measurements.

package atlas

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
)

const (
	apiEndpoint = "https://atlas.ripe.net/api/v2"
)

// GetVersion returns the API wrapper version
func GetVersion() string {
	return ourVersion
}

// getPageNum returns the value of the page= parameter
func getPageNum(url string) (page string) {
	re := regexp.MustCompile(`page=(\d+)`)
	if m := re.FindStringSubmatch(url); len(m) >= 1 {
		return m[1]
	}
	return ""
}

// AddQueryParameters adds query parameters to the URL.
func AddQueryParameters(baseURL string, queryParams map[string]string) string {
	baseURL += "?"
	params := url.Values{}
	for key, value := range queryParams {
		params.Add(key, value)
	}
	return baseURL + params.Encode()
}

// addAPIKey insert the key into options if needed
func (c *Client) addAPIKey(opts map[string]string) map[string]string {
	key, ok := c.HasAPIKey()
	// Insert key
	if ok {
		opts["key"] = key
	}
	return opts
}

// prepareRequest insert all pre-defined stuff
func (c *Client) prepareRequest(method, what string, opts map[string]string) (req *http.Request) {
	var endPoint string

	// This is a hack to fetch direct urls for results
	if method == "FETCH" {
		endPoint = what
		method = "GET"
	} else {
		endPoint = fmt.Sprintf("%s/%s/", apiEndpoint, what)
	}

	c.mergeGlobalOptions(opts)
	c.verbose("Options:\n%v", opts)
	baseURL := AddQueryParameters(endPoint, opts)

	req, err := http.NewRequest(method, baseURL, nil)
	if err != nil {
		c.log.Printf("error parsing %s: %v", baseURL, err)
		return &http.Request{}
	}

	// We need these when we POST
	if method == "POST" {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set("Accept", "application/json")
	}

	return
}

// client.handleAPIResponsese check status code & errors from the API
func (c *Client) handleAPIResponsese(r *http.Response) (err error) {
	if r == nil {
		return fmt.Errorf("error: r is nil")
	}

	// Everything is fine
	if r.StatusCode == 0 {
		return nil
	}

	// Everything is fine too
	if r.StatusCode >= 200 && r.StatusCode <= 299 {
		return nil
	}

	// Check this condition
	if r.StatusCode >= 300 && r.StatusCode <= 399 {
		var aerr APIError

		body, _ := ioutil.ReadAll(r.Body)
		defer r.Body.Close()

		err = json.Unmarshal(body, &aerr)
		if err != nil {
			c.log.Printf("Error handling error: %s - %v", r.Body, err)
		}

		c.log.Printf("Info 3XX status: %d code: %d - r:%v\n",
			aerr.Error.Status,
			aerr.Error.Code,
			aerr.Error.Detail)
		return nil
	}

	// EVerything else is an error
	var aerr APIError

	body, _ := ioutil.ReadAll(r.Body)
	defer r.Body.Close()

	err = json.Unmarshal(body, &aerr)
	if err != nil {
		c.log.Printf("Error handling error: %s - %v", r.Body, err)
	}

	err = fmt.Errorf("status: %d code: %d - r:%s\nerrors: %v",
		aerr.Error.Status,
		aerr.Error.Code,
		aerr.Error.Detail,
		aerr.Error.Errors)
	return
}
<|endoftext|>


<commit_before>// common.go
//
// This file implements the configuration part for when you need the API
// key to modify things in the Atlas configuration and manage measurements.

package atlas

import (
    "regexp"
    "errors"
	"github.com/sendgrid/rest"
	"fmt"
)

const (
	apiEndpoint = "https://atlas.ripe.net/api/v2"

	ourVersion = "0.8"
)

// APIKey is the API key
var APIKey string

// ErrInvalidMeasurementType is a new error
var ErrInvalidMeasurementType = errors.New("invalid measurement type")

// ErrInvalidAPIKey is returned when the key is invalid
var ErrInvalidAPIKey = errors.New("invalid API key")

// SetAuth stores the credentials for later use
func SetAuth(key string) {
	APIKey = key
}

// HasAPIKey returns whether an API key is stored
func HasAPIKey() (string, bool) {
	if APIKey == "" {
		return "", false
	}
	return APIKey, true
}

// getPageNum returns the value of the page= parameter
func getPageNum(url string) (page string) {
	re := regexp.MustCompile(`page=(\d+)`)
	if m := re.FindStringSubmatch(url); len(m) >= 1 {
		return m[1]
	}
	return ""
}

// prepareRequest insert all pre-defined stuff
func prepareRequest(what string) (req rest.Request) {
	endPoint := apiEndpoint + fmt.Sprintf("/%s/", what)
	key, ok := HasAPIKey()

	// Add at least one option, the APIkey if present
	hdrs := make(map[string]string)
	opts := make(map[string]string)

	// Insert our sig
	hdrs["User-Agent"] = fmt.Sprintf("ripe-atlas/%s", ourVersion)

	// Insert key
	if ok {
		opts["key"] = key
	}

	req = rest.Request{
		BaseURL: endPoint,
		Headers: hdrs,
		QueryParams: opts,
	}
	return
}
<commit_msg>Add handleAPIResponse() to deal with API errors.<commit_after>// common.go
//
// This file implements the configuration part for when you need the API
// key to modify things in the Atlas configuration and manage measurements.

package atlas

import (
    "regexp"
    "errors"
	"github.com/sendgrid/rest"
	"fmt"
	"encoding/json"
)

const (
	apiEndpoint = "https://atlas.ripe.net/api/v2"

	ourVersion = "0.8"
)

// APIKey is the API key
var APIKey string

// ErrInvalidMeasurementType is a new error
var ErrInvalidMeasurementType = errors.New("invalid measurement type")

// ErrInvalidAPIKey is returned when the key is invalid
var ErrInvalidAPIKey = errors.New("invalid API key")

// SetAuth stores the credentials for later use
func SetAuth(key string) {
	APIKey = key
}

// HasAPIKey returns whether an API key is stored
func HasAPIKey() (string, bool) {
	if APIKey == "" {
		return "", false
	}
	return APIKey, true
}

// getPageNum returns the value of the page= parameter
func getPageNum(url string) (page string) {
	re := regexp.MustCompile(`page=(\d+)`)
	if m := re.FindStringSubmatch(url); len(m) >= 1 {
		return m[1]
	}
	return ""
}

// prepareRequest insert all pre-defined stuff
func prepareRequest(what string) (req rest.Request) {
	endPoint := apiEndpoint + fmt.Sprintf("/%s/", what)
	key, ok := HasAPIKey()

	// Add at least one option, the APIkey if present
	hdrs := make(map[string]string)
	opts := make(map[string]string)

	// Insert our sig
	hdrs["User-Agent"] = fmt.Sprintf("ripe-atlas/%s", ourVersion)

	// Insert key
	if ok {
		opts["key"] = key
	}

	req = rest.Request{
		BaseURL: endPoint,
		Headers: hdrs,
		QueryParams: opts,
	}
	return
}

// handleAPIResponse check status code & errors from the API
func handleAPIResponse(r *rest.Response) (err error) {
	if r.StatusCode != 200 {
		var aerr APIError

		err = json.Unmarshal([]byte(r.Body), &aerr)
		err = fmt.Errorf("status: %d code: %d - r:%v\n",
			aerr.Error.Status,
			aerr.Error.Code,
			aerr.Error.Detail)
	}
	return
}
<|endoftext|>


<commit_before>// This file contains data structures that are shared by several sub-programs.

package bagman

import (
	"time"
	"strings"
	"encoding/json"
	"launchpad.net/goamz/s3"
	"github.com/bitly/go-nsq"
)

const (
	ReceiveBucketPrefix = "aptrust.receiving."
	RestoreBucketPrefix = "aptrust.restore."
	S3DateFormat = "2006-01-02T15:04:05.000Z"
)

// S3File contains information about the S3 file we're
// trying to process from an intake bucket. BucketName
// and Key are the S3 bucket name and key. AttemptNumber
// describes whether this is the 1st, 2nd, 3rd,
// etc. attempt to process this file.
type S3File struct {
	BucketName     string
	Key            s3.Key
}

// ProcessStatus contains summary information describing
// the status of a bag in process. This data goes to Fluctus,
// so that APTrust partners can see which of their bags have
// been processed successfully, and why failed bags failed.
// See http://bit.ly/1pf7qxD for details.
//
// Type may have one of the following values: Ingest, Delete,
// Restore
//
// Stage may have one of the following values: Fetch (fetch
// tarred bag file from S3 receiving bucket), Unpack (unpack
// the tarred bag), Validate (make sure all data files are present,
// checksums are correct, required tags are present), Store (copy
// generic files to permanent S3 bucket for archiving), Record
// (save record of intellectual object, generic files and events
// to Fedora).
//
// Status may have one of the following values: Processing,
// Succeeded, Failed.
type ProcessStatus struct {
	Id           int         `json:"id"`
	Name         string      `json:"name"`
	Bucket       string      `json:"bucket"`
	ETag         string      `json:"etag"`
	BagDate      time.Time   `json:"bag_date"`
	Institution  string      `json:"institution"`
	Date         time.Time   `json:"date"`
	Note         string      `json:"note"`
	Action       string      `json:"action"`
	Stage        string      `json:"stage"`
	Status       string      `json:"status"`
	Outcome      string      `json:"outcome"`
}

// Convert ProcessStatus to JSON, omitting id, which Rails won't permit.
// For internal use, json.Marshal() works fine.
func (status *ProcessStatus) SerializeForFluctus() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"name": status.Name,
		"bucket": status.Bucket,
		"etag": status.ETag,
		"bag_date": status.BagDate,
		"institution": status.Institution,
		"date": status.Date,
		"note": status.Note,
		"action": status.Action,
		"stage": status.Stage,
		"status": status.Status,
		"outcome": status.Outcome,
	})
}

// Retry will be set to true if the attempt to process the file
// failed and should be tried again. This would be case, for example,
// if the failure was due to a network error. Retry is
// set to false if processing failed for some reason that
// will not change: for example, if the file cannot be
// untarred, checksums were bad, or data files were missing.
// If processing succeeded, Retry is irrelevant.
type ProcessResult struct {
	NsqMessage       *nsq.Message               `json:"-"`  // Don't serialize
	NsqOutputChannel chan *nsq.FinishedMessage  `json:"-"`  // Don't serialize
	S3File           *S3File
	Error            error
	FetchResult      *FetchResult
	TarResult        *TarResult
	BagReadResult    *BagReadResult
	Stage            string
	Retry            bool
}

// IngestStatus returns a lightweight Status object suitable for reporting
// to the Fluctus results table, so that APTrust partners can view
// the status of their submitted bags.
func (result *ProcessResult) IngestStatus() (status *ProcessStatus) {
	status = &ProcessStatus{}
	status.Date = time.Now().UTC()
	status.Action = "Ingest"
	status.Name = result.S3File.Key.Key
	bagDate, _ := time.Parse(S3DateFormat, result.S3File.Key.LastModified)
	status.BagDate = bagDate
	status.Bucket = result.S3File.BucketName
	status.ETag = strings.Replace(result.S3File.Key.ETag, "\"", "", 2)
	status.Stage = result.Stage
	status.Status = "Processing"
	if result.Error != nil {
		status.Note = result.Error.Error()
		status.Status = "Failed"
	} else {
		status.Note = "No problems"
		if result.Stage == "Record" {
			// We made it through last stage with no erros
			status.Status = "Succeeded"
		}
	}
	status.Institution = OwnerOf(result.S3File.BucketName)
	status.Outcome = status.Status
	return status
}

// BucketSummary contains information about an S3 bucket and its contents.
type BucketSummary struct {
	BucketName     string
	Keys           []s3.Key // TODO: Change to slice of pointers!
}

// GenericFile contains information about a generic
// data file within the data directory of bag or tar archive.
type GenericFile struct {
	Path             string
	Size             int64
	Created          time.Time  // we currently have no way of getting this
	Modified         time.Time
	Md5              string
	Sha256           string
	Sha256Generated  time.Time
	Uuid             string
	UuidGenerated    time.Time
	MimeType         string
	Error            error
}

// TarResult contains information about the attempt to untar
// a bag.
type TarResult struct {
	InputFile       string
	OutputDir       string
	Error           error
	Warnings        []string
	FilesUnpacked   []string
	GenericFiles    []*GenericFile
}

// This Tag struct is essentially the same as the bagins
// TagField struct, but its properties are public and can
// be easily serialized to / deserialized from JSON.
type Tag struct {
	Label string
	Value string
}

// BagReadResult contains data describing the result of
// processing a single bag. If there were any processing
// errors, this structure should tell us exactly what
// happened and where.
type BagReadResult struct {
	Path             string
	Files            []string
	Error            error
	Tags             []Tag
	ChecksumErrors   []error
}

// FetchResult descibes the results of fetching a bag from S3
// and verification of that bag.
type FetchResult struct {
	BucketName       string
	Key              string
	LocalTarFile     string
	RemoteMd5        string
	LocalMd5         string
	Md5Verified      bool
	Md5Verifiable    bool
	Error            error
	Warning          string
	Retry            bool
}

// Returns the domain name of the institution that owns the specified bucket.
// For example, if bucketName is 'aptrust.receiving.unc.edu' the return value
// will be 'unc.edu'.
func OwnerOf (bucketName string) (institution string) {
	if strings.HasPrefix(bucketName, ReceiveBucketPrefix) {
		institution = strings.Replace(bucketName, ReceiveBucketPrefix, "", 1)
	} else if strings.HasPrefix(bucketName, RestoreBucketPrefix) {
		institution = strings.Replace(bucketName, RestoreBucketPrefix, "", 1)
	}
	return institution
}

// Returns the name of the specified institution's receiving bucket.
// E.g. institution 'unc.edu' returns bucketName 'aptrust.receiving.unc.edu'
func ReceivingBucketFor (institution string) (bucketName string) {
	return ReceiveBucketPrefix + institution
}

// Returns the name of the specified institution's restoration bucket.
// E.g. institution 'unc.edu' returns bucketName 'aptrust.restore.unc.edu'
func RestorationBucketFor (institution string) (bucketName string) {
	return RestoreBucketPrefix + institution
}
<commit_msg>For demo puposes, consider successful validation to be successful processing<commit_after>// This file contains data structures that are shared by several sub-programs.

package bagman

import (
	"time"
	"strings"
	"encoding/json"
	"launchpad.net/goamz/s3"
	"github.com/bitly/go-nsq"
)

const (
	ReceiveBucketPrefix = "aptrust.receiving."
	RestoreBucketPrefix = "aptrust.restore."
	S3DateFormat = "2006-01-02T15:04:05.000Z"
)

// S3File contains information about the S3 file we're
// trying to process from an intake bucket. BucketName
// and Key are the S3 bucket name and key. AttemptNumber
// describes whether this is the 1st, 2nd, 3rd,
// etc. attempt to process this file.
type S3File struct {
	BucketName     string
	Key            s3.Key
}

// ProcessStatus contains summary information describing
// the status of a bag in process. This data goes to Fluctus,
// so that APTrust partners can see which of their bags have
// been processed successfully, and why failed bags failed.
// See http://bit.ly/1pf7qxD for details.
//
// Type may have one of the following values: Ingest, Delete,
// Restore
//
// Stage may have one of the following values: Fetch (fetch
// tarred bag file from S3 receiving bucket), Unpack (unpack
// the tarred bag), Validate (make sure all data files are present,
// checksums are correct, required tags are present), Store (copy
// generic files to permanent S3 bucket for archiving), Record
// (save record of intellectual object, generic files and events
// to Fedora).
//
// Status may have one of the following values: Processing,
// Succeeded, Failed.
type ProcessStatus struct {
	Id           int         `json:"id"`
	Name         string      `json:"name"`
	Bucket       string      `json:"bucket"`
	ETag         string      `json:"etag"`
	BagDate      time.Time   `json:"bag_date"`
	Institution  string      `json:"institution"`
	Date         time.Time   `json:"date"`
	Note         string      `json:"note"`
	Action       string      `json:"action"`
	Stage        string      `json:"stage"`
	Status       string      `json:"status"`
	Outcome      string      `json:"outcome"`
}

// Convert ProcessStatus to JSON, omitting id, which Rails won't permit.
// For internal use, json.Marshal() works fine.
func (status *ProcessStatus) SerializeForFluctus() ([]byte, error) {
	return json.Marshal(map[string]interface{}{
		"name": status.Name,
		"bucket": status.Bucket,
		"etag": status.ETag,
		"bag_date": status.BagDate,
		"institution": status.Institution,
		"date": status.Date,
		"note": status.Note,
		"action": status.Action,
		"stage": status.Stage,
		"status": status.Status,
		"outcome": status.Outcome,
	})
}

// Retry will be set to true if the attempt to process the file
// failed and should be tried again. This would be case, for example,
// if the failure was due to a network error. Retry is
// set to false if processing failed for some reason that
// will not change: for example, if the file cannot be
// untarred, checksums were bad, or data files were missing.
// If processing succeeded, Retry is irrelevant.
type ProcessResult struct {
	NsqMessage       *nsq.Message               `json:"-"`  // Don't serialize
	NsqOutputChannel chan *nsq.FinishedMessage  `json:"-"`  // Don't serialize
	S3File           *S3File
	Error            error
	FetchResult      *FetchResult
	TarResult        *TarResult
	BagReadResult    *BagReadResult
	Stage            string
	Retry            bool
}

// IngestStatus returns a lightweight Status object suitable for reporting
// to the Fluctus results table, so that APTrust partners can view
// the status of their submitted bags.
func (result *ProcessResult) IngestStatus() (status *ProcessStatus) {
	status = &ProcessStatus{}
	status.Date = time.Now().UTC()
	status.Action = "Ingest"
	status.Name = result.S3File.Key.Key
	bagDate, _ := time.Parse(S3DateFormat, result.S3File.Key.LastModified)
	status.BagDate = bagDate
	status.Bucket = result.S3File.BucketName
	// Strip the quotes off the ETag
	status.ETag = strings.Replace(result.S3File.Key.ETag, "\"", "", 2)
	status.Stage = result.Stage
	status.Status = "Processing"
	if result.Error != nil {
		status.Note = result.Error.Error()
		status.Status = "Failed"
	} else {
		status.Note = "No problems"
		if result.Stage == "Validate" {
			// We made it through last stage with no errors
			// TODO: Change back to "Record" after demo.
			// *** NOTE: THE LAST STAGE SHOULD BE "Record", BUT FOR DEMO
			// WE'LL CONSIDER "Validate" TO BE SUCCESS ***
			status.Status = "Succeeded"
		}
	}
	status.Institution = OwnerOf(result.S3File.BucketName)
	status.Outcome = status.Status
	return status
}

// BucketSummary contains information about an S3 bucket and its contents.
type BucketSummary struct {
	BucketName     string
	Keys           []s3.Key // TODO: Change to slice of pointers!
}

// GenericFile contains information about a generic
// data file within the data directory of bag or tar archive.
type GenericFile struct {
	Path             string
	Size             int64
	Created          time.Time  // we currently have no way of getting this
	Modified         time.Time
	Md5              string
	Sha256           string
	Sha256Generated  time.Time
	Uuid             string
	UuidGenerated    time.Time
	MimeType         string
	Error            error
}

// TarResult contains information about the attempt to untar
// a bag.
type TarResult struct {
	InputFile       string
	OutputDir       string
	Error           error
	Warnings        []string
	FilesUnpacked   []string
	GenericFiles    []*GenericFile
}

// This Tag struct is essentially the same as the bagins
// TagField struct, but its properties are public and can
// be easily serialized to / deserialized from JSON.
type Tag struct {
	Label string
	Value string
}

// BagReadResult contains data describing the result of
// processing a single bag. If there were any processing
// errors, this structure should tell us exactly what
// happened and where.
type BagReadResult struct {
	Path             string
	Files            []string
	Error            error
	Tags             []Tag
	ChecksumErrors   []error
}

// FetchResult descibes the results of fetching a bag from S3
// and verification of that bag.
type FetchResult struct {
	BucketName       string
	Key              string
	LocalTarFile     string
	RemoteMd5        string
	LocalMd5         string
	Md5Verified      bool
	Md5Verifiable    bool
	Error            error
	Warning          string
	Retry            bool
}

// Returns the domain name of the institution that owns the specified bucket.
// For example, if bucketName is 'aptrust.receiving.unc.edu' the return value
// will be 'unc.edu'.
func OwnerOf (bucketName string) (institution string) {
	if strings.HasPrefix(bucketName, ReceiveBucketPrefix) {
		institution = strings.Replace(bucketName, ReceiveBucketPrefix, "", 1)
	} else if strings.HasPrefix(bucketName, RestoreBucketPrefix) {
		institution = strings.Replace(bucketName, RestoreBucketPrefix, "", 1)
	}
	return institution
}

// Returns the name of the specified institution's receiving bucket.
// E.g. institution 'unc.edu' returns bucketName 'aptrust.receiving.unc.edu'
func ReceivingBucketFor (institution string) (bucketName string) {
	return ReceiveBucketPrefix + institution
}

// Returns the name of the specified institution's restoration bucket.
// E.g. institution 'unc.edu' returns bucketName 'aptrust.restore.unc.edu'
func RestorationBucketFor (institution string) (bucketName string) {
	return RestoreBucketPrefix + institution
}
<|endoftext|>


<commit_before>package libkb

import (
	"path/filepath"
	"testing"
)

func TestEnvDarwin(t *testing.T) {
	env := newEnv(nil, nil, "darwin")

	runtimeDir := env.GetRuntimeDir()
	sockFile, err := env.GetSocketFile()
	if err != nil {
		t.Fatal(err)
	}
	t.Logf("%s\n", sockFile)

	expectedSockFile := filepath.Join(runtimeDir, "keybased.sock")
	if sockFile != expectedSockFile {
		t.Fatal("Clients expect sock file to be %s", expectedSockFile)
	}
}
<commit_msg>Fix vet for env test<commit_after>package libkb

import (
	"path/filepath"
	"testing"
)

func TestEnvDarwin(t *testing.T) {
	env := newEnv(nil, nil, "darwin")

	runtimeDir := env.GetRuntimeDir()
	sockFile, err := env.GetSocketFile()
	if err != nil {
		t.Fatal(err)
	}

	expectedSockFile := filepath.Join(runtimeDir, "keybased.sock")
	if sockFile != expectedSockFile {
		t.Fatalf("Clients expect sock file to be %s", expectedSockFile)
	}
}
<|endoftext|>