<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

/*
#include <utmp.h>
*/
import "C"

import (
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/coreos/go-systemd/dbus"
	"github.com/coreos/go-systemd/login1"
	"github.com/coreos/pkg/capnslog"

	"github.com/coreos/locksmith/lock"
	"github.com/coreos/locksmith/pkg/coordinatorconf"
	"github.com/coreos/locksmith/pkg/machineid"
	"github.com/coreos/locksmith/pkg/timeutil"
	"github.com/coreos/locksmith/updateengine"
)

const (
	initialInterval   = time.Second * 5
	maxInterval       = time.Minute * 5
	loginsRebootDelay = time.Minute * 5

	coordinatorName = "locksmithd"
)

var (
	etcdServices = []string{
		"etcd.service",
		"etcd2.service",
	}

	// TODO(mischief): daemon is not really a seperate package. it probably should be.
	dlog = capnslog.NewPackageLogger("github.com/coreos/locksmith", "daemon")
)

const (
	// The following constants represent the four strategies locksmith can take for
	// the checking if it is okay for the machine to reboot.

	// StrategyReboot reboots the machine as soon as it is instructed to do so,
	// without taking a lock.
	StrategyReboot = "reboot"

	// StrategyEtcdLock connects to the configured etcd and stores the lock in
	// there. Before it reboots, it aquires the lock, and will not reboot until
	// it does.
	StrategyEtcdLock = "etcd-lock"

	// StrategyBestEffort is deprecated and will be removed on 09/14/17
	// https://coreos.com/blog/locksmith-update-strategy-revision
	// StrategyBestEffort is currently the default strategy. It heuristically
	// attempts to find a running etcd cluster and connect to it. If it finds
	// one, it behaves like StrategyEtcdLock, and if it doesn't, it behaves like
	// StrategyReboot.
	StrategyBestEffort = "best-effort"

	// StrategyOff causes locksmith to exit without performing any actions
	StrategyOff = "off"
)

// attempt to broadcast msg to all lines registered in utmp
// returns count of lines successfully opened (and likely broadcasted to)
func broadcast(msg string) uint {
	var cnt uint
	C.setutent()

	for {
		var utmp *C.struct_utmp
		utmp = C.getutent()
		if utmp == nil {
			break
		}

		line := C.GoString(&utmp.ut_line[0])
		tty, _ := os.OpenFile("/dev/"+line, os.O_WRONLY, 0)
		if tty == nil {
			// ignore errors, this is just a best-effort courtesy notice
			continue
		}
		cnt++
		go func() {
			fmt.Fprintf(tty, "\r%79s\r\n", " ")
			fmt.Fprintf(tty, "%-79.79s\007\007\r\n", fmt.Sprintf("Broadcast message from locksmithd at %s:", time.Now()))
			fmt.Fprintf(tty, "%-79.79s\r\n", msg) // msg is assumed to be short and not require wrapping
			fmt.Fprintf(tty, "\r%79s\r\n", " ")
			tty.Close()
		}()
	}

	return cnt
}

func expBackoff(interval time.Duration) time.Duration {
	interval = interval * 2
	if interval > maxInterval {
		interval = maxInterval
	}
	return interval
}

func (r rebooter) rebootAndSleep() {
	// Broadcast a notice, if broadcast found lines to notify, delay the reboot.
	delaymins := loginsRebootDelay / time.Minute
	lines := broadcast(fmt.Sprintf("System reboot in %d minutes!", delaymins))
	if 0 != lines {
		dlog.Noticef("Logins detected, delaying reboot for %d minutes.", delaymins)
		time.Sleep(loginsRebootDelay)
	}
	r.lgn.Reboot(false)
	dlog.Info("Reboot sent. Going to sleep.")
	if err := r.coordinatorConfigUpdater.UpdateState(coordinatorconf.CoordinatorStateRebooting); err != nil {
		dlog.Errorf("could not update state file to indicate rebooting: %v", err)
	}

	// Wait a really long time for the reboot to occur.
	time.Sleep(time.Hour * 24 * 7)
}

// lockAndReboot attempts to acquire the lock and reboot the machine in an
// infinite loop. Returns if the reboot failed.
func (r rebooter) lockAndReboot(lck *lock.Lock) {
	interval := initialInterval
	for {
		err := lck.Lock()
		if err != nil && err != lock.ErrExist {
			interval = expBackoff(interval)
			dlog.Warningf("Failed to acquire lock: %v. Retrying in %v.", err, interval)
			time.Sleep(interval)

			continue
		}

		r.rebootAndSleep()
		return
	}
}

func setupLock() (lck *lock.Lock, err error) {
	elc, err := getClient()
	if err != nil {
		return nil, fmt.Errorf("Error initializing etcd client: %v", err)
	}

	mID := machineid.MachineID("/")
	if mID == "" {
		return nil, fmt.Errorf("Cannot read machine-id")
	}

	lck = lock.New(mID, elc)

	return lck, nil
}

// etcdActive returns true if etcd is not in an inactive state according to systemd.
func etcdActive() (active bool, name string, err error) {
	active = false
	name = ""

	sys, err := dbus.New()
	if err != nil {
		return
	}
	defer sys.Close()

	for _, service := range etcdServices {
		prop, err := sys.GetUnitProperty(service, "ActiveState")
		if err != nil {
			continue
		}

		switch prop.Value.Value().(string) {
		case "inactive":
			continue
		default:
			active = true
			name = service
			break
		}
	}

	return
}

type rebooter struct {
	strategy                 string
	lgn                      *login1.Conn
	coordinatorConfigUpdater coordinatorconf.CoordinatorConfigUpdater
}

func (r rebooter) reboot() int {
	if err := r.coordinatorConfigUpdater.UpdateState(coordinatorconf.CoordinatorStateRebootPlanned); err != nil {
		dlog.Errorf("could not update state file to indicate reboot planned: %v", err)
	}

	useLock, err := useLock(r.strategy)
	if err != nil {
		dlog.Errorf("Failed to figure out if locksmithd needs to use a lock: %v", err)
		return 1
	}

	if useLock {
		lck, err := setupLock()
		if err != nil {
			dlog.Errorf("Failed to set up lock: %v", err)
			return 1
		}

		err = unlockIfHeld(lck)
		if err != nil {
			dlog.Errorf("Failed to unlock held lock: %v", err)
			return 1
		}

		r.lockAndReboot(lck)
	}

	r.rebootAndSleep()
	dlog.Fatal("Tried to reboot but did not!")
	return 1
}

// useLock returns whether locksmith should attempt to take a lock before
// rebooting or release a lock afterwards, based on the given strategy.
// If strategy is set to best effort, this will be dependent on whether the
// local instance of etcd is active. Otherwise, the lock will always be
// attempted (in the case of strategy = etcd lock) or never be attempted (in
// the case of strategy = reboot)
func useLock(strategy string) (useLock bool, err error) {
	switch strategy {
	case StrategyBestEffort:
		active, name, err := etcdActive()
		if err != nil {
			return false, err
		}
		if active {
			dlog.Infof("%s is active", name)
			useLock = true
		} else {
			dlog.Infof("%v are inactive", etcdServices)
			useLock = false
		}
	case StrategyEtcdLock:
		useLock = true
	case StrategyReboot:
		useLock = false
	default:
		return false, fmt.Errorf("unknown strategy: %s", strategy)
	}

	return useLock, nil
}

// unlockIfHeld will unlock a lock, if it is held by this machine, or return an error.
func unlockIfHeld(lck *lock.Lock) error {
	err := lck.Unlock()
	if err == lock.ErrNotExist {
		return nil
	} else if err == nil {
		dlog.Info("Unlocked existing lock for this machine")
		return nil
	}

	return err
}

// unlockHeldLock will loop until it can confirm that any held locks are
// released or a stop signal is sent.
func unlockHeldLocks(strategy string, stop chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	interval := initialInterval
	for {
		var reason string
		select {
		case <-stop:
			return
		case <-time.After(interval):
			// Here we assume that the strategy used by locksmith
			// is consistent with that used before the previous
			// reboot (if any), and use that to decide whether to
			// attempt to unlock.
			shouldUnlock, err := useLock(strategy)
			if err != nil {
				reason = fmt.Sprintf("error checking whether lock should be released: %v", err)
				break
			}
			if !shouldUnlock {
				reason = fmt.Sprintf("%v are inactive", etcdServices)
				break
			}

			lck, err := setupLock()
			if err != nil {
				reason = "error setting up lock: " + err.Error()
				break
			}

			err = unlockIfHeld(lck)
			if err == nil {
				return
			}
			reason = err.Error()
		}

		interval = expBackoff(interval)
		dlog.Errorf("Unlocking old locks failed: %v. Retrying in %v.", reason, interval)
	}
}

// runDaemon waits for the reboot needed signal coming out of update engine and
// attempts to acquire the reboot lock. If the reboot lock is acquired then the
// machine will reboot.
func runDaemon() int {
	var period *timeutil.Periodic

	strategy := os.Getenv("REBOOT_STRATEGY")

	if strategy == "" {
		strategy = StrategyBestEffort
	}

	// XXX: complain loudly if besteffort is used
	if strategy == StrategyBestEffort {
		dlog.Errorf("Reboot strategy %q is deprecated and will be removed in the future.", strategy)
		dlog.Errorf("Please explicitly set the reboot strategy to one of %v", []string{StrategyOff, StrategyReboot, StrategyEtcdLock})
		dlog.Error("See https://coreos.com/os/docs/latest/update-strategies.html for details on configuring reboot strategies.")
	}

	if strategy == StrategyOff {
		dlog.Noticef("Reboot strategy is %q - locksmithd is exiting.", strategy)
		return 0
	}

	// XXX: REBOOT_WINDOW_* are deprecated in favor of variables with LOCKSMITHD_ prefix,
	// but the old ones are read for compatibility.
	startw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_START")
	if startw == "" {
		startw = os.Getenv("REBOOT_WINDOW_START")
	}

	lengthw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_LENGTH")
	if lengthw == "" {
		lengthw = os.Getenv("REBOOT_WINDOW_LENGTH")
	}

	if (startw == "") != (lengthw == "") {
		dlog.Fatal("Either both or neither $REBOOT_WINDOW_START and $REBOOT_WINDOW_LENGTH must be set")
	}

	if startw != "" && lengthw != "" {
		p, err := timeutil.ParsePeriodic(startw, lengthw)
		if err != nil {
			dlog.Fatalf("Error parsing reboot window: %s", err)
		}

		period = p
	}

	if period != nil {
		dlog.Infof("Reboot window start is %q and length is %q", startw, lengthw)
		next := period.Next(time.Now())
		dlog.Infof("Next window begins at %s and ends at %s", next.Start, next.End)
	} else {
		dlog.Info("No configured reboot window")
	}

	coordinatorConf, err := coordinatorconf.New(coordinatorName, strategy)
	if err != nil {
		dlog.Fatalf("unable to become 'update coordinator': %v", err)
	}

	shutdown := make(chan os.Signal, 1)
	stop := make(chan struct{}, 1)

	go func() {
		<-shutdown
		dlog.Notice("Received interrupt/termination signal - locksmithd is exiting.")
		os.Exit(0)
	}()
	signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)

	ue, err := updateengine.New()
	if err != nil {
		dlog.Fatalf("Error initializing update1 client: %v", err)
	}

	lgn, err := login1.New()
	if err != nil {
		dlog.Fatalf("Error initializing login1 client: %v", err)
	}

	var wg sync.WaitGroup
	if strategy != StrategyReboot {
		wg.Add(1)
		go unlockHeldLocks(strategy, stop, &wg)
	}

	ch := make(chan updateengine.Status, 1)
	go ue.RebootNeededSignal(ch, stop)

	r := rebooter{
		strategy: strategy,
		lgn:      lgn,
		coordinatorConfigUpdater: coordinatorConf,
	}

	result, err := ue.GetStatus()
	if err != nil {
		dlog.Fatalf("Cannot get update engine status: %v", err)
	}

	dlog.Infof("locksmithd starting currentOperation=%q strategy=%q", result.CurrentOperation, strategy)
	if err := r.coordinatorConfigUpdater.UpdateState(coordinatorconf.CoordinatorStateRunning); err != nil {
		dlog.Errorf("could not indicate 'running' in state file: %v", err)
	}

	if result.CurrentOperation != updateengine.UpdateStatusUpdatedNeedReboot {
		<-ch
	}

	close(stop)
	wg.Wait()

	if period != nil {
		now := time.Now()
		sleeptime := period.DurationToStart(now)
		if sleeptime > 0 {
			dlog.Infof("Waiting for %s to reboot.", sleeptime)
			time.Sleep(sleeptime)
		}
	}

	return r.reboot()
}
<commit_msg>locksmithctl/daemon: add additional comments and fix typo<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

