<commit_before>// package idletiming provides mechanisms for adding idle timeouts to net.Conn
// and net.Listener.
package idletiming

import (
	"net"
	"sync/atomic"
	"time"

	"github.com/getlantern/golog"
)

var (
	epoch = time.Unix(0, 0)
	log   = golog.LoggerFor("idletiming")
)

// Conn creates a new net.Conn wrapping the given net.Conn that times out after
// the specified period. Read and Write calls will timeout if they take longer
// than the indicated idleTimeout.
//
// idleTimeout specifies how long to wait for inactivity before considering
// connection idle.
//
// onIdle is a required function that's called if a connection idles.
// idletiming.Conn does not close the underlying connection on idle, you have to
// do that in your onIdle callback.
func Conn(conn net.Conn, idleTimeout time.Duration, onIdle func()) *IdleTimingConn {
	if onIdle == nil {
		panic("onIdle is required")
	}

	c := &IdleTimingConn{
		conn:             conn,
		idleTimeout:      idleTimeout,
		halfIdleTimeout:  time.Duration(idleTimeout.Nanoseconds() / 2),
		activeCh:         make(chan bool, 1),
		closedCh:         make(chan bool, 1),
		lastActivityTime: int64(time.Now().UnixNano()),
	}

	go func() {
		timer := time.NewTimer(idleTimeout)
		defer timer.Stop()
		for {
			select {
			case <-c.activeCh:
				// We're active, continue
				timer.Reset(idleTimeout)
				atomic.StoreInt64(&c.lastActivityTime, time.Now().UnixNano())
				continue
			case <-timer.C:
				onIdle()
				return
			case <-c.closedCh:
				return
			}
		}
	}()

	return c
}

// IdleTimingConn is a net.Conn that wraps another net.Conn and that times out
// if idle for more than idleTimeout.
type IdleTimingConn struct {
	conn             net.Conn
	idleTimeout      time.Duration
	halfIdleTimeout  time.Duration
	readDeadline     int64
	writeDeadline    int64
	activeCh         chan bool
	closedCh         chan bool
	lastActivityTime int64
}

// TimesOutIn returns how much time is left before this connection will time
// out, assuming there is no further activity.
func (c *IdleTimingConn) TimesOutIn() time.Duration {
	return c.TimesOutAt().Sub(time.Now())
}

// TimesOutAt returns the time at which this connection will time out, assuming
// there is no further activity
func (c *IdleTimingConn) TimesOutAt() time.Time {
	return time.Unix(0, atomic.LoadInt64(&c.lastActivityTime)).Add(c.idleTimeout)
}

