<commit_before>package logberry

import (
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
)

type Component struct {
	UID    uint64
	Parent Context
	Root   *Root
	Label  string

	Class ComponentClass

	mute bool
	highlight bool
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func newcomponent(parent Context, label string, data ...interface{}) *Component {

	var class = COMPONENT
	if data != nil && len(data) > 0 {
		if cc, ok := data[0].(ComponentClass); ok {
			class = cc
			data = data[1:]
		}
	}
	d := DAggregate(data)

	c := &Component{
		UID:    newcontextuid(),
		Parent: parent,
		Label:  label,
		Class:  class,
	}

	if parent != nil {
		c.Root = parent.GetRoot()
		d.Set(c.Root.FieldPrefix+"Parent", parent.GetUID())
	} else {
		c.Root = Std
	}

	if c.Class < 0 || c.Class >= componentclasssentinel {
		c.Root.InternalError(NewError("ComponentClass out of range", c.UID, c.Class))
		d.Set(c.Root.FieldPrefix+"Class", c.Class)
	} else {
		d.Set(c.Root.FieldPrefix+"Class", ComponentClassText[c.Class])
	}

	if parent != nil {
		c.Root.ComponentEvent(c, COMPONENT_START, "Instantiate", d)
	}

	return c

}

func (x *Component) Component(label string, data ...interface{}) *Component {
	return newcomponent(x, label, data...)
}

func (x *Component) Task(activity string, data ...interface{}) *Task {
	return newtask(x, activity, data)
}

//----------------------------------------------------------------------
func (x *Component) GetLabel() string {
	return x.Label
}

func (x *Component) GetUID() uint64 {
	return x.UID
}

func (x *Component) GetParent() Context {
	return x.Parent
}

func (x *Component) GetRoot() *Root {
	return x.Root
}

func (x *Component) Mute() *Component {
	x.mute = true
	return x
}
func (x *Component) Unmute() *Component {
	x.mute = false
	return x
}
func (x *Component) IsMute() bool {
	return x.mute
}

func (x *Component) Highlight() *Component {
	x.highlight = true
	return x
}

func (x *Component) ClearHighlight() *Component {
	x.highlight = false
	return x
}

func (x *Component) IsHighlighted() bool {
	return x.highlight
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func (x *Component) Build(build *BuildMetadata) {
	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Build", DBuild(build))
}

func (x *Component) Configuration(data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Configuration", DBuild(data))
}

func (x *Component) CommandLine() {

	hostname, err := os.Hostname()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve hostname"))
		return
	}

	u, err := user.Current()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve user info"))
		return
	}

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve program path"))
		return
	}

	prog := path.Base(os.Args[0])

	d := D{
		"Host":    hostname,
		"User":    u.Username,
		"Path":    dir,
		"Program": prog,
		"Args":    os.Args[1:],
	}

	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Command line", &d)

}

func (x *Component) Environment() {

	d := D{}
	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		d[pair[0]] = pair[1]
	}
	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Environment", &d)

}

func (x *Component) Process() {

	hostname, err := os.Hostname()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve hostname"))
		return
	}

	wd, err := os.Getwd()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve working dir"))
		return
	}

	u, err := user.Current()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve user info"))
		return
	}

	d := D{
		"Host": hostname,
		"WD":   wd,
		"UID":  u.Uid,
		"User": u.Username,
		"PID":  os.Getpid(),
	}

	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Process", &d)

}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func (x *Component) Info(msg string, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_INFO, msg, DAggregate(data))
}

func (x *Component) Recovered(msg string, err error, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_WARNING, msg,
		DAggregate(data).Set(x.Root.FieldPrefix+"Error", err.Error()))
}

func (x *Component) Warning(msg string, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_WARNING, msg, DAggregate(data))
}

func (x *Component) Error(msg string, err error, data ...interface{}) error {

	// Note that this can't/shouldn't just throw err into the data blob
	// because the standard errors package error doesn't expose
	// anything, even the message.  So you basically have to reduce to a
	// string via Error().

	x.Root.ComponentEvent(x, COMPONENT_ERROR, msg,
		DAggregate(data).Set(x.Root.FieldPrefix+"Error", err.Error()))
	return WrapError(err, msg)

}

// Failure is the same as Error but doesn't take an error object.
func (x *Component) Failure(msg string, data ...interface{}) error {

	x.Root.ComponentEvent(x, COMPONENT_ERROR, msg, DAggregate(data))
	return NewError(msg)

}

// Generally only the top level should invoke fatal, not sub-components.
func (x *Component) Fatal(msg string, err error, data ...interface{}) {

	x.Root.ComponentEvent(x, COMPONENT_FATAL, msg,
		DAggregate(data).Set(x.Root.FieldPrefix+"Error", err.Error()))
	os.Exit(1)

}

func (x *Component) Abort(msg string, data ...interface{}) {

	x.Root.ComponentEvent(x, COMPONENT_FATAL, msg, DAggregate(data))
	os.Exit(1)

}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func (x *Component) Finalize(data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_FINISH, "Finalize", DAggregate(data))
}

func (x *Component) Ready(msg string, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_READY, msg, DAggregate(data))
}
<commit_msg>Fix typo.<commit_after>package logberry

import (
	"os"
	"os/user"
	"path"
	"path/filepath"
	"strings"
)

type Component struct {
	UID    uint64
	Parent Context
	Root   *Root
	Label  string

	Class ComponentClass

	mute bool
	highlight bool
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func newcomponent(parent Context, label string, data ...interface{}) *Component {

	var class = COMPONENT
	if data != nil && len(data) > 0 {
		if cc, ok := data[0].(ComponentClass); ok {
			class = cc
			data = data[1:]
		}
	}
	d := DAggregate(data)

	c := &Component{
		UID:    newcontextuid(),
		Parent: parent,
		Label:  label,
		Class:  class,
	}

	if parent != nil {
		c.Root = parent.GetRoot()
		d.Set(c.Root.FieldPrefix+"Parent", parent.GetUID())
	} else {
		c.Root = Std
	}

	if c.Class < 0 || c.Class >= componentclass_sentinel {
		c.Root.InternalError(NewError("ComponentClass out of range", c.UID, c.Class))
		d.Set(c.Root.FieldPrefix+"Class", c.Class)
	} else {
		d.Set(c.Root.FieldPrefix+"Class", ComponentClassText[c.Class])
	}

	if parent != nil {
		c.Root.ComponentEvent(c, COMPONENT_START, "Instantiate", d)
	}

	return c

}

func (x *Component) Component(label string, data ...interface{}) *Component {
	return newcomponent(x, label, data...)
}

func (x *Component) Task(activity string, data ...interface{}) *Task {
	return newtask(x, activity, data)
}

//----------------------------------------------------------------------
func (x *Component) GetLabel() string {
	return x.Label
}

func (x *Component) GetUID() uint64 {
	return x.UID
}

func (x *Component) GetParent() Context {
	return x.Parent
}

func (x *Component) GetRoot() *Root {
	return x.Root
}

func (x *Component) Mute() *Component {
	x.mute = true
	return x
}
func (x *Component) Unmute() *Component {
	x.mute = false
	return x
}
func (x *Component) IsMute() bool {
	return x.mute
}

func (x *Component) Highlight() *Component {
	x.highlight = true
	return x
}

func (x *Component) ClearHighlight() *Component {
	x.highlight = false
	return x
}

func (x *Component) IsHighlighted() bool {
	return x.highlight
}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func (x *Component) Build(build *BuildMetadata) {
	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Build", DBuild(build))
}

func (x *Component) Configuration(data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Configuration", DBuild(data))
}

func (x *Component) CommandLine() {

	hostname, err := os.Hostname()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve hostname"))
		return
	}

	u, err := user.Current()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve user info"))
		return
	}

	dir, err := filepath.Abs(filepath.Dir(os.Args[0]))
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve program path"))
		return
	}

	prog := path.Base(os.Args[0])

	d := D{
		"Host":    hostname,
		"User":    u.Username,
		"Path":    dir,
		"Program": prog,
		"Args":    os.Args[1:],
	}

	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Command line", &d)

}

func (x *Component) Environment() {

	d := D{}
	for _, e := range os.Environ() {
		pair := strings.Split(e, "=")
		d[pair[0]] = pair[1]
	}
	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Environment", &d)

}

func (x *Component) Process() {

	hostname, err := os.Hostname()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve hostname"))
		return
	}

	wd, err := os.Getwd()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve working dir"))
		return
	}

	u, err := user.Current()
	if err != nil {
		x.Root.InternalError(WrapError(err, "Could not retrieve user info"))
		return
	}

	d := D{
		"Host": hostname,
		"WD":   wd,
		"UID":  u.Uid,
		"User": u.Username,
		"PID":  os.Getpid(),
	}

	x.Root.ComponentEvent(x, COMPONENT_CONFIGURATION, "Process", &d)

}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func (x *Component) Info(msg string, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_INFO, msg, DAggregate(data))
}

func (x *Component) Recovered(msg string, err error, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_WARNING, msg,
		DAggregate(data).Set(x.Root.FieldPrefix+"Error", err.Error()))
}

func (x *Component) Warning(msg string, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_WARNING, msg, DAggregate(data))
}

func (x *Component) Error(msg string, err error, data ...interface{}) error {

	// Note that this can't/shouldn't just throw err into the data blob
	// because the standard errors package error doesn't expose
	// anything, even the message.  So you basically have to reduce to a
	// string via Error().

	x.Root.ComponentEvent(x, COMPONENT_ERROR, msg,
		DAggregate(data).Set(x.Root.FieldPrefix+"Error", err.Error()))
	return WrapError(err, msg)

}

// Failure is the same as Error but doesn't take an error object.
func (x *Component) Failure(msg string, data ...interface{}) error {

	x.Root.ComponentEvent(x, COMPONENT_ERROR, msg, DAggregate(data))
	return NewError(msg)

}

// Generally only the top level should invoke fatal, not sub-components.
func (x *Component) Fatal(msg string, err error, data ...interface{}) {

	x.Root.ComponentEvent(x, COMPONENT_FATAL, msg,
		DAggregate(data).Set(x.Root.FieldPrefix+"Error", err.Error()))
	os.Exit(1)

}

func (x *Component) Abort(msg string, data ...interface{}) {

	x.Root.ComponentEvent(x, COMPONENT_FATAL, msg, DAggregate(data))
	os.Exit(1)

}

//----------------------------------------------------------------------
//----------------------------------------------------------------------
func (x *Component) Finalize(data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_FINISH, "Finalize", DAggregate(data))
}

func (x *Component) Ready(msg string, data ...interface{}) {
	x.Root.ComponentEvent(x, COMPONENT_READY, msg, DAggregate(data))
}
<|endoftext|>