/*
#include <utmp.h>
*/
import "C"

import (
	"fmt"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"github.com/coreos/go-systemd/dbus"
	"github.com/coreos/go-systemd/login1"
	"github.com/coreos/pkg/capnslog"

	"github.com/coreos/locksmith/lock"
	"github.com/coreos/locksmith/pkg/coordinatorconf"
	"github.com/coreos/locksmith/pkg/machineid"
	"github.com/coreos/locksmith/pkg/timeutil"
	"github.com/coreos/locksmith/updateengine"
)

const (
	initialInterval   = time.Second * 5
	maxInterval       = time.Minute * 5
	loginsRebootDelay = time.Minute * 5

	coordinatorName = "locksmithd"
)

var (
	etcdServices = []string{
		"etcd.service",
		"etcd2.service",
	}

	// TODO(mischief): daemon is not really a seperate package. it probably should be.
	dlog = capnslog.NewPackageLogger("github.com/coreos/locksmith", "daemon")
)

const (
	// The following constants represent the four strategies locksmith can take for
	// the checking if it is okay for the machine to reboot.

	// StrategyReboot reboots the machine as soon as it is instructed to do so,
	// without taking a lock.
	StrategyReboot = "reboot"

	// StrategyEtcdLock connects to the configured etcd and stores the lock in
	// there. Before it reboots, it aquires the lock, and will not reboot until
	// it does.
	StrategyEtcdLock = "etcd-lock"

	// StrategyBestEffort is deprecated and will be removed on 09/14/17
	// https://coreos.com/blog/locksmith-update-strategy-revision
	// StrategyBestEffort is currently the default strategy. It heuristically
	// attempts to find a running etcd cluster and connect to it. If it finds
	// one, it behaves like StrategyEtcdLock, and if it doesn't, it behaves like
	// StrategyReboot.
	StrategyBestEffort = "best-effort"

	// StrategyOff causes locksmith to exit without performing any actions
	StrategyOff = "off"
)

// attempt to broadcast msg to all lines registered in utmp
// returns count of lines successfully opened (and likely broadcasted to)
func broadcast(msg string) uint {
	var cnt uint

	// move the utmp file pointer to the beginning of the utmp file
	C.setutent()

	// loop until we're out of utmp lines
	for {
		// read another line from the utmp file
		var utmp *C.struct_utmp
		utmp = C.getutent()
		if utmp == nil {
			// if no struct was returned, then there are no more lines and we're
			// done
			break
		}

		// get the device name of this user's tty out of the utmp struct
		line := C.GoString(&utmp.ut_line[0])
		tty, _ := os.OpenFile("/dev/"+line, os.O_WRONLY, 0)
		if tty == nil {
			// if we couldn't open the tty for this user, skip this user
			continue
		}
		// increment the counter of lines successfully opened
		cnt++
		// attempt to (asynchronously) write to the tty
		go func() {
			fmt.Fprintf(tty, "\r%79s\r\n", " ")
			fmt.Fprintf(tty, "%-79.79s\007\007\r\n", fmt.Sprintf("Broadcast message from locksmithd at %s:", time.Now()))
			fmt.Fprintf(tty, "%-79.79s\r\n", msg) // msg is assumed to be short and not require wrapping
			fmt.Fprintf(tty, "\r%79s\r\n", " ")
			tty.Close()
		}()
	}

	return cnt
}

func expBackoff(interval time.Duration) time.Duration {
	interval = interval * 2
	if interval > maxInterval {
		interval = maxInterval
	}
	return interval
}

func (r rebooter) rebootAndSleep() {
	// Broadcast a notice, if broadcast found lines to notify, delay the reboot.
	delaymins := loginsRebootDelay / time.Minute
	lines := broadcast(fmt.Sprintf("System reboot in %d minutes!", delaymins))
	if 0 != lines {
		dlog.Noticef("Logins detected, delaying reboot for %d minutes.", delaymins)
		time.Sleep(loginsRebootDelay)
	}
	r.lgn.Reboot(false)
	dlog.Info("Reboot sent. Going to sleep.")
	if err := r.coordinatorConfigUpdater.UpdateState(coordinatorconf.CoordinatorStateRebooting); err != nil {
		dlog.Errorf("could not update state file to indicate rebooting: %v", err)
	}

	// Wait a really long time for the reboot to occur.
	time.Sleep(time.Hour * 24 * 7)
}

// lockAndReboot attempts to acquire the lock and reboot the machine in an
// infinite loop. Returns if the reboot failed.
func (r rebooter) lockAndReboot(lck *lock.Lock) {
	interval := initialInterval
	for {
		err := lck.Lock()
		if err != nil && err != lock.ErrExist {
			interval = expBackoff(interval)
			dlog.Warningf("Failed to acquire lock: %v. Retrying in %v.", err, interval)
			time.Sleep(interval)

			continue
		}

		r.rebootAndSleep()
		return
	}
}

func setupLock() (lck *lock.Lock, err error) {
	elc, err := getClient()
	if err != nil {
		return nil, fmt.Errorf("Error initializing etcd client: %v", err)
	}

	mID := machineid.MachineID("/")
	if mID == "" {
		return nil, fmt.Errorf("Cannot read machine-id")
	}

	lck = lock.New(mID, elc)

	return lck, nil
}

// etcdActive returns true if etcd is not in an inactive state according to systemd.
func etcdActive() (active bool, name string, err error) {
	active = false
	name = ""

	sys, err := dbus.New()
	if err != nil {
		return
	}
	defer sys.Close()

	for _, service := range etcdServices {
		prop, err := sys.GetUnitProperty(service, "ActiveState")
		if err != nil {
			continue
		}

		switch prop.Value.Value().(string) {
		case "inactive":
			continue
		default:
			active = true
			name = service
			break
		}
	}

	return
}

type rebooter struct {
	strategy                 string
	lgn                      *login1.Conn
	coordinatorConfigUpdater coordinatorconf.CoordinatorConfigUpdater
}

func (r rebooter) reboot() int {
	if err := r.coordinatorConfigUpdater.UpdateState(coordinatorconf.CoordinatorStateRebootPlanned); err != nil {
		dlog.Errorf("could not update state file to indicate reboot planned: %v", err)
	}

	useLock, err := useLock(r.strategy)
	if err != nil {
		dlog.Errorf("Failed to figure out if locksmithd needs to use a lock: %v", err)
		return 1
	}

	if useLock {
		lck, err := setupLock()
		if err != nil {
			dlog.Errorf("Failed to set up lock: %v", err)
			return 1
		}

		err = unlockIfHeld(lck)
		if err != nil {
			dlog.Errorf("Failed to unlock held lock: %v", err)
			return 1
		}

		r.lockAndReboot(lck)
	}

	r.rebootAndSleep()
	dlog.Fatal("Tried to reboot but did not!")
	return 1
}

// useLock returns whether locksmith should attempt to take a lock before
// rebooting or release a lock afterwards, based on the given strategy.
// If strategy is set to best effort, this will be dependent on whether the
// local instance of etcd is active. Otherwise, the lock will always be
// attempted (in the case of strategy = etcd lock) or never be attempted (in
// the case of strategy = reboot)
func useLock(strategy string) (useLock bool, err error) {
	switch strategy {
	case StrategyBestEffort:
		active, name, err := etcdActive()
		if err != nil {
			return false, err
		}
		if active {
			dlog.Infof("%s is active", name)
			useLock = true
		} else {
			dlog.Infof("%v are inactive", etcdServices)
			useLock = false
		}
	case StrategyEtcdLock:
		useLock = true
	case StrategyReboot:
		useLock = false
	default:
		return false, fmt.Errorf("unknown strategy: %s", strategy)
	}

	return useLock, nil
}

// unlockIfHeld will unlock a lock, if it is held by this machine, or return an error.
func unlockIfHeld(lck *lock.Lock) error {
	err := lck.Unlock()
	if err == lock.ErrNotExist {
		return nil
	} else if err == nil {
		dlog.Info("Unlocked existing lock for this machine")
		return nil
	}

	return err
}

// unlockHeldLocks will loop until it can confirm that any held locks are
// released or a stop signal is sent.
func unlockHeldLocks(strategy string, stop chan struct{}, wg *sync.WaitGroup) {
	defer wg.Done()
	interval := initialInterval
	for {
		var reason string
		select {
		case <-stop:
			return
		case <-time.After(interval):
			// Here we assume that the strategy used by locksmith
			// is consistent with that used before the previous
			// reboot (if any), and use that to decide whether to
			// attempt to unlock.
			shouldUnlock, err := useLock(strategy)
			if err != nil {
				reason = fmt.Sprintf("error checking whether lock should be released: %v", err)
				break
			}
			if !shouldUnlock {
				reason = fmt.Sprintf("%v are inactive", etcdServices)
				break
			}

			lck, err := setupLock()
			if err != nil {
				reason = "error setting up lock: " + err.Error()
				break
			}

			err = unlockIfHeld(lck)
			if err == nil {
				return
			}
			reason = err.Error()
		}

		interval = expBackoff(interval)
		dlog.Errorf("Unlocking old locks failed: %v. Retrying in %v.", reason, interval)
	}
}

