<commit_before>package libnetwork

import (
	"fmt"
	"strings"

	"github.com/docker/libnetwork/netlabel"
	"github.com/docker/libnetwork/types"
	"github.com/sirupsen/logrus"
)

const (
	gwEPlen = 12
)

var procGwNetwork = make(chan (bool), 1)

/*
   libnetwork creates a bridge network "docker_gw_bridge" for providing
   default gateway for the containers if none of the container's endpoints
   have GW set by the driver. ICC is set to false for the GW_bridge network.

   If a driver can't provide external connectivity it can choose to not set
   the GW IP for the endpoint.

   endpoint on the GW_bridge network is managed dynamically by libnetwork.
   ie:
   - its created when an endpoint without GW joins the container
   - its deleted when an endpoint with GW joins the container
*/

func (sb *sandbox) setupDefaultGW() error {

	// check if the container already has a GW endpoint
	if ep := sb.getEndpointInGWNetwork(); ep != nil {
		return nil
	}

	c := sb.controller

	// Look for default gw network. In case of error (includes not found),
	// retry and create it if needed in a serialized execution.
	n, err := c.NetworkByName(libnGWNetwork)
	if err != nil {
		if n, err = c.defaultGwNetwork(); err != nil {
			return err
		}
	}

	createOptions := []EndpointOption{CreateOptionAnonymous()}

	var gwName string
	if len(sb.containerID) <= gwEPlen {
		gwName = "gateway_" + sb.containerID
	} else {
		gwName = "gateway_" + sb.id[:gwEPlen]
	}

	sbLabels := sb.Labels()

	if sbLabels[netlabel.PortMap] != nil {
		createOptions = append(createOptions, CreateOptionPortMapping(sbLabels[netlabel.PortMap].([]types.PortBinding)))
	}

	if sbLabels[netlabel.ExposedPorts] != nil {
		createOptions = append(createOptions, CreateOptionExposedPorts(sbLabels[netlabel.ExposedPorts].([]types.TransportPort)))
	}

	epOption := getPlatformOption()
	if epOption != nil {
		createOptions = append(createOptions, epOption)
	}

	newEp, err := n.CreateEndpoint(gwName, createOptions...)
	if err != nil {
		return fmt.Errorf("container %s: endpoint create on GW Network failed: %v", sb.containerID, err)
	}

	defer func() {
		if err != nil {
			if err2 := newEp.Delete(true); err2 != nil {
				logrus.Warnf("Failed to remove gw endpoint for container %s after failing to join the gateway network: %v",
					sb.containerID, err2)
			}
		}
	}()

	epLocal := newEp.(*endpoint)

	if err = epLocal.sbJoin(sb); err != nil {
		return fmt.Errorf("container %s: endpoint join on GW Network failed: %v", sb.containerID, err)
	}

	return nil
}

// If present, detach and remove the endpoint connecting the sandbox to the default gw network.
func (sb *sandbox) clearDefaultGW() error {
	var ep *endpoint

	if ep = sb.getEndpointInGWNetwork(); ep == nil {
		return nil
	}
	if err := ep.sbLeave(sb, false); err != nil {
		return fmt.Errorf("container %s: endpoint leaving GW Network failed: %v", sb.containerID, err)
	}
	if err := ep.Delete(false); err != nil {
		return fmt.Errorf("container %s: deleting endpoint on GW Network failed: %v", sb.containerID, err)
	}
	return nil
}

// Evaluate whether the sandbox requires a default gateway based
// on the endpoints to which it is connected. It does not account
// for the default gateway network endpoint.

func (sb *sandbox) needDefaultGW() bool {
	var needGW bool

	for _, ep := range sb.getConnectedEndpoints() {
		if ep.endpointInGWNetwork() {
			continue
		}
		if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
			continue
		}
		if ep.getNetwork().Internal() {
			continue
		}
		// During stale sandbox cleanup, joinInfo may be nil
		if ep.joinInfo != nil && ep.joinInfo.disableGatewayService {
			continue
		}
		// TODO v6 needs to be handled.
		if len(ep.Gateway()) > 0 {
			return false
		}
		for _, r := range ep.StaticRoutes() {
			if r.Destination != nil && r.Destination.String() == "0.0.0.0/0" {
				return false
			}
		}
		needGW = true
	}

	return needGW
}

func (sb *sandbox) getEndpointInGWNetwork() *endpoint {
	for _, ep := range sb.getConnectedEndpoints() {
		if ep.getNetwork().name == libnGWNetwork && strings.HasPrefix(ep.Name(), "gateway_") {
			return ep
		}
	}
	return nil
}

func (ep *endpoint) endpointInGWNetwork() bool {
	if ep.getNetwork().name == libnGWNetwork && strings.HasPrefix(ep.Name(), "gateway_") {
		return true
	}
	return false
}

func (sb *sandbox) getEPwithoutGateway() *endpoint {
	for _, ep := range sb.getConnectedEndpoints() {
		if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
			continue
		}
		if len(ep.Gateway()) == 0 {
			return ep
		}
	}
	return nil
}

// Looks for the default gw network and creates it if not there.
// Parallel executions are serialized.
func (c *controller) defaultGwNetwork() (Network, error) {
	procGwNetwork <- true
	defer func() { <-procGwNetwork }()

	n, err := c.NetworkByName(libnGWNetwork)
	if err != nil {
		if _, ok := err.(types.NotFoundError); ok {
			n, err = c.createGWNetwork()
		}
	}
	return n, err
}

// Returns the endpoint which is providing external connectivity to the sandbox
func (sb *sandbox) getGatewayEndpoint() *endpoint {
	for _, ep := range sb.getConnectedEndpoints() {
		if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
			continue
		}
		if len(ep.Gateway()) != 0 {
			return ep
		}
	}
	return nil
}
<commit_msg>Fix gosimple<commit_after>package libnetwork

import (
	"fmt"
	"strings"

	"github.com/docker/libnetwork/netlabel"
	"github.com/docker/libnetwork/types"
	"github.com/sirupsen/logrus"
)

const (
	gwEPlen = 12
)

var procGwNetwork = make(chan (bool), 1)

/*
   libnetwork creates a bridge network "docker_gw_bridge" for providing
   default gateway for the containers if none of the container's endpoints
   have GW set by the driver. ICC is set to false for the GW_bridge network.

   If a driver can't provide external connectivity it can choose to not set
   the GW IP for the endpoint.

   endpoint on the GW_bridge network is managed dynamically by libnetwork.
   ie:
   - its created when an endpoint without GW joins the container
   - its deleted when an endpoint with GW joins the container
*/

func (sb *sandbox) setupDefaultGW() error {

	// check if the container already has a GW endpoint
	if ep := sb.getEndpointInGWNetwork(); ep != nil {
		return nil
	}

	c := sb.controller

	// Look for default gw network. In case of error (includes not found),
	// retry and create it if needed in a serialized execution.
	n, err := c.NetworkByName(libnGWNetwork)
	if err != nil {
		if n, err = c.defaultGwNetwork(); err != nil {
			return err
		}
	}

	createOptions := []EndpointOption{CreateOptionAnonymous()}

	var gwName string
	if len(sb.containerID) <= gwEPlen {
		gwName = "gateway_" + sb.containerID
	} else {
		gwName = "gateway_" + sb.id[:gwEPlen]
	}

	sbLabels := sb.Labels()

	if sbLabels[netlabel.PortMap] != nil {
		createOptions = append(createOptions, CreateOptionPortMapping(sbLabels[netlabel.PortMap].([]types.PortBinding)))
	}

	if sbLabels[netlabel.ExposedPorts] != nil {
		createOptions = append(createOptions, CreateOptionExposedPorts(sbLabels[netlabel.ExposedPorts].([]types.TransportPort)))
	}

	epOption := getPlatformOption()
	if epOption != nil {
		createOptions = append(createOptions, epOption)
	}

	newEp, err := n.CreateEndpoint(gwName, createOptions...)
	if err != nil {
		return fmt.Errorf("container %s: endpoint create on GW Network failed: %v", sb.containerID, err)
	}

	defer func() {
		if err != nil {
			if err2 := newEp.Delete(true); err2 != nil {
				logrus.Warnf("Failed to remove gw endpoint for container %s after failing to join the gateway network: %v",
					sb.containerID, err2)
			}
		}
	}()

	epLocal := newEp.(*endpoint)

	if err = epLocal.sbJoin(sb); err != nil {
		return fmt.Errorf("container %s: endpoint join on GW Network failed: %v", sb.containerID, err)
	}

	return nil
}

// If present, detach and remove the endpoint connecting the sandbox to the default gw network.
func (sb *sandbox) clearDefaultGW() error {
	var ep *endpoint

	if ep = sb.getEndpointInGWNetwork(); ep == nil {
		return nil
	}
	if err := ep.sbLeave(sb, false); err != nil {
		return fmt.Errorf("container %s: endpoint leaving GW Network failed: %v", sb.containerID, err)
	}
	if err := ep.Delete(false); err != nil {
		return fmt.Errorf("container %s: deleting endpoint on GW Network failed: %v", sb.containerID, err)
	}
	return nil
}

// Evaluate whether the sandbox requires a default gateway based
// on the endpoints to which it is connected. It does not account
// for the default gateway network endpoint.

func (sb *sandbox) needDefaultGW() bool {
	var needGW bool

	for _, ep := range sb.getConnectedEndpoints() {
		if ep.endpointInGWNetwork() {
			continue
		}
		if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
			continue
		}
		if ep.getNetwork().Internal() {
			continue
		}
		// During stale sandbox cleanup, joinInfo may be nil
		if ep.joinInfo != nil && ep.joinInfo.disableGatewayService {
			continue
		}
		// TODO v6 needs to be handled.
		if len(ep.Gateway()) > 0 {
			return false
		}
		for _, r := range ep.StaticRoutes() {
			if r.Destination != nil && r.Destination.String() == "0.0.0.0/0" {
				return false
			}
		}
		needGW = true
	}

	return needGW
}

func (sb *sandbox) getEndpointInGWNetwork() *endpoint {
	for _, ep := range sb.getConnectedEndpoints() {
		if ep.getNetwork().name == libnGWNetwork && strings.HasPrefix(ep.Name(), "gateway_") {
			return ep
		}
	}
	return nil
}