// Read implements the method from io.Reader
func (c *IdleTimingConn) Read(b []byte) (int, error) {
	totalN := 0
	readDeadline := time.Unix(0, atomic.LoadInt64(&c.readDeadline))

	// Continually read while we can, always setting a deadline that's less than
	// our idleTimeout so that we can update our active status before we hit the
	// idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if readDeadline != epoch && !maxDeadline.Before(readDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetReadDeadline(readDeadline); err != nil {
				log.Errorf("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetReadDeadline(maxDeadline); err != nil {
				log.Errorf("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Write implements the method from io.Reader
func (c *IdleTimingConn) Write(b []byte) (int, error) {
	totalN := 0
	writeDeadline := time.Unix(0, atomic.LoadInt64(&c.writeDeadline))

	// Continually write while we can, always setting a deadline that's less
	// than our idleTimeout so that we can update our active status before we
	// hit the idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if writeDeadline != epoch && !maxDeadline.Before(writeDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetWriteDeadline(writeDeadline); err != nil {
				log.Errorf("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetWriteDeadline(maxDeadline); err != nil {
				log.Errorf("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Close this IdleTimingConn. This will close the underlying net.Conn as well,
// returning the error from calling its Close method.
func (c *IdleTimingConn) Close() error {
	select {
	case c.closedCh <- true:
		// close accepted
	default:
		// already closing, ignore
	}
	return c.conn.Close()
}

func (c *IdleTimingConn) LocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

func (c *IdleTimingConn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (c *IdleTimingConn) SetDeadline(t time.Time) error {
	if err := c.SetReadDeadline(t); err != nil {
		log.Errorf("Unable to set read deadline: %v", err)
	}
	if err := c.SetWriteDeadline(t); err != nil {
		log.Errorf("Unable to set write deadline: %v", err)
	}
	return nil
}

func (c *IdleTimingConn) SetReadDeadline(t time.Time) error {
	atomic.StoreInt64(&c.readDeadline, t.UnixNano())
	return nil
}

func (c *IdleTimingConn) SetWriteDeadline(t time.Time) error {
	atomic.StoreInt64(&c.writeDeadline, t.UnixNano())
	return nil
}

func (c *IdleTimingConn) markActive(n int) bool {
	if n > 0 {
		c.activeCh <- true
		return true
	} else {
		return false
	}
}

func isTimeout(err error) bool {
	if netErr, ok := err.(net.Error); ok {
		return netErr.Timeout()
	}
	return false
}
<commit_msg>move int64 to top of struct resolves #3802<commit_after>// package idletiming provides mechanisms for adding idle timeouts to net.Conn
// and net.Listener.
package idletiming

import (
	"net"
	"sync/atomic"
	"time"

	"github.com/getlantern/golog"
)

var (
	epoch = time.Unix(0, 0)
	log   = golog.LoggerFor("idletiming")
)

// Conn creates a new net.Conn wrapping the given net.Conn that times out after
// the specified period. Read and Write calls will timeout if they take longer
// than the indicated idleTimeout.
//
// idleTimeout specifies how long to wait for inactivity before considering
// connection idle.
//
// onIdle is a required function that's called if a connection idles.
// idletiming.Conn does not close the underlying connection on idle, you have to
// do that in your onIdle callback.
func Conn(conn net.Conn, idleTimeout time.Duration, onIdle func()) *IdleTimingConn {
	if onIdle == nil {
		panic("onIdle is required")
	}

	c := &IdleTimingConn{
		conn:             conn,
		idleTimeout:      idleTimeout,
		halfIdleTimeout:  time.Duration(idleTimeout.Nanoseconds() / 2),
		activeCh:         make(chan bool, 1),
		closedCh:         make(chan bool, 1),
		lastActivityTime: int64(time.Now().UnixNano()),
	}

	go func() {
		timer := time.NewTimer(idleTimeout)
		defer timer.Stop()
		for {
			select {
			case <-c.activeCh:
				// We're active, continue
				timer.Reset(idleTimeout)
				atomic.StoreInt64(&c.lastActivityTime, time.Now().UnixNano())
				continue
			case <-timer.C:
				onIdle()
				return
			case <-c.closedCh:
				return
			}
		}
	}()

	return c
}

// IdleTimingConn is a net.Conn that wraps another net.Conn and that times out
// if idle for more than idleTimeout.
type IdleTimingConn struct {
	// Keep them at the top to make sure 64-bit alignment, see
	// https://golang.org/pkg/sync/atomic/#pkg-note-BUG
	readDeadline     int64
	writeDeadline    int64
	lastActivityTime int64

	conn            net.Conn
	idleTimeout     time.Duration
	halfIdleTimeout time.Duration
	activeCh        chan bool
	closedCh        chan bool
}

// TimesOutIn returns how much time is left before this connection will time
// out, assuming there is no further activity.
func (c *IdleTimingConn) TimesOutIn() time.Duration {
	return c.TimesOutAt().Sub(time.Now())
}

// TimesOutAt returns the time at which this connection will time out, assuming
// there is no further activity
func (c *IdleTimingConn) TimesOutAt() time.Time {
	return time.Unix(0, atomic.LoadInt64(&c.lastActivityTime)).Add(c.idleTimeout)
}

// Read implements the method from io.Reader
func (c *IdleTimingConn) Read(b []byte) (int, error) {
	totalN := 0
	readDeadline := time.Unix(0, atomic.LoadInt64(&c.readDeadline))

	// Continually read while we can, always setting a deadline that's less than
	// our idleTimeout so that we can update our active status before we hit the
	// idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if readDeadline != epoch && !maxDeadline.Before(readDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetReadDeadline(readDeadline); err != nil {
				log.Errorf("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetReadDeadline(maxDeadline); err != nil {
				log.Errorf("Unable to set read deadline: %v", err)
			}
			n, err := c.conn.Read(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Write implements the method from io.Reader
func (c *IdleTimingConn) Write(b []byte) (int, error) {
	totalN := 0
	writeDeadline := time.Unix(0, atomic.LoadInt64(&c.writeDeadline))

	// Continually write while we can, always setting a deadline that's less
	// than our idleTimeout so that we can update our active status before we
	// hit the idleTimeout.
	for {
		maxDeadline := time.Now().Add(c.halfIdleTimeout)
		if writeDeadline != epoch && !maxDeadline.Before(writeDeadline) {
			// Caller's deadline is before ours, use it
			if err := c.conn.SetWriteDeadline(writeDeadline); err != nil {
				log.Errorf("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			return totalN, err
		} else {
			// Use our own deadline
			if err := c.conn.SetWriteDeadline(maxDeadline); err != nil {
				log.Errorf("Unable to set write deadline: %v", err)
			}
			n, err := c.conn.Write(b)
			c.markActive(n)
			totalN = totalN + n
			hitMaxDeadline := isTimeout(err) && !time.Now().Before(maxDeadline)
			if hitMaxDeadline {
				// Ignore timeouts when encountering deadline based on
				// IdleTimeout
				err = nil
			}
			if n == 0 || !hitMaxDeadline {
				return totalN, err
			}
			b = b[n:]
		}
	}
}

// Close this IdleTimingConn. This will close the underlying net.Conn as well,
// returning the error from calling its Close method.
func (c *IdleTimingConn) Close() error {
	select {
	case c.closedCh <- true:
		// close accepted
	default:
		// already closing, ignore
	}
	return c.conn.Close()
}

func (c *IdleTimingConn) LocalAddr() net.Addr {
	return c.conn.LocalAddr()
}

func (c *IdleTimingConn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

func (c *IdleTimingConn) SetDeadline(t time.Time) error {
	if err := c.SetReadDeadline(t); err != nil {
		log.Errorf("Unable to set read deadline: %v", err)
	}
	if err := c.SetWriteDeadline(t); err != nil {
		log.Errorf("Unable to set write deadline: %v", err)
	}
	return nil
}

func (c *IdleTimingConn) SetReadDeadline(t time.Time) error {
	atomic.StoreInt64(&c.readDeadline, t.UnixNano())
	return nil
}

func (c *IdleTimingConn) SetWriteDeadline(t time.Time) error {
	atomic.StoreInt64(&c.writeDeadline, t.UnixNano())
	return nil
}

func (c *IdleTimingConn) markActive(n int) bool {
	if n > 0 {
		c.activeCh <- true
		return true
	} else {
		return false
	}
}

func isTimeout(err error) bool {
	if netErr, ok := err.(net.Error); ok {
		return netErr.Timeout()
	}
	return false
}
<|endoftext|>


<commit_before>package main

import _ "github.com/docker/machine/drivers/hyperv"
<commit_msg>cleanup unused hyperv file<commit_after><|endoftext|>


<commit_before>package main

import (
	"bytes"
	"sync"
	"time"

	"github.com/btcsuite/fastsha256"
	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

var (
	// debugPre is the default debug preimage which is inserted into the
	// invoice registry if the --debughtlc flag is activated on start up.
	// All nodes initialize with the flag active will immediately settle
	// any incoming HTLC whose rHash is corresponds with the debug
	// preimage.
	debugPre, _ = wire.NewShaHash(bytes.Repeat([]byte{1}, 32))

	debugHash = wire.ShaHash(fastsha256.Sum256(debugPre[:]))
)

// invoiceRegistry is a central registry of all the outstanding invoices
// created by the daemon. The registry is a thin wrapper around a map in order
// to ensure that all updates/reads are thread safe.
type invoiceRegistry struct {
	sync.RWMutex

	cdb *channeldb.DB

	clientMtx           sync.Mutex
	nextClientID        uint32
	notificationClients map[uint32]*invoiceSubscription

	// debugInvoices is a mp which stores special "debug" invoices which
	// should be only created/used when manual tests require an invoice
	// that *all* nodes are able to fully settle.
	debugInvoices map[wire.ShaHash]*channeldb.Invoice
}

// newInvoiceRegistry creates a new invoice registry. The invoice registry
// wraps the persistent on-disk invoice storage with an additional in-memory
// layer. The in-memory layer is in pace such that debug invoices can be added
// which are volatile yet available system wide within the daemon.
func newInvoiceRegistry(cdb *channeldb.DB) *invoiceRegistry {
	return &invoiceRegistry{
		cdb:                 cdb,
		debugInvoices:       make(map[wire.ShaHash]*channeldb.Invoice),
		notificationClients: make(map[uint32]*invoiceSubscription),
	}
}

// addDebugInvoice adds a debug invoice for the specified amount, identified
// by the passed preimage. Once this invoice is added, sub-systems within the
// daemon add/forward HTLC's are able to obtain the proper preimage required
// for redemption in the case that we're the final destination.
func (i *invoiceRegistry) AddDebugInvoice(amt btcutil.Amount, preimage wire.ShaHash) {
	paymentHash := wire.ShaHash(fastsha256.Sum256(preimage[:]))

	invoice := &channeldb.Invoice{
		CreationDate: time.Now(),
		Terms: channeldb.ContractTerm{
			Value:           amt,
			PaymentPreimage: preimage,
		},
	}

	i.Lock()
	i.debugInvoices[paymentHash] = invoice
	i.Unlock()

	ltndLog.Debugf("Adding debug invoice %v", newLogClosure(func() string {
		return spew.Sdump(invoice)
	}))
}

// AddInvoice adds a regular invoice for the specified amount, identified by
// the passed preimage. Additionally, any memo or recipt data provided will
// also be stored on-disk. Once this invoice is added, sub-systems within the
// daemon add/forward HTLC's are able to obtain the proper preimage required
// for redemption in the case that we're the final destination.
func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) error {
	ltndLog.Debugf("Adding invoice %v", newLogClosure(func() string {
		return spew.Sdump(invoice)
	}))

	// TODO(roasbeef): also check in memory for quick lookups/settles?
	if err := i.cdb.AddInvoice(invoice); err != nil {
		return err
	}

	// TODO(roasbeef): re-enable?
	//go i.notifyClients(invoice, false)

	return nil
}

// lookupInvoice looks up an invoice by it's payment hash (R-Hash), if found
// then we're able to pull the funds pending within an HTLC.
// TODO(roasbeef): ignore if settled?
func (i *invoiceRegistry) LookupInvoice(rHash wire.ShaHash) (*channeldb.Invoice, error) {
	// First check the in-memory debug invoice index to see if this is an
	// existing invoice added for debugging.
	i.RLock()
	invoice, ok := i.debugInvoices[rHash]
	i.RUnlock()

	// If found, then simply return the invoice directly.
	if ok {
		return invoice, nil
	}

	// Otherwise, we'll check the database to see if there's an existing
	// matching invoice.
	return i.cdb.LookupInvoice(rHash)
}

// SettleInvoice attempts to mark an invoice as settled. If the invoice is a
// dbueg invoice, then this method is a nooop as debug invoices are never fully
// settled.
func (i *invoiceRegistry) SettleInvoice(rHash wire.ShaHash) error {
	ltndLog.Debugf("Settling invoice %x", rHash[:])

	// First check the in-memory debug invoice index to see if this is an
	// existing invoice added for debugging.
	i.RLock()
	if _, ok := i.debugInvoices[rHash]; ok {
		// Debug invoices are never fully settled, so we simply return
		// immediately in this case.
		i.RUnlock()

		return nil
	}
	i.RUnlock()

	// If this isn't a debug invoice, then we'll attempt to settle an
	// invoice matching this rHash on disk (if one exists).
	if err := i.cdb.SettleInvoice(rHash); err != nil {
		return err
	}

	// Launch a new goroutine to notify any/all registered invoice
	// notification clients.
	go func() {
		invoice, err := i.cdb.LookupInvoice(rHash)
		if err != nil {
			ltndLog.Errorf("unable to find invoice: %v", err)
			return
		}

		i.notifyClients(invoice, true)
	}()

	return nil
}

// notifyClients notifies all currently registered invoice notificaiton clients
// of a newly added/settled invoice.
func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool) {
	i.clientMtx.Lock()
	defer i.clientMtx.Unlock()

	for _, client := range i.notificationClients {
		var eventChan chan *channeldb.Invoice
		if settle {
			eventChan = client.SettledInvoices
		} else {
			eventChan = client.NewInvoices
		}

		go func() {
			eventChan <- invoice
		}()
	}
}

// invoiceSubscription represents an intent to receive updates for newly added
// or settled invoices. For each newly added invoice, a copy of the invoice
// will be sent over the NewInvoices channel. Similarly, for each newly settled
// invoice, a copy of the invoice will be sent over the SettledInvoices
// channel.
type invoiceSubscription struct {
	NewInvoices     chan *channeldb.Invoice
	SettledInvoices chan *channeldb.Invoice

	inv *invoiceRegistry
	id  uint32
}

// Cancel unregisteres the invoiceSubscription, freeing any previoulsy allocate
// resources.
func (i *invoiceSubscription) Cancel() {
	i.inv.clientMtx.Lock()
	delete(i.inv.notificationClients, i.id)
	i.inv.clientMtx.Unlock()
}

// SubscribeNotifications returns an invoiceSubscription which allows the
// caller to receive async notifications when any invoices are settled or
// added.
func (i *invoiceRegistry) SubscribeNotifications() *invoiceSubscription {
	client := &invoiceSubscription{
		NewInvoices:     make(chan *channeldb.Invoice),
		SettledInvoices: make(chan *channeldb.Invoice),
	}

	i.clientMtx.Lock()
	i.notificationClients[i.nextClientID] = client
	client.id = i.nextClientID
	i.nextClientID++
	i.clientMtx.Unlock()

	return client
}
<commit_msg>invoices: properly set pointer in invoiceSubscription  to fix panic<commit_after>package main

import (
	"bytes"
	"sync"
	"time"

	"github.com/btcsuite/fastsha256"
	"github.com/davecgh/go-spew/spew"
	"github.com/lightningnetwork/lnd/channeldb"
	"github.com/roasbeef/btcd/wire"
	"github.com/roasbeef/btcutil"
)

var (
	// debugPre is the default debug preimage which is inserted into the
	// invoice registry if the --debughtlc flag is activated on start up.
	// All nodes initialize with the flag active will immediately settle
	// any incoming HTLC whose rHash is corresponds with the debug
	// preimage.
	debugPre, _ = wire.NewShaHash(bytes.Repeat([]byte{1}, 32))

	debugHash = wire.ShaHash(fastsha256.Sum256(debugPre[:]))
)

// invoiceRegistry is a central registry of all the outstanding invoices
// created by the daemon. The registry is a thin wrapper around a map in order
// to ensure that all updates/reads are thread safe.
type invoiceRegistry struct {
	sync.RWMutex

	cdb *channeldb.DB

	clientMtx           sync.Mutex
	nextClientID        uint32
	notificationClients map[uint32]*invoiceSubscription

	// debugInvoices is a mp which stores special "debug" invoices which
	// should be only created/used when manual tests require an invoice
	// that *all* nodes are able to fully settle.
	debugInvoices map[wire.ShaHash]*channeldb.Invoice
}

// newInvoiceRegistry creates a new invoice registry. The invoice registry
// wraps the persistent on-disk invoice storage with an additional in-memory
// layer. The in-memory layer is in pace such that debug invoices can be added
// which are volatile yet available system wide within the daemon.
func newInvoiceRegistry(cdb *channeldb.DB) *invoiceRegistry {
	return &invoiceRegistry{
		cdb:                 cdb,
		debugInvoices:       make(map[wire.ShaHash]*channeldb.Invoice),
		notificationClients: make(map[uint32]*invoiceSubscription),
	}
}

// addDebugInvoice adds a debug invoice for the specified amount, identified
// by the passed preimage. Once this invoice is added, sub-systems within the
// daemon add/forward HTLC's are able to obtain the proper preimage required
// for redemption in the case that we're the final destination.
func (i *invoiceRegistry) AddDebugInvoice(amt btcutil.Amount, preimage wire.ShaHash) {
	paymentHash := wire.ShaHash(fastsha256.Sum256(preimage[:]))

	invoice := &channeldb.Invoice{
		CreationDate: time.Now(),
		Terms: channeldb.ContractTerm{
			Value:           amt,
			PaymentPreimage: preimage,
		},
	}

	i.Lock()
	i.debugInvoices[paymentHash] = invoice
	i.Unlock()

	ltndLog.Debugf("Adding debug invoice %v", newLogClosure(func() string {
		return spew.Sdump(invoice)
	}))
}

// AddInvoice adds a regular invoice for the specified amount, identified by
// the passed preimage. Additionally, any memo or recipt data provided will
// also be stored on-disk. Once this invoice is added, sub-systems within the
// daemon add/forward HTLC's are able to obtain the proper preimage required
// for redemption in the case that we're the final destination.
func (i *invoiceRegistry) AddInvoice(invoice *channeldb.Invoice) error {
	ltndLog.Debugf("Adding invoice %v", newLogClosure(func() string {
		return spew.Sdump(invoice)
	}))

	// TODO(roasbeef): also check in memory for quick lookups/settles?
	if err := i.cdb.AddInvoice(invoice); err != nil {
		return err
	}

	// TODO(roasbeef): re-enable?
	//go i.notifyClients(invoice, false)

	return nil
}

// lookupInvoice looks up an invoice by it's payment hash (R-Hash), if found
// then we're able to pull the funds pending within an HTLC.
// TODO(roasbeef): ignore if settled?
func (i *invoiceRegistry) LookupInvoice(rHash wire.ShaHash) (*channeldb.Invoice, error) {
	// First check the in-memory debug invoice index to see if this is an
	// existing invoice added for debugging.
	i.RLock()
	invoice, ok := i.debugInvoices[rHash]
	i.RUnlock()

	// If found, then simply return the invoice directly.
	if ok {
		return invoice, nil
	}

	// Otherwise, we'll check the database to see if there's an existing
	// matching invoice.
	return i.cdb.LookupInvoice(rHash)
}

// SettleInvoice attempts to mark an invoice as settled. If the invoice is a
// dbueg invoice, then this method is a nooop as debug invoices are never fully
// settled.
func (i *invoiceRegistry) SettleInvoice(rHash wire.ShaHash) error {
	ltndLog.Debugf("Settling invoice %x", rHash[:])

	// First check the in-memory debug invoice index to see if this is an
	// existing invoice added for debugging.
	i.RLock()
	if _, ok := i.debugInvoices[rHash]; ok {
		// Debug invoices are never fully settled, so we simply return
		// immediately in this case.
		i.RUnlock()

		return nil
	}
	i.RUnlock()

	// If this isn't a debug invoice, then we'll attempt to settle an
	// invoice matching this rHash on disk (if one exists).
	if err := i.cdb.SettleInvoice(rHash); err != nil {
		return err
	}

	// Launch a new goroutine to notify any/all registered invoice
	// notification clients.
	go func() {
		invoice, err := i.cdb.LookupInvoice(rHash)
		if err != nil {
			ltndLog.Errorf("unable to find invoice: %v", err)
			return
		}

		i.notifyClients(invoice, true)
	}()

	return nil
}

// notifyClients notifies all currently registered invoice notificaiton clients
// of a newly added/settled invoice.
func (i *invoiceRegistry) notifyClients(invoice *channeldb.Invoice, settle bool) {
	i.clientMtx.Lock()
	defer i.clientMtx.Unlock()

	for _, client := range i.notificationClients {
		var eventChan chan *channeldb.Invoice
		if settle {
			eventChan = client.SettledInvoices
		} else {
			eventChan = client.NewInvoices
		}

		go func() {
			eventChan <- invoice
		}()
	}
}

// invoiceSubscription represents an intent to receive updates for newly added
// or settled invoices. For each newly added invoice, a copy of the invoice
// will be sent over the NewInvoices channel. Similarly, for each newly settled
// invoice, a copy of the invoice will be sent over the SettledInvoices
// channel.
type invoiceSubscription struct {
	NewInvoices     chan *channeldb.Invoice
	SettledInvoices chan *channeldb.Invoice

	inv *invoiceRegistry
	id  uint32
}

// Cancel unregisters the invoiceSubscription, freeing any previously allocate
// resources.
func (i *invoiceSubscription) Cancel() {
	i.inv.clientMtx.Lock()
	delete(i.inv.notificationClients, i.id)
	i.inv.clientMtx.Unlock()
}

// SubscribeNotifications returns an invoiceSubscription which allows the
// caller to receive async notifications when any invoices are settled or
// added.
func (i *invoiceRegistry) SubscribeNotifications() *invoiceSubscription {
	client := &invoiceSubscription{
		NewInvoices:     make(chan *channeldb.Invoice),
		SettledInvoices: make(chan *channeldb.Invoice),
		inv:             i,
	}

	i.clientMtx.Lock()
	i.notificationClients[i.nextClientID] = client
	client.id = i.nextClientID
	i.nextClientID++
	i.clientMtx.Unlock()

	return client
}
<|endoftext|>


<commit_before>package db

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/bosssauce/ponzu/content"
	"github.com/bosssauce/ponzu/management/editor"
	"github.com/bosssauce/ponzu/management/manager"

	"github.com/boltdb/bolt"
	"github.com/gorilla/schema"
)

// SetContent inserts or updates values in the database.
// The `target` argument is a string made up of namespace:id (string:int)
func SetContent(target string, data url.Values) (int, error) {
	t := strings.Split(target, ":")
	ns, id := t[0], t[1]

	// check if content id == -1 (indicating new post).
	// if so, run an insert which will assign the next auto incremented int.
	// this is done because boltdb begins its bucket auto increment value at 0,
	// which is the zero-value of an int in the Item struct field for ID.
	// this is a problem when the original first post (with auto ID = 0) gets
	// overwritten by any new post, originally having no ID, defauting to 0.
	if id == "-1" {
		return insert(ns, data)
	}

	return update(ns, id, data)
}

func update(ns, id string, data url.Values) (int, error) {
	cid, err := strconv.Atoi(id)
	if err != nil {
		return 0, err
	}

	err = store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(ns))
		if err != nil {
			return err
		}

		j, err := postToJSON(ns, data)
		if err != nil {
			return err
		}

		err = b.Put([]byte(fmt.Sprintf("%d", cid)), j)
		if err != nil {
			return err
		}

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

	go SortContent(ns)

	return cid, nil
}

func insert(ns string, data url.Values) (int, error) {
	var effectedID int

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(ns))
		if err != nil {
			return err
		}

		// get the next available ID and convert to string
		// also set effectedID to int of ID
		id, err := b.NextSequence()
		if err != nil {
			return err
		}
		cid := strconv.FormatUint(id, 10)
		effectedID, err = strconv.Atoi(cid)
		if err != nil {
			return err
		}
		data.Set("id", cid)

		j, err := postToJSON(ns, data)
		if err != nil {
			return err
		}

		err = b.Put([]byte(cid), j)
		if err != nil {
			return err
		}

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

	go SortContent(ns)

	return effectedID, nil
}

// SetPendingContent inserts submitted content for pending approval
func SetPendingContent(target string, data url.Values) error {
	if !strings.Contains(target, "_pending") {
		return errors.New("Only set items into _pending bucket using SetPendingContent. Namespace should be <Type>_pending")
	}

	ns := strings.Split(target, "_")[0]

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(target))
		if err != nil {
			return err
		}

		key := fmt.Sprintf("%d", time.Now().UTC().Unix())
		j, err := postToJSON(ns, data)
		if err != nil {
			return err
		}
		b.Put([]byte(key), j)

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

	return nil
}

// DeleteContent removes an item from the database. Deleting a non-existent item
// will return a nil error.
func DeleteContent(target string) error {
	t := strings.Split(target, ":")
	ns, id := t[0], t[1]

	err := store.Update(func(tx *bolt.Tx) error {
		tx.Bucket([]byte(ns)).Delete([]byte(id))
		return nil
	})

	if err != nil {
		return err
	}

	// exception to typical "run in goroutine" pattern:
	// we want to have an updated admin view as soon as this is deleted, so
	// in some cases, the delete and redirect is faster than the sort,
	// thus still showing a deleted post in the admin view.
	SortContent(ns)

	return nil
}

// Content retrives one item from the database. Non-existent values will return an empty []byte
// The `target` argument is a string made up of namespace:id (string:int)
func Content(target string) ([]byte, error) {
	t := strings.Split(target, ":")
	ns, id := t[0], t[1]

	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(ns))
		_, err := val.Write(b.Get([]byte(id)))
		if err != nil {
			log.Println(err)
			return err
		}

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

	return val.Bytes(), nil
}

// ContentAll retrives all items from the database within the provided namespace
func ContentAll(namespace string) [][]byte {
	var posts [][]byte
	store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(namespace))

		numKeys := b.Stats().KeyN
		posts = make([][]byte, 0, numKeys)

		b.ForEach(func(k, v []byte) error {
			posts = append(posts, v)

			return nil
		})

		return nil
	})

	return posts
}

// SortContent sorts all content of the type supplied as the namespace by time,
// in descending order, from most recent to least recent
// Should be called from a goroutine after SetContent is successful
func SortContent(namespace string) {
	all := ContentAll(namespace)

	var posts sortablePosts
	// decode each (json) into type to then sort
	for i := range all {
		j := all[i]
		post := content.Types[namespace]()

		err := json.Unmarshal(j, &post)
		if err != nil {
			log.Println("Error decoding json while sorting", namespace, ":", err)
			return
		}

		posts = append(posts, post.(editor.Sortable))
	}

	// sort posts
	sort.Sort(posts)

	// store in <namespace>_sorted bucket, first delete existing
	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(namespace + "_sorted"))
		if err != nil {
			return err
		}

		// encode to json and store as 'i:post.Time()':post
		for i := range posts {
			j, err := json.Marshal(posts[i])
			if err != nil {
				return err
			}

			cid := fmt.Sprintf("%d:%d", i, posts[i].Time())
			err = b.Put([]byte(cid), j)
			if err != nil {
				return err
			}
		}

		return nil
	})
	if err != nil {
		log.Println("Error while updating db with sorted", namespace, err)
	}

}

