<commit_before>package vegeta

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"
)

// Attacker is an attack executor which wraps an http.Client
type Attacker struct {
	dialer    *net.Dialer
	client    http.Client
	stopch    chan struct{}
	workers   uint64
	redirects int
}

const (
	// DefaultRedirects is the default number of times an Attacker follows
	// redirects.
	DefaultRedirects = 10
	// DefaultTimeout is the default amount of time an Attacker waits for a request
	// before it times out.
	DefaultTimeout = 30 * time.Second
	// DefaultConnections is the default amount of max open idle connections per
	// target host.
	DefaultConnections = 10000
	// DefaultWorkers is the default initial number of workers used to carry an attack.
	DefaultWorkers = 10
	// NoFollow is the value when redirects are not followed but marked successful
	NoFollow = -1
)

var (
	// DefaultLocalAddr is the default local IP address an Attacker uses.
	DefaultLocalAddr = net.IPAddr{IP: net.IPv4zero}
	// DefaultTLSConfig is the default tls.Config an Attacker uses.
	DefaultTLSConfig = &tls.Config{InsecureSkipVerify: true}
)

// NewAttacker returns a new Attacker with default options which are overridden
// by the optionally provided opts.
func NewAttacker(opts ...func(*Attacker)) *Attacker {
	a := &Attacker{stopch: make(chan struct{}), workers: DefaultWorkers}
	a.dialer = &net.Dialer{
		LocalAddr: &net.TCPAddr{IP: DefaultLocalAddr.IP, Zone: DefaultLocalAddr.Zone},
		KeepAlive: 30 * time.Second,
		Timeout:   DefaultTimeout,
	}
	a.client = http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial:  a.dialer.Dial,
			ResponseHeaderTimeout: DefaultTimeout,
			TLSClientConfig:       DefaultTLSConfig,
			TLSHandshakeTimeout:   10 * time.Second,
			MaxIdleConnsPerHost:   DefaultConnections,
		},
	}
	for _, opt := range opts {
		opt(a)
	}
	return a
}

// Workers returns a functional option which sets the initial number of workers
// an Attacker uses to hit its targets. More workers may be spawned dynamically
// to sustain the requested rate in the face of slow responses and errors.
func Workers(n uint64) func(*Attacker) {
	return func(a *Attacker) { a.workers = n }
}

// Connections returns a functional option which sets the number of maximum idle
// open connections per target host.
func Connections(n int) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.MaxIdleConnsPerHost = n
	}
}

// Redirects returns a functional option which sets the maximum
// number of redirects an Attacker will follow.
func Redirects(n int) func(*Attacker) {
	return func(a *Attacker) {
		a.redirects = n
		a.client.CheckRedirect = func(_ *http.Request, via []*http.Request) error {
			if len(via) > n {
				return fmt.Errorf("stopped after %d redirects", n)
			}
			return nil
		}
	}
}

// Timeout returns a functional option which sets the maximum amount of time
// an Attacker will wait for a request to be responded to.
func Timeout(d time.Duration) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.ResponseHeaderTimeout = d
		a.dialer.Timeout = d
		tr.Dial = a.dialer.Dial
	}
}

// LocalAddr returns a functional option which sets the local address
// an Attacker will use with its requests.
func LocalAddr(addr net.IPAddr) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		a.dialer.LocalAddr = &net.TCPAddr{IP: addr.IP, Zone: addr.Zone}
		tr.Dial = a.dialer.Dial
	}
}

// KeepAlive returns a functional option which toggles KeepAlive
// connections on the dialer and transport.
func KeepAlive(keepalive bool) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.DisableKeepAlives = !keepalive
		if !keepalive {
			a.dialer.KeepAlive = 0
			tr.Dial = a.dialer.Dial
		}
	}
}

// TLSConfig returns a functional option which sets the *tls.Config for a
// Attacker to use with its requests.
func TLSConfig(c *tls.Config) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.TLSClientConfig = c
	}
}

// Attack reads its Targets from the passed Targeter and attacks them at
// the rate specified for duration time. When the duration is zero the attack
// runs until Stop is called. Results are put into the returned channel as soon
// as they arrive.
func (a *Attacker) Attack(tr Targeter, rate uint64, du time.Duration) <-chan *Result {
	workers := &sync.WaitGroup{}
	results := make(chan *Result)
	ticks := make(chan time.Time)
	for i := uint64(0); i < a.workers; i++ {
		go a.attack(tr, workers, ticks, results)
	}

	go func() {
		defer close(results)
		defer workers.Wait()
		defer close(ticks)
		interval := 1e9 / rate
		hits := rate * uint64(du.Seconds())
		began, done := time.Now(), uint64(0)
		for {
			now, next := time.Now(), began.Add(time.Duration(done*interval))
			time.Sleep(next.Sub(now))
			select {
			case ticks <- max(next, now):
				if done++; done == hits {
					return
				}
			case <-a.stopch:
				return
			default: // all workers are blocked. start one more and try again
				go a.attack(tr, workers, ticks, results)
			}
		}
	}()

	return results
}

// Stop stops the current attack.
func (a *Attacker) Stop() { close(a.stopch) }

func (a *Attacker) attack(tr Targeter, workers *sync.WaitGroup, ticks <-chan time.Time, results chan<- *Result) {
	workers.Add(1)
	defer workers.Done()
	for tm := range ticks {
		results <- a.hit(tr, tm)
	}
}

func (a *Attacker) hit(tr Targeter, tm time.Time) *Result {
	var (
		res = Result{Timestamp: tm}
		tgt Target
		err error
	)

	defer func() {
		res.Latency = time.Since(tm)
		if err != nil {
			res.Error = err.Error()
		}
	}()

	if err = tr(&tgt); err != nil {
		return &res
	}

	req, err := tgt.Request()
	if err != nil {
		return &res
	}

	r, err := a.client.Do(req)
	if err != nil {
		// ignore redirect errors when the user set --redirects=NoFollow
		if a.redirects == NoFollow && strings.Contains(err.Error(), "stopped after") {
			err = nil
		}
		return &res
	}
	defer r.Body.Close()

	in, err := io.Copy(ioutil.Discard, r.Body)
	if err != nil {
		return &res
	}
	res.BytesIn = uint64(in)

	if req.ContentLength != -1 {
		res.BytesOut = uint64(req.ContentLength)
	}

	if res.Code = uint16(r.StatusCode); res.Code < 200 || res.Code >= 400 {
		res.Error = r.Status
	}

	return &res
}

func max(a, b time.Time) time.Time {
	if a.After(b) {
		return a
	}
	return b
}
<commit_msg>attacker: Allocate sync.WaitGroup in stack<commit_after>package vegeta

import (
	"crypto/tls"
	"fmt"
	"io"
	"io/ioutil"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"
)

// Attacker is an attack executor which wraps an http.Client
type Attacker struct {
	dialer    *net.Dialer
	client    http.Client
	stopch    chan struct{}
	workers   uint64
	redirects int
}

const (
	// DefaultRedirects is the default number of times an Attacker follows
	// redirects.
	DefaultRedirects = 10
	// DefaultTimeout is the default amount of time an Attacker waits for a request
	// before it times out.
	DefaultTimeout = 30 * time.Second
	// DefaultConnections is the default amount of max open idle connections per
	// target host.
	DefaultConnections = 10000
	// DefaultWorkers is the default initial number of workers used to carry an attack.
	DefaultWorkers = 10
	// NoFollow is the value when redirects are not followed but marked successful
	NoFollow = -1
)

var (
	// DefaultLocalAddr is the default local IP address an Attacker uses.
	DefaultLocalAddr = net.IPAddr{IP: net.IPv4zero}
	// DefaultTLSConfig is the default tls.Config an Attacker uses.
	DefaultTLSConfig = &tls.Config{InsecureSkipVerify: true}
)

// NewAttacker returns a new Attacker with default options which are overridden
// by the optionally provided opts.
func NewAttacker(opts ...func(*Attacker)) *Attacker {
	a := &Attacker{stopch: make(chan struct{}), workers: DefaultWorkers}
	a.dialer = &net.Dialer{
		LocalAddr: &net.TCPAddr{IP: DefaultLocalAddr.IP, Zone: DefaultLocalAddr.Zone},
		KeepAlive: 30 * time.Second,
		Timeout:   DefaultTimeout,
	}
	a.client = http.Client{
		Transport: &http.Transport{
			Proxy: http.ProxyFromEnvironment,
			Dial:  a.dialer.Dial,
			ResponseHeaderTimeout: DefaultTimeout,
			TLSClientConfig:       DefaultTLSConfig,
			TLSHandshakeTimeout:   10 * time.Second,
			MaxIdleConnsPerHost:   DefaultConnections,
		},
	}
	for _, opt := range opts {
		opt(a)
	}
	return a
}

// Workers returns a functional option which sets the initial number of workers
// an Attacker uses to hit its targets. More workers may be spawned dynamically
// to sustain the requested rate in the face of slow responses and errors.
func Workers(n uint64) func(*Attacker) {
	return func(a *Attacker) { a.workers = n }
}

// Connections returns a functional option which sets the number of maximum idle
// open connections per target host.
func Connections(n int) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.MaxIdleConnsPerHost = n
	}
}

// Redirects returns a functional option which sets the maximum
// number of redirects an Attacker will follow.
func Redirects(n int) func(*Attacker) {
	return func(a *Attacker) {
		a.redirects = n
		a.client.CheckRedirect = func(_ *http.Request, via []*http.Request) error {
			if len(via) > n {
				return fmt.Errorf("stopped after %d redirects", n)
			}
			return nil
		}
	}
}

// Timeout returns a functional option which sets the maximum amount of time
// an Attacker will wait for a request to be responded to.
func Timeout(d time.Duration) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.ResponseHeaderTimeout = d
		a.dialer.Timeout = d
		tr.Dial = a.dialer.Dial
	}
}

// LocalAddr returns a functional option which sets the local address
// an Attacker will use with its requests.
func LocalAddr(addr net.IPAddr) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		a.dialer.LocalAddr = &net.TCPAddr{IP: addr.IP, Zone: addr.Zone}
		tr.Dial = a.dialer.Dial
	}
}

// KeepAlive returns a functional option which toggles KeepAlive
// connections on the dialer and transport.
func KeepAlive(keepalive bool) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.DisableKeepAlives = !keepalive
		if !keepalive {
			a.dialer.KeepAlive = 0
			tr.Dial = a.dialer.Dial
		}
	}
}

// TLSConfig returns a functional option which sets the *tls.Config for a
// Attacker to use with its requests.
func TLSConfig(c *tls.Config) func(*Attacker) {
	return func(a *Attacker) {
		tr := a.client.Transport.(*http.Transport)
		tr.TLSClientConfig = c
	}
}

// Attack reads its Targets from the passed Targeter and attacks them at
// the rate specified for duration time. When the duration is zero the attack
// runs until Stop is called. Results are put into the returned channel as soon
// as they arrive.
func (a *Attacker) Attack(tr Targeter, rate uint64, du time.Duration) <-chan *Result {
	var workers sync.WaitGroup
	results := make(chan *Result)
	ticks := make(chan time.Time)
	for i := uint64(0); i < a.workers; i++ {
		go a.attack(tr, &workers, ticks, results)
	}

	go func() {
		defer close(results)
		defer workers.Wait()
		defer close(ticks)
		interval := 1e9 / rate
		hits := rate * uint64(du.Seconds())
		began, done := time.Now(), uint64(0)
		for {
			now, next := time.Now(), began.Add(time.Duration(done*interval))
			time.Sleep(next.Sub(now))
			select {
			case ticks <- max(next, now):
				if done++; done == hits {
					return
				}
			case <-a.stopch:
				return
			default: // all workers are blocked. start one more and try again
				go a.attack(tr, &workers, ticks, results)
			}
		}
	}()

	return results
}

// Stop stops the current attack.
func (a *Attacker) Stop() { close(a.stopch) }

func (a *Attacker) attack(tr Targeter, workers *sync.WaitGroup, ticks <-chan time.Time, results chan<- *Result) {
	workers.Add(1)
	defer workers.Done()
	for tm := range ticks {
		results <- a.hit(tr, tm)
	}
}

func (a *Attacker) hit(tr Targeter, tm time.Time) *Result {
	var (
		res = Result{Timestamp: tm}
		tgt Target
		err error
	)

	defer func() {
		res.Latency = time.Since(tm)
		if err != nil {
			res.Error = err.Error()
		}
	}()

	if err = tr(&tgt); err != nil {
		return &res
	}

	req, err := tgt.Request()
	if err != nil {
		return &res
	}

	r, err := a.client.Do(req)
	if err != nil {
		// ignore redirect errors when the user set --redirects=NoFollow
		if a.redirects == NoFollow && strings.Contains(err.Error(), "stopped after") {
			err = nil
		}
		return &res
	}
	defer r.Body.Close()

	in, err := io.Copy(ioutil.Discard, r.Body)
	if err != nil {
		return &res
	}
	res.BytesIn = uint64(in)

	if req.ContentLength != -1 {
		res.BytesOut = uint64(req.ContentLength)
	}

	if res.Code = uint16(r.StatusCode); res.Code < 200 || res.Code >= 400 {
		res.Error = r.Status
	}

	return &res
}