func (ep *endpoint) endpointInGWNetwork() bool {
	if ep.getNetwork().name == libnGWNetwork && strings.HasPrefix(ep.Name(), "gateway_") {
		return true
	}
	return false
}

func (sb *sandbox) getEPwithoutGateway() *endpoint {
	for _, ep := range sb.getConnectedEndpoints() {
		if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
			continue
		}
		if len(ep.Gateway()) == 0 {
			return ep
		}
	}
	return nil
}

// Looks for the default gw network and creates it if not there.
// Parallel executions are serialized.
func (c *controller) defaultGwNetwork() (Network, error) {
	procGwNetwork <- true
	defer func() { <-procGwNetwork }()

	n, err := c.NetworkByName(libnGWNetwork)
	if _, ok := err.(types.NotFoundError); ok {
		n, err = c.createGWNetwork()
	}
	return n, err
}

// Returns the endpoint which is providing external connectivity to the sandbox
func (sb *sandbox) getGatewayEndpoint() *endpoint {
	for _, ep := range sb.getConnectedEndpoints() {
		if ep.getNetwork().Type() == "null" || ep.getNetwork().Type() == "host" {
			continue
		}
		if len(ep.Gateway()) != 0 {
			return ep
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package osl

import (
	"fmt"
	"net"

	"github.com/docker/libnetwork/types"
	"github.com/vishvananda/netlink"
)

func (n *networkNamespace) Gateway() net.IP {
	n.Lock()
	defer n.Unlock()

	return n.gw
}

func (n *networkNamespace) GatewayIPv6() net.IP {
	n.Lock()
	defer n.Unlock()

	return n.gwv6
}

func (n *networkNamespace) StaticRoutes() []*types.StaticRoute {
	n.Lock()
	defer n.Unlock()

	routes := make([]*types.StaticRoute, len(n.staticRoutes))
	for i, route := range n.staticRoutes {
		r := route.GetCopy()
		routes[i] = r
	}

	return routes
}

func (n *networkNamespace) setGateway(gw net.IP) {
	n.Lock()
	n.gw = gw
	n.Unlock()
}

func (n *networkNamespace) setGatewayIPv6(gwv6 net.IP) {
	n.Lock()
	n.gwv6 = gwv6
	n.Unlock()
}

func (n *networkNamespace) SetGateway(gw net.IP) error {
	// Silently return if the gateway is empty
	if len(gw) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gw, true)
	if err == nil {
		n.setGateway(gw)
	}

	return err
}

func (n *networkNamespace) UnsetGateway() error {
	gw := n.Gateway()

	// Silently return if the gateway is empty
	if len(gw) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gw, false)
	if err == nil {
		n.setGateway(net.IP{})
	}

	return err
}

func programGateway(path string, gw net.IP, isAdd bool) error {
	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
		gwRoutes, err := netlink.RouteGet(gw)
		if err != nil {
			return fmt.Errorf("route for the gateway %s could not be found: %v", gw, err)
		}

		if isAdd {
			return netlink.RouteAdd(&netlink.Route{
				Scope:     netlink.SCOPE_UNIVERSE,
				LinkIndex: gwRoutes[0].LinkIndex,
				Gw:        gw,
			})
		}

		return netlink.RouteDel(&netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			LinkIndex: gwRoutes[0].LinkIndex,
			Gw:        gw,
		})
	})
}

// Program a route in to the namespace routing table.
func programRoute(path string, dest *net.IPNet, nh net.IP) error {
	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
		gwRoutes, err := netlink.RouteGet(nh)
		if err != nil {
			return fmt.Errorf("route for the next hop %s could not be found: %v", nh, err)
		}

		return netlink.RouteAdd(&netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			LinkIndex: gwRoutes[0].LinkIndex,
			Gw:        nh,
			Dst:       dest,
		})
	})
}

// Delete a route from the namespace routing table.
func removeRoute(path string, dest *net.IPNet, nh net.IP) error {
	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
		gwRoutes, err := netlink.RouteGet(nh)
		if err != nil {
			return fmt.Errorf("route for the next hop could not be found: %v", err)
		}

		return netlink.RouteDel(&netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			LinkIndex: gwRoutes[0].LinkIndex,
			Gw:        nh,
			Dst:       dest,
		})
	})
}

func (n *networkNamespace) SetGatewayIPv6(gwv6 net.IP) error {
	// Silently return if the gateway is empty
	if len(gwv6) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gwv6, true)
	if err == nil {
		n.SetGatewayIPv6(gwv6)
	}

	return err
}

func (n *networkNamespace) UnsetGatewayIPv6() error {
	gwv6 := n.GatewayIPv6()

	// Silently return if the gateway is empty
	if len(gwv6) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gwv6, false)
	if err == nil {
		n.Lock()
		n.gwv6 = net.IP{}
		n.Unlock()
	}

	return err
}

func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
	err := programRoute(n.nsPath(), r.Destination, r.NextHop)
	if err == nil {
		n.Lock()
		n.staticRoutes = append(n.staticRoutes, r)
		n.Unlock()
	}
	return err
}

func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {

	err := removeRoute(n.nsPath(), r.Destination, r.NextHop)
	if err == nil {
		n.Lock()
		lastIndex := len(n.staticRoutes) - 1
		for i, v := range n.staticRoutes {
			if v == r {
				// Overwrite the route we're removing with the last element
				n.staticRoutes[i] = n.staticRoutes[lastIndex]
				// Shorten the slice to trim the extra element
				n.staticRoutes = n.staticRoutes[:lastIndex]
				break
			}
		}
		n.Unlock()
	}
	return err
}
<commit_msg>Fix bug in osl.SetGatewayIPv6() function<commit_after>package osl

import (
	"fmt"
	"net"

	"github.com/docker/libnetwork/types"
	"github.com/vishvananda/netlink"
)

func (n *networkNamespace) Gateway() net.IP {
	n.Lock()
	defer n.Unlock()

	return n.gw
}

func (n *networkNamespace) GatewayIPv6() net.IP {
	n.Lock()
	defer n.Unlock()

	return n.gwv6
}

func (n *networkNamespace) StaticRoutes() []*types.StaticRoute {
	n.Lock()
	defer n.Unlock()

	routes := make([]*types.StaticRoute, len(n.staticRoutes))
	for i, route := range n.staticRoutes {
		r := route.GetCopy()
		routes[i] = r
	}

	return routes
}

func (n *networkNamespace) setGateway(gw net.IP) {
	n.Lock()
	n.gw = gw
	n.Unlock()
}

func (n *networkNamespace) setGatewayIPv6(gwv6 net.IP) {
	n.Lock()
	n.gwv6 = gwv6
	n.Unlock()
}

func (n *networkNamespace) SetGateway(gw net.IP) error {
	// Silently return if the gateway is empty
	if len(gw) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gw, true)
	if err == nil {
		n.setGateway(gw)
	}

	return err
}

func (n *networkNamespace) UnsetGateway() error {
	gw := n.Gateway()

	// Silently return if the gateway is empty
	if len(gw) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gw, false)
	if err == nil {
		n.setGateway(net.IP{})
	}

	return err
}

func programGateway(path string, gw net.IP, isAdd bool) error {
	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
		gwRoutes, err := netlink.RouteGet(gw)
		if err != nil {
			return fmt.Errorf("route for the gateway %s could not be found: %v", gw, err)
		}

		if isAdd {
			return netlink.RouteAdd(&netlink.Route{
				Scope:     netlink.SCOPE_UNIVERSE,
				LinkIndex: gwRoutes[0].LinkIndex,
				Gw:        gw,
			})
		}

		return netlink.RouteDel(&netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			LinkIndex: gwRoutes[0].LinkIndex,
			Gw:        gw,
		})
	})
}

// Program a route in to the namespace routing table.
func programRoute(path string, dest *net.IPNet, nh net.IP) error {
	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
		gwRoutes, err := netlink.RouteGet(nh)
		if err != nil {
			return fmt.Errorf("route for the next hop %s could not be found: %v", nh, err)
		}

		return netlink.RouteAdd(&netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			LinkIndex: gwRoutes[0].LinkIndex,
			Gw:        nh,
			Dst:       dest,
		})
	})
}

// Delete a route from the namespace routing table.
func removeRoute(path string, dest *net.IPNet, nh net.IP) error {
	return nsInvoke(path, func(nsFD int) error { return nil }, func(callerFD int) error {
		gwRoutes, err := netlink.RouteGet(nh)
		if err != nil {
			return fmt.Errorf("route for the next hop could not be found: %v", err)
		}

		return netlink.RouteDel(&netlink.Route{
			Scope:     netlink.SCOPE_UNIVERSE,
			LinkIndex: gwRoutes[0].LinkIndex,
			Gw:        nh,
			Dst:       dest,
		})
	})
}

func (n *networkNamespace) SetGatewayIPv6(gwv6 net.IP) error {
	// Silently return if the gateway is empty
	if len(gwv6) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gwv6, true)
	if err == nil {
		n.setGatewayIPv6(gwv6)
	}

	return err
}

func (n *networkNamespace) UnsetGatewayIPv6() error {
	gwv6 := n.GatewayIPv6()

	// Silently return if the gateway is empty
	if len(gwv6) == 0 {
		return nil
	}

	err := programGateway(n.nsPath(), gwv6, false)
	if err == nil {
		n.Lock()
		n.gwv6 = net.IP{}
		n.Unlock()
	}

	return err
}

func (n *networkNamespace) AddStaticRoute(r *types.StaticRoute) error {
	err := programRoute(n.nsPath(), r.Destination, r.NextHop)
	if err == nil {
		n.Lock()
		n.staticRoutes = append(n.staticRoutes, r)
		n.Unlock()
	}
	return err
}

func (n *networkNamespace) RemoveStaticRoute(r *types.StaticRoute) error {

	err := removeRoute(n.nsPath(), r.Destination, r.NextHop)
	if err == nil {
		n.Lock()
		lastIndex := len(n.staticRoutes) - 1
		for i, v := range n.staticRoutes {
			if v == r {
				// Overwrite the route we're removing with the last element
				n.staticRoutes[i] = n.staticRoutes[lastIndex]
				// Shorten the slice to trim the extra element
				n.staticRoutes = n.staticRoutes[:lastIndex]
				break
			}
		}
		n.Unlock()
	}
	return err
}
<|endoftext|>


