<commit_before>package processor

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

	"gopkg.in/queue.v1"
	"gopkg.in/queue.v1/internal"
)

const consumerBackoff = time.Second
const maxBackoff = 12 * time.Hour
const stopTimeout = 30 * time.Second

var ErrNotSupported = errors.New("processor: not supported")

type Delayer interface {
	Delay() time.Duration
}

type Stats struct {
	InFlight    uint32
	Deleting    uint32
	Processed   uint32
	Retries     uint32
	Fails       uint32
	AvgDuration time.Duration
}

type Processor struct {
	q   Queuer
	opt *queue.Options

	handler         queue.Handler
	fallbackHandler queue.Handler

	ch chan *queue.Message
	wg sync.WaitGroup

	delBatch *internal.Batcher

	_started uint32
	stop     chan struct{}

	inFlight    uint32
	deleting    uint32
	processed   uint32
	fails       uint32
	retries     uint32
	avgDuration uint32
}

func New(q Queuer, opt *queue.Options) *Processor {
	opt.Init()
	p := &Processor{
		q:   q,
		opt: opt,

		ch: make(chan *queue.Message, opt.BufferSize),
	}

	p.setHandler(opt.Handler)
	if opt.FallbackHandler != nil {
		p.setFallbackHandler(opt.FallbackHandler)
	}

	p.delBatch = internal.NewBatcher(p.opt.ScavengerNumber, p.deleteBatch)

	return p
}

func Start(q Queuer, opt *queue.Options) *Processor {
	p := New(q, opt)
	p.Start()
	return p
}

func (p *Processor) String() string {
	return fmt.Sprintf(
		"Processor<%s workers=%d scavengers=%d buffer=%d>",
		p.q.Name(), p.opt.WorkerNumber, p.opt.ScavengerNumber, p.opt.BufferSize,
	)
}

func (p *Processor) Stats() *Stats {
	return &Stats{
		InFlight:    atomic.LoadUint32(&p.inFlight),
		Deleting:    atomic.LoadUint32(&p.deleting),
		Processed:   atomic.LoadUint32(&p.processed),
		Retries:     atomic.LoadUint32(&p.retries),
		Fails:       atomic.LoadUint32(&p.fails),
		AvgDuration: time.Duration(atomic.LoadUint32(&p.avgDuration)) * time.Millisecond,
	}
}

func (p *Processor) setHandler(handler interface{}) {
	p.handler = queue.NewHandler(handler)
}

func (p *Processor) setFallbackHandler(handler interface{}) {
	p.fallbackHandler = queue.NewHandler(handler)
}

func (p *Processor) Add(msg *queue.Message) error {
	p.queueMessage(msg)
	return nil
}

func (p *Processor) Start() error {
	if !p.startWorkers() {
		return nil
	}

	p.wg.Add(1)
	go p.messageFetcher()

	return nil
}

func (p *Processor) startWorkers() bool {
	if !atomic.CompareAndSwapUint32(&p._started, 0, 1) {
		return false
	}

	p.stop = make(chan struct{})
	p.wg.Add(p.opt.WorkerNumber)
	for i := 0; i < p.opt.WorkerNumber; i++ {
		go p.worker()
	}
	return true
}

func (p *Processor) Stop() error {
	return p.StopTimeout(stopTimeout)
}

func (p *Processor) StopTimeout(timeout time.Duration) error {
	return p.stopWorkersTimeout(timeout)
}

func (p *Processor) stopWorkersTimeout(timeout time.Duration) error {
	if !atomic.CompareAndSwapUint32(&p._started, 1, 0) {
		return nil
	}

	close(p.stop)

	stopped := make(chan struct{})
	go func() {
		p.wg.Wait()
		close(stopped)
	}()

	select {
	case <-time.After(timeout):
		return fmt.Errorf("workers did not stop after %s seconds", timeout)
	case <-stopped:
		return p.delBatch.Wait()
	}
}

func (p *Processor) stopped() bool {
	return atomic.LoadUint32(&p._started) == 0
}

func (p *Processor) ProcessAll() error {
	p.startWorkers()
	var noWork int
	for {
		isIdle := atomic.LoadUint32(&p.inFlight) == 0
		n, err := p.fetchMessages()
		if err != nil {
			if err == ErrNotSupported {
				break
			}
			return err
		}
		if n == 0 && isIdle {
			noWork++
		} else {
			noWork = 0
		}
		if noWork == 2 {
			break
		}
	}
	return p.stopWorkersTimeout(stopTimeout)
}

func (p *Processor) ProcessOne() error {
	msg, err := p.reserveOne()
	if err != nil {
		return err
	}
	retErr := p.Process(msg)
	if err := p.delBatch.Wait(); err != nil && retErr == nil {
		retErr = err
	}
	return retErr
}

func (p *Processor) reserveOne() (*queue.Message, error) {
	select {
	case msg := <-p.ch:
		return msg, nil
	default:
	}

	msgs, err := p.q.ReserveN(1)
	if err != nil && err != ErrNotSupported {
		return nil, err
	}
	if len(msgs) == 0 {
		return nil, errors.New("queue is empty")
	}
	atomic.AddUint32(&p.inFlight, 1)
	return &msgs[0], nil
}

func (p *Processor) messageFetcher() {
	defer p.wg.Done()
	for {
		if p.stopped() {
			break
		}

		_, err := p.fetchMessages()
		if err != nil {
			if err == ErrNotSupported {
				break
			}
			log.Printf("%s ReserveN failed: %s (sleeping for %s)", p.q, err, consumerBackoff)
			time.Sleep(consumerBackoff)
			continue
		}
	}
}

func (p *Processor) fetchMessages() (int, error) {
	msgs, err := p.q.ReserveN(p.opt.BufferSize)
	if err != nil {
		return 0, err
	}
	for i := range msgs {
		p.queueMessage(&msgs[i])
	}
	return len(msgs), nil
}

func (p *Processor) worker() {
	defer p.wg.Done()
	for {
		msg, ok := p.dequeueMessage()
		if !ok {
			break
		}

		if p.opt.RateLimiter != nil {
			for {
				delay, allow := p.opt.RateLimiter.AllowRate(p.q.Name(), p.opt.RateLimit)
				if allow {
					break
				}
				time.Sleep(delay)
			}
		}

		p.Process(msg)
	}
}

func (p *Processor) Process(msg *queue.Message) error {
	if msg.Delay > 0 {
		p.release(msg, nil)
		return nil
	}

	start := time.Now()
	err := p.handler.HandleMessage(msg)
	msg.SetValue("err", err)
	p.updateAvgDuration(time.Since(start))

	if err == nil {
		atomic.AddUint32(&p.processed, 1)
		p.delete(msg, nil)
		return nil
	}

	if msg.ReservedCount < p.opt.RetryLimit {
		atomic.AddUint32(&p.retries, 1)
		p.release(msg, err)
	} else {
		atomic.AddUint32(&p.fails, 1)
		p.delete(msg, err)
	}

	if v := msg.Value("err"); v != nil {
		return v.(error)
	}
	return nil
}

func (p *Processor) Purge() error {
	for {
		select {
		case msg := <-p.ch:
			p.delete(msg, nil)
		default:
			return nil
		}
	}
}

func (p *Processor) queueMessage(msg *queue.Message) {
	atomic.AddUint32(&p.inFlight, 1)
	p.ch <- msg
}

func (p *Processor) dequeueMessage() (*queue.Message, bool) {
	select {
	case msg := <-p.ch:
		return msg, true
	case <-p.stop:
		select {
		case msg := <-p.ch:
			return msg, true
		default:
			return nil, false
		}
	}
}

func (p *Processor) release(msg *queue.Message, reason error) {
	delay := p.backoff(msg, reason)

	if reason != nil {
		log.Printf("%s handler failed (retry in %s): %s", p.q, delay, reason)
	}
	if err := p.q.Release(msg, delay); err != nil {
		log.Printf("%s Release failed: %s", p.q, err)
	}

	atomic.AddUint32(&p.inFlight, ^uint32(0))
}

func (p *Processor) backoff(msg *queue.Message, reason error) time.Duration {
	if reason != nil {
		if delayer, ok := reason.(Delayer); ok {
			return delayer.Delay()
		}
	}
	if msg.Delay > 0 {
		return msg.Delay
	}
	return exponentialBackoff(p.opt.MinBackoff, msg.ReservedCount)
}

func (p *Processor) delete(msg *queue.Message, reason error) {
	if reason != nil {
		log.Printf("%s handler failed: %s", p.q, reason)

		if p.fallbackHandler != nil {
			if err := p.fallbackHandler.HandleMessage(msg); err != nil {
				log.Printf("%s fallback handler failed: %s", p.q, err)
			}
		}
	}

	atomic.AddUint32(&p.inFlight, ^uint32(0))
	atomic.AddUint32(&p.deleting, 1)
	p.delBatch.Add(msg)
}

func (p *Processor) deleteBatch(msgs []*queue.Message) {
	if err := p.q.DeleteBatch(msgs); err != nil {
		log.Printf("%s DeleteBatch failed: %s", p.q, err)
	}
	atomic.AddUint32(&p.deleting, ^uint32(len(msgs)-1))
}

func (p *Processor) updateAvgDuration(dur time.Duration) {
	const decay = float64(1) / 100

	ms := float64(dur / time.Millisecond)
	for {
		avg := atomic.LoadUint32(&p.avgDuration)
		newAvg := uint32((1-decay)*float64(avg) + decay*ms)
		if atomic.CompareAndSwapUint32(&p.avgDuration, avg, newAvg) {
			break
		}
	}
}

func exponentialBackoff(dur time.Duration, retry int) time.Duration {
	dur <<= uint(retry - 1)
	if dur > maxBackoff {
		dur = maxBackoff
	}
	return dur
}
<commit_msg>processor: automatically pause queue if 100 consequent tasks fail.<commit_after>package processor

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

	"gopkg.in/queue.v1"
	"gopkg.in/queue.v1/internal"
)

const consumerBackoff = time.Second
const maxBackoff = 12 * time.Hour
const stopTimeout = 30 * time.Second

var ErrNotSupported = errors.New("processor: not supported")

type Delayer interface {
	Delay() time.Duration
}

type Stats struct {
	InFlight    uint32
	Deleting    uint32
	Processed   uint32
	Retries     uint32
	Fails       uint32
	AvgDuration time.Duration
}

type Processor struct {
	q   Queuer
	opt *queue.Options

	handler         queue.Handler
	fallbackHandler queue.Handler

	ch chan *queue.Message
	wg sync.WaitGroup

	delBatch *internal.Batcher

	_started uint32
	stop     chan struct{}

	errCount   uint32
	delayCount uint32
	delaySec   uint32

	inFlight    uint32
	deleting    uint32
	processed   uint32
	fails       uint32
	retries     uint32
	avgDuration uint32
}

func New(q Queuer, opt *queue.Options) *Processor {
	opt.Init()
	p := &Processor{
		q:   q,
		opt: opt,

		ch: make(chan *queue.Message, opt.BufferSize),
	}

	p.setHandler(opt.Handler)
	if opt.FallbackHandler != nil {
		p.setFallbackHandler(opt.FallbackHandler)
	}

	p.delBatch = internal.NewBatcher(p.opt.ScavengerNumber, p.deleteBatch)

	return p
}

func Start(q Queuer, opt *queue.Options) *Processor {
	p := New(q, opt)
	p.Start()
	return p
}

func (p *Processor) String() string {
	return fmt.Sprintf(
		"Processor<%s workers=%d scavengers=%d buffer=%d>",
		p.q.Name(), p.opt.WorkerNumber, p.opt.ScavengerNumber, p.opt.BufferSize,
	)
}