// runDaemon waits for the reboot needed signal coming out of update engine and
// attempts to acquire the reboot lock. If the reboot lock is acquired then the
// machine will reboot.
func runDaemon() int {
	var period *timeutil.Periodic

	strategy := os.Getenv("REBOOT_STRATEGY")

	if strategy == "" {
		strategy = StrategyBestEffort
	}

	// XXX: complain loudly if besteffort is used
	if strategy == StrategyBestEffort {
		dlog.Errorf("Reboot strategy %q is deprecated and will be removed in the future.", strategy)
		dlog.Errorf("Please explicitly set the reboot strategy to one of %v", []string{StrategyOff, StrategyReboot, StrategyEtcdLock})
		dlog.Error("See https://coreos.com/os/docs/latest/update-strategies.html for details on configuring reboot strategies.")
	}

	if strategy == StrategyOff {
		dlog.Noticef("Reboot strategy is %q - locksmithd is exiting.", strategy)
		return 0
	}

	// XXX: REBOOT_WINDOW_* are deprecated in favor of variables with LOCKSMITHD_ prefix,
	// but the old ones are read for compatibility.
	startw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_START")
	if startw == "" {
		startw = os.Getenv("REBOOT_WINDOW_START")
	}

	lengthw := os.Getenv("LOCKSMITHD_REBOOT_WINDOW_LENGTH")
	if lengthw == "" {
		lengthw = os.Getenv("REBOOT_WINDOW_LENGTH")
	}

	if (startw == "") != (lengthw == "") {
		dlog.Fatal("Either both or neither $REBOOT_WINDOW_START and $REBOOT_WINDOW_LENGTH must be set")
	}

	if startw != "" && lengthw != "" {
		p, err := timeutil.ParsePeriodic(startw, lengthw)
		if err != nil {
			dlog.Fatalf("Error parsing reboot window: %s", err)
		}

		period = p
	}

	if period != nil {
		dlog.Infof("Reboot window start is %q and length is %q", startw, lengthw)
		next := period.Next(time.Now())
		dlog.Infof("Next window begins at %s and ends at %s", next.Start, next.End)
	} else {
		dlog.Info("No configured reboot window")
	}

	coordinatorConf, err := coordinatorconf.New(coordinatorName, strategy)
	if err != nil {
		dlog.Fatalf("unable to become 'update coordinator': %v", err)
	}

	shutdown := make(chan os.Signal, 1)
	stop := make(chan struct{}, 1)

	go func() {
		<-shutdown
		dlog.Notice("Received interrupt/termination signal - locksmithd is exiting.")
		os.Exit(0)
	}()
	signal.Notify(shutdown, syscall.SIGINT, syscall.SIGTERM)

	ue, err := updateengine.New()
	if err != nil {
		dlog.Fatalf("Error initializing update1 client: %v", err)
	}

	lgn, err := login1.New()
	if err != nil {
		dlog.Fatalf("Error initializing login1 client: %v", err)
	}

	var wg sync.WaitGroup
	if strategy != StrategyReboot {
		wg.Add(1)
		go unlockHeldLocks(strategy, stop, &wg)
	}

	ch := make(chan updateengine.Status, 1)
	go ue.RebootNeededSignal(ch, stop)

	r := rebooter{
		strategy: strategy,
		lgn:      lgn,
		coordinatorConfigUpdater: coordinatorConf,
	}

	result, err := ue.GetStatus()
	if err != nil {
		dlog.Fatalf("Cannot get update engine status: %v", err)
	}

	dlog.Infof("locksmithd starting currentOperation=%q strategy=%q", result.CurrentOperation, strategy)
	if err := r.coordinatorConfigUpdater.UpdateState(coordinatorconf.CoordinatorStateRunning); err != nil {
		dlog.Errorf("could not indicate 'running' in state file: %v", err)
	}

	if result.CurrentOperation != updateengine.UpdateStatusUpdatedNeedReboot {
		<-ch
	}

	close(stop)
	wg.Wait()

	if period != nil {
		now := time.Now()
		sleeptime := period.DurationToStart(now)
		if sleeptime > 0 {
			dlog.Infof("Waiting for %s to reboot.", sleeptime)
			time.Sleep(sleeptime)
		}
	}

	return r.reboot()
}
<|endoftext|>


<commit_before>package gaerecords

import (
	"os"
	"fmt"
	"reflect"
	"appengine"
	"appengine/datastore"
)

// Model struct represents a single model. A model is a class of data and a Model object is used
// to interact with the datastore including reading and writing records of this type.
//
// The NewModel method creates a new model.
// The argument specified is a string description of the type of data, which is used
// in the *datastore.Key for records of this kind.
//  // create a new model for 'people'
//  People := NewModel("people")
//
//  // create a new model for 'books'
//  Books := NewModel("books")
type Model struct {

	// AfterNew gets triggered after a record has been created.
	// Useful for initializing Records.
	//
	//   Args[0] - The *Record that has been created
	//
	AfterNew Event

	// AfterFind gets triggered after a record of this kind has been
	// found.  Useful for any processing of records after they have been loaded.
	// For any operations that cause multiple finds (i.e. All() or FindByQuery()) this event
	// will be triggered once for each record.
	//
	//   Args[0] - The *Record that has been found.
	//
	AfterFind Event

	// BeforeDelete gets triggered before a record is deleted. The Args will
	// always contain the ID of the record being deleted, and depending on the
	// operation, the second argument could be the *Record itself.
	//
	// Setting Cancel to true will cancel the delete operation.
	//
	//   Args[0] - ID (int64) of the record that is about to be deleted.
	//   Args[1] - *Record to be deleted (if available)
	//
	BeforeDelete Event

	// AfterDelete gets triggered after a record has been deleted by ID. The Args will
	// always contain the ID of the record being deleted, and depending on the
	// operation, the second argument could be the *Record itself.
	///
	// The EventContext is the same one that was passed to BeforeDelete.
	//
	//   Args[0] - ID (int64) of the record that was just deleted.
	//   Args[1] - *Record that was deleted (if available)
	//
	AfterDelete Event

	// BeforePut gets triggered before a record gets Put into the datastore.
	// Use Args[0].(*Record).IsPersisted() to find out whether the record is being
	// saved or updated.
	//
	// Setting Cancel to true will prevent the record from being Put
	//
	//   Args[0] - The *Record that is about to be Put
	//
	BeforePut Event

	// AfterPut gets triggered after a record has been Put.
	// The EventContext is the same one that was passed to BeforePut.
	//
	//   Args[0] - The *Record that was just Put
	//
	AfterPut Event

	// OnChanged gets triggered after a record field has been changed
	// using one of the Set*() methods.
	//
	//   Args[0] - The record that changed
	//   Args[1] - The key of the field that changed
	//   Args[2] - The new value of the field
	//   Args[3] - The old value of the field (or nil if it's a new field)
	//
	OnChanged Event

	// recordType is the internal string holding the 'type' of this model,
	// or the kind of data this model works with
	recordType string

	// specificAppengineContext is the internal storage of appengine context to use for this model.
	specificAppengineContext appengine.Context
}

// NewModel creates a new model for data classified by the specified recordType.  You may also
// provide an optional initializer func(*Model) that will be called after the model is created,
// before it is returned, to allow you an opportunity to bind to any events, or perform any other
// initialization.
//
// For example, the following code creates a new Model called 'people':
//
//   People := NewModel("people")
//
// The following code creates a 'people' model, and binds to the 'BeforePut' event
//   People := NewModel("people", func(m *Model){
//	   m.BeforePut.On(func(e *EventContext){
//		   // do something to records just before they are
//		   // persisted in the datastore
//	   })
//	 })
func NewModel(recordType string, initializer ...func(*Model)) *Model {

	model := new(Model)

	model.recordType = recordType

	// do we have an initializer?
	if len(initializer) == 1 {

		// let it initialize the model
		initializer[0](model)

	}

	return model

}

// New creates a new record of this type.
//   people := NewModel("people")
//   person1 := people.New()
//   person2 := people.New()
func (m *Model) New() *Record {
	return NewRecord(m)
}

// RecordType gets the record type of the model as a string.  This is the string you specify
// when calling NewModel(string) and is used as the Kind in the datasource keys.
func (m *Model) RecordType() string {
	return m.recordType
}

// String gets a human readable string representation of this model.
func (m *Model) String() string {
	return fmt.Sprintf("{Model:%v}", m.RecordType())
}

/*
	AppEngine Context
	----------------------------------------------------------------------
*/

// AppEngineContext gets the appengine.Context to use for datastore interactions for this model.
// If a specific one has been provided (via Model.SetAppEngineContext()) that
// context is used, otherwise the global AppEngineContext object is returned.
func (m *Model) AppEngineContext() appengine.Context {

	// do we have a specific model context?
	if m.specificAppengineContext == nil {

		// use the global one
		return AppEngineContext

	}

	// use the specific one
	return m.specificAppengineContext

}

// SetAppEngineContext tells this model to use the specified appengine.Context instead of the global
// AppEngineContext object for its interactions with the datastore.
func (m *Model) SetAppEngineContext(context appengine.Context) *Model {

	// set the context
	m.specificAppengineContext = context

	// chain
	return m
}

// UseGlobalAppEngineContext tells this model to use the global AppEngineContext object for its interactions with the datastore,
// instead of one provided by Model.SetAppEngineContext().
func (m *Model) UseGlobalAppEngineContext() *Model {

	// set the model specific context to nil so it uses the
	// global one when Model.AppEngineContext() is called.
	m.SetAppEngineContext(nil)

	// chain
	return m
}

/*
	Persistence
	----------------------------------------------------------------------
*/

// Count returns the number of records in the datastore for this model type.
//
// You can pass an optional query modifier func (of type func(*datastore.Query))
// that will be called before the query is run to allow you to modify the
// records being counted.  If you do not provide this argument, all records of this
// type will be counted.
func (m *Model) Count(queryModifier ...func(*datastore.Query)) (int, os.Error) {

	// create a query
	query := m.NewQuery()

	// let the modifier do its work if there is one
	if len(queryModifier) == 1 {
		queryModifier[0](query)
	}

	return query.Count(m.AppEngineContext())

}

// LoadPagingInfo gets the paging information in a PagingInfo object, for records of this type.
//
//   recordsPerPage - the number of records per page
//   currentPage - the current page number
//
// You can pass an optional query modifier func (of type func(*datastore.Query))
// that will be called before the query is run to allow you to modify the
// records being counted.  If you do not provide this argument, all records of this
// type will be counted.
//
// This method panics if an error occurs when counting records.
func (m *Model) LoadPagingInfo(recordsPerPage, currentPage int, queryModifier ...func(*datastore.Query)) PagingInfo {

	var count int
	var err os.Error

	if len(queryModifier) == 1 {
		count, err = m.Count(queryModifier[0])
	} else {
		count, err = m.Count()
	}

	if err != nil {
		panic(fmt.Sprintf("gaerecords: LaodPagingInfo: %v", err))
	}

	return NewPagingInfo(count, recordsPerPage, currentPage)

}

// Find finds the record of this type with the specified id.
//  people := NewModel("people")
//  firstPerson := people.Find(1)
//
// Raises events:
//   Model.AfterFind with Args(record)
func (m *Model) Find(id int64) (*Record, os.Error) {

	key := m.NewKeyWithID(id)

	var record *Record = new(Record)

	err := datastore.Get(m.AppEngineContext(), key, datastore.PropertyLoadSaver(record))

	if err == nil {

		// setup the record object
		record.configureRecord(m, key)

		// raise the AfterFind event on the model
		m.AfterFind.Trigger(record)

		// return the record
		return record, nil

	}

	return nil, err

}

// FindAll finds all records of this type.
//   people := NewModel("people")
//   everyone := people.All()
//
// Raises events for each record:
//   Model.AfterFind with Args(record)
func (m *Model) FindAll() ([]*Record, os.Error) {
	return m.FindByQuery(m.NewQuery())
}

// FindByField finds records of this Model's type where the filterString (e.g. FieldName=) matches
// the specified value.  To add multiple filters, use FindByQuery() instead.
//
// You can pass an optional query modifier func (of type func(*datastore.Query)) that will be called
// before the query is run to allow you to add additional properties to the query.
//
// For valid filter strings, see http://code.google.com/appengine/docs/go/datastore/reference.html#Query.Filter
//
//  // get everyone over 60 years old
//  oldPeople, _ := People.FindByField("Age>", 60)
//
//  // get one person over 60
//  oldMan, _ := People.FindByField("Age>", 60, func(q *datastore.Query){
//	  q.Limit(1)
//  })
func (m *Model) FindByField(filterString string, value interface{}, queryModifier ...func(*datastore.Query)) ([]*Record, os.Error) {

	query := m.NewQuery().Filter(filterString, value)

	// let the modifier do its work if there is one
	if len(queryModifier) == 1 {
		queryModifier[0](query)
	}

	return m.FindByQuery(query)

}