type sortablePosts []editor.Sortable

func (s sortablePosts) Len() int {
	return len(s)
}

func (s sortablePosts) Less(i, j int) bool {
	return s[i].Time() > s[j].Time()
}

func (s sortablePosts) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func postToJSON(ns string, data url.Values) ([]byte, error) {
	// find the content type and decode values into it
	ns = strings.TrimSuffix(ns, "_external")
	t, ok := content.Types[ns]
	if !ok {
		return nil, fmt.Errorf(content.ErrTypeNotRegistered, ns)
	}
	post := t()

	dec := schema.NewDecoder()
	dec.SetAliasTag("json")     // allows simpler struct tagging when creating a content type
	dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct
	err := dec.Decode(post, data)
	if err != nil {
		return nil, err
	}

	slug, err := manager.Slug(post.(editor.Editable))
	if err != nil {
		return nil, err
	}
	post.(editor.Editable).SetSlug(slug)

	// marshall content struct to json for db storage
	j, err := json.Marshal(post)
	if err != nil {
		return nil, err
	}

	return j, nil
}
<commit_msg>adding db procedures and updating handler for external submissions / pending content<commit_after>package db

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net/url"
	"sort"
	"strconv"
	"strings"
	"time"

	"github.com/bosssauce/ponzu/content"
	"github.com/bosssauce/ponzu/management/editor"
	"github.com/bosssauce/ponzu/management/manager"

	"github.com/boltdb/bolt"
	"github.com/gorilla/schema"
)

// SetContent inserts or updates values in the database.
// The `target` argument is a string made up of namespace:id (string:int)
func SetContent(target string, data url.Values) (int, error) {
	t := strings.Split(target, ":")
	ns, id := t[0], t[1]

	// check if content id == -1 (indicating new post).
	// if so, run an insert which will assign the next auto incremented int.
	// this is done because boltdb begins its bucket auto increment value at 0,
	// which is the zero-value of an int in the Item struct field for ID.
	// this is a problem when the original first post (with auto ID = 0) gets
	// overwritten by any new post, originally having no ID, defauting to 0.
	if id == "-1" {
		return insert(ns, data)
	}

	return update(ns, id, data)
}

func update(ns, id string, data url.Values) (int, error) {
	cid, err := strconv.Atoi(id)
	if err != nil {
		return 0, err
	}

	err = store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(ns))
		if err != nil {
			return err
		}

		j, err := postToJSON(ns, data)
		if err != nil {
			return err
		}

		err = b.Put([]byte(fmt.Sprintf("%d", cid)), j)
		if err != nil {
			return err
		}

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

	go SortContent(ns)

	return cid, nil
}

func insert(ns string, data url.Values) (int, error) {
	var effectedID int

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(ns))
		if err != nil {
			return err
		}

		// get the next available ID and convert to string
		// also set effectedID to int of ID
		id, err := b.NextSequence()
		if err != nil {
			return err
		}
		cid := strconv.FormatUint(id, 10)
		effectedID, err = strconv.Atoi(cid)
		if err != nil {
			return err
		}
		data.Set("id", cid)

		j, err := postToJSON(ns, data)
		if err != nil {
			return err
		}

		err = b.Put([]byte(cid), j)
		if err != nil {
			return err
		}

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

	go SortContent(ns)

	return effectedID, nil
}

// SetPendingContent inserts submitted content for pending approval
func SetPendingContent(target string, data url.Values) error {
	if !strings.Contains(target, "_pending") {
		return errors.New("Only set items into _pending bucket using SetPendingContent. Namespace should be <Type>_pending")
	}

	ns := strings.Split(target, "_")[0]

	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(target))
		if err != nil {
			return err
		}

		key := fmt.Sprintf("%d", time.Now().UTC().Unix())
		j, err := postToJSON(ns, data)
		if err != nil {
			return err
		}
		b.Put([]byte(key), j)

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

	return nil
}

// DeleteContent removes an item from the database. Deleting a non-existent item
// will return a nil error.
func DeleteContent(target string) error {
	t := strings.Split(target, ":")
	ns, id := t[0], t[1]

	err := store.Update(func(tx *bolt.Tx) error {
		tx.Bucket([]byte(ns)).Delete([]byte(id))
		return nil
	})

	if err != nil {
		return err
	}

	// exception to typical "run in goroutine" pattern:
	// we want to have an updated admin view as soon as this is deleted, so
	// in some cases, the delete and redirect is faster than the sort,
	// thus still showing a deleted post in the admin view.
	SortContent(ns)

	return nil
}

// Content retrives one item from the database. Non-existent values will return an empty []byte
// The `target` argument is a string made up of namespace:id (string:int)
func Content(target string) ([]byte, error) {
	t := strings.Split(target, ":")
	ns, id := t[0], t[1]

	val := &bytes.Buffer{}
	err := store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(ns))
		_, err := val.Write(b.Get([]byte(id)))
		if err != nil {
			log.Println(err)
			return err
		}

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

	return val.Bytes(), nil
}