func (p *Processor) Stats() *Stats {
	return &Stats{
		InFlight:    atomic.LoadUint32(&p.inFlight),
		Deleting:    atomic.LoadUint32(&p.deleting),
		Processed:   atomic.LoadUint32(&p.processed),
		Retries:     atomic.LoadUint32(&p.retries),
		Fails:       atomic.LoadUint32(&p.fails),
		AvgDuration: time.Duration(atomic.LoadUint32(&p.avgDuration)) * time.Millisecond,
	}
}

func (p *Processor) setHandler(handler interface{}) {
	p.handler = queue.NewHandler(handler)
}

func (p *Processor) setFallbackHandler(handler interface{}) {
	p.fallbackHandler = queue.NewHandler(handler)
}

func (p *Processor) Add(msg *queue.Message) error {
	p.queueMessage(msg)
	return nil
}

func (p *Processor) Start() error {
	if !p.startWorkers() {
		return nil
	}

	p.wg.Add(1)
	go p.messageFetcher()

	return nil
}

func (p *Processor) startWorkers() bool {
	if !atomic.CompareAndSwapUint32(&p._started, 0, 1) {
		return false
	}

	p.stop = make(chan struct{})
	p.wg.Add(p.opt.WorkerNumber)
	for i := 0; i < p.opt.WorkerNumber; i++ {
		go p.worker()
	}
	return true
}

func (p *Processor) Stop() error {
	return p.StopTimeout(stopTimeout)
}

func (p *Processor) StopTimeout(timeout time.Duration) error {
	return p.stopWorkersTimeout(timeout)
}

func (p *Processor) stopWorkersTimeout(timeout time.Duration) error {
	if !atomic.CompareAndSwapUint32(&p._started, 1, 0) {
		return nil
	}

	close(p.stop)

	stopped := make(chan struct{})
	go func() {
		p.wg.Wait()
		close(stopped)
	}()

	select {
	case <-time.After(timeout):
		return fmt.Errorf("workers did not stop after %s seconds", timeout)
	case <-stopped:
		return p.delBatch.Wait()
	}
}

func (p *Processor) stopped() bool {
	return atomic.LoadUint32(&p._started) == 0
}

func (p *Processor) paused() time.Duration {
	const threshold = 100

	if atomic.LoadUint32(&p.delayCount) > threshold {
		return time.Duration(atomic.LoadUint32(&p.delaySec)) * time.Second
	}

	if atomic.LoadUint32(&p.errCount) > threshold {
		return time.Minute
	}

	return 0
}

func (p *Processor) ProcessAll() error {
	p.startWorkers()
	var noWork int
	for {
		isIdle := atomic.LoadUint32(&p.inFlight) == 0
		n, err := p.fetchMessages()
		if err != nil {
			if err == ErrNotSupported {
				break
			}
			return err
		}
		if n == 0 && isIdle {
			noWork++
		} else {
			noWork = 0
		}
		if noWork == 2 {
			break
		}
	}
	return p.stopWorkersTimeout(stopTimeout)
}

func (p *Processor) ProcessOne() error {
	msg, err := p.reserveOne()
	if err != nil {
		return err
	}
	retErr := p.Process(msg)
	if err := p.delBatch.Wait(); err != nil && retErr == nil {
		retErr = err
	}
	return retErr
}

func (p *Processor) reserveOne() (*queue.Message, error) {
	select {
	case msg := <-p.ch:
		return msg, nil
	default:
	}

	msgs, err := p.q.ReserveN(1)
	if err != nil && err != ErrNotSupported {
		return nil, err
	}
	if len(msgs) == 0 {
		return nil, errors.New("queue is empty")
	}
	atomic.AddUint32(&p.inFlight, 1)
	return &msgs[0], nil
}

func (p *Processor) messageFetcher() {
	defer p.wg.Done()
	for {
		if p.stopped() {
			break
		}

		if pauseTime := p.paused(); pauseTime > 0 {
			p.resetPause()
			log.Printf("%s is automatically paused for %s", p.q, pauseTime)
			time.Sleep(pauseTime)
			continue
		}

		_, err := p.fetchMessages()
		if err != nil {
			if err == ErrNotSupported {
				break
			}

			log.Printf("%s ReserveN failed: %s (sleeping for %s)", p.q, err, consumerBackoff)
			time.Sleep(consumerBackoff)
			continue
		}
	}
}

func (p *Processor) fetchMessages() (int, error) {
	msgs, err := p.q.ReserveN(p.opt.BufferSize)
	if err != nil {
		return 0, err
	}
	for i := range msgs {
		p.queueMessage(&msgs[i])
	}
	return len(msgs), nil
}

func (p *Processor) worker() {
	defer p.wg.Done()
	for {
		msg, ok := p.dequeueMessage()
		if !ok {
			break
		}

		if p.opt.RateLimiter != nil {
			for {
				delay, allow := p.opt.RateLimiter.AllowRate(p.q.Name(), p.opt.RateLimit)
				if allow {
					break
				}
				time.Sleep(delay)
			}
		}

		p.Process(msg)
	}
}

func (p *Processor) Process(msg *queue.Message) error {
	if msg.Delay > 0 {
		p.release(msg, nil)
		return nil
	}

	start := time.Now()
	err := p.handler.HandleMessage(msg)
	msg.SetValue("err", err)
	p.updateAvgDuration(time.Since(start))

	if err == nil {
		atomic.AddUint32(&p.processed, 1)
		p.delete(msg, nil)
		return nil
	}

	if msg.ReservedCount < p.opt.RetryLimit {
		atomic.AddUint32(&p.retries, 1)
		p.release(msg, err)
	} else {
		atomic.AddUint32(&p.fails, 1)
		p.delete(msg, err)
	}

	if v := msg.Value("err"); v != nil {
		return v.(error)
	}
	return nil
}

func (p *Processor) Purge() error {
	for {
		select {
		case msg := <-p.ch:
			p.delete(msg, nil)
		default:
			return nil
		}
	}
}

func (p *Processor) queueMessage(msg *queue.Message) {
	atomic.AddUint32(&p.inFlight, 1)
	p.ch <- msg
}

func (p *Processor) dequeueMessage() (*queue.Message, bool) {
	select {
	case msg := <-p.ch:
		return msg, true
	case <-p.stop:
		select {
		case msg := <-p.ch:
			return msg, true
		default:
			return nil, false
		}
	}
}

func (p *Processor) release(msg *queue.Message, reason error) {
	delay := p.releaseBackoff(msg, reason)

	if reason != nil {
		log.Printf("%s handler failed (retry in %s): %s", p.q, delay, reason)
	}
	if err := p.q.Release(msg, delay); err != nil {
		log.Printf("%s Release failed: %s", p.q, err)
	}

	atomic.AddUint32(&p.inFlight, ^uint32(0))
}

func (p *Processor) releaseBackoff(msg *queue.Message, reason error) time.Duration {
	if reason != nil {
		if delayer, ok := reason.(Delayer); ok {
			delay := delayer.Delay()
			if delay > time.Minute {
				atomic.StoreUint32(&p.delaySec, uint32(delay/time.Second))
				atomic.AddUint32(&p.delayCount, 1)
			}
			return delay
		}
	}

	if msg.Delay > 0 {
		return msg.Delay
	}

	return exponentialBackoff(p.opt.MinBackoff, msg.ReservedCount)
}

func (p *Processor) delete(msg *queue.Message, reason error) {
	if reason == nil {
		p.resetPause()
	} else {
		log.Printf("%s handler failed: %s", p.q, reason)

		if p.fallbackHandler != nil {
			if err := p.fallbackHandler.HandleMessage(msg); err != nil {
				log.Printf("%s fallback handler failed: %s", p.q, err)
			}
		}
	}

	atomic.AddUint32(&p.inFlight, ^uint32(0))
	atomic.AddUint32(&p.deleting, 1)
	p.delBatch.Add(msg)
}

func (p *Processor) deleteBatch(msgs []*queue.Message) {
	if err := p.q.DeleteBatch(msgs); err != nil {
		log.Printf("%s DeleteBatch failed: %s", p.q, err)
	}
	atomic.AddUint32(&p.deleting, ^uint32(len(msgs)-1))
}

func (p *Processor) updateAvgDuration(dur time.Duration) {
	const decay = float64(1) / 100

	ms := float64(dur / time.Millisecond)
	for {
		avg := atomic.LoadUint32(&p.avgDuration)
		newAvg := uint32((1-decay)*float64(avg) + decay*ms)
		if atomic.CompareAndSwapUint32(&p.avgDuration, avg, newAvg) {
			break
		}
	}
}

func (p *Processor) resetPause() {
	atomic.StoreUint32(&p.errCount, 0)
	atomic.StoreUint32(&p.delayCount, 0)
}

func exponentialBackoff(dur time.Duration, retry int) time.Duration {
	dur <<= uint(retry - 1)
	if dur > maxBackoff {
		dur = maxBackoff
	}
	return dur
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"github.com/BurntSushi/toml"
	docker "github.com/fsouza/go-dockerclient"
)

var (
	buildVersion            string
	version                 bool
	watch                   bool
	notifyCmd               string
	notifySigHUPContainerID string
	onlyExposed             bool
	onlyPublished           bool
	configFile              string
	configs                 ConfigFile
	interval                int
	endpoint                string
	tlsCert                 string
	tlsKey                  string
	tlsCaCert               string
	tlsVerify               bool
	wg                      sync.WaitGroup
)

type Event struct {
	ContainerID string `json:"id"`
	Status      string `json:"status"`
	Image       string `json:"from"`
}

type Address struct {
	IP       string
	Port     string
	HostPort string
	Proto    string
}

type Volume struct {
	Path      string
	HostPath  string
	ReadWrite bool
}

type RuntimeContainer struct {
	ID        string
	Addresses []Address
	Gateway   string
	Name      string
	Image     DockerImage
	Env       map[string]string
	Volumes   map[string]Volume
	Node      SwarmNode
}

type DockerImage struct {
	Registry   string
	Repository string
	Tag        string
}

type SwarmNode struct {
	ID      string
	Name    string
	Address Address
}

func (i *DockerImage) String() string {
	ret := i.Repository
	if i.Registry != "" {
		ret = i.Registry + "/" + i.Repository
	}
	if i.Tag != "" {
		ret = ret + ":" + i.Tag
	}
	return ret
}

type Config struct {
	Template         string
	Dest             string
	Watch            bool
	NotifyCmd        string
	NotifyContainers map[string]docker.Signal
	OnlyExposed      bool
	OnlyPublished    bool
	Interval         int
}

type ConfigFile struct {
	Config []Config
}

type Context []*RuntimeContainer

func (c *Context) Env() map[string]string {
	return splitKeyValueSlice(os.Environ())
}

func (c *ConfigFile) filterWatches() ConfigFile {
	configWithWatches := []Config{}

	for _, config := range c.Config {
		if config.Watch {
			configWithWatches = append(configWithWatches, config)
		}
	}
	return ConfigFile{
		Config: configWithWatches,
	}
}

func (r *RuntimeContainer) Equals(o RuntimeContainer) bool {
	return r.ID == o.ID && r.Image == o.Image
}

func (r *RuntimeContainer) PublishedAddresses() []Address {
	mapped := []Address{}
	for _, address := range r.Addresses {
		if address.HostPort != "" {
			mapped = append(mapped, address)
		}
	}
	return mapped
}