// FindByPage finds records a page at a time.
//
// pageNumber int - The page number to get (with 1 being the first page)
// recordsPerPage int - The number of records per page.  Usually this is the number of records returned
// unless you reach the end of the set.
// queryModifier func(*datastore.Query) - You can pass an optional query modifier func (of type func(*datastore.Query))
// that will be called before the query is run to allow you to add additional properties to the query.
//
// If you alter the Limit or Offset properties of the Query the paging behaviour will not work as
// expected.
//
// Useful when used in conjunction with LoadPagingInfo().
func (m *Model) FindByPage(pageNumber, recordsPerPage int, queryModifier ...func(*datastore.Query)) ([]*Record, os.Error) {

	query := m.NewQuery().
		Limit(recordsPerPage).
		Offset((pageNumber - 1) * recordsPerPage)

	// let the modifier do its work if there is one
	if len(queryModifier) == 1 {
		queryModifier[0](query)
	}

	return m.FindByQuery(query)

}

/*
	Queries
	----------------------------------------------------------------------
*/

// NewQuery creates a new datastore.Query for accessing records represented
// by the model.  For advanced use only.  Consider instead one of the
// Find* methods.
func (m *Model) NewQuery() *datastore.Query {
	return datastore.NewQuery(m.RecordType())
}

// FindByQuery finds Records handled by this Model.
//
// Returns an array of records as the first argument,
// or an error as the second return argument.
//
// The queryOrFunc argument may be one of:
//
//   *datastore.Query
// The specified query will be used to find records.
//   func(*datastore.Query)
// A new query will be created and the specified function will be
// used to further configure the query.
//
// Example:
//  model := NewModel("people")
//  women, err := model.FindByQuery(func(q *datastore.Query){
//	  q.Filter("sex=", "male")
//  })
//
// bug(matryer): Currently, filters on queries are not working. See https://github.com/matryer/gae-records/issues/2
func (m *Model) FindByQuery(queryOrFunc interface{}) ([]*Record, os.Error) {

	var query *datastore.Query

	if reflect.TypeOf(queryOrFunc).Kind() == reflect.Func {

		// create a new query
		query = m.NewQuery()

		// ask the func to configure the query
		queryOrFunc.(func(*datastore.Query))(query)

	} else {

		// just use the query
		query = queryOrFunc.(*datastore.Query)

	}

	var records []*Record
	keys, err := query.GetAll(m.AppEngineContext(), &records)

	if err == nil {

		// update the key for each loaded record
		for index, record := range records {
			record.configureRecord(m, keys[index])
			m.AfterFind.Trigger(record)
		}

		return records, nil

	}

	return nil, err

}

// Delete deletes a single record of this type.  Returns nil if successful, otherwise
// the datastore error that was returned.
//   people := NewModel("people")
//   people.Delete(1)
//
// Raises events:
//   Model.BeforeDelete with Args(id, nil)
//   Model.AfterDelete with Args(id, nil)
// Note: The Record will not be passed to the events.
func (m *Model) Delete(id int64) os.Error {

	// trigger the BeforeDeleteByID event
	context := m.BeforeDelete.Trigger(id, nil)

	if !context.Cancel {

		err := datastore.Delete(m.AppEngineContext(), m.NewKeyWithID(id))

		if err == nil {

			// trigger the AfterDeleteByID event
			m.AfterDelete.TriggerWithContext(context)

		}

		// return the error
		return err

	}

	return ErrOperationCancelledByEventCallback

}

/*
	datastore.Keys
	----------------------------------------------------------------------
*/

// NewKey creates a new datastore Key for this kind of record.
func (m *Model) NewKey() *datastore.Key {
	return datastore.NewIncompleteKey(m.AppEngineContext(), m.recordType, nil)
}

// NewKeyWithID creates a new datastore Key for this kind of record with the specified ID.
func (m *Model) NewKeyWithID(id int64) *datastore.Key {
	return datastore.NewKey(m.AppEngineContext(), m.recordType, "", int64(id), nil)
}
<commit_msg>removed bug comment<commit_after>package gaerecords

import (
	"os"
	"fmt"
	"reflect"
	"appengine"
	"appengine/datastore"
)

// Model struct represents a single model. A model is a class of data and a Model object is used
// to interact with the datastore including reading and writing records of this type.
//
// The NewModel method creates a new model.
// The argument specified is a string description of the type of data, which is used
// in the *datastore.Key for records of this kind.
//  // create a new model for 'people'
//  People := NewModel("people")
//
//  // create a new model for 'books'
//  Books := NewModel("books")
type Model struct {

	// AfterNew gets triggered after a record has been created.
	// Useful for initializing Records.
	//
	//   Args[0] - The *Record that has been created
	//
	AfterNew Event

	// AfterFind gets triggered after a record of this kind has been
	// found.  Useful for any processing of records after they have been loaded.
	// For any operations that cause multiple finds (i.e. All() or FindByQuery()) this event
	// will be triggered once for each record.
	//
	//   Args[0] - The *Record that has been found.
	//
	AfterFind Event

	// BeforeDelete gets triggered before a record is deleted. The Args will
	// always contain the ID of the record being deleted, and depending on the
	// operation, the second argument could be the *Record itself.
	//
	// Setting Cancel to true will cancel the delete operation.
	//
	//   Args[0] - ID (int64) of the record that is about to be deleted.
	//   Args[1] - *Record to be deleted (if available)
	//
	BeforeDelete Event

	// AfterDelete gets triggered after a record has been deleted by ID. The Args will
	// always contain the ID of the record being deleted, and depending on the
	// operation, the second argument could be the *Record itself.
	///
	// The EventContext is the same one that was passed to BeforeDelete.
	//
	//   Args[0] - ID (int64) of the record that was just deleted.
	//   Args[1] - *Record that was deleted (if available)
	//
	AfterDelete Event

	// BeforePut gets triggered before a record gets Put into the datastore.
	// Use Args[0].(*Record).IsPersisted() to find out whether the record is being
	// saved or updated.
	//
	// Setting Cancel to true will prevent the record from being Put
	//
	//   Args[0] - The *Record that is about to be Put
	//
	BeforePut Event

	// AfterPut gets triggered after a record has been Put.
	// The EventContext is the same one that was passed to BeforePut.
	//
	//   Args[0] - The *Record that was just Put
	//
	AfterPut Event

	// OnChanged gets triggered after a record field has been changed
	// using one of the Set*() methods.
	//
	//   Args[0] - The record that changed
	//   Args[1] - The key of the field that changed
	//   Args[2] - The new value of the field
	//   Args[3] - The old value of the field (or nil if it's a new field)
	//
	OnChanged Event

	// recordType is the internal string holding the 'type' of this model,
	// or the kind of data this model works with
	recordType string

	// specificAppengineContext is the internal storage of appengine context to use for this model.
	specificAppengineContext appengine.Context
}

// NewModel creates a new model for data classified by the specified recordType.  You may also
// provide an optional initializer func(*Model) that will be called after the model is created,
// before it is returned, to allow you an opportunity to bind to any events, or perform any other
// initialization.
//
// For example, the following code creates a new Model called 'people':
//
//   People := NewModel("people")
//
// The following code creates a 'people' model, and binds to the 'BeforePut' event
//   People := NewModel("people", func(m *Model){
//	   m.BeforePut.On(func(e *EventContext){
//		   // do something to records just before they are
//		   // persisted in the datastore
//	   })
//	 })
func NewModel(recordType string, initializer ...func(*Model)) *Model {

	model := new(Model)

	model.recordType = recordType

	// do we have an initializer?
	if len(initializer) == 1 {

		// let it initialize the model
		initializer[0](model)

	}

	return model

}

// New creates a new record of this type.
//   people := NewModel("people")
//   person1 := people.New()
//   person2 := people.New()
func (m *Model) New() *Record {
	return NewRecord(m)
}

// RecordType gets the record type of the model as a string.  This is the string you specify
// when calling NewModel(string) and is used as the Kind in the datasource keys.
func (m *Model) RecordType() string {
	return m.recordType
}

// String gets a human readable string representation of this model.
func (m *Model) String() string {
	return fmt.Sprintf("{Model:%v}", m.RecordType())
}

/*
	AppEngine Context
	----------------------------------------------------------------------
*/

// AppEngineContext gets the appengine.Context to use for datastore interactions for this model.
// If a specific one has been provided (via Model.SetAppEngineContext()) that
// context is used, otherwise the global AppEngineContext object is returned.
func (m *Model) AppEngineContext() appengine.Context {

	// do we have a specific model context?
	if m.specificAppengineContext == nil {

		// use the global one
		return AppEngineContext

	}

	// use the specific one
	return m.specificAppengineContext

}

// SetAppEngineContext tells this model to use the specified appengine.Context instead of the global
// AppEngineContext object for its interactions with the datastore.
func (m *Model) SetAppEngineContext(context appengine.Context) *Model {

	// set the context
	m.specificAppengineContext = context

	// chain
	return m
}

// UseGlobalAppEngineContext tells this model to use the global AppEngineContext object for its interactions with the datastore,
// instead of one provided by Model.SetAppEngineContext().
func (m *Model) UseGlobalAppEngineContext() *Model {

	// set the model specific context to nil so it uses the
	// global one when Model.AppEngineContext() is called.
	m.SetAppEngineContext(nil)

	// chain
	return m
}

/*
	Persistence
	----------------------------------------------------------------------
*/

// Count returns the number of records in the datastore for this model type.
//
// You can pass an optional query modifier func (of type func(*datastore.Query))
// that will be called before the query is run to allow you to modify the
// records being counted.  If you do not provide this argument, all records of this
// type will be counted.
func (m *Model) Count(queryModifier ...func(*datastore.Query)) (int, os.Error) {

	// create a query
	query := m.NewQuery()

	// let the modifier do its work if there is one
	if len(queryModifier) == 1 {
		queryModifier[0](query)
	}

	return query.Count(m.AppEngineContext())

}

// LoadPagingInfo gets the paging information in a PagingInfo object, for records of this type.
//
//   recordsPerPage - the number of records per page
//   currentPage - the current page number
//
// You can pass an optional query modifier func (of type func(*datastore.Query))
// that will be called before the query is run to allow you to modify the
// records being counted.  If you do not provide this argument, all records of this
// type will be counted.
//
// This method panics if an error occurs when counting records.
func (m *Model) LoadPagingInfo(recordsPerPage, currentPage int, queryModifier ...func(*datastore.Query)) PagingInfo {

	var count int
	var err os.Error

	if len(queryModifier) == 1 {
		count, err = m.Count(queryModifier[0])
	} else {
		count, err = m.Count()
	}

	if err != nil {
		panic(fmt.Sprintf("gaerecords: LaodPagingInfo: %v", err))
	}

	return NewPagingInfo(count, recordsPerPage, currentPage)

}

// Find finds the record of this type with the specified id.
//  people := NewModel("people")
//  firstPerson := people.Find(1)
//
// Raises events:
//   Model.AfterFind with Args(record)
func (m *Model) Find(id int64) (*Record, os.Error) {

	key := m.NewKeyWithID(id)

	var record *Record = new(Record)

	err := datastore.Get(m.AppEngineContext(), key, datastore.PropertyLoadSaver(record))

	if err == nil {

		// setup the record object
		record.configureRecord(m, key)

		// raise the AfterFind event on the model
		m.AfterFind.Trigger(record)

		// return the record
		return record, nil

	}

	return nil, err

}

// FindAll finds all records of this type.
//   people := NewModel("people")
//   everyone := people.All()
//
// Raises events for each record:
//   Model.AfterFind with Args(record)
func (m *Model) FindAll() ([]*Record, os.Error) {
	return m.FindByQuery(m.NewQuery())
}