// ContentAll retrives all items from the database within the provided namespace
func ContentAll(namespace string) [][]byte {
	var posts [][]byte
	store.View(func(tx *bolt.Tx) error {
		b := tx.Bucket([]byte(namespace))

		if b == nil {
			return nil
		}

		numKeys := b.Stats().KeyN
		posts = make([][]byte, 0, numKeys)

		b.ForEach(func(k, v []byte) error {
			posts = append(posts, v)

			return nil
		})

		return nil
	})

	return posts
}

// SortContent sorts all content of the type supplied as the namespace by time,
// in descending order, from most recent to least recent
// Should be called from a goroutine after SetContent is successful
func SortContent(namespace string) {
	all := ContentAll(namespace)

	var posts sortablePosts
	// decode each (json) into type to then sort
	for i := range all {
		j := all[i]
		post := content.Types[namespace]()

		err := json.Unmarshal(j, &post)
		if err != nil {
			log.Println("Error decoding json while sorting", namespace, ":", err)
			return
		}

		posts = append(posts, post.(editor.Sortable))
	}

	// sort posts
	sort.Sort(posts)

	// store in <namespace>_sorted bucket, first delete existing
	err := store.Update(func(tx *bolt.Tx) error {
		b, err := tx.CreateBucketIfNotExists([]byte(namespace + "_sorted"))
		if err != nil {
			return err
		}

		// encode to json and store as 'i:post.Time()':post
		for i := range posts {
			j, err := json.Marshal(posts[i])
			if err != nil {
				return err
			}

			cid := fmt.Sprintf("%d:%d", i, posts[i].Time())
			err = b.Put([]byte(cid), j)
			if err != nil {
				return err
			}
		}

		return nil
	})
	if err != nil {
		log.Println("Error while updating db with sorted", namespace, err)
	}

}

type sortablePosts []editor.Sortable

func (s sortablePosts) Len() int {
	return len(s)
}

func (s sortablePosts) Less(i, j int) bool {
	return s[i].Time() > s[j].Time()
}

func (s sortablePosts) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]
}

func postToJSON(ns string, data url.Values) ([]byte, error) {
	// find the content type and decode values into it
	ns = strings.TrimSuffix(ns, "_external")
	t, ok := content.Types[ns]
	if !ok {
		return nil, fmt.Errorf(content.ErrTypeNotRegistered, ns)
	}
	post := t()

	dec := schema.NewDecoder()
	dec.SetAliasTag("json")     // allows simpler struct tagging when creating a content type
	dec.IgnoreUnknownKeys(true) // will skip over form values submitted, but not in struct
	err := dec.Decode(post, data)
	if err != nil {
		return nil, err
	}

	slug, err := manager.Slug(post.(editor.Editable))
	if err != nil {
		return nil, err
	}
	post.(editor.Editable).SetSlug(slug)

	// marshall content struct to json for db storage
	j, err := json.Marshal(post)
	if err != nil {
		return nil, err
	}

	return j, nil
}
<|endoftext|>


<commit_before>package herd

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filter"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"os"
	"path"
)

func (sub *Sub) buildUpdateRequest(request *subproto.UpdateRequest) {
	fmt.Println("buildUpdateRequest()") // TODO(rgooch): Delete debugging.
	subFS := sub.fileSystem
	requiredImage := sub.herd.getImage(sub.requiredImage)
	requiredFS := requiredImage.FileSystem
	filter := requiredImage.Filter
	compareDirectories(request, &subFS.Directory, &requiredFS.Directory, "",
		filter)
	// TODO(rgooch): Implement this.
}

func compareDirectories(request *subproto.UpdateRequest,
	subDirectory, requiredDirectory *filesystem.Directory,
	parentName string, filter *filter.Filter) {
	// First look for entries that should be deleted.
	if subDirectory != nil {
		subPathName := path.Join(parentName, subDirectory.Name)
		for name, subEntry := range subDirectory.EntriesByName {
			pathname := path.Join(subPathName, entryName(subEntry))
			if filter.Match(pathname) {
				continue
			}
			if _, ok := requiredDirectory.EntriesByName[name]; !ok {
				request.PathsToDelete = append(request.PathsToDelete, pathname)
				fmt.Printf("Delete: %s\n", pathname) // HACK
			}
		}
		if !filesystem.CompareDirectoriesMetadata(subDirectory,
			requiredDirectory, os.Stdout) {
			fmt.Printf("Different directory: %s...\n",
				requiredDirectory.Name) // HACK
			// TODO(rgooch): Update metadata.
		}
	}
	requiredPathName := path.Join(parentName, requiredDirectory.Name)
	for name, requiredEntry := range requiredDirectory.EntriesByName {
		pathname := path.Join(requiredPathName, entryName(requiredEntry))
		if filter.Match(pathname) {
			continue
		}
		if subDirectory == nil {
			compareEntries(request, nil, requiredEntry, requiredPathName,
				filter)
		} else {
			if subEntry, ok := subDirectory.EntriesByName[name]; ok {
				compareEntries(request, subEntry, requiredEntry,
					requiredPathName, filter)
			} else {
				compareEntries(request, nil, requiredEntry, requiredPathName,
					filter)
			}
		}
	}
}

func entryName(entry interface{}) string {
	switch e := entry.(type) {
	case *filesystem.RegularFile:
		return e.Name
	case *filesystem.Symlink:
		return e.Name
	case *filesystem.File:
		return e.Name
	case *filesystem.Directory:
		return e.Name
	}
	panic("Unsupported entry type")
}

func compareEntries(request *subproto.UpdateRequest,
	subEntry, requiredEntry interface{},
	parentName string, filter *filter.Filter) {
	switch re := requiredEntry.(type) {
	case *filesystem.RegularFile:
		compareRegularFile(request, subEntry, re, parentName)
		return
	case *filesystem.Symlink:
		compareSymlink(request, subEntry, re, parentName)
		return
	case *filesystem.File:
		compareFile(request, subEntry, re, parentName)
		return
	case *filesystem.Directory:
		compareDirectory(request, subEntry, re, parentName, filter)
		return
	}
	panic("Unsupported entry type")
}

func compareRegularFile(request *subproto.UpdateRequest,
	subEntry interface{}, requiredRegularFile *filesystem.RegularFile,
	parentName string) {
	if subRegularFile, ok := subEntry.(*filesystem.RegularFile); ok {
		sameMetadata := filesystem.CompareRegularInodesMetadata(
			subRegularFile.Inode(), requiredRegularFile.Inode(),
			os.Stdout)
		sameData := filesystem.CompareRegularInodesData(subRegularFile.Inode(),
			requiredRegularFile.Inode(), os.Stdout)
		if sameMetadata && sameData {
			return
		}
		fmt.Printf("Different rfile: %s...\n", requiredRegularFile.Name) // HACK
	} else {
		fmt.Printf("Add rfile: %s...\n", requiredRegularFile.Name) // HACK
	}
	// TODO(rgooch): Delete entry and replace.
}

func compareSymlink(request *subproto.UpdateRequest,
	subEntry interface{}, requiredSymlink *filesystem.Symlink,
	parentName string) {
	if subSymlink, ok := subEntry.(*filesystem.Symlink); ok {
		if filesystem.CompareSymlinkInodes(subSymlink.Inode(),
			requiredSymlink.Inode(), os.Stdout) {
			return
		}
		fmt.Printf("Different symlink: %s...\n", requiredSymlink.Name) // HACK
	} else {
		fmt.Printf("Add symlink: %s...\n", requiredSymlink.Name) // HACK
	}
	// TODO(rgooch): Delete entry and replace.
}

func compareFile(request *subproto.UpdateRequest,
	subEntry interface{}, requiredFile *filesystem.File,
	parentName string) {
	if subFile, ok := subEntry.(*filesystem.File); ok {
		if filesystem.CompareInodes(subFile.Inode(), requiredFile.Inode(),
			os.Stdout) {
			return
		}
		fmt.Printf("Different file: %s...\n", requiredFile.Name) // HACK
	} else {
		fmt.Printf("Add file: %s...\n", requiredFile.Name) // HACK
	}
	// TODO(rgooch): Delete entry and replace.
}

func compareDirectory(request *subproto.UpdateRequest,
	subEntry interface{}, requiredDirectory *filesystem.Directory,
	parentName string, filter *filter.Filter) {
	if subDirectory, ok := subEntry.(*filesystem.Directory); ok {
		compareDirectories(request, subDirectory, requiredDirectory,
			parentName, filter)
	} else {
		// TODO(rgooch): Delete entry and replace.
	}
}
<commit_msg>buildUpdateRequest(): make directories.<commit_after>package herd

import (
	"fmt"
	"github.com/Symantec/Dominator/lib/filesystem"
	"github.com/Symantec/Dominator/lib/filter"
	subproto "github.com/Symantec/Dominator/proto/sub"
	"os"
	"path"
)

func (sub *Sub) buildUpdateRequest(request *subproto.UpdateRequest) {
	fmt.Println("buildUpdateRequest()") // TODO(rgooch): Delete debugging.
	subFS := sub.fileSystem
	requiredImage := sub.herd.getImage(sub.requiredImage)
	requiredFS := requiredImage.FileSystem
	filter := requiredImage.Filter
	compareDirectories(request, &subFS.Directory, &requiredFS.Directory, "",
		filter)
	// TODO(rgooch): Implement this.
}

func compareDirectories(request *subproto.UpdateRequest,
	subDirectory, requiredDirectory *filesystem.Directory,
	parentName string, filter *filter.Filter) {
	requiredPathName := path.Join(parentName, requiredDirectory.Name)
	// First look for entries that should be deleted.
	makeSubDirectory := false
	if subDirectory == nil {
		makeSubDirectory = true
	} else {
		subPathName := path.Join(parentName, subDirectory.Name)
		for name, subEntry := range subDirectory.EntriesByName {
			pathname := path.Join(subPathName, entryName(subEntry))
			if filter.Match(pathname) {
				continue
			}
			if _, ok := requiredDirectory.EntriesByName[name]; !ok {
				request.PathsToDelete = append(request.PathsToDelete, pathname)
				fmt.Printf("Delete: %s\n", pathname) // HACK
			}
		}
		if !filesystem.CompareDirectoriesMetadata(subDirectory,
			requiredDirectory, os.Stdout) {
			fmt.Printf("Different directory: %s...\n", requiredPathName) // HACK
			makeSubDirectory = true
			// TODO(rgooch): Update metadata.
		}
	}
	if makeSubDirectory {
		var newdir subproto.Directory
		newdir.Name = requiredPathName
		newdir.Mode = uint32(requiredDirectory.Mode)
		newdir.Uid = requiredDirectory.Uid
		newdir.Gid = requiredDirectory.Gid
		request.DirectoriesToMake = append(request.DirectoriesToMake, newdir)
	}
	for name, requiredEntry := range requiredDirectory.EntriesByName {
		pathname := path.Join(requiredPathName, entryName(requiredEntry))
		if filter.Match(pathname) {
			continue
		}
		if subDirectory == nil {
			compareEntries(request, nil, requiredEntry, requiredPathName,
				filter)
		} else {
			if subEntry, ok := subDirectory.EntriesByName[name]; ok {
				compareEntries(request, subEntry, requiredEntry,
					requiredPathName, filter)
			} else {
				compareEntries(request, nil, requiredEntry, requiredPathName,
					filter)
			}
		}
	}
}

func entryName(entry interface{}) string {
	switch e := entry.(type) {
	case *filesystem.RegularFile:
		return e.Name
	case *filesystem.Symlink:
		return e.Name
	case *filesystem.File:
		return e.Name
	case *filesystem.Directory:
		return e.Name
	}
	panic("Unsupported entry type")
}