func usage() {
	println("Usage: docker-gen [-config file] [-watch=false] [-notify=\"restart xyz\"] [-notify-sighup=\"container-ID\"] [-interval=0] [-endpoint tcp|unix://..] [-tlscert file] [-tlskey file] [-tlscacert file] [-tlsverify] <template> [<dest>]")
}

func NewDockerClient(endpoint string) (*docker.Client, error) {
	if strings.HasPrefix(endpoint, "unix:") {
		return docker.NewClient(endpoint)
	} else if tlsVerify || tlsCert != "" || tlsKey != "" || tlsCaCert != "" {
		if tlsVerify {
			if tlsCaCert == "" {
				return nil, errors.New("TLS verification was requested, but no -tlscacert was provided")
			}
		}

		return docker.NewTLSClient(endpoint, tlsCert, tlsKey, tlsCaCert)
	}
	return docker.NewClient(endpoint)
}

func generateFromContainers(client *docker.Client) {
	containers, err := getContainers(client)
	if err != nil {
		log.Printf("error listing containers: %s\n", err)
		return
	}
	for _, config := range configs.Config {
		changed := generateFile(config, containers)
		if !changed {
			log.Printf("Contents of %s did not change. Skipping notification '%s'", config.Dest, config.NotifyCmd)
			continue
		}
		runNotifyCmd(config)
		sendSignalToContainer(client, config)
	}
}

func runNotifyCmd(config Config) {
	if config.NotifyCmd == "" {
		return
	}

	log.Printf("Running '%s'", config.NotifyCmd)
	cmd := exec.Command("/bin/sh", "-c", config.NotifyCmd)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Printf("Error running notify command: %s, %s\n", config.NotifyCmd, err)
		log.Print(string(out))
	}
}

func sendSignalToContainer(client *docker.Client, config Config) {
	if len(config.NotifyContainers) < 1 {
		return
	}

	for container, signal := range config.NotifyContainers {
		log.Printf("Sending container '%s' signal '%v'", container, signal)
		killOpts := docker.KillContainerOptions{
			ID:     container,
			Signal: signal,
		}
		if err := client.KillContainer(killOpts); err != nil {
			log.Printf("Error sending signal to container: %s", err)
		}
	}
}

func loadConfig(file string) error {
	_, err := toml.DecodeFile(file, &configs)
	if err != nil {
		return err
	}
	return nil
}

func generateAtInterval(client *docker.Client, configs ConfigFile) {
	for _, config := range configs.Config {

		if config.Interval == 0 {
			continue
		}

		log.Printf("Generating every %d seconds", config.Interval)
		wg.Add(1)
		ticker := time.NewTicker(time.Duration(config.Interval) * time.Second)
		quit := make(chan struct{})
		configCopy := config
		go func() {
			defer wg.Done()
			for {
				select {
				case <-ticker.C:
					containers, err := getContainers(client)
					if err != nil {
						log.Printf("Error listing containers: %s\n", err)
						continue
					}
					// ignore changed return value. always run notify command
					generateFile(configCopy, containers)
					runNotifyCmd(configCopy)
					sendSignalToContainer(client, configCopy)
				case <-quit:
					ticker.Stop()
					return
				}
			}
		}()
	}
}

func generateFromEvents(client *docker.Client, configs ConfigFile) {
	configs = configs.filterWatches()
	if len(configs.Config) == 0 {
		return
	}

	wg.Add(1)
	defer wg.Done()

	for {
		if client == nil {
			var err error
			endpoint, err := getEndpoint()
			if err != nil {
				log.Printf("Bad endpoint: %s", err)
				time.Sleep(10 * time.Second)
				continue
			}

			client, err = NewDockerClient(endpoint)
			if err != nil {
				log.Printf("Unable to connect to docker daemon: %s", err)
				time.Sleep(10 * time.Second)
				continue
			}
			generateFromContainers(client)
		}

		eventChan := make(chan *docker.APIEvents, 100)
		defer close(eventChan)

		watching := false
		for {

			if client == nil {
				break
			}
			err := client.Ping()
			if err != nil {
				log.Printf("Unable to ping docker daemon: %s", err)
				if watching {
					client.RemoveEventListener(eventChan)
					watching = false
					client = nil
				}
				time.Sleep(10 * time.Second)
				break

			}

			if !watching {
				err = client.AddEventListener(eventChan)
				if err != nil && err != docker.ErrListenerAlreadyExists {
					log.Printf("Error registering docker event listener: %s", err)
					time.Sleep(10 * time.Second)
					continue
				}
				watching = true
				log.Println("Watching docker events")
			}

			select {

			case event := <-eventChan:
				if event == nil {
					if watching {
						client.RemoveEventListener(eventChan)
						watching = false
						client = nil
					}
					break
				}

				if event.Status == "start" || event.Status == "stop" || event.Status == "die" {
					log.Printf("Received event %s for container %s", event.Status, event.ID[:12])
					generateFromContainers(client)
				}
			case <-time.After(10 * time.Second):
				// check for docker liveness
			}

		}
	}
}

func initFlags() {
	flag.BoolVar(&version, "version", false, "show version")
	flag.BoolVar(&watch, "watch", false, "watch for container changes")
	flag.BoolVar(&onlyExposed, "only-exposed", false, "only include containers with exposed ports")
	flag.BoolVar(&onlyPublished, "only-published", false, "only include containers with published ports (implies -only-exposed)")
	flag.StringVar(&notifyCmd, "notify", "", "run command after template is regenerated")
	flag.StringVar(&notifySigHUPContainerID, "notify-sighup", "", "send HUP signal to container.  Equivalent to `docker kill -s HUP container-ID`")
	flag.StringVar(&configFile, "config", "", "config file with template directives")
	flag.IntVar(&interval, "interval", 0, "notify command interval (s)")
	flag.StringVar(&endpoint, "endpoint", "", "docker api endpoint")
	flag.StringVar(&tlsCert, "tlscert", "", "path to TLS client certificate file")
	flag.StringVar(&tlsKey, "tlskey", "", "path to TLS client key file")
	flag.StringVar(&tlsCaCert, "tlscacert", "", "path to TLS CA certificate file")
	flag.BoolVar(&tlsVerify, "tlsverify", false, "verify docker daemon's TLS certicate")
	flag.Parse()
}

func main() {
	initFlags()

	if version {
		fmt.Println(buildVersion)
		return
	}

	if flag.NArg() < 1 && configFile == "" {
		usage()
		os.Exit(1)
	}

	if configFile != "" {
		err := loadConfig(configFile)
		if err != nil {
			log.Fatalf("error loading config %s: %s\n", configFile, err)
		}
	} else {
		config := Config{
			Template:         flag.Arg(0),
			Dest:             flag.Arg(1),
			Watch:            watch,
			NotifyCmd:        notifyCmd,
			NotifyContainers: make(map[string]docker.Signal),
			OnlyExposed:      onlyExposed,
			OnlyPublished:    onlyPublished,
			Interval:         interval,
		}
		if notifySigHUPContainerID != "" {
			config.NotifyContainers[notifySigHUPContainerID] = docker.SIGHUP
		}
		configs = ConfigFile{
			Config: []Config{config}}
	}

	endpoint, err := getEndpoint()
	if err != nil {
		log.Fatalf("Bad endpoint: %s", err)
	}

	client, err := NewDockerClient(endpoint)
	if err != nil {
		log.Fatalf("Unable to create docker client: %s", err)
	}

	generateFromContainers(client)
	generateAtInterval(client, configs)
	generateFromEvents(client, configs)
	wg.Wait()
}
<commit_msg>Support passing -config multiple times<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"log"
	"os"
	"os/exec"
	"strings"
	"sync"
	"time"

	"github.com/BurntSushi/toml"
	docker "github.com/fsouza/go-dockerclient"
)

type stringslice []string

var (
	buildVersion            string
	version                 bool
	watch                   bool
	notifyCmd               string
	notifySigHUPContainerID string
	onlyExposed             bool
	onlyPublished           bool
	configFiles             stringslice
	configs                 ConfigFile
	interval                int
	endpoint                string
	tlsCert                 string
	tlsKey                  string
	tlsCaCert               string
	tlsVerify               bool
	wg                      sync.WaitGroup
)

type Event struct {
	ContainerID string `json:"id"`
	Status      string `json:"status"`
	Image       string `json:"from"`
}

type Address struct {
	IP       string
	Port     string
	HostPort string
	Proto    string
}

type Volume struct {
	Path      string
	HostPath  string
	ReadWrite bool
}

type RuntimeContainer struct {
	ID        string
	Addresses []Address
	Gateway   string
	Name      string
	Image     DockerImage
	Env       map[string]string
	Volumes   map[string]Volume
	Node      SwarmNode
}

type DockerImage struct {
	Registry   string
	Repository string
	Tag        string
}

type SwarmNode struct {
	ID      string
	Name    string
	Address Address
}

func (strings *stringslice) String() string {
	return "[]"
}

func (strings *stringslice) Set(value string) error {
	// TODO: Throw an error for duplicate `dest`
	*strings = append(*strings, value)
	return nil
}

func (i *DockerImage) String() string {
	ret := i.Repository
	if i.Registry != "" {
		ret = i.Registry + "/" + i.Repository
	}
	if i.Tag != "" {
		ret = ret + ":" + i.Tag
	}
	return ret
}

type Config struct {
	Template         string
	Dest             string
	Watch            bool
	NotifyCmd        string
	NotifyContainers map[string]docker.Signal
	OnlyExposed      bool
	OnlyPublished    bool
	Interval         int
}

type ConfigFile struct {
	Config []Config
}

type Context []*RuntimeContainer

func (c *Context) Env() map[string]string {
	return splitKeyValueSlice(os.Environ())
}

func (c *ConfigFile) filterWatches() ConfigFile {
	configWithWatches := []Config{}

	for _, config := range c.Config {
		if config.Watch {
			configWithWatches = append(configWithWatches, config)
		}
	}
	return ConfigFile{
		Config: configWithWatches,
	}
}

func (r *RuntimeContainer) Equals(o RuntimeContainer) bool {
	return r.ID == o.ID && r.Image == o.Image
}

func (r *RuntimeContainer) PublishedAddresses() []Address {
	mapped := []Address{}
	for _, address := range r.Addresses {
		if address.HostPort != "" {
			mapped = append(mapped, address)
		}
	}
	return mapped
}

func usage() {
	println("Usage: docker-gen [-config file] [-watch=false] [-notify=\"restart xyz\"] [-notify-sighup=\"container-ID\"] [-interval=0] [-endpoint tcp|unix://..] [-tlscert file] [-tlskey file] [-tlscacert file] [-tlsverify] <template> [<dest>]")
}

func NewDockerClient(endpoint string) (*docker.Client, error) {
	if strings.HasPrefix(endpoint, "unix:") {
		return docker.NewClient(endpoint)
	} else if tlsVerify || tlsCert != "" || tlsKey != "" || tlsCaCert != "" {
		if tlsVerify {
			if tlsCaCert == "" {
				return nil, errors.New("TLS verification was requested, but no -tlscacert was provided")
			}
		}

		return docker.NewTLSClient(endpoint, tlsCert, tlsKey, tlsCaCert)
	}
	return docker.NewClient(endpoint)
}

func generateFromContainers(client *docker.Client) {
	containers, err := getContainers(client)
	if err != nil {
		log.Printf("error listing containers: %s\n", err)
		return
	}
	for _, config := range configs.Config {
		changed := generateFile(config, containers)
		if !changed {
			log.Printf("Contents of %s did not change. Skipping notification '%s'", config.Dest, config.NotifyCmd)
			continue
		}
		runNotifyCmd(config)
		sendSignalToContainer(client, config)
	}
}