// FindByField finds records of this Model's type where the filterString (e.g. FieldName=) matches
// the specified value.  To add multiple filters, use FindByQuery() instead.
//
// You can pass an optional query modifier func (of type func(*datastore.Query)) that will be called
// before the query is run to allow you to add additional properties to the query.
//
// For valid filter strings, see http://code.google.com/appengine/docs/go/datastore/reference.html#Query.Filter
//
//  // get everyone over 60 years old
//  oldPeople, _ := People.FindByField("Age>", 60)
//
//  // get one person over 60
//  oldMan, _ := People.FindByField("Age>", 60, func(q *datastore.Query){
//	  q.Limit(1)
//  })
func (m *Model) FindByField(filterString string, value interface{}, queryModifier ...func(*datastore.Query)) ([]*Record, os.Error) {

	query := m.NewQuery().Filter(filterString, value)

	// let the modifier do its work if there is one
	if len(queryModifier) == 1 {
		queryModifier[0](query)
	}

	return m.FindByQuery(query)

}

// FindByPage finds records a page at a time.
//
// pageNumber int - The page number to get (with 1 being the first page)
// recordsPerPage int - The number of records per page.  Usually this is the number of records returned
// unless you reach the end of the set.
// queryModifier func(*datastore.Query) - You can pass an optional query modifier func (of type func(*datastore.Query))
// that will be called before the query is run to allow you to add additional properties to the query.
//
// If you alter the Limit or Offset properties of the Query the paging behaviour will not work as
// expected.
//
// Useful when used in conjunction with LoadPagingInfo().
func (m *Model) FindByPage(pageNumber, recordsPerPage int, queryModifier ...func(*datastore.Query)) ([]*Record, os.Error) {

	query := m.NewQuery().
		Limit(recordsPerPage).
		Offset((pageNumber - 1) * recordsPerPage)

	// let the modifier do its work if there is one
	if len(queryModifier) == 1 {
		queryModifier[0](query)
	}

	return m.FindByQuery(query)

}

/*
	Queries
	----------------------------------------------------------------------
*/

// NewQuery creates a new datastore.Query for accessing records represented
// by the model.  For advanced use only.  Consider instead one of the
// Find* methods.
func (m *Model) NewQuery() *datastore.Query {
	return datastore.NewQuery(m.RecordType())
}

// FindByQuery finds Records handled by this Model.
//
// Returns an array of records as the first argument,
// or an error as the second return argument.
//
// The queryOrFunc argument may be one of:
//
//   *datastore.Query
// The specified query will be used to find records.
//   func(*datastore.Query)
// A new query will be created and the specified function will be
// used to further configure the query.
//
// Example:
//  model := NewModel("people")
//  women, err := model.FindByQuery(func(q *datastore.Query){
//	  q.Filter("sex=", "male")
//  })
func (m *Model) FindByQuery(queryOrFunc interface{}) ([]*Record, os.Error) {

	var query *datastore.Query

	if reflect.TypeOf(queryOrFunc).Kind() == reflect.Func {

		// create a new query
		query = m.NewQuery()

		// ask the func to configure the query
		queryOrFunc.(func(*datastore.Query))(query)

	} else {

		// just use the query
		query = queryOrFunc.(*datastore.Query)

	}

	var records []*Record
	keys, err := query.GetAll(m.AppEngineContext(), &records)

	if err == nil {

		// update the key for each loaded record
		for index, record := range records {
			record.configureRecord(m, keys[index])
			m.AfterFind.Trigger(record)
		}

		return records, nil

	}

	return nil, err

}

// Delete deletes a single record of this type.  Returns nil if successful, otherwise
// the datastore error that was returned.
//   people := NewModel("people")
//   people.Delete(1)
//
// Raises events:
//   Model.BeforeDelete with Args(id, nil)
//   Model.AfterDelete with Args(id, nil)
// Note: The Record will not be passed to the events.
func (m *Model) Delete(id int64) os.Error {

	// trigger the BeforeDeleteByID event
	context := m.BeforeDelete.Trigger(id, nil)

	if !context.Cancel {

		err := datastore.Delete(m.AppEngineContext(), m.NewKeyWithID(id))

		if err == nil {

			// trigger the AfterDeleteByID event
			m.AfterDelete.TriggerWithContext(context)

		}

		// return the error
		return err

	}

	return ErrOperationCancelledByEventCallback

}

/*
	datastore.Keys
	----------------------------------------------------------------------
*/

// NewKey creates a new datastore Key for this kind of record.
func (m *Model) NewKey() *datastore.Key {
	return datastore.NewIncompleteKey(m.AppEngineContext(), m.recordType, nil)
}

// NewKeyWithID creates a new datastore Key for this kind of record with the specified ID.
func (m *Model) NewKeyWithID(id int64) *datastore.Key {
	return datastore.NewKey(m.AppEngineContext(), m.recordType, "", int64(id), nil)
}
<|endoftext|>


<commit_before>package types

import (
	// Stdlib
	"bytes"
	"encoding/json"
	"reflect"

	// Vendor
	"github.com/asuleymanov/golos-go/encoding/transaction"
	"github.com/pkg/errors"
)

// dataObjects keeps mapping operation type -> operation data object.
// This is used later on to unmarshal operation data based on the operation type.
var dataObjects = map[OpType]Operation{
	TypeVote:                        &VoteOperation{},
	TypeComment:                     &CommentOperation{},
	TypeTransfer:                    &TransferOperation{},
	TypeTransferToVesting:           &TransferToVestingOperation{},
	TypeWithdrawVesting:             &WithdrawVestingOperation{},
	TypeLimitOrderCreate:            &LimitOrderCreateOperation{},
	TypeLimitOrderCancel:            &LimitOrderCancelOperation{},
	TypeFeedPublish:                 &FeedPublishOperation{},
	TypeConvert:                     &ConvertOperation{},
	TypeAccountCreate:               &AccountCreateOperation{},
	TypeAccountUpdate:               &AccountUpdateOperation{},
	TypeWitnessUpdate:               &WitnessUpdateOperation{},
	TypeAccountWitnessVote:          &AccountWitnessVoteOperation{},
	TypeAccountWitnessProxy:         &AccountWitnessProxyOperation{},
	TypePOW:                         &POWOperation{},
	TypeCustom:                      &CustomOperation{},
	TypeReportOverProduction:        &ReportOverProductionOperation{},
	TypeDeleteComment:               &DeleteCommentOperation{},
	TypeCustomJSON:                  &CustomJSONOperation{},
	TypeCommentOptions:              &CommentOptionsOperation{},
	TypeSetWithdrawVestingRoute:     &SetWithdrawVestingRouteOperation{},
	TypeLimitOrderCreate2:           &LimitOrderCreate2Operation{},
	TypeChallengeAuthority:          &ChallengeAuthorityOperation{},
	TypeProveAuthority:              &ProveAuthorityOperation{},
	TypeRequestAccountRecovery:      &RequestAccountRecoveryOperation{},
	TypeRecoverAccount:              &RecoverAccountOperation{},
	TypeChangeRecoveryAccount:       &ChangeRecoveryAccountOperation{},
	TypeEscrowTransfer:              &EscrowTransferOperation{},
	TypeEscrowDispute:               &EscrowDisputeOperation{},
	TypeEscrowRelease:               &EscrowReleaseOperation{},
	TypePOW2:                        &POW2Operation{},
	TypeEscrowApprove:               &EscrowApproveOperation{},
	TypeTransferToSavings:           &TransferToSavingsOperation{},
	TypeTransferFromSavings:         &TransferFromSavingsOperation{},
	TypeCancelTransferFromSavings:   &CancelTransferFromSavingsOperation{},
	TypeCustomBinary:                &CustomBinaryOperation{},
	TypeDeclineVotingRights:         &DeclineVotingRightsOperation{},
	TypeResetAccount:                &ResetAccountOperation{},
	TypeSetResetAccount:             &SetResetAccountOperation{},
	TypeDelegateVestingShares:       &DelegateVestingSharesOperation{},
	TypeAccountCreateWithDelegation: &AccountCreateWithDelegationOperation{},
	TypeAccountMetadata:             &AccountMetadataOperation{},
	TypeProposalCreate:              &ProposalCreateOperation{},
	TypeProposalUpdate:              &ProposalUpdateOperation{},
	TypeProposalDelete:              &ProposalDeleteOperation{},
	TypeChainPropertiesUpdate:       &ChainPropertiesUpdateOperation{},
	TypeFillConvertRequest:          &FillConvertRequestOperation{},
	TypeAuthorReward:                &AuthorRewardOperation{},
	TypeCurationReward:              &CurationRewardOperation{},
	TypeCommentReward:               &CommentRewardOperation{},
	TypeLiquidityReward:             &LiquidityRewardOperation{},
	TypeInterest:                    &InterestOperation{},
	TypeFillVestingWithdraw:         &FillVestingWithdrawOperation{},
	TypeFillOrder:                   &FillOrderOperation{},
	TypeShutdownWitness:             &ShutdownWitnessOperation{},
	TypeFillTransferFromSavings:     &FillTransferFromSavingsOperation{},
	TypeHardfork:                    &HardforkOperation{},
	TypeCommentPayoutUpdate:         &CommentPayoutUpdateOperation{},
	TypeCommentBenefactorReward:     &CommentBenefactorRewardOperation{},
	TypeReturnVestingDelegation:     &ReturnVestingDelegationOperation{},
}

// Operation represents an operation stored in a transaction.
type Operation interface {
	// Type returns the operation type as present in the operation object, element [0].
	Type() OpType

	// Data returns the operation data as present in the operation object, element [1].
	//
	// When the operation type is known to this package, this field contains
	// the operation data object associated with the given operation type,
	// e.g. Type is TypeVote -> Data contains *VoteOperation.
	// Otherwise this field contains raw JSON (type *json.RawMessage).
	Data() interface{}
}

type Operations []Operation

func (ops *Operations) UnmarshalJSON(data []byte) error {
	var tuples []*operationTuple
	if err := json.Unmarshal(data, &tuples); err != nil {
		return err
	}

	items := make([]Operation, 0, len(tuples))
	for _, tuple := range tuples {
		items = append(items, tuple.Data)
	}

	*ops = items
	return nil
}

func (ops Operations) MarshalJSON() ([]byte, error) {
	tuples := make([]*operationTuple, 0, len(ops))
	for _, op := range ops {
		tuples = append(tuples, &operationTuple{
			Type: op.Type(),
			Data: op.Data().(Operation),
		})
	}
	return JSONMarshal(tuples)
}

type operationTuple struct {
	Type OpType
	Data Operation
}

func (op *operationTuple) MarshalJSON() ([]byte, error) {
	return JSONMarshal([]interface{}{
		op.Type,
		op.Data,
	})
}

func JSONMarshal(t interface{}) ([]byte, error) {
	buffer := &bytes.Buffer{}
	encoder := json.NewEncoder(buffer)
	encoder.SetEscapeHTML(false)
	err := encoder.Encode(t)
	return buffer.Bytes(), err
}

func (op *operationTuple) UnmarshalJSON(data []byte) error {
	// The operation object is [opType, opBody].
	raw := make([]*json.RawMessage, 2)
	if err := json.Unmarshal(data, &raw); err != nil {
		return errors.Wrapf(err, "failed to unmarshal operation object: %v", string(data))
	}
	if len(raw) != 2 {
		return errors.Errorf("invalid operation object: %v", string(data))
	}

	// Unmarshal the type.
	var opType OpType
	if err := json.Unmarshal(*raw[0], &opType); err != nil {
		return errors.Wrapf(err, "failed to unmarshal Operation.Type: %v", string(*raw[0]))
	}

	// Unmarshal the data.
	var opData Operation
	template, ok := dataObjects[opType]
	if ok {
		opData = reflect.New(
			reflect.Indirect(reflect.ValueOf(template)).Type(),
		).Interface().(Operation)

		if err := json.Unmarshal(*raw[1], opData); err != nil {
			return errors.Wrapf(err, "failed to unmarshal Operation.Data: %v", string(*raw[1]))
		}
	} else {
		opData = &UnknownOperation{opType, raw[1]}
	}

	// Update fields.
	op.Type = opType
	op.Data = opData
	return nil
}