func compareEntries(request *subproto.UpdateRequest,
	subEntry, requiredEntry interface{},
	parentName string, filter *filter.Filter) {
	switch re := requiredEntry.(type) {
	case *filesystem.RegularFile:
		compareRegularFile(request, subEntry, re, parentName)
		return
	case *filesystem.Symlink:
		compareSymlink(request, subEntry, re, parentName)
		return
	case *filesystem.File:
		compareFile(request, subEntry, re, parentName)
		return
	case *filesystem.Directory:
		compareDirectory(request, subEntry, re, parentName, filter)
		return
	}
	panic("Unsupported entry type")
}

func compareRegularFile(request *subproto.UpdateRequest,
	subEntry interface{}, requiredRegularFile *filesystem.RegularFile,
	parentName string) {
	if subRegularFile, ok := subEntry.(*filesystem.RegularFile); ok {
		sameMetadata := filesystem.CompareRegularInodesMetadata(
			subRegularFile.Inode(), requiredRegularFile.Inode(),
			os.Stdout)
		sameData := filesystem.CompareRegularInodesData(subRegularFile.Inode(),
			requiredRegularFile.Inode(), os.Stdout)
		if sameMetadata && sameData {
			return
		}
		fmt.Printf("Different rfile: %s...\n", requiredRegularFile.Name) // HACK
	} else {
		fmt.Printf("Add rfile: %s...\n", requiredRegularFile.Name) // HACK
	}
	// TODO(rgooch): Delete entry and replace.
}

func compareSymlink(request *subproto.UpdateRequest,
	subEntry interface{}, requiredSymlink *filesystem.Symlink,
	parentName string) {
	if subSymlink, ok := subEntry.(*filesystem.Symlink); ok {
		if filesystem.CompareSymlinkInodes(subSymlink.Inode(),
			requiredSymlink.Inode(), os.Stdout) {
			return
		}
		fmt.Printf("Different symlink: %s...\n", requiredSymlink.Name) // HACK
	} else {
		fmt.Printf("Add symlink: %s...\n", requiredSymlink.Name) // HACK
	}
	// TODO(rgooch): Delete entry and replace.
}

func compareFile(request *subproto.UpdateRequest,
	subEntry interface{}, requiredFile *filesystem.File,
	parentName string) {
	if subFile, ok := subEntry.(*filesystem.File); ok {
		if filesystem.CompareInodes(subFile.Inode(), requiredFile.Inode(),
			os.Stdout) {
			return
		}
		fmt.Printf("Different file: %s...\n", requiredFile.Name) // HACK
	} else {
		fmt.Printf("Add file: %s...\n", requiredFile.Name) // HACK
	}
	// TODO(rgooch): Delete entry and replace.
}

func compareDirectory(request *subproto.UpdateRequest,
	subEntry interface{}, requiredDirectory *filesystem.Directory,
	parentName string, filter *filter.Filter) {
	if subDirectory, ok := subEntry.(*filesystem.Directory); ok {
		compareDirectories(request, subDirectory, requiredDirectory,
			parentName, filter)
	} else {
		compareDirectories(request, nil, requiredDirectory, parentName, filter)
	}
}
<|endoftext|>


<commit_before>package digestRequest

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/abbot/go-http-auth"
	"golang.org/x/net/context"
)

// startServer is written with referring to
// https://github.com/abbot/go-http-auth/blob/master/examples/digest.go
func startServer(ctx context.Context) *httptest.Server {
	a := auth.NewDigestAuthenticator("example.com", func(user, realm string) string {
		if user == "john" {
			return "b98e16cbc3d01734b264adba7baa3bf9" // password is "hello"
		}
		return ""
	})
	ts := httptest.NewServer(a.Wrap(func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
		fmt.Fprintf(w, "OK")
	}))

	go func() {
		<-ctx.Done()
		ts.Close()
	}()

	return ts
}

func TestDigestRequest(t *testing.T) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	ctx = ContextWithClient(ctx, http.DefaultClient)
	ts := startServer(ctx)

	r := New(ctx, "john", "hello")

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Errorf("error in NewRequest: %v", err)
	}

	resp, err := r.Do(req)
	if err != nil {
		t.Errorf("error in Do: %v", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("error status code: %s", resp.Status)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("error in ReadAll: %v", err)
	}

	if string(b) != "OK" {
		t.Errorf("invalid body: %s", string(b))
	}
}
<commit_msg>Add test for w/o Client pattern<commit_after>package digestRequest

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"testing"

	"github.com/abbot/go-http-auth"
	"golang.org/x/net/context"
)

// startServer is written with referring to
// https://github.com/abbot/go-http-auth/blob/master/examples/digest.go
func startServer(ctx context.Context) *httptest.Server {
	a := auth.NewDigestAuthenticator("example.com", func(user, realm string) string {
		if user == "john" {
			return "b98e16cbc3d01734b264adba7baa3bf9" // password is "hello"
		}
		return ""
	})
	ts := httptest.NewServer(a.Wrap(func(w http.ResponseWriter, r *auth.AuthenticatedRequest) {
		fmt.Fprintf(w, "OK")
	}))

	go func() {
		<-ctx.Done()
		ts.Close()
	}()

	return ts
}

func testRequest(t *testing.T, setClient func(ctx context.Context) context.Context) {
	ctx, cancel := context.WithCancel(context.Background())
	defer cancel()

	if setClient != nil {
		ctx = setClient(ctx)
	}

	ts := startServer(ctx)

	r := New(ctx, "john", "hello")

	req, err := http.NewRequest("GET", ts.URL, nil)
	if err != nil {
		t.Errorf("error in NewRequest: %v", err)
	}

	resp, err := r.Do(req)
	if err != nil {
		t.Errorf("error in Do: %v", err)
	}
	defer func() { _ = resp.Body.Close() }()

	if resp.StatusCode != http.StatusOK {
		t.Errorf("error status code: %s", resp.Status)
	}

	b, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		t.Errorf("error in ReadAll: %v", err)
	}

	if string(b) != "OK" {
		t.Errorf("invalid body: %s", string(b))
	}
}

func TestDigestRequestWithClient(t *testing.T) {
	testRequest(t, func(ctx context.Context) context.Context {
		return ContextWithClient(ctx, http.DefaultClient)
	})
}

func TestDigestRequestWithoutClient(t *testing.T) {
	testRequest(t, nil)
}
<|endoftext|>


<commit_before>// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package asm

import (
	"fmt"
	"testing"
)

func TestDaxpyUnitary(t *testing.T) {
	for i, test := range []struct {
		alpha float64
		xData []float64
		yData []float64

		want []float64
	}{
		{
			alpha: 0,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-3},
		},
		{
			alpha: 1,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-1},
		},
		{
			alpha: 3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{3},
		},
		{
			alpha: -3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-9},
		},
		{
			alpha: 0,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 0, 3, -4, 5, -6},
		},
		{
			alpha: 1,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 1, 4, -2, 2, -10},
		},
		{
			alpha: 3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 3, 6, 2, -4, -18},
		},
		{
			alpha: -3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, -3, 0, -10, 14, 6},
		},
		{
			alpha: -5,
			xData: []float64{0, 0, 1, 1, 2, -3, -4, 5},
			yData: []float64{0, 1, 0, 3, -4, 5, -6, 7},
			want:  []float64{0, 1, -5, -2, -14, 20, 14, -18},
		},
	} {
		const msgGuard = "%v: out-of-bounds write to %v argument\nfront guard: %v\nback guard: %v"

		// Test z = alpha * x + y.
		prefix := fmt.Sprintf("test %v (z=a*x+y)", i)
		x, xFront, xBack := newGuardedVector(test.xData, 1)
		y, yFront, yBack := newGuardedVector(test.yData, 1)
		z, zFront, zBack := newGuardedVector(test.xData, 1)
		DaxpyUnitary(test.alpha, x, y, z)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !allNaN(zFront) || !allNaN(zBack) {
			t.Errorf(msgGuard, prefix, "z", zFront, zBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("%v: modified read-only x argument", prefix)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("%v: modified read-only y argument", prefix)
		}

		if !equalStrided(test.want, z, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, z)
		}

		// Test y = alpha * x + y.
		prefix = fmt.Sprintf("test %v (y=a*x+y)", i)
		x, xFront, xBack = newGuardedVector(test.xData, 1)
		y, yFront, yBack = newGuardedVector(test.yData, 1)
		DaxpyUnitary(test.alpha, x, y, y)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("%v: modified read-only x argument", prefix)
		}

		if !equalStrided(test.want, y, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, y)
		}

		// Test x = alpha * x + y.
		prefix = fmt.Sprintf("test %v (x=a*x+y)", i)
		x, xFront, xBack = newGuardedVector(test.xData, 1)
		y, yFront, yBack = newGuardedVector(test.yData, 1)

		DaxpyUnitary(test.alpha, x, y, x)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("%v: modified read-only y argument", prefix)
		}

		if !equalStrided(test.want, x, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, x)
		}
	}
}
<commit_msg>Add test for DaxpyInc<commit_after>// Copyright ©2015 The gonum Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package asm

import (
	"fmt"
	"testing"
)

func TestDaxpyUnitary(t *testing.T) {
	for i, test := range []struct {
		alpha float64
		xData []float64
		yData []float64

		want []float64
	}{
		{
			alpha: 0,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-3},
		},
		{
			alpha: 1,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-1},
		},
		{
			alpha: 3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{3},
		},
		{
			alpha: -3,
			xData: []float64{2},
			yData: []float64{-3},
			want:  []float64{-9},
		},
		{
			alpha: 0,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 0, 3, -4, 5, -6},
		},
		{
			alpha: 1,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 1, 4, -2, 2, -10},
		},
		{
			alpha: 3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, 3, 6, 2, -4, -18},
		},
		{
			alpha: -3,
			xData: []float64{0, 0, 1, 1, 2, -3, -4},
			yData: []float64{0, 1, 0, 3, -4, 5, -6},
			want:  []float64{0, 1, -3, 0, -10, 14, 6},
		},
		{
			alpha: -5,
			xData: []float64{0, 0, 1, 1, 2, -3, -4, 5},
			yData: []float64{0, 1, 0, 3, -4, 5, -6, 7},
			want:  []float64{0, 1, -5, -2, -14, 20, 14, -18},
		},
	} {
		const msgGuard = "%v: out-of-bounds write to %v argument\nfront guard: %v\nback guard: %v"

		// Test z = alpha * x + y.
		prefix := fmt.Sprintf("test %v (z=a*x+y)", i)
		x, xFront, xBack := newGuardedVector(test.xData, 1)
		y, yFront, yBack := newGuardedVector(test.yData, 1)
		z, zFront, zBack := newGuardedVector(test.xData, 1)
		DaxpyUnitary(test.alpha, x, y, z)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !allNaN(zFront) || !allNaN(zBack) {
			t.Errorf(msgGuard, prefix, "z", zFront, zBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("%v: modified read-only x argument", prefix)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("%v: modified read-only y argument", prefix)
		}

		if !equalStrided(test.want, z, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, z)
		}

		// Test y = alpha * x + y.
		prefix = fmt.Sprintf("test %v (y=a*x+y)", i)
		x, xFront, xBack = newGuardedVector(test.xData, 1)
		y, yFront, yBack = newGuardedVector(test.yData, 1)
		DaxpyUnitary(test.alpha, x, y, y)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !equalStrided(test.xData, x, 1) {
			t.Errorf("%v: modified read-only x argument", prefix)
		}

		if !equalStrided(test.want, y, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, y)
		}

		// Test x = alpha * x + y.
		prefix = fmt.Sprintf("test %v (x=a*x+y)", i)
		x, xFront, xBack = newGuardedVector(test.xData, 1)
		y, yFront, yBack = newGuardedVector(test.yData, 1)

		DaxpyUnitary(test.alpha, x, y, x)

		if !allNaN(xFront) || !allNaN(xBack) {
			t.Errorf(msgGuard, prefix, "x", xFront, xBack)
		}
		if !allNaN(yFront) || !allNaN(yBack) {
			t.Errorf(msgGuard, prefix, "y", yFront, yBack)
		}
		if !equalStrided(test.yData, y, 1) {
			t.Errorf("%v: modified read-only y argument", prefix)
		}

		if !equalStrided(test.want, x, 1) {
			t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, test.want, x)
		}
	}
}

