<commit_before>package flux

import (
	"errors"
	"log"
	"sync"
	"sync/atomic"
	"time"
)

//WaitInterface defines the flux.Wait interface method definitions
type WaitInterface interface {
	Add()
	Done()
	Count() int
	Flush()
	Then() ActionInterface
}

var (
	//ErrBadState stands for a struct in a bad state
	ErrBadState = errors.New("")
)

/*WhileTicker provides a time.After derivative with
**Important**
When using WhileTicker signal channel is built for use in a select/forselect loop
ensure you as such.
*/
type WhileTicker struct {
	Duration time.Duration
	signals  chan struct{}
	initd    int64
	closed   int64
}

//Signals return the signal channel use for signal a clock
func (w *WhileTicker) Signals() chan struct{} {
	return w.signals
}

//Stop begins the ticker
func (w *WhileTicker) Stop() {
	state := atomic.LoadInt64(&w.initd)
	if state <= 0 {
		return
	}
	atomic.StoreInt64(&w.initd, 0)
	atomic.StoreInt64(&w.closed, 1)
	w.signals = nil
}

//Start begins the ticker
func (w *WhileTicker) Start() {
	cs := int(atomic.LoadInt64(&w.closed))
	if cs > 0 {
		w.signals = make(chan struct{})
		atomic.StoreInt64(&w.closed, 0)
	}

	state := atomic.LoadInt64(&w.initd)
	if state > 0 {
		return
	}

	atomic.StoreInt64(&w.initd, 1)

	GoDefer("WhileTickerLoop", func() {
	tickloop:
		for {

			end := atomic.LoadInt64(&w.initd)

			if end <= 0 {
				break tickloop
			}

			time.Sleep(w.Duration)

			// go func() {
			w.signals <- struct{}{}
			// }()
		}
	})

}

//While returns a new WhileTicker instance
func While(ms time.Duration) *WhileTicker {
	return &WhileTicker{Duration: ms, signals: make(chan struct{}), initd: 0, closed: 0}
}

//ResetTimer runs a timer and performs an action
type ResetTimer struct {
	reset    chan struct{}
	kill     chan struct{}
	duration time.Duration
	do       *sync.Once
	init     func()
	done     func()
	state    int64
	started  int64
	lock     *sync.Mutex
}

//NewResetTimer returns a new reset timer
func NewResetTimer(init func(), done func(), d time.Duration, run, boot bool) *ResetTimer {
	rs := &ResetTimer{
		reset:    make(chan struct{}),
		kill:     make(chan struct{}),
		duration: d,
		do:       new(sync.Once),
		init:     init,
		done:     done,
		state:    0,
		started:  1,
		lock:     new(sync.Mutex),
	}

	if run {
		rs.runInit()
	}

	if boot {
		rs.handle()
	}

	return rs
}

//RunInit runs the init function
func (r *ResetTimer) runInit() {
	if r.init != nil {
		r.init()
	}
}

//Add reset the timer threshold
func (r *ResetTimer) Add() {
	r.lock.Lock()
	defer r.lock.Unlock()
	state := int(atomic.LoadInt64(&r.state))
	startd := int(atomic.LoadInt64(&r.started))

	if r.kill == nil {
		r.kill = make(chan struct{})
	}

	if r.reset == nil {
		r.reset = make(chan struct{})
	}

	log.Printf("Waiter checking State! State at %d Started %d", state, startd)

	if state > 0 {
		r.reset <- struct{}{}
	} else {
		r.runInit()
		r.handle()
		atomic.StoreInt64(&r.state, 1)
		atomic.StoreInt64(&r.started, 1)
	}
}

//Close closes this timer
func (r *ResetTimer) Close() {
	r.lock.Lock()
	defer r.lock.Unlock()
	state := atomic.LoadInt64(&r.started)

	if state > 0 {
		defer func() {
			r.kill = nil
			r.reset = nil
		}()
		close(r.kill)
		close(r.reset)
	}

	atomic.StoreInt64(&r.state, 0)
	atomic.StoreInt64(&r.started, 0)
}

func (r *ResetTimer) makeTime() <-chan time.Time {
	return time.After(r.duration)
}

func (r *ResetTimer) handle() {
	go func() {
		threshold := r.makeTime()

		reset := r.reset
		kill := r.kill

	resetloop:
		for {
			select {
			case <-reset:
				log.Printf("ResetTimer: reseting timer by duration %+s", r.duration)
				threshold = r.makeTime()
			case <-threshold:
				log.Printf("ResetTimer: timer expired with duration %+s", r.duration)
				r.done()
				atomic.StoreInt64(&r.state, 0)
				break resetloop
			case <-kill:
				log.Printf("ResetTimer: timer killed using duration %+s", r.duration)
				atomic.StoreInt64(&r.state, 0)
				break resetloop
			}
		}

	}()
}

//SwitchInterface defines a flux.Switch interface method definition
type SwitchInterface interface {
	Switch()
	IsOn() bool
	WhenOn() ActionInterface
	WhenOff() ActionInterface
}

//WaitGen is a nice way of creating regenerative timers for use
//wait timers are once timers, once they are clocked out they are of no more use,to allow their nature which has its benefits we get to create WaitGen that generates a new once once a wait gen is over
type WaitGen struct {
	current WaitInterface
	gen     func() WaitInterface
}

//Make returns a new WaitInterface or returns the current once
func (w *WaitGen) Make() WaitInterface {
	if w.current != nil {
		return w.current
	}
	wt := w.gen()
	wt.Then().WhenOnly(func(_ interface{}) {
		w.current = nil
	})
	return wt
}

//NewTimeWaitGen returns a wait generator making a timewaiter
func NewTimeWaitGen(steps int, ms time.Duration, init func(WaitInterface)) *WaitGen {
	return &WaitGen{
		nil,
		func() WaitInterface {
			nt := NewTimeWait(steps, ms)
			init(nt)
			return nt
		},
	}
}

//NewSimpleWaitGen returns a wait generator making a timewaiter
func NewSimpleWaitGen(init func(WaitInterface)) *WaitGen {
	return &WaitGen{
		nil,
		func() WaitInterface {
			nt := NewWait()
			init(nt)
			return nt
		},
	}
}

//baseWait defines the base wait structure for all waiters
type baseWait struct {
	action ActionInterface
}

//Then returns an ActionInterface which gets fullfilled when this wait
//counter reaches zero
func (w *baseWait) Then() ActionInterface {
	return w.action.Wrap()
}

func newBaseWait() *baseWait {
	return &baseWait{NewAction()}
}

//TimeWait defines a time lock waiter
type TimeWait struct {
	*baseWait
	closer chan struct{}
	hits   int64
	max    int
	ms     time.Duration
	doonce *sync.Once
}

//NewTimeWait returns a new timer wait locker
//You specifiy two arguments:
//max int: the maximum number of time you want to check for idleness
//duration time.Duration: the time to check for each idle times and reduce
//until zero is reached then close
//eg. to do a 15seconds check for idleness
//NewTimeWait(15,time.Duration(1)*time.Second)
//eg. to do a 25 maximum check before closing per minute
//NewTimeWait(15,time.Duration(1)*time.Minute)
func NewTimeWait(max int, duration time.Duration) *TimeWait {

	tm := &TimeWait{
		newBaseWait(),
		make(chan struct{}),
		int64(max),
		max,
		duration,
		new(sync.Once),
	}

	// tm.Add()
	go tm.handle()

	return tm
}

//handle effects the necessary time process for checking and reducing the
//time checker for each duration of time,till the Waiter is done
func (w *TimeWait) handle() {
	var state int64
	atomic.StoreInt64(&state, 0)

	go func() {
		<-w.closer
		atomic.StoreInt64(&state, 1)
	}()

	for {
		time.Sleep(w.ms)

		bit := atomic.LoadInt64(&state)
		if bit > 0 {
			break
		}

		w.Done()
	}
}

//Flush drops the lock count and forces immediate unlocking of the wait
func (w *TimeWait) Flush() {
	w.doonce.Do(func() {
		close(w.closer)
		w.action.Fullfill(0)
		atomic.StoreInt64(&w.hits, 0)
	})
}

//Count returns the total left count to completed before unlock
func (w *TimeWait) Count() int {
	return int(atomic.LoadInt64(&w.hits))
}

//Add increments the lock state to the lock counter unless its already unlocked
func (w *TimeWait) Add() {
	if w.Count() < 0 || w.Count() >= w.max {
		return
	}

	atomic.AddInt64(&w.hits, 1)
}

//Done decrements the totalcount of this waitlocker by 1 until its below zero
//and fullfills with the 0 value
func (w *TimeWait) Done() {
	hits := atomic.LoadInt64(&w.hits)

	if hits < 0 {
		return
	}

	newhit := atomic.AddInt64(&w.hits, -1)
	log.Printf("TimeWait: Count Down now %d before %d!", newhit, hits)
	if int(newhit) <= 0 {
		w.Flush()
		log.Printf("TimeWait: Count Down Finished!")
	}
}

//Wait implements the WiatInterface for creating a wait lock which
//waits until the lock lockcount is finished then executes a action
//can only be used once, that is ,once the wait counter is -1,you cant add
//to it anymore
type Wait struct {
	*baseWait
	totalCount int64
}

//NewWait returns a new Wait instance for the WaitInterface
func NewWait() WaitInterface {
	return &Wait{newBaseWait(), int64(0)}
}

//Flush drops the lock count and forces immediate unlocking of the wait
func (w *Wait) Flush() {
	curr := int(atomic.LoadInt64(&w.totalCount))
	if curr < 0 {
		return
	}

	atomic.StoreInt64(&w.totalCount, 0)
	w.Done()
}

//Count returns the total left count to completed before unlock
func (w *Wait) Count() int {
	return int(atomic.LoadInt64(&w.totalCount))
}

//Add increments the lock state to the lock counter unless its already unlocked
func (w *Wait) Add() {
	curr := atomic.LoadInt64(&w.totalCount)

	if curr < 0 {
		return
	}

	atomic.AddInt64(&w.totalCount, 1)
}

//Done decrements the totalcount of this waitlocker by 1 until its below zero
//and fullfills with the 0 value
func (w *Wait) Done() {
	curr := atomic.LoadInt64(&w.totalCount)

	if curr < 0 {
		return
	}

	nc := atomic.AddInt64(&w.totalCount, -1)
	// log.Printf("Wait: Count Down now %d before %d", nc, curr)

	if int(nc) <= 0 {
		w.action.Fullfill(0)
	}
}
<commit_msg>adding whileticker<commit_after>package flux

import (
	"errors"
	"log"
	"sync"
	"sync/atomic"
	"time"
)

//WaitInterface defines the flux.Wait interface method definitions
type WaitInterface interface {
	Add()
	Done()
	Count() int
	Flush()
	Then() ActionInterface
}

var (
	//ErrBadState stands for a struct in a bad state
	ErrBadState = errors.New("")
)

/*WhileTicker provides a time.After derivative with
**Important**
When using WhileTicker signal channel is built for use in a select/forselect loop
ensure you as such.
*/
type WhileTicker struct {
	Duration time.Duration
	signals  chan struct{}
	initd    int64
	reset    int64
	closed   int64
}

//Signals return the signal channel use for signal a clock
func (w *WhileTicker) Signals() chan struct{} {
	return w.signals
}

//Stop begins the ticker
func (w *WhileTicker) Stop() {
	state := atomic.LoadInt64(&w.initd)
	if state <= 0 {
		return
	}
	atomic.StoreInt64(&w.initd, 0)
	atomic.StoreInt64(&w.reset, 0)
	atomic.StoreInt64(&w.closed, 1)
	w.signals = nil
}

//Skip begins the ticker
func (w *WhileTicker) Skip() {
	atomic.StoreInt64(&w.initd, 1)
}

//Start begins the ticker
func (w *WhileTicker) Start() {

	cs := int(atomic.LoadInt64(&w.closed))

	if cs > 0 {
		w.signals = make(chan struct{})
		atomic.StoreInt64(&w.closed, 0)
	}

	state := atomic.LoadInt64(&w.initd)

	if state > 0 {
		return
	}

	atomic.StoreInt64(&w.initd, 1)

	GoDefer("WhileTickerLoop", func() {
	tickloop:
		for {

			rs := int(atomic.LoadInt64(&w.reset))

			if rs > 0 {
				atomic.StoreInt64(&w.reset, 0)
				continue
			}

			end := atomic.LoadInt64(&w.initd)

			if end <= 0 {
				break tickloop
			}

			time.Sleep(w.Duration)

			// go func() {
			w.signals <- struct{}{}
			// }()
		}
	})

}

//While returns a new WhileTicker instance
func While(ms time.Duration) *WhileTicker {
	return &WhileTicker{
		Duration: ms,
		signals:  make(chan struct{}),
		initd:    0,
		closed:   0,
		reset:    0,
	}
}

//ResetTimer runs a timer and performs an action
type ResetTimer struct {
	reset    chan struct{}
	kill     chan struct{}
	duration time.Duration
	do       *sync.Once
	init     func()
	done     func()
	state    int64
	started  int64
	lock     *sync.Mutex
}

//NewResetTimer returns a new reset timer
func NewResetTimer(init func(), done func(), d time.Duration, run, boot bool) *ResetTimer {
	rs := &ResetTimer{
		reset:    make(chan struct{}),
		kill:     make(chan struct{}),
		duration: d,
		do:       new(sync.Once),
		init:     init,
		done:     done,
		state:    0,
		started:  1,
		lock:     new(sync.Mutex),
	}

	if run {
		rs.runInit()
	}

	if boot {
		rs.handle()
	}

	return rs
}

//RunInit runs the init function
func (r *ResetTimer) runInit() {
	if r.init != nil {
		r.init()
	}
}