func (ops Operations) MarshalTransaction(encoder *transaction.Encoder) error {
	enc := transaction.NewRollingEncoder(encoder)

	for _, val := range ops {
		enc.Encode(val)
	}

	return enc.Err()
}
<commit_msg>Update operation.go<commit_after>package types

import (
	// Stdlib
	"bytes"
	"encoding/json"
	"reflect"

	// Vendor
	"github.com/pkg/errors"
)

// dataObjects keeps mapping operation type -> operation data object.
// This is used later on to unmarshal operation data based on the operation type.
var dataObjects = map[OpType]Operation{
	TypeVote:                        &VoteOperation{},
	TypeComment:                     &CommentOperation{},
	TypeTransfer:                    &TransferOperation{},
	TypeTransferToVesting:           &TransferToVestingOperation{},
	TypeWithdrawVesting:             &WithdrawVestingOperation{},
	TypeLimitOrderCreate:            &LimitOrderCreateOperation{},
	TypeLimitOrderCancel:            &LimitOrderCancelOperation{},
	TypeFeedPublish:                 &FeedPublishOperation{},
	TypeConvert:                     &ConvertOperation{},
	TypeAccountCreate:               &AccountCreateOperation{},
	TypeAccountUpdate:               &AccountUpdateOperation{},
	TypeWitnessUpdate:               &WitnessUpdateOperation{},
	TypeAccountWitnessVote:          &AccountWitnessVoteOperation{},
	TypeAccountWitnessProxy:         &AccountWitnessProxyOperation{},
	TypePOW:                         &POWOperation{},
	TypeCustom:                      &CustomOperation{},
	TypeReportOverProduction:        &ReportOverProductionOperation{},
	TypeDeleteComment:               &DeleteCommentOperation{},
	TypeCustomJSON:                  &CustomJSONOperation{},
	TypeCommentOptions:              &CommentOptionsOperation{},
	TypeSetWithdrawVestingRoute:     &SetWithdrawVestingRouteOperation{},
	TypeLimitOrderCreate2:           &LimitOrderCreate2Operation{},
	TypeChallengeAuthority:          &ChallengeAuthorityOperation{},
	TypeProveAuthority:              &ProveAuthorityOperation{},
	TypeRequestAccountRecovery:      &RequestAccountRecoveryOperation{},
	TypeRecoverAccount:              &RecoverAccountOperation{},
	TypeChangeRecoveryAccount:       &ChangeRecoveryAccountOperation{},
	TypeEscrowTransfer:              &EscrowTransferOperation{},
	TypeEscrowDispute:               &EscrowDisputeOperation{},
	TypeEscrowRelease:               &EscrowReleaseOperation{},
	TypePOW2:                        &POW2Operation{},
	TypeEscrowApprove:               &EscrowApproveOperation{},
	TypeTransferToSavings:           &TransferToSavingsOperation{},
	TypeTransferFromSavings:         &TransferFromSavingsOperation{},
	TypeCancelTransferFromSavings:   &CancelTransferFromSavingsOperation{},
	TypeCustomBinary:                &CustomBinaryOperation{},
	TypeDeclineVotingRights:         &DeclineVotingRightsOperation{},
	TypeResetAccount:                &ResetAccountOperation{},
	TypeSetResetAccount:             &SetResetAccountOperation{},
	TypeDelegateVestingShares:       &DelegateVestingSharesOperation{},
	TypeAccountCreateWithDelegation: &AccountCreateWithDelegationOperation{},
	TypeAccountMetadata:             &AccountMetadataOperation{},
	TypeProposalCreate:              &ProposalCreateOperation{},
	TypeProposalUpdate:              &ProposalUpdateOperation{},
	TypeProposalDelete:              &ProposalDeleteOperation{},
	TypeChainPropertiesUpdate:       &ChainPropertiesUpdateOperation{},
	TypeFillConvertRequest:          &FillConvertRequestOperation{},
	TypeAuthorReward:                &AuthorRewardOperation{},
	TypeCurationReward:              &CurationRewardOperation{},
	TypeCommentReward:               &CommentRewardOperation{},
	TypeLiquidityReward:             &LiquidityRewardOperation{},
	TypeInterest:                    &InterestOperation{},
	TypeFillVestingWithdraw:         &FillVestingWithdrawOperation{},
	TypeFillOrder:                   &FillOrderOperation{},
	TypeShutdownWitness:             &ShutdownWitnessOperation{},
	TypeFillTransferFromSavings:     &FillTransferFromSavingsOperation{},
	TypeHardfork:                    &HardforkOperation{},
	TypeCommentPayoutUpdate:         &CommentPayoutUpdateOperation{},
	TypeCommentBenefactorReward:     &CommentBenefactorRewardOperation{},
	TypeReturnVestingDelegation:     &ReturnVestingDelegationOperation{},
}

// Operation represents an operation stored in a transaction.
type Operation interface {
	// Type returns the operation type as present in the operation object, element [0].
	Type() OpType

	// Data returns the operation data as present in the operation object, element [1].
	//
	// When the operation type is known to this package, this field contains
	// the operation data object associated with the given operation type,
	// e.g. Type is TypeVote -> Data contains *VoteOperation.
	// Otherwise this field contains raw JSON (type *json.RawMessage).
	Data() interface{}
}

type Operations []Operation

func (ops *Operations) UnmarshalJSON(data []byte) error {
	var tuples []*operationTuple
	if err := json.Unmarshal(data, &tuples); err != nil {
		return err
	}

	items := make([]Operation, 0, len(tuples))
	for _, tuple := range tuples {
		items = append(items, tuple.Data)
	}

	*ops = items
	return nil
}

func (ops Operations) MarshalJSON() ([]byte, error) {
	tuples := make([]*operationTuple, 0, len(ops))
	for _, op := range ops {
		tuples = append(tuples, &operationTuple{
			Type: op.Type(),
			Data: op.Data().(Operation),
		})
	}
	return JSONMarshal(tuples)
}

type operationTuple struct {
	Type OpType
	Data Operation
}

func (op *operationTuple) MarshalJSON() ([]byte, error) {
	return JSONMarshal([]interface{}{
		op.Type,
		op.Data,
	})
}

func JSONMarshal(t interface{}) ([]byte, error) {
	buffer := &bytes.Buffer{}
	encoder := json.NewEncoder(buffer)
	encoder.SetEscapeHTML(false)
	err := encoder.Encode(t)
	return buffer.Bytes(), err
}

func (op *operationTuple) UnmarshalJSON(data []byte) error {
	// The operation object is [opType, opBody].
	raw := make([]*json.RawMessage, 2)
	if err := json.Unmarshal(data, &raw); err != nil {
		return errors.Wrapf(err, "failed to unmarshal operation object: %v", string(data))
	}
	if len(raw) != 2 {
		return errors.Errorf("invalid operation object: %v", string(data))
	}

	// Unmarshal the type.
	var opType OpType
	if err := json.Unmarshal(*raw[0], &opType); err != nil {
		return errors.Wrapf(err, "failed to unmarshal Operation.Type: %v", string(*raw[0]))
	}

	// Unmarshal the data.
	var opData Operation
	template, ok := dataObjects[opType]
	if ok {
		opData = reflect.New(
			reflect.Indirect(reflect.ValueOf(template)).Type(),
		).Interface().(Operation)

		if err := json.Unmarshal(*raw[1], opData); err != nil {
			return errors.Wrapf(err, "failed to unmarshal Operation.Data: %v", string(*raw[1]))
		}
	} else {
		opData = &UnknownOperation{opType, raw[1]}
	}

	// Update fields.
	op.Type = opType
	op.Data = opData
	return nil
}
<|endoftext|>


<commit_before>package types

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	wire "github.com/tendermint/go-wire"
)

func exampleVote() *Vote {
	var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z")
	if err != nil {
		panic(err)
	}

	return &Vote{
		ValidatorAddress: []byte("addr"),
		ValidatorIndex:   56789,
		Height:           12345,
		Round:            2,
		Timestamp:        stamp,
		Type:             byte(2),
		BlockID: BlockID{
			Hash: []byte("hash"),
			PartsHeader: PartSetHeader{
				Total: 1000000,
				Hash:  []byte("parts_hash"),
			},
		},
	}
}

func TestVoteSignable(t *testing.T) {
	vote := exampleVote()
	signBytes := SignBytes("test_chain_id", vote)
	signStr := string(signBytes)

	expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":2,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}`
	if signStr != expected {
		// NOTE: when this fails, you probably want to fix up consensus/replay_test too
		t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}

func TestVoteString(t *testing.T) {
	str := exampleVote().String()
	expected := `Vote{56789:616464720000 12345/02/2(Precommit) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`
	if str != expected {
		t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", expected, str)
	}
}

func TestVoteVerifySignature(t *testing.T) {
	privVal := GenPrivValidatorFS("")
	pubKey := privVal.GetPubKey()

	vote := exampleVote()
	signBytes := SignBytes("test_chain_id", vote)

	// sign it
	signature, err := privVal.Signer.Sign(signBytes)
	require.NoError(t, err)

	// verify the same vote
	valid := pubKey.VerifyBytes(SignBytes("test_chain_id", vote), signature)
	require.True(t, valid)

	// serialize, deserialize and verify again....
	precommit := new(Vote)
	bs := wire.BinaryBytes(vote)
	err = wire.ReadBinaryBytes(bs, &precommit)
	require.NoError(t, err)

	// verify the transmitted vote
	newSignBytes := SignBytes("test_chain_id", precommit)
	require.Equal(t, string(signBytes), string(newSignBytes))
	valid = pubKey.VerifyBytes(newSignBytes, signature)
	require.True(t, valid)
}
<commit_msg>types: Update String() test to assert Prevote type<commit_after>package types

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	wire "github.com/tendermint/go-wire"
)

func exampleVote() *Vote {
	var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z")
	if err != nil {
		panic(err)
	}

	return &Vote{
		ValidatorAddress: []byte("addr"),
		ValidatorIndex:   56789,
		Height:           12345,
		Round:            2,
		Timestamp:        stamp,
		Type:             byte(2),
		BlockID: BlockID{
			Hash: []byte("hash"),
			PartsHeader: PartSetHeader{
				Total: 1000000,
				Hash:  []byte("parts_hash"),
			},
		},
	}
}

func examplePrevote() *Vote {
	var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z")
	if err != nil {
		panic(err)
	}

	return &Vote{
		ValidatorAddress: []byte("addr"),
		ValidatorIndex:   56789,
		Height:           12345,
		Round:            2,
		Timestamp:        stamp,
		Type:             byte(1),
		BlockID: BlockID{
			Hash: []byte("hash"),
			PartsHeader: PartSetHeader{
				Total: 1000000,
				Hash:  []byte("parts_hash"),
			},
		},
	}
}