func TestDaxpyInc(t *testing.T) {
	for i, test := range []struct {
		alpha float64
		xData []float64
		yData []float64

		want    []float64
		wantRev []float64 // Result when one vector is traversed in reverse direction.
	}{
		{
			alpha:   0,
			xData:   []float64{2},
			yData:   []float64{-3},
			want:    []float64{-3},
			wantRev: []float64{-3},
		},
		{
			alpha:   1,
			xData:   []float64{2},
			yData:   []float64{-3},
			want:    []float64{-1},
			wantRev: []float64{-1},
		},
		{
			alpha:   3,
			xData:   []float64{2},
			yData:   []float64{-3},
			want:    []float64{3},
			wantRev: []float64{3},
		},
		{
			alpha:   -3,
			xData:   []float64{2},
			yData:   []float64{-3},
			want:    []float64{-9},
			wantRev: []float64{-9},
		},
		{
			alpha:   0,
			xData:   []float64{0, 0, 1, 1, 2, -3, -4},
			yData:   []float64{0, 1, 0, 3, -4, 5, -6},
			want:    []float64{0, 1, 0, 3, -4, 5, -6},
			wantRev: []float64{0, 1, 0, 3, -4, 5, -6},
		},
		{
			alpha:   1,
			xData:   []float64{0, 0, 1, 1, 2, -3, -4},
			yData:   []float64{0, 1, 0, 3, -4, 5, -6},
			want:    []float64{0, 1, 1, 4, -2, 2, -10},
			wantRev: []float64{-4, -2, 2, 4, -3, 5, -6},
		},
		{
			alpha:   3,
			xData:   []float64{0, 0, 1, 1, 2, -3, -4},
			yData:   []float64{0, 1, 0, 3, -4, 5, -6},
			want:    []float64{0, 1, 3, 6, 2, -4, -18},
			wantRev: []float64{-12, -8, 6, 6, -1, 5, -6},
		},
		{
			alpha:   -3,
			xData:   []float64{0, 0, 1, 1, 2, -3, -4},
			yData:   []float64{0, 1, 0, 3, -4, 5, -6},
			want:    []float64{0, 1, -3, 0, -10, 14, 6},
			wantRev: []float64{12, 10, -6, 0, -7, 5, -6},
		},
		{
			alpha:   -5,
			xData:   []float64{0, 0, 1, 1, 2, -3, -4, 5},
			yData:   []float64{0, 1, 0, 3, -4, 5, -6, 7},
			want:    []float64{0, 1, -5, -2, -14, 20, 14, -18},
			wantRev: []float64{-25, 21, 15, -7, -9, 0, -6, 7},
		},
	} {
		const msgGuard = "%v: out-of-bounds write to %v argument\nfront guard: %v\nback guard: %v"
		n := len(test.xData)

		for _, incX := range []int{-7, -4, -3, -2, -1, 1, 2, 3, 4, 7} {
			for _, incY := range []int{-7, -4, -3, -2, -1, 1, 2, 3, 4, 7} {
				var ix, iy int
				if incX < 0 {
					ix = (-n + 1) * incX
				}
				if incY < 0 {
					iy = (-n + 1) * incY
				}

				prefix := fmt.Sprintf("test %v, incX = %v, incY = %v", i, incX, incY)
				x, xFront, xBack := newGuardedVector(test.xData, incX)
				y, yFront, yBack := newGuardedVector(test.yData, incY)
				DaxpyInc(test.alpha, x, y, uintptr(n), uintptr(incX), uintptr(incY), uintptr(ix), uintptr(iy))

				if !allNaN(xFront) || !allNaN(xBack) {
					t.Errorf(msgGuard, prefix, "x", xFront, xBack)
				}
				if !allNaN(yFront) || !allNaN(yBack) {
					t.Errorf(msgGuard, prefix, "y", yFront, yBack)
				}
				if nonStridedWrite(x, incX) || !equalStrided(test.xData, x, incX) {
					t.Errorf("%v: modified read-only x argument", prefix)
				}
				if nonStridedWrite(y, incY) {
					t.Errorf("%v: modified y argument at non-stride position", prefix)
				}

				want := test.want
				if incX*incY < 0 {
					want = test.wantRev
				}
				if !equalStrided(want, y, incY) {
					t.Errorf("%v: unexpected result:\nwant: %v\ngot: %v", prefix, want, y)
				}
			}
		}
	}
}
<|endoftext|>


<commit_before>// Package httphandler implements a HTTP handler
package httphandler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"

	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/brocaar/lora-app-server/internal/handler"
)

var headerNameValidator = regexp.MustCompile(`^[A-Za-z0-9-]+$`)

// HandlerConfig contains the configuration for a HTTP handler.
type HandlerConfig struct {
	Headers              map[string]string `json:"headers"`
	DataUpURL            string            `json:"dataUpURL"`
	JoinNotificationURL  string            `json:"joinNotificationURL"`
	ACKNotificationURL   string            `json:"ackNotificationURL"`
	ErrorNotificationURL string            `json:"errorNotificationURL"`
}

// Validate validates the HandlerConfig data.
func (c HandlerConfig) Validate() error {
	for k := range c.Headers {
		if !headerNameValidator.MatchString(k) {
			return ErrInvalidHeaderName
		}
	}
	return nil
}

// Handler implements a HTTP handler for sending and notifying a HTTP
// endpoint.
type Handler struct {
	config HandlerConfig
}

// NewHandler creates a new HTTPHandler.
func NewHandler(conf HandlerConfig) (*Handler, error) {
	return &Handler{
		config: conf,
	}, nil
}

func (h *Handler) send(url string, payload interface{}) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return errors.Wrap(err, "marshal json error")
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		return errors.Wrap(err, "new request error")
	}

	for k, v := range h.config.Headers {
		req.Header.Set("Content-Type", "application/json")
		req.Header.Set(k, v)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "http request error")
	}

	// check that response is in 200 range
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("expected 2XX response, got: %d", resp.StatusCode)
	}

	return nil
}

// Close closes the handler.
func (h *Handler) Close() error {
	return nil
}

// SendDataUp sends a data-up payload.
func (h *Handler) SendDataUp(pl handler.DataUpPayload) error {
	if h.config.DataUpURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.DataUpURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing data-up payload")
	return h.send(h.config.DataUpURL, pl)
}

// SendJoinNotification sends a join notification.
func (h *Handler) SendJoinNotification(pl handler.JoinNotification) error {
	if h.config.JoinNotificationURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.JoinNotificationURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing join notification")
	return h.send(h.config.JoinNotificationURL, pl)
}

// SendACKNotification sends an ACK notification.
func (h *Handler) SendACKNotification(pl handler.ACKNotification) error {
	if h.config.ACKNotificationURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.ACKNotificationURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing ack notification")
	return h.send(h.config.ACKNotificationURL, pl)
}

// SendErrorNotification sends an error notification.
func (h *Handler) SendErrorNotification(pl handler.ErrorNotification) error {
	if h.config.ErrorNotificationURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.ErrorNotificationURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing error notification")
	return h.send(h.config.ErrorNotificationURL, pl)
}
<commit_msg>Fix setting content-type header.<commit_after>// Package httphandler implements a HTTP handler
package httphandler

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net/http"
	"regexp"

	"github.com/pkg/errors"
	log "github.com/sirupsen/logrus"

	"github.com/brocaar/lora-app-server/internal/handler"
)

var headerNameValidator = regexp.MustCompile(`^[A-Za-z0-9-]+$`)

// HandlerConfig contains the configuration for a HTTP handler.
type HandlerConfig struct {
	Headers              map[string]string `json:"headers"`
	DataUpURL            string            `json:"dataUpURL"`
	JoinNotificationURL  string            `json:"joinNotificationURL"`
	ACKNotificationURL   string            `json:"ackNotificationURL"`
	ErrorNotificationURL string            `json:"errorNotificationURL"`
}

// Validate validates the HandlerConfig data.
func (c HandlerConfig) Validate() error {
	for k := range c.Headers {
		if !headerNameValidator.MatchString(k) {
			return ErrInvalidHeaderName
		}
	}
	return nil
}

// Handler implements a HTTP handler for sending and notifying a HTTP
// endpoint.
type Handler struct {
	config HandlerConfig
}

// NewHandler creates a new HTTPHandler.
func NewHandler(conf HandlerConfig) (*Handler, error) {
	return &Handler{
		config: conf,
	}, nil
}

func (h *Handler) send(url string, payload interface{}) error {
	b, err := json.Marshal(payload)
	if err != nil {
		return errors.Wrap(err, "marshal json error")
	}

	req, err := http.NewRequest("POST", url, bytes.NewReader(b))
	if err != nil {
		return errors.Wrap(err, "new request error")
	}

	req.Header.Set("Content-Type", "application/json")
	for k, v := range h.config.Headers {
		req.Header.Set(k, v)
	}

	resp, err := http.DefaultClient.Do(req)
	if err != nil {
		return errors.Wrap(err, "http request error")
	}

	// check that response is in 200 range
	if resp.StatusCode < 200 || resp.StatusCode >= 300 {
		return fmt.Errorf("expected 2XX response, got: %d", resp.StatusCode)
	}

	return nil
}

// Close closes the handler.
func (h *Handler) Close() error {
	return nil
}

// SendDataUp sends a data-up payload.
func (h *Handler) SendDataUp(pl handler.DataUpPayload) error {
	if h.config.DataUpURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.DataUpURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing data-up payload")
	return h.send(h.config.DataUpURL, pl)
}

// SendJoinNotification sends a join notification.
func (h *Handler) SendJoinNotification(pl handler.JoinNotification) error {
	if h.config.JoinNotificationURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.JoinNotificationURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing join notification")
	return h.send(h.config.JoinNotificationURL, pl)
}

// SendACKNotification sends an ACK notification.
func (h *Handler) SendACKNotification(pl handler.ACKNotification) error {
	if h.config.ACKNotificationURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.ACKNotificationURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing ack notification")
	return h.send(h.config.ACKNotificationURL, pl)
}

// SendErrorNotification sends an error notification.
func (h *Handler) SendErrorNotification(pl handler.ErrorNotification) error {
	if h.config.ErrorNotificationURL == "" {
		return nil
	}

	log.WithFields(log.Fields{
		"url":     h.config.ErrorNotificationURL,
		"dev_eui": pl.DevEUI,
	}).Info("handler/http: publishing error notification")
	return h.send(h.config.ErrorNotificationURL, pl)
}
<|endoftext|>


<commit_before>package driver

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/docker/machine/commands/mcndirs"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/stretchr/testify/assert"
)

const (
	testStoreDir          = ".store-test"
	machineTestName       = "test-host"
	machineTestCaCert     = "test-cert"
	machineTestPrivateKey = "test-key"
)

type DriverOptionsMock struct {
	Data map[string]interface{}
}

func (d DriverOptionsMock) String(key string) string {
	if value, ok := d.Data[key]; ok {
		return value.(string)
	}
	return ""
}

func (d DriverOptionsMock) StringSlice(key string) []string {
	if value, ok := d.Data[key]; ok {
		return value.([]string)
	}
	return []string{}
}

func (d DriverOptionsMock) Int(key string) int {
	if value, ok := d.Data[key]; ok {
		return value.(int)
	}
	return 0
}

func (d DriverOptionsMock) Bool(key string) bool {
	if value, ok := d.Data[key]; ok {
		return value.(bool)
	}
	return false
}

func cleanup() error {
	return os.RemoveAll(testStoreDir)
}

func getTestStorePath() (string, error) {
	tmpDir, err := ioutil.TempDir("", "machine-test-")
	if err != nil {
		return "", err
	}
	mcndirs.BaseDir = tmpDir
	return tmpDir, nil
}