func max(a, b time.Time) time.Time {
	if a.After(b) {
		return a
	}
	return b
}
<|endoftext|>


<commit_before>package lib

import (
	auth3 "bytemark.co.uk/auth3/client"
)

type Client struct {
	Endpoint    string
	Auth        *auth3.Client
	AuthSession *auth3.SessionData
	DebugLevel  int
}

// Creates a new Client using the auth.Client you specify.
func NewWithSession(auth *auth3.Client, session *auth3.SessionData) (bigv *Client, err error) {
	// check your session's good or smth

	bigv = new(Client)
	bigv.Endpoint = "https://uk0.bigv.io"
	bigv.Auth = auth
	bigv.AuthSession = session
	bigv.DebugLevel = 0

	return bigv, nil
}

// Creates a new Client authenticating against auth.bytemark.co.uk
func NewWithCredentials(credentials auth3.Credentials) (bigv *Client, err error) {
	// sort out all the auth shit yo
	auth, err := auth3.New("https://auth.bytemark.co.uk")
	if err != nil {
		return nil, err
	}

	session, err := auth.CreateSession(credentials)

	return NewWithSession(auth, session)
}

// Creates a new Client authenticating against auth.bytemark.co.uk
func NewWithToken(token string) (bigv *Client, err error) {

	auth, err := auth3.New("https://auth.bytemark.co.uk")
	if err != nil {
		return nil, err
	}

	session, err := auth.ReadSession(token)
	if err != nil {
		return nil, err
	}

	return NewWithSession(auth, session)
}
<commit_msg>Added error checking to lib.NewClientWithCredentials<commit_after>package lib

import (
	auth3 "bytemark.co.uk/auth3/client"
)

type Client struct {
	Endpoint    string
	Auth        *auth3.Client
	AuthSession *auth3.SessionData
	DebugLevel  int
}

// Creates a new Client using the auth.Client you specify.
func NewWithSession(auth *auth3.Client, session *auth3.SessionData) (bigv *Client, err error) {
	// check your session's good or smth

	bigv = new(Client)
	bigv.Endpoint = "https://uk0.bigv.io"
	bigv.Auth = auth
	bigv.AuthSession = session
	bigv.DebugLevel = 0

	return bigv, nil
}

// Creates a new Client authenticating against auth.bytemark.co.uk
func NewWithCredentials(credentials auth3.Credentials) (bigv *Client, err error) {
	// sort out all the auth shit yo
	auth, err := auth3.New("https://auth.bytemark.co.uk")
	if err != nil {
		return nil, err
	}

	session, err := auth.CreateSession(credentials)
	if err != nil {
		return nil, err
	}

	return NewWithSession(auth, session)
}

// Creates a new Client authenticating against auth.bytemark.co.uk
func NewWithToken(token string) (bigv *Client, err error) {

	auth, err := auth3.New("https://auth.bytemark.co.uk")
	if err != nil {
		return nil, err
	}

	session, err := auth.ReadSession(token)
	if err != nil {
		return nil, err
	}

	return NewWithSession(auth, session)
}
<|endoftext|>


<commit_before>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsNatGateway() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsNatGatewayRead,

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"vpc_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"subnet_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"tags": tagsSchemaComputed(),

			"filter": ec2CustomFiltersSchema(),
		},
	}
}

func dataSourceAwsNatGatewayRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	req := &ec2.DescribeNatGatewaysInput{}

	if id, ok := d.GetOk("id"); ok {
		req.NatGatewayIds = aws.StringSlice([]string{id.(string)})
	}

	if vpc_id, ok := d.GetOk("vpc_id"); ok {
		req.Filter = append(req.Filter, buildEC2AttributeFilterList(
			map[string]string{
				"vpc-id": vpc_id.(string),
			},
		)...)
	}

	if state, ok := d.GetOk("state"); ok {
		req.Filter = append(req.Filter, buildEC2AttributeFilterList(
			map[string]string{
				"state": state.(string),
			},
		)...)
	}

	if subnet_id, ok := d.GetOk("subnet_id"); ok {
		req.Filter = append(req.Filter, buildEC2AttributeFilterList(
			map[string]string{
				"subnet-id": subnet_id.(string),
			},
		)...)
	}

	req.Filter = append(req.Filter, buildEC2CustomFilterList(
		d.Get("filter").(*schema.Set),
	)...)
	if len(req.Filter) == 0 {
		// Don't send an empty filters list; the EC2 API won't accept it.
		req.Filter = nil
	}
	log.Printf("[DEBUG] Reading NAT Gateway: %s", req)
	resp, err := conn.DescribeNatGateways(req)
	if err != nil {
		return err
	}
	if resp == nil || len(resp.NatGateways) == 0 {
		return fmt.Errorf("no matching NAT gateway found: %#v", req)
	}
	if len(resp.NatGateways) > 1 {
		return fmt.Errorf("multiple NAT gateways matched; use additional constraints to reduce matches to a single NAT gateway")
	}

	ngw := resp.NatGateways[0]

	d.SetId(aws.StringValue(ngw.NatGatewayId))
	d.Set("state", ngw.State)
	d.Set("subnet_id", ngw.SubnetId)
	d.Set("vpc_id", ngw.VpcId)

	for _, address := range ngw.NatGatewayAddresses {
		if *address.AllocationId != "" {
			d.Set("allocation_id", address.AllocationId)
			d.Set("network_interface_id", address.NetworkInterfaceId)
			d.Set("private_ip", address.PrivateIp)
			d.Set("public_ip", address.PublicIp)
			break
		}
	}

	return nil
}
<commit_msg>Include Address attributes in NAT Gateway data source<commit_after>package aws

import (
	"fmt"
	"log"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/service/ec2"
	"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceAwsNatGateway() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceAwsNatGatewayRead,

		Schema: map[string]*schema.Schema{
			"id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"vpc_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"subnet_id": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"allocation_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"network_interface_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"public_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"private_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"tags":   tagsSchemaComputed(),
			"filter": ec2CustomFiltersSchema(),
		},
	}
}