//Add reset the timer threshold
func (r *ResetTimer) Add() {
	r.lock.Lock()
	defer r.lock.Unlock()
	state := int(atomic.LoadInt64(&r.state))
	startd := int(atomic.LoadInt64(&r.started))

	if r.kill == nil {
		r.kill = make(chan struct{})
	}

	if r.reset == nil {
		r.reset = make(chan struct{})
	}

	log.Printf("Waiter checking State! State at %d Started %d", state, startd)

	if state > 0 {
		r.reset <- struct{}{}
	} else {
		r.runInit()
		r.handle()
		atomic.StoreInt64(&r.state, 1)
		atomic.StoreInt64(&r.started, 1)
	}
}

//Close closes this timer
func (r *ResetTimer) Close() {
	r.lock.Lock()
	defer r.lock.Unlock()
	state := atomic.LoadInt64(&r.started)

	if state > 0 {
		defer func() {
			r.kill = nil
			r.reset = nil
		}()
		close(r.kill)
		close(r.reset)
	}

	atomic.StoreInt64(&r.state, 0)
	atomic.StoreInt64(&r.started, 0)
}

func (r *ResetTimer) makeTime() <-chan time.Time {
	return time.After(r.duration)
}

func (r *ResetTimer) handle() {
	go func() {
		threshold := r.makeTime()

		reset := r.reset
		kill := r.kill

	resetloop:
		for {
			select {
			case <-reset:
				log.Printf("ResetTimer: reseting timer by duration %+s", r.duration)
				threshold = r.makeTime()
			case <-threshold:
				log.Printf("ResetTimer: timer expired with duration %+s", r.duration)
				r.done()
				atomic.StoreInt64(&r.state, 0)
				break resetloop
			case <-kill:
				log.Printf("ResetTimer: timer killed using duration %+s", r.duration)
				atomic.StoreInt64(&r.state, 0)
				break resetloop
			}
		}

	}()
}

//SwitchInterface defines a flux.Switch interface method definition
type SwitchInterface interface {
	Switch()
	IsOn() bool
	WhenOn() ActionInterface
	WhenOff() ActionInterface
}

//WaitGen is a nice way of creating regenerative timers for use
//wait timers are once timers, once they are clocked out they are of no more use,to allow their nature which has its benefits we get to create WaitGen that generates a new once once a wait gen is over
type WaitGen struct {
	current WaitInterface
	gen     func() WaitInterface
}

//Make returns a new WaitInterface or returns the current once
func (w *WaitGen) Make() WaitInterface {
	if w.current != nil {
		return w.current
	}
	wt := w.gen()
	wt.Then().WhenOnly(func(_ interface{}) {
		w.current = nil
	})
	return wt
}

//NewTimeWaitGen returns a wait generator making a timewaiter
func NewTimeWaitGen(steps int, ms time.Duration, init func(WaitInterface)) *WaitGen {
	return &WaitGen{
		nil,
		func() WaitInterface {
			nt := NewTimeWait(steps, ms)
			init(nt)
			return nt
		},
	}
}

//NewSimpleWaitGen returns a wait generator making a timewaiter
func NewSimpleWaitGen(init func(WaitInterface)) *WaitGen {
	return &WaitGen{
		nil,
		func() WaitInterface {
			nt := NewWait()
			init(nt)
			return nt
		},
	}
}

//baseWait defines the base wait structure for all waiters
type baseWait struct {
	action ActionInterface
}

//Then returns an ActionInterface which gets fullfilled when this wait
//counter reaches zero
func (w *baseWait) Then() ActionInterface {
	return w.action.Wrap()
}

func newBaseWait() *baseWait {
	return &baseWait{NewAction()}
}

//TimeWait defines a time lock waiter
type TimeWait struct {
	*baseWait
	closer chan struct{}
	hits   int64
	max    int
	ms     time.Duration
	doonce *sync.Once
}

//NewTimeWait returns a new timer wait locker
//You specifiy two arguments:
//max int: the maximum number of time you want to check for idleness
//duration time.Duration: the time to check for each idle times and reduce
//until zero is reached then close
//eg. to do a 15seconds check for idleness
//NewTimeWait(15,time.Duration(1)*time.Second)
//eg. to do a 25 maximum check before closing per minute
//NewTimeWait(15,time.Duration(1)*time.Minute)
func NewTimeWait(max int, duration time.Duration) *TimeWait {

	tm := &TimeWait{
		newBaseWait(),
		make(chan struct{}),
		int64(max),
		max,
		duration,
		new(sync.Once),
	}

	// tm.Add()
	go tm.handle()

	return tm
}

//handle effects the necessary time process for checking and reducing the
//time checker for each duration of time,till the Waiter is done
func (w *TimeWait) handle() {
	var state int64
	atomic.StoreInt64(&state, 0)

	go func() {
		<-w.closer
		atomic.StoreInt64(&state, 1)
	}()

	for {
		time.Sleep(w.ms)

		bit := atomic.LoadInt64(&state)
		if bit > 0 {
			break
		}

		w.Done()
	}
}

//Flush drops the lock count and forces immediate unlocking of the wait
func (w *TimeWait) Flush() {
	w.doonce.Do(func() {
		close(w.closer)
		w.action.Fullfill(0)
		atomic.StoreInt64(&w.hits, 0)
	})
}

//Count returns the total left count to completed before unlock
func (w *TimeWait) Count() int {
	return int(atomic.LoadInt64(&w.hits))
}

//Add increments the lock state to the lock counter unless its already unlocked
func (w *TimeWait) Add() {
	if w.Count() < 0 || w.Count() >= w.max {
		return
	}

	atomic.AddInt64(&w.hits, 1)
}

//Done decrements the totalcount of this waitlocker by 1 until its below zero
//and fullfills with the 0 value
func (w *TimeWait) Done() {
	hits := atomic.LoadInt64(&w.hits)

	if hits < 0 {
		return
	}

	newhit := atomic.AddInt64(&w.hits, -1)
	log.Printf("TimeWait: Count Down now %d before %d!", newhit, hits)
	if int(newhit) <= 0 {
		w.Flush()
		log.Printf("TimeWait: Count Down Finished!")
	}
}

//Wait implements the WiatInterface for creating a wait lock which
//waits until the lock lockcount is finished then executes a action
//can only be used once, that is ,once the wait counter is -1,you cant add
//to it anymore
type Wait struct {
	*baseWait
	totalCount int64
}

//NewWait returns a new Wait instance for the WaitInterface
func NewWait() WaitInterface {
	return &Wait{newBaseWait(), int64(0)}
}

//Flush drops the lock count and forces immediate unlocking of the wait
func (w *Wait) Flush() {
	curr := int(atomic.LoadInt64(&w.totalCount))
	if curr < 0 {
		return
	}

	atomic.StoreInt64(&w.totalCount, 0)
	w.Done()
}

//Count returns the total left count to completed before unlock
func (w *Wait) Count() int {
	return int(atomic.LoadInt64(&w.totalCount))
}

//Add increments the lock state to the lock counter unless its already unlocked
func (w *Wait) Add() {
	curr := atomic.LoadInt64(&w.totalCount)

	if curr < 0 {
		return
	}

	atomic.AddInt64(&w.totalCount, 1)
}

//Done decrements the totalcount of this waitlocker by 1 until its below zero
//and fullfills with the 0 value
func (w *Wait) Done() {
	curr := atomic.LoadInt64(&w.totalCount)

	if curr < 0 {
		return
	}

	nc := atomic.AddInt64(&w.totalCount, -1)
	// log.Printf("Wait: Count Down now %d before %d", nc, curr)

	if int(nc) <= 0 {
		w.action.Fullfill(0)
	}
}
<|endoftext|>


<commit_before>// +build windows

package main

/*
// for timeBeginPeriod()
#pragma comment(lib, "Winmm")
// for RegisterWindowMessageA() and SendMessage()
#pragma comment(lib, "User32")
*/
import "C"
import (
	"errors"
	"fmt"
	"syscall"
	"unsafe"
)

const (
	HWND_BROADCAST = HWND(0xffff)
)

type (
	HWND   HANDLE
	HANDLE uintptr
)

var (
	kernel32 = syscall.NewLazyDLL("kernel32.dll")
	user32   = syscall.NewLazyDLL("user32.dll")

	wSleep                  = kernel32.NewProc("Sleep")
	wOpenFileMappingW       = kernel32.NewProc("OpenFileMappingW")
	wMapViewOfFile          = kernel32.NewProc("MapViewOfFile")
	wCloseHandle            = kernel32.NewProc("CloseHandle")
	wUnmapViewOfFile        = kernel32.NewProc("UnmapViewOfFile")
	wOpenEvent              = kernel32.NewProc("OpenEventW")
	wWaitForSingleObject    = kernel32.NewProc("WaitForSingleObject")
	wRegisterWindowMessageA = user32.NewProc("RegisterWindowMessageA")
	wSendNotifyMessageA     = user32.NewProc("SendNotifyMessageA")
)

func sleep(timeout int) error {
	_, _, err := wSleep.Call(uintptr(timeout))

	if err != nil {
		errMsg := fmt.Sprintf("Timeout failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func openFileMapping(lpName string) (uintptr, error) {
	dwDesiredAccess := syscall.FILE_MAP_READ

	hMemMapFile, _, err := wOpenFileMappingW.Call(
		uintptr(dwDesiredAccess), // DWORD
		0, // BOOL
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpName))), // LPCTSTR
	)

	if hMemMapFile == 0 {
		errMsg := fmt.Sprintf("OpenFileMapping failed (%s)", err)
		return hMemMapFile, errors.New(errMsg)
	}

	return hMemMapFile, nil
}

func mapViewOfFile(hMemMapFile uintptr, dwNumberOfBytesToMap int) (uintptr, error) {
	dwDesiredAccess := syscall.FILE_MAP_READ
	dwFileOffsetHigh := 0
	dwFileOffsetLow := 0

	sharedMemPtr, _, err := wMapViewOfFile.Call(
		hMemMapFile,
		uintptr(dwDesiredAccess),      // DWORD
		uintptr(dwFileOffsetHigh),     // DWORD
		uintptr(dwFileOffsetLow),      // DWORD
		uintptr(dwNumberOfBytesToMap), // SIZE_T
	)

	if sharedMemPtr == 0 {
		errMsg := fmt.Sprintf("MapViewOfFile failed (%s)", err)
		return hMemMapFile, errors.New(errMsg)
	}

	return sharedMemPtr, nil
}

func closeHandle(handle uintptr) error {
	result, _, err := wCloseHandle.Call(handle)

	if result == 0 {
		errMsg := fmt.Sprintf("CloseHandle failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func unmapViewOfFile(lpBaseAddress uintptr) error {
	result, _, err := wUnmapViewOfFile.Call(lpBaseAddress)

	if result == 0 {
		errMsg := fmt.Sprintf("UnmapViewOfFile failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func openEvent(lpName string) (uintptr, error) {
	dwDesiredAccess := syscall.SYNCHRONIZE
	bInheritHandle := 0

	hDataValidEvent, _, err := wOpenEvent.Call(
		uintptr(dwDesiredAccess),                                  // DWORD
		uintptr(bInheritHandle),                                   // BOOL
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpName))), // LPCTSTR
	)

	if hDataValidEvent == 0 {
		errMsg := fmt.Sprintf("OpenEvent failed (%s)", err)
		return hDataValidEvent, errors.New(errMsg)
	}

	return hDataValidEvent, nil
}

func waitForSingleObject(hDataValidEvent uintptr, timeOut int) error {
	dwMilliseconds := timeOut

	result, _, err := wWaitForSingleObject.Call(
		hDataValidEvent,         // HANDLE
		uintptr(dwMilliseconds), // DWORD
	)

	if result != 0 {
		errMsg := fmt.Sprintf("WaitForSingleObject failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func registerWindowMessageA(lpString string) (uint, error) {
	msgID, _, err := wRegisterWindowMessageA.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpString))), // LPCTSTR
	)

	if msgID == 0 {
		errMsg := fmt.Sprintf("registerWindowMessageA failed (%s)", err)
		return 0, errors.New(errMsg)
	}

	return uint(msgID), nil
}