func getDefaultTestDriverFlags() *DriverOptionsMock {
	return &DriverOptionsMock{
		Data: map[string]interface{}{
			"name":                            "test",
			"url":                             "unix:///var/run/docker.sock",
			"sakuracloud-access-token":        "accesstoken",
			"sakuracloud-access-token-secret": "secret",
			"sakuracloud-region":              "is1a",
		},
	}
}

func getTestDriver() (*Driver, error) {
	storePath, err := getTestStorePath()
	if err != nil {
		return nil, err
	}
	defer cleanup()

	d := NewDriver(machineTestName, storePath)
	d.SetConfigFromFlags(getDefaultTestDriverFlags())
	drv := d.(*Driver)
	return drv, nil
}

func TestSetConfigFromFlags(t *testing.T) {
	driver := NewDriver("default", "path")

	checkFlags := &drivers.CheckDriverOptions{
		FlagsValues: map[string]interface{}{
			"sakuracloud-access-token":        "token",
			"sakuracloud-access-token-secret": "secret",
			"sakuracloud-region":              "region",
		},
		CreateFlags: driver.GetCreateFlags(),
	}

	driver.SetConfigFromFlags(checkFlags)

	//assert.NoError(t, err)
	assert.Empty(t, checkFlags.InvalidFlags)
}
<commit_msg>update changelogs<commit_after>package driver

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/docker/machine/commands/mcndirs"
	"github.com/docker/machine/libmachine/drivers"
	"github.com/stretchr/testify/assert"
)

const (
	testStoreDir          = ".store-test"
	machineTestName       = "test-host"
	machineTestCaCert     = "test-cert"
	machineTestPrivateKey = "test-key"
)

type DriverOptionsMock struct {
	Data map[string]interface{}
}

func (d DriverOptionsMock) String(key string) string {
	if value, ok := d.Data[key]; ok {
		return value.(string)
	}
	return ""
}

func (d DriverOptionsMock) StringSlice(key string) []string {
	if value, ok := d.Data[key]; ok {
		return value.([]string)
	}
	return []string{}
}

func (d DriverOptionsMock) Int(key string) int {
	if value, ok := d.Data[key]; ok {
		return value.(int)
	}
	return 0
}

func (d DriverOptionsMock) Bool(key string) bool {
	if value, ok := d.Data[key]; ok {
		return value.(bool)
	}
	return false
}

func cleanup() error {
	return os.RemoveAll(testStoreDir)
}

func getTestStorePath() (string, error) {
	tmpDir, err := ioutil.TempDir("", "machine-test-")
	if err != nil {
		return "", err
	}
	mcndirs.BaseDir = tmpDir
	return tmpDir, nil
}

func getDefaultTestDriverFlags() *DriverOptionsMock {
	return &DriverOptionsMock{
		Data: map[string]interface{}{
			"name": "test",
			"url":  "unix:///var/run/docker.sock",
			"sakuracloud-access-token":        "accesstoken",
			"sakuracloud-access-token-secret": "secret",
			"sakuracloud-region":              "is1a",
		},
	}
}

func getTestDriver() (*Driver, error) {
	storePath, err := getTestStorePath()
	if err != nil {
		return nil, err
	}
	defer cleanup()

	d := NewDriver(machineTestName, storePath)
	d.SetConfigFromFlags(getDefaultTestDriverFlags())
	drv := d.(*Driver)
	return drv, nil
}

func TestSetConfigFromFlags(t *testing.T) {
	driver := NewDriver("default", "path")

	checkFlags := &drivers.CheckDriverOptions{
		FlagsValues: map[string]interface{}{
			"sakuracloud-access-token":        "token",
			"sakuracloud-access-token-secret": "secret",
			"sakuracloud-region":              "region",
		},
		CreateFlags: driver.GetCreateFlags(),
	}

	driver.SetConfigFromFlags(checkFlags)

	//assert.NoError(t, err)
	assert.Empty(t, checkFlags.InvalidFlags)
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"errors"
	"net/http"
	"time"
)

const strategyGoogle string = "Google"
const strategyFitbit string = "FitBit"
const strategyMicrosoft string = "Microsoft"
const strategyParamName string = "FitnessService"

type FitnessReader interface {
	//getData takes a start and end time, and HTTP client for communication with
	//the service an output channle to return data for writing to the command line,
	// and a wait group to make sure things stay open until we are done with all
	// of the go routines.
	// Returns a time of the last data retrived.
	getData(
		client *http.Client,
		output *bufio.Writer,
		username User) time.Time
}

func readerFactory(strategy string, startTime time.Time, endTime time.Time) (FitnessReader, error) {
	switch {
	case strategy == strategyGoogle:
		reader := &GoogleFitnessReader{startTime: startTime, endTime: endTime}
		return reader, nil
	case strategy == strategyFitbit:
		reader := &FitBitReader{startTime: startTime, endTime: endTime}
		return reader, nil
	default:
		return nil, errors.New("Unsupported reader requested: " + string(strategy))
	}
}
<commit_msg>Changed readerFactory to use the fitbit reader constructor.<commit_after>package main

import (
	"bufio"
	"errors"
	"net/http"
	"time"
)

const strategyGoogle string = "Google"
const strategyFitbit string = "FitBit"
const strategyMicrosoft string = "Microsoft"
const strategyParamName string = "FitnessService"

type FitnessReader interface {
	//getData takes a start and end time, and HTTP client for communication with
	//the service an output channle to return data for writing to the command line,
	// and a wait group to make sure things stay open until we are done with all
	// of the go routines.
	// Returns a time of the last data retrived.
	getData(
		client *http.Client,
		output *bufio.Writer,
		username User) time.Time
}

func readerFactory(strategy string, startTime time.Time, endTime time.Time) (FitnessReader, error) {
	switch {
	case strategy == strategyGoogle:
		reader := &GoogleFitnessReader{startTime: startTime, endTime: endTime}
		return reader, nil
	case strategy == strategyFitbit:
		return NewFitbitReader(startTime, endTime)
	default:
		return nil, errors.New("Unsupported reader requested: " + string(strategy))
	}
}
<|endoftext|>


<commit_before>package fakes

import (
	"bytes"
	"errors"
	"os"
	"strings"

	gouuid "github.com/nu7hatch/gouuid"

	bosherr "bosh/errors"
)

type FakeFileType string

const (
	FakeFileTypeFile    FakeFileType = "file"
	FakeFileTypeSymlink FakeFileType = "symlink"
	FakeFileTypeDir     FakeFileType = "dir"
)

type FakeFileSystem struct {
	Files map[string]*FakeFileStats

	HomeDirUsername string
	HomeDirHomePath string

	FilesToOpen map[string]*os.File

	ReadFileError    error
	WriteToFileError error
	MkdirAllError    error
	SymlinkError     error

	CopyDirEntriesError   error
	CopyDirEntriesSrcPath string
	CopyDirEntriesDstPath string

	CopyFileError error

	RenameError    error
	RenameOldPaths []string
	RenameNewPaths []string

	RemoveAllError error

	ReadLinkError error

	TempFileError  error
	ReturnTempFile *os.File

	TempDirDir   string
	TempDirError error

	GlobErr error

	globsMap map[string][][]string
}

type FakeFileStats struct {
	FileMode      os.FileMode
	Username      string
	Content       []byte
	SymlinkTarget string
	FileType      FakeFileType
}

func (stats FakeFileStats) StringContents() string {
	return string(stats.Content)
}

func NewFakeFileSystem() *FakeFileSystem {
	return &FakeFileSystem{
		globsMap: make(map[string][][]string),
	}
}

func (fs *FakeFileSystem) GetFileTestStat(path string) (stats *FakeFileStats) {
	stats = fs.Files[path]
	return
}

func (fs *FakeFileSystem) HomeDir(username string) (path string, err error) {
	fs.HomeDirUsername = username
	path = fs.HomeDirHomePath
	return
}

func (fs *FakeFileSystem) MkdirAll(path string, perm os.FileMode) (err error) {
	if fs.MkdirAllError == nil {
		stats := fs.getOrCreateFile(path)
		stats.FileMode = perm
		stats.FileType = FakeFileTypeDir
	}

	err = fs.MkdirAllError
	return
}

func (fs *FakeFileSystem) Chown(path, username string) (err error) {
	stats := fs.GetFileTestStat(path)
	stats.Username = username
	return
}

func (fs *FakeFileSystem) Chmod(path string, perm os.FileMode) (err error) {
	stats := fs.GetFileTestStat(path)
	stats.FileMode = perm
	return
}

func (fs *FakeFileSystem) WriteFileString(path, content string) (err error) {
	return fs.WriteFile(path, []byte(content))
}

func (fs *FakeFileSystem) WriteFile(path string, content []byte) (err error) {
	if fs.WriteToFileError != nil {
		err = fs.WriteToFileError
		return
	}

	stats := fs.getOrCreateFile(path)
	stats.FileType = FakeFileTypeFile
	stats.Content = content
	return
}

func (fs *FakeFileSystem) ConvergeFileContents(path string, content []byte) (written bool, err error) {
	if fs.WriteToFileError != nil {
		err = fs.WriteToFileError
		return
	}

	stats := fs.getOrCreateFile(path)
	stats.FileType = FakeFileTypeFile

	if bytes.Compare(stats.Content, content) != 0 {
		stats.Content = content
		written = true
	}
	return
}

func (fs *FakeFileSystem) ReadFileString(path string) (content string, err error) {
	bytes, err := fs.ReadFile(path)
	if err != nil {
		return
	}

	content = string(bytes)
	return
}

func (fs *FakeFileSystem) ReadFile(path string) ([]byte, error) {
	stats := fs.GetFileTestStat(path)
	if stats != nil {
		if fs.ReadFileError != nil {
			return nil, fs.ReadFileError
		}
		return stats.Content, nil
	}
	return nil, errors.New("File not found")
}

func (fs *FakeFileSystem) FileExists(path string) bool {
	return fs.GetFileTestStat(path) != nil
}

func (fs *FakeFileSystem) Rename(oldPath, newPath string) (err error) {
	if fs.RenameError != nil {
		err = fs.RenameError
		return
	}

	stats := fs.GetFileTestStat(oldPath)
	if stats == nil {
		err = errors.New("Old path did not exist")
		return
	}

	fs.RenameOldPaths = append(fs.RenameOldPaths, oldPath)
	fs.RenameNewPaths = append(fs.RenameNewPaths, newPath)

	newStats := fs.getOrCreateFile(newPath)
	newStats.Content = stats.Content
	newStats.FileMode = stats.FileMode
	newStats.FileType = stats.FileType

	fs.RemoveAll(oldPath)

	return
}

func (fs *FakeFileSystem) Symlink(oldPath, newPath string) (err error) {
	if fs.SymlinkError == nil {
		stats := fs.getOrCreateFile(newPath)
		stats.FileType = FakeFileTypeSymlink
		stats.SymlinkTarget = oldPath
		return
	}

	err = fs.SymlinkError
	return
}

func (fs *FakeFileSystem) ReadLink(symlinkPath string) (string, error) {
	if fs.ReadLinkError != nil {
		return "", fs.ReadLinkError
	}

	stat := fs.GetFileTestStat(symlinkPath)
	if stat != nil {
		return stat.SymlinkTarget, nil
	}

	return "", os.ErrNotExist
}

func (fs *FakeFileSystem) CopyDirEntries(srcPath, dstPath string) (err error) {
	if fs.CopyDirEntriesError != nil {
		return fs.CopyDirEntriesError
	}

	filesToCopy := []string{}

	for filePath := range fs.Files {
		if strings.HasPrefix(filePath, srcPath) {
			filesToCopy = append(filesToCopy, filePath)
		}
	}

	for _, filePath := range filesToCopy {
		newPath := strings.Replace(filePath, srcPath, dstPath, 1)
		fs.Files[newPath] = fs.Files[filePath]
	}

	return
}