<commit_before>/*
Copyright 2015 Google Inc. All rights reserved.

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 e2e

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

	. "github.com/onsi/ginkgo"
)

var (
	root = absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), "..")))
)

var _ = Describe("Shell", func() {

	defer GinkgoRecover()
	// Slurp up all the tests in hack/e2e-suite
	bashE2ERoot := filepath.Join(root, "hack/e2e-suite")
	files, err := ioutil.ReadDir(bashE2ERoot)
	if err != nil {
		Fail(fmt.Sprintf("Error reading test suites from %v %v", bashE2ERoot, err.Error()))
	}

	for _, file := range files {
		fileName := file.Name() // Make a copy
		It(fmt.Sprintf("tests that %v passes", fileName), func() {
			runCmdTest(filepath.Join(bashE2ERoot, fileName))
		})
	}
})

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

// Runs the given cmd test.
func runCmdTest(path string) {
	By(fmt.Sprintf("Running %v", path))
	cmd := exec.Command(path)
	cmd.Stdout = bytes.NewBuffer(nil)
	cmd.Stderr = cmd.Stdout

	if err := cmd.Run(); err != nil {
		Fail(fmt.Sprintf("Error running %v:\nCommand output:\n%v\n", cmd, cmd.Stdout))
		return
	}
	return
}
<commit_msg>Disable shell test for non-gce|gke providers<commit_after>/*
Copyright 2015 Google Inc. All rights reserved.

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 e2e

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

	. "github.com/onsi/ginkgo"
)

var (
	root = absOrDie(filepath.Clean(filepath.Join(path.Base(os.Args[0]), "..")))
)

var _ = Describe("Shell", func() {

	defer GinkgoRecover()

	// A number of scripts only work on gce
	if testContext.Provider != "gce" && testContext.Provider != "gke" {
		By(fmt.Sprintf("Skipping Shell test, which is only supported for provider gce and gke (not %s)",
			testContext.Provider))
		return
	}

	// Slurp up all the tests in hack/e2e-suite
	bashE2ERoot := filepath.Join(root, "hack/e2e-suite")
	files, err := ioutil.ReadDir(bashE2ERoot)
	if err != nil {
		Fail(fmt.Sprintf("Error reading test suites from %v %v", bashE2ERoot, err.Error()))
	}

	for _, file := range files {
		fileName := file.Name() // Make a copy
		It(fmt.Sprintf("tests that %v passes", fileName), func() {
			runCmdTest(filepath.Join(bashE2ERoot, fileName))
		})
	}
})

func absOrDie(path string) string {
	out, err := filepath.Abs(path)
	if err != nil {
		panic(err)
	}
	return out
}

// Runs the given cmd test.
func runCmdTest(path string) {
	By(fmt.Sprintf("Running %v", path))
	cmd := exec.Command(path)
	cmd.Stdout = bytes.NewBuffer(nil)
	cmd.Stderr = cmd.Stdout

	if err := cmd.Run(); err != nil {
		Fail(fmt.Sprintf("Error running %v:\nCommand output:\n%v\n", cmd, cmd.Stdout))
		return
	}
	return
}
<|endoftext|>


<commit_before>package controller

import (
	"fmt"

	"github.com/golang/glog"
	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch"

	routeapi "github.com/openshift/origin/pkg/route/api"
	"github.com/openshift/origin/pkg/router"
)

// RouteAdmissionFunc determines whether or not to admit a route.
type RouteAdmissionFunc func(*routeapi.Route) error

// RouteMap contains all routes associated with a key
type RouteMap map[string][]*routeapi.Route

// RemoveRoute removes any existing routes that match the given route's namespace and name for a key
func (srm RouteMap) RemoveRoute(key string, route *routeapi.Route) bool {
	k := 0
	removed := false

	m := srm[key]
	for i, v := range m {
		if m[i].Namespace == route.Namespace && m[i].Name == route.Name {
			removed = true
		} else {
			m[k] = v
			k++
		}
	}

	// set the slice length to the final size.
	m = m[:k]

	if len(m) > 0 {
		srm[key] = m
	} else {
		delete(srm, key)
	}

	return removed
}

func (srm RouteMap) InsertRoute(key string, route *routeapi.Route) {
	// To replace any existing route[s], first we remove all old entries.
	srm.RemoveRoute(key, route)

	m := srm[key]
	for idx := range m {
		if routeapi.RouteLessThan(route, m[idx]) {
			m = append(m, &routeapi.Route{})
			// From: https://github.com/golang/go/wiki/SliceTricks
			copy(m[idx+1:], m[idx:])
			m[idx] = route
			srm[key] = m

			// Ensure we return from here as we change the iterator.
			return
		}
	}

	// Newest route or empty slice, add to the end.
	srm[key] = append(m, route)
}

// HostAdmitter implements the router.Plugin interface to add admission
// control checks for routes in template based, backend-agnostic routers.
type HostAdmitter struct {
	// plugin is the next plugin in the chain.
	plugin router.Plugin

	// admitter is a route admission function used to determine whether
	// or not to admit routes.
	admitter RouteAdmissionFunc

	// recorder is an interface for indicating route rejections.
	recorder RejectionRecorder

	// restrictOwnership adds admission checks to restrict ownership
	// (of subdomains) to a single owner/namespace.
	restrictOwnership bool

	claimedHosts     RouteMap
	claimedWildcards RouteMap
	blockedWildcards RouteMap
}

// NewHostAdmitter creates a plugin wrapper that checks whether or not to
// admit routes and relay them to the next plugin in the chain.
// Recorder is an interface for indicating why a route was rejected.
func NewHostAdmitter(plugin router.Plugin, fn RouteAdmissionFunc, restrict bool, recorder RejectionRecorder) *HostAdmitter {
	return &HostAdmitter{
		plugin:   plugin,
		admitter: fn,
		recorder: recorder,

		restrictOwnership: restrict,
		claimedHosts:      RouteMap{},
		claimedWildcards:  RouteMap{},
		blockedWildcards:  RouteMap{},
	}
}

// HandleNode processes watch events on the Node resource.
func (p *HostAdmitter) HandleNode(eventType watch.EventType, node *kapi.Node) error {
	return p.plugin.HandleNode(eventType, node)
}

// HandleEndpoints processes watch events on the Endpoints resource.
func (p *HostAdmitter) HandleEndpoints(eventType watch.EventType, endpoints *kapi.Endpoints) error {
	return p.plugin.HandleEndpoints(eventType, endpoints)
}

// HandleRoute processes watch events on the Route resource.
func (p *HostAdmitter) HandleRoute(eventType watch.EventType, route *routeapi.Route) error {
	if err := p.admitter(route); err != nil {
		glog.Errorf("Route %s not admitted: %s", routeNameKey(route), err.Error())
		p.recordRejection(route, "RouteNotAdmitted", err.Error())
		return err
	}

	if p.restrictOwnership && len(route.Spec.Host) > 0 {
		switch eventType {
		case watch.Added, watch.Modified:
			if err := p.addRoute(route); err != nil {
				glog.Errorf("Route %s not admitted: %s", routeNameKey(route), err.Error())
				return err
			}

		case watch.Deleted:
			p.claimedHosts.RemoveRoute(route.Spec.Host, route)
			wildcardKey := routeapi.GetDomainForHost(route.Spec.Host)
			p.claimedWildcards.RemoveRoute(wildcardKey, route)
			p.blockedWildcards.RemoveRoute(wildcardKey, route)
		}
	}

	return p.plugin.HandleRoute(eventType, route)
}

// HandleAllowedNamespaces limits the scope of valid routes to only those that match
// the provided namespace list.
func (p *HostAdmitter) HandleNamespaces(namespaces sets.String) error {
	return p.plugin.HandleNamespaces(namespaces)
}

func (p *HostAdmitter) SetLastSyncProcessed(processed bool) error {
	return p.plugin.SetLastSyncProcessed(processed)
}

// recordRejection records why the route was rejected.
func (p *HostAdmitter) recordRejection(route *routeapi.Route, reason, message string) {
	oldPolicy := route.Spec.WildcardPolicy
	route.Spec.WildcardPolicy = routeapi.WildcardPolicyNone
	p.recorder.RecordRouteRejection(route, reason, message)
	route.Spec.WildcardPolicy = oldPolicy
}

// addRoute admits routes based on subdomain ownership - returns errors if the route is not admitted.
func (p *HostAdmitter) addRoute(route *routeapi.Route) error {
	// Find displaced routes (or error if an existing route displaces us)
	displacedRoutes, err := p.displacedRoutes(route)
	if err != nil {
		msg := fmt.Sprintf("a route in another namespace holds host %s", route.Spec.Host)
		p.recordRejection(route, "HostAlreadyClaimed", msg)
		return fmt.Errorf("%s (%s)", msg, err)
	}

	// Remove displaced routes
	for _, displacedRoute := range displacedRoutes {
		wildcardKey := routeapi.GetDomainForHost(displacedRoute.Spec.Host)
		p.claimedHosts.RemoveRoute(displacedRoute.Spec.Host, displacedRoute)
		p.blockedWildcards.RemoveRoute(wildcardKey, displacedRoute)
		p.claimedWildcards.RemoveRoute(wildcardKey, displacedRoute)

		msg := fmt.Sprintf("a route in another namespace holds host %s", displacedRoute.Spec.Host)
		p.recordRejection(displacedRoute, "HostAlreadyClaimed", msg)
		p.plugin.HandleRoute(watch.Deleted, displacedRoute)
	}

	if len(route.Spec.WildcardPolicy) == 0 {
		route.Spec.WildcardPolicy = routeapi.WildcardPolicyNone
	}

	// Add the new route
	wildcardKey := routeapi.GetDomainForHost(route.Spec.Host)

	switch route.Spec.WildcardPolicy {
	case routeapi.WildcardPolicyNone:
		// claim the host, block wildcards that would conflict with this host
		p.claimedHosts.InsertRoute(route.Spec.Host, route)
		p.blockedWildcards.InsertRoute(wildcardKey, route)
		// ensure the route doesn't exist as a claimed wildcard (in case it previously was)
		p.claimedWildcards.RemoveRoute(wildcardKey, route)

	case routeapi.WildcardPolicySubdomain:
		// claim the wildcard
		p.claimedWildcards.InsertRoute(wildcardKey, route)
		// ensure the route doesn't exist as a claimed host or blocked wildcard
		p.claimedHosts.RemoveRoute(route.Spec.Host, route)
		p.blockedWildcards.RemoveRoute(wildcardKey, route)
	default:
		p.claimedHosts.RemoveRoute(route.Spec.Host, route)
		p.claimedWildcards.RemoveRoute(wildcardKey, route)
		p.blockedWildcards.RemoveRoute(wildcardKey, route)
		err := fmt.Errorf("unsupported wildcard policy %s", route.Spec.WildcardPolicy)
		p.recordRejection(route, "RouteNotAdmitted", err.Error())
		return err
	}

	return nil
}

func (p *HostAdmitter) displacedRoutes(newRoute *routeapi.Route) ([]*routeapi.Route, error) {
	displaced := []*routeapi.Route{}

	// See if any existing routes block our host, or if we displace their host
	for i, route := range p.claimedHosts[newRoute.Spec.Host] {
		if route.Namespace == newRoute.Namespace {
			// Never displace a route in our namespace
			continue
		}
		if routeapi.RouteLessThan(route, newRoute) {
			return nil, fmt.Errorf("route %s/%s has host %s", route.Namespace, route.Name, route.Spec.Host)
		}
		displaced = append(displaced, p.claimedHosts[newRoute.Spec.Host][i])
	}

	wildcardKey := routeapi.GetDomainForHost(newRoute.Spec.Host)

	// See if any existing wildcard routes block our domain, or if we displace them
	for i, route := range p.claimedWildcards[wildcardKey] {
		if route.Namespace == newRoute.Namespace {
			// Never displace a route in our namespace
			continue
		}
		if routeapi.RouteLessThan(route, newRoute) {
			return nil, fmt.Errorf("wildcard route %s/%s has host *.%s, blocking %s", route.Namespace, route.Name, wildcardKey, newRoute.Spec.Host)
		}
		displaced = append(displaced, p.claimedWildcards[wildcardKey][i])
	}

	// If this is a wildcard route, see if any specific hosts block our wildcardSpec, or if we displace them
	if newRoute.Spec.WildcardPolicy == routeapi.WildcardPolicySubdomain {
		for i, route := range p.blockedWildcards[wildcardKey] {
			if route.Namespace == newRoute.Namespace {
				// Never displace a route in our namespace
				continue
			}
			if routeapi.RouteLessThan(route, newRoute) {
				return nil, fmt.Errorf("route %s/%s has host %s, blocking *.%s", route.Namespace, route.Name, route.Spec.Host, wildcardKey)
			}
			displaced = append(displaced, p.blockedWildcards[wildcardKey][i])
		}
	}

	return displaced, nil
}
<commit_msg>Fix as per @liggitt review comments - write wildcard spec as is in the rejection.<commit_after>package controller

import (
	"fmt"

	"github.com/golang/glog"
	kapi "k8s.io/kubernetes/pkg/api"
	"k8s.io/kubernetes/pkg/util/sets"
	"k8s.io/kubernetes/pkg/watch"

	routeapi "github.com/openshift/origin/pkg/route/api"
	"github.com/openshift/origin/pkg/router"
)

// RouteAdmissionFunc determines whether or not to admit a route.
type RouteAdmissionFunc func(*routeapi.Route) error

// RouteMap contains all routes associated with a key
type RouteMap map[string][]*routeapi.Route

// RemoveRoute removes any existing routes that match the given route's namespace and name for a key
func (srm RouteMap) RemoveRoute(key string, route *routeapi.Route) bool {
	k := 0
	removed := false

	m := srm[key]
	for i, v := range m {
		if m[i].Namespace == route.Namespace && m[i].Name == route.Name {
			removed = true
		} else {
			m[k] = v
			k++
		}
	}

	// set the slice length to the final size.
	m = m[:k]

	if len(m) > 0 {
		srm[key] = m
	} else {
		delete(srm, key)
	}

	return removed
}

func (srm RouteMap) InsertRoute(key string, route *routeapi.Route) {
	// To replace any existing route[s], first we remove all old entries.
	srm.RemoveRoute(key, route)

	m := srm[key]
	for idx := range m {
		if routeapi.RouteLessThan(route, m[idx]) {
			m = append(m, &routeapi.Route{})
			// From: https://github.com/golang/go/wiki/SliceTricks
			copy(m[idx+1:], m[idx:])
			m[idx] = route
			srm[key] = m

			// Ensure we return from here as we change the iterator.
			return
		}
	}

	// Newest route or empty slice, add to the end.
	srm[key] = append(m, route)
}

// HostAdmitter implements the router.Plugin interface to add admission
// control checks for routes in template based, backend-agnostic routers.
type HostAdmitter struct {
	// plugin is the next plugin in the chain.
	plugin router.Plugin

	// admitter is a route admission function used to determine whether
	// or not to admit routes.
	admitter RouteAdmissionFunc

	// recorder is an interface for indicating route rejections.
	recorder RejectionRecorder

	// restrictOwnership adds admission checks to restrict ownership
	// (of subdomains) to a single owner/namespace.
	restrictOwnership bool

	claimedHosts     RouteMap
	claimedWildcards RouteMap
	blockedWildcards RouteMap
}

// NewHostAdmitter creates a plugin wrapper that checks whether or not to
// admit routes and relay them to the next plugin in the chain.
// Recorder is an interface for indicating why a route was rejected.
func NewHostAdmitter(plugin router.Plugin, fn RouteAdmissionFunc, restrict bool, recorder RejectionRecorder) *HostAdmitter {
	return &HostAdmitter{
		plugin:   plugin,
		admitter: fn,
		recorder: recorder,

		restrictOwnership: restrict,
		claimedHosts:      RouteMap{},
		claimedWildcards:  RouteMap{},
		blockedWildcards:  RouteMap{},
	}
}

// HandleNode processes watch events on the Node resource.
func (p *HostAdmitter) HandleNode(eventType watch.EventType, node *kapi.Node) error {
	return p.plugin.HandleNode(eventType, node)
}

// HandleEndpoints processes watch events on the Endpoints resource.
func (p *HostAdmitter) HandleEndpoints(eventType watch.EventType, endpoints *kapi.Endpoints) error {
	return p.plugin.HandleEndpoints(eventType, endpoints)
}

// HandleRoute processes watch events on the Route resource.
func (p *HostAdmitter) HandleRoute(eventType watch.EventType, route *routeapi.Route) error {
	if err := p.admitter(route); err != nil {
		glog.Errorf("Route %s not admitted: %s", routeNameKey(route), err.Error())
		p.recorder.RecordRouteRejection(route, "RouteNotAdmitted", err.Error())
		return err
	}

	if p.restrictOwnership && len(route.Spec.Host) > 0 {
		switch eventType {
		case watch.Added, watch.Modified:
			if err := p.addRoute(route); err != nil {
				glog.Errorf("Route %s not admitted: %s", routeNameKey(route), err.Error())
				return err
			}

		case watch.Deleted:
			p.claimedHosts.RemoveRoute(route.Spec.Host, route)
			wildcardKey := routeapi.GetDomainForHost(route.Spec.Host)
			p.claimedWildcards.RemoveRoute(wildcardKey, route)
			p.blockedWildcards.RemoveRoute(wildcardKey, route)
		}
	}

	return p.plugin.HandleRoute(eventType, route)
}

// HandleAllowedNamespaces limits the scope of valid routes to only those that match
// the provided namespace list.
func (p *HostAdmitter) HandleNamespaces(namespaces sets.String) error {
	return p.plugin.HandleNamespaces(namespaces)
}

func (p *HostAdmitter) SetLastSyncProcessed(processed bool) error {
	return p.plugin.SetLastSyncProcessed(processed)
}

// addRoute admits routes based on subdomain ownership - returns errors if the route is not admitted.
func (p *HostAdmitter) addRoute(route *routeapi.Route) error {
	// Find displaced routes (or error if an existing route displaces us)
	displacedRoutes, err := p.displacedRoutes(route)
	if err != nil {
		msg := fmt.Sprintf("a route in another namespace holds host %s", route.Spec.Host)
		p.recorder.RecordRouteRejection(route, "HostAlreadyClaimed", msg)
		return fmt.Errorf("%s (%s)", msg, err)
	}

	// Remove displaced routes
	for _, displacedRoute := range displacedRoutes {
		wildcardKey := routeapi.GetDomainForHost(displacedRoute.Spec.Host)
		p.claimedHosts.RemoveRoute(displacedRoute.Spec.Host, displacedRoute)
		p.blockedWildcards.RemoveRoute(wildcardKey, displacedRoute)
		p.claimedWildcards.RemoveRoute(wildcardKey, displacedRoute)

		msg := fmt.Sprintf("a route in another namespace holds host %s", displacedRoute.Spec.Host)
		p.recorder.RecordRouteRejection(displacedRoute, "HostAlreadyClaimed", msg)
		p.plugin.HandleRoute(watch.Deleted, displacedRoute)
	}

	if len(route.Spec.WildcardPolicy) == 0 {
		route.Spec.WildcardPolicy = routeapi.WildcardPolicyNone
	}

	// Add the new route
	wildcardKey := routeapi.GetDomainForHost(route.Spec.Host)

	switch route.Spec.WildcardPolicy {
	case routeapi.WildcardPolicyNone:
		// claim the host, block wildcards that would conflict with this host
		p.claimedHosts.InsertRoute(route.Spec.Host, route)
		p.blockedWildcards.InsertRoute(wildcardKey, route)
		// ensure the route doesn't exist as a claimed wildcard (in case it previously was)
		p.claimedWildcards.RemoveRoute(wildcardKey, route)

	case routeapi.WildcardPolicySubdomain:
		// claim the wildcard
		p.claimedWildcards.InsertRoute(wildcardKey, route)
		// ensure the route doesn't exist as a claimed host or blocked wildcard
		p.claimedHosts.RemoveRoute(route.Spec.Host, route)
		p.blockedWildcards.RemoveRoute(wildcardKey, route)
	default:
		p.claimedHosts.RemoveRoute(route.Spec.Host, route)
		p.claimedWildcards.RemoveRoute(wildcardKey, route)
		p.blockedWildcards.RemoveRoute(wildcardKey, route)
		err := fmt.Errorf("unsupported wildcard policy %s", route.Spec.WildcardPolicy)
		p.recorder.RecordRouteRejection(route, "RouteNotAdmitted", err.Error())
		return err
	}

	return nil
}

func (p *HostAdmitter) displacedRoutes(newRoute *routeapi.Route) ([]*routeapi.Route, error) {
	displaced := []*routeapi.Route{}

	// See if any existing routes block our host, or if we displace their host
	for i, route := range p.claimedHosts[newRoute.Spec.Host] {
		if route.Namespace == newRoute.Namespace {
			// Never displace a route in our namespace
			continue
		}
		if routeapi.RouteLessThan(route, newRoute) {
			return nil, fmt.Errorf("route %s/%s has host %s", route.Namespace, route.Name, route.Spec.Host)
		}
		displaced = append(displaced, p.claimedHosts[newRoute.Spec.Host][i])
	}

	wildcardKey := routeapi.GetDomainForHost(newRoute.Spec.Host)

	// See if any existing wildcard routes block our domain, or if we displace them
	for i, route := range p.claimedWildcards[wildcardKey] {
		if route.Namespace == newRoute.Namespace {
			// Never displace a route in our namespace
			continue
		}
		if routeapi.RouteLessThan(route, newRoute) {
			return nil, fmt.Errorf("wildcard route %s/%s has host *.%s, blocking %s", route.Namespace, route.Name, wildcardKey, newRoute.Spec.Host)
		}
		displaced = append(displaced, p.claimedWildcards[wildcardKey][i])
	}

	// If this is a wildcard route, see if any specific hosts block our wildcardSpec, or if we displace them
	if newRoute.Spec.WildcardPolicy == routeapi.WildcardPolicySubdomain {
		for i, route := range p.blockedWildcards[wildcardKey] {
			if route.Namespace == newRoute.Namespace {
				// Never displace a route in our namespace
				continue
			}
			if routeapi.RouteLessThan(route, newRoute) {
				return nil, fmt.Errorf("route %s/%s has host %s, blocking *.%s", route.Namespace, route.Name, route.Spec.Host, wildcardKey)
			}
			displaced = append(displaced, p.blockedWildcards[wildcardKey][i])
		}
	}

	return displaced, nil
}
<|endoftext|>


<commit_before>package cache

import (
	"hash/crc32"
	"sync"
)

// A cache that is comprised other caches in a ring.
//
// Each cache is protected with a Mutex that is locked for write
// operations.
//
// Keys are hashed into the ring and added to their respective
// caches.
//
type ConcurrentRingCache struct {
	SizeLimit int
	RingSize  int
	Caches    []*LIFOCache
	Locks     []sync.RWMutex

	// A function that hashes a string into one of the caches in the Caches array.
	// The ringSize is the length of the Cache and Locks arrays.
	KeyHash func(key string, ringSize int) int
}

func NewConcurrentRingCache(ringSize int, cacheSize int) *ConcurrentRingCache {
	c := ConcurrentRingCache{}

	c.RingSize = ringSize
	c.SizeLimit = cacheSize
	c.Caches = make([]*LIFOCache, ringSize)
	c.Locks = make([]sync.RWMutex, ringSize)
	c.KeyHash = func(s string, ringSize int) int {
		h := int(crc32.ChecksumIEEE([]byte(s)))

		if h < 0 {
			h = -1
		}

		h = h % ringSize

		return h
	}

	for i := 0; i < ringSize; i++ {
		c.Caches[i] = NewLIFOCache()
		c.Locks[i] = sync.RWMutex{}
	}

	return &c
}

// Add an item and enforce the cache size limit.
func (c *ConcurrentRingCache) Put(key string, item interface{}) {
	h := c.KeyHash(key, c.RingSize)

	c.Locks[h].Lock()
	c.Caches[h].Put(key, item)
	c.Locks[h].Unlock()
}

func (c *ConcurrentRingCache) PutWithHandler(key string, item interface{}, evictionHandler func(string, interface{})) {
	h := c.KeyHash(key, c.RingSize)

	c.Locks[h].Lock()
	c.Caches[h].PutWithHandler(key, item, evictionHandler)
	c.Locks[h].Unlock()
}

func (c *ConcurrentRingCache) Get(key string) (interface{}, bool) {
	h := c.KeyHash(key, c.RingSize)

	c.Locks[h].RLock()
	defer c.Locks[h].RUnlock()
	return c.Caches[h].Get(key)
}

// Evict items from every sub-cache until they contain the ceiling of 1/N
// items where N is the size limit for this entire cache.
func (c *ConcurrentRingCache) EnforceSizeLimit() {
	limit := c.SizeLimit

	for i := 0; i < c.RingSize; i++ {
		c.Locks[i].Lock()
		for c.Caches[i].Len() > limit {
			c.Caches[i].EvictNext()
		}
		c.Locks[i].Unlock()
	}
}

func (c *ConcurrentRingCache) EvictOrderThan(tm int64) {
	for i := 0; i < c.RingSize; i++ {
		c.Locks[i].Lock()

		c.Caches[i].EvictOlderThan(tm)

		c.Locks[i].Unlock()
	}
}

func (c *ConcurrentRingCache) Size() int {
	size := 0

	for i := 0; i < c.RingSize; i++ {
		c.Locks[i].RLock()
		size += c.Caches[i].Len()
		c.Locks[i].RUnlock()
	}

	return size
}
<commit_msg>pkg/sdsai/cache/concurrentringcache.go: Mutexes should be using pointers.<commit_after>package cache

import (
	"hash/crc32"
	"sync"
)

// A cache that is comprised other caches in a ring.
//
// Each cache is protected with a Mutex that is locked for write
// operations.
//
// Keys are hashed into the ring and added to their respective
// caches.
//
type ConcurrentRingCache struct {
	SizeLimit int
	RingSize  int
	Caches    []*LIFOCache
	Locks     []*sync.RWMutex

	// A function that hashes a string into one of the caches in the Caches array.
	// The ringSize is the length of the Cache and Locks arrays.
	KeyHash func(key string, ringSize int) int
}

func NewConcurrentRingCache(ringSize int, cacheSize int) *ConcurrentRingCache {
	c := ConcurrentRingCache{}

	c.RingSize = ringSize
	c.SizeLimit = cacheSize
	c.Caches = make([]*LIFOCache, ringSize)
	c.Locks = make([]*sync.RWMutex, ringSize)
	c.KeyHash = func(s string, ringSize int) int {
		h := int(crc32.ChecksumIEEE([]byte(s)))

		if h < 0 {
			h = -1
		}

		h = h % ringSize

		return h
	}

	for i := 0; i < ringSize; i++ {
		c.Caches[i] = NewLIFOCache()
		c.Locks[i] = &sync.RWMutex{}
	}

	return &c
}

// Add an item and enforce the cache size limit.
func (c *ConcurrentRingCache) Put(key string, item interface{}) {
	h := c.KeyHash(key, c.RingSize)

	c.Locks[h].Lock()
	c.Caches[h].Put(key, item)
	c.Locks[h].Unlock()
}

func (c *ConcurrentRingCache) PutWithHandler(key string, item interface{}, evictionHandler func(string, interface{})) {
	h := c.KeyHash(key, c.RingSize)

	c.Locks[h].Lock()
	c.Caches[h].PutWithHandler(key, item, evictionHandler)
	c.Locks[h].Unlock()
}

func (c *ConcurrentRingCache) Get(key string) (interface{}, bool) {
	h := c.KeyHash(key, c.RingSize)

	c.Locks[h].RLock()
	defer c.Locks[h].RUnlock()
	return c.Caches[h].Get(key)
}

// Evict items from every sub-cache until they contain the ceiling of 1/N
// items where N is the size limit for this entire cache.
func (c *ConcurrentRingCache) EnforceSizeLimit() {
	limit := c.SizeLimit

	for i := 0; i < c.RingSize; i++ {
		c.Locks[i].Lock()
		for c.Caches[i].Len() > limit {
			c.Caches[i].EvictNext()
		}
		c.Locks[i].Unlock()
	}
}

func (c *ConcurrentRingCache) EvictOrderThan(tm int64) {
	for i := 0; i < c.RingSize; i++ {
		c.Locks[i].Lock()

		c.Caches[i].EvictOlderThan(tm)

		c.Locks[i].Unlock()
	}
}

func (c *ConcurrentRingCache) Size() int {
	size := 0

	for i := 0; i < c.RingSize; i++ {
		c.Locks[i].RLock()
		size += c.Caches[i].Len()
		c.Locks[i].RUnlock()
	}

	return size
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, 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 main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"net/http"
	"net/url"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/rafthttp"
	"github.com/coreos/etcd/wal"
	"github.com/coreos/etcd/wal/walpb"
)

// A key-value stream backed by raft
type raftNode struct {
	proposeC    <-chan string            // proposed messages (k,v)
	confChangeC <-chan raftpb.ConfChange // proposed cluster config changes
	commitC     chan *string             // entries committed to log (k,v)
	errorC      chan error               // errors from raft session

	id     int      // client ID for raft session
	peers  []string // raft peer URLs
	join   bool     // node is joining an existing cluster
	waldir string   // path to WAL directory

	// raft backing for the commit/error channel
	node        raft.Node
	raftStorage *raft.MemoryStorage
	wal         *wal.WAL
	transport   *rafthttp.Transport
	stopc       chan struct{} // signals proposal channel closed
	httpstopc   chan struct{} // signals http server to shutdown
	httpdonec   chan struct{} // signals http server shutdown complete
}

// newRaftNode initiates a raft instance and returns a committed log entry
// channel and error channel. Proposals for log updates are sent over the
// provided the proposal channel. All log entries are replayed over the
// commit channel, followed by a nil message (to indicate the channel is
// current), then new log entries. To shutdown, close proposeC and read errorC.
func newRaftNode(id int, peers []string, join bool, proposeC <-chan string,
	confChangeC <-chan raftpb.ConfChange) (<-chan *string, <-chan error) {

	rc := &raftNode{
		proposeC:    proposeC,
		confChangeC: confChangeC,
		commitC:     make(chan *string),
		errorC:      make(chan error),
		id:          id,
		peers:       peers,
		join:        join,
		waldir:      fmt.Sprintf("raftexample-%d", id),
		raftStorage: raft.NewMemoryStorage(),
		stopc:       make(chan struct{}),
		httpstopc:   make(chan struct{}),
		httpdonec:   make(chan struct{}),
		// rest of structure populated after WAL replay
	}
	go rc.startRaft()
	return rc.commitC, rc.errorC
}

// publishEntries writes committed log entries to commit channel and returns
// whether all entries could be published.
func (rc *raftNode) publishEntries(ents []raftpb.Entry) bool {
	for i := range ents {
		switch ents[i].Type {
		case raftpb.EntryNormal:
			if len(ents[i].Data) == 0 {
				// ignore conf changes and empty messages
				continue
			}
			s := string(ents[i].Data)
			select {
			case rc.commitC <- &s:
			case <-rc.stopc:
				return false
			}

		case raftpb.EntryConfChange:
			var cc raftpb.ConfChange
			cc.Unmarshal(ents[i].Data)

			rc.node.ApplyConfChange(cc)
			switch cc.Type {
			case raftpb.ConfChangeAddNode:
				if len(cc.Context) > 0 {
					rc.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
				}
			case raftpb.ConfChangeRemoveNode:
				if cc.NodeID == uint64(rc.id) {
					log.Println("I've been removed from the cluster! Shutting down.")
					return false
				}
				rc.transport.RemovePeer(types.ID(cc.NodeID))
			}
		}
	}
	return true
}

// openWAL returns a WAL ready for reading.
func (rc *raftNode) openWAL() *wal.WAL {
	if wal.Exist(rc.waldir) == false {
		if err := os.Mkdir(rc.waldir, 0750); err != nil {
			log.Fatalf("raftexample: cannot create dir for wal (%v)", err)
		}

		w, err := wal.Create(rc.waldir, nil)
		if err != nil {
			log.Fatalf("raftexample: create wal error (%v)", err)
		}
		w.Close()
	}

	w, err := wal.Open(rc.waldir, walpb.Snapshot{})
	if err != nil {
		log.Fatalf("raftexample: error loading wal (%v)", err)
	}

	return w
}

// replayWAL replays WAL entries into the raft instance and the commit
// channel and returns an appendable WAL.
func (rc *raftNode) replayWAL() *wal.WAL {
	w := rc.openWAL()
	_, _, ents, err := w.ReadAll()
	if err != nil {
		log.Fatalf("raftexample: failed to read WAL (%v)", err)
	}
	// append to storage so raft starts at the right place in log
	rc.raftStorage.Append(ents)
	rc.publishEntries(ents)
	// send nil value so client knows commit channel is current
	rc.commitC <- nil
	return w
}

func (rc *raftNode) writeError(err error) {
	rc.stopHTTP()
	close(rc.commitC)
	rc.errorC <- err
	close(rc.errorC)
	rc.node.Stop()
}

func (rc *raftNode) startRaft() {
	oldwal := wal.Exist(rc.waldir)
	rc.wal = rc.replayWAL()

	rpeers := make([]raft.Peer, len(rc.peers))
	for i := range rpeers {
		rpeers[i] = raft.Peer{ID: uint64(i + 1)}
	}
	c := &raft.Config{
		ID:              uint64(rc.id),
		ElectionTick:    10,
		HeartbeatTick:   1,
		Storage:         rc.raftStorage,
		MaxSizePerMsg:   1024 * 1024,
		MaxInflightMsgs: 256,
	}

	if oldwal {
		rc.node = raft.RestartNode(c)
	} else {
		startPeers := rpeers
		if rc.join {
			startPeers = nil
		}
		rc.node = raft.StartNode(c, startPeers)
	}

	ss := &stats.ServerStats{}
	ss.Initialize()

	rc.transport = &rafthttp.Transport{
		ID:          types.ID(rc.id),
		ClusterID:   0x1000,
		Raft:        rc,
		ServerStats: ss,
		LeaderStats: stats.NewLeaderStats(strconv.Itoa(rc.id)),
		ErrorC:      make(chan error),
	}

	rc.transport.Start()
	for i := range rc.peers {
		if i+1 != rc.id {
			rc.transport.AddPeer(types.ID(i+1), []string{rc.peers[i]})
		}
	}

	go rc.serveRaft()
	go rc.serveChannels()
}

// stop closes http, closes all channels, and stops raft.
func (rc *raftNode) stop() {
	rc.stopHTTP()
	close(rc.commitC)
	close(rc.errorC)
	rc.node.Stop()

	os.Exit(0)
}

func (rc *raftNode) stopHTTP() {
	rc.transport.Stop()
	close(rc.httpstopc)
	<-rc.httpdonec
}

func (rc *raftNode) serveChannels() {
	defer rc.wal.Close()

	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()

	// send proposals over raft
	go func() {
		var confChangeCount uint64 = 0

		for rc.proposeC != nil && rc.confChangeC != nil {
			select {
			case prop, ok := <-rc.proposeC:
				if !ok {
					rc.proposeC = nil
				} else {
					// blocks until accepted by raft state machine
					rc.node.Propose(context.TODO(), []byte(prop))
				}

			case cc, ok := <-rc.confChangeC:
				if !ok {
					rc.confChangeC = nil
				} else {
					confChangeCount += 1
					cc.ID = confChangeCount
					rc.node.ProposeConfChange(context.TODO(), cc)
				}
			}
		}
		// client closed channel; shutdown raft if not already
		close(rc.stopc)
	}()

	// event loop on raft state machine updates
	for {
		select {
		case <-ticker.C:
			rc.node.Tick()

		// store raft entries to wal, then publish over commit channel
		case rd := <-rc.node.Ready():
			rc.wal.Save(rd.HardState, rd.Entries)
			rc.raftStorage.Append(rd.Entries)
			rc.transport.Send(rd.Messages)
			if ok := rc.publishEntries(rd.CommittedEntries); !ok {
				rc.stop()
				return
			}
			rc.node.Advance()

		case err := <-rc.transport.ErrorC:
			rc.writeError(err)
			return

		case <-rc.stopc:
			rc.stop()
			return
		}
	}
}

func (rc *raftNode) serveRaft() {
	url, err := url.Parse(rc.peers[rc.id-1])
	if err != nil {
		log.Fatalf("raftexample: Failed parsing URL (%v)", err)
	}

	ln, err := newStoppableListener(url.Host, rc.httpstopc)
	if err != nil {
		log.Fatalf("raftexample: Failed to listen rafthttp (%v)", err)
	}

	err = (&http.Server{Handler: rc.transport.Handler()}).Serve(ln)
	select {
	case <-rc.httpstopc:
	default:
		log.Fatalf("raftexample: Failed to serve rafthttp (%v)", err)
	}
	close(rc.httpdonec)
}

func (rc *raftNode) Process(ctx context.Context, m raftpb.Message) error {
	return rc.node.Step(ctx, m)
}
func (rc *raftNode) IsIDRemoved(id uint64) bool                           { return false }
func (rc *raftNode) ReportUnreachable(id uint64)                          {}
func (rc *raftNode) ReportSnapshot(id uint64, status raft.SnapshotStatus) {}
<commit_msg>contrib/raftexample: fix restart path<commit_after>// Copyright 2015 CoreOS, 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 main

import (
	"fmt"
	"log"
	"os"
	"strconv"
	"time"

	"net/http"
	"net/url"

	"github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
	"github.com/coreos/etcd/etcdserver/stats"
	"github.com/coreos/etcd/pkg/types"
	"github.com/coreos/etcd/raft"
	"github.com/coreos/etcd/raft/raftpb"
	"github.com/coreos/etcd/rafthttp"
	"github.com/coreos/etcd/wal"
	"github.com/coreos/etcd/wal/walpb"
)

// A key-value stream backed by raft
type raftNode struct {
	proposeC    <-chan string            // proposed messages (k,v)
	confChangeC <-chan raftpb.ConfChange // proposed cluster config changes
	commitC     chan *string             // entries committed to log (k,v)
	errorC      chan error               // errors from raft session

	id        int      // client ID for raft session
	peers     []string // raft peer URLs
	join      bool     // node is joining an existing cluster
	waldir    string   // path to WAL directory
	lastIndex uint64   // index of log at start

	// raft backing for the commit/error channel
	node        raft.Node
	raftStorage *raft.MemoryStorage
	wal         *wal.WAL
	transport   *rafthttp.Transport
	stopc       chan struct{} // signals proposal channel closed
	httpstopc   chan struct{} // signals http server to shutdown
	httpdonec   chan struct{} // signals http server shutdown complete
}

// newRaftNode initiates a raft instance and returns a committed log entry
// channel and error channel. Proposals for log updates are sent over the
// provided the proposal channel. All log entries are replayed over the
// commit channel, followed by a nil message (to indicate the channel is
// current), then new log entries. To shutdown, close proposeC and read errorC.
func newRaftNode(id int, peers []string, join bool, proposeC <-chan string,
	confChangeC <-chan raftpb.ConfChange) (<-chan *string, <-chan error) {

	rc := &raftNode{
		proposeC:    proposeC,
		confChangeC: confChangeC,
		commitC:     make(chan *string),
		errorC:      make(chan error),
		id:          id,
		peers:       peers,
		join:        join,
		waldir:      fmt.Sprintf("raftexample-%d", id),
		raftStorage: raft.NewMemoryStorage(),
		stopc:       make(chan struct{}),
		httpstopc:   make(chan struct{}),
		httpdonec:   make(chan struct{}),
		// rest of structure populated after WAL replay
	}
	go rc.startRaft()
	return rc.commitC, rc.errorC
}

// publishEntries writes committed log entries to commit channel and returns
// whether all entries could be published.
func (rc *raftNode) publishEntries(ents []raftpb.Entry) bool {
	for i := range ents {
		switch ents[i].Type {
		case raftpb.EntryNormal:
			if len(ents[i].Data) == 0 {
				// ignore empty messages
				break
			}
			s := string(ents[i].Data)
			select {
			case rc.commitC <- &s:
			case <-rc.stopc:
				return false
			}

		case raftpb.EntryConfChange:
			var cc raftpb.ConfChange
			cc.Unmarshal(ents[i].Data)
			rc.node.ApplyConfChange(cc)
			switch cc.Type {
			case raftpb.ConfChangeAddNode:
				if len(cc.Context) > 0 {
					rc.transport.AddPeer(types.ID(cc.NodeID), []string{string(cc.Context)})
				}
			case raftpb.ConfChangeRemoveNode:
				if cc.NodeID == uint64(rc.id) {
					log.Println("I've been removed from the cluster! Shutting down.")
					return false
				}
				rc.transport.RemovePeer(types.ID(cc.NodeID))
			}
		}

		// special nil commit to signal replay has finished
		if ents[i].Index == rc.lastIndex {
			select {
			case rc.commitC <- nil:
			case <-rc.stopc:
				return false
			}
		}
	}
	return true
}

// openWAL returns a WAL ready for reading.
func (rc *raftNode) openWAL() *wal.WAL {
	if wal.Exist(rc.waldir) == false {
		if err := os.Mkdir(rc.waldir, 0750); err != nil {
			log.Fatalf("raftexample: cannot create dir for wal (%v)", err)
		}

		w, err := wal.Create(rc.waldir, nil)
		if err != nil {
			log.Fatalf("raftexample: create wal error (%v)", err)
		}
		w.Close()
	}

	w, err := wal.Open(rc.waldir, walpb.Snapshot{})
	if err != nil {
		log.Fatalf("raftexample: error loading wal (%v)", err)
	}

	return w
}

// replayWAL replays WAL entries into the raft instance.
func (rc *raftNode) replayWAL() *wal.WAL {
	w := rc.openWAL()
	_, st, ents, err := w.ReadAll()
	if err != nil {
		log.Fatalf("raftexample: failed to read WAL (%v)", err)
	}
	// append to storage so raft starts at the right place in log
	rc.raftStorage.Append(ents)
	// send nil once lastIndex is published so client knows commit channel is current
	if len(ents) > 0 {
		rc.lastIndex = ents[len(ents)-1].Index
	} else {
		rc.commitC <- nil
	}
	rc.raftStorage.SetHardState(st)
	return w
}

func (rc *raftNode) writeError(err error) {
	rc.stopHTTP()
	close(rc.commitC)
	rc.errorC <- err
	close(rc.errorC)
	rc.node.Stop()
}

func (rc *raftNode) startRaft() {
	oldwal := wal.Exist(rc.waldir)
	rc.wal = rc.replayWAL()

	rpeers := make([]raft.Peer, len(rc.peers))
	for i := range rpeers {
		rpeers[i] = raft.Peer{ID: uint64(i + 1)}
	}
	c := &raft.Config{
		ID:              uint64(rc.id),
		ElectionTick:    10,
		HeartbeatTick:   1,
		Storage:         rc.raftStorage,
		MaxSizePerMsg:   1024 * 1024,
		MaxInflightMsgs: 256,
	}

	if oldwal {
		rc.node = raft.RestartNode(c)
	} else {
		startPeers := rpeers
		if rc.join {
			startPeers = nil
		}
		rc.node = raft.StartNode(c, startPeers)
	}

	ss := &stats.ServerStats{}
	ss.Initialize()

	rc.transport = &rafthttp.Transport{
		ID:          types.ID(rc.id),
		ClusterID:   0x1000,
		Raft:        rc,
		ServerStats: ss,
		LeaderStats: stats.NewLeaderStats(strconv.Itoa(rc.id)),
		ErrorC:      make(chan error),
	}

	rc.transport.Start()
	for i := range rc.peers {
		if i+1 != rc.id {
			rc.transport.AddPeer(types.ID(i+1), []string{rc.peers[i]})
		}
	}

	go rc.serveRaft()
	go rc.serveChannels()
}

// stop closes http, closes all channels, and stops raft.
func (rc *raftNode) stop() {
	rc.stopHTTP()
	close(rc.commitC)
	close(rc.errorC)
	rc.node.Stop()

	os.Exit(0)
}

func (rc *raftNode) stopHTTP() {
	rc.transport.Stop()
	close(rc.httpstopc)
	<-rc.httpdonec
}

func (rc *raftNode) serveChannels() {
	defer rc.wal.Close()

	ticker := time.NewTicker(100 * time.Millisecond)
	defer ticker.Stop()

	// send proposals over raft
	go func() {
		var confChangeCount uint64 = 0

		for rc.proposeC != nil && rc.confChangeC != nil {
			select {
			case prop, ok := <-rc.proposeC:
				if !ok {
					rc.proposeC = nil
				} else {
					// blocks until accepted by raft state machine
					rc.node.Propose(context.TODO(), []byte(prop))
				}

			case cc, ok := <-rc.confChangeC:
				if !ok {
					rc.confChangeC = nil
				} else {
					confChangeCount += 1
					cc.ID = confChangeCount
					rc.node.ProposeConfChange(context.TODO(), cc)
				}
			}
		}
		// client closed channel; shutdown raft if not already
		close(rc.stopc)
	}()

	// event loop on raft state machine updates
	for {
		select {
		case <-ticker.C:
			rc.node.Tick()

		// store raft entries to wal, then publish over commit channel
		case rd := <-rc.node.Ready():
			rc.wal.Save(rd.HardState, rd.Entries)
			rc.raftStorage.Append(rd.Entries)
			rc.transport.Send(rd.Messages)
			if ok := rc.publishEntries(rd.CommittedEntries); !ok {
				rc.stop()
				return
			}
			rc.node.Advance()

		case err := <-rc.transport.ErrorC:
			rc.writeError(err)
			return

		case <-rc.stopc:
			rc.stop()
			return
		}
	}
}

func (rc *raftNode) serveRaft() {
	url, err := url.Parse(rc.peers[rc.id-1])
	if err != nil {
		log.Fatalf("raftexample: Failed parsing URL (%v)", err)
	}

	ln, err := newStoppableListener(url.Host, rc.httpstopc)
	if err != nil {
		log.Fatalf("raftexample: Failed to listen rafthttp (%v)", err)
	}

	err = (&http.Server{Handler: rc.transport.Handler()}).Serve(ln)
	select {
	case <-rc.httpstopc:
	default:
		log.Fatalf("raftexample: Failed to serve rafthttp (%v)", err)
	}
	close(rc.httpdonec)
}

func (rc *raftNode) Process(ctx context.Context, m raftpb.Message) error {
	return rc.node.Step(ctx, m)
}
func (rc *raftNode) IsIDRemoved(id uint64) bool                           { return false }
func (rc *raftNode) ReportUnreachable(id uint64)                          {}
func (rc *raftNode) ReportSnapshot(id uint64, status raft.SnapshotStatus) {}
<|endoftext|>


<commit_before>// +build linux,cgo,!agent

package db

import (
	"database/sql"
	"fmt"
	"sort"
	"time"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/pkg/errors"
)

// StorageVolumeArgs is a value object holding all db-related details about a
// storage volume.
type StorageVolumeArgs struct {
	Name string

	// At least one of Type or TypeName must be set.
	Type     int
	TypeName string

	// At least one of PoolID or PoolName must be set.
	PoolID   int64
	PoolName string

	Snapshot bool

	Config       map[string]string
	Description  string
	CreationDate time.Time
	ExpiryDate   time.Time
}

// StorageVolumeNodeAddresses returns the addresses of all nodes on which the
// volume with the given name if defined.
//
// The volume name can be either a regular name or a volume snapshot name.
//
// The empty string is used in place of the address of the current node.
func (c *ClusterTx) StorageVolumeNodeAddresses(poolID int64, project, name string, typ int) ([]string, error) {
	nodes := []struct {
		id      int64
		address string
	}{}
	dest := func(i int) []interface{} {
		nodes = append(nodes, struct {
			id      int64
			address string
		}{})
		return []interface{}{&nodes[i].id, &nodes[i].address}

	}
	sql := `
SELECT nodes.id, nodes.address
  FROM nodes
  JOIN storage_volumes_all ON storage_volumes_all.node_id=nodes.id
  JOIN projects ON projects.id = storage_volumes_all.project_id
 WHERE storage_volumes_all.storage_pool_id=?
   AND projects.name=?
   AND storage_volumes_all.name=?
   AND storage_volumes_all.type=?
`
	stmt, err := c.tx.Prepare(sql)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, poolID, project, name, typ)
	if err != nil {
		return nil, err
	}

	addresses := []string{}
	for _, node := range nodes {
		address := node.address
		if node.id == c.nodeID {
			address = ""
		}
		addresses = append(addresses, address)
	}

	sort.Strings(addresses)

	if len(addresses) == 0 {
		return nil, ErrNoSuchObject
	}

	return addresses, nil
}

// Return the name of the node a storage volume is on.
func (c *Cluster) storageVolumeNodeGet(volumeID int64) (string, error) {
	name := ""
	query := `
SELECT nodes.name FROM storage_volumes_all
  JOIN nodes ON nodes.id=storage_volumes_all.node_id
   WHERE storage_volumes_all.id=?
`
	inargs := []interface{}{volumeID}
	outargs := []interface{}{&name}

	err := dbQueryRowScan(c.db, query, inargs, outargs)
	if err != nil {
		if err == sql.ErrNoRows {
			return "", ErrNoSuchObject
		}

		return "", err
	}

	return name, nil
}

// Get the config of a storage volume.
func (c *Cluster) storageVolumeConfigGet(volumeID int64, isSnapshot bool) (map[string]string, error) {
	var key, value string
	var query string
	if isSnapshot {
		query = "SELECT key, value FROM storage_volumes_snapshots_config WHERE storage_volume_snapshot_id=?"
	} else {
		query = "SELECT key, value FROM storage_volumes_config WHERE storage_volume_id=?"
	}
	inargs := []interface{}{volumeID}
	outargs := []interface{}{key, value}

	results, err := queryScan(c.db, query, inargs, outargs)
	if err != nil {
		return nil, err
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

// StorageVolumeDescriptionGet gets the description of a storage volume.
func (c *Cluster) StorageVolumeDescriptionGet(volumeID int64) (string, error) {
	description := sql.NullString{}
	query := "SELECT description FROM storage_volumes_all WHERE id=?"
	inargs := []interface{}{volumeID}
	outargs := []interface{}{&description}

	err := dbQueryRowScan(c.db, query, inargs, outargs)
	if err != nil {
		if err == sql.ErrNoRows {
			return "", ErrNoSuchObject
		}
		return "", err
	}

	return description.String, nil
}

// StorageVolumeNextSnapshot returns the index of the next snapshot of the storage
// volume with the given name should have.
//
// Note, the code below doesn't deal with snapshots of snapshots.
// To do that, we'll need to weed out based on # slashes in names
func (c *Cluster) StorageVolumeNextSnapshot(name string, typ int) int {
	base := "snap"
	length := len(base)
	q := fmt.Sprintf(`
SELECT storage_volumes_snapshots.name FROM storage_volumes_snapshots
  JOIN storage_volumes ON storage_volumes_snapshots.storage_volume_id=storage_volumes.id
 WHERE storage_volumes.type=?
   AND storage_volumes.name=?
   AND SUBSTR(storage_volumes_snapshots.name,1,?)=?
`)
	var numstr string
	inargs := []interface{}{typ, name, length, base}
	outfmt := []interface{}{numstr}
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return 0
	}
	max := 0

	for _, r := range results {
		substr := r[0].(string)
		var num int
		count, err := fmt.Sscanf(substr, base+"%d", &num)
		if err != nil || count != 1 {
			continue
		}
		if num >= max {
			max = num + 1
		}
	}

	return max
}

// StorageVolumeIsAvailable checks that if a custom volume available for being attached.
//
// Always return true for non-Ceph volumes.
//
// For Ceph volumes, return true if the volume is either not attached to any
// other container, or attached to containers on this node.
func (c *Cluster) StorageVolumeIsAvailable(pool, volume string) (bool, error) {
	isAvailable := false

	err := c.Transaction(func(tx *ClusterTx) error {
		id, err := tx.StoragePoolID(pool)
		if err != nil {
			return errors.Wrapf(err, "Fetch storage pool ID for %q", pool)
		}

		driver, err := tx.StoragePoolDriver(id)
		if err != nil {
			return errors.Wrapf(err, "Fetch storage pool driver for %q", pool)
		}

		if driver != "ceph" {
			isAvailable = true
			return nil
		}

		node, err := tx.NodeName()
		if err != nil {
			return errors.Wrapf(err, "Fetch node name")
		}

		containers, err := tx.instanceListExpanded()
		if err != nil {
			return errors.Wrapf(err, "Fetch instances")
		}

		for _, container := range containers {
			for _, device := range container.Devices {
				if device["type"] != "disk" {
					continue
				}
				if device["pool"] != pool {
					continue
				}
				if device["source"] != volume {
					continue
				}
				if container.Node != node {
					// This ceph volume is already attached
					// to a container on a different node.
					return nil
				}
			}
		}
		isAvailable = true

		return nil
	})
	if err != nil {
		return false, err
	}

	return isAvailable, nil
}

// Updates the description of a storage volume.
func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description string, isSnapshot bool) error {
	var table string
	if isSnapshot {
		table = "storage_volumes_snapshots"
	} else {
		table = "storage_volumes"
	}
	stmt := fmt.Sprintf("UPDATE %s SET description=? WHERE id=?", table)
	_, err := tx.Exec(stmt, description, volumeID)
	return err
}

// Add a new storage volume config into database.
func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig map[string]string, isSnapshot bool) error {
	var str string
	if isSnapshot {
		str = "INSERT INTO storage_volumes_snapshots_config (storage_volume_snapshot_id, key, value) VALUES(?, ?, ?)"
	} else {
		str = "INSERT INTO storage_volumes_config (storage_volume_id, key, value) VALUES(?, ?, ?)"
	}
	stmt, err := tx.Prepare(str)
	defer stmt.Close()
	if err != nil {
		return err
	}

	for k, v := range volumeConfig {
		if v == "" {
			continue
		}

		_, err = stmt.Exec(volumeID, k, v)
		if err != nil {
			return err
		}
	}

	return nil
}

// Delete storage volume config.
func storageVolumeConfigClear(tx *sql.Tx, volumeID int64, isSnapshot bool) error {
	var stmt string
	if isSnapshot {
		stmt = "DELETE FROM storage_volumes_snapshots_config WHERE storage_volume_snapshot_id=?"
	} else {
		stmt = "DELETE FROM storage_volumes_config WHERE storage_volume_id=?"
	}
	_, err := tx.Exec(stmt, volumeID)
	if err != nil {
		return err
	}

	return nil
}

// Get the IDs of all volumes with the given name and type associated with the
// given pool, regardless of their node_id column.
func storageVolumeIDsGet(tx *sql.Tx, project, volumeName string, volumeType int, poolID int64) ([]int64, error) {
	ids, err := query.SelectIntegers(tx, `
SELECT storage_volumes_all.id
  FROM storage_volumes_all
  JOIN projects ON projects.id = storage_volumes_all.project_id
 WHERE projects.name=?
   AND storage_volumes_all.name=?
   AND storage_volumes_all.type=?
   AND storage_volumes_all.storage_pool_id=?
`, project, volumeName, volumeType, poolID)
	if err != nil {
		return nil, err
	}
	ids64 := make([]int64, len(ids))
	for i, id := range ids {
		ids64[i] = int64(id)
	}
	return ids64, nil
}

// StorageVolumeCleanupImages removes the volumes with the given fingerprints.
func (c *Cluster) StorageVolumeCleanupImages(fingerprints []string) error {
	stmt := fmt.Sprintf(
		"DELETE FROM storage_volumes WHERE type=? AND name NOT IN %s",
		query.Params(len(fingerprints)))
	args := []interface{}{StoragePoolVolumeTypeImage}
	for _, fingerprint := range fingerprints {
		args = append(args, fingerprint)
	}
	err := exec(c.db, stmt, args...)
	return err
}

// StorageVolumeMoveToLVMThinPoolNameKey upgrades the config keys of LVM
// volumes.
func (c *Cluster) StorageVolumeMoveToLVMThinPoolNameKey() error {
	err := exec(c.db, "UPDATE storage_pools_config SET key='lvm.thinpool_name' WHERE key='volume.lvm.thinpool_name';")
	if err != nil {
		return err
	}

	err = exec(c.db, "DELETE FROM storage_volumes_config WHERE key='lvm.thinpool_name';")
	if err != nil {
		return err
	}
	err = exec(c.db, "DELETE FROM storage_volumes_snapshots_config WHERE key='lvm.thinpool_name';")
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>lxd/db: Add ProjectName to StorageVolumeArgs<commit_after>// +build linux,cgo,!agent

package db

import (
	"database/sql"
	"fmt"
	"sort"
	"time"

	"github.com/lxc/lxd/lxd/db/query"
	"github.com/pkg/errors"
)

// StorageVolumeArgs is a value object holding all db-related details about a
// storage volume.
type StorageVolumeArgs struct {
	Name string

	// At least one of Type or TypeName must be set.
	Type     int
	TypeName string

	// At least one of PoolID or PoolName must be set.
	PoolID   int64
	PoolName string

	Snapshot bool

	Config       map[string]string
	Description  string
	CreationDate time.Time
	ExpiryDate   time.Time
	ProjectName  string
}

// StorageVolumeNodeAddresses returns the addresses of all nodes on which the
// volume with the given name if defined.
//
// The volume name can be either a regular name or a volume snapshot name.
//
// The empty string is used in place of the address of the current node.
func (c *ClusterTx) StorageVolumeNodeAddresses(poolID int64, project, name string, typ int) ([]string, error) {
	nodes := []struct {
		id      int64
		address string
	}{}
	dest := func(i int) []interface{} {
		nodes = append(nodes, struct {
			id      int64
			address string
		}{})
		return []interface{}{&nodes[i].id, &nodes[i].address}

	}
	sql := `
SELECT nodes.id, nodes.address
  FROM nodes
  JOIN storage_volumes_all ON storage_volumes_all.node_id=nodes.id
  JOIN projects ON projects.id = storage_volumes_all.project_id
 WHERE storage_volumes_all.storage_pool_id=?
   AND projects.name=?
   AND storage_volumes_all.name=?
   AND storage_volumes_all.type=?
`
	stmt, err := c.tx.Prepare(sql)
	if err != nil {
		return nil, err
	}
	defer stmt.Close()
	err = query.SelectObjects(stmt, dest, poolID, project, name, typ)
	if err != nil {
		return nil, err
	}

	addresses := []string{}
	for _, node := range nodes {
		address := node.address
		if node.id == c.nodeID {
			address = ""
		}
		addresses = append(addresses, address)
	}

	sort.Strings(addresses)

	if len(addresses) == 0 {
		return nil, ErrNoSuchObject
	}

	return addresses, nil
}

// Return the name of the node a storage volume is on.
func (c *Cluster) storageVolumeNodeGet(volumeID int64) (string, error) {
	name := ""
	query := `
SELECT nodes.name FROM storage_volumes_all
  JOIN nodes ON nodes.id=storage_volumes_all.node_id
   WHERE storage_volumes_all.id=?
`
	inargs := []interface{}{volumeID}
	outargs := []interface{}{&name}

	err := dbQueryRowScan(c.db, query, inargs, outargs)
	if err != nil {
		if err == sql.ErrNoRows {
			return "", ErrNoSuchObject
		}

		return "", err
	}

	return name, nil
}

// Get the config of a storage volume.
func (c *Cluster) storageVolumeConfigGet(volumeID int64, isSnapshot bool) (map[string]string, error) {
	var key, value string
	var query string
	if isSnapshot {
		query = "SELECT key, value FROM storage_volumes_snapshots_config WHERE storage_volume_snapshot_id=?"
	} else {
		query = "SELECT key, value FROM storage_volumes_config WHERE storage_volume_id=?"
	}
	inargs := []interface{}{volumeID}
	outargs := []interface{}{key, value}

	results, err := queryScan(c.db, query, inargs, outargs)
	if err != nil {
		return nil, err
	}

	config := map[string]string{}

	for _, r := range results {
		key = r[0].(string)
		value = r[1].(string)

		config[key] = value
	}

	return config, nil
}

// StorageVolumeDescriptionGet gets the description of a storage volume.
func (c *Cluster) StorageVolumeDescriptionGet(volumeID int64) (string, error) {
	description := sql.NullString{}
	query := "SELECT description FROM storage_volumes_all WHERE id=?"
	inargs := []interface{}{volumeID}
	outargs := []interface{}{&description}

	err := dbQueryRowScan(c.db, query, inargs, outargs)
	if err != nil {
		if err == sql.ErrNoRows {
			return "", ErrNoSuchObject
		}
		return "", err
	}

	return description.String, nil
}

// StorageVolumeNextSnapshot returns the index of the next snapshot of the storage
// volume with the given name should have.
//
// Note, the code below doesn't deal with snapshots of snapshots.
// To do that, we'll need to weed out based on # slashes in names
func (c *Cluster) StorageVolumeNextSnapshot(name string, typ int) int {
	base := "snap"
	length := len(base)
	q := fmt.Sprintf(`
SELECT storage_volumes_snapshots.name FROM storage_volumes_snapshots
  JOIN storage_volumes ON storage_volumes_snapshots.storage_volume_id=storage_volumes.id
 WHERE storage_volumes.type=?
   AND storage_volumes.name=?
   AND SUBSTR(storage_volumes_snapshots.name,1,?)=?
`)
	var numstr string
	inargs := []interface{}{typ, name, length, base}
	outfmt := []interface{}{numstr}
	results, err := queryScan(c.db, q, inargs, outfmt)
	if err != nil {
		return 0
	}
	max := 0

	for _, r := range results {
		substr := r[0].(string)
		var num int
		count, err := fmt.Sscanf(substr, base+"%d", &num)
		if err != nil || count != 1 {
			continue
		}
		if num >= max {
			max = num + 1
		}
	}

	return max
}

// StorageVolumeIsAvailable checks that if a custom volume available for being attached.
//
// Always return true for non-Ceph volumes.
//
// For Ceph volumes, return true if the volume is either not attached to any
// other container, or attached to containers on this node.
func (c *Cluster) StorageVolumeIsAvailable(pool, volume string) (bool, error) {
	isAvailable := false

	err := c.Transaction(func(tx *ClusterTx) error {
		id, err := tx.StoragePoolID(pool)
		if err != nil {
			return errors.Wrapf(err, "Fetch storage pool ID for %q", pool)
		}

		driver, err := tx.StoragePoolDriver(id)
		if err != nil {
			return errors.Wrapf(err, "Fetch storage pool driver for %q", pool)
		}

		if driver != "ceph" {
			isAvailable = true
			return nil
		}

		node, err := tx.NodeName()
		if err != nil {
			return errors.Wrapf(err, "Fetch node name")
		}

		containers, err := tx.instanceListExpanded()
		if err != nil {
			return errors.Wrapf(err, "Fetch instances")
		}

		for _, container := range containers {
			for _, device := range container.Devices {
				if device["type"] != "disk" {
					continue
				}
				if device["pool"] != pool {
					continue
				}
				if device["source"] != volume {
					continue
				}
				if container.Node != node {
					// This ceph volume is already attached
					// to a container on a different node.
					return nil
				}
			}
		}
		isAvailable = true

		return nil
	})
	if err != nil {
		return false, err
	}

	return isAvailable, nil
}

// Updates the description of a storage volume.
func storageVolumeDescriptionUpdate(tx *sql.Tx, volumeID int64, description string, isSnapshot bool) error {
	var table string
	if isSnapshot {
		table = "storage_volumes_snapshots"
	} else {
		table = "storage_volumes"
	}
	stmt := fmt.Sprintf("UPDATE %s SET description=? WHERE id=?", table)
	_, err := tx.Exec(stmt, description, volumeID)
	return err
}

// Add a new storage volume config into database.
func storageVolumeConfigAdd(tx *sql.Tx, volumeID int64, volumeConfig map[string]string, isSnapshot bool) error {
	var str string
	if isSnapshot {
		str = "INSERT INTO storage_volumes_snapshots_config (storage_volume_snapshot_id, key, value) VALUES(?, ?, ?)"
	} else {
		str = "INSERT INTO storage_volumes_config (storage_volume_id, key, value) VALUES(?, ?, ?)"
	}
	stmt, err := tx.Prepare(str)
	defer stmt.Close()
	if err != nil {
		return err
	}

	for k, v := range volumeConfig {
		if v == "" {
			continue
		}

		_, err = stmt.Exec(volumeID, k, v)
		if err != nil {
			return err
		}
	}

	return nil
}

// Delete storage volume config.
func storageVolumeConfigClear(tx *sql.Tx, volumeID int64, isSnapshot bool) error {
	var stmt string
	if isSnapshot {
		stmt = "DELETE FROM storage_volumes_snapshots_config WHERE storage_volume_snapshot_id=?"
	} else {
		stmt = "DELETE FROM storage_volumes_config WHERE storage_volume_id=?"
	}
	_, err := tx.Exec(stmt, volumeID)
	if err != nil {
		return err
	}

	return nil
}

// Get the IDs of all volumes with the given name and type associated with the
// given pool, regardless of their node_id column.
func storageVolumeIDsGet(tx *sql.Tx, project, volumeName string, volumeType int, poolID int64) ([]int64, error) {
	ids, err := query.SelectIntegers(tx, `
SELECT storage_volumes_all.id
  FROM storage_volumes_all
  JOIN projects ON projects.id = storage_volumes_all.project_id
 WHERE projects.name=?
   AND storage_volumes_all.name=?
   AND storage_volumes_all.type=?
   AND storage_volumes_all.storage_pool_id=?
`, project, volumeName, volumeType, poolID)
	if err != nil {
		return nil, err
	}
	ids64 := make([]int64, len(ids))
	for i, id := range ids {
		ids64[i] = int64(id)
	}
	return ids64, nil
}

// StorageVolumeCleanupImages removes the volumes with the given fingerprints.
func (c *Cluster) StorageVolumeCleanupImages(fingerprints []string) error {
	stmt := fmt.Sprintf(
		"DELETE FROM storage_volumes WHERE type=? AND name NOT IN %s",
		query.Params(len(fingerprints)))
	args := []interface{}{StoragePoolVolumeTypeImage}
	for _, fingerprint := range fingerprints {
		args = append(args, fingerprint)
	}
	err := exec(c.db, stmt, args...)
	return err
}

// StorageVolumeMoveToLVMThinPoolNameKey upgrades the config keys of LVM
// volumes.
func (c *Cluster) StorageVolumeMoveToLVMThinPoolNameKey() error {
	err := exec(c.db, "UPDATE storage_pools_config SET key='lvm.thinpool_name' WHERE key='volume.lvm.thinpool_name';")
	if err != nil {
		return err
	}

	err = exec(c.db, "DELETE FROM storage_volumes_config WHERE key='lvm.thinpool_name';")
	if err != nil {
		return err
	}
	err = exec(c.db, "DELETE FROM storage_volumes_snapshots_config WHERE key='lvm.thinpool_name';")
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/nsf/termbox-go"
    "strconv"
    "log"
)

type Tab interface {
    Name() string
    Status() string
    HandleKeyEvent(*termbox.Event) bool
    Draw()
    Query(string)
}

type Apollo struct {
    running bool
    width int
    height int
    events chan termbox.Event
    configuration *Configuration
    database *Database
    currentTab int
    tabs []Tab
}

func createApollo() *Apollo {
    width, height := termbox.Size()

    var tabs []Tab

    a := &Apollo{
        running: true,
        width: width,
        height: height,
        events: make(chan termbox.Event, 20),
        configuration: createConfiguration(),
        database: createDatabase(),
        currentTab: 0,
        tabs: tabs,
    }

    a.tabs = append(tabs, Tab(createStatusTab(a)))

    return a
}

func (a *Apollo) handleEvent(ev *termbox.Event) error {
    switch ev.Type {
    case termbox.EventKey:
        a.handleKeyEvent(ev)
    case termbox.EventResize:
        a.width, a.height = termbox.Size()
    case termbox.EventError:
        return ev.Err
    }

    return nil
}

func (a *Apollo) handleKeyEvent(ev *termbox.Event) {
    if ev.Key == termbox.KeyCtrlC {
        a.running = false
    }
}

func (a *Apollo) draw() {
    termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

    runes := []rune("top status bar - " + a.tabs[a.currentTab].Status())
    for i := 0; i < a.width; i++ {
        if i < len(runes) {
            termbox.SetCell(i, 0, runes[i], termbox.ColorWhite | termbox.AttrBold, termbox.ColorBlack | termbox.AttrBold)
        } else {
            termbox.SetCell(i, 0, ' ', termbox.ColorDefault, termbox.ColorBlack | termbox.AttrBold)
        }
    }

    a.tabs[a.currentTab].Draw()

    for i := 0; i < a.width; i++ {
        termbox.SetCell(i, a.height - 2, ' ', termbox.ColorDefault, termbox.ColorBlack | termbox.AttrBold)
    }
    x := 0
    for i := 0; i < len(a.tabs); i++ {
        runes := []rune(strconv.Itoa(i+1) + "." + a.tabs[i].Name() + " ")
        for j := 0; j < len(runes); j++ {
            termbox.SetCell(x, a.height - 2, runes[j], termbox.ColorWhite | termbox.AttrBold, termbox.ColorBlack | termbox.AttrBold)
            x++
        }
    }

    termbox.Flush()
}

func (a *Apollo) loop() {
    go func() {
        for {
            a.events <- termbox.PollEvent()
        }
    }()
    for (a.running) {
        select {
        case ev := <-a.events:
            err := a.handleEvent(&ev)
            if err != nil {
                a.running = false
                log.Fatal(err)
            }
            a.draw()
        }
    }
}

func main() {
    err := termbox.Init()
    if err != nil {
        log.Fatal(err)
    }
    defer termbox.Close()

    termbox.SetInputMode(termbox.InputAlt)

    apollo := createApollo()
    apollo.loop()
}
<commit_msg>Added an input text field<commit_after>package main

import (
    "github.com/nsf/termbox-go"
    "strconv"
    "unicode"
    "log"
)

type Tab interface {
    Name() string
    Status() string
    HandleKeyEvent(*termbox.Event) bool
    Draw()
    Query(string)
}

type Apollo struct {
    running bool
    width int
    height int
    events chan termbox.Event
    configuration *Configuration
    database *Database
    currentTab int
    tabs []Tab
    input []rune
    inputCursor int
    inputActive bool
}

func createApollo() *Apollo {
    width, height := termbox.Size()

    var tabs []Tab

    a := &Apollo{
        running: true,
        width: width,
        height: height,
        events: make(chan termbox.Event, 20),
        configuration: createConfiguration(),
        database: createDatabase(),
        tabs: tabs,
    }

    a.tabs = append(tabs, Tab(createStatusTab(a)))

    return a
}

func (a *Apollo) handleEvent(ev *termbox.Event) error {
    switch ev.Type {
    case termbox.EventKey:
        a.handleKeyEvent(ev)
    case termbox.EventResize:
        a.width, a.height = termbox.Size()
    case termbox.EventError:
        return ev.Err
    }

    return nil
}

func (a *Apollo) handleKeyEvent(ev *termbox.Event) {
    handled := false
    if !a.inputActive {
        handled = a.tabs[a.currentTab].HandleKeyEvent(ev)
    }

    if !handled {
        switch ev.Key {
        case termbox.KeyEnter:
            if a.inputActive {
                a.inputActive = false
            } else {
                a.inputActive = true
            }
        case termbox.KeyCtrlC:
            a.running = false
        case termbox.KeyBackspace, termbox.KeyBackspace2:
            if a.inputActive {
                if a.inputCursor > 0 {
                    a.input = append(a.input[:a.inputCursor-1], a.input[a.inputCursor:]...)
                    a.inputCursor--
                }
            }
        case termbox.KeySpace:
            if a.inputActive {
                a.input = append(a.input, ' ')
                copy(a.input[a.inputCursor+1:], a.input[a.inputCursor:])
                a.input[a.inputCursor] = ' '
                a.inputCursor++
            }
        case termbox.KeyArrowLeft:
            if a.inputActive {
                a.inputCursor--
                if a.inputCursor < 0 {
                    a.inputCursor = 0
                }
            }
        case termbox.KeyArrowRight:
            if a.inputActive {
                a.inputCursor++
                if a.inputCursor > len(a.input) {
                    a.inputCursor = len(a.input)
                }
            }
        default:
            if ev.Mod == termbox.ModAlt {
                numbers := map[rune]int{'1': 1, '2': 2, '3': 3,
                                        '4': 4, '5': 5, '6': 6,
                                        '7': 7, '8': 8, '9': 9,}
                if number, exist := numbers[ev.Ch]; exist {
                    if len(a.tabs) > number - 1 {
                        a.currentTab = number - 1
                    }
                }
            } else {
                if unicode.IsPrint(ev.Ch) && a.inputActive {
                    a.input = append(a.input, ' ')
                    copy(a.input[a.inputCursor+1:], a.input[a.inputCursor:])
                    a.input[a.inputCursor] = ev.Ch
                    a.inputCursor++
                }
            }
        }
    }
}

func (a *Apollo) draw() {
    termbox.Clear(termbox.ColorDefault, termbox.ColorDefault)

    runes := []rune("top status bar - " + a.tabs[a.currentTab].Status())
    for i := 0; i < a.width; i++ {
        if i < len(runes) {
            termbox.SetCell(i, 0, runes[i], termbox.ColorWhite | termbox.AttrBold, termbox.ColorBlack | termbox.AttrBold)
        } else {
            termbox.SetCell(i, 0, ' ', termbox.ColorDefault, termbox.ColorBlack | termbox.AttrBold)
        }
    }

    a.tabs[a.currentTab].Draw()

    for i := 0; i < a.width; i++ {
        termbox.SetCell(i, a.height - 2, ' ', termbox.ColorDefault, termbox.ColorBlack | termbox.AttrBold)
    }
    x := 0
    for i := 0; i < len(a.tabs); i++ {
        runes := []rune(strconv.Itoa(i+1) + "." + a.tabs[i].Name() + " ")
        for j := 0; j < len(runes); j++ {
            termbox.SetCell(x, a.height - 2, runes[j], termbox.ColorWhite | termbox.AttrBold, termbox.ColorBlack | termbox.AttrBold)
            x++
        }
    }

    if len(a.input) < a.width {
        for i := 0; i < len(a.input); i++ {
            termbox.SetCell(i, a.height - 1, a.input[i], termbox.ColorWhite, termbox.ColorDefault)
        }
    } else {
        offset := len(a.input) - a.width + 1
        for i := 0; i < a.width - 1; i++ {
            termbox.SetCell(i, a.height - 1, a.input[i + offset], termbox.ColorWhite, termbox.ColorDefault)
        }
    }
    if a.inputActive {
        termbox.SetCursor(a.inputCursor, a.height - 1)
    } else {
        termbox.HideCursor()
    }

    termbox.Flush()
}

func (a *Apollo) loop() {
    go func() {
        for {
            a.events <- termbox.PollEvent()
        }
    }()
    for (a.running) {
        select {
        case ev := <-a.events:
            err := a.handleEvent(&ev)
            if err != nil {
                a.running = false
                log.Fatal(err)
            }
            a.draw()
        }
    }
}

func main() {
    err := termbox.Init()
    if err != nil {
        log.Fatal(err)
    }
    defer termbox.Close()

    termbox.SetInputMode(termbox.InputAlt)

    apollo := createApollo()
    apollo.draw()
    apollo.loop()
}
<|endoftext|>


<commit_before>package action

import (
	"appengine"
	"appengine/datastore"
	"time"
)

const (
	Activated   ActionType = iota
	Deactivated ActionType = iota
)

//ActionType defines the type of the Action performed
type ActionType int

//String returns the type of action in human-readable form
func (at ActionType) String() string {
	switch at {
	case Activated:
		return "Activated"
	case Deactivated:
		return "Deactivated"
	}
	return "Unknown action"
}

//Action is a type used for recording the time when clients
//did an action specified by the ActionType.
type Action struct {
	Type   ActionType     //Type of action
	Client string         //Name of the client
	Time   time.Time      //Time of the action
	Model  *datastore.Key //What object is the action related to.
	Key    string         `datastore:"-"`
}

//Model is an interface specifying models - structs stored in Datastore.
type Model interface {
	GetKey(appengine.Context) (*datastore.Key, error)
}

//GetKey is used to obtain Model's key.
func (a Action) GetKey() (k *datastore.Key, err error) {
	k, err = datastore.DecodeKey(a.Key)
	return
}

//New returns a pointer to an Action with its fields set according to arguments.
func New(k *datastore.Key, at ActionType, client string) (a *Action) {
	a = new(Action)
	a.Model = k
	a.Type = at
	a.Client = client
	a.Time = time.Now()
	return a
}

//Make creates a new Action using New and then saves it to Datastore.
func Make(m Model, at ActionType, client string, c appengine.Context) (a *Action, err error) {
	k, err := m.GetKey(c)
	if err != nil {
		return
	}
	a = New(k, at, client)
	err = a.Save(c)
	return
}

//Save saves an Action to Datastore.
//If its Key field is set, it will replace existing record
//that has that key. If not, it will use datastore.NewIncompleteKey()
//to create a new key and set the field.
func (a *Action) Save(c appengine.Context) (err error) {
	if a.Key == "" {
		var key *datastore.Key
		key, err = datastore.Put(c, datastore.NewIncompleteKey(c, "Action", a.Model), a)
		a.Key = key.Encode()
		return
	} else {
		var key *datastore.Key
		key, err = datastore.DecodeKey(a.Key)
		if err != nil {
			return
		}
		_, err = datastore.Put(c, key, a)
	}
	return
}

//Log works like Make but logs errors instead of returning them.
func Log(m Model, at ActionType, client string, c appengine.Context) {
	if client == "" {
		c.Infof("%v called without client name.", at)
		return
	}

	_, err := Make(m, at, client, c)
	if err != nil {
		c.Infof("Can't log Action to Datastore: %v", err)
	}
}

//GetFor returns a slice containing all the Actions for a given Model.
func GetFor(m Model, c appengine.Context) (as []Action, err error) {
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	as = make([]Action, 12)
	keys, err := datastore.NewQuery("Action").Ancestor(key).GetAll(c, &as)
	if err != nil {
		return
	}
	for i := range keys {
		as[i].Key = keys[i].Encode()
	}
	return
}

//GetCountFor returns how many times given ActionType was performed on a Model.
func GetCountFor(at ActionType, m Model, c appengine.Context) (count int, err error) {
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	count, err = datastore.NewQuery("Action").Ancestor(key).Filter("Type =", at).Count(c)
	return
}

//WasPerformedOn returns whether given client performed given ActionType on gived Model.
func WasPerformedOn(at ActionType, m Model, client string, c appengine.Context) (bool, error) {
	key, err := m.GetKey(c)
	if err != nil {
		return false, err
	}
	i := datastore.NewQuery("Action").Ancestor(key).Filter("Client =", client).Filter("Type =", at).KeysOnly().Run(c)
	_, err = i.Next(nil)
	if err == datastore.Done {
		return false, nil
	}
	return true, err
}

//DeleteFor deletes all Actions for a specified Model.
func DeleteFor(m Model, c appengine.Context) (err error) {
	var keys []*datastore.Key
	key, err := m.GetKey(c)
	if err != nil {
		return
	}
	keys, err = datastore.NewQuery("Action").Ancestor(key).KeysOnly().GetAll(c, nil)
	if err != nil {
		return
	}
	err = datastore.DeleteMulti(c, keys)
	if err != nil {
		return
	}
	return
}
<commit_msg>Converted actions to use gaemodel<commit_after>package action

import (
	"appengine"
	"appengine/datastore"
	"github.com/sellweek/gaemodel"
	"reflect"
	"time"
)

const (
	Activated   ActionType = iota
	Deactivated ActionType = iota
)

//ActionType defines the type of the Action performed
type ActionType int

//String returns the type of action in human-readable form
func (at ActionType) String() string {
	switch at {
	case Activated:
		return "Activated"
	case Deactivated:
		return "Deactivated"
	}
	return "Unknown action"
}

//Action is a type used for recording the time when clients
//did an action specified by the ActionType.
type Action struct {
	Type   ActionType     //Type of action
	Client string         //Name of the client
	Time   time.Time      //Time of the action
	Model  *datastore.Key //What object is the action related to.
	key    *datastore.Key `datastore:"-"`
}

var typ = reflect.TypeOf(Action{})

func (a *Action) Key() *datastore.Key {
	return a.key
}

func (a *Action) SetKey(k *datastore.Key) {
	a.key = k
}

func (a *Action) Kind() string {
	return "Action"
}

func (a *Action) Ancestor() *datastore.Key {
	return a.Model
}

//New returns a pointer to an Action with its fields set according to arguments.
func New(k *datastore.Key, at ActionType, client string) (a *Action) {
	a = new(Action)
	a.Model = k
	a.Type = at
	a.Client = client
	a.Time = time.Now()
	return a
}

//Make creates a new Action using New and then saves it to Datastore.
func Make(m Model, at ActionType, client string, c appengine.Context) (a *Action, err error) {
	k, err := m.GetKey(c)
	if err != nil {
		return
	}
	a = New(k, at, client)
	err = a.Save(c)
	return
}

//Save saves an Action to Datastore.
//If its Key field is set, it will replace existing record
//that has that key. If not, it will use datastore.NewIncompleteKey()
//to create a new key and set the field.
func (a *Action) Save(c appengine.Context) error {
	return gaemodel.Save(c, a)
}

//Log works like Make but logs errors instead of returning them.
func Log(m gaemodel.Model, at ActionType, client string, c appengine.Context) {
	if client == "" {
		c.Infof("%v called without client name.", at)
		return
	}

	_, err := Make(m, at, client, c)
	if err != nil {
		c.Infof("Can't log Action to Datastore: %v", err)
	}
}

//GetFor returns a slice containing all the Actions for a given Model.
func GetFor(m gaemodel.Model, c appengine.Context) (as []Action, err error) {
	is, err := gaemodel.GetByAncestor(c, typ, "Action", m.Key())
	if err != nil {
		return
	}
	as = is.([]*Action)
	return
}

//GetCountFor returns how many times given ActionType was performed on a Model.
func GetCountFor(at ActionType, m gaemodel.Model, c appengine.Context) (count int, err error) {
	key, err := m.Key(c)
	if err != nil {
		return
	}
	count, err = datastore.NewQuery("Action").Ancestor(key).Filter("Type =", at).Count(c)
	return
}

//WasPerformedOn returns whether given client performed given ActionType on gived Model.
func WasPerformedOn(at ActionType, m gaemodel.Model, client string, c appengine.Context) (bool, error) {
	key, err := m.Key(c)
	if err != nil {
		return false, err
	}
	i := datastore.NewQuery("Action").Ancestor(key).Filter("Client =", client).Filter("Type =", at).KeysOnly().Run(c)
	_, err = i.Next(nil)
	if err == datastore.Done {
		return false, nil
	}
	return true, err
}

//DeleteFor deletes all Actions for a specified Model.
func DeleteFor(m gaemodel.Model, c appengine.Context) (err error) {
	var keys []*datastore.Key
	key, err := m.Key(c)
	if err != nil {
		return
	}
	keys, err = datastore.NewQuery("Action").Ancestor(key).KeysOnly().GetAll(c, nil)
	if err != nil {
		return
	}
	err = datastore.DeleteMulti(c, keys)
	if err != nil {
		return
	}
	return
}
<|endoftext|>