<commit_before>package regression

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/vcsinfo"
	"go.skia.org/infra/go/vec32"
	"go.skia.org/infra/perf/go/cid"
	"go.skia.org/infra/perf/go/dataframe"
	"go.skia.org/infra/perf/go/types"
)

const (
	e = vec32.MISSING_DATA_SENTINEL
)

func TestTooMuchMissingData(t *testing.T) {
	testutils.SmallTest(t)
	testCases := []struct {
		value    types.Trace
		expected bool
		message  string
	}{
		{
			value:    types.Trace{e, e, 1, 1, 1},
			expected: true,
			message:  "missing one side",
		},
		{
			value:    types.Trace{1, e, 1, 1, 1},
			expected: false,
			message:  "exactly 50%",
		},
		{
			value:    types.Trace{1, 1, e, 1, 1},
			expected: true,
			message:  "missing midpoint",
		},
		{
			value:    types.Trace{e, e, 1, 1},
			expected: true,
			message:  "missing one side - even",
		},
		{
			value:    types.Trace{e, 1, 1, 1},
			expected: false,
			message:  "exactly 50% - even",
		},
		{
			value:    types.Trace{e, 1, 1},
			expected: true,
			message:  "Radius = 1",
		},
		{
			value:    types.Trace{1},
			expected: false,
			message:  "len(tr) < 3",
		},
	}

	for _, tc := range testCases {
		if got, want := tooMuchMissingData(tc.value), tc.expected; got != want {
			t.Errorf("Failed case Got %v Want %v: %s", got, want, tc.message)
		}
	}
}

func TestCalcCidsNotSparse(t *testing.T) {
	testutils.SmallTest(t)

	r := &ClusterRequest{
		Source: "master",
		Offset: 2000,
		Radius: 3,
		Query:  "config=8888",
		Sparse: false,
	}

	cids, err := calcCids(r, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, "master-001997", cids[0].ID())
	assert.Equal(t, "master-002003", cids[6].ID())
}

type mockVcs struct{}

func (m *mockVcs) LastNIndex(N int) []*vcsinfo.IndexCommit {
	return []*vcsinfo.IndexCommit{&vcsinfo.IndexCommit{Index: 2005}}
}
func (m *mockVcs) Update(ctx context.Context, pull, allBranches bool) error        { return nil }
func (m *mockVcs) From(start time.Time) []string                                   { return nil }
func (m *mockVcs) Range(begin, end time.Time) []*vcsinfo.IndexCommit               { return nil }
func (m *mockVcs) IndexOf(ctx context.Context, hash string) (int, error)           { return 0, nil }
func (m *mockVcs) ByIndex(ctx context.Context, N int) (*vcsinfo.LongCommit, error) { return nil, nil }
func (m *mockVcs) Details(ctx context.Context, hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	return nil, nil
}
func (m *mockVcs) GetFile(ctx context.Context, fileName, commitHash string) (string, error) {
	return "", nil
}
func (m *mockVcs) ResolveCommit(ctx context.Context, commitHash string) (string, error) {
	return "", nil
}

func TestCalcCidsSparse(t *testing.T) {
	testutils.SmallTest(t)

	r := &ClusterRequest{
		Source: "master",
		Offset: 2000,
		Radius: 3,
		Query:  "config=8888",
		Sparse: true,
	}

	i := 0
	ends := []int{}
	begins := []int{}
	type cidSlice []*cid.CommitID
	rets := []cidSlice{
		cidSlice{&cid.CommitID{Source: "master", Offset: 2000}},
		cidSlice{
			&cid.CommitID{Source: "master", Offset: 2001},
			&cid.CommitID{Source: "master", Offset: 2002},
			&cid.CommitID{Source: "master", Offset: 2004},
		},
		cidSlice{
			&cid.CommitID{Source: "master", Offset: 1997},
			&cid.CommitID{Source: "master", Offset: 1998},
			&cid.CommitID{Source: "master", Offset: 1999},
		},
	}
	cidsWithDataInRange := func(begin, end int) ([]*cid.CommitID, error) {
		defer func() { i += 1 }()
		ends = append(ends, end)
		begins = append(begins, begin)
		return rets[i], nil
	}

	cids, err := calcCids(r, &mockVcs{}, cidsWithDataInRange)
	assert.NoError(t, err)
	assert.Equal(t, "master-001997", cids[0].ID())
	assert.Equal(t, "master-002004", cids[6].ID())
	assert.Equal(t, []int{2000, 2001, 1700}, begins)
	assert.Equal(t, []int{2001, 2005, 2000}, ends)
}

func TestCalcCidsSparseFails(t *testing.T) {
	testutils.SmallTest(t)

	r := &ClusterRequest{
		Source: "master",
		Offset: 2000,
		Radius: 3,
		Query:  "config=8888",
		Sparse: true,
	}

	cidsWithDataInRange := func(begin, end int) ([]*cid.CommitID, error) {
		return []*cid.CommitID{}, nil
	}

	_, err := calcCids(r, &mockVcs{}, cidsWithDataInRange)
	assert.Error(t, err)
}

func TestCidsWithData(t *testing.T) {
	testutils.SmallTest(t)

	e := vec32.MISSING_DATA_SENTINEL
	headers := []*dataframe.ColumnHeader{
		{Source: "master", Offset: 2000},
		{Source: "master", Offset: 2001},
		{Source: "master", Offset: 2002},
	}
	traceSet := types.TraceSet{
		",arch=x86,config=565,":  types.Trace([]float32{e, 2.1, e}),
		",arch=x86,config=8888,": types.Trace([]float32{e, 3.1, e}),
		",arch=x86,config=gpu,":  types.Trace([]float32{1.4, 4.1, e}),
	}
	d := &dataframe.DataFrame{
		TraceSet: traceSet,
		Header:   headers,
	}

	cids := cidsWithData(d)
	assert.Len(t, cids, 2)
	assert.Equal(t, "master-002000", cids[0].ID())
	assert.Equal(t, "master-002001", cids[1].ID())
}

func TestCidsWithDataEmpty(t *testing.T) {
	testutils.SmallTest(t)

	d := &dataframe.DataFrame{
		TraceSet: types.TraceSet{},
		Header:   []*dataframe.ColumnHeader{},
	}

	cids := cidsWithData(d)
	assert.Len(t, cids, 0)
}
<commit_msg>Perf - Fix unit test.<commit_after>package regression

import (
	"context"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
	"go.skia.org/infra/go/testutils"
	"go.skia.org/infra/go/vcsinfo"
	"go.skia.org/infra/go/vec32"
	"go.skia.org/infra/perf/go/cid"
	"go.skia.org/infra/perf/go/dataframe"
	"go.skia.org/infra/perf/go/types"
)

const (
	e = vec32.MISSING_DATA_SENTINEL
)

func TestTooMuchMissingData(t *testing.T) {
	testutils.SmallTest(t)
	testCases := []struct {
		value    types.Trace
		expected bool
		message  string
	}{
		{
			value:    types.Trace{e, e, 1, 1, 1},
			expected: true,
			message:  "missing one side",
		},
		{
			value:    types.Trace{1, e, 1, 1, 1},
			expected: false,
			message:  "exactly 50%",
		},
		{
			value:    types.Trace{1, 1, e, 1, 1},
			expected: true,
			message:  "missing midpoint",
		},
		{
			value:    types.Trace{e, e, 1, 1},
			expected: true,
			message:  "missing one side - even",
		},
		{
			value:    types.Trace{e, 1, 1, 1},
			expected: false,
			message:  "exactly 50% - even",
		},
		{
			value:    types.Trace{e, 1, 1},
			expected: true,
			message:  "Radius = 1",
		},
		{
			value:    types.Trace{1},
			expected: false,
			message:  "len(tr) < 3",
		},
	}

	for _, tc := range testCases {
		if got, want := tooMuchMissingData(tc.value), tc.expected; got != want {
			t.Errorf("Failed case Got %v Want %v: %s", got, want, tc.message)
		}
	}
}

func TestCalcCidsNotSparse(t *testing.T) {
	testutils.SmallTest(t)

	r := &ClusterRequest{
		Source: "master",
		Offset: 2000,
		Radius: 3,
		Query:  "config=8888",
		Sparse: false,
	}

	cids, err := calcCids(r, nil, nil)
	assert.NoError(t, err)
	assert.Equal(t, "master-001997", cids[0].ID())
	assert.Equal(t, "master-002003", cids[6].ID())
}

type mockVcs struct{}

func (m *mockVcs) LastNIndex(N int) []*vcsinfo.IndexCommit {
	return []*vcsinfo.IndexCommit{&vcsinfo.IndexCommit{Index: 2005}}
}
func (m *mockVcs) Update(ctx context.Context, pull, allBranches bool) error        { return nil }
func (m *mockVcs) From(start time.Time) []string                                   { return nil }
func (m *mockVcs) Range(begin, end time.Time) []*vcsinfo.IndexCommit               { return nil }
func (m *mockVcs) IndexOf(ctx context.Context, hash string) (int, error)           { return 0, nil }
func (m *mockVcs) ByIndex(ctx context.Context, N int) (*vcsinfo.LongCommit, error) { return nil, nil }
func (m *mockVcs) Details(ctx context.Context, hash string, includeBranchInfo bool) (*vcsinfo.LongCommit, error) {
	return nil, nil
}
func (m *mockVcs) GetFile(ctx context.Context, fileName, commitHash string) (string, error) {
	return "", nil
}
func (m *mockVcs) ResolveCommit(ctx context.Context, commitHash string) (string, error) {
	return "", nil
}