func runNotifyCmd(config Config) {
	if config.NotifyCmd == "" {
		return
	}

	log.Printf("Running '%s'", config.NotifyCmd)
	cmd := exec.Command("/bin/sh", "-c", config.NotifyCmd)
	out, err := cmd.CombinedOutput()
	if err != nil {
		log.Printf("Error running notify command: %s, %s\n", config.NotifyCmd, err)
		log.Print(string(out))
	}
}

func sendSignalToContainer(client *docker.Client, config Config) {
	if len(config.NotifyContainers) < 1 {
		return
	}

	for container, signal := range config.NotifyContainers {
		log.Printf("Sending container '%s' signal '%v'", container, signal)
		killOpts := docker.KillContainerOptions{
			ID:     container,
			Signal: signal,
		}
		if err := client.KillContainer(killOpts); err != nil {
			log.Printf("Error sending signal to container: %s", err)
		}
	}
}

func loadConfig(file string) error {
	_, err := toml.DecodeFile(file, &configs)
	if err != nil {
		return err
	}
	return nil
}

func generateAtInterval(client *docker.Client, configs ConfigFile) {
	for _, config := range configs.Config {

		if config.Interval == 0 {
			continue
		}

		log.Printf("Generating every %d seconds", config.Interval)
		wg.Add(1)
		ticker := time.NewTicker(time.Duration(config.Interval) * time.Second)
		quit := make(chan struct{})
		configCopy := config
		go func() {
			defer wg.Done()
			for {
				select {
				case <-ticker.C:
					containers, err := getContainers(client)
					if err != nil {
						log.Printf("Error listing containers: %s\n", err)
						continue
					}
					// ignore changed return value. always run notify command
					generateFile(configCopy, containers)
					runNotifyCmd(configCopy)
					sendSignalToContainer(client, configCopy)
				case <-quit:
					ticker.Stop()
					return
				}
			}
		}()
	}
}

func generateFromEvents(client *docker.Client, configs ConfigFile) {
	configs = configs.filterWatches()
	if len(configs.Config) == 0 {
		return
	}

	wg.Add(1)
	defer wg.Done()

	for {
		if client == nil {
			var err error
			endpoint, err := getEndpoint()
			if err != nil {
				log.Printf("Bad endpoint: %s", err)
				time.Sleep(10 * time.Second)
				continue
			}

			client, err = NewDockerClient(endpoint)
			if err != nil {
				log.Printf("Unable to connect to docker daemon: %s", err)
				time.Sleep(10 * time.Second)
				continue
			}
			generateFromContainers(client)
		}

		eventChan := make(chan *docker.APIEvents, 100)
		defer close(eventChan)

		watching := false
		for {

			if client == nil {
				break
			}
			err := client.Ping()
			if err != nil {
				log.Printf("Unable to ping docker daemon: %s", err)
				if watching {
					client.RemoveEventListener(eventChan)
					watching = false
					client = nil
				}
				time.Sleep(10 * time.Second)
				break

			}

			if !watching {
				err = client.AddEventListener(eventChan)
				if err != nil && err != docker.ErrListenerAlreadyExists {
					log.Printf("Error registering docker event listener: %s", err)
					time.Sleep(10 * time.Second)
					continue
				}
				watching = true
				log.Println("Watching docker events")
			}

			select {

			case event := <-eventChan:
				if event == nil {
					if watching {
						client.RemoveEventListener(eventChan)
						watching = false
						client = nil
					}
					break
				}

				if event.Status == "start" || event.Status == "stop" || event.Status == "die" {
					log.Printf("Received event %s for container %s", event.Status, event.ID[:12])
					generateFromContainers(client)
				}
			case <-time.After(10 * time.Second):
				// check for docker liveness
			}

		}
	}
}

func initFlags() {
	flag.BoolVar(&version, "version", false, "show version")
	flag.BoolVar(&watch, "watch", false, "watch for container changes")
	flag.BoolVar(&onlyExposed, "only-exposed", false, "only include containers with exposed ports")
	flag.BoolVar(&onlyPublished, "only-published", false, "only include containers with published ports (implies -only-exposed)")
	flag.StringVar(&notifyCmd, "notify", "", "run command after template is regenerated")
	flag.StringVar(&notifySigHUPContainerID, "notify-sighup", "", "send HUP signal to container.  Equivalent to `docker kill -s HUP container-ID`")
	flag.Var(&configFiles, "config", "config files with template directives. Config files will be merged if this option is specified multiple times.")
	flag.IntVar(&interval, "interval", 0, "notify command interval (s)")
	flag.StringVar(&endpoint, "endpoint", "", "docker api endpoint")
	flag.StringVar(&tlsCert, "tlscert", "", "path to TLS client certificate file")
	flag.StringVar(&tlsKey, "tlskey", "", "path to TLS client key file")
	flag.StringVar(&tlsCaCert, "tlscacert", "", "path to TLS CA certificate file")
	flag.BoolVar(&tlsVerify, "tlsverify", false, "verify docker daemon's TLS certicate")
	flag.Parse()
}

func main() {
	initFlags()

	if version {
		fmt.Println(buildVersion)
		return
	}

	if flag.NArg() < 1 && len(configFiles) == 0 {
		usage()
		os.Exit(1)
	}

	if len(configFiles) > 0 {
		for _, configFile := range configFiles {
			err := loadConfig(configFile)
			if err != nil {
				log.Fatalf("error loading config %s: %s\n", configFile, err)
			}
		}
	} else {
		config := Config{
			Template:         flag.Arg(0),
			Dest:             flag.Arg(1),
			Watch:            watch,
			NotifyCmd:        notifyCmd,
			NotifyContainers: make(map[string]docker.Signal),
			OnlyExposed:      onlyExposed,
			OnlyPublished:    onlyPublished,
			Interval:         interval,
		}
		if notifySigHUPContainerID != "" {
			config.NotifyContainers[notifySigHUPContainerID] = docker.SIGHUP
		}
		configs = ConfigFile{
			Config: []Config{config}}
	}

	endpoint, err := getEndpoint()
	if err != nil {
		log.Fatalf("Bad endpoint: %s", err)
	}

	client, err := NewDockerClient(endpoint)
	if err != nil {
		log.Fatalf("Unable to create docker client: %s", err)
	}

	generateFromContainers(client)
	generateAtInterval(client, configs)
	generateFromEvents(client, configs)
	wg.Wait()
}
<|endoftext|>


<commit_before>package bild

import (
	"image"
	"image/color"
)

type normColor struct {
	r, g, b, a float64
}