func sendNotifyMessage(msgID uint, wParam uint32, lParam uint32) error {
	hWnd := HWND_BROADCAST

	result, _, err := wSendNotifyMessageA.Call(
		uintptr(hWnd),   // HWND
		uintptr(msgID),  // UINT
		uintptr(wParam), // WPARAM
		uintptr(lParam), // LPARAM
	)

	fmt.Println(err)
	if result == 0 {
		errMsg := fmt.Sprintf("sendNotifyMessage failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func MAKELONG(lo, hi uint16) uint32 {
	return uint32(uint32(lo) | ((uint32(hi)) << 16))
}
<commit_msg>Be more explicit about which Windows syscall is used<commit_after>// +build windows

package main

/*
// for timeBeginPeriod()
#pragma comment(lib, "Winmm")
// for RegisterWindowMessageA() and SendMessage()
#pragma comment(lib, "User32")
*/
import "C"
import (
	"errors"
	"fmt"
	"syscall"
	"unsafe"
)

const (
	HWND_BROADCAST = HWND(0xffff)
)

type (
	HWND   HANDLE
	HANDLE uintptr
)

var (
	kernel32 = syscall.NewLazyDLL("kernel32.dll")
	user32   = syscall.NewLazyDLL("user32.dll")

	wSleep                  = kernel32.NewProc("Sleep")
	wOpenFileMappingW       = kernel32.NewProc("OpenFileMappingW")
	wMapViewOfFile          = kernel32.NewProc("MapViewOfFile")
	wCloseHandle            = kernel32.NewProc("CloseHandle")
	wUnmapViewOfFile        = kernel32.NewProc("UnmapViewOfFile")
	wOpenEventW             = kernel32.NewProc("OpenEventW")
	wWaitForSingleObject    = kernel32.NewProc("WaitForSingleObject")
	wRegisterWindowMessageA = user32.NewProc("RegisterWindowMessageA")
	wSendNotifyMessageA     = user32.NewProc("SendNotifyMessageA")
)

func sleep(timeout int) error {
	_, _, err := wSleep.Call(uintptr(timeout))

	if err != nil {
		errMsg := fmt.Sprintf("Timeout failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func openFileMapping(lpName string) (uintptr, error) {
	dwDesiredAccess := syscall.FILE_MAP_READ

	hMemMapFile, _, err := wOpenFileMappingW.Call(
		uintptr(dwDesiredAccess), // DWORD
		0, // BOOL
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpName))), // LPCTSTR
	)

	if hMemMapFile == 0 {
		errMsg := fmt.Sprintf("OpenFileMapping failed (%s)", err)
		return hMemMapFile, errors.New(errMsg)
	}

	return hMemMapFile, nil
}

func mapViewOfFile(hMemMapFile uintptr, dwNumberOfBytesToMap int) (uintptr, error) {
	dwDesiredAccess := syscall.FILE_MAP_READ
	dwFileOffsetHigh := 0
	dwFileOffsetLow := 0

	sharedMemPtr, _, err := wMapViewOfFile.Call(
		hMemMapFile,
		uintptr(dwDesiredAccess),      // DWORD
		uintptr(dwFileOffsetHigh),     // DWORD
		uintptr(dwFileOffsetLow),      // DWORD
		uintptr(dwNumberOfBytesToMap), // SIZE_T
	)

	if sharedMemPtr == 0 {
		errMsg := fmt.Sprintf("MapViewOfFile failed (%s)", err)
		return hMemMapFile, errors.New(errMsg)
	}

	return sharedMemPtr, nil
}

func closeHandle(handle uintptr) error {
	result, _, err := wCloseHandle.Call(handle)

	if result == 0 {
		errMsg := fmt.Sprintf("CloseHandle failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func unmapViewOfFile(lpBaseAddress uintptr) error {
	result, _, err := wUnmapViewOfFile.Call(lpBaseAddress)

	if result == 0 {
		errMsg := fmt.Sprintf("UnmapViewOfFile failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func openEvent(lpName string) (uintptr, error) {
	dwDesiredAccess := syscall.SYNCHRONIZE
	bInheritHandle := 0

	hDataValidEvent, _, err := wOpenEventW.Call(
		uintptr(dwDesiredAccess),                                  // DWORD
		uintptr(bInheritHandle),                                   // BOOL
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpName))), // LPCTSTR
	)

	if hDataValidEvent == 0 {
		errMsg := fmt.Sprintf("OpenEvent failed (%s)", err)
		return hDataValidEvent, errors.New(errMsg)
	}

	return hDataValidEvent, nil
}

func waitForSingleObject(hDataValidEvent uintptr, timeOut int) error {
	dwMilliseconds := timeOut

	result, _, err := wWaitForSingleObject.Call(
		hDataValidEvent,         // HANDLE
		uintptr(dwMilliseconds), // DWORD
	)

	if result != 0 {
		errMsg := fmt.Sprintf("WaitForSingleObject failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func registerWindowMessageA(lpString string) (uint, error) {
	msgID, _, err := wRegisterWindowMessageA.Call(
		uintptr(unsafe.Pointer(syscall.StringToUTF16Ptr(lpString))), // LPCTSTR
	)

	if msgID == 0 {
		errMsg := fmt.Sprintf("registerWindowMessageA failed (%s)", err)
		return 0, errors.New(errMsg)
	}

	return uint(msgID), nil
}

func sendNotifyMessage(msgID uint, wParam uint32, lParam uint32) error {
	hWnd := HWND_BROADCAST

	result, _, err := wSendNotifyMessageA.Call(
		uintptr(hWnd),   // HWND
		uintptr(msgID),  // UINT
		uintptr(wParam), // WPARAM
		uintptr(lParam), // LPARAM
	)

	fmt.Println(err)
	if result == 0 {
		errMsg := fmt.Sprintf("sendNotifyMessage failed (%s)", err)
		return errors.New(errMsg)
	}

	return nil
}

func MAKELONG(lo, hi uint16) uint32 {
	return uint32(uint32(lo) | ((uint32(hi)) << 16))
}
<|endoftext|>


<commit_before>package system

import (
	"flag"
	"fmt"
	"go/parser"
	"go/token"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

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

func ensureMainEntrypoint(logger *logrus.Logger) error {
	// Don't do this for "go test" runs
	if flag.Lookup("test.v") != nil {
		logger.Debug("Skipping main() check for test")
		return nil
	}

	fset := token.NewFileSet()
	packageMap, parseErr := parser.ParseDir(fset, ".", nil, parser.PackageClauseOnly)
	if parseErr != nil {
		return errors.Errorf("Failed to parse source input: %s", parseErr.Error())
	}
	logger.WithFields(logrus.Fields{
		"SourcePackages": packageMap,
	}).Debug("Checking working directory")

	// If there isn't a main defined, we're in the wrong directory..
	mainPackageCount := 0
	for eachPackage := range packageMap {
		if eachPackage == "main" {
			mainPackageCount++
		}
	}
	if mainPackageCount <= 0 {
		unlikelyBinaryErr := fmt.Errorf("error: It appears your application's `func main() {}` is not in the current working directory. Please run this command in the same directory as `func main() {}`")
		return unlikelyBinaryErr
	}
	return nil
}

// GoVersion returns the configured go version for this system
func GoVersion(logger *logrus.Logger) (string, error) {
	runtimeVersion := runtime.Version()
	// Get the golang version from the output:
	// Matts-MBP:Sparta mweagle$ go version
	// go version go1.8.1 darwin/amd64
	golangVersionRE := regexp.MustCompile(`go(\d+\.\d+(\.\d+)?)`)
	matches := golangVersionRE.FindStringSubmatch(runtimeVersion)
	if len(matches) > 2 {
		return matches[1], nil
	}
	logger.WithFields(logrus.Fields{
		"Output": runtimeVersion,
	}).Warn("Unable to find Golang version using RegExp - using current version")
	return runtimeVersion, nil
}

// GoPath returns either $GOPATH or the new $HOME/go path
// introduced with Go 1.8
func GoPath() string {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		home := os.Getenv("HOME")
		gopath = filepath.Join(home, "go")
	}
	return gopath
}

// BuildGoBinary is a helper to build a go binary with the given options
func BuildGoBinary(serviceName string,
	executableOutput string,
	useCGO bool,
	buildID string,
	userSuppliedBuildTags string,
	linkFlags string,
	noop bool,
	logger *logrus.Logger) error {

	// Before we do anything, let's make sure there's a `main` package in this directory.
	ensureMainPackageErr := ensureMainEntrypoint(logger)
	if ensureMainPackageErr != nil {
		return ensureMainPackageErr
	}
	// Go generate
	cmd := exec.Command("go", "generate")
	if logger.Level == logrus.DebugLevel {
		cmd = exec.Command("go", "generate", "-v", "-x")
	}
	cmd.Env = os.Environ()
	commandString := fmt.Sprintf("%s", cmd.Args)
	logger.Info(fmt.Sprintf("Running `%s`", strings.Trim(commandString, "[]")))
	goGenerateErr := RunOSCommand(cmd, logger)
	if nil != goGenerateErr {
		return goGenerateErr
	}
	// TODO: Smaller binaries via linker flags
	// Ref: https://blog.filippo.io/shrink-your-go-binaries-with-this-one-weird-trick/
	noopTag := ""
	if noop {
		noopTag = "noop "
	}

	buildTags := []string{
		"lambdabinary",
		"linux",
	}
	if noopTag != "" {
		buildTags = append(buildTags, noopTag)
	}
	if userSuppliedBuildTags != "" {
		userBuildTagsParts := strings.Split(userSuppliedBuildTags, " ")
		for _, eachTag := range userBuildTagsParts {
			buildTags = append(buildTags, eachTag)
		}
	}
	userBuildFlags := []string{"-tags", strings.Join(buildTags, " ")}

	// Append all the linker flags
	// Stamp the service name into the binary
	// We need to stamp the servicename into the aws binary so that if the user
	// chose some type of dynamic stack name at provision time, the name
	// we use at execution time has that value. This is necessary because
	// the function dispatch logic uses the AWS_LAMBDA_FUNCTION_NAME environment
	// variable to do the lookup. And in effect, this value has to be unique
	// across an account, since functions cannot have the same name
	// Custom flags for the binary
	linkerFlags := map[string]string{
		"StampedServiceName": serviceName,
		"StampedBuildID":     buildID,
	}
	for eachFlag, eachValue := range linkerFlags {
		linkFlags = fmt.Sprintf("%s -s -w -X github.com/mweagle/Sparta.%s=%s",
			linkFlags,
			eachFlag,
			eachValue)
	}
	linkFlags = strings.TrimSpace(linkFlags)
	if len(linkFlags) != 0 {
		userBuildFlags = append(userBuildFlags, "-ldflags", linkFlags)
	}
	// If this is CGO, do the Docker build if we're doing an actual
	// provision. Otherwise use the "normal" build to keep things
	// a bit faster.
	var cmdError error
	if useCGO {
		currentDir, currentDirErr := os.Getwd()
		if nil != currentDirErr {
			return currentDirErr
		}
		gopathVersion, gopathVersionErr := GoVersion(logger)
		if nil != gopathVersionErr {
			return gopathVersionErr
		}

		gopath := GoPath()
		containerGoPath := "/usr/src/gopath"
		// Get the package path in the current directory
		// so that we can it to the container path
		packagePath := strings.TrimPrefix(currentDir, gopath)
		volumeMountMapping := fmt.Sprintf("%s:%s", gopath, containerGoPath)
		containerSourcePath := fmt.Sprintf("%s%s", containerGoPath, packagePath)

		// Any CGO paths?
		cgoLibPath := fmt.Sprintf("%s/cgo/lib", containerSourcePath)
		cgoIncludePath := fmt.Sprintf("%s/cgo/include", containerSourcePath)

		// Pass any SPARTA_* prefixed environment variables to the docker build
		//
		goosTarget := os.Getenv("SPARTA_GOOS")
		if goosTarget == "" {
			goosTarget = "linux"
		}
		goArch := os.Getenv("SPARTA_GOARCH")
		if goArch == "" {
			goArch = "amd64"
		}
		spartaEnvVars := []string{
			// "-e",
			// fmt.Sprintf("GOPATH=%s", containerGoPath),
			"-e",
			fmt.Sprintf("GOOS=%s", goosTarget),
			"-e",
			fmt.Sprintf("GOARCH=%s", goArch),
			"-e",
			fmt.Sprintf("CGO_LDFLAGS=\"-L%s\"", cgoLibPath),
			"-e",
			fmt.Sprintf("CGO_CFLAGS=\"-I%s\"", cgoIncludePath),
		}
		// User vars
		for _, eachPair := range os.Environ() {
			if strings.HasPrefix(eachPair, "SPARTA_") {
				spartaEnvVars = append(spartaEnvVars, "-e", eachPair)
			}
		}
		dockerBuildArgs := []string{
			"run",
			"--rm",
			"-v",
			volumeMountMapping,
			"-w",
			containerSourcePath}
		dockerBuildArgs = append(dockerBuildArgs, spartaEnvVars...)
		dockerBuildArgs = append(dockerBuildArgs,
			fmt.Sprintf("golang:%s", gopathVersion),
			"go",
			"build",
			"-o",
			executableOutput,
			"-buildmode=default",
		)
		dockerBuildArgs = append(dockerBuildArgs, userBuildFlags...)
		cmd = exec.Command("docker", dockerBuildArgs...)
		cmd.Env = os.Environ()
		logger.WithFields(logrus.Fields{
			"Name": executableOutput,
			"Args": dockerBuildArgs,
		}).Info("Building `cgo` library in Docker")
		cmdError = RunOSCommand(cmd, logger)

		// If this succeeded, let's find the .h file and move it into the scratch
		// Try to keep things tidy...
		if nil == cmdError {
			soExtension := filepath.Ext(executableOutput)
			headerFilepath := fmt.Sprintf("%s.h", strings.TrimSuffix(executableOutput, soExtension))
			_, headerFileErr := os.Stat(headerFilepath)
			if nil == headerFileErr {
				targetPath, targetPathErr := TemporaryFile(".sparta", filepath.Base(headerFilepath))
				if nil != targetPathErr {
					headerFileErr = targetPathErr
				} else {
					headerFileErr = os.Rename(headerFilepath, targetPath.Name())
				}
			}
			if nil != headerFileErr {
				logger.WithFields(logrus.Fields{
					"Path": headerFilepath,
				}).Warn("Failed to move .h file to scratch directory")
			}
		}
	} else {
		// Build the regular version
		buildArgs := []string{
			"build",
			"-o",
			executableOutput,
		}
		// Debug flags?
		if logger.Level == logrus.DebugLevel {
			buildArgs = append(buildArgs, "-v")
		}
		buildArgs = append(buildArgs, userBuildFlags...)
		buildArgs = append(buildArgs, ".")
		cmd = exec.Command("go", buildArgs...)
		cmd.Env = os.Environ()
		cmd.Env = append(cmd.Env, "GOOS=linux", "GOARCH=amd64")
		logger.WithFields(logrus.Fields{
			"Name": executableOutput,
		}).Info("Compiling binary")
		cmdError = RunOSCommand(cmd, logger)
	}
	return cmdError
}

// TemporaryFile creates a stable temporary filename in the current working
// directory
func TemporaryFile(scratchDir string, name string) (*os.File, error) {
	workingDir, err := os.Getwd()
	if nil != err {
		return nil, err
	}

	// Use a stable temporary name
	temporaryPath := filepath.Join(workingDir, scratchDir, name)
	buildDir := filepath.Dir(temporaryPath)
	mkdirErr := os.MkdirAll(buildDir, os.ModePerm)
	if nil != mkdirErr {
		return nil, mkdirErr
	}

	tmpFile, err := os.Create(temporaryPath)
	if err != nil {
		return nil, errors.New("Failed to create temporary file: " + err.Error())
	}

	return tmpFile, nil
}
<commit_msg>Computers<commit_after>package system

import (
	"flag"
	"fmt"
	"go/parser"
	"go/token"
	"os"
	"os/exec"
	"path/filepath"
	"regexp"
	"runtime"
	"strings"

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

func ensureMainEntrypoint(logger *logrus.Logger) error {
	// Don't do this for "go test" runs
	if flag.Lookup("test.v") != nil {
		logger.Debug("Skipping main() check for test")
		return nil
	}

	fset := token.NewFileSet()
	packageMap, parseErr := parser.ParseDir(fset, ".", nil, parser.PackageClauseOnly)
	if parseErr != nil {
		return errors.Errorf("Failed to parse source input: %s", parseErr.Error())
	}
	logger.WithFields(logrus.Fields{
		"SourcePackages": packageMap,
	}).Debug("Checking working directory")

	// If there isn't a main defined, we're in the wrong directory..
	mainPackageCount := 0
	for eachPackage := range packageMap {
		if eachPackage == "main" {
			mainPackageCount++
		}
	}
	if mainPackageCount <= 0 {
		unlikelyBinaryErr := fmt.Errorf("error: It appears your application's `func main() {}` is not in the current working directory. Please run this command in the same directory as `func main() {}`")
		return unlikelyBinaryErr
	}
	return nil
}

// GoVersion returns the configured go version for this system
func GoVersion(logger *logrus.Logger) (string, error) {
	runtimeVersion := runtime.Version()
	// Get the golang version from the output:
	// Matts-MBP:Sparta mweagle$ go version
	// go version go1.8.1 darwin/amd64
	golangVersionRE := regexp.MustCompile(`go(\d+\.\d+(\.\d+)?)`)
	matches := golangVersionRE.FindStringSubmatch(runtimeVersion)
	if len(matches) > 2 {
		return matches[1], nil
	}
	logger.WithFields(logrus.Fields{
		"Output": runtimeVersion,
	}).Warn("Unable to find Golang version using RegExp - using current version")
	return runtimeVersion, nil
}

// GoPath returns either $GOPATH or the new $HOME/go path
// introduced with Go 1.8
func GoPath() string {
	gopath := os.Getenv("GOPATH")
	if gopath == "" {
		home := os.Getenv("HOME")
		gopath = filepath.Join(home, "go")
	}
	return gopath
}

// BuildGoBinary is a helper to build a go binary with the given options
func BuildGoBinary(serviceName string,
	executableOutput string,
	useCGO bool,
	buildID string,
	userSuppliedBuildTags string,
	linkFlags string,
	noop bool,
	logger *logrus.Logger) error {

	// Before we do anything, let's make sure there's a `main` package in this directory.
	ensureMainPackageErr := ensureMainEntrypoint(logger)
	if ensureMainPackageErr != nil {
		return ensureMainPackageErr
	}
	// Go generate
	cmd := exec.Command("go", "generate")
	if logger.Level == logrus.DebugLevel {
		cmd = exec.Command("go", "generate", "-v", "-x")
	}
	cmd.Env = os.Environ()
	commandString := fmt.Sprintf("%s", cmd.Args)
	logger.Info(fmt.Sprintf("Running `%s`", strings.Trim(commandString, "[]")))
	goGenerateErr := RunOSCommand(cmd, logger)
	if nil != goGenerateErr {
		return goGenerateErr
	}
	// TODO: Smaller binaries via linker flags
	// Ref: https://blog.filippo.io/shrink-your-go-binaries-with-this-one-weird-trick/
	noopTag := ""
	if noop {
		noopTag = "noop "
	}

	buildTags := []string{
		"lambdabinary",
		"linux",
	}
	if noopTag != "" {
		buildTags = append(buildTags, noopTag)
	}
	if userSuppliedBuildTags != "" {
		userBuildTagsParts := strings.Split(userSuppliedBuildTags, " ")
		for _, eachTag := range userBuildTagsParts {
			buildTags = append(buildTags, eachTag)
		}
	}
	userBuildFlags := []string{"-tags", strings.Join(buildTags, " ")}

	// Append all the linker flags
	// Stamp the service name into the binary
	// We need to stamp the servicename into the aws binary so that if the user
	// chose some type of dynamic stack name at provision time, the name
	// we use at execution time has that value. This is necessary because
	// the function dispatch logic uses the AWS_LAMBDA_FUNCTION_NAME environment
	// variable to do the lookup. And in effect, this value has to be unique
	// across an account, since functions cannot have the same name
	// Custom flags for the binary
	linkerFlags := map[string]string{
		"StampedServiceName": serviceName,
		"StampedBuildID":     buildID,
	}
	for eachFlag, eachValue := range linkerFlags {
		linkFlags = fmt.Sprintf("%s -s -w -X github.com/mweagle/Sparta.%s=%s",
			linkFlags,
			eachFlag,
			eachValue)
	}
	linkFlags = strings.TrimSpace(linkFlags)
	if len(linkFlags) != 0 {
		userBuildFlags = append(userBuildFlags, "-ldflags", linkFlags)
	}
	// If this is CGO, do the Docker build if we're doing an actual
	// provision. Otherwise use the "normal" build to keep things
	// a bit faster.
	var cmdError error
	if useCGO {
		currentDir, currentDirErr := os.Getwd()
		if nil != currentDirErr {
			return currentDirErr
		}
		gopathVersion, gopathVersionErr := GoVersion(logger)
		if nil != gopathVersionErr {
			return gopathVersionErr
		}

		gopath := GoPath()
		containerGoPath := "/usr/src/gopath"
		// Get the package path in the current directory
		// so that we can it to the container path
		packagePath := strings.TrimPrefix(currentDir, gopath)
		volumeMountMapping := fmt.Sprintf("%s:%s", gopath, containerGoPath)
		containerSourcePath := fmt.Sprintf("%s%s", containerGoPath, packagePath)

		// If there's one from the environment, use that...
		// TODO

		// Otherwise, make one...

		// Any CGO paths?
		cgoLibPath := fmt.Sprintf("%s/cgo/lib", containerSourcePath)
		cgoIncludePath := fmt.Sprintf("%s/cgo/include", containerSourcePath)

		// Pass any SPARTA_* prefixed environment variables to the docker build
		//
		goosTarget := os.Getenv("SPARTA_GOOS")
		if goosTarget == "" {
			goosTarget = "linux"
		}
		goArch := os.Getenv("SPARTA_GOARCH")
		if goArch == "" {
			goArch = "amd64"
		}
		spartaEnvVars := []string{
			// "-e",
			// fmt.Sprintf("GOPATH=%s", containerGoPath),
			"-e",
			fmt.Sprintf("GOOS=%s", goosTarget),
			"-e",
			fmt.Sprintf("GOARCH=%s", goArch),
			"-e",
			fmt.Sprintf("CGO_LDFLAGS=\"-L%s\"", cgoLibPath),
			"-e",
			fmt.Sprintf("CGO_CFLAGS=-I%s", cgoIncludePath),
		}
		// User vars
		for _, eachPair := range os.Environ() {
			if strings.HasPrefix(eachPair, "SPARTA_") {
				spartaEnvVars = append(spartaEnvVars, "-e", eachPair)
			}
		}
		dockerBuildArgs := []string{
			"run",
			"--rm",
			"-v",
			volumeMountMapping,
			"-w",
			containerSourcePath}
		dockerBuildArgs = append(dockerBuildArgs, spartaEnvVars...)
		dockerBuildArgs = append(dockerBuildArgs,
			fmt.Sprintf("golang:%s", gopathVersion),
			"go",
			"build",
			"-o",
			executableOutput,
			"-buildmode=default",
		)
		dockerBuildArgs = append(dockerBuildArgs, userBuildFlags...)
		cmd = exec.Command("docker", dockerBuildArgs...)
		cmd.Env = os.Environ()
		logger.WithFields(logrus.Fields{
			"Name": executableOutput,
			"Args": dockerBuildArgs,
		}).Info("Building `cgo` library in Docker")
		cmdError = RunOSCommand(cmd, logger)

		// If this succeeded, let's find the .h file and move it into the scratch
		// Try to keep things tidy...
		if nil == cmdError {
			soExtension := filepath.Ext(executableOutput)
			headerFilepath := fmt.Sprintf("%s.h", strings.TrimSuffix(executableOutput, soExtension))
			_, headerFileErr := os.Stat(headerFilepath)
			if nil == headerFileErr {
				targetPath, targetPathErr := TemporaryFile(".sparta", filepath.Base(headerFilepath))
				if nil != targetPathErr {
					headerFileErr = targetPathErr
				} else {
					headerFileErr = os.Rename(headerFilepath, targetPath.Name())
				}
			}
			if nil != headerFileErr {
				logger.WithFields(logrus.Fields{
					"Path": headerFilepath,
				}).Warn("Failed to move .h file to scratch directory")
			}
		}
	} else {
		// Build the regular version
		buildArgs := []string{
			"build",
			"-o",
			executableOutput,
		}
		// Debug flags?
		if logger.Level == logrus.DebugLevel {
			buildArgs = append(buildArgs, "-v")
		}
		buildArgs = append(buildArgs, userBuildFlags...)
		buildArgs = append(buildArgs, ".")
		cmd = exec.Command("go", buildArgs...)
		cmd.Env = os.Environ()
		cmd.Env = append(cmd.Env, "GOOS=linux", "GOARCH=amd64")
		logger.WithFields(logrus.Fields{
			"Name": executableOutput,
		}).Info("Compiling binary")
		cmdError = RunOSCommand(cmd, logger)
	}
	return cmdError
}

// TemporaryFile creates a stable temporary filename in the current working
// directory
func TemporaryFile(scratchDir string, name string) (*os.File, error) {
	workingDir, err := os.Getwd()
	if nil != err {
		return nil, err
	}

	// Use a stable temporary name
	temporaryPath := filepath.Join(workingDir, scratchDir, name)
	buildDir := filepath.Dir(temporaryPath)
	mkdirErr := os.MkdirAll(buildDir, os.ModePerm)
	if nil != mkdirErr {
		return nil, mkdirErr
	}

	tmpFile, err := os.Create(temporaryPath)
	if err != nil {
		return nil, errors.New("Failed to create temporary file: " + err.Error())
	}

	return tmpFile, nil
}
<|endoftext|>


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

package file

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"

	"go.uber.org/zap"
	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"

	"github.com/open-telemetry/opentelemetry-log-collection/errors"
)

// File attributes contains information about file paths
type fileAttributes struct {
	Name         string
	Path         string
	ResolvedName string
	ResolvedPath string
}

// resolveFileAttributes resolves file attributes
// and sets it to empty string in case of error
func (f *InputOperator) resolveFileAttributes(path string) *fileAttributes {
	resolved, err := filepath.EvalSymlinks(path)
	if err != nil {
		f.Error(err)
	}

	abs, err := filepath.Abs(resolved)
	if err != nil {
		f.Error(err)
	}

	return &fileAttributes{
		Path:         path,
		Name:         filepath.Base(path),
		ResolvedPath: abs,
		ResolvedName: filepath.Base(abs),
	}
}

// Reader manages a single file
type Reader struct {
	Fingerprint *Fingerprint
	Offset      int64

	generation     int
	fileInput      *InputOperator
	file           *os.File
	fileAttributes *fileAttributes

	decoder      *encoding.Decoder
	decodeBuffer []byte

	*zap.SugaredLogger `json:"-"`
}

// NewReader creates a new file reader
func (f *InputOperator) NewReader(path string, file *os.File, fp *Fingerprint) (*Reader, error) {
	r := &Reader{
		Fingerprint:    fp,
		file:           file,
		fileInput:      f,
		SugaredLogger:  f.SugaredLogger.With("path", path),
		decoder:        f.encoding.Encoding.NewDecoder(),
		decodeBuffer:   make([]byte, 1<<12),
		fileAttributes: f.resolveFileAttributes(path),
	}
	return r, nil
}

// Copy creates a deep copy of a Reader
func (f *Reader) Copy(file *os.File) (*Reader, error) {
	reader, err := f.fileInput.NewReader(f.fileAttributes.Path, file, f.Fingerprint.Copy())
	if err != nil {
		return nil, err
	}
	reader.Offset = f.Offset
	return reader, nil
}

// InitializeOffset sets the starting offset
func (f *Reader) InitializeOffset(startAtBeginning bool) error {
	if !startAtBeginning {
		info, err := f.file.Stat()
		if err != nil {
			return fmt.Errorf("stat: %s", err)
		}
		f.Offset = info.Size()
	}
	return nil
}

// ReadToEnd will read until the end of the file
func (f *Reader) ReadToEnd(ctx context.Context) {
	if _, err := f.file.Seek(f.Offset, 0); err != nil {
		f.Errorw("Failed to seek", zap.Error(err))
		return
	}

	scanner := NewPositionalScanner(f, f.fileInput.MaxLogSize, f.Offset, f.fileInput.SplitFunc)

	// Iterate over the tokenized file, emitting entries as we go
	for {
		select {
		case <-ctx.Done():
			return
		default:
		}

		ok := scanner.Scan()
		if !ok {
			if err := getScannerError(scanner); err != nil {
				f.Errorw("Failed during scan", zap.Error(err))
			}
			break
		}

		if err := f.emit(ctx, scanner.Bytes()); err != nil {
			f.Error("Failed to emit entry", zap.Error(err))
		}
		f.Offset = scanner.Pos()
	}
}

// Close will close the file
func (f *Reader) Close() {
	if f.file != nil {
		if err := f.file.Close(); err != nil {
			f.Debugf("Problem closing reader", "Error", err.Error())
		}
	}
}

// Emit creates an entry with the decoded message and sends it to the next
// operator in the pipeline
func (f *Reader) emit(ctx context.Context, msgBuf []byte) error {
	// Skip the entry if it's empty
	if len(msgBuf) == 0 {
		return nil
	}

	msg, err := f.decode(msgBuf)
	if err != nil {
		return fmt.Errorf("decode: %s", err)
	}

	e, err := f.fileInput.NewEntry(msg)
	if err != nil {
		return fmt.Errorf("create entry: %s", err)
	}

	if err := e.Set(f.fileInput.FilePathField, f.fileAttributes.Path); err != nil {
		return err
	}
	if err := e.Set(f.fileInput.FileNameField, f.fileAttributes.Name); err != nil {
		return err
	}

	if err := e.Set(f.fileInput.FilePathResolvedField, f.fileAttributes.ResolvedPath); err != nil {
		return err
	}

	if err := e.Set(f.fileInput.FileNameResolvedField, f.fileAttributes.ResolvedName); err != nil {
		return err
	}

	f.fileInput.Write(ctx, e)
	return nil
}

// decode converts the bytes in msgBuf to utf-8 from the configured encoding
func (f *Reader) decode(msgBuf []byte) (string, error) {
	for {
		f.decoder.Reset()
		nDst, _, err := f.decoder.Transform(f.decodeBuffer, msgBuf, true)
		if err != nil && err == transform.ErrShortDst {
			f.decodeBuffer = make([]byte, len(f.decodeBuffer)*2)
			continue
		} else if err != nil {
			return "", fmt.Errorf("transform encoding: %s", err)
		}
		return string(f.decodeBuffer[:nDst]), nil
	}
}

func getScannerError(scanner *PositionalScanner) error {
	err := scanner.Err()
	if err == bufio.ErrTooLong {
		return errors.NewError("log entry too large", "increase max_log_size or ensure that multiline regex patterns terminate")
	} else if err != nil {
		return errors.Wrap(err, "scanner error")
	}
	return nil
}

// Read from the file and update the fingerprint if necessary
func (f *Reader) Read(dst []byte) (int, error) {
	if len(f.Fingerprint.FirstBytes) == f.fileInput.fingerprintSize {
		return f.file.Read(dst)
	}
	n, err := f.file.Read(dst)
	appendCount := min0(n, f.fileInput.fingerprintSize-int(f.Offset))
	f.Fingerprint.FirstBytes = append(f.Fingerprint.FirstBytes[:f.Offset], dst[:appendCount]...)
	return n, err
}

func min0(a, b int) int {
	if a < 0 || b < 0 {
		return 0
	}
	if a < b {
		return a
	}
	return b
}
<commit_msg>Change Reader receiver variable name to r instead of legacy f, which was confusing (#205)<commit_after>// Copyright The OpenTelemetry Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package file

import (
	"bufio"
	"context"
	"fmt"
	"os"
	"path/filepath"

	"go.uber.org/zap"
	"golang.org/x/text/encoding"
	"golang.org/x/text/transform"

	"github.com/open-telemetry/opentelemetry-log-collection/errors"
)

// File attributes contains information about file paths
type fileAttributes struct {
	Name         string
	Path         string
	ResolvedName string
	ResolvedPath string
}

// resolveFileAttributes resolves file attributes
// and sets it to empty string in case of error
func (f *InputOperator) resolveFileAttributes(path string) *fileAttributes {
	resolved, err := filepath.EvalSymlinks(path)
	if err != nil {
		f.Error(err)
	}

	abs, err := filepath.Abs(resolved)
	if err != nil {
		f.Error(err)
	}

	return &fileAttributes{
		Path:         path,
		Name:         filepath.Base(path),
		ResolvedPath: abs,
		ResolvedName: filepath.Base(abs),
	}
}

// Reader manages a single file
type Reader struct {
	Fingerprint *Fingerprint
	Offset      int64

	generation     int
	fileInput      *InputOperator
	file           *os.File
	fileAttributes *fileAttributes

	decoder      *encoding.Decoder
	decodeBuffer []byte

	*zap.SugaredLogger `json:"-"`
}

// NewReader creates a new file reader
func (f *InputOperator) NewReader(path string, file *os.File, fp *Fingerprint) (*Reader, error) {
	r := &Reader{
		Fingerprint:    fp,
		file:           file,
		fileInput:      f,
		SugaredLogger:  f.SugaredLogger.With("path", path),
		decoder:        f.encoding.Encoding.NewDecoder(),
		decodeBuffer:   make([]byte, 1<<12),
		fileAttributes: f.resolveFileAttributes(path),
	}
	return r, nil
}

// Copy creates a deep copy of a Reader
func (r *Reader) Copy(file *os.File) (*Reader, error) {
	reader, err := r.fileInput.NewReader(r.fileAttributes.Path, file, r.Fingerprint.Copy())
	if err != nil {
		return nil, err
	}
	reader.Offset = r.Offset
	return reader, nil
}

// InitializeOffset sets the starting offset
func (r *Reader) InitializeOffset(startAtBeginning bool) error {
	if !startAtBeginning {
		info, err := r.file.Stat()
		if err != nil {
			return fmt.Errorf("stat: %s", err)
		}
		r.Offset = info.Size()
	}
	return nil
}

// ReadToEnd will read until the end of the file
func (r *Reader) ReadToEnd(ctx context.Context) {
	if _, err := r.file.Seek(r.Offset, 0); err != nil {
		r.Errorw("Failed to seek", zap.Error(err))
		return
	}

	scanner := NewPositionalScanner(r, r.fileInput.MaxLogSize, r.Offset, r.fileInput.SplitFunc)

	// Iterate over the tokenized file, emitting entries as we go
	for {
		select {
		case <-ctx.Done():
			return
		default:
		}

		ok := scanner.Scan()
		if !ok {
			if err := getScannerError(scanner); err != nil {
				r.Errorw("Failed during scan", zap.Error(err))
			}
			break
		}

		if err := r.emit(ctx, scanner.Bytes()); err != nil {
			r.Error("Failed to emit entry", zap.Error(err))
		}
		r.Offset = scanner.Pos()
	}
}

// Close will close the file
func (r *Reader) Close() {
	if r.file != nil {
		if err := r.file.Close(); err != nil {
			r.Debugf("Problem closing reader", "Error", err.Error())
		}
	}
}

// Emit creates an entry with the decoded message and sends it to the next
// operator in the pipeline
func (r *Reader) emit(ctx context.Context, msgBuf []byte) error {
	// Skip the entry if it's empty
	if len(msgBuf) == 0 {
		return nil
	}

	msg, err := r.decode(msgBuf)
	if err != nil {
		return fmt.Errorf("decode: %s", err)
	}

	e, err := r.fileInput.NewEntry(msg)
	if err != nil {
		return fmt.Errorf("create entry: %s", err)
	}

	if err := e.Set(r.fileInput.FilePathField, r.fileAttributes.Path); err != nil {
		return err
	}
	if err := e.Set(r.fileInput.FileNameField, r.fileAttributes.Name); err != nil {
		return err
	}

	if err := e.Set(r.fileInput.FilePathResolvedField, r.fileAttributes.ResolvedPath); err != nil {
		return err
	}

	if err := e.Set(r.fileInput.FileNameResolvedField, r.fileAttributes.ResolvedName); err != nil {
		return err
	}

	r.fileInput.Write(ctx, e)
	return nil
}

// decode converts the bytes in msgBuf to utf-8 from the configured encoding
func (r *Reader) decode(msgBuf []byte) (string, error) {
	for {
		r.decoder.Reset()
		nDst, _, err := r.decoder.Transform(r.decodeBuffer, msgBuf, true)
		if err != nil && err == transform.ErrShortDst {
			r.decodeBuffer = make([]byte, len(r.decodeBuffer)*2)
			continue
		} else if err != nil {
			return "", fmt.Errorf("transform encoding: %s", err)
		}
		return string(r.decodeBuffer[:nDst]), nil
	}
}

func getScannerError(scanner *PositionalScanner) error {
	err := scanner.Err()
	if err == bufio.ErrTooLong {
		return errors.NewError("log entry too large", "increase max_log_size or ensure that multiline regex patterns terminate")
	} else if err != nil {
		return errors.Wrap(err, "scanner error")
	}
	return nil
}

// Read from the file and update the fingerprint if necessary
func (r *Reader) Read(dst []byte) (int, error) {
	if len(r.Fingerprint.FirstBytes) == r.fileInput.fingerprintSize {
		return r.file.Read(dst)
	}
	n, err := r.file.Read(dst)
	appendCount := min0(n, r.fileInput.fingerprintSize-int(r.Offset))
	r.Fingerprint.FirstBytes = append(r.Fingerprint.FirstBytes[:r.Offset], dst[:appendCount]...)
	return n, err
}

func min0(a, b int) int {
	if a < 0 || b < 0 {
		return 0
	}
	if a < b {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>// warren takes home monitoring data and feeds it into
// [influxdb](http://influxdb.org/).
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"syscall"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/huin/warren/cc"
	"github.com/huin/warren/linux"
	promm "github.com/prometheus/client_golang/prometheus"
)

var (
	configFile = flag.String("config", "", "Path to configuration file")
)

type Config struct {
	LogPath     string
	Prometheus  PrometheusConfig
	System      *linux.Config
	CurrentCost []cc.Config
}

type PrometheusConfig struct {
	HandlerPath string
	ServeAddr   string
}

func initLogging(logpath string) error {
	if logpath == "" {
		// Leave default logging as STDERR.
		return nil
	}

	f, err := os.OpenFile(logpath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, syscall.S_IWUSR|syscall.S_IRUSR)
	if err != nil {
		return fmt.Errorf("failed to open log file: %v", err)
	}
	log.SetOutput(f)
	return nil
}

func readConfig(filename string) (*Config, error) {
	config := new(Config)
	_, err := toml.DecodeFile(filename, &config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func monitorLoop(desc string, fn func() error) {
	for {
		if err := fn(); err != nil {
			log.Printf("%s monitoring error (restarting): %v", desc, err)
		} else {
			log.Printf("%s returned without error (restarting)", desc)
		}
		// Avoid tightlooping on recurring failure.
		time.Sleep(5 * time.Second)
	}
}

func main() {
	flag.Parse()
	if *configFile == "" {
		log.Fatal("--config is required with a filename")
	}
	config, err := readConfig(*configFile)
	if err != nil {
		log.Fatal("Failed to read configuration: ", err)
	}
	initLogging(config.LogPath)

	log.Printf("Starting %d CurrentCost collectors", len(config.CurrentCost))
	for _, ccConfig := range config.CurrentCost {
		ccc, err := cc.NewCurrentCostCollector(ccConfig)
		if err != nil {
			log.Fatal("Error in CurrentCost config: %v", err)
		}
		promm.MustRegister(ccc)
		go monitorLoop("Current Cost", func() error {
			return ccc.Run()
		})
	}

	if config.System != nil {
		log.Print("Starting local system monitoring")
		promm.MustRegister(linux.NewLinuxCollector(*config.System))
	}

	log.Print("Starting Prometheus metrics handler")
	http.Handle(config.Prometheus.HandlerPath, promm.Handler())
	http.ListenAndServe(config.Prometheus.ServeAddr, nil)
}
<commit_msg>Fix package comment for warren binary.<commit_after>// Warren is a program to act as part of a monitoring system on a home network.
// It exports data for external programs to acquire and log to timeseries
// databases. Currently, Warren exports data in a way that is intended for
// scraping by Prometheus - http://prometheus.io/.
package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"syscall"
	"time"

	"github.com/BurntSushi/toml"
	"github.com/huin/warren/cc"
	"github.com/huin/warren/linux"
	promm "github.com/prometheus/client_golang/prometheus"
)

var (
	configFile = flag.String("config", "", "Path to configuration file")
)

type Config struct {
	LogPath     string
	Prometheus  PrometheusConfig
	System      *linux.Config
	CurrentCost []cc.Config
}

type PrometheusConfig struct {
	HandlerPath string
	ServeAddr   string
}

func initLogging(logpath string) error {
	if logpath == "" {
		// Leave default logging as STDERR.
		return nil
	}

	f, err := os.OpenFile(logpath, os.O_CREATE|os.O_APPEND|os.O_WRONLY, syscall.S_IWUSR|syscall.S_IRUSR)
	if err != nil {
		return fmt.Errorf("failed to open log file: %v", err)
	}
	log.SetOutput(f)
	return nil
}

func readConfig(filename string) (*Config, error) {
	config := new(Config)
	_, err := toml.DecodeFile(filename, &config)
	if err != nil {
		return nil, err
	}
	return config, nil
}

func monitorLoop(desc string, fn func() error) {
	for {
		if err := fn(); err != nil {
			log.Printf("%s monitoring error (restarting): %v", desc, err)
		} else {
			log.Printf("%s returned without error (restarting)", desc)
		}
		// Avoid tightlooping on recurring failure.
		time.Sleep(5 * time.Second)
	}
}

func main() {
	flag.Parse()
	if *configFile == "" {
		log.Fatal("--config is required with a filename")
	}
	config, err := readConfig(*configFile)
	if err != nil {
		log.Fatal("Failed to read configuration: ", err)
	}
	initLogging(config.LogPath)

	log.Printf("Starting %d CurrentCost collectors", len(config.CurrentCost))
	for _, ccConfig := range config.CurrentCost {
		ccc, err := cc.NewCurrentCostCollector(ccConfig)
		if err != nil {
			log.Fatal("Error in CurrentCost config: %v", err)
		}
		promm.MustRegister(ccc)
		go monitorLoop("Current Cost", func() error {
			return ccc.Run()
		})
	}

	if config.System != nil {
		log.Print("Starting local system monitoring")
		promm.MustRegister(linux.NewLinuxCollector(*config.System))
	}

	log.Print("Starting Prometheus metrics handler")
	http.Handle(config.Prometheus.HandlerPath, promm.Handler())
	http.ListenAndServe(config.Prometheus.ServeAddr, nil)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os/exec"

	"github.com/kataras/iris"
)

func main() {
	server := iris.New()

	//监听微信服务器的信息
	server.Post("/", func(c *iris.Context) {
		//记录请求
		fmt.Println(c.MethodString(), c.URI(), c.RemoteAddr())

		//建议域名是否真确
		if hostname := c.HostString(); hostname != "weixin.chenlixin.net" {
			c.Log("异常域名:", hostname)
			c.Write("404")
			return
		}

		//检验是否是微信服务器的请求
		signature := c.URLParam("signature")
		timestamp := c.URLParam("timestamp")
		nonce := c.URLParam("nonce")
		if !validateURL(signature, timestamp, nonce) {
			c.Log("参数错误%s,%s,%s.\n", signature, timestamp, nonce)
			c.Write("404")
			return
		}

		//过滤openid
		openid := c.URLParam("openid")
		filteOpenid(openid)

		//处理请求数据
		handlerMux(c)
	})

	//监听github.com的自动更新
	server.Post("/update", func(c *iris.Context) {
		//记录请求
		fmt.Println(c.MethodString(), c.URI(), c.RemoteAddr())

		//执行命令
		cmd := exec.Command("go", "get", "-u", "github.com/sunclx/weixin")
		err := cmd.Run()
		if err != nil {
			fmt.Println("errors:", err)

		}
	})

	//启动服务
	server.Listen(":80")
}

func filteOpenid(openid string) {

}
<commit_msg>排除非post<commit_after>package main

import (
	"fmt"
	"os/exec"

	"github.com/kataras/iris"
)

func main() {
	server := iris.New()

	//监听微信服务器的信息
	server.HandleFunc("", "/", func(c *iris.Context) {
		//记录请求
		fmt.Println(c.MethodString(), c.URI(), c.RemoteAddr())

		//排除非POST请求
		if c.MethodString() != iris.MethodPost {
			c.WriteString("404")
			return
		}

		//建议域名是否真确
		if hostname := c.HostString(); hostname != "weixin.chenlixin.net" {
			c.Log("异常域名:", hostname)
			c.Write("404")
			return
		}

		//检验是否是微信服务器的请求
		signature := c.URLParam("signature")
		timestamp := c.URLParam("timestamp")
		nonce := c.URLParam("nonce")
		if !validateURL(signature, timestamp, nonce) {
			c.Log("参数错误%s,%s,%s.\n", signature, timestamp, nonce)
			c.Write("404")
			return
		}

		//过滤openid
		openid := c.URLParam("openid")
		filteOpenid(openid)

		//处理请求数据
		handlerMux(c)
	})

	//监听github.com的自动更新
	server.Post("/update", func(c *iris.Context) {
		//记录请求
		fmt.Println(c.MethodString(), c.URI(), c.RemoteAddr())

		//执行命令
		cmd := exec.Command("go", "get", "-u", "github.com/sunclx/weixin")
		err := cmd.Run()
		if err != nil {
			fmt.Println("errors:", err)

		}
	})

	//启动服务
	server.Listen(":80")
}

func filteOpenid(openid string) {

}
<|endoftext|>


<commit_before>// Copyright 2017 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package datum

import (
	"fmt"
	"sync/atomic"
	"time"
)

type Type int

const (
	Int Type = iota
	Float
)

// Datum
type Datum interface {
	// Type returns the Datum type.
	Type() Type

	Value() string

	Time() string
}

type datum struct {
	time int64 // nanoseconds since unix epoch
}

var zeroTime time.Time

func (d *datum) stamp(timestamp time.Time) {
	if timestamp.IsZero() {
		atomic.StoreInt64(&d.time, time.Now().UTC().UnixNano())
	} else {
		atomic.StoreInt64(&d.time, timestamp.UnixNano())
	}
}

func (d *datum) Time() string {
	return fmt.Sprintf("%d", atomic.LoadInt64(&d.time)/1e9)
}

func NewInt() Datum {
	return MakeInt(0, zeroTime)
}

func NewFloat() Datum {
	return MakeFloat(0., zeroTime)
}

func MakeInt(v int64, ts time.Time) Datum {
	d := &intDatum{}
	d.Set(v, ts)
	return d
}

func MakeFloat(v float64, ts time.Time) Datum {
	d := &floatDatum{}
	d.Set(v, ts)
	return d
}

func GetInt(d Datum) int64 {
	switch d := d.(type) {
	case *intDatum:
		return d.Get()
	default:
		panic(fmt.Sprintf("%v is not an Int", d))
	}
}

func GetFloat(d Datum) float64 {
	switch d := d.(type) {
	case *floatDatum:
		return d.Get()
	default:
		panic(fmt.Sprintf("%v is not a Float", d))
	}
}

func SetInt(d Datum, v int64, ts time.Time) {
	switch d := d.(type) {
	case *intDatum:
		d.Set(v, ts)
	default:
		panic(fmt.Sprintf("%v is not an Int", d))
	}
}

func SetFloat(d Datum, v float64, ts time.Time) {
	switch d := d.(type) {
	case *floatDatum:
		d.Set(v, ts)
	default:
		panic(fmt.Sprintf("datum %v is not a Float", d))
	}
}

func IncIntBy(d Datum, v int64, ts time.Time) {
	switch d := d.(type) {
	case *intDatum:
		d.IncBy(v, ts)
	default:
		panic(fmt.Sprintf("%v is not an Int", d))
	}
}
<commit_msg>Update panic messages for invalid use of datum.<commit_after>// Copyright 2017 Google Inc. All Rights Reserved.
// This file is available under the Apache license.

package datum

import (
	"fmt"
	"sync/atomic"
	"time"
)

type Type int

const (
	Int Type = iota
	Float
)

// Datum
type Datum interface {
	// Type returns the Datum type.
	Type() Type

	Value() string

	Time() string
}

type datum struct {
	time int64 // nanoseconds since unix epoch
}

var zeroTime time.Time

func (d *datum) stamp(timestamp time.Time) {
	if timestamp.IsZero() {
		atomic.StoreInt64(&d.time, time.Now().UTC().UnixNano())
	} else {
		atomic.StoreInt64(&d.time, timestamp.UnixNano())
	}
}

func (d *datum) Time() string {
	return fmt.Sprintf("%d", atomic.LoadInt64(&d.time)/1e9)
}

func NewInt() Datum {
	return MakeInt(0, zeroTime)
}

func NewFloat() Datum {
	return MakeFloat(0., zeroTime)
}

func MakeInt(v int64, ts time.Time) Datum {
	d := &intDatum{}
	d.Set(v, ts)
	return d
}

func MakeFloat(v float64, ts time.Time) Datum {
	d := &floatDatum{}
	d.Set(v, ts)
	return d
}

func GetInt(d Datum) int64 {
	switch d := d.(type) {
	case *intDatum:
		return d.Get()
	default:
		panic(fmt.Sprintf("datum %v is not an Int", d))
	}
}

func GetFloat(d Datum) float64 {
	switch d := d.(type) {
	case *floatDatum:
		return d.Get()
	default:
		panic(fmt.Sprintf("datum %v is not a Float", d))
	}
}

func SetInt(d Datum, v int64, ts time.Time) {
	switch d := d.(type) {
	case *intDatum:
		d.Set(v, ts)
	default:
		panic(fmt.Sprintf("datum %v is not an Int", d))
	}
}

func SetFloat(d Datum, v float64, ts time.Time) {
	switch d := d.(type) {
	case *floatDatum:
		d.Set(v, ts)
	default:
		panic(fmt.Sprintf("datum %v is not a Float", d))
	}
}

func IncIntBy(d Datum, v int64, ts time.Time) {
	switch d := d.(type) {
	case *intDatum:
		d.IncBy(v, ts)
	default:
		panic(fmt.Sprintf("datum %v is not an Int", d))
	}
}
<|endoftext|>


<commit_before>package middleware

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

// replacer is a type which can replace placeholder
// substrings in a string with actual values from a
// http.Request and responseRecorder. Always use
// NewReplacer to get one of these.
type replacer map[string]string

// NewReplacer makes a new replacer based on r and rr.
// Do not create a new replacer until r and rr have all
// the needed values, because this function copies those
// values into the replacer.
func NewReplacer(r *http.Request, rr *responseRecorder) replacer {
	rep := replacer{
		"{method}": r.Method,
		"{scheme}": func() string {
			if r.TLS != nil {
				return "https"
			}
			return "http"
		}(),
		"{host}":     r.Host,
		"{path}":     r.URL.Path,
		"{query}":    r.URL.RawQuery,
		"{fragment}": r.URL.Fragment,
		"{proto}":    r.Proto,
		"{remote}": func() string {
			if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" {
				return fwdFor
			}
			host, _, err := net.SplitHostPort(r.RemoteAddr)
			if err != nil {
				return r.RemoteAddr
			}
			return host
		}(),
		"{port}": func() string {
			_, port, err := net.SplitHostPort(r.RemoteAddr)
			if err != nil {
				return ""
			}
			return port
		}(),
		"{uri}": r.RequestURI,
		"{when}": func() string {
			return time.Now().Format(timeFormat)
		}(),
	}
	if rr != nil {
		rep["{status}"] = strconv.Itoa(rr.status)
		rep["{size}"] = strconv.Itoa(rr.size)
		rep["{latency}"] = time.Since(rr.start).String()
	}

	// Header placeholders
	for header, val := range r.Header {
		rep[headerReplacer+header+"}"] = strings.Join(val, ",")
	}

	return rep
}

// Replace performs a replacement of values on s and returns
// the string with the replaced values.
func (r replacer) Replace(s string) string {
	for placeholder, replacement := range r {
		if replacement == "" {
			replacement = EmptyStringReplacer
		}
		s = strings.Replace(s, placeholder, replacement, -1)
	}

	// Replace any header placeholders that weren't found
	for strings.Contains(s, headerReplacer) {
		idxStart := strings.Index(s, headerReplacer)
		endOffset := idxStart + len(headerReplacer)
		idxEnd := strings.Index(s[endOffset:], "}")
		if idxEnd > -1 {
			s = s[:idxStart] + EmptyStringReplacer + s[endOffset+idxEnd+1:]
		} else {
			break
		}
	}
	return s
}

const (
	timeFormat          = "02/Jan/2006:15:04:05 -0700"
	headerReplacer      = "{>"
	EmptyStringReplacer = "-"
)
<commit_msg>Export Replacer type<commit_after>package middleware

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

// Replacer is a type which can replace placeholder
// substrings in a string with actual values from a
// http.Request and responseRecorder. Always use
// NewReplacer to get one of these.
type Replacer interface {
	Replace(string) string
}

type replacer map[string]string

// NewReplacer makes a new replacer based on r and rr.
// Do not create a new replacer until r and rr have all
// the needed values, because this function copies those
// values into the replacer.
func NewReplacer(r *http.Request, rr *responseRecorder) Replacer {
	rep := replacer{
		"{method}": r.Method,
		"{scheme}": func() string {
			if r.TLS != nil {
				return "https"
			}
			return "http"
		}(),
		"{host}":     r.Host,
		"{path}":     r.URL.Path,
		"{query}":    r.URL.RawQuery,
		"{fragment}": r.URL.Fragment,
		"{proto}":    r.Proto,
		"{remote}": func() string {
			if fwdFor := r.Header.Get("X-Forwarded-For"); fwdFor != "" {
				return fwdFor
			}
			host, _, err := net.SplitHostPort(r.RemoteAddr)
			if err != nil {
				return r.RemoteAddr
			}
			return host
		}(),
		"{port}": func() string {
			_, port, err := net.SplitHostPort(r.RemoteAddr)
			if err != nil {
				return ""
			}
			return port
		}(),
		"{uri}": r.RequestURI,
		"{when}": func() string {
			return time.Now().Format(timeFormat)
		}(),
	}
	if rr != nil {
		rep["{status}"] = strconv.Itoa(rr.status)
		rep["{size}"] = strconv.Itoa(rr.size)
		rep["{latency}"] = time.Since(rr.start).String()
	}

	// Header placeholders
	for header, val := range r.Header {
		rep[headerReplacer+header+"}"] = strings.Join(val, ",")
	}

	return rep
}

// Replace performs a replacement of values on s and returns
// the string with the replaced values.
func (r replacer) Replace(s string) string {
	for placeholder, replacement := range r {
		if replacement == "" {
			replacement = EmptyStringReplacer
		}
		s = strings.Replace(s, placeholder, replacement, -1)
	}

	// Replace any header placeholders that weren't found
	for strings.Contains(s, headerReplacer) {
		idxStart := strings.Index(s, headerReplacer)
		endOffset := idxStart + len(headerReplacer)
		idxEnd := strings.Index(s[endOffset:], "}")
		if idxEnd > -1 {
			s = s[:idxStart] + EmptyStringReplacer + s[endOffset+idxEnd+1:]
		} else {
			break
		}
	}
	return s
}

const (
	timeFormat          = "02/Jan/2006:15:04:05 -0700"
	headerReplacer      = "{>"
	EmptyStringReplacer = "-"
)
<|endoftext|>


<commit_before><commit_msg>Variable rename<commit_after><|endoftext|>


<commit_before>package fmt

import (
	"bytes"
	"nvim-go/util"

	"github.com/garyburd/neovim-go/vim"
	"github.com/garyburd/neovim-go/vim/plugin"
	"golang.org/x/tools/imports"
)

func init() {
	plugin.HandleCommand("Gofmt", &plugin.CommandOptions{Range: "%", Eval: "expand('%:p')"}, fmt)
}

var options = imports.Options{
	AllErrors: true,
	Comments:  true,
	TabIndent: true,
	TabWidth:  8,
}

func fmt(v *vim.Vim, r [2]int, file string) error {
	defer util.WithGoBuildForPath(file)()

	b, err := v.CurrentBuffer()
	if err != nil {
		return err
	}

	in, err := v.BufferLineSlice(b, 0, -1, true, true)
	if err != nil {
		return err
	}

	buf, err := imports.Process("", bytes.Join(in, []byte{'\n'}), &options)
	if err != nil {
		return util.ReportErrors(v, b, err)
	}

	out := bytes.Split(bytes.TrimSuffix(buf, []byte{'\n'}), []byte{'\n'})

	return minUpdate(v, b, in, out)
}

func minUpdate(v *vim.Vim, b vim.Buffer, in [][]byte, out [][]byte) error {

	// Find matching head lines.
	n := len(out)
	if len(in) < len(out) {
		n = len(in)
	}
	head := 0
	for ; head < n; head++ {
		if !bytes.Equal(in[head], out[head]) {
			break
		}
	}

	// Nothing to do?
	if head == len(in) && head == len(out) {
		return nil
	}

	// Find matching tail lines.
	n -= head
	tail := 0
	for ; tail < n; tail++ {
		if !bytes.Equal(in[len(in)-tail-1], out[len(out)-tail-1]) {
			break
		}
	}

	// Update the buffer.
	includeStart := true
	start := head
	end := len(in) - tail
	repl := out[head : len(out)-tail]

	if start == len(in) {
		start = -1
		includeStart = false
	}

	return v.SetBufferLineSlice(b, start, end, includeStart, false, repl)
}
<commit_msg>Change util package name to gb<commit_after>package fmt

import (
	"bytes"
	"go/scanner"

	"nvim-go/gb"

	"github.com/garyburd/neovim-go/vim"
	"github.com/garyburd/neovim-go/vim/plugin"
	"github.com/garyburd/neovim-go/vim/vimutil"
	"golang.org/x/tools/imports"
)

func init() {
	plugin.HandleCommand("Gofmt", &plugin.CommandOptions{Range: "%", Eval: "expand('%:p')"}, fmt)
}

var options = imports.Options{
	AllErrors: true,
	Comments:  true,
	TabIndent: true,
	TabWidth:  8,
}

func fmt(v *vim.Vim, r [2]int, file string) error {
	defer gb.WithGoBuildForPath(file)()

	b, err := v.CurrentBuffer()
	if err != nil {
		return err
	}

	in, err := v.BufferLineSlice(b, 0, -1, true, true)
	if err != nil {
		return err
	}

	buf, err := imports.Process("", bytes.Join(in, []byte{'\n'}), &options)
	if err != nil {
		return reportErrors(v, b, err)
	}

	out := bytes.Split(bytes.TrimSuffix(buf, []byte{'\n'}), []byte{'\n'})

	return minUpdate(v, b, in, out)
}

func reportErrors(v *vim.Vim, b vim.Buffer, formatErr error) error {
	var qfl []*vimutil.QuickfixError
	if e, ok := formatErr.(scanner.Error); ok {
		qfl = append(qfl, &vimutil.QuickfixError{
			LNum: e.Pos.Line,
			Col:  e.Pos.Column,
			Text: e.Msg,
		})
	} else if el, ok := formatErr.(scanner.ErrorList); ok {
		for _, e := range el {
			qfl = append(qfl, &vimutil.QuickfixError{
				LNum: e.Pos.Line,
				Col:  e.Pos.Column,
				Text: e.Msg,
			})
		}
	}

	if len(qfl) == 0 {
		return formatErr
	}

	bufnr, err := v.BufferNumber(b)
	if err != nil {
		return err
	}
	for i := range qfl {
		qfl[i].Bufnr = bufnr
	}

	if err := v.Call("setqflist", nil, qfl); err != nil {
		return err
	}

	return v.Command("cc")
}

func minUpdate(v *vim.Vim, b vim.Buffer, in [][]byte, out [][]byte) error {

	// Find matching head lines.
	n := len(out)
	if len(in) < len(out) {
		n = len(in)
	}
	head := 0
	for ; head < n; head++ {
		if !bytes.Equal(in[head], out[head]) {
			break
		}
	}

	// Nothing to do?
	if head == len(in) && head == len(out) {
		return nil
	}

	// Find matching tail lines.
	n -= head
	tail := 0
	for ; tail < n; tail++ {
		if !bytes.Equal(in[len(in)-tail-1], out[len(out)-tail-1]) {
			break
		}
	}

	// Update the buffer.
	includeStart := true
	start := head
	end := len(in) - tail
	repl := out[head : len(out)-tail]

	if start == len(in) {
		start = -1
		includeStart = false
	}

	return v.SetBufferLineSlice(b, start, end, includeStart, false, repl)
}
<|endoftext|>


<commit_before>package omniscient

import (
	"strings"
	"time"

	"github.com/satori/go.uuid"
)

const (
	fieldNoteID        = "id"
	fieldNoteContent   = "content"
	fieldNoteCreatedAt = "created_at"
	fieldNoteUpdatedAt = "updated_at"

	catalogKey = "catalog"
)

var (
	defaultRedisClient RedisClient
	defaultIDGenFn     = func() string {
		return uuid.NewV4().String()
	}
)

// Note is note.
type Note struct {
	ID        string    `json:"id"`
	Content   string    `json:"content"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func noteFromMap(m map[string]string) *Note {
	n := &Note{
		ID:      m[fieldNoteID],
		Content: m[fieldNoteContent],
	}

	if t, err := time.Parse(time.RFC3339, m[fieldNoteCreatedAt]); err == nil {
		n.CreatedAt = t
	}

	if t, err := time.Parse(time.RFC3339, m[fieldNoteUpdatedAt]); err == nil {
		n.UpdatedAt = t
	}

	return n
}

// NoteRepository is a repository for managing notes.
type NoteRepository interface {
	Create(content string) (*Note, error)
	Retrieve(id string) (*Note, error)
	Update(id, content string) (*Note, error)
	Delete(id string) error
	List() ([]Note, error)
}

type idGenFn func() string

// RedisNoteRepository is an implementation of NoteRepository backed by Redis.
type RedisNoteRepository struct {
	base        string
	redisClient RedisClient
	idGen       idGenFn
}

var _ NoteRepository = (*RedisNoteRepository)(nil)

// RedisNoteRepositoryOption is an option for configuring RedisNoteRepository.
type RedisNoteRepositoryOption func(*RedisNoteRepository) error

// NewRedisNoteRepository creates an instance of NoteRepository.
func NewRedisNoteRepository(opts ...RedisNoteRepositoryOption) (NoteRepository, error) {
	rnr := &RedisNoteRepository{
		base:        "notes",
		redisClient: defaultRedisClient,
		idGen:       defaultIDGenFn,
	}

	for _, opt := range opts {
		err := opt(rnr)
		if err != nil {
			return nil, err
		}
	}

	return rnr, nil
}

// RedisClientOption sets the Redis client for RedisNoteRepository.
func RedisClientOption(rc RedisClient) RedisNoteRepositoryOption {
	return func(rnr *RedisNoteRepository) error {
		rnr.redisClient = rc
		return nil
	}
}

// NoteIDGenFn sets the id generator for RedisNoteRepository.
func NoteIDGenFn(idGen func() string) RedisNoteRepositoryOption {
	return func(rnr *RedisNoteRepository) error {
		rnr.idGen = idGen
		return nil
	}
}

// Create creates a new note.
func (nr *RedisNoteRepository) Create(content string) (*Note, error) {
	now := time.Now()

	note := Note{
		ID:        nr.idGen(),
		Content:   content,
		CreatedAt: now,
		UpdatedAt: now,
	}

	err := nr.save(&note)
	if err != nil {
		return nil, err
	}

	_, err = nr.redisClient.LPush(nr.keyForID(catalogKey),
		note.ID)
	if err != nil {
		return nil, err
	}

	return &note, nil
}

// Retrieve retrieves an existing note.
func (nr *RedisNoteRepository) Retrieve(id string) (*Note, error) {
	return nr.load(id)
}

// Update updates an existing note.
func (nr *RedisNoteRepository) Update(id, content string) (*Note, error) {
	m, err := nr.redisClient.HGetAllMap(nr.keyForID(id))
	if err != nil {
		return nil, err
	}

	n := noteFromMap(m)
	n.Content = content
	n.UpdatedAt = time.Now()

	err = nr.save(n)
	if err != nil {
		return nil, err
	}

	return n, nil
}

// Delete deletes an existing note.
func (nr *RedisNoteRepository) Delete(id string) error {
	_, err := nr.redisClient.LRem(nr.keyForID(catalogKey), 0, id)
	if err != nil {
		return err
	}

	_, err = nr.redisClient.Delete(nr.keyForID(id))

	return err
}

// List retrieves all the notes.
func (nr *RedisNoteRepository) List() ([]Note, error) {
	notes := []Note{}
	ids, err := nr.redisClient.LRange(nr.keyForID(catalogKey), 0, -1)
	if err != nil {
		return nil, err
	}

	for _, id := range ids {
		note, err := nr.load(id)
		if err != nil {
			return nil, err
		}

		notes = append(notes, *note)
	}

	return notes, nil
}

func (nr *RedisNoteRepository) keyForID(id string) string {
	return strings.Join([]string{
		nr.base, id,
	}, ":")
}

func (nr *RedisNoteRepository) save(note *Note) error {
	key := nr.keyForID(note.ID)

	_, err := nr.redisClient.HMSet(key,
		fieldNoteID, note.ID,
		fieldNoteContent, note.Content,
		fieldNoteCreatedAt, note.CreatedAt.Format(time.RFC3339),
		fieldNoteUpdatedAt, note.UpdatedAt.Format(time.RFC3339))

	return err
}

func (nr *RedisNoteRepository) load(id string) (*Note, error) {
	m, err := nr.redisClient.HGetAllMap(nr.keyForID(id))
	if err != nil {
		return nil, err
	}

	return noteFromMap(m), nil
}
<commit_msg>allow changing of the note base key<commit_after>package omniscient

import (
	"strings"
	"time"

	"github.com/satori/go.uuid"
)

const (
	fieldNoteID        = "id"
	fieldNoteContent   = "content"
	fieldNoteCreatedAt = "created_at"
	fieldNoteUpdatedAt = "updated_at"

	catalogKey = "catalog"
)

var (
	defaultRedisClient RedisClient
	defaultIDGenFn     = func() string {
		return uuid.NewV4().String()
	}
)

// Note is note.
type Note struct {
	ID        string    `json:"id"`
	Content   string    `json:"content"`
	CreatedAt time.Time `json:"created_at"`
	UpdatedAt time.Time `json:"updated_at"`
}

func noteFromMap(m map[string]string) *Note {
	n := &Note{
		ID:      m[fieldNoteID],
		Content: m[fieldNoteContent],
	}

	if t, err := time.Parse(time.RFC3339, m[fieldNoteCreatedAt]); err == nil {
		n.CreatedAt = t
	}

	if t, err := time.Parse(time.RFC3339, m[fieldNoteUpdatedAt]); err == nil {
		n.UpdatedAt = t
	}

	return n
}

// NoteRepository is a repository for managing notes.
type NoteRepository interface {
	Create(content string) (*Note, error)
	Retrieve(id string) (*Note, error)
	Update(id, content string) (*Note, error)
	Delete(id string) error
	List() ([]Note, error)
}

type idGenFn func() string

// RedisNoteRepository is an implementation of NoteRepository backed by Redis.
type RedisNoteRepository struct {
	base        string
	redisClient RedisClient
	idGen       idGenFn
}

var _ NoteRepository = (*RedisNoteRepository)(nil)

// RedisNoteRepositoryOption is an option for configuring RedisNoteRepository.
type RedisNoteRepositoryOption func(*RedisNoteRepository) error

// NewRedisNoteRepository creates an instance of NoteRepository.
func NewRedisNoteRepository(opts ...RedisNoteRepositoryOption) (NoteRepository, error) {
	rnr := &RedisNoteRepository{
		base:        "notes",
		redisClient: defaultRedisClient,
		idGen:       defaultIDGenFn,
	}

	for _, opt := range opts {
		err := opt(rnr)
		if err != nil {
			return nil, err
		}
	}

	return rnr, nil
}

// RedisClientOption sets the Redis client for RedisNoteRepository.
func RedisClientOption(rc RedisClient) RedisNoteRepositoryOption {
	return func(rnr *RedisNoteRepository) error {
		rnr.redisClient = rc
		return nil
	}
}

// NoteIDGenFn sets the id generator for RedisNoteRepository.
func NoteIDGenFn(idGen func() string) RedisNoteRepositoryOption {
	return func(rnr *RedisNoteRepository) error {
		rnr.idGen = idGen
		return nil
	}
}

// NoteBase is the base string for all note keys.
func NoteBase(base string) RedisNoteRepositoryOption {
	return func(rnr *RedisNoteRepository) error {
		rnr.base = base
		return nil
	}
}

// Create creates a new note.
func (nr *RedisNoteRepository) Create(content string) (*Note, error) {
	now := time.Now()

	note := Note{
		ID:        nr.idGen(),
		Content:   content,
		CreatedAt: now,
		UpdatedAt: now,
	}

	err := nr.save(&note)
	if err != nil {
		return nil, err
	}

	_, err = nr.redisClient.LPush(nr.keyForID(catalogKey),
		note.ID)
	if err != nil {
		return nil, err
	}

	return &note, nil
}

// Retrieve retrieves an existing note.
func (nr *RedisNoteRepository) Retrieve(id string) (*Note, error) {
	return nr.load(id)
}

// Update updates an existing note.
func (nr *RedisNoteRepository) Update(id, content string) (*Note, error) {
	m, err := nr.redisClient.HGetAllMap(nr.keyForID(id))
	if err != nil {
		return nil, err
	}

	n := noteFromMap(m)
	n.Content = content
	n.UpdatedAt = time.Now()

	err = nr.save(n)
	if err != nil {
		return nil, err
	}

	return n, nil
}

// Delete deletes an existing note.
func (nr *RedisNoteRepository) Delete(id string) error {
	_, err := nr.redisClient.LRem(nr.keyForID(catalogKey), 0, id)
	if err != nil {
		return err
	}

	_, err = nr.redisClient.Delete(nr.keyForID(id))

	return err
}

// List retrieves all the notes.
func (nr *RedisNoteRepository) List() ([]Note, error) {
	notes := []Note{}
	ids, err := nr.redisClient.LRange(nr.keyForID(catalogKey), 0, -1)
	if err != nil {
		return nil, err
	}

	for _, id := range ids {
		note, err := nr.load(id)
		if err != nil {
			return nil, err
		}

		notes = append(notes, *note)
	}

	return notes, nil
}

func (nr *RedisNoteRepository) keyForID(id string) string {
	return strings.Join([]string{
		nr.base, id,
	}, ":")
}

func (nr *RedisNoteRepository) save(note *Note) error {
	key := nr.keyForID(note.ID)

	_, err := nr.redisClient.HMSet(key,
		fieldNoteID, note.ID,
		fieldNoteContent, note.Content,
		fieldNoteCreatedAt, note.CreatedAt.Format(time.RFC3339),
		fieldNoteUpdatedAt, note.UpdatedAt.Format(time.RFC3339))

	return err
}

func (nr *RedisNoteRepository) load(id string) (*Note, error) {
	m, err := nr.redisClient.HGetAllMap(nr.keyForID(id))
	if err != nil {
		return nil, err
	}

	return noteFromMap(m), nil
}
<|endoftext|>


<commit_before><commit_msg>fix staticcheck:vendor/k8s.io/kubectl/pkg/cmd/scale<commit_after><|endoftext|>


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

package envoy

import (
	"sort"

	envoyAPI "github.com/cilium/cilium/pkg/envoy/cilium"
	"github.com/cilium/cilium/pkg/envoy/xds"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/ipcache"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
)

const (
	// ListenerTypeURL is the type URL of Listener resources.
	ListenerTypeURL = "type.googleapis.com/envoy.api.v2.Listener"

	// NetworkPolicyTypeURL is the type URL of NetworkPolicy resources.
	NetworkPolicyTypeURL = "type.googleapis.com/cilium.NetworkPolicy"

	// NetworkPolicyHostsTypeURL is the type URL of NetworkPolicyHosts resources.
	NetworkPolicyHostsTypeURL = "type.googleapis.com/cilium.NetworkPolicyHosts"
)

// NPHDSCache is a cache of resources in the Network Policy Hosts Discovery
// Service.
type NPHDSCache struct {
	*xds.Cache
}

func newNPHDSCache() NPHDSCache {
	return NPHDSCache{Cache: xds.NewCache()}
}

var (
	// NetworkPolicyHostsCache is the global cache of resources of type
	// NetworkPolicyHosts. Resources in this cache must have the
	// NetworkPolicyHostsTypeURL type URL.
	NetworkPolicyHostsCache = newNPHDSCache()
)

// OnIPIdentityCacheGC is required to implement IPIdentityMappingListener.
func (cache *NPHDSCache) OnIPIdentityCacheGC() {
	// We don't have anything to synchronize in this case.
}

// OnIPIdentityCacheChange pushes modifications to the IP<->Identity mapping
// into the Network Policy Host Discovery Service (NPHDS).
func (cache *NPHDSCache) OnIPIdentityCacheChange(
	modType ipcache.CacheModification, ipIDPair identity.IPIdentityPair) {

	scopedLog := log.WithFields(logrus.Fields{
		logfields.IPAddr:       ipIDPair.IP,
		logfields.Identity:     ipIDPair.ID,
		logfields.Modification: modType,
	})
	// Look up the current resources for the specified Identity.
	msg, err := cache.Lookup(NetworkPolicyHostsTypeURL, ipIDPair.ID.StringID())
	if err != nil {
		scopedLog.WithError(err).Warning("Can't lookup NPHDS cache")
		return
	}

	switch modType {
	case ipcache.Upsert:
		var hostAddresses []string
		if msg == nil {
			hostAddresses = make([]string, 0, 1)
		} else {
			// If the resource already exists, create a copy of it and insert
			// the new IP address into its HostAddresses list.
			npHost := msg.(*envoyAPI.NetworkPolicyHosts)
			hostAddresses = make([]string, 0, len(npHost.HostAddresses)+1)
			hostAddresses = append(hostAddresses, npHost.HostAddresses...)
		}
		hostAddresses = append(hostAddresses, ipIDPair.PrefixString())
		sort.Strings(hostAddresses)

		newNpHost := envoyAPI.NetworkPolicyHosts{
			Policy:        uint64(ipIDPair.ID),
			HostAddresses: hostAddresses,
		}
		if err := newNpHost.Validate(); err != nil {
			scopedLog.WithError(err).WithFields(logrus.Fields{
				logfields.XDSResource: newNpHost,
			}).Warning("Could not validate NPHDS resource update on upsert")
			return
		}
		cache.Upsert(NetworkPolicyHostsTypeURL, ipIDPair.ID.StringID(), &newNpHost, false)
	case ipcache.Delete:
		if msg == nil {
			// Doesn't exist; already deleted.
			return
		}
		cache.handleIPDelete(msg.(*envoyAPI.NetworkPolicyHosts), ipIDPair.ID.StringID(), ipIDPair.PrefixString())
	}
}

// handleIPUpsert deletes elements from the NPHDS cache with the specified peer IP->ID mapping.
func (cache *NPHDSCache) handleIPDelete(npHost *envoyAPI.NetworkPolicyHosts, peerIdentity, peerIP string) {
	targetIndex := -1

	scopedLog := log.WithFields(logrus.Fields{
		logfields.IPAddr:       peerIP,
		logfields.Identity:     peerIdentity,
		logfields.Modification: ipcache.Delete,
	})
	for i, endpointIP := range npHost.HostAddresses {
		if endpointIP == peerIP {
			targetIndex = i
			break
		}
	}
	if targetIndex < 0 {
		scopedLog.Fatal("Can't find IP in NPHDS cache")
		return
	}

	// If removing this host would result in empty list, delete it.
	// Otherwise, update to a list that doesn't contain the target IP
	if len(npHost.HostAddresses) <= 1 {
		cache.Delete(NetworkPolicyHostsTypeURL, peerIdentity, false)
	} else {
		// If the resource is to be updated, create a copy of it before
		// removing the IP address from its HostAddresses list.
		var hostAddresses []string
		if len(npHost.HostAddresses) == targetIndex {
			hostAddresses = npHost.HostAddresses[0:targetIndex]
		} else {
			hostAddresses = append(npHost.HostAddresses[0:targetIndex], npHost.HostAddresses[targetIndex+1:]...)
		}

		newNpHost := envoyAPI.NetworkPolicyHosts{
			Policy:        uint64(npHost.Policy),
			HostAddresses: hostAddresses,
		}
		if err := newNpHost.Validate(); err != nil {
			scopedLog.WithError(err).Warning("Could not validate NPHDS resource update on delete")
			return
		}
		cache.Upsert(NetworkPolicyHostsTypeURL, peerIdentity, &newNpHost, false)
	}
}
<commit_msg>ipcache: Create copies of NPHDS cache resources when deleting<commit_after>// Copyright 2018 Authors of Cilium
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package envoy

import (
	"sort"

	envoyAPI "github.com/cilium/cilium/pkg/envoy/cilium"
	"github.com/cilium/cilium/pkg/envoy/xds"
	"github.com/cilium/cilium/pkg/identity"
	"github.com/cilium/cilium/pkg/ipcache"
	"github.com/cilium/cilium/pkg/logging/logfields"

	"github.com/sirupsen/logrus"
)

const (
	// ListenerTypeURL is the type URL of Listener resources.
	ListenerTypeURL = "type.googleapis.com/envoy.api.v2.Listener"

	// NetworkPolicyTypeURL is the type URL of NetworkPolicy resources.
	NetworkPolicyTypeURL = "type.googleapis.com/cilium.NetworkPolicy"

	// NetworkPolicyHostsTypeURL is the type URL of NetworkPolicyHosts resources.
	NetworkPolicyHostsTypeURL = "type.googleapis.com/cilium.NetworkPolicyHosts"
)

// NPHDSCache is a cache of resources in the Network Policy Hosts Discovery
// Service.
type NPHDSCache struct {
	*xds.Cache
}

func newNPHDSCache() NPHDSCache {
	return NPHDSCache{Cache: xds.NewCache()}
}

var (
	// NetworkPolicyHostsCache is the global cache of resources of type
	// NetworkPolicyHosts. Resources in this cache must have the
	// NetworkPolicyHostsTypeURL type URL.
	NetworkPolicyHostsCache = newNPHDSCache()
)

// OnIPIdentityCacheGC is required to implement IPIdentityMappingListener.
func (cache *NPHDSCache) OnIPIdentityCacheGC() {
	// We don't have anything to synchronize in this case.
}

// OnIPIdentityCacheChange pushes modifications to the IP<->Identity mapping
// into the Network Policy Host Discovery Service (NPHDS).
func (cache *NPHDSCache) OnIPIdentityCacheChange(
	modType ipcache.CacheModification, ipIDPair identity.IPIdentityPair) {

	scopedLog := log.WithFields(logrus.Fields{
		logfields.IPAddr:       ipIDPair.IP,
		logfields.Identity:     ipIDPair.ID,
		logfields.Modification: modType,
	})
	// Look up the current resources for the specified Identity.
	msg, err := cache.Lookup(NetworkPolicyHostsTypeURL, ipIDPair.ID.StringID())
	if err != nil {
		scopedLog.WithError(err).Warning("Can't lookup NPHDS cache")
		return
	}

	switch modType {
	case ipcache.Upsert:
		var hostAddresses []string
		if msg == nil {
			hostAddresses = make([]string, 0, 1)
		} else {
			// If the resource already exists, create a copy of it and insert
			// the new IP address into its HostAddresses list.
			npHost := msg.(*envoyAPI.NetworkPolicyHosts)
			hostAddresses = make([]string, 0, len(npHost.HostAddresses)+1)
			hostAddresses = append(hostAddresses, npHost.HostAddresses...)
		}
		hostAddresses = append(hostAddresses, ipIDPair.PrefixString())
		sort.Strings(hostAddresses)

		newNpHost := envoyAPI.NetworkPolicyHosts{
			Policy:        uint64(ipIDPair.ID),
			HostAddresses: hostAddresses,
		}
		if err := newNpHost.Validate(); err != nil {
			scopedLog.WithError(err).WithFields(logrus.Fields{
				logfields.XDSResource: newNpHost,
			}).Warning("Could not validate NPHDS resource update on upsert")
			return
		}
		cache.Upsert(NetworkPolicyHostsTypeURL, ipIDPair.ID.StringID(), &newNpHost, false)
	case ipcache.Delete:
		if msg == nil {
			// Doesn't exist; already deleted.
			return
		}
		cache.handleIPDelete(msg.(*envoyAPI.NetworkPolicyHosts), ipIDPair.ID.StringID(), ipIDPair.PrefixString())
	}
}

// handleIPUpsert deletes elements from the NPHDS cache with the specified peer IP->ID mapping.
func (cache *NPHDSCache) handleIPDelete(npHost *envoyAPI.NetworkPolicyHosts, peerIdentity, peerIP string) {
	targetIndex := -1

	scopedLog := log.WithFields(logrus.Fields{
		logfields.IPAddr:       peerIP,
		logfields.Identity:     peerIdentity,
		logfields.Modification: ipcache.Delete,
	})
	for i, endpointIP := range npHost.HostAddresses {
		if endpointIP == peerIP {
			targetIndex = i
			break
		}
	}
	if targetIndex < 0 {
		scopedLog.Fatal("Can't find IP in NPHDS cache")
		return
	}

	// If removing this host would result in empty list, delete it.
	// Otherwise, update to a list that doesn't contain the target IP
	if len(npHost.HostAddresses) <= 1 {
		cache.Delete(NetworkPolicyHostsTypeURL, peerIdentity, false)
	} else {
		// If the resource is to be updated, create a copy of it before
		// removing the IP address from its HostAddresses list.
		hostAddresses := make([]string, 0, len(npHost.HostAddresses)-1)
		if len(npHost.HostAddresses) == targetIndex {
			hostAddresses = append(hostAddresses, npHost.HostAddresses[0:targetIndex]...)
		} else {
			hostAddresses = append(hostAddresses, npHost.HostAddresses[0:targetIndex]...)
			hostAddresses = append(hostAddresses, npHost.HostAddresses[targetIndex+1:]...)
		}

		newNpHost := envoyAPI.NetworkPolicyHosts{
			Policy:        uint64(npHost.Policy),
			HostAddresses: hostAddresses,
		}
		if err := newNpHost.Validate(); err != nil {
			scopedLog.WithError(err).Warning("Could not validate NPHDS resource update on delete")
			return
		}
		cache.Upsert(NetworkPolicyHostsTypeURL, peerIdentity, &newNpHost, false)
	}
}
<|endoftext|>