func TestCalcCidsSparse(t *testing.T) {
	testutils.SmallTest(t)

	r := &ClusterRequest{
		Source: "master",
		Offset: 2000,
		Radius: 3,
		Query:  "config=8888",
		Sparse: true,
	}

	i := 0
	ends := []int{}
	begins := []int{}
	type cidSlice []*cid.CommitID
	rets := []cidSlice{
		cidSlice{&cid.CommitID{Source: "master", Offset: 2000}},
		cidSlice{
			&cid.CommitID{Source: "master", Offset: 2001},
			&cid.CommitID{Source: "master", Offset: 2002},
			&cid.CommitID{Source: "master", Offset: 2004},
		},
		cidSlice{
			&cid.CommitID{Source: "master", Offset: 1997},
			&cid.CommitID{Source: "master", Offset: 1998},
			&cid.CommitID{Source: "master", Offset: 1999},
		},
	}
	cidsWithDataInRange := func(begin, end int) ([]*cid.CommitID, error) {
		defer func() { i += 1 }()
		ends = append(ends, end)
		begins = append(begins, begin)
		return rets[i], nil
	}

	cids, err := calcCids(r, &mockVcs{}, cidsWithDataInRange)
	assert.NoError(t, err)
	assert.Equal(t, "master-001997", cids[0].ID())
	assert.Equal(t, "master-002004", cids[6].ID())
	assert.Equal(t, []int{2000, 2001, 1400}, begins)
	assert.Equal(t, []int{2001, 2005, 2000}, ends)
}

func TestCalcCidsSparseFails(t *testing.T) {
	testutils.SmallTest(t)

	r := &ClusterRequest{
		Source: "master",
		Offset: 2000,
		Radius: 3,
		Query:  "config=8888",
		Sparse: true,
	}

	cidsWithDataInRange := func(begin, end int) ([]*cid.CommitID, error) {
		return []*cid.CommitID{}, nil
	}

	_, err := calcCids(r, &mockVcs{}, cidsWithDataInRange)
	assert.Error(t, err)
}

func TestCidsWithData(t *testing.T) {
	testutils.SmallTest(t)

	e := vec32.MISSING_DATA_SENTINEL
	headers := []*dataframe.ColumnHeader{
		{Source: "master", Offset: 2000},
		{Source: "master", Offset: 2001},
		{Source: "master", Offset: 2002},
	}
	traceSet := types.TraceSet{
		",arch=x86,config=565,":  types.Trace([]float32{e, 2.1, e}),
		",arch=x86,config=8888,": types.Trace([]float32{e, 3.1, e}),
		",arch=x86,config=gpu,":  types.Trace([]float32{1.4, 4.1, e}),
	}
	d := &dataframe.DataFrame{
		TraceSet: traceSet,
		Header:   headers,
	}

	cids := cidsWithData(d)
	assert.Len(t, cids, 2)
	assert.Equal(t, "master-002000", cids[0].ID())
	assert.Equal(t, "master-002001", cids[1].ID())
}

func TestCidsWithDataEmpty(t *testing.T) {
	testutils.SmallTest(t)

	d := &dataframe.DataFrame{
		TraceSet: types.TraceSet{},
		Header:   []*dataframe.ColumnHeader{},
	}

	cids := cidsWithData(d)
	assert.Len(t, cids, 0)
}
<|endoftext|>


<commit_before>package handlers

import (
	// "log"
	"net/http"
	"strconv"
	"strings"
)

// CORSOption represents a functional option for configuring the CORS middleware.
type CORSOption func(*cors) error

type cors struct {
	h                http.Handler
	allowedHeaders   []string
	allowedMethods   []string
	allowedOrigins   []string
	exposedHeaders   []string
	maxAge           int
	ignoreOptions    bool
	allowCredentials bool
}

var (
	defaultCorsMethods = []string{"GET", "HEAD", "POST"}
	defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language"}
)

const (
	corsOptionMethod           string = "OPTIONS"
	corsAllowOriginHeader      string = "Access-Control-Allow-Origin"
	corsExposeHeadersHeader    string = "Access-Control-Expose-Headers"
	corsMaxAgeHeader           string = "Access-Control-Max-Age"
	corsAllowMethodsHeader     string = "Access-Control-Allow-Methods"
	corsAllowHeadersHeader     string = "Access-Control-Allow-Headers"
	corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials"
	corsRequestMethodHeader    string = "Access-Control-Request-Method"
	corsRequestHeadersHeader   string = "Access-Control-Request-Headers"
	corsOriginHeader           string = "Origin"
	corsVaryHeader             string = "Vary"
	corsGlobMatch              string = "*"
)

func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	origin := r.Header.Get(corsOriginHeader)

	if !ch.isOriginAllowed(origin) {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	handler := ch.h
	defer func() {
		handler.ServeHTTP(w, r)
	}()

	if r.Method == corsOptionMethod && !ch.ignoreOptions {
		handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return })
		if _, ok := r.Header[corsRequestMethodHeader]; !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		method := r.Header.Get(corsRequestMethodHeader)
		if !ch.isMatch(method, ch.allowedMethods) {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",")
		allowedHeaders := []string{}
		for _, v := range requestHeaders {
			canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) {
				continue
			}

			if !ch.isMatch(canonicalHeader, ch.allowedHeaders) {
				w.WriteHeader(http.StatusForbidden)
				return
			}

			allowedHeaders = append(allowedHeaders, canonicalHeader)
		}

		if len(allowedHeaders) > 0 {
			w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ","))
		}

		if ch.maxAge > 0 {
			w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge))
		}

		if !ch.isMatch(method, defaultCorsMethods) {
			w.Header().Set(corsAllowMethodsHeader, method)
		}
	} else {
		if len(ch.exposedHeaders) > 0 {
			w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
		}
	}

	if ch.allowCredentials {
		w.Header().Set(corsAllowCredentialsHeader, "true")
	}

	if len(ch.allowedOrigins) > 1 {
		w.Header().Set(corsVaryHeader, corsOriginHeader)
	}

	w.Header().Set(corsAllowOriginHeader, origin)
}

// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
//  import (
//      "net/http"
//
//      "github.com/gorilla/handlers"
//      "github.com/gorilla/mux"
//  )
//
//  func main() {
//      r := mux.NewRouter()
//      r.HandleFunc("/users", UserEndpoint)
//      r.HandleFunc("/projects", ProjectEndpoint)
//
//      // Apply the CORS middleware to our top-level router, with the defaults.
//      http.ListenAndServe(":8000", handlers.CORS()(r))
//  }
//
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
	ch := parseCORSOptions(opts...)

	// TODO(all): Set defaults
	// Note: append(allowedHeaders, defaultHeaders...) - the default headers here
	// should always be allowed:
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests

	return func(h http.Handler) http.Handler {
		ch.h = h
		return ch
	}
}

func parseCORSOptions(opts ...CORSOption) *cors {
	ch := &cors{
		allowedMethods: defaultCorsMethods,
		allowedHeaders: defaultCorsHeaders,
	}

	for _, option := range opts {
		option(ch)
	}

	return ch
}

//
// Functional options for configuring CORS.
//

// AllowedHeaders adds the provided headers to the list of allowed headers in a
// CORS request.
// The headers Content-Type, Expires, Cache-Control, ... are always allowed.
func AllowedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range headers {
			normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if normalizedHeader == "" {
				continue
			}

			if !ch.isMatch(normalizedHeader, ch.allowedHeaders) {
				ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader)
			}
		}

		return nil
	}
}

// AllowedMethods ...
func AllowedMethods(methods []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range methods {
			normalizedMethod := strings.ToUpper(strings.TrimSpace(v))
			if normalizedMethod == "" {
				continue
			}

			if !ch.isMatch(normalizedMethod, ch.allowedMethods) {
				ch.allowedHeaders = append(ch.allowedHeaders, normalizedMethod)
			}
		}

		return nil
	}
}

// AllowedOrigins sets the allowed origins for CORS requests, as used in the
// 'Allow-Access-Control-Origin' HTTP header.
// Note: Passing in a []string{"*"} will allow any domain.
func AllowedOrigins(origins []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range origins {
			if v == corsGlobMatch {
				ch.allowedOrigins = []string{corsGlobMatch}
				return nil
			}
		}

		ch.allowedOrigins = origins
		return nil
	}
}

// ExposeHeaders are additional headers outside of those which are apart
// of the simple response headers (http://www.w3.org/TR/cors/#simple-response-header)
func ExposedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		ch.exposedHeaders = headers
		return nil
	}
}

// MaxAge determines the maximum age (in seconds) between preflight requests. A
// maximum of 10 minutes is allowed. An age above this value will default to 10
// minutes.
func MaxAge(age int) CORSOption {
	return func(ch *cors) error {
		// Maximum of 10 minutes.
		if age > 600 {
			age = 600
		}

		ch.maxAge = age
		return nil
	}
}

// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
// passing them through to the next handler. This is useful when your application
// or framework has a pre-existing mechanism for responding to OPTIONS requests.
func IgnoreOptions() CORSOption {
	return func(ch *cors) error {
		ch.ignoreOptions = true
		return nil
	}
}

// AllowCredentials ...
func AllowCredentials() CORSOption {
	return func(ch *cors) error {
		ch.allowCredentials = true
		return nil
	}
}

func (ch *cors) isOriginAllowed(origin string) bool {
	if origin == "" {
		return false
	}

	for _, allowedOrigin := range ch.allowedOrigins {
		if allowedOrigin == origin || allowedOrigin == corsGlobMatch {
			return true
		}
	}

	return false
}

func (ch *cors) isMatch(needle string, haystack []string) bool {
	for _, v := range haystack {
		if v == needle {
			return true
		}
	}

	return false
}
<commit_msg>Fall to next handler if ignoreOptions set<commit_after>package handlers

import (
	// "log"
	"net/http"
	"strconv"
	"strings"
)

// CORSOption represents a functional option for configuring the CORS middleware.
type CORSOption func(*cors) error

type cors struct {
	h                http.Handler
	allowedHeaders   []string
	allowedMethods   []string
	allowedOrigins   []string
	exposedHeaders   []string
	maxAge           int
	ignoreOptions    bool
	allowCredentials bool
}

var (
	defaultCorsMethods = []string{"GET", "HEAD", "POST"}
	defaultCorsHeaders = []string{"Accept", "Accept-Language", "Content-Language"}
)