// Add returns an image with the added color values of two images
func Add(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R)
		g0 := float64(c0.G)
		b0 := float64(c0.B)
		a0 := float64(c0.A)

		r1 := float64(c1.R)
		g1 := float64(c1.G)
		b1 := float64(c1.B)
		a1 := float64(c1.A)

		r2 := uint8(clampFloat64(r0+r1, 0, 255))
		g2 := uint8(clampFloat64(g0+g1, 0, 255))
		b2 := uint8(clampFloat64(b0+b1, 0, 255))
		a2 := uint8(clampFloat64(a0+a1, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Multiply returns an image with the normalized color values of two images multiplied
func Multiply(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R)
		g0 := float64(c0.G)
		b0 := float64(c0.B)
		a0 := float64(c0.A)

		r1 := float64(c1.R)
		g1 := float64(c1.G)
		b1 := float64(c1.B)
		a1 := float64(c1.A)

		r2 := uint8(r0 * r1 / 255)
		g2 := uint8(g0 * g1 / 255)
		b2 := uint8(b0 * b1 / 255)
		a2 := uint8(a0 * a1 / 255)

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Overlay returns an image that combines Multiply and Screen blend modes
func Overlay(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		var r2, g2, b2, a2 uint8
		if 0.3*r1+0.6*g1+0.1*b1 < 0.5 {
			r2 = uint8(clampFloat64(r0*r1*2*255, 0, 255))
			g2 = uint8(clampFloat64(g0*g1*2*255, 0, 255))
			b2 = uint8(clampFloat64(b0*b1*2*255, 0, 255))
			a2 = uint8(clampFloat64(a0*a1*2*255, 0, 255))
		} else {
			r2 = uint8(clampFloat64((1-2*(1-r0)*(1-r1))*255, 0, 255))
			g2 = uint8(clampFloat64((1-2*(1-g0)*(1-g1))*255, 0, 255))
			b2 = uint8(clampFloat64((1-2*(1-b0)*(1-b1))*255, 0, 255))
			a2 = uint8(clampFloat64((1-2*(1-a0)*(1-a1))*255, 0, 255))
		}

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// SoftLight returns an image has the Soft Light blend mode applied
func SoftLight(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		r2 := uint8(clampFloat64(((1-2*r1)*r0*r0+2*r0*r1)*255, 0, 255))
		g2 := uint8(clampFloat64(((1-2*g1)*g0*g0+2*g0*g1)*255, 0, 255))
		b2 := uint8(clampFloat64(((1-2*b1)*b0*b0+2*b0*b1)*255, 0, 255))
		a2 := uint8(clampFloat64(((1-2*a1)*a0*a0+2*a0*a1)*255, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Screen returns an image that has the screen blend mode applied
func Screen(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		r2 := uint8(clampFloat64((1-(1-r0)*(1-r1))*255, 0, 255))
		g2 := uint8(clampFloat64((1-(1-g0)*(1-g1))*255, 0, 255))
		b2 := uint8(clampFloat64((1-(1-b0)*(1-b1))*255, 0, 255))
		a2 := uint8(clampFloat64((1-(1-a0)*(1-a1))*255, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

func blendOperation(a image.Image, b image.Image, fn func(color.RGBA, color.RGBA) color.RGBA) *image.RGBA {
	// Currently only equal size images are supported
	if a.Bounds() != b.Bounds() {
		panic("blend operation: only equal size images are supported")
	}

	bounds := a.Bounds()
	srcA := CloneAsRGBA(a)
	srcB := CloneAsRGBA(b)

	dst := image.NewRGBA(bounds)

	w, h := bounds.Max.X, bounds.Max.Y

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {
				pos := y*dst.Stride + x*4
				result := fn(color.RGBA{srcA.Pix[pos+0], srcA.Pix[pos+1], srcA.Pix[pos+2], srcA.Pix[pos+3]},
					color.RGBA{srcB.Pix[pos+0], srcB.Pix[pos+1], srcB.Pix[pos+2], srcB.Pix[pos+3]})

				dst.Pix[pos+0] = result.R
				dst.Pix[pos+1] = result.G
				dst.Pix[pos+2] = result.B
				dst.Pix[pos+3] = result.A
			}

		}
	})

	return dst
}
<commit_msg>added difference and opacity blend operations<commit_after>package bild

import (
	"image"
	"image/color"
	"math"
)

type normColor struct {
	r, g, b, a float64
}

// Add returns an image with the added color values of two images
func Add(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R)
		g0 := float64(c0.G)
		b0 := float64(c0.B)
		a0 := float64(c0.A)

		r1 := float64(c1.R)
		g1 := float64(c1.G)
		b1 := float64(c1.B)
		a1 := float64(c1.A)

		r2 := uint8(clampFloat64(r0+r1, 0, 255))
		g2 := uint8(clampFloat64(g0+g1, 0, 255))
		b2 := uint8(clampFloat64(b0+b1, 0, 255))
		a2 := uint8(clampFloat64(a0+a1, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Multiply returns an image with the normalized color values of two images multiplied
func Multiply(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R)
		g0 := float64(c0.G)
		b0 := float64(c0.B)
		a0 := float64(c0.A)

		r1 := float64(c1.R)
		g1 := float64(c1.G)
		b1 := float64(c1.B)
		a1 := float64(c1.A)

		r2 := uint8(r0 * r1 / 255)
		g2 := uint8(g0 * g1 / 255)
		b2 := uint8(b0 * b1 / 255)
		a2 := uint8(a0 * a1 / 255)

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Overlay returns an image that combines Multiply and Screen blend modes
func Overlay(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		var r2, g2, b2, a2 uint8
		if 0.3*r1+0.6*g1+0.1*b1 < 0.5 {
			r2 = uint8(clampFloat64(r0*r1*2*255, 0, 255))
			g2 = uint8(clampFloat64(g0*g1*2*255, 0, 255))
			b2 = uint8(clampFloat64(b0*b1*2*255, 0, 255))
			a2 = uint8(clampFloat64(a0*a1*2*255, 0, 255))
		} else {
			r2 = uint8(clampFloat64((1-2*(1-r0)*(1-r1))*255, 0, 255))
			g2 = uint8(clampFloat64((1-2*(1-g0)*(1-g1))*255, 0, 255))
			b2 = uint8(clampFloat64((1-2*(1-b0)*(1-b1))*255, 0, 255))
			a2 = uint8(clampFloat64((1-2*(1-a0)*(1-a1))*255, 0, 255))
		}

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// SoftLight returns an image has the Soft Light blend mode applied
func SoftLight(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		r2 := uint8(clampFloat64(((1-2*r1)*r0*r0+2*r0*r1)*255, 0, 255))
		g2 := uint8(clampFloat64(((1-2*g1)*g0*g0+2*g0*g1)*255, 0, 255))
		b2 := uint8(clampFloat64(((1-2*b1)*b0*b0+2*b0*b1)*255, 0, 255))
		a2 := uint8(clampFloat64(((1-2*a1)*a0*a0+2*a0*a1)*255, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Screen returns an image that has the screen blend mode applied
func Screen(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		r2 := uint8(clampFloat64((1-(1-r0)*(1-r1))*255, 0, 255))
		g2 := uint8(clampFloat64((1-(1-g0)*(1-g1))*255, 0, 255))
		b2 := uint8(clampFloat64((1-(1-b0)*(1-b1))*255, 0, 255))
		a2 := uint8(clampFloat64((1-(1-a0)*(1-a1))*255, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Difference returns an image which represts the absolute difference between the input images
func Difference(a image.Image, b image.Image) *image.RGBA {
	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		r2 := uint8(clampFloat64(math.Abs(r0-r1)*255, 0, 255))
		g2 := uint8(clampFloat64(math.Abs(g0-g1)*255, 0, 255))
		b2 := uint8(clampFloat64(math.Abs(b0-b1)*255, 0, 255))
		a2 := uint8(clampFloat64(math.Abs(a0-a1)*255, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

// Opacity returns an image which blends the two input images by the percentage provided.
// Percent must be of range 0 <= percent <= 1.0
func Opacity(a image.Image, b image.Image, percent float64) *image.RGBA {
	percent = clampFloat64(percent, 0, 1.0)

	dst := blendOperation(a, b, func(c0 color.RGBA, c1 color.RGBA) color.RGBA {

		r0 := float64(c0.R) / 255
		g0 := float64(c0.G) / 255
		b0 := float64(c0.B) / 255
		a0 := float64(c0.A) / 255

		r1 := float64(c1.R) / 255
		g1 := float64(c1.G) / 255
		b1 := float64(c1.B) / 255
		a1 := float64(c1.A) / 255

		r2 := uint8(clampFloat64((percent*r1+(1-percent)*r0)*255, 0, 255))
		g2 := uint8(clampFloat64((percent*g1+(1-percent)*g0)*255, 0, 255))
		b2 := uint8(clampFloat64((percent*b1+(1-percent)*b0)*255, 0, 255))
		a2 := uint8(clampFloat64((percent*a1+(1-percent)*a0)*255, 0, 255))

		return color.RGBA{r2, g2, b2, a2}
	})

	return dst
}

func blendOperation(a image.Image, b image.Image, fn func(color.RGBA, color.RGBA) color.RGBA) *image.RGBA {
	// Currently only equal size images are supported
	if a.Bounds() != b.Bounds() {
		panic("blend operation: only equal size images are supported")
	}

	bounds := a.Bounds()
	srcA := CloneAsRGBA(a)
	srcB := CloneAsRGBA(b)

	dst := image.NewRGBA(bounds)

	w, h := bounds.Max.X, bounds.Max.Y

	parallelize(h, func(start, end int) {
		for x := 0; x < w; x++ {
			for y := start; y < end; y++ {
				pos := y*dst.Stride + x*4
				result := fn(color.RGBA{srcA.Pix[pos+0], srcA.Pix[pos+1], srcA.Pix[pos+2], srcA.Pix[pos+3]},
					color.RGBA{srcB.Pix[pos+0], srcB.Pix[pos+1], srcB.Pix[pos+2], srcB.Pix[pos+3]})

				dst.Pix[pos+0] = result.R
				dst.Pix[pos+1] = result.G
				dst.Pix[pos+2] = result.B
				dst.Pix[pos+3] = result.A
			}

		}
	})

	return dst
}
<|endoftext|>


<commit_before>package schedule

import (
	"testing"
	"time"
)

func TestIsLeapYear(t *testing.T) {
	var tests = []struct {
		date     time.Time
		expected bool
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC), false},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC), false},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC), true},
		{time.Date(2012, time.February, 29, 0, 0, 0, 0, time.UTC), true},
	}
	for _, test := range tests {
		actual := isLeapDay(test.date)
		if actual != test.expected {
			fmt := "isLeapYear(%v) == %b; but wanted %b"
			t.Errorf(fmt, test.date, actual, test.expected)
		}
	}
}

func TestGetDateLastYear(t *testing.T) {
	var tests = []struct {
		date     time.Time
		expected time.Time
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC),
			time.Date(2008, time.November, 10, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC),
			time.Date(2015, time.February, 28, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC),
			time.Date(2015, time.February, 28, 0, 0, 0, 0, time.UTC)},
		{time.Date(2012, time.February, 29, 0, 0, 0, 0, time.UTC),
			time.Date(2011, time.February, 28, 0, 0, 0, 0, time.UTC)},
	}
	for _, test := range tests {
		actual := getDayMinusOneYear(test.date)
		if actual != test.expected {
			fmt := "getDayLastYear(%v) == %v; but wanted %v"
			t.Errorf(fmt, test.date, actual, test.expected)
		}
	}
}

func TestGetRandomNumber(t *testing.T) {
	var tests = []struct {
		min int
		max int
	}{
		{0, 0},
		{0, 1},
		{1, 1},
		{0, 10},
		{10, 20},
	}
	for _, test := range tests {
		actual := getRandomNumber(test.min, test.max)
		if test.min > actual || actual > test.max {
			fmt := "getRandomNumber(%d, %d) == %d; not min <= actual <= max"
			t.Errorf(fmt, test.min, test.max, actual)
		}
	}
}

func TestGetDaysSinceDateMinusOneYear(t *testing.T) {
	var tests = []struct {
		date           time.Time
		expectedLength int
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC), 365},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC), 365},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC), 366},
		{time.Date(2012, time.February, 29, 0, 0, 0, 0, time.UTC), 366},
	}
	for _, test := range tests {
		// TODO how to get the size of a channel
		actual := getDaysSinceDateMinusOneYear(test.date)
		if len(actual) == test.expectedLength {
			fmt := "getDaysSinceDateMinusOneYear(%v) == %d; but wanted %d"
			t.Errorf(fmt, test.date, len(actual), test.expectedLength)
		}
	}
}
<commit_msg>Make getDays test succeed.<commit_after>package schedule

import (
	"testing"
	"time"
)

func TestIsLeapYear(t *testing.T) {
	var tests = []struct {
		date     time.Time
		expected bool
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC), false},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC), false},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC), true},
		{time.Date(2012, time.February, 29, 0, 0, 0, 0, time.UTC), true},
	}
	for _, test := range tests {
		actual := isLeapDay(test.date)
		if actual != test.expected {
			fmt := "isLeapYear(%v) == %b; but wanted %b"
			t.Errorf(fmt, test.date, actual, test.expected)
		}
	}
}

func TestGetDateLastYear(t *testing.T) {
	var tests = []struct {
		date     time.Time
		expected time.Time
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC),
			time.Date(2008, time.November, 10, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC),
			time.Date(2015, time.February, 28, 0, 0, 0, 0, time.UTC)},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC),
			time.Date(2015, time.February, 28, 0, 0, 0, 0, time.UTC)},
		{time.Date(2012, time.February, 29, 0, 0, 0, 0, time.UTC),
			time.Date(2011, time.February, 28, 0, 0, 0, 0, time.UTC)},
	}
	for _, test := range tests {
		actual := getDayMinusOneYear(test.date)
		if actual != test.expected {
			fmt := "getDayLastYear(%v) == %v; but wanted %v"
			t.Errorf(fmt, test.date, actual, test.expected)
		}
	}
}

func TestGetRandomNumber(t *testing.T) {
	var tests = []struct {
		min int
		max int
	}{
		{0, 0},
		{0, 1},
		{1, 1},
		{0, 10},
		{10, 20},
	}
	for _, test := range tests {
		actual := getRandomNumber(test.min, test.max)
		if test.min > actual || actual > test.max {
			fmt := "getRandomNumber(%d, %d) == %d; not min <= actual <= max"
			t.Errorf(fmt, test.min, test.max, actual)
		}
	}
}

func TestGetDaysSinceDateMinusOneYear(t *testing.T) {
	var tests = []struct {
		date           time.Time
		expectedLength int
	}{
		{time.Date(2009, time.November, 10, 0, 0, 0, 0, time.UTC), 366},
		{time.Date(2016, time.February, 28, 0, 0, 0, 0, time.UTC), 366},
		{time.Date(2016, time.February, 29, 0, 0, 0, 0, time.UTC), 367},
		{time.Date(2013, time.February, 28, 0, 0, 0, 0, time.UTC), 367},
	}
	for _, test := range tests {
		actual := getDaysSinceDateMinusOneYear(test.date)
		length := 0
		for day := range actual {
			// just logging so day is used here, otherwise it wouldn't compile
			t.Log(day.String())
			length++
		}
		if length != test.expectedLength {
			fmt := "len(getDaysSinceDateMinusOneYear(%v)) == %d; but wanted %d"
			t.Errorf(fmt, test.date, length, test.expectedLength)
		}
	}
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"os/signal"
	"syscall"

	"github.com/coreos/go-iptables/iptables"
	"github.com/spf13/cobra"
	coreapi "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"

	"github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/net"
	"github.com/weaveworks/weave/net/ipset"
	"github.com/weaveworks/weave/npc"
	"github.com/weaveworks/weave/npc/metrics"
	"github.com/weaveworks/weave/npc/ulogd"
)

var (
	version     = "unreleased"
	metricsAddr string
	logLevel    string
	allowMcast  bool
	nodeName    string
	maxList     int
)

func handleError(err error) { common.CheckFatal(err) }

func makeController(getter cache.Getter, resource string,
	objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) cache.Controller {
	listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
	_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
	return controller
}