func TestVoteSignable(t *testing.T) {
	vote := exampleVote()
	signBytes := SignBytes("test_chain_id", vote)
	signStr := string(signBytes)

	expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":2,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}`
	if signStr != expected {
		// NOTE: when this fails, you probably want to fix up consensus/replay_test too
		t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}

func TestVoteString(t *testing.T) {
	tc := []struct {
		name string
		in   string
		out  string
	}{
		{"Precommit", exampleVote().String(), `Vote{56789:616464720000 12345/02/2(Precommit) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`},
		{"Prevote", examplePrevote().String(), `Vote{56789:616464720000 12345/02/1(Prevote) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`},
	}

	for _, tt := range tc {
		tt := tt
		t.Run(tt.name, func(st *testing.T) {
			if tt.in != tt.out {
				t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", tt.in, tt.out)
			}
		})
	}
}

func TestVoteVerifySignature(t *testing.T) {
	privVal := GenPrivValidatorFS("")
	pubKey := privVal.GetPubKey()

	vote := exampleVote()
	signBytes := SignBytes("test_chain_id", vote)

	// sign it
	signature, err := privVal.Signer.Sign(signBytes)
	require.NoError(t, err)

	// verify the same vote
	valid := pubKey.VerifyBytes(SignBytes("test_chain_id", vote), signature)
	require.True(t, valid)

	// serialize, deserialize and verify again....
	precommit := new(Vote)
	bs := wire.BinaryBytes(vote)
	err = wire.ReadBinaryBytes(bs, &precommit)
	require.NoError(t, err)

	// verify the transmitted vote
	newSignBytes := SignBytes("test_chain_id", precommit)
	require.Equal(t, string(signBytes), string(newSignBytes))
	valid = pubKey.VerifyBytes(newSignBytes, signature)
	require.True(t, valid)
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"github.com/Financial-Times/coco-logfilter"
	"io"
	"os"
)

func main() {
	dec := json.NewDecoder(os.Stdin)
	enc := json.NewEncoder(os.Stdout)
	for {
		m := make(map[string]interface{})
		err := dec.Decode(&m)
		if err != nil {
			if err == io.EOF {
				return
			}
			panic(err)
		}
		munge(m)
		enc.Encode(m)
	}
}

func munge(m map[string]interface{}) {

	message := m["MESSAGE"].(string)

	ent, ok := logfilter.Extract(message)
	if !ok {
		return
	}

	// hackity
	j, err := json.Marshal(ent)
	if err != nil {
		panic(err)
	}
	entMap := make(map[string]interface{})
	err = json.Unmarshal(j, &entMap)
	if err != nil {
		panic(err)
	}
	for k, v := range entMap {
		m[k] = v
	}
}
<commit_msg>Check type of message<commit_after>package main

import (
	"encoding/json"
	"github.com/Financial-Times/coco-logfilter"
	"io"
	"os"
)

func main() {
	dec := json.NewDecoder(os.Stdin)
	enc := json.NewEncoder(os.Stdout)
	for {
		m := make(map[string]interface{})
		err := dec.Decode(&m)
		if err != nil {
			if err == io.EOF {
				return
			}
			panic(err)
		}
		munge(m)
		enc.Encode(m)
	}
}

func munge(m map[string]interface{}) {

	message, ok := m["MESSAGE"].(string)
	if !ok {
		return
	}

	ent, ok := logfilter.Extract(message)
	if !ok {
		return
	}

	// hackity
	j, err := json.Marshal(ent)
	if err != nil {
		panic(err)
	}
	entMap := make(map[string]interface{})
	err = json.Unmarshal(j, &entMap)
	if err != nil {
		panic(err)
	}
	for k, v := range entMap {
		m[k] = v
	}
}
<|endoftext|>


<commit_before>package types

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	wire "github.com/tendermint/go-wire"
)

func examplePrevote() *Vote {
	return exampleVote(VoteTypePrevote)
}

func examplePrecommit() *Vote {
	return exampleVote(VoteTypePrecommit)
}

func exampleVote(t byte) *Vote {
	var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z")
	if err != nil {
		panic(err)
	}

	return &Vote{
		ValidatorAddress: []byte("addr"),
		ValidatorIndex:   56789,
		Height:           12345,
		Round:            2,
		Timestamp:        stamp,
		Type:             t,
		BlockID: BlockID{
			Hash: []byte("hash"),
			PartsHeader: PartSetHeader{
				Total: 1000000,
				Hash:  []byte("parts_hash"),
			},
		},
	}
}

func TestVoteSignable(t *testing.T) {
	vote := examplePrecommit()
	signBytes := SignBytes("test_chain_id", vote)
	signStr := string(signBytes)

	expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":2,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}`
	if signStr != expected {
		// NOTE: when this fails, you probably want to fix up consensus/replay_test too
		t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}

func TestVoteString(t *testing.T) {
	tc := []struct {
		name string
		in   string
		out  string
	}{
		{"Precommit", examplePrecommit().String(), `Vote{56789:616464720000 12345/02/2(Precommit) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`},
		{"Prevote", examplePrevote().String(), `Vote{56789:616464720000 12345/02/1(Prevote) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`},
	}

	for _, tt := range tc {
		tt := tt
		t.Run(tt.name, func(st *testing.T) {
			if tt.in != tt.out {
				t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", tt.in, tt.out)
			}
		})
	}
}

func TestVoteVerifySignature(t *testing.T) {
	privVal := GenPrivValidatorFS("")
	pubKey := privVal.GetPubKey()

	vote := examplePrecommit()
	signBytes := SignBytes("test_chain_id", vote)

	// sign it
	signature, err := privVal.Signer.Sign(signBytes)
	require.NoError(t, err)

	// verify the same vote
	valid := pubKey.VerifyBytes(SignBytes("test_chain_id", vote), signature)
	require.True(t, valid)

	// serialize, deserialize and verify again....
	precommit := new(Vote)
	bs := wire.BinaryBytes(vote)
	err = wire.ReadBinaryBytes(bs, &precommit)
	require.NoError(t, err)

	// verify the transmitted vote
	newSignBytes := SignBytes("test_chain_id", precommit)
	require.Equal(t, string(signBytes), string(newSignBytes))
	valid = pubKey.VerifyBytes(newSignBytes, signature)
	require.True(t, valid)
}
<commit_msg>types: Add test for IsVoteTypeValid<commit_after>package types

import (
	"testing"
	"time"

	"github.com/stretchr/testify/require"

	wire "github.com/tendermint/go-wire"
)

func examplePrevote() *Vote {
	return exampleVote(VoteTypePrevote)
}

func examplePrecommit() *Vote {
	return exampleVote(VoteTypePrecommit)
}

func exampleVote(t byte) *Vote {
	var stamp, err = time.Parse(timeFormat, "2017-12-25T03:00:01.234Z")
	if err != nil {
		panic(err)
	}

	return &Vote{
		ValidatorAddress: []byte("addr"),
		ValidatorIndex:   56789,
		Height:           12345,
		Round:            2,
		Timestamp:        stamp,
		Type:             t,
		BlockID: BlockID{
			Hash: []byte("hash"),
			PartsHeader: PartSetHeader{
				Total: 1000000,
				Hash:  []byte("parts_hash"),
			},
		},
	}
}

func TestVoteSignable(t *testing.T) {
	vote := examplePrecommit()
	signBytes := SignBytes("test_chain_id", vote)
	signStr := string(signBytes)

	expected := `{"chain_id":"test_chain_id","vote":{"block_id":{"hash":"68617368","parts":{"hash":"70617274735F68617368","total":1000000}},"height":12345,"round":2,"timestamp":"2017-12-25T03:00:01.234Z","type":2}}`
	if signStr != expected {
		// NOTE: when this fails, you probably want to fix up consensus/replay_test too
		t.Errorf("Got unexpected sign string for Vote. Expected:\n%v\nGot:\n%v", expected, signStr)
	}
}

func TestVoteString(t *testing.T) {
	tc := []struct {
		name string
		in   string
		out  string
	}{
		{"Precommit", examplePrecommit().String(), `Vote{56789:616464720000 12345/02/2(Precommit) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`},
		{"Prevote", examplePrevote().String(), `Vote{56789:616464720000 12345/02/1(Prevote) 686173680000 {<nil>} @ 2017-12-25T03:00:01.234Z}`},
	}

	for _, tt := range tc {
		tt := tt
		t.Run(tt.name, func(st *testing.T) {
			if tt.in != tt.out {
				t.Errorf("Got unexpected string for Proposal. Expected:\n%v\nGot:\n%v", tt.in, tt.out)
			}
		})
	}
}

func TestVoteVerifySignature(t *testing.T) {
	privVal := GenPrivValidatorFS("")
	pubKey := privVal.GetPubKey()

	vote := examplePrecommit()
	signBytes := SignBytes("test_chain_id", vote)

	// sign it
	signature, err := privVal.Signer.Sign(signBytes)
	require.NoError(t, err)

	// verify the same vote
	valid := pubKey.VerifyBytes(SignBytes("test_chain_id", vote), signature)
	require.True(t, valid)

	// serialize, deserialize and verify again....
	precommit := new(Vote)
	bs := wire.BinaryBytes(vote)
	err = wire.ReadBinaryBytes(bs, &precommit)
	require.NoError(t, err)

	// verify the transmitted vote
	newSignBytes := SignBytes("test_chain_id", precommit)
	require.Equal(t, string(signBytes), string(newSignBytes))
	valid = pubKey.VerifyBytes(newSignBytes, signature)
	require.True(t, valid)
}

func TestIsVoteTypeValid(t *testing.T) {
	tc := []struct {
		name string
		in   byte
		out  bool
	}{
		{"Prevote", VoteTypePrevote, true},
		{"Precommit", VoteTypePrecommit, true},
		{"InvalidType", byte(3), false},
	}

	for _, tt := range tc {
		tt := tt
		t.Run(tt.name, func(st *testing.T) {
			if rs := IsVoteTypeValid(tt.in); rs != tt.out {
				t.Errorf("Got unexpected Vote type. Expected:\n%v\nGot:\n%v", rs, tt.out)
			}
		})
	}
}
<|endoftext|>


<commit_before>package typesys

const (
  TYPE_VOID = 1 << iota
  TYPE_INTEGER
  TYPE_SIGNED
  TYPE_POINTER
  TYPE_ARRAY
  TYPE_STRUCT
  TYPE_UNION
  TYPE_USERTYPE
  TYPE_FUNCTION
)

// IType
type IType interface {
  Size() int
  AllocSize() int
  Alignment() int

  IsVoid() bool
  IsInteger() bool
  IsSigned() bool
  IsPointer() bool
  IsArray() bool
  IsCompositeType() bool
  IsStruct() bool
  IsUnion() bool
  IsUserType() bool
  IsFunction() bool
}

// ITypeRef
type ITypeRef interface {
  GetLocation() ast.Location
  TypeRefId() int
}
<commit_msg>Fix missing import<commit_after>package typesys

import (
  "bitbucket.org/yyuu/bs/ast"
)

const (
  TYPE_VOID = 1 << iota
  TYPE_INTEGER
  TYPE_SIGNED
  TYPE_POINTER
  TYPE_ARRAY
  TYPE_STRUCT
  TYPE_UNION
  TYPE_USERTYPE
  TYPE_FUNCTION
)

// IType
type IType interface {
  Size() int
  AllocSize() int
  Alignment() int

  IsVoid() bool
  IsInteger() bool
  IsSigned() bool
  IsPointer() bool
  IsArray() bool
  IsCompositeType() bool
  IsStruct() bool
  IsUnion() bool
  IsUserType() bool
  IsFunction() bool
}

// ITypeRef
type ITypeRef interface {
  GetLocation() ast.Location
  TypeRefId() int
}
<|endoftext|>


<commit_before>package typewriter

import (
	"bytes"
	"fmt"
	"strings"
	"testing"

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

func doTest(t *testing.T, tc testCase) {
	// Remove the blank starting newlines and prefix \t that goformatting leaves
	tc.expected = tc.expected[2:]
	expectedSplit := strings.Split(tc.expected, "\n")
	var buf bytes.Buffer
	for _, line := range expectedSplit {
		buf.WriteString(strings.TrimLeft(line, "\t") + "\n")
	}

	expected := buf.String()

	actual := Run(tc.lines1, tc.lines2, tc.config)

	if expected != actual {
		if tc.config.MarkFirstDifference {
			t.Log("Not using typewriter to show difference because color characters mess up output")
		} else {
			t.Log("\n" + Run(strings.Split(expected, "\n"), strings.Split(actual, "\n"), Config{
				MarkFirstDifference: true,
				Separator:           "‖",
				Padding:             3,

				LeftHeader:  "expected",
				RightHeader: "actual",
			}))
		}
	}
	assert.Equal(t, expected, actual)
}

type testCase struct {
	name string

	lines1 []string
	lines2 []string
	config Config

	expected string
}

var testCases = []testCase{
	{
		name: "simple",

		lines1: []string{"a", "b", "c"},
		lines2: []string{"d", "e", "f"},
		config: Config{},

		expected: `
		ad
		be
		cf`,
	},
	{
		name: "different lengths",

		lines1: []string{"one", "two", "three", "four"},
		lines2: []string{"five", "six", "seven", "eight"},
		config: Config{},

		expected: `
		one  five
		two  six
		threeseven
		four eight`,
	},
	{
		name: "column 1 longer",

		lines1: []string{"1", "100", "10"},
		lines2: []string{"2000", "2"},
		config: Config{},

		expected: `
		1  2000
		1002
		10 `,
	},
	{
		name: "column 2 longer",

		lines1: []string{"2000", "2"},
		lines2: []string{"1", "100", "10"},
		config: Config{},

		expected: `
		20001
		2   100
		    10`,
	},
	{
		name: "padding",

		lines1: []string{"foo", "bars"},
		lines2: []string{"baz", "sizzle"},
		config: Config{
			Padding: 3,
		},

		expected: `
		foo    baz
		bars   sizzle`,
	},
	{
		name: "separator",

		lines1: []string{"foo", "bars"},
		lines2: []string{"baz", "sizzle"},
		config: Config{
			Padding:   3,
			Separator: "|",
		},

		expected: `
		foo    |baz
		bars   |sizzle`,
	},
	{
		name: "line numbers",

		lines1: []string{"foo", "bars"},
		lines2: []string{"baz", "sizzle", "bat"},
		config: Config{
			Padding:         3,
			Separator:       "|",
			ShowLineNumbers: true,
		},

		expected: `
		1. foo    |baz
		2. bars   |sizzle
		3.        |bat`,
	},
	{
		name: "lots of line numbers",

		lines1: []string{"a", "a"},
		lines2: []string{"a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a"},
		config: Config{
			Padding:         3,
			Separator:       "|",
			ShowLineNumbers: true,
		},

		expected: `
		 1. a   |a
		 2. a   |a
		 3.     |a
		 4.     |a
		 5.     |a
		 6.     |a
		 7.     |a
		 8.     |a
		 9.     |a
		10.     |a
		11.     |a
		12.     |a`,
	},
	{
		name: "mark no difference",

		lines1: []string{"a", "a"},
		lines2: []string{"a", "a"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: `
		a|a
		a|a`,
	},
	{
		name: "mark simple difference",

		lines1: []string{"a", "a"},
		lines2: []string{"a", "b"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		a|a
		%sa%s|%sb%s`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark difference",

		lines1: []string{"abc", "def", "ghi"},
		lines2: []string{"abc", "dff", "ghi"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		d%sef%s|d%sff%s
		ghi|ghi`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark left column missing line difference",

		lines1: []string{"abc", "def"},
		lines2: []string{"abc", "def", "ghi"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		def|def
		%s%s   |%sghi%s`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark right column missing line difference",

		lines1: []string{"abc", "def", "ghi"},
		lines2: []string{"abc", "def"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		def|def
		%sghi%s|%s%s`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark difference left column longer",

		lines1: []string{"abc", "defg", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc |abc
		def%sg%s|def%s%s
		hij |hij`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark difference right column longer",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "defg", "hij"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		def%s%s|def%sg%s
		hij|hij`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "simple headers",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:   "|",
			LeftHeader:  "l",
			RightHeader: "r",
		},

		expected: `
		l  |r

		abc|abc
		def|def
		hij|hij`,
	},
	{
		name: "long headers",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:   "|",
			LeftHeader:  "left header",
			RightHeader: "right header",
		},

		expected: `
		left header|right header

		abc        |abc
		def        |def
		hij        |hij`,
	},
	{
		name: "left header only",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:  "|",
			LeftHeader: "left header",
		},

		expected: `
		left header|

		abc        |abc
		def        |def
		hij        |hij`,
	},
	{
		name: "right header only",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:   "|",
			RightHeader: "right header",
		},

		expected: `
		   |right header

		abc|abc
		def|def
		hij|hij`,
	},
}

func TestRun(t *testing.T) {
	t.Parallel()

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			doTest(t, tc)
		})
	}
}
<commit_msg>Simple benchmarks<commit_after>package typewriter