const (
	corsOptionMethod           string = "OPTIONS"
	corsAllowOriginHeader      string = "Access-Control-Allow-Origin"
	corsExposeHeadersHeader    string = "Access-Control-Expose-Headers"
	corsMaxAgeHeader           string = "Access-Control-Max-Age"
	corsAllowMethodsHeader     string = "Access-Control-Allow-Methods"
	corsAllowHeadersHeader     string = "Access-Control-Allow-Headers"
	corsAllowCredentialsHeader string = "Access-Control-Allow-Credentials"
	corsRequestMethodHeader    string = "Access-Control-Request-Method"
	corsRequestHeadersHeader   string = "Access-Control-Request-Headers"
	corsOriginHeader           string = "Origin"
	corsVaryHeader             string = "Vary"
	corsMatchAll               string = "*"
)

func (ch *cors) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	origin := r.Header.Get(corsOriginHeader)

	if !ch.isOriginAllowed(origin) {
		w.WriteHeader(http.StatusBadRequest)
		return
	}

	handler := ch.h
	defer func() {
		handler.ServeHTTP(w, r)
	}()

	if r.Method == corsOptionMethod {
		if ch.ignoreOptions {
			return
		}

		handler = http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return })
		if _, ok := r.Header[corsRequestMethodHeader]; !ok {
			w.WriteHeader(http.StatusBadRequest)
			return
		}

		method := r.Header.Get(corsRequestMethodHeader)
		if !ch.isMatch(method, ch.allowedMethods) {
			w.WriteHeader(http.StatusMethodNotAllowed)
			return
		}

		requestHeaders := strings.Split(r.Header.Get(corsRequestHeadersHeader), ",")
		allowedHeaders := []string{}
		for _, v := range requestHeaders {
			canonicalHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if canonicalHeader == "" || ch.isMatch(canonicalHeader, defaultCorsHeaders) {
				continue
			}

			if !ch.isMatch(canonicalHeader, ch.allowedHeaders) {
				w.WriteHeader(http.StatusForbidden)
				return
			}

			allowedHeaders = append(allowedHeaders, canonicalHeader)
		}

		if len(allowedHeaders) > 0 {
			w.Header().Set(corsAllowHeadersHeader, strings.Join(allowedHeaders, ","))
		}

		if ch.maxAge > 0 {
			w.Header().Set(corsMaxAgeHeader, strconv.Itoa(ch.maxAge))
		}

		if !ch.isMatch(method, defaultCorsMethods) {
			w.Header().Set(corsAllowMethodsHeader, method)
		}
	} else {
		if len(ch.exposedHeaders) > 0 {
			w.Header().Set(corsExposeHeadersHeader, strings.Join(ch.exposedHeaders, ","))
		}
	}

	if ch.allowCredentials {
		w.Header().Set(corsAllowCredentialsHeader, "true")
	}

	if len(ch.allowedOrigins) > 1 {
		w.Header().Set(corsVaryHeader, corsOriginHeader)
	}

	w.Header().Set(corsAllowOriginHeader, origin)
}

// CORS provides Cross-Origin Resource Sharing middleware.
// Example:
//
//  import (
//      "net/http"
//
//      "github.com/gorilla/handlers"
//      "github.com/gorilla/mux"
//  )
//
//  func main() {
//      r := mux.NewRouter()
//      r.HandleFunc("/users", UserEndpoint)
//      r.HandleFunc("/projects", ProjectEndpoint)
//
//      // Apply the CORS middleware to our top-level router, with the defaults.
//      http.ListenAndServe(":8000", handlers.CORS()(r))
//  }
//
func CORS(opts ...CORSOption) func(http.Handler) http.Handler {
	ch := parseCORSOptions(opts...)

	// TODO(all): Set defaults
	// Note: append(allowedHeaders, defaultHeaders...) - the default headers here
	// should always be allowed:
	// https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS#Simple_requests

	return func(h http.Handler) http.Handler {
		ch.h = h
		return ch
	}
}

func parseCORSOptions(opts ...CORSOption) *cors {
	ch := &cors{
		allowedMethods: defaultCorsMethods,
		allowedHeaders: defaultCorsHeaders,
	}

	for _, option := range opts {
		option(ch)
	}

	return ch
}

//
// Functional options for configuring CORS.
//

// AllowedHeaders adds the provided headers to the list of allowed headers in a
// CORS request.
// The headers Content-Type, Expires, Cache-Control, ... are always allowed.
func AllowedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range headers {
			normalizedHeader := http.CanonicalHeaderKey(strings.TrimSpace(v))
			if normalizedHeader == "" {
				continue
			}

			if !ch.isMatch(normalizedHeader, ch.allowedHeaders) {
				ch.allowedHeaders = append(ch.allowedHeaders, normalizedHeader)
			}
		}

		return nil
	}
}

// AllowedMethods ...
func AllowedMethods(methods []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range methods {
			normalizedMethod := strings.ToUpper(strings.TrimSpace(v))
			if normalizedMethod == "" {
				continue
			}

			if !ch.isMatch(normalizedMethod, ch.allowedMethods) {
				ch.allowedHeaders = append(ch.allowedHeaders, normalizedMethod)
			}
		}

		return nil
	}
}

// AllowedOrigins sets the allowed origins for CORS requests, as used in the
// 'Allow-Access-Control-Origin' HTTP header.
// Note: Passing in a []string{"*"} will allow any domain.
func AllowedOrigins(origins []string) CORSOption {
	return func(ch *cors) error {
		for _, v := range origins {
			if v == corsMatchAll {
				ch.allowedOrigins = []string{corsMatchAll}
				return nil
			}
		}

		ch.allowedOrigins = origins
		return nil
	}
}

// ExposeHeaders are additional headers outside of those which are apart
// of the simple response headers (http://www.w3.org/TR/cors/#simple-response-header)
func ExposedHeaders(headers []string) CORSOption {
	return func(ch *cors) error {
		ch.exposedHeaders = headers
		return nil
	}
}

// MaxAge determines the maximum age (in seconds) between preflight requests. A
// maximum of 10 minutes is allowed. An age above this value will default to 10
// minutes.
func MaxAge(age int) CORSOption {
	return func(ch *cors) error {
		// Maximum of 10 minutes.
		if age > 600 {
			age = 600
		}

		ch.maxAge = age
		return nil
	}
}

// IgnoreOptions causes the CORS middleware to ignore OPTIONS requests, instead
// passing them through to the next handler. This is useful when your application
// or framework has a pre-existing mechanism for responding to OPTIONS requests.
func IgnoreOptions() CORSOption {
	return func(ch *cors) error {
		ch.ignoreOptions = true
		return nil
	}
}

// AllowCredentials ...
func AllowCredentials() CORSOption {
	return func(ch *cors) error {
		ch.allowCredentials = true
		return nil
	}
}

func (ch *cors) isOriginAllowed(origin string) bool {
	if origin == "" {
		return false
	}

	for _, allowedOrigin := range ch.allowedOrigins {
		if allowedOrigin == origin || allowedOrigin == corsMatchAll {
			return true
		}
	}

	return false
}

func (ch *cors) isMatch(needle string, haystack []string) bool {
	for _, v := range haystack {
		if v == needle {
			return true
		}
	}

	return false
}
<|endoftext|>


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

// Package environment provides an interface for defining how browser environments
// are managed.
package environment

import (
	"context"
	"sync"

	"github.com/bazelbuild/rules_webtesting/go/launcher/diagnostics"
	"github.com/bazelbuild/rules_webtesting/go/launcher/errors"
	"github.com/bazelbuild/rules_webtesting/go/launcher/healthreporter"
	"github.com/bazelbuild/rules_webtesting/go/metadata"
	"github.com/bazelbuild/rules_webtesting/go/metadata/capabilities"
)

// Env allows web_test environments to be started for controlling a browser
// using Selenium WebDriver.
type Env interface {
	healthreporter.HealthReporter
	// SetUp is called once at the beginning of the test run, and should start a
	// WebDriver server. It is not necessary that the environment be healthy when
	// this returns.
	SetUp(ctx context.Context) error
	// StartSession is called for each new WebDriver session, before the new
	// session command is sent to the WebDriver server. caps is the capabilities
	// sent to the proxy from the client, and the return value is the capabilities
	// that should be actually sent to the WebDriver server new session command.
	StartSession(ctx context.Context, id int, caps capabilities.Spec) (capabilities.Spec, error)
	// StopSession is called for each new WebDriver session, before the delete
	// session command is sent to the WebDriver server.
	StopSession(ctx context.Context, id int) error
	// TearDown is called at the end of the test run.
	TearDown(ctx context.Context) error
	// WDAddress returns the address of the WebDriver server.
	WDAddress(context.Context) string
}

// Base is a partial implementation of Env useful as the base struct for
// implementations of Env.
type Base struct {
	*metadata.Metadata
	diagnostics.Diagnostics
	name string

	mu      sync.RWMutex
	started bool
	stopped bool
}

// NewBase creates a new Base environment with the given component name.
func NewBase(name string, m *metadata.Metadata, d diagnostics.Diagnostics) (*Base, error) {
	return &Base{
		name:        name,
		Metadata:    m,
		Diagnostics: d,
	}, nil
}

// SetUp starts the Environment.
func (b *Base) SetUp(ctx context.Context) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if b.started {
		return errors.NewPermanent(b.Name(), "cannot be started; it has already been started once")
	}
	b.started = true
	return nil
}

// StartSession merges the passed in caps with b.Metadata.caps and returns the merged
// capabilities that should be used when calling new session on the WebDriver
// server.
func (b *Base) StartSession(ctx context.Context, id int, caps capabilities.Spec) (capabilities.Spec, error) {
	if err := b.Healthy(ctx); err != nil {
		return capabilities.Spec{}, err
	}
	resolved, err := b.Metadata.ResolvedCapabilities()
	if err != nil {
		return capabilities.Spec{}, err
	}
	updated := capabilities.MergeSpecOntoCaps(resolved, caps)
	return updated, nil
}

// StopSession is a no-op implementation of Env.StopSession.
func (b *Base) StopSession(ctx context.Context, _ int) error {
	return b.Healthy(ctx)
}

// TearDown is a no-op implementation of Env.TearDown.
func (b *Base) TearDown(ctx context.Context) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if !b.started {
		return errors.NewPermanent(b.Name(), "cannot be stopped; it was never started")
	}
	if b.stopped {
		return errors.NewPermanent(b.Name(), "cannot be stopped; it was already stopped once")
	}
	b.stopped = true
	return nil
}