func (fs *FakeFileSystem) CopyFile(srcPath, dstPath string) (err error) {
	if fs.CopyFileError != nil {
		err = fs.CopyFileError
		return
	}

	fs.Files[dstPath] = fs.Files[srcPath]
	return
}

func (fs *FakeFileSystem) TempFile(prefix string) (file *os.File, err error) {
	if fs.TempFileError != nil {
		return nil, fs.TempFileError
	}

	if fs.ReturnTempFile != nil {
		return fs.ReturnTempFile, nil
	}

	file, err = os.Open("/dev/null")
	if err != nil {
		err = bosherr.WrapError(err, "Opening /dev/null")
		return
	}

	// Make sure to record a reference for FileExist, etc. to work
	stats := fs.getOrCreateFile(file.Name())
	stats.FileType = FakeFileTypeFile
	return
}

func (fs *FakeFileSystem) TempDir(prefix string) (string, error) {
	if fs.TempDirError != nil {
		return "", fs.TempDirError
	}

	var path string
	if len(fs.TempDirDir) > 0 {
		path = fs.TempDirDir
	} else {
		uuid, err := gouuid.NewV4()
		if err != nil {
			return "", err
		}

		path = uuid.String()
	}

	// Make sure to record a reference for FileExist, etc. to work
	stats := fs.getOrCreateFile(path)
	stats.FileType = FakeFileTypeDir

	return path, nil
}

func (fs *FakeFileSystem) RemoveAll(path string) (err error) {
	if fs.RemoveAllError != nil {
		return fs.RemoveAllError
	}

	filesToRemove := []string{}

	for name := range fs.Files {
		if strings.HasPrefix(name, path) {
			filesToRemove = append(filesToRemove, name)
		}
	}

	for _, name := range filesToRemove {
		delete(fs.Files, name)
	}
	return
}

func (fs *FakeFileSystem) Open(path string) (file *os.File, err error) {
	file = fs.FilesToOpen[path]
	return
}

func (fs *FakeFileSystem) Glob(pattern string) (matches []string, err error) {
	remainingMatches, found := fs.globsMap[pattern]
	if found {
		matches = remainingMatches[0]
		if len(remainingMatches) > 1 {
			fs.globsMap[pattern] = remainingMatches[1:]
		}
	} else {
		matches = []string{}
	}
	return matches, fs.GlobErr
}

func (fs *FakeFileSystem) SetGlob(pattern string, matches ...[]string) {
	fs.globsMap[pattern] = matches
	return
}

func (fs *FakeFileSystem) getOrCreateFile(path string) (stats *FakeFileStats) {
	stats = fs.GetFileTestStat(path)
	if stats == nil {
		if fs.Files == nil {
			fs.Files = make(map[string]*FakeFileStats)
		}

		stats = new(FakeFileStats)
		fs.Files[path] = stats
	}
	return
}
<commit_msg>added ChownErr and ChmodErr<commit_after>package fakes

import (
	"bytes"
	"errors"
	"os"
	"strings"

	gouuid "github.com/nu7hatch/gouuid"

	bosherr "bosh/errors"
)

type FakeFileType string

const (
	FakeFileTypeFile    FakeFileType = "file"
	FakeFileTypeSymlink FakeFileType = "symlink"
	FakeFileTypeDir     FakeFileType = "dir"
)

type FakeFileSystem struct {
	Files map[string]*FakeFileStats

	HomeDirUsername string
	HomeDirHomePath string

	FilesToOpen map[string]*os.File

	ReadFileError    error
	WriteToFileError error
	MkdirAllError    error
	SymlinkError     error

	ChownErr error
	ChmodErr error

	CopyDirEntriesError   error
	CopyDirEntriesSrcPath string
	CopyDirEntriesDstPath string

	CopyFileError error

	RenameError    error
	RenameOldPaths []string
	RenameNewPaths []string

	RemoveAllError error

	ReadLinkError error

	TempFileError  error
	ReturnTempFile *os.File

	TempDirDir   string
	TempDirError error

	GlobErr error

	globsMap map[string][][]string
}

type FakeFileStats struct {
	FileMode      os.FileMode
	Username      string
	Content       []byte
	SymlinkTarget string
	FileType      FakeFileType
}

func (stats FakeFileStats) StringContents() string {
	return string(stats.Content)
}

func NewFakeFileSystem() *FakeFileSystem {
	return &FakeFileSystem{
		globsMap: make(map[string][][]string),
	}
}

func (fs *FakeFileSystem) GetFileTestStat(path string) (stats *FakeFileStats) {
	stats = fs.Files[path]
	return
}

func (fs *FakeFileSystem) HomeDir(username string) (path string, err error) {
	fs.HomeDirUsername = username
	path = fs.HomeDirHomePath
	return
}

func (fs *FakeFileSystem) MkdirAll(path string, perm os.FileMode) (err error) {
	if fs.MkdirAllError == nil {
		stats := fs.getOrCreateFile(path)
		stats.FileMode = perm
		stats.FileType = FakeFileTypeDir
	}

	err = fs.MkdirAllError
	return
}

func (fs *FakeFileSystem) Chown(path, username string) error {
	// check early to avoid requiring file presence
	if fs.ChownErr != nil {
		return fs.ChownErr
	}
	stats := fs.GetFileTestStat(path)
	stats.Username = username
	return nil
}

func (fs *FakeFileSystem) Chmod(path string, perm os.FileMode) error {
	// check early to avoid requiring file presence
	if fs.ChmodErr != nil {
		return fs.ChmodErr
	}
	stats := fs.GetFileTestStat(path)
	stats.FileMode = perm
	return nil
}

func (fs *FakeFileSystem) WriteFileString(path, content string) (err error) {
	return fs.WriteFile(path, []byte(content))
}

func (fs *FakeFileSystem) WriteFile(path string, content []byte) (err error) {
	if fs.WriteToFileError != nil {
		err = fs.WriteToFileError
		return
	}

	stats := fs.getOrCreateFile(path)
	stats.FileType = FakeFileTypeFile
	stats.Content = content
	return
}

func (fs *FakeFileSystem) ConvergeFileContents(path string, content []byte) (written bool, err error) {
	if fs.WriteToFileError != nil {
		err = fs.WriteToFileError
		return
	}

	stats := fs.getOrCreateFile(path)
	stats.FileType = FakeFileTypeFile

	if bytes.Compare(stats.Content, content) != 0 {
		stats.Content = content
		written = true
	}
	return
}

func (fs *FakeFileSystem) ReadFileString(path string) (content string, err error) {
	bytes, err := fs.ReadFile(path)
	if err != nil {
		return
	}

	content = string(bytes)
	return
}

func (fs *FakeFileSystem) ReadFile(path string) ([]byte, error) {
	stats := fs.GetFileTestStat(path)
	if stats != nil {
		if fs.ReadFileError != nil {
			return nil, fs.ReadFileError
		}
		return stats.Content, nil
	}
	return nil, errors.New("File not found")
}

func (fs *FakeFileSystem) FileExists(path string) bool {
	return fs.GetFileTestStat(path) != nil
}

func (fs *FakeFileSystem) Rename(oldPath, newPath string) (err error) {
	if fs.RenameError != nil {
		err = fs.RenameError
		return
	}

	stats := fs.GetFileTestStat(oldPath)
	if stats == nil {
		err = errors.New("Old path did not exist")
		return
	}

	fs.RenameOldPaths = append(fs.RenameOldPaths, oldPath)
	fs.RenameNewPaths = append(fs.RenameNewPaths, newPath)

	newStats := fs.getOrCreateFile(newPath)
	newStats.Content = stats.Content
	newStats.FileMode = stats.FileMode
	newStats.FileType = stats.FileType

	fs.RemoveAll(oldPath)

	return
}

func (fs *FakeFileSystem) Symlink(oldPath, newPath string) (err error) {
	if fs.SymlinkError == nil {
		stats := fs.getOrCreateFile(newPath)
		stats.FileType = FakeFileTypeSymlink
		stats.SymlinkTarget = oldPath
		return
	}

	err = fs.SymlinkError
	return
}

func (fs *FakeFileSystem) ReadLink(symlinkPath string) (string, error) {
	if fs.ReadLinkError != nil {
		return "", fs.ReadLinkError
	}

	stat := fs.GetFileTestStat(symlinkPath)
	if stat != nil {
		return stat.SymlinkTarget, nil
	}

	return "", os.ErrNotExist
}

func (fs *FakeFileSystem) CopyDirEntries(srcPath, dstPath string) (err error) {
	if fs.CopyDirEntriesError != nil {
		return fs.CopyDirEntriesError
	}

	filesToCopy := []string{}

	for filePath := range fs.Files {
		if strings.HasPrefix(filePath, srcPath) {
			filesToCopy = append(filesToCopy, filePath)
		}
	}

	for _, filePath := range filesToCopy {
		newPath := strings.Replace(filePath, srcPath, dstPath, 1)
		fs.Files[newPath] = fs.Files[filePath]
	}

	return
}

func (fs *FakeFileSystem) CopyFile(srcPath, dstPath string) (err error) {
	if fs.CopyFileError != nil {
		err = fs.CopyFileError
		return
	}

	fs.Files[dstPath] = fs.Files[srcPath]
	return
}

func (fs *FakeFileSystem) TempFile(prefix string) (file *os.File, err error) {
	if fs.TempFileError != nil {
		return nil, fs.TempFileError
	}

	if fs.ReturnTempFile != nil {
		return fs.ReturnTempFile, nil
	}

	file, err = os.Open("/dev/null")
	if err != nil {
		err = bosherr.WrapError(err, "Opening /dev/null")
		return
	}

	// Make sure to record a reference for FileExist, etc. to work
	stats := fs.getOrCreateFile(file.Name())
	stats.FileType = FakeFileTypeFile
	return
}

func (fs *FakeFileSystem) TempDir(prefix string) (string, error) {
	if fs.TempDirError != nil {
		return "", fs.TempDirError
	}

	var path string
	if len(fs.TempDirDir) > 0 {
		path = fs.TempDirDir
	} else {
		uuid, err := gouuid.NewV4()
		if err != nil {
			return "", err
		}

		path = uuid.String()
	}

	// Make sure to record a reference for FileExist, etc. to work
	stats := fs.getOrCreateFile(path)
	stats.FileType = FakeFileTypeDir

	return path, nil
}

func (fs *FakeFileSystem) RemoveAll(path string) (err error) {
	if fs.RemoveAllError != nil {
		return fs.RemoveAllError
	}

	filesToRemove := []string{}

	for name := range fs.Files {
		if strings.HasPrefix(name, path) {
			filesToRemove = append(filesToRemove, name)
		}
	}

	for _, name := range filesToRemove {
		delete(fs.Files, name)
	}
	return
}

func (fs *FakeFileSystem) Open(path string) (file *os.File, err error) {
	file = fs.FilesToOpen[path]
	return
}

func (fs *FakeFileSystem) Glob(pattern string) (matches []string, err error) {
	remainingMatches, found := fs.globsMap[pattern]
	if found {
		matches = remainingMatches[0]
		if len(remainingMatches) > 1 {
			fs.globsMap[pattern] = remainingMatches[1:]
		}
	} else {
		matches = []string{}
	}
	return matches, fs.GlobErr
}

func (fs *FakeFileSystem) SetGlob(pattern string, matches ...[]string) {
	fs.globsMap[pattern] = matches
	return
}

func (fs *FakeFileSystem) getOrCreateFile(path string) (stats *FakeFileStats) {
	stats = fs.GetFileTestStat(path)
	if stats == nil {
		if fs.Files == nil {
			fs.Files = make(map[string]*FakeFileStats)
		}

		stats = new(FakeFileStats)
		fs.Files[path] = stats
	}
	return
}
<|endoftext|>