func resetIPTables(ipt *iptables.IPTables) error {
	// Flush chains first so there are no refs to extant ipsets
	if err := ipt.ClearChain(npc.TableFilter, npc.IngressChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.DefaultChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.EgressMarkChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.EgressCustomChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.EgressDefaultChain); err != nil {
		return err
	}

	// We do not clear npc.EgressChain here because otherwise, in the case of restarting
	// weave-npc process, all egress traffic is allowed for a short period of time.
	// The chain is created in createBaseRules.

	return nil
}

func resetIPSets(ips ipset.Interface) error {
	// Remove ipsets prefixed `weave-` only.

	sets, err := ips.List(npc.IpsetNamePrefix)
	if err != nil {
		common.Log.Errorf("Failed to retrieve list of ipsets")
		return err
	}

	common.Log.Debugf("Got list of ipsets: %v", sets)

	// Must remove references to ipsets by other ipsets before they're destroyed
	for _, s := range sets {
		common.Log.Debugf("Flushing ipset '%s'", string(s))
		if err := ips.Flush(s); err != nil {
			common.Log.Errorf("Failed to flush ipset '%s'", string(s))
			return err
		}
	}

	for _, s := range sets {
		// LocalIPset might be used by WEAVE-NPC-EGRESS chain which we do not
		// flush, so we cannot destroy it.
		if s == npc.LocalIpset {
			continue
		}
		common.Log.Debugf("Destroying ipset '%s'", string(s))
		if err := ips.Destroy(s); err != nil {
			common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
			return err
		}
	}

	return nil
}

func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
	// Configure main chain static rules
	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
		return err
	}

	if allowMcast {
		if err := ipt.Append(npc.TableFilter, npc.MainChain,
			"-d", "224.0.0.0/4", "-j", "ACCEPT"); err != nil {
			return err
		}
	}

	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "state", "--state", "NEW", "-j", string(npc.DefaultChain)); err != nil {
		return err
	}

	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "state", "--state", "NEW", "-j", string(npc.IngressChain)); err != nil {
		return err
	}

	// If the destination address is not any of the local pods, let it through
	found, err := ipsetExist(ips, npc.LocalIpset)
	if err != nil {
		return err
	}
	if !found {
		if err := ips.Create(npc.LocalIpset, ipset.HashIP); err != nil {
			return err
		}
	}
	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "set", "!", "--match-set", npc.LocalIpset, "dst", "-j", "ACCEPT"); err != nil {
		return err
	}

	if err := ipt.Append(npc.TableFilter, npc.EgressMarkChain,
		"-j", "MARK", "--set-xmark", npc.EgressMark); err != nil {
		return err
	}

	// Egress rules:
	//
	// -A WEAVE-NPC-EGRESS -m state --state RELATED,ESTABLISHED -j ACCEPT
	// -A WEAVE-NPC-EGRESS -m set ! --match-set weave-local-pods src -j RETURN
	// -A WEAVE-NPC-EGRESS -d 224.0.0.0/4 -j RETURN
	// -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
	// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
	// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j NFLOG --nflog-group 86
	// -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
	//
	// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j MARK --set-xmark 0x40000/0x40000
	// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j RETURN
	//
	// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j MARK --set-xmark 0x40000/0x40000
	// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j RETURN
	//
	// For each rule we create two (mark and return). We cannot just accept
	// a packet if it matches any rule, as a packet might need to traverse
	// the ingress npc as well which happens later in the chain (in some cases
	// we cannot detect whether packet is ingress or egress, so we need to
	// check both chains).

	ruleSpecs := [][]string{
		{"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
		{"-m", "state", "--state", "NEW", "-m", "set", "!", "--match-set", npc.LocalIpset, "src", "-j", "RETURN"},
	}
	if allowMcast {
		ruleSpecs = append(ruleSpecs, []string{"-d", "224.0.0.0/4", "-j", "RETURN"})
	}
	ruleSpecs = append(ruleSpecs, [][]string{
		{"-m", "state", "--state", "NEW", "-j", string(npc.EgressDefaultChain)},
		{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", string(npc.EgressCustomChain)},
		{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", "NFLOG", "--nflog-group", "86"},
		{"-m", "mark", "!", "--mark", npc.EgressMark, "-j", "DROP"},
	}...)
	if err := net.AddChainWithRules(ipt, npc.TableFilter, npc.EgressChain, ruleSpecs); err != nil {
		return err
	}

	return nil
}

// Dummy way to check whether a given ipset exists.
// TODO(brb) Use "ipset -exist create <..>" for our purpose instead (for some reasons
// creating an ipset with -exist fails).
func ipsetExist(ips ipset.Interface, name ipset.Name) (bool, error) {
	sets, err := ips.List(string(name))
	if err != nil {
		return false, err
	}
	for _, s := range sets {
		if s == name {
			return true, nil
		}
	}
	return false, nil
}

func root(cmd *cobra.Command, args []string) {
	var npController cache.Controller

	common.SetLogLevel(logLevel)
	if nodeName == "" {
		// HOSTNAME is set by Kubernetes for pods in the host network namespace
		nodeName = os.Getenv("HOSTNAME")
	}
	if nodeName == "" {
		common.Log.Fatalf("Must set node name via --node-name or $HOSTNAME")
	}
	common.Log.Infof("Starting Weaveworks NPC %s; node name %q", version, nodeName)

	if err := metrics.Start(metricsAddr); err != nil {
		common.Log.Fatalf("Failed to start metrics: %v", err)
	}

	if err := ulogd.Start(); err != nil {
		common.Log.Fatalf("Failed to start ulogd: %v", err)
	}

	config, err := rest.InClusterConfig()
	handleError(err)

	client, err := kubernetes.NewForConfig(config)
	handleError(err)

	ipt, err := iptables.New()
	handleError(err)

	ips := ipset.New(common.LogLogger(), maxList)

	handleError(resetIPTables(ipt))
	handleError(resetIPSets(ips))
	handleError(createBaseRules(ipt, ips))

	npc := npc.New(nodeName, ipt, ips)

	nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				handleError(npc.AddNamespace(obj.(*coreapi.Namespace)))
			},
			DeleteFunc: func(obj interface{}) {
				switch obj := obj.(type) {
				case *coreapi.Namespace:
					handleError(npc.DeleteNamespace(obj))
				case cache.DeletedFinalStateUnknown:
					// We know this object has gone away, but its final state is no longer
					// available from the API server. Instead we use the last copy of it
					// that we have, which is good enough for our cleanup.
					handleError(npc.DeleteNamespace(obj.Obj.(*coreapi.Namespace)))
				}
			},
			UpdateFunc: func(old, new interface{}) {
				handleError(npc.UpdateNamespace(old.(*coreapi.Namespace), new.(*coreapi.Namespace)))
			}})

	podController := makeController(client.Core().RESTClient(), "pods", &coreapi.Pod{},
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				handleError(npc.AddPod(obj.(*coreapi.Pod)))
			},
			DeleteFunc: func(obj interface{}) {
				switch obj := obj.(type) {
				case *coreapi.Pod:
					handleError(npc.DeletePod(obj))
				case cache.DeletedFinalStateUnknown:
					// We know this object has gone away, but its final state is no longer
					// available from the API server. Instead we use the last copy of it
					// that we have, which is good enough for our cleanup.
					handleError(npc.DeletePod(obj.Obj.(*coreapi.Pod)))
				}
			},
			UpdateFunc: func(old, new interface{}) {
				handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
			}})

	npHandlers := cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			handleError(npc.AddNetworkPolicy(obj))
		},
		DeleteFunc: func(obj interface{}) {
			switch obj := obj.(type) {
			case cache.DeletedFinalStateUnknown:
				// We know this object has gone away, but its final state is no longer
				// available from the API server. Instead we use the last copy of it
				// that we have, which is good enough for our cleanup.
				handleError(npc.DeleteNetworkPolicy(obj.Obj))
			default:
				handleError(npc.DeleteNetworkPolicy(obj))
			}
		},
		UpdateFunc: func(old, new interface{}) {
			handleError(npc.UpdateNetworkPolicy(old, new))
		},
	}
	npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)

	go nsController.Run(wait.NeverStop)
	go podController.Run(wait.NeverStop)
	go npController.Run(wait.NeverStop)

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
	common.Log.Fatalf("Exiting: %v", <-signals)
}

func main() {
	rootCmd := &cobra.Command{
		Use:   "weave-npc",
		Short: "Weaveworks Kubernetes Network Policy Controller",
		Run:   root}

	rootCmd.PersistentFlags().StringVar(&metricsAddr, "metrics-addr", ":6781", "metrics server bind address")
	rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
	rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
	rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
	rootCmd.PersistentFlags().IntVar(&maxList, "max-list-size", 1024, "maximum size of ipset list (for namespaces)")

	handleError(rootCmd.Execute())
}
<commit_msg>Replace weave-local-pods ipset with physdev iptables rule<commit_after>package main

import (
	"os"
	"os/signal"
	"syscall"

	"github.com/coreos/go-iptables/iptables"
	"github.com/spf13/cobra"
	coreapi "k8s.io/api/core/v1"
	networkingv1 "k8s.io/api/networking/v1"
	"k8s.io/apimachinery/pkg/fields"
	"k8s.io/apimachinery/pkg/runtime"
	"k8s.io/apimachinery/pkg/util/wait"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/cache"

	"github.com/weaveworks/weave/common"
	"github.com/weaveworks/weave/net"
	"github.com/weaveworks/weave/net/ipset"
	"github.com/weaveworks/weave/npc"
	"github.com/weaveworks/weave/npc/metrics"
	"github.com/weaveworks/weave/npc/ulogd"
)

var (
	version     = "unreleased"
	metricsAddr string
	logLevel    string
	allowMcast  bool
	nodeName    string
	maxList     int
)

func handleError(err error) { common.CheckFatal(err) }

func makeController(getter cache.Getter, resource string,
	objType runtime.Object, handlers cache.ResourceEventHandlerFuncs) cache.Controller {
	listWatch := cache.NewListWatchFromClient(getter, resource, "", fields.Everything())
	_, controller := cache.NewInformer(listWatch, objType, 0, handlers)
	return controller
}

func resetIPTables(ipt *iptables.IPTables) error {
	// Flush chains first so there are no refs to extant ipsets
	if err := ipt.ClearChain(npc.TableFilter, npc.IngressChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.DefaultChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.MainChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.EgressMarkChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.EgressCustomChain); err != nil {
		return err
	}

	if err := ipt.ClearChain(npc.TableFilter, npc.EgressDefaultChain); err != nil {
		return err
	}

	// We do not clear npc.EgressChain here because otherwise, in the case of restarting
	// weave-npc process, all egress traffic is allowed for a short period of time.
	// The chain is created in createBaseRules.

	return nil
}

func resetIPSets(ips ipset.Interface) error {
	// Remove ipsets prefixed `weave-` only.

	sets, err := ips.List(npc.IpsetNamePrefix)
	if err != nil {
		common.Log.Errorf("Failed to retrieve list of ipsets")
		return err
	}

	common.Log.Debugf("Got list of ipsets: %v", sets)

	// Must remove references to ipsets by other ipsets before they're destroyed
	for _, s := range sets {
		common.Log.Debugf("Flushing ipset '%s'", string(s))
		if err := ips.Flush(s); err != nil {
			common.Log.Errorf("Failed to flush ipset '%s'", string(s))
			return err
		}
	}

	for _, s := range sets {
		// LocalIPset might be used by WEAVE-NPC-EGRESS chain which we do not
		// flush, so we cannot destroy it.
		if s == npc.LocalIpset {
			continue
		}
		common.Log.Debugf("Destroying ipset '%s'", string(s))
		if err := ips.Destroy(s); err != nil {
			common.Log.Errorf("Failed to destroy ipset '%s'", string(s))
			return err
		}
	}

	return nil
}