// Healthy returns nil (i.e., healthy) iff the environment has been started and
// has not yet been stopped.
func (b *Base) Healthy(context.Context) error {
	b.mu.RLock()
	defer b.mu.RUnlock()
	if !b.started {
		return errors.NewPermanent(b.Name(), "has not been started")
	}
	if b.stopped {
		return errors.NewPermanent(b.Name(), "has been stopped")
	}
	return nil
}

// WDAddress returns the empty string.
func (*Base) WDAddress(context.Context) string {
	return ""
}

// Name returns the name of this environment to be used in error messages.
func (b *Base) Name() string {
	return b.name
}
<commit_msg>Make an environment health check err non-permanent (#180)<commit_after>// Copyright 2016 Google Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package environment provides an interface for defining how browser environments
// are managed.
package environment

import (
	"context"
	"sync"

	"github.com/bazelbuild/rules_webtesting/go/launcher/diagnostics"
	"github.com/bazelbuild/rules_webtesting/go/launcher/errors"
	"github.com/bazelbuild/rules_webtesting/go/launcher/healthreporter"
	"github.com/bazelbuild/rules_webtesting/go/metadata"
	"github.com/bazelbuild/rules_webtesting/go/metadata/capabilities"
)

// Env allows web_test environments to be started for controlling a browser
// using Selenium WebDriver.
type Env interface {
	healthreporter.HealthReporter
	// SetUp is called once at the beginning of the test run, and should start a
	// WebDriver server. It is not necessary that the environment be healthy when
	// this returns.
	SetUp(ctx context.Context) error
	// StartSession is called for each new WebDriver session, before the new
	// session command is sent to the WebDriver server. caps is the capabilities
	// sent to the proxy from the client, and the return value is the capabilities
	// that should be actually sent to the WebDriver server new session command.
	StartSession(ctx context.Context, id int, caps capabilities.Spec) (capabilities.Spec, error)
	// StopSession is called for each new WebDriver session, before the delete
	// session command is sent to the WebDriver server.
	StopSession(ctx context.Context, id int) error
	// TearDown is called at the end of the test run.
	TearDown(ctx context.Context) error
	// WDAddress returns the address of the WebDriver server.
	WDAddress(context.Context) string
}

// Base is a partial implementation of Env useful as the base struct for
// implementations of Env.
type Base struct {
	*metadata.Metadata
	diagnostics.Diagnostics
	name string

	mu      sync.RWMutex
	started bool
	stopped bool
}

// NewBase creates a new Base environment with the given component name.
func NewBase(name string, m *metadata.Metadata, d diagnostics.Diagnostics) (*Base, error) {
	return &Base{
		name:        name,
		Metadata:    m,
		Diagnostics: d,
	}, nil
}

// SetUp starts the Environment.
func (b *Base) SetUp(ctx context.Context) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if b.started {
		return errors.NewPermanent(b.Name(), "cannot be started; it has already been started once")
	}
	b.started = true
	return nil
}

// StartSession merges the passed in caps with b.Metadata.caps and returns the merged
// capabilities that should be used when calling new session on the WebDriver
// server.
func (b *Base) StartSession(ctx context.Context, id int, caps capabilities.Spec) (capabilities.Spec, error) {
	if err := b.Healthy(ctx); err != nil {
		return capabilities.Spec{}, err
	}
	resolved, err := b.Metadata.ResolvedCapabilities()
	if err != nil {
		return capabilities.Spec{}, err
	}
	updated := capabilities.MergeSpecOntoCaps(resolved, caps)
	return updated, nil
}

// StopSession is a no-op implementation of Env.StopSession.
func (b *Base) StopSession(ctx context.Context, _ int) error {
	return b.Healthy(ctx)
}

// TearDown is a no-op implementation of Env.TearDown.
func (b *Base) TearDown(ctx context.Context) error {
	b.mu.Lock()
	defer b.mu.Unlock()
	if !b.started {
		return errors.NewPermanent(b.Name(), "cannot be stopped; it was never started")
	}
	if b.stopped {
		return errors.NewPermanent(b.Name(), "cannot be stopped; it was already stopped once")
	}
	b.stopped = true
	return nil
}

// Healthy returns nil (i.e., healthy) iff the environment has been started and
// has not yet been stopped.
func (b *Base) Healthy(context.Context) error {
	b.mu.RLock()
	defer b.mu.RUnlock()
	if !b.started {
		return errors.New(b.Name(), "has not been started")
	}
	if b.stopped {
		return errors.NewPermanent(b.Name(), "has been stopped")
	}
	return nil
}

// WDAddress returns the empty string.
func (*Base) WDAddress(context.Context) string {
	return ""
}

// Name returns the name of this environment to be used in error messages.
func (b *Base) Name() string {
	return b.name
}
<|endoftext|>


<commit_before>package config

import (
	"os"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

const (
	testConfigPath = "./test.toml"
	env            = "MehmetAli"
	hostname       = "Koding"
	port           = 5672
)

func TestConfigMustRead(t *testing.T) {
	Convey("while testing MustRead", t, func() {
		Convey("valid testConfigPath should return config", func() {
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			So(MustRead(testConfigPath), ShouldNotBeNil)
		})
		Convey("invalid testConfigPath should panic", func() {
			So(func() { MustRead("testConfigPath") }, ShouldPanic)
		})
		Convey("setting socialapi env should override config", func() {
			err := os.Setenv("KONFIG_SOCIALAPI_ENVIRONMENT", env)
			So(err, ShouldBeNil)
			// just to be sure about the function will not panic
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			a := MustRead(testConfigPath)
			So(a.Environment, ShouldEqual, env)
		})
		Convey("setting socialapi hostname should override config", func() {
			err := os.Setenv("KONFIG_SOCIALAPI_HOSTNAME", hostname)
			So(err, ShouldBeNil)
			aPath := MustRead(testConfigPath)
			So(aPath.Hostname, ShouldEqual, hostname)
		})
	})
}
<commit_msg>Socialapi: fix tests<commit_after>package config

import (
	"os"
	"testing"

	. "github.com/smartystreets/goconvey/convey"
)

const (
	testConfigPath = "./test.toml"
	env            = "MehmetAli"
)

func TestConfigMustRead(t *testing.T) {
	Convey("while testing MustRead", t, func() {
		Convey("valid testConfigPath should return config", func() {
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			So(MustRead(testConfigPath), ShouldNotBeNil)
		})
		Convey("invalid testConfigPath should panic", func() {
			So(func() { MustRead("testConfigPath") }, ShouldPanic)
		})
		Convey("setting socialapi env should override config", func() {
			err := os.Setenv("KONFIG_SOCIALAPI_PROTOCOL", env)
			So(err, ShouldBeNil)
			// just to be sure about the function will not panic
			So(func() { MustRead(testConfigPath) }, ShouldNotPanic)
			a := MustRead(testConfigPath)
			So(a.Protocol, ShouldEqual, env)
		})
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"github.com/stevencorona/elastic-haproxy/elb"
	"github.com/stevencorona/elastic-haproxy/haproxy"
	"github.com/stevencorona/elastic-haproxy/statsd"
	"log"
	"os"
	"os/signal"
	"syscall"
)

const DEFAULT_CONFIG_FILE = "config/elastic.toml"
var flagConfigFile string

func main() {

	// Read in the configuration file settings. I think we should instead
	// serialize to JSON and treat this like a view.
	haproxy.Transform()

	flag.StringVar(&flagConfigFile, "configFile", DEFAULT_CONFIG_FILE, "Path to toml file")
	flag.Parse()

	conf, err := LoadConfig(flagConfigFile)

	if err != nil {
		log.Fatal("Could not load configuration", err)
	}

	server := new(haproxy.Server)

	// We use two channels— one to send actions to the server and one to recieve
	// notifications from it. Create them right now.
	actionChan := make(chan haproxy.Action)
	notificationChan := make(chan haproxy.Event)

	// Handle signals gracefully in another goroutine
	go gracefulSignals(server, actionChan, notificationChan)

	// Start up the HAProxy Server
	go server.Start(notificationChan, actionChan)

	// Setup the ELB HTTP Handlers
	go elb.InitApiHandlers()

	// Fire up statsd goroutine if statsd is enabled. This might be better off in
	// a seperate binary to monitor HAProxy.
	if conf.Statsd.Enabled {
		go statsd.SendMetrics(server)
	}

	// Event loop for handling events from the HAProxy server
	// (right now, it only sends start/stop notifications)
	for _ = range notificationChan {
		log.Println("Received a notification")

		server.Socket = conf.Haproxy.Socket
		serverInfo := server.GetInfo()
		log.Println(serverInfo)
	}

}

// Capture, capture, capture those signals. We gracefully shutdown on a
// SIGQUIT (this is what docker sends w/ docker stop) and everything else
// causes a graceful (zero-downtime) restart of HAProxy. Remember, HAProxy
// does not support reloads or graceful restarts without some... trickery.
func gracefulSignals(server *haproxy.Server, actionChan chan haproxy.Action, notificationChan chan haproxy.Event) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT)

	for s := range signals {
		log.Println("Received a signal", s)

		switch s {
		case syscall.SIGQUIT:
			log.Println("Caught SIGQUIT, Stopping HAProxy")
			actionChan <- haproxy.WantsStop
			<-notificationChan
			os.Exit(1)
		default:
			actionChan <- haproxy.WantsReload
		}
	}
}
<commit_msg>don't transform on load<commit_after>package main

import (
	"flag"
	"github.com/stevencorona/elastic-haproxy/elb"
	"github.com/stevencorona/elastic-haproxy/haproxy"
	"github.com/stevencorona/elastic-haproxy/statsd"
	"log"
	"os"
	"os/signal"
	"syscall"
)

const DEFAULT_CONFIG_FILE = "config/elastic.toml"
var flagConfigFile string

func main() {

	// Read in the configuration file settings. I think we should instead
	// serialize to JSON and treat this like a view.
	// haproxy.Transform()

	flag.StringVar(&flagConfigFile, "configFile", DEFAULT_CONFIG_FILE, "Path to toml file")
	flag.Parse()

	conf, err := LoadConfig(flagConfigFile)

	if err != nil {
		log.Fatal("Could not load configuration", err)
	}

	server := new(haproxy.Server)

	// We use two channels— one to send actions to the server and one to recieve
	// notifications from it. Create them right now.
	actionChan := make(chan haproxy.Action)
	notificationChan := make(chan haproxy.Event)

	// Handle signals gracefully in another goroutine
	go gracefulSignals(server, actionChan, notificationChan)

	// Start up the HAProxy Server
	go server.Start(notificationChan, actionChan)

	// Setup the ELB HTTP Handlers
	go elb.InitApiHandlers()

	// Fire up statsd goroutine if statsd is enabled. This might be better off in
	// a seperate binary to monitor HAProxy.
	if conf.Statsd.Enabled {
		go statsd.SendMetrics(server)
	}

	// Event loop for handling events from the HAProxy server
	// (right now, it only sends start/stop notifications)
	for _ = range notificationChan {
		log.Println("Received a notification")

		server.Socket = conf.Haproxy.Socket
		serverInfo := server.GetInfo()
		log.Println(serverInfo)
	}

}

// Capture, capture, capture those signals. We gracefully shutdown on a
// SIGQUIT (this is what docker sends w/ docker stop) and everything else
// causes a graceful (zero-downtime) restart of HAProxy. Remember, HAProxy
// does not support reloads or graceful restarts without some... trickery.
func gracefulSignals(server *haproxy.Server, actionChan chan haproxy.Action, notificationChan chan haproxy.Event) {
	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT)

	for s := range signals {
		log.Println("Received a signal", s)

		switch s {
		case syscall.SIGQUIT:
			log.Println("Caught SIGQUIT, Stopping HAProxy")
			actionChan <- haproxy.WantsStop
			<-notificationChan
			os.Exit(1)
		default:
			actionChan <- haproxy.WantsReload
		}
	}
}
<|endoftext|>