import (
	"bytes"
	"fmt"
	"strings"
	"testing"

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

func doTest(t *testing.T, tc testCase) {
	// Remove the blank starting newlines and prefix \t that goformatting leaves
	tc.expected = tc.expected[2:]
	expectedSplit := strings.Split(tc.expected, "\n")
	var buf bytes.Buffer
	for _, line := range expectedSplit {
		buf.WriteString(strings.TrimLeft(line, "\t") + "\n")
	}

	expected := buf.String()

	actual := Run(tc.lines1, tc.lines2, tc.config)

	if expected != actual {
		if tc.config.MarkFirstDifference {
			t.Log("Not using typewriter to show difference because color characters mess up output")
		} else {
			t.Log("\n" + Run(strings.Split(expected, "\n"), strings.Split(actual, "\n"), Config{
				MarkFirstDifference: true,
				Separator:           "‖",
				Padding:             3,

				LeftHeader:  "expected",
				RightHeader: "actual",
			}))
		}
	}
	assert.Equal(t, expected, actual)
}

type testCase struct {
	name string

	lines1 []string
	lines2 []string
	config Config

	expected string
}

var testCases = []testCase{
	{
		name: "simple",

		lines1: []string{"a", "b", "c"},
		lines2: []string{"d", "e", "f"},
		config: Config{},

		expected: `
		ad
		be
		cf`,
	},
	{
		name: "different lengths",

		lines1: []string{"one", "two", "three", "four"},
		lines2: []string{"five", "six", "seven", "eight"},
		config: Config{},

		expected: `
		one  five
		two  six
		threeseven
		four eight`,
	},
	{
		name: "column 1 longer",

		lines1: []string{"1", "100", "10"},
		lines2: []string{"2000", "2"},
		config: Config{},

		expected: `
		1  2000
		1002
		10 `,
	},
	{
		name: "column 2 longer",

		lines1: []string{"2000", "2"},
		lines2: []string{"1", "100", "10"},
		config: Config{},

		expected: `
		20001
		2   100
		    10`,
	},
	{
		name: "padding",

		lines1: []string{"foo", "bars"},
		lines2: []string{"baz", "sizzle"},
		config: Config{
			Padding: 3,
		},

		expected: `
		foo    baz
		bars   sizzle`,
	},
	{
		name: "separator",

		lines1: []string{"foo", "bars"},
		lines2: []string{"baz", "sizzle"},
		config: Config{
			Padding:   3,
			Separator: "|",
		},

		expected: `
		foo    |baz
		bars   |sizzle`,
	},
	{
		name: "line numbers",

		lines1: []string{"foo", "bars"},
		lines2: []string{"baz", "sizzle", "bat"},
		config: Config{
			Padding:         3,
			Separator:       "|",
			ShowLineNumbers: true,
		},

		expected: `
		1. foo    |baz
		2. bars   |sizzle
		3.        |bat`,
	},
	{
		name: "lots of line numbers",

		lines1: []string{"a", "a"},
		lines2: []string{"a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a", "a"},
		config: Config{
			Padding:         3,
			Separator:       "|",
			ShowLineNumbers: true,
		},

		expected: `
		 1. a   |a
		 2. a   |a
		 3.     |a
		 4.     |a
		 5.     |a
		 6.     |a
		 7.     |a
		 8.     |a
		 9.     |a
		10.     |a
		11.     |a
		12.     |a`,
	},
	{
		name: "mark no difference",

		lines1: []string{"a", "a"},
		lines2: []string{"a", "a"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: `
		a|a
		a|a`,
	},
	{
		name: "mark simple difference",

		lines1: []string{"a", "a"},
		lines2: []string{"a", "b"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		a|a
		%sa%s|%sb%s`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark difference",

		lines1: []string{"abc", "def", "ghi"},
		lines2: []string{"abc", "dff", "ghi"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		d%sef%s|d%sff%s
		ghi|ghi`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark left column missing line difference",

		lines1: []string{"abc", "def"},
		lines2: []string{"abc", "def", "ghi"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		def|def
		%s%s   |%sghi%s`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark right column missing line difference",

		lines1: []string{"abc", "def", "ghi"},
		lines2: []string{"abc", "def"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		def|def
		%sghi%s|%s%s`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark difference left column longer",

		lines1: []string{"abc", "defg", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc |abc
		def%sg%s|def%s%s
		hij |hij`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "mark difference right column longer",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "defg", "hij"},
		config: Config{
			Separator:           "|",
			MarkFirstDifference: true,
		},

		expected: fmt.Sprintf(`
		abc|abc
		def%s%s|def%sg%s
		hij|hij`, colorDefault, colorOff, colorDefault, colorOff),
	},
	{
		name: "simple headers",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:   "|",
			LeftHeader:  "l",
			RightHeader: "r",
		},

		expected: `
		l  |r

		abc|abc
		def|def
		hij|hij`,
	},
	{
		name: "long headers",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:   "|",
			LeftHeader:  "left header",
			RightHeader: "right header",
		},

		expected: `
		left header|right header

		abc        |abc
		def        |def
		hij        |hij`,
	},
	{
		name: "left header only",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:  "|",
			LeftHeader: "left header",
		},

		expected: `
		left header|

		abc        |abc
		def        |def
		hij        |hij`,
	},
	{
		name: "right header only",

		lines1: []string{"abc", "def", "hij"},
		lines2: []string{"abc", "def", "hij"},
		config: Config{
			Separator:   "|",
			RightHeader: "right header",
		},

		expected: `
		   |right header

		abc|abc
		def|def
		hij|hij`,
	},
}

func TestRun(t *testing.T) {
	t.Parallel()

	for _, tc := range testCases {
		tc := tc
		t.Run(tc.name, func(t *testing.T) {
			t.Parallel()
			doTest(t, tc)
		})
	}
}

var (
	simpleLine1 = []string{"abc", "defg", "hijk", "l", "mnopq", "rstuv", "wx", "y", "z"}
	simpleLine2 = []string{"abcde", "f", "g", "hijkl", "mn", "opqrstuv", "wx", "yz"}
)

var benchmarks = []testCase{
	{
		name: "base",

		config: Config{},
	},
	{
		name: "difference",

		config: Config{
			MarkFirstDifference: true,
		},
	},
	{
		name: "line numbers",

		config: Config{
			ShowLineNumbers: true,
		},
	},
	{
		name: "padding",

		config: Config{
			Padding: 50,
		},
	},
	{
		name: "separator",

		config: Config{
			Separator: "|",
		},
	},
	{
		name: "headers",

		config: Config{
			LeftHeader:  "Left Header",
			RightHeader: "RightHeader",
		},
	},
}

func BenchmarkRunSimple(b *testing.B) {
	for _, tc := range benchmarks {
		tc := tc
		tc.lines1 = simpleLine1
		tc.lines2 = simpleLine2
		b.Run(tc.name, func(b *testing.B) {
			for i := 0; i < b.N; i++ {
				Run(tc.lines1, tc.lines2, tc.config)
			}
		})
	}
}
<|endoftext|>