func createBaseRules(ipt *iptables.IPTables, ips ipset.Interface) error {
	// Configure main chain static rules
	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"); err != nil {
		return err
	}

	if allowMcast {
		if err := ipt.Append(npc.TableFilter, npc.MainChain,
			"-d", "224.0.0.0/4", "-j", "ACCEPT"); err != nil {
			return err
		}
	}

	// If the destination address is not any of the local pods, let it through
	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "physdev", "--physdev-out=vethwe-bridge", "-j", "ACCEPT"); err != nil {
		return err
	}

	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "state", "--state", "NEW", "-j", string(npc.DefaultChain)); err != nil {
		return err
	}

	if err := ipt.Append(npc.TableFilter, npc.MainChain,
		"-m", "state", "--state", "NEW", "-j", string(npc.IngressChain)); err != nil {
		return err
	}

	if err := ipt.Append(npc.TableFilter, npc.EgressMarkChain,
		"-j", "MARK", "--set-xmark", npc.EgressMark); err != nil {
		return err
	}

	// Egress rules:
	//
	// -A WEAVE-NPC-EGRESS -m state --state RELATED,ESTABLISHED -j ACCEPT
	// -A WEAVE-NPC-EGRESS -m physdev --physdev-in=vethwe-bridge -j RETURN
	// -A WEAVE-NPC-EGRESS -d 224.0.0.0/4 -j RETURN
	// -A WEAVE-NPC-EGRESS -m state --state NEW -j WEAVE-NPC-EGRESS-DEFAULT
	// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j WEAVE-NPC-EGRESS-CUSTOM
	// -A WEAVE-NPC-EGRESS -m state --state NEW -m mark ! --mark 0x40000/0x40000 -j NFLOG --nflog-group 86
	// -A WEAVE-NPC-EGRESS -m mark ! --mark 0x40000/0x40000 -j DROP
	//
	// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j MARK --set-xmark 0x40000/0x40000
	// -A WEAVE-NPC-EGRESS-CUSTOM <rulespec> -j RETURN
	//
	// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j MARK --set-xmark 0x40000/0x40000
	// -A WEAVE-NPC-EGRESS-DEFAULT <rulespec> -j RETURN
	//
	// For each rule we create two (mark and return). We cannot just accept
	// a packet if it matches any rule, as a packet might need to traverse
	// the ingress npc as well which happens later in the chain (in some cases
	// we cannot detect whether packet is ingress or egress, so we need to
	// check both chains).

	ruleSpecs := [][]string{
		{"-m", "state", "--state", "RELATED,ESTABLISHED", "-j", "ACCEPT"},
		{"-m", "physdev", "--physdev-in=vethwe-bridge", "-j", "RETURN"},
	}
	if allowMcast {
		ruleSpecs = append(ruleSpecs, []string{"-d", "224.0.0.0/4", "-j", "RETURN"})
	}
	ruleSpecs = append(ruleSpecs, [][]string{
		{"-m", "state", "--state", "NEW", "-j", string(npc.EgressDefaultChain)},
		{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", string(npc.EgressCustomChain)},
		{"-m", "state", "--state", "NEW", "-m", "mark", "!", "--mark", npc.EgressMark, "-j", "NFLOG", "--nflog-group", "86"},
		{"-m", "mark", "!", "--mark", npc.EgressMark, "-j", "DROP"},
	}...)
	if err := net.AddChainWithRules(ipt, npc.TableFilter, npc.EgressChain, ruleSpecs); err != nil {
		return err
	}

	return nil
}

// Dummy way to check whether a given ipset exists.
// TODO(brb) Use "ipset -exist create <..>" for our purpose instead (for some reasons
// creating an ipset with -exist fails).
func ipsetExist(ips ipset.Interface, name ipset.Name) (bool, error) {
	sets, err := ips.List(string(name))
	if err != nil {
		return false, err
	}
	for _, s := range sets {
		if s == name {
			return true, nil
		}
	}
	return false, nil
}

func root(cmd *cobra.Command, args []string) {
	var npController cache.Controller

	common.SetLogLevel(logLevel)
	if nodeName == "" {
		// HOSTNAME is set by Kubernetes for pods in the host network namespace
		nodeName = os.Getenv("HOSTNAME")
	}
	if nodeName == "" {
		common.Log.Fatalf("Must set node name via --node-name or $HOSTNAME")
	}
	common.Log.Infof("Starting Weaveworks NPC %s; node name %q", version, nodeName)

	if err := metrics.Start(metricsAddr); err != nil {
		common.Log.Fatalf("Failed to start metrics: %v", err)
	}

	if err := ulogd.Start(); err != nil {
		common.Log.Fatalf("Failed to start ulogd: %v", err)
	}

	config, err := rest.InClusterConfig()
	handleError(err)

	client, err := kubernetes.NewForConfig(config)
	handleError(err)

	ipt, err := iptables.New()
	handleError(err)

	ips := ipset.New(common.LogLogger(), maxList)

	handleError(resetIPTables(ipt))
	handleError(resetIPSets(ips))
	handleError(createBaseRules(ipt, ips))

	npc := npc.New(nodeName, ipt, ips)

	nsController := makeController(client.Core().RESTClient(), "namespaces", &coreapi.Namespace{},
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				handleError(npc.AddNamespace(obj.(*coreapi.Namespace)))
			},
			DeleteFunc: func(obj interface{}) {
				switch obj := obj.(type) {
				case *coreapi.Namespace:
					handleError(npc.DeleteNamespace(obj))
				case cache.DeletedFinalStateUnknown:
					// We know this object has gone away, but its final state is no longer
					// available from the API server. Instead we use the last copy of it
					// that we have, which is good enough for our cleanup.
					handleError(npc.DeleteNamespace(obj.Obj.(*coreapi.Namespace)))
				}
			},
			UpdateFunc: func(old, new interface{}) {
				handleError(npc.UpdateNamespace(old.(*coreapi.Namespace), new.(*coreapi.Namespace)))
			}})

	podController := makeController(client.Core().RESTClient(), "pods", &coreapi.Pod{},
		cache.ResourceEventHandlerFuncs{
			AddFunc: func(obj interface{}) {
				handleError(npc.AddPod(obj.(*coreapi.Pod)))
			},
			DeleteFunc: func(obj interface{}) {
				switch obj := obj.(type) {
				case *coreapi.Pod:
					handleError(npc.DeletePod(obj))
				case cache.DeletedFinalStateUnknown:
					// We know this object has gone away, but its final state is no longer
					// available from the API server. Instead we use the last copy of it
					// that we have, which is good enough for our cleanup.
					handleError(npc.DeletePod(obj.Obj.(*coreapi.Pod)))
				}
			},
			UpdateFunc: func(old, new interface{}) {
				handleError(npc.UpdatePod(old.(*coreapi.Pod), new.(*coreapi.Pod)))
			}})

	npHandlers := cache.ResourceEventHandlerFuncs{
		AddFunc: func(obj interface{}) {
			handleError(npc.AddNetworkPolicy(obj))
		},
		DeleteFunc: func(obj interface{}) {
			switch obj := obj.(type) {
			case cache.DeletedFinalStateUnknown:
				// We know this object has gone away, but its final state is no longer
				// available from the API server. Instead we use the last copy of it
				// that we have, which is good enough for our cleanup.
				handleError(npc.DeleteNetworkPolicy(obj.Obj))
			default:
				handleError(npc.DeleteNetworkPolicy(obj))
			}
		},
		UpdateFunc: func(old, new interface{}) {
			handleError(npc.UpdateNetworkPolicy(old, new))
		},
	}
	npController = makeController(client.NetworkingV1().RESTClient(), "networkpolicies", &networkingv1.NetworkPolicy{}, npHandlers)

	go nsController.Run(wait.NeverStop)
	go podController.Run(wait.NeverStop)
	go npController.Run(wait.NeverStop)

	signals := make(chan os.Signal, 1)
	signal.Notify(signals, syscall.SIGINT, syscall.SIGTERM)
	common.Log.Fatalf("Exiting: %v", <-signals)
}

func main() {
	rootCmd := &cobra.Command{
		Use:   "weave-npc",
		Short: "Weaveworks Kubernetes Network Policy Controller",
		Run:   root}

	rootCmd.PersistentFlags().StringVar(&metricsAddr, "metrics-addr", ":6781", "metrics server bind address")
	rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "debug", "logging level (debug, info, warning, error)")
	rootCmd.PersistentFlags().BoolVar(&allowMcast, "allow-mcast", true, "allow all multicast traffic")
	rootCmd.PersistentFlags().StringVar(&nodeName, "node-name", "", "only generate rules that apply to this node")
	rootCmd.PersistentFlags().IntVar(&maxList, "max-list-size", 1024, "maximum size of ipset list (for namespaces)")

	handleError(rootCmd.Execute())
}
<|endoftext|>


<commit_before>package git

/*
#include <git2.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type BlameOptions struct {
	Flags              BlameOptionsFlag
	MinMatchCharacters uint16
	NewestCommit       *Oid
	OldestCommit       *Oid
	MinLine            uint32
	MaxLine            uint32
}

func DefaultBlameOptions() (BlameOptions, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	opts := C.git_blame_options{}
	ecode := C.git_blame_options_init(&opts, C.GIT_BLAME_OPTIONS_VERSION)
	if ecode < 0 {
		return BlameOptions{}, MakeGitError(ecode)
	}

	return BlameOptions{
		Flags:              BlameOptionsFlag(opts.flags),
		MinMatchCharacters: uint16(opts.min_match_characters),
		NewestCommit:       newOidFromC(&opts.newest_commit),
		OldestCommit:       newOidFromC(&opts.oldest_commit),
		MinLine:            uint32(opts.min_line),
		MaxLine:            uint32(opts.max_line),
	}, nil
}

type BlameOptionsFlag uint32

const (
	BlameNormal                      BlameOptionsFlag = C.GIT_BLAME_NORMAL
	BlameTrackCopiesSameFile         BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE
	BlameTrackCopiesSameCommitMoves  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
	BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
	BlameTrackCopiesAnyCommitCopies  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES
	BlameFirstParent                 BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT
)

func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) {
	var blamePtr *C.git_blame

	var copts *C.git_blame_options
	if opts != nil {
		copts = &C.git_blame_options{
			version:              C.GIT_BLAME_OPTIONS_VERSION,
			flags:                C.uint32_t(opts.Flags),
			min_match_characters: C.uint16_t(opts.MinMatchCharacters),
			min_line:             C.size_t(opts.MinLine),
			max_line:             C.size_t(opts.MaxLine),
		}
		if opts.NewestCommit != nil {
			copts.newest_commit = *opts.NewestCommit.toC()
		}
		if opts.OldestCommit != nil {
			copts.oldest_commit = *opts.OldestCommit.toC()
		}
	}

	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_blame_file(&blamePtr, v.ptr, cpath, copts)
	runtime.KeepAlive(v)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newBlameFromC(blamePtr), nil
}

type Blame struct {
	ptr *C.git_blame
}

func (blame *Blame) HunkCount() int {
	ret := int(C.git_blame_get_hunk_count(blame.ptr))
	runtime.KeepAlive(blame)

	return ret
}

func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
	ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index))
	runtime.KeepAlive(blame)
	if ptr == nil {
		return BlameHunk{}, ErrInvalid
	}
	return blameHunkFromC(ptr), nil
}

func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) {
	ptr := C.git_blame_get_hunk_byline(blame.ptr, C.size_t(lineno))
	runtime.KeepAlive(blame)
	if ptr == nil {
		return BlameHunk{}, ErrInvalid
	}
	return blameHunkFromC(ptr), nil
}

func newBlameFromC(ptr *C.git_blame) *Blame {
	if ptr == nil {
		return nil
	}

	blame := &Blame{
		ptr: ptr,
	}

	runtime.SetFinalizer(blame, (*Blame).Free)
	return blame
}

func (blame *Blame) Free() error {
	if blame.ptr == nil {
		return ErrInvalid
	}
	runtime.SetFinalizer(blame, nil)
	C.git_blame_free(blame.ptr)
	blame.ptr = nil
	return nil
}

type BlameHunk struct {
	LinesInHunk          uint16
	FinalCommitId        *Oid
	FinalStartLineNumber uint16
	FinalSignature       *Signature
	OrigCommitId         *Oid
	OrigPath             string
	OrigStartLineNumber  uint16
	OrigSignature        *Signature
	Boundary             bool
}

func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk {
	return BlameHunk{
		LinesInHunk:          uint16(hunk.lines_in_hunk),
		FinalCommitId:        newOidFromC(&hunk.final_commit_id),
		FinalStartLineNumber: uint16(hunk.final_start_line_number),
		FinalSignature:       newSignatureFromC(hunk.final_signature),
		OrigCommitId:         newOidFromC(&hunk.orig_commit_id),
		OrigPath:             C.GoString(hunk.orig_path),
		OrigStartLineNumber:  uint16(hunk.orig_start_line_number),
		OrigSignature:        newSignatureFromC(hunk.orig_signature),
		Boundary:             hunk.boundary == 1,
	}
}
<commit_msg>Add GIT_BLAME_USE_MAILMAP flag (#676)<commit_after>package git

/*
#include <git2.h>
*/
import "C"
import (
	"runtime"
	"unsafe"
)