<commit_before>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

type entries []*Entry

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  entries
	stop     chan struct{}
	add      chan *Entry
	remove   chan string
	snapshot chan entries
	running  bool
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// Unique name to identify the Entry so as to be able to remove it later.
	Name string
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		remove:   make(chan string),
		stop:     make(chan struct{}),
		snapshot: make(chan entries),
		running:  false,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func(), name string) {
	c.AddJob(spec, FuncJob(cmd), name)
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job, name string) {
	c.Schedule(Parse(spec), cmd, name)
}

// RemoveJob removes a Job from the Cron based on name.
func (c *Cron) RemoveJob(name string) {
	if !c.running {
		i := c.entries.pos(name)

		if i == -1 {
			return
		}

		c.entries = c.entries[:i+copy(c.entries[i:], c.entries[i+1:])]
		return
	}

	c.remove <- name
}

func (entrySlice entries) pos(name string) int {
	for p, e := range entrySlice {
		if e.Name == name {
			return p
		}
	}
	return -1
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, name string) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		Name:     name,
	}

	if !c.running {
		i := c.entries.pos(entry.Name)
		if i != -1 {
			return
		}
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().Local()
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		select {
		case now = <-time.After(effective.Sub(now)):
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				go e.Job.Run()
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue

		case newEntry := <-c.add:
			i := c.entries.pos(newEntry.Name)
			if i != -1 {
				break
			}
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(time.Now().Local())

		case name := <-c.remove:
			i := c.entries.pos(name)

			if i == -1 {
				break
			}

			c.entries = c.entries[:i+copy(c.entries[i:], c.entries[i+1:])]

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
		})
	}
	return entries
}
<commit_msg>显示 job name<commit_after>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"sort"
	"time"
)

type entries []*Entry

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  entries
	stop     chan struct{}
	add      chan *Entry
	remove   chan string
	snapshot chan entries
	running  bool
}

// Job is an interface for submitted cron jobs.
type Job interface {
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job

	// Unique name to identify the Entry so as to be able to remove it later.
	Name string
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner.
func New() *Cron {
	return &Cron{
		entries:  nil,
		add:      make(chan *Entry),
		remove:   make(chan string),
		stop:     make(chan struct{}),
		snapshot: make(chan entries),
		running:  false,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func(), name string) {
	c.AddJob(spec, FuncJob(cmd), name)
}

// AddFunc adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job, name string) {
	c.Schedule(Parse(spec), cmd, name)
}

// RemoveJob removes a Job from the Cron based on name.
func (c *Cron) RemoveJob(name string) {
	if !c.running {
		i := c.entries.pos(name)

		if i == -1 {
			return
		}

		c.entries = c.entries[:i+copy(c.entries[i:], c.entries[i+1:])]
		return
	}

	c.remove <- name
}

func (entrySlice entries) pos(name string) int {
	for p, e := range entrySlice {
		if e.Name == name {
			return p
		}
	}
	return -1
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job, name string) {
	entry := &Entry{
		Schedule: schedule,
		Job:      cmd,
		Name:     name,
	}

	if !c.running {
		i := c.entries.pos(entry.Name)
		if i != -1 {
			return
		}
		c.entries = append(c.entries, entry)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Start the cron scheduler in its own go-routine.
func (c *Cron) Start() {
	c.running = true
	go c.run()
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().Local()
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		select {
		case now = <-time.After(effective.Sub(now)):
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				go e.Job.Run()
				e.Prev = e.Next
				e.Next = e.Schedule.Next(effective)
			}
			continue

		case newEntry := <-c.add:
			i := c.entries.pos(newEntry.Name)
			if i != -1 {
				break
			}
			c.entries = append(c.entries, newEntry)
			newEntry.Next = newEntry.Schedule.Next(time.Now().Local())

		case name := <-c.remove:
			i := c.entries.pos(name)

			if i == -1 {
				break
			}

			c.entries = c.entries[:i+copy(c.entries[i:], c.entries[i+1:])]

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().Local()
	}
}

// Stop the cron scheduler.
func (c *Cron) Stop() {
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
			Name:     e.Name,
		})
	}
	return entries
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"path"
	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/tcw/saxer/saxReader"
	"io"
	"bufio"
	"strings"
	"log"
	"runtime/pprof"
)

var (
	query = kingpin.Arg("query", "Sax query expression").Required().String()
	filename = kingpin.Arg("file", "xml-file").String()
	isInnerXml = kingpin.Flag("inner", "Inner-xml of selected element (default false)").Short('i').Default("false").Bool()
	count = kingpin.Flag("count", "Number of matches (default false)").Short('n').Default("false").Bool()
	contentBuffer = kingpin.Flag("cont-buf", "Size of content buffer in MB - returned elements size").Short('e').Default("4").Int()
	tagBuffer = kingpin.Flag("tag-buf", "Size of element tag buffer in KB - tag size").Short('t').Default("4").Int()
	firstN = kingpin.Flag("firstN", "First n matches (default (0 = all matches))").Short('f').Default("0").Int()
	cpuProfile = kingpin.Flag("profile", "Profile parser").Short('p').Bool()
)

const ONE_KB  int = 1024
const ONE_MB  int = ONE_KB * ONE_KB

func main() {
	kingpin.Version("0.0.4")
	kingpin.Parse()

	//go tool pprof --pdf saxer cpu.pprof > callgraph.pdf
	//evince callgraph.pdf

	if *cpuProfile {
		f, err := os.Create("cpu.pprof")
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		fmt.Println("profiling!")
		defer pprof.StopCPUProfile()
	}

	if strings.TrimSpace(*filename) != "" {
		absFilename, err := abs(*filename)
		if err != nil {
			panic(err.Error())
		}
		file, err := os.Open(absFilename)
		if err != nil {
			panic(err.Error())
		}
		defer file.Close()
		SaxXmlInput(file)
	}else {
		reader := bufio.NewReader(os.Stdin)
		SaxXmlInput(reader)
	}
}

func emitterPrinter(emitter chan string) {
	for {
		message := <-emitter
		if len(message) != 0{
			fmt.Println(message)
		}
	}
}

func SaxXmlInput(reader io.Reader) {
	var err error
	var sr saxReader.SaxReader
	sr = saxReader.NewSaxReaderNoEmitter()
	sr.IsInnerXml = *isInnerXml
	sr.ContentBufferSize = *contentBuffer * ONE_MB
	sr.ElementBufferSize = *tagBuffer * ONE_KB
	if *count {
		var counter uint64 = 0
		emitterCounter := func(element string) bool {
			counter++
			return false
		};
		sr.EmitterFn = emitterCounter
		err = sr.Read(reader, *query)
		fmt.Println(counter)
	}else if *firstN > 0 {
		counter := 0
		elemChan := make(chan string, 100)
		go emitterPrinter(elemChan)
		emitter := func(element string) bool{
			elemChan <- element
			counter++
			if counter >= *firstN{
				close(elemChan)
				return true
			}else {
				return false
			}
		};

		sr.EmitterFn = emitter
		err = sr.Read(reader, *query)
		for{
			if len(elemChan) == 0{
				break
			}
		}
	}else {
		elemChan := make(chan string, 100)
		defer close(elemChan)
		go emitterPrinter(elemChan)
		emitter := func(element string) bool{
			elemChan <- element
			return false
		};
		sr.EmitterFn = emitter
		err = sr.Read(reader, *query)
		for{
			if len(elemChan) == 0{
				break
			}
		}
	}
	if err != nil {
		panic(err)
	}
}

func abs(name string) (string, error) {
	if path.IsAbs(name) {
		return name, nil
	}
	wd, err := os.Getwd()
	return path.Join(wd, name), err
}
<commit_msg>Stop execution through emitterFn<commit_after>package main

import (
	"fmt"
	"os"
	"path"
	"gopkg.in/alecthomas/kingpin.v2"
	"github.com/tcw/saxer/saxReader"
	"io"
	"bufio"
	"strings"
	"log"
	"runtime/pprof"
	"sync"
)

var (
	query = kingpin.Arg("query", "Sax query expression").Required().String()
	filename = kingpin.Arg("file", "xml-file").String()
	isInnerXml = kingpin.Flag("inner", "Inner-xml of selected element (default false)").Short('i').Default("false").Bool()
	count = kingpin.Flag("count", "Number of matches (default false)").Short('n').Default("false").Bool()
	contentBuffer = kingpin.Flag("cont-buf", "Size of content buffer in MB - returned elements size").Short('e').Default("4").Int()
	tagBuffer = kingpin.Flag("tag-buf", "Size of element tag buffer in KB - tag size").Short('t').Default("4").Int()
	firstN = kingpin.Flag("firstN", "First n matches (default (0 = all matches))").Short('f').Default("0").Int()
	cpuProfile = kingpin.Flag("profile", "Profile parser").Short('p').Bool()
)