func dataSourceAwsNatGatewayRead(d *schema.ResourceData, meta interface{}) error {
	conn := meta.(*AWSClient).ec2conn

	req := &ec2.DescribeNatGatewaysInput{}

	if id, ok := d.GetOk("id"); ok {
		req.NatGatewayIds = aws.StringSlice([]string{id.(string)})
	}

	if vpc_id, ok := d.GetOk("vpc_id"); ok {
		req.Filter = append(req.Filter, buildEC2AttributeFilterList(
			map[string]string{
				"vpc-id": vpc_id.(string),
			},
		)...)
	}

	if state, ok := d.GetOk("state"); ok {
		req.Filter = append(req.Filter, buildEC2AttributeFilterList(
			map[string]string{
				"state": state.(string),
			},
		)...)
	}

	if subnet_id, ok := d.GetOk("subnet_id"); ok {
		req.Filter = append(req.Filter, buildEC2AttributeFilterList(
			map[string]string{
				"subnet-id": subnet_id.(string),
			},
		)...)
	}

	req.Filter = append(req.Filter, buildEC2CustomFilterList(
		d.Get("filter").(*schema.Set),
	)...)
	if len(req.Filter) == 0 {
		// Don't send an empty filters list; the EC2 API won't accept it.
		req.Filter = nil
	}
	log.Printf("[DEBUG] Reading NAT Gateway: %s", req)
	resp, err := conn.DescribeNatGateways(req)
	if err != nil {
		return err
	}
	if resp == nil || len(resp.NatGateways) == 0 {
		return fmt.Errorf("no matching NAT gateway found: %#v", req)
	}
	if len(resp.NatGateways) > 1 {
		return fmt.Errorf("multiple NAT gateways matched; use additional constraints to reduce matches to a single NAT gateway")
	}

	ngw := resp.NatGateways[0]

	log.Printf("[DEBUG] NAT Gateway response: %s", ngw)

	d.SetId(aws.StringValue(ngw.NatGatewayId))
	d.Set("state", ngw.State)
	d.Set("subnet_id", ngw.SubnetId)
	d.Set("vpc_id", ngw.VpcId)

	for _, address := range ngw.NatGatewayAddresses {
		if *address.AllocationId != "" {
			d.Set("allocation_id", address.AllocationId)
			d.Set("network_interface_id", address.NetworkInterfaceId)
			d.Set("private_ip", address.PrivateIp)
			d.Set("public_ip", address.PublicIp)
			break
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Package libsacloud is the SAKURA CLOUD API v1.1 Client for Go.
package libsacloud

// Version バージョン
const Version = "1.22.3"
<commit_msg>Bump to v1.22.4<commit_after>// Package libsacloud is the SAKURA CLOUD API v1.1 Client for Go.
package libsacloud

// Version バージョン
const Version = "1.22.4"
<|endoftext|>


<commit_before>package lightswarm

import (
	"encoding/binary"
	"io"
)

// Byte constants
const (
	END byte = 192 // End byte
	ESC byte = 219 // Escape byte
)

// Command constants
const (
	ON                         byte = 32 // on
	OFF                        byte = 33 // off
	SET_LEVEL                  byte = 34 // set level
	FADE_TO_LEVEL              byte = 35 // fade to level
	SET_PSUEDO_ADDRESS         byte = 37 // set psuedo address
	ERASE_PSUEDO_ADDRESS_TABLE byte = 38 // erase psuedo address table
	SET_RGB_LEVELS             byte = 44 // set rgb levels
	TOGGLE                     byte = 45 // toggle
	FADE_MULTIPLE_TO_LEVEL     byte = 48 // fade multiple to level
	FADE_RGB_TO_LEVEL          byte = 49 // fade rgb to level
)

type Frame struct {
	// Exported Fields
	Addr    uint16
	Cmd     byte
	CmdArgs []byte
}

// Returns the frame address broken into 2 bytes
func (f Frame) address() (b1, b2 byte) {
	bs := make([]byte, 2)
	binary.BigEndian.PutUint16(bs, f.Addr)
	return bs[0], bs[1]
}

// Calculates checksum for given bytes
func (f Frame) checksum(bs []byte) byte {
	var checksum byte
	for _, b := range bs {
		checksum ^= b
	}
	return checksum
}

// Wrap the given bytes in end bytes with escape sequences
func (f Frame) wrap(bs []byte) []byte {
	// create a new byte slice to store the wrapped frame, starting with the END byte
	nbs := []byte{END}
	// loop over the given byte slice, appending to the new byte slice
	// and performing any escapes required
	for _, b := range bs {
		switch b {
		case END:
			nbs = append(nbs, ESC, 220)
		case ESC:
			nbs = append(nbs, ESC, 221)
		default:
			nbs = append(nbs, b)
		}
	}
	// now add the end byte last
	nbs = append(nbs, END)
	return nbs
}

// Returns the frame in byte format for writing to lightswarm
func (f Frame) Bytes() []byte {
	bs := []byte{}
	// Add address bytes
	addr1, addr2 := f.address()
	bs = append(bs, addr1)
	bs = append(bs, addr2)
	// Add command byte
	bs = append(bs, f.Cmd)
	// Add command arg bytes
	bs = append(bs, f.CmdArgs...)
	// Add Checksum
	bs = append(bs, f.checksum(bs))
	// Wrap bytes in end bytes with escape sequences
	bs = f.wrap(bs)
	return bs
}

// Represents a single Lightswarm LED
type LED struct {
	// Exported Fields
	Addr   uint16
	Writer io.Writer
}

// Send the On command to the LED writer
func (led *LED) On() {
	frame := Frame{Addr: led.Addr, Cmd: ON}
	led.Writer.Write(frame.Bytes())
}

// Send the Off command to the LED writer
func (led *LED) Off() {
	frame := Frame{Addr: led.Addr, Cmd: OFF}
	led.Writer.Write(frame.Bytes())
}

// Constructs a new LED
func New(addr uint16, writer io.Writer) *LED {
	return &LED{
		Addr:   addr,
		Writer: writer,
	}
}
<commit_msg>Return number of bytes written and error for on / off commands<commit_after>package lightswarm

import (
	"encoding/binary"
	"io"
)

// Byte constants
const (
	END byte = 192 // End byte
	ESC byte = 219 // Escape byte
)

// Command constants
const (
	ON                         byte = 32 // on
	OFF                        byte = 33 // off
	SET_LEVEL                  byte = 34 // set level
	FADE_TO_LEVEL              byte = 35 // fade to level
	SET_PSUEDO_ADDRESS         byte = 37 // set psuedo address
	ERASE_PSUEDO_ADDRESS_TABLE byte = 38 // erase psuedo address table
	SET_RGB_LEVELS             byte = 44 // set rgb levels
	TOGGLE                     byte = 45 // toggle
	FADE_MULTIPLE_TO_LEVEL     byte = 48 // fade multiple to level
	FADE_RGB_TO_LEVEL          byte = 49 // fade rgb to level
)

type Frame struct {
	// Exported Fields
	Addr    uint16
	Cmd     byte
	CmdArgs []byte
}

// Returns the frame address broken into 2 bytes
func (f Frame) address() (b1, b2 byte) {
	bs := make([]byte, 2)
	binary.BigEndian.PutUint16(bs, f.Addr)
	return bs[0], bs[1]
}

// Calculates checksum for given bytes
func (f Frame) checksum(bs []byte) byte {
	var checksum byte
	for _, b := range bs {
		checksum ^= b
	}
	return checksum
}

// Wrap the given bytes in end bytes with escape sequences
func (f Frame) wrap(bs []byte) []byte {
	// create a new byte slice to store the wrapped frame, starting with the END byte
	nbs := []byte{END}
	// loop over the given byte slice, appending to the new byte slice
	// and performing any escapes required
	for _, b := range bs {
		switch b {
		case END:
			nbs = append(nbs, ESC, 220)
		case ESC:
			nbs = append(nbs, ESC, 221)
		default:
			nbs = append(nbs, b)
		}
	}
	// now add the end byte last
	nbs = append(nbs, END)
	return nbs
}

// Returns the frame in byte format for writing to lightswarm
func (f Frame) Bytes() []byte {
	bs := []byte{}
	// Add address bytes
	addr1, addr2 := f.address()
	bs = append(bs, addr1)
	bs = append(bs, addr2)
	// Add command byte
	bs = append(bs, f.Cmd)
	// Add command arg bytes
	bs = append(bs, f.CmdArgs...)
	// Add Checksum
	bs = append(bs, f.checksum(bs))
	// Wrap bytes in end bytes with escape sequences
	bs = f.wrap(bs)
	return bs
}

// Represents a single Lightswarm LED
type LED struct {
	// Exported Fields
	Addr   uint16
	Writer io.Writer
}

// Send the On command to the LED writer
func (led *LED) On() (int, error) {
	frame := Frame{Addr: led.Addr, Cmd: ON}
	return led.Writer.Write(frame.Bytes())
}

// Send the Off command to the LED writer
func (led *LED) Off() (int, error) {
	frame := Frame{Addr: led.Addr, Cmd: OFF}
	return led.Writer.Write(frame.Bytes())
}

// Constructs a new LED
func New(addr uint16, writer io.Writer) *LED {
	return &LED{
		Addr:   addr,
		Writer: writer,
	}
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"sort"
	"strings"
	"time"
)

func init() {
	parser.AddCommand("log",
		"Manipulate time log",
		"The log command permits the listing and logging of time on specific stories.",
		&logCommand)

}

type LogCommand struct {
	MyLog     bool   `short:"m" long:"mine" description:"Show my log for current sprint" default:"true" group:"Application Options"`
	Author    string `short:"a" long:"author" description:"Show log for given author"`
	Yesterday bool   `short:"y" long:"yesterday" description:"Log time yesterday. Has precedence over -d."`
	Day       string `short:"d" long:"day" description:"Day, in the format 'yyyy-mm-dd'"`
	jc        *JiraClient
}

var logCommand LogCommand

type TimeLog struct {
	Key     string
	Date    time.Time
	Seconds int
}

func (tl TimeLog) String() string {
	return fmt.Sprintf("%s : %s", tl.Key, prettySeconds(tl.Seconds))
}

func prettySeconds(seconds int) string {
	t, _ := time.ParseDuration(fmt.Sprintf("%ds", seconds))
	return fmt.Sprint(t)

}

type Period struct {
	Begin time.Time
	End   time.Time
}
type TimeLogMap map[time.Time][]TimeLog
type TimeSlice []time.Time

func (ts TimeSlice) Len() int {
	return len(ts)
}

func (ts TimeSlice) Swap(i, j int) {
	ts[i], ts[j] = ts[j], ts[i]
}

func (ts TimeSlice) Less(i, j int) bool {
	return ts[i].Before(ts[j])
}

func (tlm TimeLogMap) GetSortedKeys() []time.Time {
	times := make(TimeSlice, 0)
	for k, _ := range tlm {
		times = append(times, k)
	}
	sort.Sort(times)
	return times
}

func (tlm TimeLogMap) SumForKey(k time.Time) int {
	seconds := 0
	for _, v := range tlm[k] {
		seconds += v.Seconds
	}
	return seconds
}

func (tlm TimeLogMap) SumForMap() int {
	seconds := 0
	for k, _ := range tlm {
		seconds += tlm.SumForKey(k)
	}
	return seconds
}

func (lc *LogCommand) GetTimeLog(targetAuthor string, period Period, issue *Issue) error {
	issuestring := ""
	if issue != nil {
		issuestring = fmt.Sprintf(" AND key = '%s'", issue.Key)
	}
	issues, err := lc.jc.Search(&SearchOptions{JQL: fmt.Sprintf("timespent > 0 AND updated >= '%s' and project = '%s'%s", period.Begin.Format("2006-01-02"), options.Project, issuestring)})
	if err != nil {
		return err
	}

	logs_for_times := TimeLogMap{}
	logchan := make(chan TimeLogMap)
	for _, issue := range issues {
		go func(issue *Issue) {
			logs_for_times := TimeLogMap{}
			url := fmt.Sprintf("https://%s/rest/api/2/issue/%s/worklog", options.Server, issue.Key)
			resp, _ := lc.jc.Get(url)
			worklog, _ := JsonToInterface(resp.Body)
			logs_json, _ := jsonWalker("worklogs", worklog)
			logs, ok := logs_json.([]interface{})
			if ok {
				for _, log := range logs {
					//We got good json and it's by our user
					authorjson, _ := jsonWalker("author/name", log)
					if author, ok := authorjson.(string); ok && (author == targetAuthor || targetAuthor == "") {
						dsjson, _ := jsonWalker("started", log)
						if date_string, ok := dsjson.(string); ok {
							//"2013-11-08T11:37:03.000-0500" <-- date format
							precise_time, _ := time.Parse(JIRA_TIME_FORMAT, date_string)
							if precise_time.After(period.Begin) && precise_time.Before(period.End) {
								date := time.Date(precise_time.Year(), precise_time.Month(), precise_time.Day(), 0, 0, 0, 0, precise_time.Location())
								secondsjson, _ := jsonWalker("timeSpentSeconds", log)
								seconds := int(secondsjson.(float64))
								if _, ok := logs_for_times[date]; !ok {
									logs_for_times[date] = make([]TimeLog, 0)
								}
								logs_for_times[date] = append(logs_for_times[date], TimeLog{issue.Key, date, seconds})
							}
						}
					}
				}
				logchan <- logs_for_times
			}
		}(issue)
	}

	for _, _ = range issues {
		tlm := <-logchan
		for k, v := range tlm {
			logs_for_times[k] = append(logs_for_times[k], v...)
		}
		fmt.Print(".")
	}
	fmt.Print("\n")

	for _, l := range logs_for_times.GetSortedKeys() {
		fmt.Println(l)
		for _, singlelog := range logs_for_times[l] {
			fmt.Println(singlelog)
		}
		fmt.Println(fmt.Sprintf("Total for day: %s", prettySeconds(logs_for_times.SumForKey(l))))
	}
	fmt.Println(fmt.Sprintf("Total for period: %s", prettySeconds(logs_for_times.SumForMap())))
	return nil
}

func (lc *LogCommand) Execute(args []string) error {
	jc := NewJiraClient(options)
	lc.jc = jc
	n := time.Now()
	if d, err := time.Parse("2006-01-02", lc.Day); err == nil {
		d = time.Date(d.Year(), d.Month(), d.Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond(), time.Local)
		n = d
	}
	if lc.MyLog || len(args) < 2 {
		author := ""
		if lc.MyLog {
			author = options.User
		}
		if lc.Author != "" {
			author = lc.Author
		}
		var issue *Issue = nil
		if len(args) > 0 {
			issue = &Issue{Key: args[0]}
		}

		beg := time.Date(n.Year(), n.Month(), n.Day()-int(n.Weekday()), 0, 0, 0, 0, n.Location())
		end := time.Date(n.Year(), n.Month(), n.Day()-int(n.Weekday())+6, 0, 0, 0, 0, n.Location())
		fmt.Println(fmt.Sprintf("Time sheet for week starting on %v and ending on %v", beg, end))
		err := lc.GetTimeLog(author, Period{beg, end}, issue)
		if err != nil {
			return err
		}
	} else {
		key := args[0]
		timeSpent := strings.Join(args[1:], " ")
		started := n
		if lc.Yesterday {
			started = time.Unix(n.Unix()-SECONDS_IN_A_DAY, 0)
		}
		postdata, _ := json.Marshal(map[string]string{"timeSpent": timeSpent, "started": started.Format(JIRA_TIME_FORMAT)})

		url := fmt.Sprintf("https://%s/rest/api/2/issue/%s/worklog", options.Server, key)
		resp, err := jc.Post(url, "application/json", bytes.NewBuffer(postdata))
		if err != nil {
			panic(err)
		}
		if resp.StatusCode == 201 {
			log.Println("Log successful")
		} else {
			if options.Verbose {
				log.Println(resp)
				s, _ := ioutil.ReadAll(resp.Body)
				log.Println(string(s))
			}
			log.Println("Log Failed!")
		}
	}
	return nil
}

const JIRA_TIME_FORMAT = "2006-01-02T15:04:05.000-0700"
const SECONDS_IN_A_DAY = 24 * 60 * 60
<commit_msg>Added --comment flag<commit_after>package main

import (
	"bytes"
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"sort"
	"strings"
	"time"
)

func init() {
	parser.AddCommand("log",
		"Manipulate time log",
		"The log command permits the listing and logging of time on specific stories.",
		&logCommand)

}

type LogCommand struct {
	MyLog     bool   `short:"m" long:"mine" description:"Show my log for current sprint" default:"true" group:"Application Options"`
	Author    string `short:"a" long:"author" description:"Show log for given author"`
	Yesterday bool   `short:"y" long:"yesterday" description:"Log time yesterday. Has precedence over -d."`
	Day       string `short:"d" long:"day" description:"Day, in the format 'yyyy-mm-dd'"`
	Comment   string `short:"c" long:"comment" description:"Comment for the worklog"`
	jc        *JiraClient
}

var logCommand LogCommand

type TimeLog struct {
	Key     string
	Date    time.Time
	Seconds int
}

func (tl TimeLog) String() string {
	return fmt.Sprintf("%s : %s", tl.Key, prettySeconds(tl.Seconds))
}

func prettySeconds(seconds int) string {
	t, _ := time.ParseDuration(fmt.Sprintf("%ds", seconds))
	return fmt.Sprint(t)

}

type Period struct {
	Begin time.Time
	End   time.Time
}
type TimeLogMap map[time.Time][]TimeLog
type TimeSlice []time.Time

func (ts TimeSlice) Len() int {
	return len(ts)
}

func (ts TimeSlice) Swap(i, j int) {
	ts[i], ts[j] = ts[j], ts[i]
}

func (ts TimeSlice) Less(i, j int) bool {
	return ts[i].Before(ts[j])
}

func (tlm TimeLogMap) GetSortedKeys() []time.Time {
	times := make(TimeSlice, 0)
	for k, _ := range tlm {
		times = append(times, k)
	}
	sort.Sort(times)
	return times
}

func (tlm TimeLogMap) SumForKey(k time.Time) int {
	seconds := 0
	for _, v := range tlm[k] {
		seconds += v.Seconds
	}
	return seconds
}

func (tlm TimeLogMap) SumForMap() int {
	seconds := 0
	for k, _ := range tlm {
		seconds += tlm.SumForKey(k)
	}
	return seconds
}

func (lc *LogCommand) GetTimeLog(targetAuthor string, period Period, issue *Issue) error {
	issuestring := ""
	if issue != nil {
		issuestring = fmt.Sprintf(" AND key = '%s'", issue.Key)
	}
	issues, err := lc.jc.Search(&SearchOptions{JQL: fmt.Sprintf("timespent > 0 AND updated >= '%s' and project = '%s'%s", period.Begin.Format("2006-01-02"), options.Project, issuestring)})
	if err != nil {
		return err
	}

	logs_for_times := TimeLogMap{}
	logchan := make(chan TimeLogMap)
	for _, issue := range issues {
		go func(issue *Issue) {
			logs_for_times := TimeLogMap{}
			url := fmt.Sprintf("https://%s/rest/api/2/issue/%s/worklog", options.Server, issue.Key)
			resp, _ := lc.jc.Get(url)
			worklog, _ := JsonToInterface(resp.Body)
			logs_json, _ := jsonWalker("worklogs", worklog)
			logs, ok := logs_json.([]interface{})
			if ok {
				for _, log := range logs {
					//We got good json and it's by our user
					authorjson, _ := jsonWalker("author/name", log)
					if author, ok := authorjson.(string); ok && (author == targetAuthor || targetAuthor == "") {
						dsjson, _ := jsonWalker("started", log)
						if date_string, ok := dsjson.(string); ok {
							//"2013-11-08T11:37:03.000-0500" <-- date format
							precise_time, _ := time.Parse(JIRA_TIME_FORMAT, date_string)
							if precise_time.After(period.Begin) && precise_time.Before(period.End) {
								date := time.Date(precise_time.Year(), precise_time.Month(), precise_time.Day(), 0, 0, 0, 0, precise_time.Location())
								secondsjson, _ := jsonWalker("timeSpentSeconds", log)
								seconds := int(secondsjson.(float64))
								if _, ok := logs_for_times[date]; !ok {
									logs_for_times[date] = make([]TimeLog, 0)
								}
								logs_for_times[date] = append(logs_for_times[date], TimeLog{issue.Key, date, seconds})
							}
						}
					}
				}
				logchan <- logs_for_times
			}
		}(issue)
	}

	for _, _ = range issues {
		tlm := <-logchan
		for k, v := range tlm {
			logs_for_times[k] = append(logs_for_times[k], v...)
		}
		fmt.Print(".")
	}
	fmt.Print("\n")

	for _, l := range logs_for_times.GetSortedKeys() {
		fmt.Println(l)
		for _, singlelog := range logs_for_times[l] {
			fmt.Println(singlelog)
		}
		fmt.Println(fmt.Sprintf("Total for day: %s", prettySeconds(logs_for_times.SumForKey(l))))
	}
	fmt.Println(fmt.Sprintf("Total for period: %s", prettySeconds(logs_for_times.SumForMap())))
	return nil
}

func (lc *LogCommand) Execute(args []string) error {
	jc := NewJiraClient(options)
	lc.jc = jc
	n := time.Now()
	if d, err := time.Parse("2006-01-02", lc.Day); err == nil {
		d = time.Date(d.Year(), d.Month(), d.Day(), time.Now().Hour(), time.Now().Minute(), time.Now().Second(), time.Now().Nanosecond(), time.Local)
		n = d
	}
	if lc.MyLog || len(args) < 2 {
		author := ""
		if lc.MyLog {
			author = options.User
		}
		if lc.Author != "" {
			author = lc.Author
		}
		var issue *Issue = nil
		if len(args) > 0 {
			issue = &Issue{Key: args[0]}
		}

		beg := time.Date(n.Year(), n.Month(), n.Day()-int(n.Weekday()), 0, 0, 0, 0, n.Location())
		end := time.Date(n.Year(), n.Month(), n.Day()-int(n.Weekday())+6, 0, 0, 0, 0, n.Location())
		fmt.Println(fmt.Sprintf("Time sheet for week starting on %v and ending on %v", beg, end))
		err := lc.GetTimeLog(author, Period{beg, end}, issue)
		if err != nil {
			return err
		}
	} else {
		key := args[0]
		timeSpent := strings.Join(args[1:], " ")
		started := n
		if lc.Yesterday {
			started = time.Unix(n.Unix()-SECONDS_IN_A_DAY, 0)
		}
		data := map[string]string{"timeSpent": timeSpent, "started": started.Format(JIRA_TIME_FORMAT), "comment": lc.Comment}
		postdata, _ := json.Marshal(data)

		url := fmt.Sprintf("https://%s/rest/api/2/issue/%s/worklog", options.Server, key)
		resp, err := jc.Post(url, "application/json", bytes.NewBuffer(postdata))
		if err != nil {
			panic(err)
		}
		if resp.StatusCode == 201 {
			log.Println("Log successful")
		} else {
			if options.Verbose {
				log.Println(resp)
				s, _ := ioutil.ReadAll(resp.Body)
				log.Println(string(s))
			}
			log.Println("Log Failed!")
		}
	}
	return nil
}

const JIRA_TIME_FORMAT = "2006-01-02T15:04:05.000-0700"
const SECONDS_IN_A_DAY = 24 * 60 * 60
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
)

type actionCmd struct {
	action         shared.ContainerAction
	hasTimeout     bool
	visible        bool
	name           string
	timeout        int
	force          bool
	stateful       bool
	stateless      bool
	additionalHelp string
}

func (c *actionCmd) showByDefault() bool {
	return c.visible
}

func (c *actionCmd) usage() string {
	if c.additionalHelp != "" {
		c.additionalHelp = fmt.Sprintf("\n\n%s", c.additionalHelp)
	}

	return fmt.Sprintf(i18n.G(
		`Change state of one or more containers to %s.

lxc %s [<remote>:]<container> [[<remote>:]<container>...]%s`), c.name, c.name, c.additionalHelp)
}

func (c *actionCmd) flags() {
	if c.hasTimeout {
		gnuflag.IntVar(&c.timeout, "timeout", -1, i18n.G("Time to wait for the container before killing it"))
		gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the container to shutdown"))
		gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the container to shutdown"))
	}
	gnuflag.BoolVar(&c.stateful, "stateful", false, i18n.G("Store the container state (only for stop)"))
	gnuflag.BoolVar(&c.stateless, "stateless", false, i18n.G("Ignore the container state (only for start)"))
}

func (c *actionCmd) doAction(config *lxd.Config, nameArg string) error {
	state := false

	// Only store state if asked to
	if c.action == "stop" && c.stateful {
		state = true
	}

	remote, name := config.ParseRemoteAndContainer(nameArg)
	d, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	if name == "" {
		return fmt.Errorf(i18n.G("Must supply container name for: ")+"\"%s\"", nameArg)
	}

	if c.action == shared.Start {
		current, err := d.ContainerInfo(name)
		if err != nil {
			return err
		}

		// "start" for a frozen container means "unfreeze"
		if current.StatusCode == api.Frozen {
			c.action = shared.Unfreeze
		}

		// Always restore state (if present) unless asked not to
		if c.action == shared.Start && current.Stateful && !c.stateless {
			state = true
		}
	}

	resp, err := d.Action(name, c.action, c.timeout, c.force, state)
	if err != nil {
		return err
	}

	if resp.Type != api.AsyncResponse {
		return fmt.Errorf(i18n.G("bad result type from action"))
	}

	if err := d.WaitForSuccess(resp.Operation); err != nil {
		return fmt.Errorf("%s\n"+i18n.G("Try `lxc info --show-log %s` for more info"), err, nameArg)
	}

	return nil
}

func (c *actionCmd) run(config *lxd.Config, args []string) error {
	if len(args) == 0 {
		return errArgs
	}

	// Run the action for every listed container
	results := runBatch(args, func(name string) error { return c.doAction(config, name) })

	// Single container is easy
	if len(results) == 1 {
		return results[0].err
	}

	// Do fancier rendering for batches
	success := true

	for _, result := range results {
		if result.err == nil {
			continue
		}

		success = false
		msg := fmt.Sprintf(i18n.G("error: %v"), result.err)
		for _, line := range strings.Split(msg, "\n") {
			fmt.Fprintln(os.Stderr, fmt.Sprintf("%s: %s", result.name, line))
		}
	}

	if !success {
		fmt.Fprintln(os.Stderr, "")
		return fmt.Errorf(i18n.G("Some containers failed to %s"), c.name)
	}

	return nil
}
<commit_msg>lxc/action: Rework usage to be parsable by help2man<commit_after>package main

import (
	"fmt"
	"os"
	"strings"

	"github.com/lxc/lxd"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/gnuflag"
	"github.com/lxc/lxd/shared/i18n"
)

type actionCmd struct {
	action      shared.ContainerAction
	description string
	hasTimeout  bool
	visible     bool
	name        string
	timeout     int
	force       bool
	stateful    bool
	stateless   bool
}

func (c *actionCmd) showByDefault() bool {
	return c.visible
}

func (c *actionCmd) usage() string {
	extra := ""
	if c.name == "pause" {
		extra = "\n" + i18n.G("The opposite of \"lxc pause\" is \"lxc start\".")
	}

	return fmt.Sprintf(i18n.G(
		`Usage: lxc %s [<remote>:]<container> [[<remote>:]<container>...]

%s%s`), c.name, c.description, extra)
}

func (c *actionCmd) flags() {
	if c.hasTimeout {
		gnuflag.IntVar(&c.timeout, "timeout", -1, i18n.G("Time to wait for the container before killing it"))
		gnuflag.BoolVar(&c.force, "f", false, i18n.G("Force the container to shutdown"))
		gnuflag.BoolVar(&c.force, "force", false, i18n.G("Force the container to shutdown"))
	}
	gnuflag.BoolVar(&c.stateful, "stateful", false, i18n.G("Store the container state (only for stop)"))
	gnuflag.BoolVar(&c.stateless, "stateless", false, i18n.G("Ignore the container state (only for start)"))
}

func (c *actionCmd) doAction(config *lxd.Config, nameArg string) error {
	state := false

	// Only store state if asked to
	if c.action == "stop" && c.stateful {
		state = true
	}

	remote, name := config.ParseRemoteAndContainer(nameArg)
	d, err := lxd.NewClient(config, remote)
	if err != nil {
		return err
	}

	if name == "" {
		return fmt.Errorf(i18n.G("Must supply container name for: ")+"\"%s\"", nameArg)
	}

	if c.action == shared.Start {
		current, err := d.ContainerInfo(name)
		if err != nil {
			return err
		}

		// "start" for a frozen container means "unfreeze"
		if current.StatusCode == api.Frozen {
			c.action = shared.Unfreeze
		}

		// Always restore state (if present) unless asked not to
		if c.action == shared.Start && current.Stateful && !c.stateless {
			state = true
		}
	}

	resp, err := d.Action(name, c.action, c.timeout, c.force, state)
	if err != nil {
		return err
	}

	if resp.Type != api.AsyncResponse {
		return fmt.Errorf(i18n.G("bad result type from action"))
	}

	if err := d.WaitForSuccess(resp.Operation); err != nil {
		return fmt.Errorf("%s\n"+i18n.G("Try `lxc info --show-log %s` for more info"), err, nameArg)
	}

	return nil
}

func (c *actionCmd) run(config *lxd.Config, args []string) error {
	if len(args) == 0 {
		return errArgs
	}

	// Run the action for every listed container
	results := runBatch(args, func(name string) error { return c.doAction(config, name) })

	// Single container is easy
	if len(results) == 1 {
		return results[0].err
	}

	// Do fancier rendering for batches
	success := true

	for _, result := range results {
		if result.err == nil {
			continue
		}

		success = false
		msg := fmt.Sprintf(i18n.G("error: %v"), result.err)
		for _, line := range strings.Split(msg, "\n") {
			fmt.Fprintln(os.Stderr, fmt.Sprintf("%s: %s", result.name, line))
		}
	}

	if !success {
		fmt.Fprintln(os.Stderr, "")
		return fmt.Errorf(i18n.G("Some containers failed to %s"), c.name)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"time"

	"context"

	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/lxd/task"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/idmap"
	"github.com/lxc/lxd/shared/instancewriter"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
)

// Create a new backup.
func backupCreate(s *state.State, args db.InstanceBackup, sourceInst instance.Instance) error {
	logger := logging.AddContext(logger.Log, log.Ctx{"project": sourceInst.Project(), "instance": sourceInst.Name(), "name": args.Name})
	logger.Debug("Instance backup started")
	defer logger.Debug("Instance backup finished")

	revert := revert.New()
	defer revert.Fail()

	// Get storage pool.
	pool, err := storagePools.GetPoolByInstance(s, sourceInst)
	if err != nil {
		return errors.Wrap(err, "Load instance storage pool")
	}

	// Ignore requests for optimized backups when pool driver doesn't support it.
	if args.OptimizedStorage && !pool.Driver().Info().OptimizedBackups {
		args.OptimizedStorage = false
	}

	// Create the database entry.
	err = s.Cluster.CreateInstanceBackup(args)
	if err != nil {
		if err == db.ErrAlreadyDefined {
			return fmt.Errorf("Backup %q already exists", args.Name)
		}

		return errors.Wrap(err, "Insert backup info into database")
	}

	revert.Add(func() { s.Cluster.DeleteInstanceBackup(args.Name) })

	// Get the backup struct.
	b, err := instance.BackupLoadByName(s, sourceInst.Project(), args.Name)
	if err != nil {
		return errors.Wrap(err, "Load backup object")
	}

	// Detect compression method.
	var compress string
	b.SetCompressionAlgorithm(args.CompressionAlgorithm)
	if b.CompressionAlgorithm() != "" {
		compress = b.CompressionAlgorithm()
	} else {
		compress, err = cluster.ConfigGetString(s.Cluster, "backups.compression_algorithm")
		if err != nil {
			return err
		}
	}

	// Create the target path if needed.
	backupsPath := shared.VarPath("backups", "instances", project.Instance(sourceInst.Project(), sourceInst.Name()))
	if !shared.PathExists(backupsPath) {
		err := os.MkdirAll(backupsPath, 0700)
		if err != nil {
			return err
		}

		revert.Add(func() { os.Remove(backupsPath) })
	}

	target := shared.VarPath("backups", "instances", project.Instance(sourceInst.Project(), b.Name()))

	// Setup the tarball writer.
	logger.Debug("Opening backup tarball for writing", log.Ctx{"path": target})
	tarFileWriter, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		return errors.Wrapf(err, "Error opening backup tarball for writing %q", target)
	}
	defer tarFileWriter.Close()
	revert.Add(func() { os.Remove(target) })

	// Get IDMap to unshift container as the tarball is created.
	var idmap *idmap.IdmapSet
	if sourceInst.Type() == instancetype.Container {
		c := sourceInst.(instance.Container)
		idmap, err = c.DiskIdmap()
		if err != nil {
			return errors.Wrap(err, "Error getting container IDMAP")
		}
	}

	// Create the tarball.
	tarPipeReader, tarPipeWriter := io.Pipe()
	defer tarPipeWriter.Close() // Ensure that go routine below always ends.
	tarWriter := instancewriter.NewInstanceTarWriter(tarPipeWriter, idmap)

	// Setup tar writer go routine, with optional compression.
	tarWriterRes := make(chan error, 0)
	var compressErr error

	go func(resCh chan<- error) {
		logger.Debug("Started backup tarball writer")
		defer logger.Debug("Finished backup tarball writer")
		if compress != "none" {
			compressErr = compressFile(compress, tarPipeReader, tarFileWriter)

			// If a compression error occurred, close the tarPipeWriter to end the export.
			if compressErr != nil {
				tarPipeWriter.Close()
			}
		} else {
			_, err = io.Copy(tarFileWriter, tarPipeReader)
		}
		resCh <- err
	}(tarWriterRes)

	// Write index file.
	logger.Debug("Adding backup index file")
	err = backupWriteIndex(sourceInst, pool, b.OptimizedStorage(), !b.InstanceOnly(), tarWriter)

	// Check compression errors.
	if compressErr != nil {
		return compressErr
	}

	// Check backupWriteIndex for errors.
	if err != nil {
		return errors.Wrapf(err, "Error writing backup index file")
	}

	err = pool.BackupInstance(sourceInst, tarWriter, b.OptimizedStorage(), !b.InstanceOnly(), nil)
	if err != nil {
		return errors.Wrap(err, "Backup create")
	}

	// Close off the tarball file.
	err = tarWriter.Close()
	if err != nil {
		return errors.Wrap(err, "Error closing tarball writer")
	}

	// Close off the tarball pipe writer (this will end the go routine above).
	err = tarPipeWriter.Close()
	if err != nil {
		return errors.Wrap(err, "Error closing tarball pipe writer")
	}

	err = <-tarWriterRes
	if err != nil {
		return errors.Wrap(err, "Error writing tarball")
	}

	revert.Success()
	return nil
}

// backupWriteIndex generates an index.yaml file and then writes it to the root of the backup tarball.
func backupWriteIndex(sourceInst instance.Instance, pool storagePools.Pool, optimized bool, snapshots bool, tarWriter *instancewriter.InstanceTarWriter) error {
	// Indicate whether the driver will include a driver-specific optimized header.
	poolDriverOptimizedHeader := false
	if optimized {
		poolDriverOptimizedHeader = pool.Driver().Info().OptimizedBackupHeader
	}

	indexInfo := backup.Info{
		Name:             sourceInst.Name(),
		Pool:             pool.Name(),
		Snapshots:        []string{},
		Backend:          pool.Driver().Info().Name,
		Type:             api.InstanceType(sourceInst.Type().String()),
		OptimizedStorage: &optimized,
		OptimizedHeader:  &poolDriverOptimizedHeader,
	}

	if snapshots {
		snaps, err := sourceInst.Snapshots()
		if err != nil {
			return err
		}

		for _, snap := range snaps {
			_, snapName, _ := shared.InstanceGetParentAndSnapshotName(snap.Name())
			indexInfo.Snapshots = append(indexInfo.Snapshots, snapName)
		}
	}

	// Convert to YAML.
	indexData, err := yaml.Marshal(&indexInfo)
	if err != nil {
		return err
	}
	r := bytes.NewReader(indexData)

	indexFileInfo := instancewriter.FileInfo{
		FileName:    "backup/index.yaml",
		FileSize:    int64(len(indexData)),
		FileMode:    0644,
		FileModTime: time.Now(),
	}

	// Write to tarball.
	err = tarWriter.WriteFileFromReader(r, &indexFileInfo)
	if err != nil {
		return err
	}

	return nil
}

func pruneExpiredContainerBackupsTask(d *Daemon) (task.Func, task.Schedule) {
	f := func(ctx context.Context) {
		opRun := func(op *operations.Operation) error {
			return pruneExpiredContainerBackups(ctx, d)
		}

		op, err := operations.OperationCreate(d.State(), "", operations.OperationClassTask, db.OperationBackupsExpire, nil, nil, opRun, nil, nil)
		if err != nil {
			logger.Error("Failed to start expired instance backups operation", log.Ctx{"err": err})
			return
		}

		logger.Info("Pruning expired instance backups")
		_, err = op.Run()
		if err != nil {
			logger.Error("Failed to expire instance backups", log.Ctx{"err": err})
		}
		logger.Info("Done pruning expired instance backups")
	}

	f(context.Background())

	first := true
	schedule := func() (time.Duration, error) {
		interval := time.Hour

		if first {
			first = false
			return interval, task.ErrSkip
		}

		return interval, nil
	}

	return f, schedule
}

func pruneExpiredContainerBackups(ctx context.Context, d *Daemon) error {
	// Get the list of expired backups.
	backups, err := d.cluster.GetExpiredInstanceBackups()
	if err != nil {
		return errors.Wrap(err, "Unable to retrieve the list of expired instance backups")
	}

	for _, b := range backups {
		inst, err := instance.LoadByID(d.State(), b.InstanceID)
		if err != nil {
			return errors.Wrapf(err, "Error deleting instance backup %s", b.Name)
		}

		err = backup.DoBackupDelete(d.State(), inst.Project(), b.Name, inst.Name())
		if err != nil {
			return errors.Wrapf(err, "Error deleting instance backup %s", b.Name)
		}
	}

	return nil
}
<commit_msg>lxd/backup: Changes pruneExpiredContainerBackups to use InstanceBackup.Delete() function<commit_after>package main

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"time"

	"context"

	"github.com/pkg/errors"
	"gopkg.in/yaml.v2"

	"github.com/lxc/lxd/lxd/backup"
	"github.com/lxc/lxd/lxd/cluster"
	"github.com/lxc/lxd/lxd/db"
	"github.com/lxc/lxd/lxd/instance"
	"github.com/lxc/lxd/lxd/instance/instancetype"
	"github.com/lxc/lxd/lxd/operations"
	"github.com/lxc/lxd/lxd/project"
	"github.com/lxc/lxd/lxd/revert"
	"github.com/lxc/lxd/lxd/state"
	storagePools "github.com/lxc/lxd/lxd/storage"
	"github.com/lxc/lxd/lxd/task"
	"github.com/lxc/lxd/shared"
	"github.com/lxc/lxd/shared/api"
	"github.com/lxc/lxd/shared/idmap"
	"github.com/lxc/lxd/shared/instancewriter"
	log "github.com/lxc/lxd/shared/log15"
	"github.com/lxc/lxd/shared/logger"
	"github.com/lxc/lxd/shared/logging"
)

// Create a new backup.
func backupCreate(s *state.State, args db.InstanceBackup, sourceInst instance.Instance) error {
	logger := logging.AddContext(logger.Log, log.Ctx{"project": sourceInst.Project(), "instance": sourceInst.Name(), "name": args.Name})
	logger.Debug("Instance backup started")
	defer logger.Debug("Instance backup finished")

	revert := revert.New()
	defer revert.Fail()

	// Get storage pool.
	pool, err := storagePools.GetPoolByInstance(s, sourceInst)
	if err != nil {
		return errors.Wrap(err, "Load instance storage pool")
	}

	// Ignore requests for optimized backups when pool driver doesn't support it.
	if args.OptimizedStorage && !pool.Driver().Info().OptimizedBackups {
		args.OptimizedStorage = false
	}

	// Create the database entry.
	err = s.Cluster.CreateInstanceBackup(args)
	if err != nil {
		if err == db.ErrAlreadyDefined {
			return fmt.Errorf("Backup %q already exists", args.Name)
		}

		return errors.Wrap(err, "Insert backup info into database")
	}

	revert.Add(func() { s.Cluster.DeleteInstanceBackup(args.Name) })

	// Get the backup struct.
	b, err := instance.BackupLoadByName(s, sourceInst.Project(), args.Name)
	if err != nil {
		return errors.Wrap(err, "Load backup object")
	}

	// Detect compression method.
	var compress string
	b.SetCompressionAlgorithm(args.CompressionAlgorithm)
	if b.CompressionAlgorithm() != "" {
		compress = b.CompressionAlgorithm()
	} else {
		compress, err = cluster.ConfigGetString(s.Cluster, "backups.compression_algorithm")
		if err != nil {
			return err
		}
	}

	// Create the target path if needed.
	backupsPath := shared.VarPath("backups", "instances", project.Instance(sourceInst.Project(), sourceInst.Name()))
	if !shared.PathExists(backupsPath) {
		err := os.MkdirAll(backupsPath, 0700)
		if err != nil {
			return err
		}

		revert.Add(func() { os.Remove(backupsPath) })
	}

	target := shared.VarPath("backups", "instances", project.Instance(sourceInst.Project(), b.Name()))

	// Setup the tarball writer.
	logger.Debug("Opening backup tarball for writing", log.Ctx{"path": target})
	tarFileWriter, err := os.OpenFile(target, os.O_CREATE|os.O_WRONLY, 0600)
	if err != nil {
		return errors.Wrapf(err, "Error opening backup tarball for writing %q", target)
	}
	defer tarFileWriter.Close()
	revert.Add(func() { os.Remove(target) })

	// Get IDMap to unshift container as the tarball is created.
	var idmap *idmap.IdmapSet
	if sourceInst.Type() == instancetype.Container {
		c := sourceInst.(instance.Container)
		idmap, err = c.DiskIdmap()
		if err != nil {
			return errors.Wrap(err, "Error getting container IDMAP")
		}
	}

	// Create the tarball.
	tarPipeReader, tarPipeWriter := io.Pipe()
	defer tarPipeWriter.Close() // Ensure that go routine below always ends.
	tarWriter := instancewriter.NewInstanceTarWriter(tarPipeWriter, idmap)

	// Setup tar writer go routine, with optional compression.
	tarWriterRes := make(chan error, 0)
	var compressErr error

	go func(resCh chan<- error) {
		logger.Debug("Started backup tarball writer")
		defer logger.Debug("Finished backup tarball writer")
		if compress != "none" {
			compressErr = compressFile(compress, tarPipeReader, tarFileWriter)

			// If a compression error occurred, close the tarPipeWriter to end the export.
			if compressErr != nil {
				tarPipeWriter.Close()
			}
		} else {
			_, err = io.Copy(tarFileWriter, tarPipeReader)
		}
		resCh <- err
	}(tarWriterRes)

	// Write index file.
	logger.Debug("Adding backup index file")
	err = backupWriteIndex(sourceInst, pool, b.OptimizedStorage(), !b.InstanceOnly(), tarWriter)

	// Check compression errors.
	if compressErr != nil {
		return compressErr
	}

	// Check backupWriteIndex for errors.
	if err != nil {
		return errors.Wrapf(err, "Error writing backup index file")
	}

	err = pool.BackupInstance(sourceInst, tarWriter, b.OptimizedStorage(), !b.InstanceOnly(), nil)
	if err != nil {
		return errors.Wrap(err, "Backup create")
	}

	// Close off the tarball file.
	err = tarWriter.Close()
	if err != nil {
		return errors.Wrap(err, "Error closing tarball writer")
	}

	// Close off the tarball pipe writer (this will end the go routine above).
	err = tarPipeWriter.Close()
	if err != nil {
		return errors.Wrap(err, "Error closing tarball pipe writer")
	}

	err = <-tarWriterRes
	if err != nil {
		return errors.Wrap(err, "Error writing tarball")
	}

	revert.Success()
	return nil
}

// backupWriteIndex generates an index.yaml file and then writes it to the root of the backup tarball.
func backupWriteIndex(sourceInst instance.Instance, pool storagePools.Pool, optimized bool, snapshots bool, tarWriter *instancewriter.InstanceTarWriter) error {
	// Indicate whether the driver will include a driver-specific optimized header.
	poolDriverOptimizedHeader := false
	if optimized {
		poolDriverOptimizedHeader = pool.Driver().Info().OptimizedBackupHeader
	}

	indexInfo := backup.Info{
		Name:             sourceInst.Name(),
		Pool:             pool.Name(),
		Snapshots:        []string{},
		Backend:          pool.Driver().Info().Name,
		Type:             api.InstanceType(sourceInst.Type().String()),
		OptimizedStorage: &optimized,
		OptimizedHeader:  &poolDriverOptimizedHeader,
	}

	if snapshots {
		snaps, err := sourceInst.Snapshots()
		if err != nil {
			return err
		}

		for _, snap := range snaps {
			_, snapName, _ := shared.InstanceGetParentAndSnapshotName(snap.Name())
			indexInfo.Snapshots = append(indexInfo.Snapshots, snapName)
		}
	}

	// Convert to YAML.
	indexData, err := yaml.Marshal(&indexInfo)
	if err != nil {
		return err
	}
	r := bytes.NewReader(indexData)

	indexFileInfo := instancewriter.FileInfo{
		FileName:    "backup/index.yaml",
		FileSize:    int64(len(indexData)),
		FileMode:    0644,
		FileModTime: time.Now(),
	}

	// Write to tarball.
	err = tarWriter.WriteFileFromReader(r, &indexFileInfo)
	if err != nil {
		return err
	}

	return nil
}

func pruneExpiredContainerBackupsTask(d *Daemon) (task.Func, task.Schedule) {
	f := func(ctx context.Context) {
		opRun := func(op *operations.Operation) error {
			return pruneExpiredContainerBackups(ctx, d)
		}

		op, err := operations.OperationCreate(d.State(), "", operations.OperationClassTask, db.OperationBackupsExpire, nil, nil, opRun, nil, nil)
		if err != nil {
			logger.Error("Failed to start expired instance backups operation", log.Ctx{"err": err})
			return
		}

		logger.Info("Pruning expired instance backups")
		_, err = op.Run()
		if err != nil {
			logger.Error("Failed to expire instance backups", log.Ctx{"err": err})
		}
		logger.Info("Done pruning expired instance backups")
	}

	f(context.Background())

	first := true
	schedule := func() (time.Duration, error) {
		interval := time.Hour

		if first {
			first = false
			return interval, task.ErrSkip
		}

		return interval, nil
	}

	return f, schedule
}

func pruneExpiredContainerBackups(ctx context.Context, d *Daemon) error {
	// Get the list of expired backups.
	backups, err := d.cluster.GetExpiredInstanceBackups()
	if err != nil {
		return errors.Wrap(err, "Unable to retrieve the list of expired instance backups")
	}

	for _, b := range backups {
		inst, err := instance.LoadByID(d.State(), b.InstanceID)
		if err != nil {
			return errors.Wrapf(err, "Error loading instance for deleting backup %q", b.Name)
		}

		instBackup := backup.NewInstanceBackup(d.State(), inst, b.ID, b.Name, b.CreationDate, b.ExpiryDate, b.InstanceOnly, b.OptimizedStorage)
		err = instBackup.Delete()
		if err != nil {
			return errors.Wrapf(err, "Error deleting instance backup %q", b.Name)
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright 2016 The Prometheus 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.

// Responsible for managing the ingester lifecycle.

package ring

import (
	"fmt"
	"math/rand"
	"net"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/prometheus/common/log"
)

const (
	infName           = "eth0"
	consulKey         = "ring"
	heartbeatInterval = 5 * time.Second
)

// IngesterRegistration manages the connection between the ingester and Consul.
type IngesterRegistration struct {
	consul    ConsulClient
	numTokens int

	id       string
	hostname string
	quit     chan struct{}
	wait     sync.WaitGroup
}

// RegisterIngester registers an ingester with Consul.
func RegisterIngester(consulClient ConsulClient, listenPort, numTokens int) (*IngesterRegistration, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	addr, err := getFirstAddressOf(infName)
	if err != nil {
		return nil, err
	}

	r := &IngesterRegistration{
		consul:    consulClient,
		numTokens: numTokens,

		id: hostname,
		// hostname is the ip+port of this instance, written to consul so
		// the distributors know where to connect.
		hostname: fmt.Sprintf("%s:%d", addr, listenPort),
		quit:     make(chan struct{}),
	}

	r.wait.Add(1)
	go r.loop()
	return r, nil
}

// Unregister removes ingester config from Consul; will block
// until we'll successfully unregistered.
func (r *IngesterRegistration) Unregister() {
	log.Info("Removing ingester from consul")

	// closing r.quit triggers loop() to exit, which in turn will trigger
	// the removal of out tokens.
	close(r.quit)
	r.wait.Wait()
}

func (r *IngesterRegistration) loop() {
	defer r.wait.Done()
	tokens := r.pickTokens()
	defer r.unregister(tokens)
	r.heartbeat(tokens)
}

func (r *IngesterRegistration) pickTokens() []uint32 {
	var tokens []uint32
	pickTokens := func(in interface{}) (out interface{}, retry bool, err error) {
		var ringDesc *Desc
		if in == nil {
			ringDesc = newDesc()
		} else {
			ringDesc = in.(*Desc)
		}

		takenTokens := []uint32{}
		for _, token := range ringDesc.Tokens {
			takenTokens = append(takenTokens, token.Token)
		}
		tokens = generateTokens(r.numTokens, takenTokens)

		ringDesc.addIngester(r.id, r.hostname, tokens)
		return ringDesc, true, nil
	}
	if err := r.consul.CAS(consulKey, descFactory, pickTokens); err != nil {
		log.Fatalf("Failed to pick tokens in consul: %v", err)
	}
	return tokens
}

func (r *IngesterRegistration) heartbeat(tokens []uint32) {
	heartbeat := func(in interface{}) (out interface{}, retry bool, err error) {
		ringDesc := &Desc{}
		if in != nil {
			ringDesc = in.(*Desc)
		}

		ingesterDesc, ok := ringDesc.Ingesters[r.id]
		if !ok {
			// consul must have restarted
			log.Infof("Found empty ring, inserting tokens!")
			ringDesc.addIngester(r.id, r.hostname, tokens)
		} else {
			ingesterDesc.Timestamp = time.Now()
			ringDesc.Ingesters[r.id] = ingesterDesc
		}

		return ringDesc, true, nil
	}
	ticker := time.NewTicker(heartbeatInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			log.Infof("Heartbeating to consul...")
			if err := r.consul.CAS(consulKey, descFactory, heartbeat); err != nil {
				log.Errorf("Failed to write to consul, sleeping: %v", err)
			}
		case <-r.quit:
			return
		}
	}
}

func (r *IngesterRegistration) unregister(tokens []uint32) {
	unregister := func(in interface{}) (out interface{}, retry bool, err error) {
		if in == nil {
			log.Error("Found empty ring when trying to unregister!")
			return nil, false, nil
		}

		ringDesc := in.(*Desc)
		ringDesc.removeIngester(r.id, tokens)
		return ringDesc, true, nil
	}
	if err := r.consul.CAS(consulKey, descFactory, unregister); err != nil {
		log.Fatalf("Failed to unregister from consul: %v", err)
	}
}

type sortableUint32 []uint32

func (ts sortableUint32) Len() int           { return len(ts) }
func (ts sortableUint32) Swap(i, j int)      { ts[i], ts[j] = ts[j], ts[i] }
func (ts sortableUint32) Less(i, j int) bool { return ts[i] < ts[j] }

// generateTokens make numTokens random tokens, none of which clash
// with takenTokens.  Assumes takenTokens is sorted.
func generateTokens(numTokens int, takenTokens []uint32) []uint32 {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	tokens := sortableUint32{}
	for i := 0; i < numTokens; {
		candidate := r.Uint32()
		j := sort.Search(len(takenTokens), func(i int) bool {
			return takenTokens[i] >= candidate
		})
		if j < len(takenTokens) && takenTokens[j] == candidate {
			continue
		}
		tokens = append(tokens, candidate)
		i++
	}
	sort.Sort(tokens)
	return tokens
}

// getFirstAddressOf returns the first IPv4 address of the supplied interface name.
func getFirstAddressOf(name string) (string, error) {
	inf, err := net.InterfaceByName(name)
	if err != nil {
		return "", err
	}

	addrs, err := inf.Addrs()
	if err != nil {
		return "", err
	}
	if len(addrs) <= 0 {
		return "", fmt.Errorf("No address found for %s", name)
	}

	for _, addr := range addrs {
		switch v := addr.(type) {
		case *net.IPNet:
			if ip := v.IP.To4(); ip != nil {
				return v.IP.String(), nil
			}
		}
	}

	return "", fmt.Errorf("No address found for %s", name)
}
<commit_msg>Avoid panics and nil map assignments in the ingester_lifecycle<commit_after>// Copyright 2016 The Prometheus 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.

// Responsible for managing the ingester lifecycle.

package ring

import (
	"fmt"
	"math/rand"
	"net"
	"os"
	"sort"
	"sync"
	"time"

	"github.com/prometheus/common/log"
)

const (
	infName           = "eth0"
	consulKey         = "ring"
	heartbeatInterval = 5 * time.Second
)

// IngesterRegistration manages the connection between the ingester and Consul.
type IngesterRegistration struct {
	consul    ConsulClient
	numTokens int

	id       string
	hostname string
	quit     chan struct{}
	wait     sync.WaitGroup
}

// RegisterIngester registers an ingester with Consul.
func RegisterIngester(consulClient ConsulClient, listenPort, numTokens int) (*IngesterRegistration, error) {
	hostname, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	addr, err := getFirstAddressOf(infName)
	if err != nil {
		return nil, err
	}

	r := &IngesterRegistration{
		consul:    consulClient,
		numTokens: numTokens,

		id: hostname,
		// hostname is the ip+port of this instance, written to consul so
		// the distributors know where to connect.
		hostname: fmt.Sprintf("%s:%d", addr, listenPort),
		quit:     make(chan struct{}),
	}

	r.wait.Add(1)
	go r.loop()
	return r, nil
}

// Unregister removes ingester config from Consul; will block
// until we'll successfully unregistered.
func (r *IngesterRegistration) Unregister() {
	log.Info("Removing ingester from consul")

	// closing r.quit triggers loop() to exit, which in turn will trigger
	// the removal of out tokens.
	close(r.quit)
	r.wait.Wait()
}

func (r *IngesterRegistration) loop() {
	defer r.wait.Done()
	tokens := r.pickTokens()
	defer r.unregister(tokens)
	r.heartbeat(tokens)
}

func (r *IngesterRegistration) pickTokens() []uint32 {
	var tokens []uint32
	pickTokens := func(in interface{}) (out interface{}, retry bool, err error) {
		var ringDesc *Desc
		if in == nil {
			ringDesc = newDesc()
		} else {
			ringDesc = in.(*Desc)
		}

		takenTokens := []uint32{}
		for _, token := range ringDesc.Tokens {
			takenTokens = append(takenTokens, token.Token)
		}
		tokens = generateTokens(r.numTokens, takenTokens)

		ringDesc.addIngester(r.id, r.hostname, tokens)
		return ringDesc, true, nil
	}
	if err := r.consul.CAS(consulKey, descFactory, pickTokens); err != nil {
		log.Fatalf("Failed to pick tokens in consul: %v", err)
	}
	return tokens
}

func (r *IngesterRegistration) heartbeat(tokens []uint32) {
	heartbeat := func(in interface{}) (out interface{}, retry bool, err error) {
		var ringDesc *Desc
		if in == nil {
			ringDesc = newDesc()
		} else {
			ringDesc = in.(*Desc)
		}

		ingesterDesc, ok := ringDesc.Ingesters[r.id]
		if !ok {
			// consul must have restarted
			log.Infof("Found empty ring, inserting tokens!")
			ringDesc.addIngester(r.id, r.hostname, tokens)
		} else {
			ingesterDesc.Timestamp = time.Now()
			ringDesc.Ingesters[r.id] = ingesterDesc
		}

		return ringDesc, true, nil
	}
	ticker := time.NewTicker(heartbeatInterval)
	defer ticker.Stop()
	for {
		select {
		case <-ticker.C:
			log.Infof("Heartbeating to consul...")
			if err := r.consul.CAS(consulKey, descFactory, heartbeat); err != nil {
				log.Errorf("Failed to write to consul, sleeping: %v", err)
			}
		case <-r.quit:
			return
		}
	}
}

func (r *IngesterRegistration) unregister(tokens []uint32) {
	unregister := func(in interface{}) (out interface{}, retry bool, err error) {
		if in == nil {
			return nil, false, fmt.Errorf("found empty ring when trying to unregister")
		}

		ringDesc := in.(*Desc)
		ringDesc.removeIngester(r.id, tokens)
		return ringDesc, true, nil
	}
	if err := r.consul.CAS(consulKey, descFactory, unregister); err != nil {
		log.Fatalf("Failed to unregister from consul: %v", err)
	}
}

type sortableUint32 []uint32

func (ts sortableUint32) Len() int           { return len(ts) }
func (ts sortableUint32) Swap(i, j int)      { ts[i], ts[j] = ts[j], ts[i] }
func (ts sortableUint32) Less(i, j int) bool { return ts[i] < ts[j] }

// generateTokens make numTokens random tokens, none of which clash
// with takenTokens.  Assumes takenTokens is sorted.
func generateTokens(numTokens int, takenTokens []uint32) []uint32 {
	r := rand.New(rand.NewSource(time.Now().UnixNano()))
	tokens := sortableUint32{}
	for i := 0; i < numTokens; {
		candidate := r.Uint32()
		j := sort.Search(len(takenTokens), func(i int) bool {
			return takenTokens[i] >= candidate
		})
		if j < len(takenTokens) && takenTokens[j] == candidate {
			continue
		}
		tokens = append(tokens, candidate)
		i++
	}
	sort.Sort(tokens)
	return tokens
}

// getFirstAddressOf returns the first IPv4 address of the supplied interface name.
func getFirstAddressOf(name string) (string, error) {
	inf, err := net.InterfaceByName(name)
	if err != nil {
		return "", err
	}

	addrs, err := inf.Addrs()
	if err != nil {
		return "", err
	}
	if len(addrs) <= 0 {
		return "", fmt.Errorf("No address found for %s", name)
	}

	for _, addr := range addrs {
		switch v := addr.(type) {
		case *net.IPNet:
			if ip := v.IP.To4(); ip != nil {
				return v.IP.String(), nil
			}
		}
	}

	return "", fmt.Errorf("No address found for %s", name)
}
<|endoftext|>


<commit_before>//go:build turbojpeg
// +build turbojpeg

/*
 Copyright (c) 2013 Jeremy Torres, https://github.com/jeremytorres/rawparser

 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package rawparser

// Note: modify these flags for your enviornment if required.

// #cgo CFLAGS: -I/usr/local/opt/jpeg-turbo/include -O2
// #cgo LDFLAGS: -L/usr/local/opt/jpeg-turbo/lib -lturbojpeg
// #include "jpeg_wrapper.h"
import "C"

import (
	"fmt"
	"log"
	"unsafe"
)

func init() {
	log.Println("Using turbojpeg native library")
}

func decodeAndWriteJpeg(data []byte, quality int, filename string) error {
	var rc C.int
	f := C.CString(filename)
	defer C.cleanupString(f)

	rc = C.decodeEncodeWrite((*C.uchar)(unsafe.Pointer(&data[0])),
		C.int(len(data)), C.int(quality), f)

	if rc != 0 {
		return fmt.Errorf("error re-encoding JPEG")
	}
	return nil
}
<commit_msg>Change darwin libturbo to use more modern homebrew installation.<commit_after>//go:build turbojpeg
// +build turbojpeg

/*
 Copyright (c) 2013 Jeremy Torres, https://github.com/jeremytorres/rawparser

 Permission is hereby granted, free of charge, to any person obtaining
 a copy of this software and associated documentation files (the
 "Software"), to deal in the Software without restriction, including
 without limitation the rights to use, copy, modify, merge, publish,
 distribute, sublicense, and/or sell copies of the Software, and to
 permit persons to whom the Software is furnished to do so, subject to
 the following conditions:

 The above copyright notice and this permission notice shall be
 included in all copies or substantial portions of the Software.

 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

package rawparser

// Note: modify these flags for your enviornment if required.

// #cgo CFLAGS: -I/opt/homebrew/opt/jpeg-turbo/include -O2
// #cgo LDFLAGS: -L/opt/homebrew/opt/jpeg-turbo/lib -lturbojpeg
// #include "jpeg_wrapper.h"
import "C"

import (
	"fmt"
	"log"
	"unsafe"
)

func init() {
	log.Println("Using turbojpeg native library")
}

func decodeAndWriteJpeg(data []byte, quality int, filename string) error {
	var rc C.int
	f := C.CString(filename)
	defer C.cleanupString(f)

	rc = C.decodeEncodeWrite((*C.uchar)(unsafe.Pointer(&data[0])),
		C.int(len(data)), C.int(quality), f)

	if rc != 0 {
		return fmt.Errorf("error re-encoding JPEG")
	}
	return nil
}
<|endoftext|>


<commit_before>package completion

import (
	"context"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/mitchellh/go-ps"
)

func completionSet(ctx context.Context, ua UncAccess, params []string) ([]Element, error) {
	result := []Element{}
	base := strings.ToUpper(params[len(params)-1])
	for _, env1 := range os.Environ() {
		if strings.HasPrefix(strings.ToUpper(env1), base) {
			result = append(result, Element1(env1))
		}
	}
	return result, nil
}

func completionDir(ctx context.Context, ua UncAccess, params []string) ([]Element, error) {
	return listUpDirs(ctx, ua, params[len(params)-1])
}

func completionCd(ctx context.Context, ua UncAccess, params []string) ([]Element, error) {

	list, err := completionDir(ctx, ua, params)
	source := params[len(params)-1]
	if source[0] == '.' || strings.ContainsAny(source, "/\\:") {
		return list, err
	}
	cdpath := os.Getenv("CDPATH")
	if cdpath == "" {
		return list, err
	}
	base := strings.ToUpper(source)
	for _, cdpath1 := range filepath.SplitList(cdpath) {
		if files, err := ioutil.ReadDir(cdpath1); err == nil {
			for _, file1 := range files {
				if file1.IsDir() {
					name := strings.ToUpper(file1.Name())
					if strings.HasPrefix(name, base) {
						list = append(list, Element1(file1.Name()))
					}
				}
			}
		}
	}
	return list, nil
}

func completionEnv(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	eq := -1
	for i := 1; i < len(param); i++ {
		if strings.Contains(param[i], "=") {
			eq = i
		}
	}
	current := len(param) - 1

	if current == eq || current == 1 {
		return completionSet(ctx, ua, param)
	} else if current == eq+1 {
		return listUpCommands(ctx, param[current])
	} else {
		return nil, nil
	}
}

func completionWhich(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	if len(param) == 2 {
		return listUpCommands(ctx, param[len(param)-1])
	}
	return nil, nil
}

func completionProcessName(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	processes, err := ps.Processes()
	if err != nil {
		return nil, err
	}
	uniq := map[string]struct{}{}
	base := strings.ToUpper(param[len(param)-1])
	for _, ps1 := range processes {
		name := ps1.Executable()
		if strings.HasPrefix(strings.ToUpper(name), base) {
			uniq[name] = struct{}{}
		}
	}
	result := make([]Element, 0, len(uniq))
	for name := range uniq {
		result = append(result, Element1(name))
	}
	return result, nil
}

func completionTaskKill(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	if len(param) >= 3 && strings.EqualFold(param[len(param)-2], "/IM") {
		return completionProcessName(ctx, ua, param)
	}
	return nil, nil
}
<commit_msg>Fix code for CDPATH which causes crash when `cd<SPACE><TAB>` was typed<commit_after>package completion

import (
	"context"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/mitchellh/go-ps"
)

func completionSet(ctx context.Context, ua UncAccess, params []string) ([]Element, error) {
	result := []Element{}
	base := strings.ToUpper(params[len(params)-1])
	for _, env1 := range os.Environ() {
		if strings.HasPrefix(strings.ToUpper(env1), base) {
			result = append(result, Element1(env1))
		}
	}
	return result, nil
}

func completionDir(ctx context.Context, ua UncAccess, params []string) ([]Element, error) {
	return listUpDirs(ctx, ua, params[len(params)-1])
}

func completionCd(ctx context.Context, ua UncAccess, params []string) ([]Element, error) {

	list, err := completionDir(ctx, ua, params)
	source := params[len(params)-1]
	if len(source) < 1 || source[0] == '.' || strings.ContainsAny(source, "/\\:") {
		return list, err
	}
	cdpath := os.Getenv("CDPATH")
	if cdpath == "" {
		return list, err
	}
	base := strings.ToUpper(source)
	for _, cdpath1 := range filepath.SplitList(cdpath) {
		if files, err := ioutil.ReadDir(cdpath1); err == nil {
			for _, file1 := range files {
				if file1.IsDir() {
					name := strings.ToUpper(file1.Name())
					if strings.HasPrefix(name, base) {
						list = append(list, Element1(file1.Name()))
					}
				}
			}
		}
	}
	return list, nil
}

func completionEnv(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	eq := -1
	for i := 1; i < len(param); i++ {
		if strings.Contains(param[i], "=") {
			eq = i
		}
	}
	current := len(param) - 1

	if current == eq || current == 1 {
		return completionSet(ctx, ua, param)
	} else if current == eq+1 {
		return listUpCommands(ctx, param[current])
	} else {
		return nil, nil
	}
}

func completionWhich(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	if len(param) == 2 {
		return listUpCommands(ctx, param[len(param)-1])
	}
	return nil, nil
}

func completionProcessName(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	processes, err := ps.Processes()
	if err != nil {
		return nil, err
	}
	uniq := map[string]struct{}{}
	base := strings.ToUpper(param[len(param)-1])
	for _, ps1 := range processes {
		name := ps1.Executable()
		if strings.HasPrefix(strings.ToUpper(name), base) {
			uniq[name] = struct{}{}
		}
	}
	result := make([]Element, 0, len(uniq))
	for name := range uniq {
		result = append(result, Element1(name))
	}
	return result, nil
}

func completionTaskKill(ctx context.Context, ua UncAccess, param []string) ([]Element, error) {
	if len(param) >= 3 && strings.EqualFold(param[len(param)-2], "/IM") {
		return completionProcessName(ctx, ua, param)
	}
	return nil, nil
}
<|endoftext|>


<commit_before>package conf

import (
	"testing"
)

func TestDuplicates(t *testing.T) {

	testData := []struct {
		imports    []Import
		duplicates int
	}{
		{[]Import{
			{"package1", "version1", ""},
		}, 0},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
		}, 0},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
			{"package1", "version1", ""},
		}, 1},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
			{"package1", "version1", ""},
			{"package1", "version1", ""},
		}, 2},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
			{"package1", "version1", ""},
			{"package1", "version1", ""},
			{"package2", "version2", "repoB"},
			{"package3", "version1", "repoA"},
		}, 3},
	}

	for i, d := range testData {
		trash := Trash{"", d.imports}
		trash.Dedupe()

		if d.duplicates != len(d.imports)-len(trash.Imports) {
			t.Errorf("Case %d failed: expected %d duplicates but removed %d", i, d.duplicates, len(d.imports)-len(trash.Imports))
		}

	}

}
<commit_msg>Fix conf_test.go compile error<commit_after>package conf

import (
	"testing"
)

func TestDuplicates(t *testing.T) {

	testData := []struct {
		imports    []Import
		duplicates int
	}{
		{[]Import{
			{"package1", "version1", ""},
		}, 0},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
		}, 0},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
			{"package1", "version1", ""},
		}, 1},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
			{"package1", "version1", ""},
			{"package1", "version1", ""},
		}, 2},
		{[]Import{
			{"package1", "version1", ""},
			{"package2", "version1", "repoA"},
			{"package1", "version1", ""},
			{"package1", "version1", ""},
			{"package2", "version2", "repoB"},
			{"package3", "version1", "repoA"},
		}, 3},
	}

	for i, d := range testData {
		trash := Trash{"", d.imports, nil}
		trash.Dedupe()

		if d.duplicates != len(d.imports)-len(trash.Imports) {
			t.Errorf("Case %d failed: expected %d duplicates but removed %d", i, d.duplicates, len(d.imports)-len(trash.Imports))
		}

	}

}
<|endoftext|>


<commit_before>package config

import (
	"fmt"
	"github.com/osrg/gobgp/packet/bgp"
	"github.com/osrg/gobgp/packet/bmp"
	"github.com/osrg/gobgp/packet/rtr"
	"github.com/spf13/viper"
	"net"
)

const (
	DEFAULT_HOLDTIME                  = 90
	DEFAULT_IDLE_HOLDTIME_AFTER_RESET = 30
	DEFAULT_CONNECT_RETRY             = 120
	DEFAULT_MPLS_LABEL_MIN            = 16000
	DEFAULT_MPLS_LABEL_MAX            = 1048575
)

func SetDefaultConfigValues(v *viper.Viper, b *BgpConfigSet) error {
	if v == nil {
		v = viper.New()
	}

	defaultAfiSafi := func(typ AfiSafiType, enable bool) AfiSafi {
		return AfiSafi{
			Config: AfiSafiConfig{
				AfiSafiName: typ,
				Enabled:     enable,
			},
			State: AfiSafiState{
				AfiSafiName: typ,
			},
		}
	}

	if b.Global.Zebra.Url == "" {
		b.Global.Zebra.Url = "unix:/var/run/quagga/zserv.api"
	}

	if len(b.Global.AfiSafis) == 0 {
		b.Global.AfiSafis = []AfiSafi{}
		for k, _ := range AfiSafiTypeToIntMap {
			b.Global.AfiSafis = append(b.Global.AfiSafis, defaultAfiSafi(k, true))
		}
	}

	if b.Global.Config.Port == 0 {
		b.Global.Config.Port = bgp.BGP_PORT
	}

	if len(b.Global.Config.LocalAddressList) == 0 {
		b.Global.Config.LocalAddressList = []string{"0.0.0.0", "::"}
	}

	for idx, server := range b.BmpServers {
		if server.Config.Port == 0 {
			server.Config.Port = bmp.BMP_DEFAULT_PORT
		}
		b.BmpServers[idx] = server
	}

	if b.Global.MplsLabelRange.MinLabel == 0 {
		b.Global.MplsLabelRange.MinLabel = DEFAULT_MPLS_LABEL_MIN
	}

	if b.Global.MplsLabelRange.MaxLabel == 0 {
		b.Global.MplsLabelRange.MaxLabel = DEFAULT_MPLS_LABEL_MAX
	}

	// yaml is decoded as []interface{}
	// but toml is decoded as []map[string]interface{}.
	// currently, viper can't hide this difference.
	// handle the difference here.
	extractArray := func(intf interface{}) ([]interface{}, error) {
		if intf != nil {
			list, ok := intf.([]interface{})
			if ok {
				return list, nil
			}
			l, ok := intf.([]map[string]interface{})
			if !ok {
				return nil, fmt.Errorf("invalid configuration: neither []interface{} nor []map[string]interface{}")
			}
			list = make([]interface{}, 0, len(l))
			for _, m := range l {
				list = append(list, m)
			}
			return list, nil
		}
		return nil, nil
	}

	list, err := extractArray(v.Get("neighbors"))
	if err != nil {
		return err
	}
	for idx, n := range b.Neighbors {
		vv := viper.New()
		if len(list) > idx {
			vv.Set("neighbor", list[idx])
		}
		if !vv.IsSet("neighbor.timers.config.connect-retry") {
			n.Timers.Config.ConnectRetry = float64(DEFAULT_CONNECT_RETRY)
		}
		if !vv.IsSet("neighbor.timers.config.hold-time") {
			n.Timers.Config.HoldTime = float64(DEFAULT_HOLDTIME)
		}
		if !vv.IsSet("neighbor.timers.config.keepalive-interval") {
			n.Timers.Config.KeepaliveInterval = n.Timers.Config.HoldTime / 3
		}
		if !vv.IsSet("neighbor.timers.config.idle-hold-time-after-reset") {
			n.Timers.Config.IdleHoldTimeAfterReset = float64(DEFAULT_IDLE_HOLDTIME_AFTER_RESET)
		}

		if !vv.IsSet("neighbor.afi-safis") {
			if ip := net.ParseIP(n.Config.NeighborAddress); ip.To4() != nil {
				n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true)}
			} else if ip.To16() != nil {
				n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true)}
			} else {
				return fmt.Errorf("invalid neighbor address: %s", n.Config.NeighborAddress)
			}
		} else {
			afs, err := extractArray(vv.Get("neighbor.afi-safis"))
			if err != nil {
				return err
			}
			for i, af := range n.AfiSafis {
				vvv := viper.New()
				if len(afs) > i {
					vvv.Set("afi-safi", afs[i])
				}
				af.State.AfiSafiName = af.Config.AfiSafiName
				if !vvv.IsSet("afi-safi.config") {
					af.Config.Enabled = true
				}
				n.AfiSafis[i] = af
			}
		}

		if !vv.IsSet("neighbor.config.local-as") {
			n.Config.LocalAs = b.Global.Config.As
		}

		if !vv.IsSet("neighbor.config.peer-type") {
			if n.Config.PeerAs != n.Config.LocalAs {
				n.Config.PeerType = PEER_TYPE_EXTERNAL
			} else {
				n.Config.PeerType = PEER_TYPE_INTERNAL
			}
		}

		if n.GracefulRestart.Config.Enabled {
			if !vv.IsSet("neighbor.graceful-restart.config.restart-time") {
				// RFC 4724 4. Operation
				// A suggested default for the Restart Time is a value less than or
				// equal to the HOLDTIME carried in the OPEN.
				n.GracefulRestart.Config.RestartTime = uint16(n.Timers.Config.HoldTime)
			}
			if !vv.IsSet("neighbor.graceful-restart.config.deferral-time") {
				// RFC 4724 4.1. Procedures for the Restarting Speaker
				// The value of this timer should be large
				// enough, so as to provide all the peers of the Restarting Speaker with
				// enough time to send all the routes to the Restarting Speaker
				n.GracefulRestart.Config.DeferralTime = uint16(360)
			}
		}
		b.Neighbors[idx] = n
	}

	for _, r := range b.RpkiServers {
		if r.Config.Port == 0 {
			r.Config.Port = rtr.RPKI_DEFAULT_PORT
		}
	}

	return nil
}
<commit_msg>config: avoid peer reset when only Config.Description is changed<commit_after>package config

import (
	"fmt"
	"github.com/osrg/gobgp/packet/bgp"
	"github.com/osrg/gobgp/packet/bmp"
	"github.com/osrg/gobgp/packet/rtr"
	"github.com/spf13/viper"
	"net"
)

const (
	DEFAULT_HOLDTIME                  = 90
	DEFAULT_IDLE_HOLDTIME_AFTER_RESET = 30
	DEFAULT_CONNECT_RETRY             = 120
	DEFAULT_MPLS_LABEL_MIN            = 16000
	DEFAULT_MPLS_LABEL_MAX            = 1048575
)

func SetDefaultConfigValues(v *viper.Viper, b *BgpConfigSet) error {
	if v == nil {
		v = viper.New()
	}

	defaultAfiSafi := func(typ AfiSafiType, enable bool) AfiSafi {
		return AfiSafi{
			Config: AfiSafiConfig{
				AfiSafiName: typ,
				Enabled:     enable,
			},
			State: AfiSafiState{
				AfiSafiName: typ,
			},
		}
	}

	if b.Global.Zebra.Url == "" {
		b.Global.Zebra.Url = "unix:/var/run/quagga/zserv.api"
	}

	if len(b.Global.AfiSafis) == 0 {
		b.Global.AfiSafis = []AfiSafi{}
		for k, _ := range AfiSafiTypeToIntMap {
			b.Global.AfiSafis = append(b.Global.AfiSafis, defaultAfiSafi(k, true))
		}
	}

	if b.Global.Config.Port == 0 {
		b.Global.Config.Port = bgp.BGP_PORT
	}

	if len(b.Global.Config.LocalAddressList) == 0 {
		b.Global.Config.LocalAddressList = []string{"0.0.0.0", "::"}
	}

	for idx, server := range b.BmpServers {
		if server.Config.Port == 0 {
			server.Config.Port = bmp.BMP_DEFAULT_PORT
		}
		b.BmpServers[idx] = server
	}

	if b.Global.MplsLabelRange.MinLabel == 0 {
		b.Global.MplsLabelRange.MinLabel = DEFAULT_MPLS_LABEL_MIN
	}

	if b.Global.MplsLabelRange.MaxLabel == 0 {
		b.Global.MplsLabelRange.MaxLabel = DEFAULT_MPLS_LABEL_MAX
	}

	// yaml is decoded as []interface{}
	// but toml is decoded as []map[string]interface{}.
	// currently, viper can't hide this difference.
	// handle the difference here.
	extractArray := func(intf interface{}) ([]interface{}, error) {
		if intf != nil {
			list, ok := intf.([]interface{})
			if ok {
				return list, nil
			}
			l, ok := intf.([]map[string]interface{})
			if !ok {
				return nil, fmt.Errorf("invalid configuration: neither []interface{} nor []map[string]interface{}")
			}
			list = make([]interface{}, 0, len(l))
			for _, m := range l {
				list = append(list, m)
			}
			return list, nil
		}
		return nil, nil
	}

	list, err := extractArray(v.Get("neighbors"))
	if err != nil {
		return err
	}
	for idx, n := range b.Neighbors {
		vv := viper.New()
		if len(list) > idx {
			vv.Set("neighbor", list[idx])
		}
		if !vv.IsSet("neighbor.timers.config.connect-retry") {
			n.Timers.Config.ConnectRetry = float64(DEFAULT_CONNECT_RETRY)
		}
		if !vv.IsSet("neighbor.timers.config.hold-time") {
			n.Timers.Config.HoldTime = float64(DEFAULT_HOLDTIME)
		}
		if !vv.IsSet("neighbor.timers.config.keepalive-interval") {
			n.Timers.Config.KeepaliveInterval = n.Timers.Config.HoldTime / 3
		}
		if !vv.IsSet("neighbor.timers.config.idle-hold-time-after-reset") {
			n.Timers.Config.IdleHoldTimeAfterReset = float64(DEFAULT_IDLE_HOLDTIME_AFTER_RESET)
		}

		if !vv.IsSet("neighbor.afi-safis") {
			if ip := net.ParseIP(n.Config.NeighborAddress); ip.To4() != nil {
				n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV4_UNICAST, true)}
			} else if ip.To16() != nil {
				n.AfiSafis = []AfiSafi{defaultAfiSafi(AFI_SAFI_TYPE_IPV6_UNICAST, true)}
			} else {
				return fmt.Errorf("invalid neighbor address: %s", n.Config.NeighborAddress)
			}
		} else {
			afs, err := extractArray(vv.Get("neighbor.afi-safis"))
			if err != nil {
				return err
			}
			for i, af := range n.AfiSafis {
				vvv := viper.New()
				if len(afs) > i {
					vvv.Set("afi-safi", afs[i])
				}
				af.State.AfiSafiName = af.Config.AfiSafiName
				if !vvv.IsSet("afi-safi.config") {
					af.Config.Enabled = true
				}
				n.AfiSafis[i] = af
			}
		}

		n.State.Description = n.Config.Description
		n.Config.Description = ""

		if !vv.IsSet("neighbor.config.local-as") {
			n.Config.LocalAs = b.Global.Config.As
		}

		if !vv.IsSet("neighbor.config.peer-type") {
			if n.Config.PeerAs != n.Config.LocalAs {
				n.Config.PeerType = PEER_TYPE_EXTERNAL
			} else {
				n.Config.PeerType = PEER_TYPE_INTERNAL
			}
		}

		if n.GracefulRestart.Config.Enabled {
			if !vv.IsSet("neighbor.graceful-restart.config.restart-time") {
				// RFC 4724 4. Operation
				// A suggested default for the Restart Time is a value less than or
				// equal to the HOLDTIME carried in the OPEN.
				n.GracefulRestart.Config.RestartTime = uint16(n.Timers.Config.HoldTime)
			}
			if !vv.IsSet("neighbor.graceful-restart.config.deferral-time") {
				// RFC 4724 4.1. Procedures for the Restarting Speaker
				// The value of this timer should be large
				// enough, so as to provide all the peers of the Restarting Speaker with
				// enough time to send all the routes to the Restarting Speaker
				n.GracefulRestart.Config.DeferralTime = uint16(360)
			}
		}
		b.Neighbors[idx] = n
	}

	for _, r := range b.RpkiServers {
		if r.Config.Port == 0 {
			r.Config.Port = rtr.RPKI_DEFAULT_PORT
		}
	}

	return nil
}
<|endoftext|>