type BlameOptions struct {
	Flags              BlameOptionsFlag
	MinMatchCharacters uint16
	NewestCommit       *Oid
	OldestCommit       *Oid
	MinLine            uint32
	MaxLine            uint32
}

func DefaultBlameOptions() (BlameOptions, error) {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	opts := C.git_blame_options{}
	ecode := C.git_blame_options_init(&opts, C.GIT_BLAME_OPTIONS_VERSION)
	if ecode < 0 {
		return BlameOptions{}, MakeGitError(ecode)
	}

	return BlameOptions{
		Flags:              BlameOptionsFlag(opts.flags),
		MinMatchCharacters: uint16(opts.min_match_characters),
		NewestCommit:       newOidFromC(&opts.newest_commit),
		OldestCommit:       newOidFromC(&opts.oldest_commit),
		MinLine:            uint32(opts.min_line),
		MaxLine:            uint32(opts.max_line),
	}, nil
}

type BlameOptionsFlag uint32

const (
	BlameNormal                      BlameOptionsFlag = C.GIT_BLAME_NORMAL
	BlameTrackCopiesSameFile         BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_FILE
	BlameTrackCopiesSameCommitMoves  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_MOVES
	BlameTrackCopiesSameCommitCopies BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_SAME_COMMIT_COPIES
	BlameTrackCopiesAnyCommitCopies  BlameOptionsFlag = C.GIT_BLAME_TRACK_COPIES_ANY_COMMIT_COPIES
	BlameFirstParent                 BlameOptionsFlag = C.GIT_BLAME_FIRST_PARENT
	BlameUseMailmap                  BlameOptionsFlag = C.GIT_BLAME_USE_MAILMAP
)

func (v *Repository) BlameFile(path string, opts *BlameOptions) (*Blame, error) {
	var blamePtr *C.git_blame

	var copts *C.git_blame_options
	if opts != nil {
		copts = &C.git_blame_options{
			version:              C.GIT_BLAME_OPTIONS_VERSION,
			flags:                C.uint32_t(opts.Flags),
			min_match_characters: C.uint16_t(opts.MinMatchCharacters),
			min_line:             C.size_t(opts.MinLine),
			max_line:             C.size_t(opts.MaxLine),
		}
		if opts.NewestCommit != nil {
			copts.newest_commit = *opts.NewestCommit.toC()
		}
		if opts.OldestCommit != nil {
			copts.oldest_commit = *opts.OldestCommit.toC()
		}
	}

	cpath := C.CString(path)
	defer C.free(unsafe.Pointer(cpath))

	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	ecode := C.git_blame_file(&blamePtr, v.ptr, cpath, copts)
	runtime.KeepAlive(v)
	if ecode < 0 {
		return nil, MakeGitError(ecode)
	}

	return newBlameFromC(blamePtr), nil
}

type Blame struct {
	ptr *C.git_blame
}

func (blame *Blame) HunkCount() int {
	ret := int(C.git_blame_get_hunk_count(blame.ptr))
	runtime.KeepAlive(blame)

	return ret
}

func (blame *Blame) HunkByIndex(index int) (BlameHunk, error) {
	ptr := C.git_blame_get_hunk_byindex(blame.ptr, C.uint32_t(index))
	runtime.KeepAlive(blame)
	if ptr == nil {
		return BlameHunk{}, ErrInvalid
	}
	return blameHunkFromC(ptr), nil
}

func (blame *Blame) HunkByLine(lineno int) (BlameHunk, error) {
	ptr := C.git_blame_get_hunk_byline(blame.ptr, C.size_t(lineno))
	runtime.KeepAlive(blame)
	if ptr == nil {
		return BlameHunk{}, ErrInvalid
	}
	return blameHunkFromC(ptr), nil
}

func newBlameFromC(ptr *C.git_blame) *Blame {
	if ptr == nil {
		return nil
	}

	blame := &Blame{
		ptr: ptr,
	}

	runtime.SetFinalizer(blame, (*Blame).Free)
	return blame
}

func (blame *Blame) Free() error {
	if blame.ptr == nil {
		return ErrInvalid
	}
	runtime.SetFinalizer(blame, nil)
	C.git_blame_free(blame.ptr)
	blame.ptr = nil
	return nil
}

type BlameHunk struct {
	LinesInHunk          uint16
	FinalCommitId        *Oid
	FinalStartLineNumber uint16
	FinalSignature       *Signature
	OrigCommitId         *Oid
	OrigPath             string
	OrigStartLineNumber  uint16
	OrigSignature        *Signature
	Boundary             bool
}

func blameHunkFromC(hunk *C.git_blame_hunk) BlameHunk {
	return BlameHunk{
		LinesInHunk:          uint16(hunk.lines_in_hunk),
		FinalCommitId:        newOidFromC(&hunk.final_commit_id),
		FinalStartLineNumber: uint16(hunk.final_start_line_number),
		FinalSignature:       newSignatureFromC(hunk.final_signature),
		OrigCommitId:         newOidFromC(&hunk.orig_commit_id),
		OrigPath:             C.GoString(hunk.orig_path),
		OrigStartLineNumber:  uint16(hunk.orig_start_line_number),
		OrigSignature:        newSignatureFromC(hunk.orig_signature),
		Boundary:             hunk.boundary == 1,
	}
}
<|endoftext|>


<commit_before>// Copyright 2020 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"testing"
)

func TestEditChange_Marshal_TitleChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: &EditTitle{
			From: String("TitleFrom"),
		},
		Body: nil,
		Base: nil,
	}

	want := `{
		"title": {
			"from": "TitleFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: nil,
		Body: &EditBody{
			From: String("BodyFrom"),
		},
		Base: nil,
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BaseChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	Base := EditBase{
		Ref: &EditRef{
			From: String("BaseRefFrom"),
		},
		SHA: &EditSHA{
			From: String("BaseSHAFrom"),
		},
	}

	u := &EditChange{
		Title: nil,
		Body:  nil,
		Base:  &Base,
	}

	want := `{
		"base": {
			"ref": {
				"from": "BaseRefFrom"
			},
			"sha": {
				"from": "BaseSHAFrom"
			}
		}
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectChange_Marshal_NameChange(t *testing.T) {
	testJSONMarshal(t, &ProjectChange{}, "{}")

	NameFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("NameFrom"),
	}

	u := &ProjectChange{
		Name: &NameFrom,
		Body: nil,
	}

	want := `{
		"name": {
			"from": "NameFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &ProjectChange{}, "{}")

	BodyFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("BodyFrom"),
	}

	u := &ProjectChange{
		Name: nil,
		Body: &BodyFrom,
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectCardChange_Marshal_NoteChange(t *testing.T) {
	testJSONMarshal(t, &ProjectCardChange{}, "{}")

	NoteFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("NoteFrom"),
	}

	u := &ProjectCardChange{
		Note: &NoteFrom,
	}

	want := `{
		"note": {
			"from": "NoteFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectColumnChange_Marshal_NameChange(t *testing.T) {
	testJSONMarshal(t, &ProjectColumnChange{}, "{}")

	NameFrom := struct {
		From *string `json:"from,omitempty"`
	}{
		From: String("NameFrom"),
	}

	u := &ProjectColumnChange{
		Name: &NameFrom,
	}

	want := `{
		"name": {
			"from": "NameFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}
<commit_msg>Fix tests for merge of #1902 (#1954)<commit_after>// Copyright 2020 The go-github AUTHORS. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package github

import (
	"testing"
)

func TestEditChange_Marshal_TitleChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: &EditTitle{
			From: String("TitleFrom"),
		},
		Body: nil,
		Base: nil,
	}

	want := `{
		"title": {
			"from": "TitleFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	u := &EditChange{
		Title: nil,
		Body: &EditBody{
			From: String("BodyFrom"),
		},
		Base: nil,
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestEditChange_Marshal_BaseChange(t *testing.T) {
	testJSONMarshal(t, &EditChange{}, "{}")

	Base := EditBase{
		Ref: &EditRef{
			From: String("BaseRefFrom"),
		},
		SHA: &EditSHA{
			From: String("BaseSHAFrom"),
		},
	}

	u := &EditChange{
		Title: nil,
		Body:  nil,
		Base:  &Base,
	}

	want := `{
		"base": {
			"ref": {
				"from": "BaseRefFrom"
			},
			"sha": {
				"from": "BaseSHAFrom"
			}
		}
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectChange_Marshal_NameChange(t *testing.T) {
	testJSONMarshal(t, &ProjectChange{}, "{}")

	u := &ProjectChange{
		Name: &ProjectName{From: String("NameFrom")},
		Body: nil,
	}

	want := `{
		"name": {
			"from": "NameFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectChange_Marshal_BodyChange(t *testing.T) {
	testJSONMarshal(t, &ProjectChange{}, "{}")

	u := &ProjectChange{
		Name: nil,
		Body: &ProjectBody{From: String("BodyFrom")},
	}

	want := `{
		"body": {
			"from": "BodyFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectCardChange_Marshal_NoteChange(t *testing.T) {
	testJSONMarshal(t, &ProjectCardChange{}, "{}")

	u := &ProjectCardChange{
		Note: &ProjectCardNote{From: String("NoteFrom")},
	}

	want := `{
		"note": {
			"from": "NoteFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}

func TestProjectColumnChange_Marshal_NameChange(t *testing.T) {
	testJSONMarshal(t, &ProjectColumnChange{}, "{}")

	u := &ProjectColumnChange{
		Name: &ProjectColumnName{From: String("NameFrom")},
	}

	want := `{
		"name": {
			"from": "NameFrom"
		  }
	}`

	testJSONMarshal(t, u, want)
}
<|endoftext|>