const ONE_KB  int = 1024
const ONE_MB  int = ONE_KB * ONE_KB

func main() {
	kingpin.Version("0.0.4")
	kingpin.Parse()

	//go tool pprof --pdf saxer cpu.pprof > callgraph.pdf
	//evince callgraph.pdf

	if *cpuProfile {
		f, err := os.Create("cpu.pprof")
		if err != nil {
			log.Fatal(err)
		}
		pprof.StartCPUProfile(f)
		fmt.Println("profiling!")
		defer pprof.StopCPUProfile()
	}

	if strings.TrimSpace(*filename) != "" {
		absFilename, err := abs(*filename)
		if err != nil {
			panic(err.Error())
		}
		file, err := os.Open(absFilename)
		if err != nil {
			panic(err.Error())
		}
		defer file.Close()
		SaxXmlInput(file)
	}else {
		reader := bufio.NewReader(os.Stdin)
		SaxXmlInput(reader)
	}
}

func emitterPrinter(emitter chan string, wg *sync.WaitGroup) {
	for {
		fmt.Println(<-emitter)
		wg.Done()
	}
}

func SaxXmlInput(reader io.Reader) {
	var err error
	var sr saxReader.SaxReader
	sr = saxReader.NewSaxReaderNoEmitter()
	sr.IsInnerXml = *isInnerXml
	sr.ContentBufferSize = *contentBuffer * ONE_MB
	sr.ElementBufferSize = *tagBuffer * ONE_KB
	if *count {
		var counter uint64 = 0
		emitterCounter := func(element string) bool {
			counter++
			return false
		};
		sr.EmitterFn = emitterCounter
		err = sr.Read(reader, *query)
		fmt.Println(counter)
	}else if *firstN > 0 {
		counter := 0
		elemChan := make(chan string, 100)
		var wg sync.WaitGroup
		go emitterPrinter(elemChan, &wg)
		emitter := func(element string) bool {
			wg.Add(1)
			elemChan <- element
			counter++
			if counter >= *firstN {
				return true
			}else {
				return false
			}
		};
		sr.EmitterFn = emitter
		err = sr.Read(reader, *query)
		wg.Wait()
	}else {
		elemChan := make(chan string, 100)
		var wg sync.WaitGroup
		go emitterPrinter(elemChan, &wg)
		emitter := func(element string) bool {
			elemChan <- element
			return false
		};
		sr.EmitterFn = emitter
		err = sr.Read(reader, *query)
		wg.Wait()
	}
	if err != nil {
		panic(err)
	}
}

func abs(name string) (string, error) {
	if path.IsAbs(name) {
		return name, nil
	}
	wd, err := os.Getwd()
	return path.Join(wd, name), err
}
<|endoftext|>


<commit_before>package main

import (
	"path/filepath"
	"runtime"
	"strings"
	"testing"
)

var (
	dirname  string
	fixtures = map[string]string{
		"insert.sql":        "INSERT INTO post (slug, title, created, markdown, html)\nVALUES (?, ?, ?, ?, ?)",
		"query_all.sql":     "SELECT\nid,\nslug,\ntitle,\ncreated,\nmarkdown,\nhtml\nFROM post",
		"query_by_slug.sql": "SELECT\nid,\nslug,\ntitle,\ncreated,\nmarkdown,\nhtml\nFROM post\nWHERE slug = ?",
	}
)

func init() {
	dirname = filepath.Join(".", "fixtures")

	// replace newlines for running unit tests on windows
	if runtime.GOOS == "windows" {
		for k, v := range fixtures {
			fixtures[k] = strings.Replace(v, "\n", "\r\n", -1)
		}
	}
}

func TestNew(t *testing.T) {
	s, err := newPurse(dirname)
	if err != nil {
		t.Errorf("unexpected error from New() on fixtures directory")
	}

	if len(fixtures) != len(s.Items) {
		t.Errorf("invalid number of loaded SQL files")
	}

	for key := range fixtures {
		_, ok := s.Items[key]
		if !ok {
			t.Errorf("unable to find loaded file %s in file map", key)
		}
	}

	// verify only SQL files were loaded
	for key := range s.Items {
		if filepath.Ext(key) != ".sql" {
			t.Errorf("loaded unexpected file type: %s", key)
		}
	}

	// try to load file instead of directory
	_, err = newPurse(filepath.Join(".", "purse.go"))
	if err == nil {
		t.Errorf("expected error trying to load from non-directory")
	}

	// try to load directory that does not exist
	_, err = newPurse(filepath.Join(".", "foo"))
	if err == nil {
		t.Errorf("expected error trying to load directory that does not exist")
	}
}

func TestGet(t *testing.T) {
	/*s, err := newPurse(dirname)
	if err != nil {
		t.Errorf("unexpected error from New() on fixtures directory")
	}

	for key, val := range fixtures {
		v, ok := s.getContents(key)
		if !ok {
			t.Errorf("unable to find loaded file %s in file map", key)
		}
		if v != val {
			t.Errorf("invalid %s file content:\n%v\n%v", key, []byte(v), []byte(val))
		}
	}*/
}

func BenchmarkGet(b *testing.B) {
	/*s, _ := newPurse(dirname)
	key := "query_by_slug.sql"

	for i := 0; i < b.N; i++ {
		s.getContents(key)
	}*/
}
<commit_msg>Removed existing tests.<commit_after><|endoftext|>


<commit_before>package tasks

import (
	"database/sql"
	"time"

	"pixur.org/pixur/schema"
	"pixur.org/pixur/status"
)

var _ Task = &SoftDeletePicTask{}

type SoftDeletePicTask struct {
	// deps
	DB *sql.DB

	// input
	PicID int64
	// Represents when this Pic can be hard deleted.  Optional.
	PendingDeletionTime *time.Time

	// Why is this being deleted
	Reason  schema.Pic_DeletionStatus_Reason
	Details string

	// Can this picture ever be re uploaded?
	Temporary bool
}

func (task *SoftDeletePicTask) Run() error {
	if task.Reason == schema.Pic_DeletionStatus_UNKNOWN {
		return status.InternalError(nil, "Invalid deletion reason", task.Reason)
	}

	tx, err := task.DB.Begin()
	if err != nil {
		return status.InternalError(err, "Unable to Begin TX")
	}
	defer tx.Rollback()

	p, err := lookupPicForUpdate(task.PicID, tx)
	if err != nil {
		return err
	}

	now := time.Now()

	if p.DeletionStatus != nil {
		if p.HardDeleted() {
			return status.InvalidArgument(nil, "Pic is already Hard Deleted")
		}
	} else {
		p.DeletionStatus = &schema.Pic_DeletionStatus{}
	}

	p.DeletionStatus.Reason = task.Reason
	p.DeletionStatus.Details = task.Details
	p.DeletionStatus.Temporary = task.Temporary
	if task.PendingDeletionTime != nil {
		p.DeletionStatus.PendingDeletedTs = schema.ToTs(*task.PendingDeletionTime)
	} else {
		p.DeletionStatus.PendingDeletedTs = nil
	}

	if p.DeletionStatus.MarkedDeletedTs == nil {
		p.DeletionStatus.MarkedDeletedTs = schema.ToTs(now)
	}
	p.SetModifiedTime(now)
	if err := p.Update(tx); err != nil {
		return status.InternalError(err, "Unable to update")
	}

	if err := tx.Commit(); err != nil {
		return status.InternalError(err, "Unable to Commit")
	}

	return nil
}
<commit_msg>tasks: use new table api for soft delete pic task<commit_after>package tasks

import (
	"database/sql"
	"time"

	"pixur.org/pixur/schema"
	"pixur.org/pixur/schema/db"
	tab "pixur.org/pixur/schema/tables"
	"pixur.org/pixur/status"
)

var _ Task = &SoftDeletePicTask{}

type SoftDeletePicTask struct {
	// deps
	DB *sql.DB

	// input
	PicID int64
	// Represents when this Pic can be hard deleted.  Optional.
	PendingDeletionTime *time.Time

	// Why is this being deleted
	Reason  schema.Pic_DeletionStatus_Reason
	Details string

	// Can this picture ever be re uploaded?
	Temporary bool
}

func (task *SoftDeletePicTask) Run() (errCap error) {
	if task.Reason == schema.Pic_DeletionStatus_UNKNOWN {
		return status.InternalError(nil, "Invalid deletion reason", task.Reason)
	}

	j, err := tab.NewJob(task.DB)
	if err != nil {
		return status.InternalError(err, "can't create job")
	}
	defer cleanUp(j, errCap)

	pics, err := j.FindPics(db.Opts{
		Prefix: tab.PicsPrimary{&task.PicID},
		Lock:   db.LockWrite,
		Limit:  1,
	})
	if err != nil {
		return status.InternalError(err, "can't find pics")
	}
	if len(pics) != 1 {
		return status.NotFound(nil, "can't lookup pic")
	}
	p := pics[0]

	now := time.Now()

	if p.DeletionStatus != nil {
		if p.HardDeleted() {
			return status.InvalidArgument(nil, "pic already hard deleted")
		}
	} else {
		p.DeletionStatus = &schema.Pic_DeletionStatus{}
	}

	p.DeletionStatus.Reason = task.Reason
	p.DeletionStatus.Details = task.Details
	p.DeletionStatus.Temporary = task.Temporary
	if task.PendingDeletionTime != nil {
		p.DeletionStatus.PendingDeletedTs = schema.ToTs(*task.PendingDeletionTime)
	} else {
		p.DeletionStatus.PendingDeletedTs = nil
	}

	if p.DeletionStatus.MarkedDeletedTs == nil {
		p.DeletionStatus.MarkedDeletedTs = schema.ToTs(now)
	}
	p.SetModifiedTime(now)
	if err := j.UpdatePic(p); err != nil {
		return status.InternalError(err, "can't update pic")
	}

	if err := j.Commit(); err != nil {
		return status.InternalError(err, "can't commit job")
	}

	return nil
}
<|endoftext|>