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

package identity

import (
	"fmt"
	"net"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/labels"
)

// Identity is the representation of the security context for a particular set of
// labels.
type Identity struct {
	// Identity's ID.
	ID NumericIdentity `json:"id"`
	// Set of labels that belong to this Identity.
	Labels labels.Labels `json:"labels"`
	// SHA256 of labels.
	LabelsSHA256 string `json:"labelsSHA256"`

	// LabelArray contains the same labels as Labels in a form of a list, used
	// for faster lookup.
	LabelArray labels.LabelArray `json:"-"`

	// CIDRLabel is the primary identity label when the identity represents
	// a CIDR. The Labels field will consist of all matching prefixes, e.g.
	// 10.0.0.0/8
	// 10.0.0.0/7
	// 10.0.0.0/6
	// [...]
	// reserverd:world
	//
	// The CIDRLabel field will only contain 10.0.0.0/8
	CIDRLabel labels.Labels `json:"-"`

	// ReferenceCount counts the number of references pointing to this
	// identity. This field is used by the owning cache of the identity.
	ReferenceCount int `json:"-"`
}

// IPIdentityPair is a pairing of an IP and the security identity to which that
// IP corresponds. May include an optional Mask which, if present, denotes that
// the IP represents a CIDR with the specified Mask.
//
// WARNING - STABLE API
// This structure is written as JSON to the key-value store. Do NOT modify this
// structure in ways which are not JSON forward compatible.
type IPIdentityPair struct {
	IP       net.IP          `json:"IP"`
	Mask     net.IPMask      `json:"Mask"`
	HostIP   net.IP          `json:"HostIP"`
	ID       NumericIdentity `json:"ID"`
	Key      uint8           `json:"Key"`
	Metadata string          `json:"Metadata"`
}

func NewIdentityFromModel(base *models.Identity) *Identity {
	if base == nil {
		return nil
	}

	id := &Identity{
		ID:     NumericIdentity(base.ID),
		Labels: make(labels.Labels),
	}
	for _, v := range base.Labels {
		lbl := labels.ParseLabel(v)
		id.Labels[lbl.Key] = lbl
	}
	id.Sanitize()

	return id
}

// Sanitize takes a partially initialized Identity (for example, deserialized
// from json) and reconstitutes the full object from what has been restored.
func (id *Identity) Sanitize() {
	if id.Labels != nil {
		id.LabelArray = id.Labels.LabelArray()
	}
}

// GetLabelsSHA256 returns the SHA256 of the labels associated with the
// identity. The SHA is calculated if not already cached.
func (id *Identity) GetLabelsSHA256() string {
	if id.LabelsSHA256 == "" {
		id.LabelsSHA256 = id.Labels.SHA256Sum()
	}

	return id.LabelsSHA256
}

// StringID returns the identity identifier as string
func (id *Identity) StringID() string {
	return id.ID.StringID()
}

func (id *Identity) GetModel() *models.Identity {
	if id == nil {
		return nil
	}

	ret := &models.Identity{
		ID:           int64(id.ID),
		Labels:       []string{},
		LabelsSHA256: "",
	}

	for _, v := range id.Labels {
		ret.Labels = append(ret.Labels, v.String())
	}
	ret.LabelsSHA256 = id.GetLabelsSHA256()
	return ret
}

// IsReserved returns whether the identity represents a reserved identity
// (true), or not (false).
func (id *Identity) IsReserved() bool {
	return LookupReservedIdentity(id.ID) != nil
}

// IsFixed returns whether the identity represents a fixed identity
// (true), or not (false).
func (id *Identity) IsFixed() bool {
	return LookupReservedIdentity(id.ID) != nil && IsUserReservedIdentity(id.ID)
}

// IsWellKnown returns whether the identity represents a well known identity
// (true), or not (false).
func (id *Identity) IsWellKnown() bool {
	return WellKnown.lookupByNumericIdentity(id.ID) != nil
}

// NewIdentityFromLabelArray creates a new identity
func NewIdentityFromLabelArray(id NumericIdentity, lblArray labels.LabelArray) *Identity {
	var lbls labels.Labels

	if lblArray != nil {
		lbls = lblArray.Labels()
	}
	return &Identity{ID: id, Labels: lbls, LabelArray: lblArray}
}

// NewIdentity creates a new identity
func NewIdentity(id NumericIdentity, lbls labels.Labels) *Identity {
	var lblArray labels.LabelArray

	if lbls != nil {
		lblArray = lbls.LabelArray()
	}
	return &Identity{ID: id, Labels: lbls, LabelArray: lblArray}
}

// IsHost determines whether the IP in the pair represents a host (true) or a
// CIDR prefix (false)
func (pair *IPIdentityPair) IsHost() bool {
	return pair.Mask == nil
}

// PrefixString returns the IPIdentityPair's IP as either a host IP in the
// format w.x.y.z if 'host' is true, or as a prefix in the format the w.x.y.z/N
// if 'host' is false.
func (pair *IPIdentityPair) PrefixString() string {
	var suffix string
	if !pair.IsHost() {
		var ones int
		if pair.Mask == nil {
			if pair.IP.To4() != nil {
				ones = net.IPv4len
			} else {
				ones = net.IPv6len
			}
		} else {
			ones, _ = pair.Mask.Size()
		}
		suffix = fmt.Sprintf("/%d", ones)
	}
	return fmt.Sprintf("%s%s", pair.IP.String(), suffix)
}

// RequiresGlobalIdentity returns true if the label combination requires a
// global identity
func RequiresGlobalIdentity(lbls labels.Labels) bool {
	for _, label := range lbls {
		switch label.Source {
		case labels.LabelSourceCIDR, labels.LabelSourceReserved:
		default:
			return true
		}
	}

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

package identity

import (
	"fmt"
	"net"

	"github.com/cilium/cilium/api/v1/models"
	"github.com/cilium/cilium/pkg/labels"
)

// Identity is the representation of the security context for a particular set of
// labels.
type Identity struct {
	// Identity's ID.
	ID NumericIdentity `json:"id"`
	// Set of labels that belong to this Identity.
	Labels labels.Labels `json:"labels"`
	// SHA256 of labels.
	LabelsSHA256 string `json:"labelsSHA256"`

	// LabelArray contains the same labels as Labels in a form of a list, used
	// for faster lookup.
	LabelArray labels.LabelArray `json:"-"`

	// CIDRLabel is the primary identity label when the identity represents
	// a CIDR. The Labels field will consist of all matching prefixes, e.g.
	// 10.0.0.0/8
	// 10.0.0.0/7
	// 10.0.0.0/6
	// [...]
	// reserverd:world
	//
	// The CIDRLabel field will only contain 10.0.0.0/8
	CIDRLabel labels.Labels `json:"-"`

	// ReferenceCount counts the number of references pointing to this
	// identity. This field is used by the owning cache of the identity.
	ReferenceCount int `json:"-"`
}

// IPIdentityPair is a pairing of an IP and the security identity to which that
// IP corresponds. May include an optional Mask which, if present, denotes that
// the IP represents a CIDR with the specified Mask.
//
// WARNING - STABLE API
// This structure is written as JSON to the key-value store. Do NOT modify this
// structure in ways which are not JSON forward compatible.
type IPIdentityPair struct {
	IP       net.IP          `json:"IP"`
	Mask     net.IPMask      `json:"Mask"`
	HostIP   net.IP          `json:"HostIP"`
	ID       NumericIdentity `json:"ID"`
	Key      uint8           `json:"Key"`
	Metadata string          `json:"Metadata"`
}

func NewIdentityFromModel(base *models.Identity) *Identity {
	if base == nil {
		return nil
	}

	id := &Identity{
		ID:     NumericIdentity(base.ID),
		Labels: make(labels.Labels),
	}
	for _, v := range base.Labels {
		lbl := labels.ParseLabel(v)
		id.Labels[lbl.Key] = lbl
	}
	id.Sanitize()

	return id
}

// Sanitize takes a partially initialized Identity (for example, deserialized
// from json) and reconstitutes the full object from what has been restored.
func (id *Identity) Sanitize() {
	if id.Labels != nil {
		id.LabelArray = id.Labels.LabelArray()
	}
}

// GetLabelsSHA256 returns the SHA256 of the labels associated with the
// identity. The SHA is calculated if not already cached.
func (id *Identity) GetLabelsSHA256() string {
	if id.LabelsSHA256 == "" {
		id.LabelsSHA256 = id.Labels.SHA256Sum()
	}

	return id.LabelsSHA256
}

// StringID returns the identity identifier as string
func (id *Identity) StringID() string {
	return id.ID.StringID()
}

// StringID returns the identity identifier as string
func (id *Identity) String() string {
	return id.ID.StringID()
}

func (id *Identity) GetModel() *models.Identity {
	if id == nil {
		return nil
	}

	ret := &models.Identity{
		ID:           int64(id.ID),
		Labels:       []string{},
		LabelsSHA256: "",
	}

	for _, v := range id.Labels {
		ret.Labels = append(ret.Labels, v.String())
	}
	ret.LabelsSHA256 = id.GetLabelsSHA256()
	return ret
}

// IsReserved returns whether the identity represents a reserved identity
// (true), or not (false).
func (id *Identity) IsReserved() bool {
	return LookupReservedIdentity(id.ID) != nil
}

// IsFixed returns whether the identity represents a fixed identity
// (true), or not (false).
func (id *Identity) IsFixed() bool {
	return LookupReservedIdentity(id.ID) != nil && IsUserReservedIdentity(id.ID)
}

// IsWellKnown returns whether the identity represents a well known identity
// (true), or not (false).
func (id *Identity) IsWellKnown() bool {
	return WellKnown.lookupByNumericIdentity(id.ID) != nil
}

// NewIdentityFromLabelArray creates a new identity
func NewIdentityFromLabelArray(id NumericIdentity, lblArray labels.LabelArray) *Identity {
	var lbls labels.Labels

	if lblArray != nil {
		lbls = lblArray.Labels()
	}
	return &Identity{ID: id, Labels: lbls, LabelArray: lblArray}
}

// NewIdentity creates a new identity
func NewIdentity(id NumericIdentity, lbls labels.Labels) *Identity {
	var lblArray labels.LabelArray

	if lbls != nil {
		lblArray = lbls.LabelArray()
	}
	return &Identity{ID: id, Labels: lbls, LabelArray: lblArray}
}

// IsHost determines whether the IP in the pair represents a host (true) or a
// CIDR prefix (false)
func (pair *IPIdentityPair) IsHost() bool {
	return pair.Mask == nil
}

// PrefixString returns the IPIdentityPair's IP as either a host IP in the
// format w.x.y.z if 'host' is true, or as a prefix in the format the w.x.y.z/N
// if 'host' is false.
func (pair *IPIdentityPair) PrefixString() string {
	var suffix string
	if !pair.IsHost() {
		var ones int
		if pair.Mask == nil {
			if pair.IP.To4() != nil {
				ones = net.IPv4len
			} else {
				ones = net.IPv6len
			}
		} else {
			ones, _ = pair.Mask.Size()
		}
		suffix = fmt.Sprintf("/%d", ones)
	}
	return fmt.Sprintf("%s%s", pair.IP.String(), suffix)
}

// RequiresGlobalIdentity returns true if the label combination requires a
// global identity
func RequiresGlobalIdentity(lbls labels.Labels) bool {
	for _, label := range lbls {
		switch label.Source {
		case labels.LabelSourceCIDR, labels.LabelSourceReserved:
		default:
			return true
		}
	}

	return false
}
<|endoftext|>


<commit_before>package jobs

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"sync/atomic"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/prefixer"
	"github.com/go-redis/redis"
	multierror "github.com/hashicorp/go-multierror"
)

const (
	// redisPrefix is the prefix for jobs queues in redis.
	redisPrefix = "j/"
	// redisHighPrioritySuffix suffix is the suffix used for prioritized queue.
	redisHighPrioritySuffix = "/p0"
)

type redisBroker struct {
	client       redis.UniversalClient
	workers      []*Worker
	workersTypes []string
	running      uint32
	closed       chan struct{}
}

// NewRedisBroker creates a new broker that will use redis to distribute
// the jobs among several cozy-stack processes.
func NewRedisBroker(client redis.UniversalClient) Broker {
	return &redisBroker{
		client: client,
		closed: make(chan struct{}),
	}
}

// StartWorkers polling jobs from redis queues
func (b *redisBroker) StartWorkers(ws WorkersList) error {
	if !atomic.CompareAndSwapUint32(&b.running, 0, 1) {
		return ErrClosed
	}

	for _, conf := range ws {
		b.workersTypes = append(b.workersTypes, conf.WorkerType)
		ch := make(chan *Job)
		w := NewWorker(conf)
		b.workers = append(b.workers, w)
		if conf.Concurrency <= 0 {
			continue
		}
		if err := w.Start(ch); err != nil {
			return err
		}
		go b.pollLoop(redisPrefix+conf.WorkerType, ch)
	}

	if len(b.workers) > 0 {
		joblog.Infof("Started redis broker for %d workers type", len(b.workers))
	}

	// XXX for retro-compat
	if slots := config.GetConfig().Jobs.NbWorkers; len(b.workers) > 0 && slots > 0 {
		joblog.Warnf("Limiting the number of total concurrent workers to %d", slots)
		joblog.Warnf("Please update your configuration file to avoid a hard limit")
		setNbSlots(slots)
	}

	return nil
}

func (b *redisBroker) WorkersTypes() []string {
	return b.workersTypes
}

func (b *redisBroker) ShutdownWorkers(ctx context.Context) error {
	if !atomic.CompareAndSwapUint32(&b.running, 1, 0) {
		return ErrClosed
	}
	if len(b.workers) == 0 {
		return nil
	}

	fmt.Print("  shutting down redis broker...")
	defer b.client.Close()

	for i := 0; i < len(b.workers); i++ {
		select {
		case <-ctx.Done():
			fmt.Println("failed:", ctx.Err())
			return ctx.Err()
		case <-b.closed:
		}
	}

	errs := make(chan error)
	for _, w := range b.workers {
		go func(w *Worker) { errs <- w.Shutdown(ctx) }(w)
	}

	var errm error
	for i := 0; i < len(b.workers); i++ {
		if err := <-errs; err != nil {
			errm = multierror.Append(errm, err)
		}
	}

	if errm != nil {
		fmt.Println("failed: ", errm)
	} else {
		fmt.Println("ok")
	}
	return errm
}

var redisBRPopTimeout = 10 * time.Second

func (b *redisBroker) pollLoop(key string, ch chan<- *Job) {
	defer func() {
		b.closed <- struct{}{}
	}()

	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
	for {
		if atomic.LoadUint32(&b.running) == 0 {
			return
		}

		// The brpop redis command will always take elements in priority from the
		// first key containing elements at the call. By always priorizing the
		// manual queue, this would cause a starvation for our main queue if too
		// many "manual" jobs are pushed. By randomizing the order we make sure we
		// avoid such starvation. For one in three call, the main queue is
		// selected.
		keyP0 := key + redisHighPrioritySuffix
		keyP1 := key
		if rng.Intn(3) == 0 {
			keyP1, keyP0 = keyP0, keyP1
		}
		results, err := b.client.BRPop(redisBRPopTimeout, keyP0, keyP1).Result()
		if err != nil || len(results) < 2 {
			time.Sleep(100 * time.Millisecond)
			continue
		}

		key := results[0]
		val := results[1]
		if len(key) < len(redisPrefix) {
			joblog.Warnf("Invalid key %s", key)
			continue
		}

		parts := strings.SplitN(val, "/", 2)
		if len(parts) != 2 {
			joblog.Warnf("Invalid val %s", val)
			continue
		}

		prefix, jobID := parts[0], parts[1]
		job, err := Get(prefixer.NewPrefixer("", prefix), jobID)
		if err != nil {
			joblog.Warnf("Cannot find job %s on domain %s: %s", parts[1], parts[0], err)
			continue
		}

		ch <- job
	}
}

// PushJob will produce a new Job with the given options and enqueue the job in
// the proper queue.
func (b *redisBroker) PushJob(db prefixer.Prefixer, req *JobRequest) (*Job, error) {
	if atomic.LoadUint32(&b.running) == 0 {
		return nil, ErrClosed
	}

	var worker *Worker
	for _, w := range b.workers {
		if w.Type == req.WorkerType {
			worker = w
			break
		}
	}
	if worker == nil {
		return nil, ErrUnknownWorker
	}
	if worker.Conf.AdminOnly && !req.Admin {
		return nil, ErrUnknownWorker
	}

	job := NewJob(db, req)
	if worker.Conf.BeforeHook != nil {
		ok, err := worker.Conf.BeforeHook(req)
		if err != nil {
			return nil, err
		}
		if !ok {
			return job, nil
		}
	}

	if err := job.Create(); err != nil {
		return nil, err
	}

	key := redisPrefix + job.WorkerType
	val := job.DBPrefix() + "/" + job.JobID

	// When the job is manual, it is being pushed in a specific prioritized
	// queue.
	if job.Manual {
		key += redisHighPrioritySuffix
	}

	if err := b.client.LPush(key, val).Err(); err != nil {
		return nil, err
	}

	return job, nil
}

// QueueLen returns the size of the number of elements in queue of the
// specified worker type.
func (b *redisBroker) WorkerQueueLen(workerType string) (int, error) {
	key := redisPrefix + workerType
	l1, err := b.client.LLen(key).Result()
	if err != nil {
		return 0, err
	}
	l2, err := b.client.LLen(key + redisHighPrioritySuffix).Result()
	if err != nil {
		return 0, err
	}
	return int(l1 + l2), nil
}
<commit_msg>Fix shutdown of redis broker<commit_after>package jobs

import (
	"context"
	"fmt"
	"math/rand"
	"strings"
	"sync/atomic"
	"time"

	"github.com/cozy/cozy-stack/pkg/config"
	"github.com/cozy/cozy-stack/pkg/prefixer"
	"github.com/go-redis/redis"
	multierror "github.com/hashicorp/go-multierror"
)

const (
	// redisPrefix is the prefix for jobs queues in redis.
	redisPrefix = "j/"
	// redisHighPrioritySuffix suffix is the suffix used for prioritized queue.
	redisHighPrioritySuffix = "/p0"
)

type redisBroker struct {
	client       redis.UniversalClient
	workers      []*Worker
	workersTypes []string
	running      uint32
	closed       chan struct{}
}

// NewRedisBroker creates a new broker that will use redis to distribute
// the jobs among several cozy-stack processes.
func NewRedisBroker(client redis.UniversalClient) Broker {
	return &redisBroker{
		client: client,
		closed: make(chan struct{}),
	}
}

// StartWorkers polling jobs from redis queues
func (b *redisBroker) StartWorkers(ws WorkersList) error {
	if !atomic.CompareAndSwapUint32(&b.running, 0, 1) {
		return ErrClosed
	}

	for _, conf := range ws {
		b.workersTypes = append(b.workersTypes, conf.WorkerType)
		if conf.Concurrency <= 0 {
			continue
		}
		ch := make(chan *Job)
		w := NewWorker(conf)
		b.workers = append(b.workers, w)
		if err := w.Start(ch); err != nil {
			return err
		}
		go b.pollLoop(redisPrefix+conf.WorkerType, ch)
	}

	if len(b.workers) > 0 {
		joblog.Infof("Started redis broker for %d workers type", len(b.workers))
	}

	// XXX for retro-compat
	if slots := config.GetConfig().Jobs.NbWorkers; len(b.workers) > 0 && slots > 0 {
		joblog.Warnf("Limiting the number of total concurrent workers to %d", slots)
		joblog.Warnf("Please update your configuration file to avoid a hard limit")
		setNbSlots(slots)
	}

	return nil
}

func (b *redisBroker) WorkersTypes() []string {
	return b.workersTypes
}

func (b *redisBroker) ShutdownWorkers(ctx context.Context) error {
	if !atomic.CompareAndSwapUint32(&b.running, 1, 0) {
		return ErrClosed
	}
	if len(b.workers) == 0 {
		return nil
	}

	fmt.Print("  shutting down redis broker...")
	defer b.client.Close()

	for i := 0; i < len(b.workers); i++ {
		select {
		case <-ctx.Done():
			fmt.Println("failed:", ctx.Err())
			return ctx.Err()
		case <-b.closed:
		}
	}

	errs := make(chan error)
	for _, w := range b.workers {
		go func(w *Worker) { errs <- w.Shutdown(ctx) }(w)
	}

	var errm error
	for i := 0; i < len(b.workers); i++ {
		if err := <-errs; err != nil {
			errm = multierror.Append(errm, err)
		}
	}

	if errm != nil {
		fmt.Println("failed: ", errm)
	} else {
		fmt.Println("ok")
	}
	return errm
}

var redisBRPopTimeout = 10 * time.Second

func (b *redisBroker) pollLoop(key string, ch chan<- *Job) {
	defer func() {
		b.closed <- struct{}{}
	}()

	rng := rand.New(rand.NewSource(time.Now().UnixNano()))
	for {
		if atomic.LoadUint32(&b.running) == 0 {
			return
		}

		// The brpop redis command will always take elements in priority from the
		// first key containing elements at the call. By always priorizing the
		// manual queue, this would cause a starvation for our main queue if too
		// many "manual" jobs are pushed. By randomizing the order we make sure we
		// avoid such starvation. For one in three call, the main queue is
		// selected.
		keyP0 := key + redisHighPrioritySuffix
		keyP1 := key
		if rng.Intn(3) == 0 {
			keyP1, keyP0 = keyP0, keyP1
		}
		results, err := b.client.BRPop(redisBRPopTimeout, keyP0, keyP1).Result()
		if err != nil || len(results) < 2 {
			time.Sleep(100 * time.Millisecond)
			continue
		}

		key := results[0]
		val := results[1]
		if len(key) < len(redisPrefix) {
			joblog.Warnf("Invalid key %s", key)
			continue
		}

		parts := strings.SplitN(val, "/", 2)
		if len(parts) != 2 {
			joblog.Warnf("Invalid val %s", val)
			continue
		}

		prefix, jobID := parts[0], parts[1]
		job, err := Get(prefixer.NewPrefixer("", prefix), jobID)
		if err != nil {
			joblog.Warnf("Cannot find job %s on domain %s: %s", parts[1], parts[0], err)
			continue
		}

		ch <- job
	}
}

// PushJob will produce a new Job with the given options and enqueue the job in
// the proper queue.
func (b *redisBroker) PushJob(db prefixer.Prefixer, req *JobRequest) (*Job, error) {
	if atomic.LoadUint32(&b.running) == 0 {
		return nil, ErrClosed
	}

	var worker *Worker
	for _, w := range b.workers {
		if w.Type == req.WorkerType {
			worker = w
			break
		}
	}
	if worker == nil {
		return nil, ErrUnknownWorker
	}
	if worker.Conf.AdminOnly && !req.Admin {
		return nil, ErrUnknownWorker
	}

	job := NewJob(db, req)
	if worker.Conf.BeforeHook != nil {
		ok, err := worker.Conf.BeforeHook(req)
		if err != nil {
			return nil, err
		}
		if !ok {
			return job, nil
		}
	}

	if err := job.Create(); err != nil {
		return nil, err
	}

	key := redisPrefix + job.WorkerType
	val := job.DBPrefix() + "/" + job.JobID

	// When the job is manual, it is being pushed in a specific prioritized
	// queue.
	if job.Manual {
		key += redisHighPrioritySuffix
	}

	if err := b.client.LPush(key, val).Err(); err != nil {
		return nil, err
	}

	return job, nil
}

// QueueLen returns the size of the number of elements in queue of the
// specified worker type.
func (b *redisBroker) WorkerQueueLen(workerType string) (int, error) {
	key := redisPrefix + workerType
	l1, err := b.client.LLen(key).Result()
	if err != nil {
		return 0, err
	}
	l2, err := b.client.LLen(key + redisHighPrioritySuffix).Result()
	if err != nil {
		return 0, err
	}
	return int(l1 + l2), nil
}
<|endoftext|>


<commit_before><commit_msg>Add tests for gdoc countTwo.<commit_after><|endoftext|>


<commit_before>package models

import (
	"errors"
	"strings"
	"time"

	"github.com/gosimple/slug"
)

// Typed errors
var (
	ErrDashboardNotFound           = errors.New("Dashboard not found")
	ErrDashboardSnapshotNotFound   = errors.New("Dashboard snapshot not found")
	ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
	ErrDashboardVersionMismatch    = errors.New("The dashboard has been changed by someone else")
)

var (
	DashTypeJson     = "file"
	DashTypeDB       = "db"
	DashTypeScript   = "script"
	DashTypeSnapshot = "snapshot"
)

// Dashboard model
type Dashboard struct {
	Id      int64
	Slug    string
	OrgId   int64
	Version int

	Created time.Time
	Updated time.Time

	Title string
	Data  map[string]interface{}
}

// NewDashboard creates a new dashboard
func NewDashboard(title string) *Dashboard {
	dash := &Dashboard{}
	dash.Data = make(map[string]interface{})
	dash.Data["title"] = title
	dash.Title = title
	dash.UpdateSlug()
	return dash
}

// GetTags turns the tags in data json into go string array
func (dash *Dashboard) GetTags() []string {
	jsonTags := dash.Data["tags"]
	if jsonTags == nil || jsonTags == "" {
		return []string{}
	}

	arr := jsonTags.([]interface{})
	b := make([]string, len(arr))
	for i := range arr {
		b[i] = arr[i].(string)
	}
	return b
}

func NewDashboardFromJson(data map[string]interface{}) *Dashboard {
	dash := &Dashboard{}
	dash.Data = data
	dash.Title = dash.Data["title"].(string)
	dash.UpdateSlug()

	if dash.Data["id"] != nil {
		dash.Id = int64(dash.Data["id"].(float64))

		if dash.Data["version"] != nil {
			dash.Version = int(dash.Data["version"].(float64))
		}
	} else {
		dash.Data["version"] = 0
	}

	return dash
}

// GetDashboardModel turns the command into the savable model
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
	dash := NewDashboardFromJson(cmd.Dashboard)
	dash.OrgId = cmd.OrgId
	dash.UpdateSlug()
	return dash
}

// GetString a
func (dash *Dashboard) GetString(prop string) string {
	return dash.Data[prop].(string)
}

// UpdateSlug updates the slug
func (dash *Dashboard) UpdateSlug() {
	title := strings.ToLower(dash.Data["title"].(string))
	dash.Slug = slug.Make(title)
}

//
// COMMANDS
//

type SaveDashboardCommand struct {
	Dashboard map[string]interface{} `json:"dashboard" binding:"Required"`
	Overwrite bool                   `json:"overwrite"`
	OrgId     int64                  `json:"-"`

	Result *Dashboard
}

type DeleteDashboardCommand struct {
	Slug  string
	OrgId int64
}

//
// QUERIES
//

type GetDashboardQuery struct {
	Slug  string
	OrgId int64

	Result *Dashboard
}

type DashboardTagCloudItem struct {
	Term  string `json:"term"`
	Count int    `json:"count"`
}

type GetDashboardTagsQuery struct {
	OrgId  int64
	Result []*DashboardTagCloudItem
}
<commit_msg>Fixed created & updated columns in dashboard table<commit_after>package models

import (
	"errors"
	"strings"
	"time"

	"github.com/gosimple/slug"
)

// Typed errors
var (
	ErrDashboardNotFound           = errors.New("Dashboard not found")
	ErrDashboardSnapshotNotFound   = errors.New("Dashboard snapshot not found")
	ErrDashboardWithSameNameExists = errors.New("A dashboard with the same name already exists")
	ErrDashboardVersionMismatch    = errors.New("The dashboard has been changed by someone else")
)

var (
	DashTypeJson     = "file"
	DashTypeDB       = "db"
	DashTypeScript   = "script"
	DashTypeSnapshot = "snapshot"
)

// Dashboard model
type Dashboard struct {
	Id      int64
	Slug    string
	OrgId   int64
	Version int

	Created time.Time
	Updated time.Time

	Title string
	Data  map[string]interface{}
}

// NewDashboard creates a new dashboard
func NewDashboard(title string) *Dashboard {
	dash := &Dashboard{}
	dash.Data = make(map[string]interface{})
	dash.Data["title"] = title
	dash.Title = title
	dash.UpdateSlug()
	return dash
}

// GetTags turns the tags in data json into go string array
func (dash *Dashboard) GetTags() []string {
	jsonTags := dash.Data["tags"]
	if jsonTags == nil || jsonTags == "" {
		return []string{}
	}

	arr := jsonTags.([]interface{})
	b := make([]string, len(arr))
	for i := range arr {
		b[i] = arr[i].(string)
	}
	return b
}

func NewDashboardFromJson(data map[string]interface{}) *Dashboard {
	dash := &Dashboard{}
	dash.Data = data
	dash.Title = dash.Data["title"].(string)
	dash.UpdateSlug()

	if dash.Data["id"] != nil {
		dash.Id = int64(dash.Data["id"].(float64))

		if dash.Data["version"] != nil {
			dash.Version = int(dash.Data["version"].(float64))
			dash.Updated = time.Now()
		}
	} else {
		dash.Data["version"] = 0
		dash.Created = time.Now()
		dash.Updated = time.Now()
	}

	return dash
}

// GetDashboardModel turns the command into the savable model
func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
	dash := NewDashboardFromJson(cmd.Dashboard)
	dash.OrgId = cmd.OrgId
	dash.UpdateSlug()
	return dash
}

// GetString a
func (dash *Dashboard) GetString(prop string) string {
	return dash.Data[prop].(string)
}

// UpdateSlug updates the slug
func (dash *Dashboard) UpdateSlug() {
	title := strings.ToLower(dash.Data["title"].(string))
	dash.Slug = slug.Make(title)
}

//
// COMMANDS
//

type SaveDashboardCommand struct {
	Dashboard map[string]interface{} `json:"dashboard" binding:"Required"`
	Overwrite bool                   `json:"overwrite"`
	OrgId     int64                  `json:"-"`

	Result *Dashboard
}

type DeleteDashboardCommand struct {
	Slug  string
	OrgId int64
}

//
// QUERIES
//

type GetDashboardQuery struct {
	Slug  string
	OrgId int64

	Result *Dashboard
}

type DashboardTagCloudItem struct {
	Term  string `json:"term"`
	Count int    `json:"count"`
}

type GetDashboardTagsQuery struct {
	OrgId  int64
	Result []*DashboardTagCloudItem
}
<|endoftext|>


<commit_before><commit_msg>cmd/spatialize: encapsulated mappers for slow/fast paths<commit_after><|endoftext|>


<commit_before>package parser

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"testing"

	"github.com/demizer/go-rst/pkg/testutil"

	doc "github.com/demizer/go-rst/pkg/document"
)

func nodeListToInterface(v *doc.NodeList) []interface{} {
	v2 := []doc.Node(*v)
	s := make([]interface{}, len(v2))
	for i, j := range v2 {
		s[i] = j
	}
	return s
}

// checkParseNodes compares the expected parser output (*_nodes.json) against the actual parser output using the jd library.
func checkParseNodes(t *testing.T, eParser []interface{}, pNodes *doc.NodeList, testPath string) {
	pJson, err := json.MarshalIndent(pNodes, "", "    ")
	if err != nil {
		t.Errorf("Error Marshalling JSON: %s", err.Error())
		return
	}

	// Json diff output has a syntax:

	// https://github.com/josephburnett/jd#diff-language
	o, err := testutil.JsonDiff(eParser, nodeListToInterface(pNodes))
	if err != nil {
		fmt.Println(o)
		fmt.Printf("Error diffing JSON: %s", err.Error())
		return
	}

	// There should be no output from the diff
	if len(o) != 0 {
		// Give all other output time to print
		// time.Sleep(time.Second / 2)

		testutil.Log("\nFAIL: parsed nodes do not match expected nodes!")

		testutil.Log("\n[Parsed Nodes JSON]\n\n")
		testutil.Log(string(pJson))

		testutil.Log("\n\n[JSON DIFF]\n\n")
		testutil.Log(o)

		t.FailNow()
	}
}

func LoadParserTest(t *testing.T, path string) (test *testutil.Test) {
	iDPath := path + ".rst"
	inputData, err := ioutil.ReadFile(iDPath)
	if err != nil {
		t.Fatal(err)
	}
	if len(inputData) == 0 {
		t.Fatalf("\"%s\" is empty!", iDPath)
	}
	nDPath := path + "-nodes.json"
	nodeData, err := ioutil.ReadFile(nDPath)
	if err != nil {
		t.Fatal(err)
	}
	if len(nodeData) == 0 {
		t.Fatalf("\"%s\" is empty!", nDPath)
	}
	return &testutil.Test{
		Path:     path,
		Data:     string(inputData[:len(inputData)-1]),
		NodeData: string(nodeData),
	}
}

// parseTest initiates the parser and parses a test using test.data is input.
func parseTest(t *testing.T, test *testutil.Test) *Parser {
	testutil.LogDebug(fmt.Sprintf("Test path: %s", test.Path))
	testutil.LogDebug(fmt.Sprintf("Test Input:\n\n%s\n", test.Data))
	p, err := NewParser(test.Path, test.Data, testutil.StdLogger, testutil.CallDepth)
	if err != nil {
		panic(err)
	}
	p.Parse()
	return p
}
<commit_msg>output test input to logger<commit_after>package parser

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"testing"

	"github.com/demizer/go-rst/pkg/testutil"

	doc "github.com/demizer/go-rst/pkg/document"
)

func nodeListToInterface(v *doc.NodeList) []interface{} {
	v2 := []doc.Node(*v)
	s := make([]interface{}, len(v2))
	for i, j := range v2 {
		s[i] = j
	}
	return s
}

// checkParseNodes compares the expected parser output (*_nodes.json) against the actual parser output using the jd library.
func checkParseNodes(t *testing.T, eParser []interface{}, pNodes *doc.NodeList, testPath string) {
	pJson, err := json.MarshalIndent(pNodes, "", "    ")
	if err != nil {
		t.Errorf("Error Marshalling JSON: %s", err.Error())
		return
	}

	// Json diff output has a syntax:

	// https://github.com/josephburnett/jd#diff-language
	o, err := testutil.JsonDiff(eParser, nodeListToInterface(pNodes))
	if err != nil {
		fmt.Println(o)
		fmt.Printf("Error diffing JSON: %s", err.Error())
		return
	}

	// There should be no output from the diff
	if len(o) != 0 {
		// Give all other output time to print
		// time.Sleep(time.Second / 2)

		testutil.Log("\nFAIL: parsed nodes do not match expected nodes!")

		testutil.Log("\n[Parsed Nodes JSON]\n\n")
		testutil.Log(string(pJson))

		testutil.Log("\n\n[JSON DIFF]\n\n")
		testutil.Log(o)

		t.FailNow()
	}
}

func LoadParserTest(t *testing.T, path string) (test *testutil.Test) {
	iDPath := path + ".rst"
	inputData, err := ioutil.ReadFile(iDPath)
	if err != nil {
		t.Fatal(err)
	}
	if len(inputData) == 0 {
		t.Fatalf("\"%s\" is empty!", iDPath)
	}
	nDPath := path + "-nodes.json"
	nodeData, err := ioutil.ReadFile(nDPath)
	if err != nil {
		t.Fatal(err)
	}
	if len(nodeData) == 0 {
		t.Fatalf("\"%s\" is empty!", nDPath)
	}
	return &testutil.Test{
		Path:     path,
		Data:     string(inputData[:len(inputData)-1]),
		NodeData: string(nodeData),
	}
}

// parseTest initiates the parser and parses a test using test.data is input.
func parseTest(t *testing.T, test *testutil.Test) *Parser {
	p, err := NewParser(test.Path, test.Data, testutil.StdLogger, testutil.CallDepth)
	if err != nil {
		panic(err)
	}
	p.Msgr("test path", "path", test.Path)
	p.Msgr("test input", "input", test.Data)
	p.Parse()
	return p
}
<|endoftext|>


<commit_before>/*
 * This file is part of remco.
 * Based on code from confd. https://github.com/kelseyhightower/confd
 * © 2013 Kelsey Hightower
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package template

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"sync"
	"text/template"

	"github.com/HeavyHorst/pongo2"
	"github.com/HeavyHorst/remco/pkg/template/fileutil"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

func init() {
	pongo2.SetAutoescape(false)
}

// Renderer contains all data needed for the template processing
type Renderer struct {
	Src       string `json:"src"`
	Dst       string `json:"dst"`
	MkDirs    bool   `toml:"make_directories"`
	Mode      string `json:"mode"`
	UID       int    `json:"uid"`
	GID       int    `json:"gid"`
	ReloadCmd string `toml:"reload_cmd" json:"reload_cmd"`
	CheckCmd  string `toml:"check_cmd" json:"check_cmd"`
	stageFile *os.File
	logger    *logrus.Entry
	ReapLock  *sync.RWMutex
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (s *Renderer) createStageFile(funcMap map[string]interface{}) error {
	if !fileutil.IsFileExist(s.Src) {
		return fmt.Errorf("missing template: %s", s.Src)
	}

	s.logger.WithFields(logrus.Fields{
		"template": s.Src,
	}).Debug("compiling source template")

	set := pongo2.NewSet("local", &pongo2.LocalFilesystemLoader{})
	set.Options = &pongo2.Options{
		TrimBlocks:   true,
		LStripBlocks: true,
	}
	tmpl, err := set.FromFile(s.Src)
	if err != nil {
		return errors.Wrapf(err, "set.FromFile(%s) failed", s.Src)
	}

	// create TempFile in Dest directory to avoid cross-filesystem issues
	if s.MkDirs {
		if err := os.MkdirAll(filepath.Dir(s.Dst), 0755); err != nil {
			return errors.Wrap(err, "MkdirAll failed")
		}
	}
	temp, err := ioutil.TempFile(filepath.Dir(s.Dst), "."+filepath.Base(s.Dst))
	if err != nil {
		return errors.Wrap(err, "couldn't create tempfile")
	}

	if err = tmpl.ExecuteWriter(funcMap, temp); err != nil {
		temp.Close()
		os.Remove(temp.Name())
		return errors.Wrap(err, "template execution failed")
	}

	temp.Close()

	fileMode, err := s.getFileMode()
	if err != nil {
		return errors.Wrap(err, "getFileMode failed")
	}

	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), fileMode)
	os.Chown(temp.Name(), s.UID, s.GID)
	s.stageFile = temp

	return nil
}

// syncFiles compares the staged and dest config files and attempts to sync them
// if they differ. syncFiles will run a config check command if set before
// overwriting the target config file. Finally, syncFile will run a reload command
// if set to have the application or service pick up the changes.
// It returns a boolean indicating if the file has changed and an error if any.
func (s *Renderer) syncFiles(runCommands bool) (bool, error) {
	var changed bool
	staged := s.stageFile.Name()
	defer os.Remove(staged)

	s.logger.WithFields(logrus.Fields{
		"staged": path.Base(staged),
		"dest":   s.Dst,
	}).Debug("comparing staged and dest config files")

	ok, err := fileutil.SameFile(staged, s.Dst, s.logger)
	if err != nil {
		s.logger.Error(err.Error())
	}

	if !ok {
		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Info("target config out of sync")

		if runCommands {
			if err := s.check(staged); err != nil {
				return changed, errors.Wrap(err, "config check failed")
			}
		}

		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Debug("overwriting target config")

		fileMode, err := s.getFileMode()
		if err != nil {
			return changed, errors.Wrap(err, "getFileMode failed")
		}
		if err := fileutil.ReplaceFile(staged, s.Dst, fileMode, s.logger); err != nil {
			return changed, errors.Wrap(err, "replace file failed")
		}

		// make sure owner and group match the temp file, in case the file was created with WriteFile
		os.Chown(s.Dst, s.UID, s.GID)
		changed = true

		if runCommands {
			if err := s.reload(s.Dst); err != nil {
				return changed, errors.Wrap(err, "reload command failed")
			}
		}

		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Info("target config has been updated")

	} else {
		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Debug("target config in sync")

	}
	return changed, nil
}

func (s *Renderer) getFileMode() (os.FileMode, error) {
	if s.Mode == "" {
		if !fileutil.IsFileExist(s.Dst) {
			return 0644, nil
		}
		fi, err := os.Stat(s.Dst)
		if err != nil {
			return 0, errors.Wrap(err, "os.Stat failed")
		}
		return fi.Mode(), nil
	}
	mode, err := strconv.ParseUint(s.Mode, 0, 32)
	if err != nil {
		return 0, errors.Wrapf(err, "parsing filemode failed: %s", s.Mode)
	}
	return os.FileMode(mode), nil

}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config file.
// It returns nil if the check command returns 0 and there are no other errors.
func (s *Renderer) check(stageFile string) error {
	if s.CheckCmd == "" {
		return nil
	}
	var cmdBuffer bytes.Buffer
	data := make(map[string]string)
	data["src"] = stageFile
	tmpl, err := template.New("checkcmd").Parse(s.CheckCmd)
	if err != nil {
		return errors.Wrap(err, "parsing check command failed")
	}
	if err := tmpl.Execute(&cmdBuffer, data); err != nil {
		return errors.Wrap(err, "template execution failed")
	}
	s.logger.Debug("Running " + cmdBuffer.String())
	c := exec.Command("/bin/sh", "-c", cmdBuffer.String())

	if s.ReapLock != nil {
		s.ReapLock.RLock()
		defer s.ReapLock.RUnlock()
	}

	output, err := c.CombinedOutput()
	if err != nil {
		s.logger.Error(fmt.Sprintf("%q", string(output)))
		return errors.Wrap(err, "the check command failed")
	}
	s.logger.Debug(fmt.Sprintf("%q", string(output)))
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0 and an error otherwise.
func (s *Renderer) reload(renderedFile string) error {
	if s.ReloadCmd == "" {
		return nil
	}
	cmd, err := renderTemplate(s.ReloadCmd, map[string]string{"dst": renderedFile})
	if err != nil {
		return errors.Wrap(err, "rendering reload command failed")
	}
	output, err := execCommand(cmd, s.logger, s.ReapLock)
	if err != nil {
		s.logger.Error(fmt.Sprintf("%q", string(output)))
		return errors.Wrap(err, "the reload command failed")
	}
	s.logger.Debug(fmt.Sprintf("%q", string(output)))
	return nil
}

func renderTemplate(unparsed string, data interface{}) (string, error) {
	var rendered bytes.Buffer
	tmpl, err := template.New("").Parse(unparsed)
	if err != nil {
		return "", errors.Wrap(err, "parsing template failed")
	}
	if err := tmpl.Execute(&rendered, data); err != nil {
		return "", errors.Wrap(err, "template execution failed")
	}
	return rendered.String(), nil
}

func execCommand(cmd string, logger *logrus.Entry, rl *sync.RWMutex) ([]byte, error) {
	logger.Debugf("Running %q", cmd)
	c := exec.Command("/bin/sh", "-c", cmd)

	if rl != nil {
		rl.RLock()
		defer rl.RUnlock()
	}

	return c.CombinedOutput()
}
<commit_msg>Abstract templating and command exec logic from the 'check_cmd'<commit_after>/*
 * This file is part of remco.
 * Based on code from confd. https://github.com/kelseyhightower/confd
 * © 2013 Kelsey Hightower
 * © 2016 The Remco Authors
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 */

package template

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"path"
	"path/filepath"
	"strconv"
	"sync"
	"text/template"

	"github.com/HeavyHorst/pongo2"
	"github.com/HeavyHorst/remco/pkg/template/fileutil"
	"github.com/pkg/errors"
	"github.com/sirupsen/logrus"
)

func init() {
	pongo2.SetAutoescape(false)
}

// Renderer contains all data needed for the template processing
type Renderer struct {
	Src       string `json:"src"`
	Dst       string `json:"dst"`
	MkDirs    bool   `toml:"make_directories"`
	Mode      string `json:"mode"`
	UID       int    `json:"uid"`
	GID       int    `json:"gid"`
	ReloadCmd string `toml:"reload_cmd" json:"reload_cmd"`
	CheckCmd  string `toml:"check_cmd" json:"check_cmd"`
	stageFile *os.File
	logger    *logrus.Entry
	ReapLock  *sync.RWMutex
}

// createStageFile stages the src configuration file by processing the src
// template and setting the desired owner, group, and mode. It also sets the
// StageFile for the template resource.
// It returns an error if any.
func (s *Renderer) createStageFile(funcMap map[string]interface{}) error {
	if !fileutil.IsFileExist(s.Src) {
		return fmt.Errorf("missing template: %s", s.Src)
	}

	s.logger.WithFields(logrus.Fields{
		"template": s.Src,
	}).Debug("compiling source template")

	set := pongo2.NewSet("local", &pongo2.LocalFilesystemLoader{})
	set.Options = &pongo2.Options{
		TrimBlocks:   true,
		LStripBlocks: true,
	}
	tmpl, err := set.FromFile(s.Src)
	if err != nil {
		return errors.Wrapf(err, "set.FromFile(%s) failed", s.Src)
	}

	// create TempFile in Dest directory to avoid cross-filesystem issues
	if s.MkDirs {
		if err := os.MkdirAll(filepath.Dir(s.Dst), 0755); err != nil {
			return errors.Wrap(err, "MkdirAll failed")
		}
	}
	temp, err := ioutil.TempFile(filepath.Dir(s.Dst), "."+filepath.Base(s.Dst))
	if err != nil {
		return errors.Wrap(err, "couldn't create tempfile")
	}

	if err = tmpl.ExecuteWriter(funcMap, temp); err != nil {
		temp.Close()
		os.Remove(temp.Name())
		return errors.Wrap(err, "template execution failed")
	}

	temp.Close()

	fileMode, err := s.getFileMode()
	if err != nil {
		return errors.Wrap(err, "getFileMode failed")
	}

	// Set the owner, group, and mode on the stage file now to make it easier to
	// compare against the destination configuration file later.
	os.Chmod(temp.Name(), fileMode)
	os.Chown(temp.Name(), s.UID, s.GID)
	s.stageFile = temp

	return nil
}

// syncFiles compares the staged and dest config files and attempts to sync them
// if they differ. syncFiles will run a config check command if set before
// overwriting the target config file. Finally, syncFile will run a reload command
// if set to have the application or service pick up the changes.
// It returns a boolean indicating if the file has changed and an error if any.
func (s *Renderer) syncFiles(runCommands bool) (bool, error) {
	var changed bool
	staged := s.stageFile.Name()
	defer os.Remove(staged)

	s.logger.WithFields(logrus.Fields{
		"staged": path.Base(staged),
		"dest":   s.Dst,
	}).Debug("comparing staged and dest config files")

	ok, err := fileutil.SameFile(staged, s.Dst, s.logger)
	if err != nil {
		s.logger.Error(err.Error())
	}

	if !ok {
		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Info("target config out of sync")

		if runCommands {
			if err := s.check(staged); err != nil {
				return changed, errors.Wrap(err, "config check failed")
			}
		}

		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Debug("overwriting target config")

		fileMode, err := s.getFileMode()
		if err != nil {
			return changed, errors.Wrap(err, "getFileMode failed")
		}
		if err := fileutil.ReplaceFile(staged, s.Dst, fileMode, s.logger); err != nil {
			return changed, errors.Wrap(err, "replace file failed")
		}

		// make sure owner and group match the temp file, in case the file was created with WriteFile
		os.Chown(s.Dst, s.UID, s.GID)
		changed = true

		if runCommands {
			if err := s.reload(s.Dst); err != nil {
				return changed, errors.Wrap(err, "reload command failed")
			}
		}

		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Info("target config has been updated")

	} else {
		s.logger.WithFields(logrus.Fields{
			"config": s.Dst,
		}).Debug("target config in sync")

	}
	return changed, nil
}

func (s *Renderer) getFileMode() (os.FileMode, error) {
	if s.Mode == "" {
		if !fileutil.IsFileExist(s.Dst) {
			return 0644, nil
		}
		fi, err := os.Stat(s.Dst)
		if err != nil {
			return 0, errors.Wrap(err, "os.Stat failed")
		}
		return fi.Mode(), nil
	}
	mode, err := strconv.ParseUint(s.Mode, 0, 32)
	if err != nil {
		return 0, errors.Wrapf(err, "parsing filemode failed: %s", s.Mode)
	}
	return os.FileMode(mode), nil

}

// check executes the check command to validate the staged config file. The
// command is modified so that any references to src template are substituted
// with a string representing the full path of the staged file. This allows the
// check to be run on the staged file before overwriting the destination config file.
// It returns nil if the check command returns 0 and there are no other errors.
func (s *Renderer) check(stageFile string) error {
	if s.CheckCmd == "" {
		return nil
	}
	cmd, err := renderTemplate(s.CheckCmd, map[string]string{"src": stageFile})
	if err != nil {
		return errors.Wrap(err, "rendering check command failed")
	}
	output, err := execCommand(cmd, s.logger, s.ReapLock)
	if err != nil {
		s.logger.Error(fmt.Sprintf("%q", string(output)))
		return errors.Wrap(err, "the check command failed")
	}
	s.logger.Debug(fmt.Sprintf("%q", string(output)))
	return nil
}

// reload executes the reload command.
// It returns nil if the reload command returns 0 and an error otherwise.
func (s *Renderer) reload(renderedFile string) error {
	if s.ReloadCmd == "" {
		return nil
	}
	cmd, err := renderTemplate(s.ReloadCmd, map[string]string{"dst": renderedFile})
	if err != nil {
		return errors.Wrap(err, "rendering reload command failed")
	}
	output, err := execCommand(cmd, s.logger, s.ReapLock)
	if err != nil {
		s.logger.Error(fmt.Sprintf("%q", string(output)))
		return errors.Wrap(err, "the reload command failed")
	}
	s.logger.Debug(fmt.Sprintf("%q", string(output)))
	return nil
}

func renderTemplate(unparsed string, data interface{}) (string, error) {
	var rendered bytes.Buffer
	tmpl, err := template.New("").Parse(unparsed)
	if err != nil {
		return "", errors.Wrap(err, "parsing template failed")
	}
	if err := tmpl.Execute(&rendered, data); err != nil {
		return "", errors.Wrap(err, "template execution failed")
	}
	return rendered.String(), nil
}

func execCommand(cmd string, logger *logrus.Entry, rl *sync.RWMutex) ([]byte, error) {
	logger.Debugf("Running %q", cmd)
	c := exec.Command("/bin/sh", "-c", cmd)

	if rl != nil {
		rl.RLock()
		defer rl.RUnlock()
	}

	return c.CombinedOutput()
}
<|endoftext|>


<commit_before>package term // import "github.com/docker/docker/pkg/term"

import (
	"io"
	"os"
	"os/signal"
	"syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE

	"github.com/Azure/go-ansiterm/winterm"
	"github.com/docker/docker/pkg/term/windows"
)

// State holds the console mode for the terminal.
type State struct {
	mode uint32
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
}

// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
var vtInputSupported bool

// StdStreams returns the standard streams (stdin, stdout, stderr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	// Turn on VT handling on all std handles, if possible. This might
	// fail, in which case we will fall back to terminal emulation.
	var emulateStdin, emulateStdout, emulateStderr bool
	fd := os.Stdin.Fd()
	if mode, err := winterm.GetConsoleMode(fd); err == nil {
		// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
			emulateStdin = true
		} else {
			vtInputSupported = true
		}
		// Unconditionally set the console mode back even on failure because SetConsoleMode
		// remembers invalid bits on input handles.
		winterm.SetConsoleMode(fd, mode)
	}

	fd = os.Stdout.Fd()
	if mode, err := winterm.GetConsoleMode(fd); err == nil {
		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
			emulateStdout = true
		} else {
			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
		}
	}

	fd = os.Stderr.Fd()
	if mode, err := winterm.GetConsoleMode(fd); err == nil {
		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
			emulateStderr = true
		} else {
			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
		}
	}

	if os.Getenv("ConEmuANSI") == "ON" || os.Getenv("ConsoleZVersion") != "" {
		// The ConEmu and ConsoleZ terminals emulate ANSI on output streams well.
		emulateStdin = true
		emulateStdout = false
		emulateStderr = false
	}

	// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
	// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
	// go-ansiterm hasn't switch to x/sys/windows.
	// TODO: switch back to x/sys/windows once go-ansiterm has switched
	if emulateStdin {
		stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE)
	} else {
		stdIn = os.Stdin
	}

	if emulateStdout {
		stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
	} else {
		stdOut = os.Stdout
	}

	if emulateStderr {
		stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
	} else {
		stdErr = os.Stderr
	}

	return
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
	return windowsconsole.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
	info, err := winterm.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	winsize := &Winsize{
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
	}

	return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	return windowsconsole.IsConsole(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	return winterm.SetConsoleMode(fd, state.mode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
	mode, e := winterm.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}

	return &State{mode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
	mode := state.mode
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
	err := winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state. On UNIX, this puts both the input
// and output into raw mode. On Windows, it only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (*State, error) {
	state, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return state, err
}

// SetRawTerminalOutput puts the output of terminal connected to the given file
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
// state. On Windows, it disables LF -> CRLF translation.
func SetRawTerminalOutput(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
	// version of Windows.
	winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN)
	return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	mode := state.mode

	// See
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

	// Disable these modes
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode &^= winterm.ENABLE_LINE_INPUT
	mode &^= winterm.ENABLE_MOUSE_INPUT
	mode &^= winterm.ENABLE_WINDOW_INPUT
	mode &^= winterm.ENABLE_PROCESSED_INPUT

	// Enable these modes
	mode |= winterm.ENABLE_EXTENDED_FLAGS
	mode |= winterm.ENABLE_INSERT_MODE
	mode |= winterm.ENABLE_QUICK_EDIT_MODE
	if vtInputSupported {
		mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT
	}

	err = winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func restoreAtInterrupt(fd uintptr, state *State) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)

	go func() {
		_ = <-sigchan
		RestoreTerminal(fd, state)
		os.Exit(0)
	}()
}
<commit_msg>Fix #28814<commit_after>package term // import "github.com/docker/docker/pkg/term"

import (
	"io"
	"os"
	"os/signal"
	"syscall" // used for STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and STD_ERROR_HANDLE

	"github.com/Azure/go-ansiterm/winterm"
	"github.com/docker/docker/pkg/term/windows"
)

// State holds the console mode for the terminal.
type State struct {
	mode uint32
}

// Winsize is used for window size.
type Winsize struct {
	Height uint16
	Width  uint16
}

// vtInputSupported is true if winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported by the console
var vtInputSupported bool

// StdStreams returns the standard streams (stdin, stdout, stderr).
func StdStreams() (stdIn io.ReadCloser, stdOut, stdErr io.Writer) {
	// Turn on VT handling on all std handles, if possible. This might
	// fail, in which case we will fall back to terminal emulation.
	var emulateStdin, emulateStdout, emulateStderr bool
	fd := os.Stdin.Fd()
	if mode, err := winterm.GetConsoleMode(fd); err == nil {
		// Validate that winterm.ENABLE_VIRTUAL_TERMINAL_INPUT is supported, but do not set it.
		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_INPUT); err != nil {
			emulateStdin = true
		} else {
			vtInputSupported = true
		}
		// Unconditionally set the console mode back even on failure because SetConsoleMode
		// remembers invalid bits on input handles.
		winterm.SetConsoleMode(fd, mode)
	}

	fd = os.Stdout.Fd()
	if mode, err := winterm.GetConsoleMode(fd); err == nil {
		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
			emulateStdout = true
		} else {
			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
		}
	}

	fd = os.Stderr.Fd()
	if mode, err := winterm.GetConsoleMode(fd); err == nil {
		// Validate winterm.DISABLE_NEWLINE_AUTO_RETURN is supported, but do not set it.
		if err = winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING|winterm.DISABLE_NEWLINE_AUTO_RETURN); err != nil {
			emulateStderr = true
		} else {
			winterm.SetConsoleMode(fd, mode|winterm.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
		}
	}

	// Temporarily use STD_INPUT_HANDLE, STD_OUTPUT_HANDLE and
	// STD_ERROR_HANDLE from syscall rather than x/sys/windows as long as
	// go-ansiterm hasn't switch to x/sys/windows.
	// TODO: switch back to x/sys/windows once go-ansiterm has switched
	if emulateStdin {
		stdIn = windowsconsole.NewAnsiReader(syscall.STD_INPUT_HANDLE)
	} else {
		stdIn = os.Stdin
	}

	if emulateStdout {
		stdOut = windowsconsole.NewAnsiWriter(syscall.STD_OUTPUT_HANDLE)
	} else {
		stdOut = os.Stdout
	}

	if emulateStderr {
		stdErr = windowsconsole.NewAnsiWriter(syscall.STD_ERROR_HANDLE)
	} else {
		stdErr = os.Stderr
	}

	return
}

// GetFdInfo returns the file descriptor for an os.File and indicates whether the file represents a terminal.
func GetFdInfo(in interface{}) (uintptr, bool) {
	return windowsconsole.GetHandleInfo(in)
}

// GetWinsize returns the window size based on the specified file descriptor.
func GetWinsize(fd uintptr) (*Winsize, error) {
	info, err := winterm.GetConsoleScreenBufferInfo(fd)
	if err != nil {
		return nil, err
	}

	winsize := &Winsize{
		Width:  uint16(info.Window.Right - info.Window.Left + 1),
		Height: uint16(info.Window.Bottom - info.Window.Top + 1),
	}

	return winsize, nil
}

// IsTerminal returns true if the given file descriptor is a terminal.
func IsTerminal(fd uintptr) bool {
	return windowsconsole.IsConsole(fd)
}

// RestoreTerminal restores the terminal connected to the given file descriptor
// to a previous state.
func RestoreTerminal(fd uintptr, state *State) error {
	return winterm.SetConsoleMode(fd, state.mode)
}

// SaveState saves the state of the terminal connected to the given file descriptor.
func SaveState(fd uintptr) (*State, error) {
	mode, e := winterm.GetConsoleMode(fd)
	if e != nil {
		return nil, e
	}

	return &State{mode: mode}, nil
}

// DisableEcho disables echo for the terminal connected to the given file descriptor.
// -- See https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx
func DisableEcho(fd uintptr, state *State) error {
	mode := state.mode
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode |= winterm.ENABLE_PROCESSED_INPUT | winterm.ENABLE_LINE_INPUT
	err := winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return nil
}

// SetRawTerminal puts the terminal connected to the given file descriptor into
// raw mode and returns the previous state. On UNIX, this puts both the input
// and output into raw mode. On Windows, it only puts the input into raw mode.
func SetRawTerminal(fd uintptr) (*State, error) {
	state, err := MakeRaw(fd)
	if err != nil {
		return nil, err
	}

	// Register an interrupt handler to catch and restore prior state
	restoreAtInterrupt(fd, state)
	return state, err
}

// SetRawTerminalOutput puts the output of terminal connected to the given file
// descriptor into raw mode. On UNIX, this does nothing and returns nil for the
// state. On Windows, it disables LF -> CRLF translation.
func SetRawTerminalOutput(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	// Ignore failures, since winterm.DISABLE_NEWLINE_AUTO_RETURN might not be supported on this
	// version of Windows.
	winterm.SetConsoleMode(fd, state.mode|winterm.DISABLE_NEWLINE_AUTO_RETURN)
	return state, err
}

// MakeRaw puts the terminal (Windows Console) connected to the given file descriptor into raw
// mode and returns the previous state of the terminal so that it can be restored.
func MakeRaw(fd uintptr) (*State, error) {
	state, err := SaveState(fd)
	if err != nil {
		return nil, err
	}

	mode := state.mode

	// See
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms686033(v=vs.85).aspx
	// -- https://msdn.microsoft.com/en-us/library/windows/desktop/ms683462(v=vs.85).aspx

	// Disable these modes
	mode &^= winterm.ENABLE_ECHO_INPUT
	mode &^= winterm.ENABLE_LINE_INPUT
	mode &^= winterm.ENABLE_MOUSE_INPUT
	mode &^= winterm.ENABLE_WINDOW_INPUT
	mode &^= winterm.ENABLE_PROCESSED_INPUT

	// Enable these modes
	mode |= winterm.ENABLE_EXTENDED_FLAGS
	mode |= winterm.ENABLE_INSERT_MODE
	mode |= winterm.ENABLE_QUICK_EDIT_MODE
	if vtInputSupported {
		mode |= winterm.ENABLE_VIRTUAL_TERMINAL_INPUT
	}

	err = winterm.SetConsoleMode(fd, mode)
	if err != nil {
		return nil, err
	}
	return state, nil
}

func restoreAtInterrupt(fd uintptr, state *State) {
	sigchan := make(chan os.Signal, 1)
	signal.Notify(sigchan, os.Interrupt)

	go func() {
		_ = <-sigchan
		RestoreTerminal(fd, state)
		os.Exit(0)
	}()
}
<|endoftext|>


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

package pwdTool

import (
	"fmt"
	"os"

	"github.com/howeyc/gopass"
	"github.com/opensds/opensds/pkg/utils/pwd"
)

func main() {
	fmt.Print("Enter password: ")
	plainText, err := gopass.GetPasswdMasked()
	if err != nil {
		fmt.Println("Input password error: %v", err)
		os.Exit(1)
	}

	// Encrypt the password
	pwdTool := pwd.NewPwdTool("aes")
	pwd, err := pwdTool.Encrypter(string(plainText))

	if err != nil {
		fmt.Println("Encrypt password error: %v", err)
		os.Exit(1)
	}

	fmt.Println("Encrypted password:", pwd)
}
<commit_msg>fix bugs<commit_after>// Copyright (c) 2019 Huawei Technologies Co., Ltd. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package pwdTool

import (
	"fmt"
	"os"

	"github.com/howeyc/gopass"
	"github.com/opensds/opensds/pkg/utils/pwd"
)

func main() {
	fmt.Print("Enter password: ")
	plainText, err := gopass.GetPasswdMasked()
	if err != nil {
		fmt.Printf("Input password error: %v\n", err)
		os.Exit(1)
	}

	// Encrypt the password
	pwdTool := pwd.NewPwdTool("aes")
	pwd, err := pwdTool.Encrypter(string(plainText))

	if err != nil {
		fmt.Printf("Encrypt password error: %v\n", err)
		os.Exit(1)
	}

	fmt.Println("Encrypted password:", pwd)
}
<|endoftext|>


<commit_before>package aabb

import (
	"github.com/teomat/mater/vect"
)

//axis aligned bounding box.
type AABB struct {
	Lower, Upper vect.Vect
}

//returns the center of the aabb
func (aabb *AABB) Center() vect.Vect {
	return vect.Mult(vect.Add(aabb.Lower, aabb.Upper), 0.5)
}

//returns if other is contained inside this aabb.
func (aabb *AABB) Contains(other AABB) bool {
	return aabb.Lower.X <= other.Lower.X &&
		aabb.Upper.X >= other.Upper.X &&
		aabb.Lower.Y <= other.Lower.Y &&
		aabb.Upper.Y >= other.Upper.Y
}

//returns if v is contained inside this aabb.
func (aabb *AABB) ContainsVect(v vect.Vect) bool {
	return aabb.Lower.X <= v.X &&
		aabb.Upper.X >= v.X &&
		aabb.Lower.Y <= v.Y &&
		aabb.Upper.Y >= v.Y
}

//returns an AABB that holds both a and b.
func Combine(a, b AABB) AABB {
	return AABB{
		vect.Min(a.Lower, b.Lower),
		vect.Max(a.Upper, b.Upper),
	}
}

//returns an AABB that holds both a and v.
func Expand(a AABB, v vect.Vect) AABB {
	return AABB{
		vect.Min(a.Lower, v),
		vect.Max(a.Upper, v),
	}
}

//returns the area of the bounding box.
func (aabb *AABB) Area() float64 {
	return (aabb.Upper.X - aabb.Lower.X) * (aabb.Upper.Y - aabb.Lower.Y)
}

func TestOverlap(a, b AABB) bool {
	d1 := vect.Sub(b.Lower, a.Upper)
	d2 := vect.Sub(a.Lower, b.Upper)

	if d1.X > 0.0 || d1.Y > 0.0 {
		return false
	}

	if d2.X > 0.0 || d2.Y > 0.0 {
		return false
	}

	return true
}
<commit_msg>added aabb.Extents and aabb.Perimeter functions<commit_after>package aabb

import (
	"github.com/teomat/mater/vect"
)

//axis aligned bounding box.
type AABB struct {
	Lower, Upper vect.Vect
}

//returns the center of the aabb
func (aabb *AABB) Center() vect.Vect {
	return vect.Mult(vect.Add(aabb.Lower, aabb.Upper), 0.5)
}

//returns if other is contained inside this aabb.
func (aabb *AABB) Contains(other AABB) bool {
	return aabb.Lower.X <= other.Lower.X &&
		aabb.Upper.X >= other.Upper.X &&
		aabb.Lower.Y <= other.Lower.Y &&
		aabb.Upper.Y >= other.Upper.Y
}

//returns if v is contained inside this aabb.
func (aabb *AABB) ContainsVect(v vect.Vect) bool {
	return aabb.Lower.X <= v.X &&
		aabb.Upper.X >= v.X &&
		aabb.Lower.Y <= v.Y &&
		aabb.Upper.Y >= v.Y
}

func (aabb *AABB) Extents () vect.Vect {
	return vect.Mult(vect.Sub(aabb.Upper, aabb.Lower),.5)
}

func (aabb *AABB) Perimeter () float64 {
	w := vect.Sub(aabb.Upper, aabb.Lower)
	return 2 * (w.X + w.Y)
}

//returns an AABB that holds both a and b.
func Combine(a, b AABB) AABB {
	return AABB{
		vect.Min(a.Lower, b.Lower),
		vect.Max(a.Upper, b.Upper),
	}
}

//returns an AABB that holds both a and v.
func Expand(a AABB, v vect.Vect) AABB {
	return AABB{
		vect.Min(a.Lower, v),
		vect.Max(a.Upper, v),
	}
}

//returns the area of the bounding box.
func (aabb *AABB) Area() float64 {
	return (aabb.Upper.X - aabb.Lower.X) * (aabb.Upper.Y - aabb.Lower.Y)
}

func TestOverlap(a, b AABB) bool {
	d1 := vect.Sub(b.Lower, a.Upper)
	d2 := vect.Sub(a.Lower, b.Upper)

	if d1.X > 0.0 || d1.Y > 0.0 {
		return false
	}

	if d2.X > 0.0 || d2.Y > 0.0 {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package algorithm

import (
	sort "github.com/ilovelili/Algorithms/sorts"

	"github.com/ilovelili/Algorithms/algorithmutil"
)

var (
	sorter *sort.Sorter
)

// BubbleSort BubbleSort entry
func BubbleSort(arrayzor []algorithmutil.IComparable) []algorithmutil.IComparable {
	return sorter.BubbleSort(arrayzor)
}
<commit_msg>remove wrapper<commit_after><|endoftext|>


<commit_before>package alog

import "testing"

// A test appender that adds messages to a list
type TestAppender struct {
	MessageQueue chan string
	quit         chan int
	logs         []string
}

func (app *TestAppender) StartListening(l *Logger) {
	for {
		select {
		case msg := <-app.MessageQueue:
			app.logs = append(app.logs, msg)
		case <-app.quit:
			l.wg.Done()
			return
		}
	}
}

func (app *TestAppender) Append(msg string) {
	app.MessageQueue <- msg
}

// Finalize triggers the end of the logging loop
func (app *TestAppender) Finalize() {
	app.quit <- 1
}
func TestLogger(t *testing.T) {
	var appender TestAppender
	appender.MessageQueue = make(chan string)
	appender.logs = make([]string, 0)
	appender.quit = make(chan int)
	logger := NewLogger("testlogger", []Appender{&appender})
	if logger == nil {
		t.Fail()
	}
	if len(logger.appenders) != 1 {
		t.Fail()
	}
	logger.Log("Test message", INFO)
	logger.Finalize()
}
<commit_msg>Added extra checks in test<commit_after>package alog

import (
	"strings"
	"testing"
)

// A test appender that adds messages to a list
type TestAppender struct {
	MessageQueue chan string
	quit         chan int
	logs         []string
}

func (app *TestAppender) StartListening(l *Logger) {
	for {
		select {
		case msg := <-app.MessageQueue:
			app.logs = append(app.logs, msg)
		case <-app.quit:
			l.wg.Done()
			return
		}
	}
}

func (app *TestAppender) Append(msg string) {
	app.MessageQueue <- msg
}

// Finalize triggers the end of the logging loop
func (app *TestAppender) Finalize() {
	app.quit <- 1
}
func TestLogger(t *testing.T) {
	var appender TestAppender
	appender.MessageQueue = make(chan string)
	appender.logs = make([]string, 0)
	appender.quit = make(chan int)
	logger := NewLogger("testlogger", []Appender{&appender})
	if logger == nil {
		t.Fail()
	}
	if len(logger.appenders) != 1 {
		t.Fail()
	}
	logger.Log("Test message", INFO)
	if len(appender.logs) != 1 {
		t.Fail()
	}
	if strings.Compare(appender.logs[0], "Test message") != 0 {
		t.Fail()
	}
	logger.Finalize()
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// kill is a channel that is used during testing only to trigger a termination of all commands
// and an immediate return from the alternate function.
var kill chan struct{}

// alternate runs a command with alternating parameters inserted in place of the placeholder. Each
// time a SIGUSR1 is received, a new command is run with the next parameter, and a SIGINT is sent to
// the previous command after the overlap duration has elapsed. The alternate logs are written to
// stderr, and the command logs are written to cmdStdout and cmdStderr.
func alternate(command string, placeholder string, params []string, overlap time.Duration,
	stderr, cmdStdout, cmdStderr io.Writer) {

	log.SetPrefix("alternate | ")
	log.SetOutput(stderr)
	log.SetFlags(0)

	log.Printf("Starting with command %q, placeholder %q, params = %q, overlap = %v\n",
		command, placeholder, params, overlap)

	// When a command exits, cmdExit receives the parameter this command was run with.
	cmdExit := make(chan string)

	// When the overlap duration has elapsed, overlapEnd receives an empty struct.
	overlapEnd := make(chan struct{})

	next := make(chan os.Signal, 1)
	// Buffer a fake signal to run the first command.
	next <- syscall.Signal(0)
	// Then listen to USR1 signals dispatched by the user.
	signal.Notify(next, syscall.SIGUSR1)

	m := newManager(params)

	for {
		select {
		case <-testExit:
		case <-kill:
			log.Println("Test exit channel received a value, exiting alternate")
			killAllCmds(params, m)
			return

		case param := <-cmdExit:
			log.Printf("Command with parameter %q exited\n", param)
			m.unsetCmd(param)
			if !m.hasCmds() {
				log.Println("All commands have exited, exiting alternate")
				return
			}

		case <-overlapEnd:
			finishRotation(m)

		case signal := <-next:
			first := signal != syscall.SIGUSR1
			nextParam := m.nextParam()

			if !first {
				log.Printf("Received signal USR1, rotating to next parameter %q", nextParam)
			}

			if m.nextCmd() != nil {
				log.Printf("A command with parameter %q is already running, cannot run again",
					nextParam)
				break
			}

			s := strings.Replace(command, placeholder, nextParam, 1)
			nextCmd := cmd(s, cmdStdout, cmdStderr)
			m.setCmd(nextParam, nextCmd)

			log.Printf("Running command with parameter %q\n", nextParam)
			if err := run(nextCmd, nextParam, cmdExit); err != nil {
				log.Printf("Failed to run the command with parameter %q, error: %v\n",
					err.Error())
				m.unsetCmd(nextParam)
				break
			}

			if first {
				// There is no previous command to interrupt.
				m.rotate()
				break
			}

			if overlap == 0 {
				finishRotation(m)
			} else {
				log.Printf("Waiting %v before sending interrupt to command with parameter %q\n",
					overlap, m.currentParam())
				go func() {
					time.Sleep(overlap)
					overlapEnd <- struct{}{}
				}()
			}
		}
	}
}

func finishRotation(m *manager) {
	if m.nextCmd() == nil {
		// The next command is not running. Cancel the rotation without interrupting the current
		// command.
		return
	}
	if c := m.currentCmd(); c != nil {
		currentParam := m.currentParam()
		log.Printf("Sending interrupt to command with parameter %q\n", currentParam)
		if err := interruptCmd(c); err != nil {
			log.Printf("Failed to send interrupt to command with parameter %q, error: %v\n",
				currentParam, err)
		}
	}
	m.rotate()
}

// interruptCmd sends SIGINT to a command.
func interruptCmd(c *exec.Cmd) error {
	if c == nil {
		return errors.New("interruptCmd error: cmd is nil")
	}
	p := c.Process
	if p == nil {
		return errors.New("interruptCmd error: cmd.Process is nil")
	}
	p.Signal(os.Interrupt)
	return nil
}

// cmd returns a command built from the given string. The command will print to the given stdout and
// stderr.
func cmd(s string, stdout, stderr io.Writer) *exec.Cmd {
	f := strings.Fields(s)
	c := exec.Command(f[0], f[1:]...)
	c.Stdout = stdout
	c.Stderr = stderr
	return c
}

// run runs a command without blocking. After the command exits, it sends exitMsg on the exit
// channel.
func run(c *exec.Cmd, exitMsg string, exit chan string) error {
	if err := c.Start(); err != nil {
		return err
	}
	go func() {
		c.Wait()
		exit <- exitMsg
	}()
	return nil
}

// killAllCmds sends a SIGKILL to all commands in the manager.
func killAllCmds(params []string, m *manager) {
	for _, param := range params {
		if c := m.cmd(param); c != nil {
			if p := c.Process; p != nil {
				log.Printf("Killing command with parameter %q\n", param)
				p.Signal(os.Kill)
			}
		}
	}
}
<commit_msg>Intercept interrupt signal to exit only after all commands have exited<commit_after>package main

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"os/signal"
	"strings"
	"syscall"
	"time"
)

// kill is a channel that is used during testing only to trigger a termination of all commands
// and an immediate return from the alternate function.
var kill chan struct{}

// alternate runs a command with alternating parameters inserted in place of the placeholder. Each
// time a SIGUSR1 is received, a new command is run with the next parameter, and a SIGINT is sent to
// the previous command after the overlap duration has elapsed. The alternate logs are written to
// stderr, and the command logs are written to cmdStdout and cmdStderr.
func alternate(command string, placeholder string, params []string, overlap time.Duration,
	stderr, cmdStdout, cmdStderr io.Writer) {

	log.SetPrefix("alternate | ")
	log.SetOutput(stderr)
	log.SetFlags(0)

	log.Printf("Starting with command %q, placeholder %q, params = %q, overlap = %v\n",
		command, placeholder, params, overlap)

	// When a command exits, cmdExit receives the parameter this command was run with.
	cmdExit := make(chan string)

	// When the overlap duration has elapsed, overlapEnd receives an empty struct.
	overlapEnd := make(chan struct{})

	next := make(chan os.Signal, 1)
	// Buffer a fake signal to run the first command.
	next <- syscall.Signal(0)
	// Then listen to USR1 signals dispatched by the user.
	signal.Notify(next, syscall.SIGUSR1)

	interrupt := make(chan os.Signal)
	signal.Notify(interrupt, os.Interrupt)

	m := newManager(params)

	for {
		select {
		case <-interrupt:
			log.Println("Intercepted interrupt signal, will exit after all commands have exited")

		case <-kill:
			log.Println("Test exit channel received a value, exiting alternate")
			killAllCmds(params, m)
			return

		case param := <-cmdExit:
			log.Printf("Command with parameter %q exited\n", param)
			m.unsetCmd(param)
			if !m.hasCmds() {
				log.Println("All commands have exited, exiting alternate")
				return
			}

		case <-overlapEnd:
			finishRotation(m)

		case signal := <-next:
			first := signal != syscall.SIGUSR1
			nextParam := m.nextParam()

			if !first {
				log.Printf("Received signal USR1, rotating to next parameter %q", nextParam)
			}

			if m.nextCmd() != nil {
				log.Printf("A command with parameter %q is already running, cannot run again",
					nextParam)
				break
			}

			s := strings.Replace(command, placeholder, nextParam, 1)
			nextCmd := cmd(s, cmdStdout, cmdStderr)
			m.setCmd(nextParam, nextCmd)

			log.Printf("Running command with parameter %q\n", nextParam)
			if err := run(nextCmd, nextParam, cmdExit); err != nil {
				log.Printf("Failed to run the command with parameter %q, error: %v\n",
					err.Error())
				m.unsetCmd(nextParam)
				break
			}

			if first {
				// There is no previous command to interrupt.
				m.rotate()
				break
			}

			if overlap == 0 {
				finishRotation(m)
			} else {
				log.Printf("Waiting %v before sending interrupt to command with parameter %q\n",
					overlap, m.currentParam())
				go func() {
					time.Sleep(overlap)
					overlapEnd <- struct{}{}
				}()
			}
		}
	}
}

func finishRotation(m *manager) {
	if m.nextCmd() == nil {
		// The next command is not running. Cancel the rotation without interrupting the current
		// command.
		return
	}
	if c := m.currentCmd(); c != nil {
		currentParam := m.currentParam()
		log.Printf("Sending interrupt to command with parameter %q\n", currentParam)
		if err := interruptCmd(c); err != nil {
			log.Printf("Failed to send interrupt to command with parameter %q, error: %v\n",
				currentParam, err)
		}
	}
	m.rotate()
}

// interruptCmd sends SIGINT to a command.
func interruptCmd(c *exec.Cmd) error {
	if c == nil {
		return errors.New("interruptCmd error: cmd is nil")
	}
	p := c.Process
	if p == nil {
		return errors.New("interruptCmd error: cmd.Process is nil")
	}
	p.Signal(os.Interrupt)
	return nil
}

// cmd returns a command built from the given string. The command will print to the given stdout and
// stderr.
func cmd(s string, stdout, stderr io.Writer) *exec.Cmd {
	f := strings.Fields(s)
	c := exec.Command(f[0], f[1:]...)
	c.Stdout = stdout
	c.Stderr = stderr
	return c
}

// run runs a command without blocking. After the command exits, it sends exitMsg on the exit
// channel.
func run(c *exec.Cmd, exitMsg string, exit chan string) error {
	if err := c.Start(); err != nil {
		return err
	}
	go func() {
		c.Wait()
		exit <- exitMsg
	}()
	return nil
}

// killAllCmds sends a SIGKILL to all commands in the manager.
func killAllCmds(params []string, m *manager) {
	for _, param := range params {
		if c := m.cmd(param); c != nil {
			if p := c.Process; p != nil {
				log.Printf("Killing command with parameter %q\n", param)
				p.Signal(os.Kill)
			}
		}
	}
}
<|endoftext|>


<commit_before>package divsd

import (
	"fmt"
	"net"
	"time"

	"github.com/hashicorp/memberlist"
	rendezvous "github.com/inercia/divs/divsd/rendezvous"
)

// A rendezvous service: something we can use for joining a groups of nodes that
// also provide a service, or for announcing ourselves...
type RendezvousService interface {
	// Announce the service, publishing with the external address provided
	Announce(string) error

	// Discover other nodes for the same service
	Discover(chan string) error

	// Leave the rendezvous
	Leave() error
}

// the peers manager is responsible for
// - establishing and keeping connections to peers
// - sending/receiving data to/from peers
type NodesManager struct {
	config *Config

	newNodesChan chan string // we'll send to this channel when we discover a new peer

	externalMemberAddr string
	devManager        *DevManager
	members           *memberlist.Memberlist
}

// Create a new peers manager
func NewNodesManager(config *Config, dm *DevManager) (*NodesManager, error) {
	log.Debug("Creating new nodes manager")
	d := NodesManager{
		config:        config,
		newNodesChan:  make(chan string),
		devManager:    dm,
	}

	return &d, nil
}

// Start the nodes manager
func (nm *NodesManager) Start() (err error) {
	membersExternalAddr, err := NewExternalUDPAddr(fmt.Sprintf("%s:%d", nm.config.Global.Host,
		nm.config.Global.Port))

	membersConfig := memberlist.DefaultWANConfig()
	membersConfig.BindAddr = membersExternalAddr.IP.String()
	membersConfig.BindPort = membersExternalAddr.Port
	membersConfig.Delegate = nm
	members, err := memberlist.Create(membersConfig)
	if err != nil {
		return fmt.Errorf("Failed to create memberlist: " + err.Error())
	}
	nm.members = members

	discovered := make(chan string)
	go func() {
		for address := range discovered {
			// Join an existing cluster by specifying at least one known member.
			contacted, err := nm.members.Join([]string{address})
			if err != nil {
				log.Error("Failed to join cluster: " + err.Error())
			} else {
				log.Debug("Joined %d nodes in the cluster", contacted)
			}

			nm.newNodesChan <- address
		}
	}()

	// create the MDNS service
	mdnsAddr := fmt.Sprintf("0.0.0.0:%d", nm.config.Mdns.Port)
	mdnsService, err := rendezvous.NewMdnsService(mdnsAddr, nm.config.Global.Serial)
	if err != nil {
		return err
	}
	mdnsService.Announce(membersExternalAddr.String())
	mdnsService.Discover(discovered)

	// create the DHT service by previously obtaining an external TCP address
	dhtAddr, err := NewExternalTCPAddr(fmt.Sprintf("0.0.0.0:%d", nm.config.Discover.Port))
	if err != nil {
		return
	}
	dhtService, err := rendezvous.NewDhtService(dhtAddr.String(), nm.config.Global.Serial)
	if err != nil {
		return
	}
	dhtService.Announce(membersExternalAddr.String())
	dhtService.Discover(discovered)

	return nil
}

// wait some time for some peers
func (d *NodesManager) Stop() (err error) {
	log.Debug("Signaling stop for discovery")
	// TODO
	return nil
}

// wait some time for some peers
func (this *NodesManager) WaitForNodesTime(seconds time.Duration) (err error) {
	if this.members.NumMembers() > 0 {
		return nil
	} else {
		log.Info("Waiting for %d seconds for peers to join...", seconds)
		select {
		case <-this.newNodesChan:
			err = nil
			break
		case <-time.After(seconds * time.Second):
			err = fmt.Errorf("no valid peers found in %d seconds", seconds)
		}
		return err
	}
}

// Wait for some peers
func (this *NodesManager) WaitForNodes() error {
	if this.members.NumMembers() > 0 {
		return nil
	} else {
		log.Debug("Waiting forever for new peers to join...")
		<-this.newNodesChan
		return nil
	}
}

// Sends some data to some other node
func (this *NodesManager) SendTo(packet []byte, node *Node) error {
	addr := net.IPAddr{node.Addr, ""}
	err := this.members.SendTo(&addr, packet)
	if err != nil {
		return fmt.Errorf("error when sending data to peer %s", node)
	}
	return nil
}

// NodeMeta is used to retrieve meta-data about the current node
// when broadcasting an alive message. It's length is limited to
// the given byte size. This metadata is available in the Node structure.
func (p *NodesManager) NodeMeta(limit int) []byte {
	res := make([]byte, 0)
	return res
}

// NotifyMsg is called when a user-data message is received.
// Care should be taken that this method does not block, since doing
// so would block the entire UDP packet receive loop. Additionally, the byte
// slice may be modified after the call returns, so it should be copied if needed.
func (p *NodesManager) NotifyMsg([]byte) {

}

// GetBroadcasts is called when user data messages can be broadcast.
// It can return a list of buffers to send. Each buffer should assume an
// overhead as provided with a limit on the total byte size allowed.
// The total byte size of the resulting data to send must not exceed
// the limit.
func (p *NodesManager) GetBroadcasts(overhead, limit int) [][]byte {
	res := make([][]byte, 0)
	return res
}

// LocalState is used for a TCP Push/Pull. This is sent to
// the remote side in addition to the membership information. Any
// data can be sent here. See MergeRemoteState as well. The `join`
// boolean indicates this is for a join instead of a push/pull.
func (p *NodesManager) LocalState(join bool) []byte {
	res := make([]byte, 0)
	return res
}

// MergeRemoteState is invoked after a TCP Push/Pull. This is the
// state received from the remote side and is the result of the
// remote side's LocalState call. The 'join'
// boolean indicates this is for a join instead of a push/pull.
func (p *NodesManager) MergeRemoteState(buf []byte, join bool) {

}
<commit_msg>Some refactorings Cleaned up some external IP/port calculations<commit_after>package divsd

import (
	"fmt"
	"net"
	"time"

	"github.com/hashicorp/memberlist"
	rendezvous "github.com/inercia/divs/divsd/rendezvous"
)

// A rendezvous service: something we can use for joining a groups of nodes that
// also provide a service, or for announcing ourselves...
type RendezvousService interface {
	// Announce the service, publishing with the external address provided
	Announce(string) error

	// Discover other nodes for the same service
	Discover(chan string) error

	// Leave the rendezvous
	Leave() error
}

// the peers manager is responsible for
// - establishing and keeping connections to peers
// - sending/receiving data to/from peers
type NodesManager struct {
	config *Config

	newNodesChan chan string // we'll send to this channel when we discover a new peer

	externalMemberAddr string
	devManager        *DevManager
	members           *memberlist.Memberlist
}

// Create a new peers manager
func NewNodesManager(config *Config, dm *DevManager) (*NodesManager, error) {
	log.Debug("Creating new nodes manager")
	d := NodesManager{
		config:        config,
		newNodesChan:  make(chan string),
		devManager:    dm,
	}

	return &d, nil
}

// Start the nodes manager
func (nm *NodesManager) Start(membersExternalAddr net.UDPAddr) (err error) {
	membersConfig := memberlist.DefaultWANConfig()
	membersConfig.BindAddr = membersExternalAddr.IP.String()
	membersConfig.BindPort = membersExternalAddr.Port
	membersConfig.Delegate = nm
	members, err := memberlist.Create(membersConfig)
	if err != nil {
		return fmt.Errorf("Failed to create memberlist: " + err.Error())
	}
	nm.members = members

	discovered := make(chan string)
	go func() {
		for address := range discovered {
			// Join an existing cluster by specifying at least one known member.
			contacted, err := nm.members.Join([]string{address})
			if err != nil {
				log.Error("Failed to join cluster: " + err.Error())
			} else {
				log.Debug("Joined %d nodes in the cluster", contacted)
			}

			nm.newNodesChan <- address
		}
	}()

	// create the MDNS service
	mdnsAddr := fmt.Sprintf("0.0.0.0:%d", nm.config.Mdns.Port)
	mdnsService, err := rendezvous.NewMdnsService(mdnsAddr, nm.config.Global.Serial)
	if err != nil {
		return err
	}
	mdnsService.Announce(membersExternalAddr.String())
	mdnsService.Discover(discovered)

	// create the DHT service by previously obtaining an external TCP address
	dhtAddr, err := NewExternalTCPAddr(fmt.Sprintf("0.0.0.0:%d", nm.config.Discover.Port))
	if err != nil {
		return
	}
	dhtService, err := rendezvous.NewDhtService(dhtAddr.String(), nm.config.Global.Serial)
	if err != nil {
		return
	}
	dhtService.Announce(membersExternalAddr.String())
	dhtService.Discover(discovered)

	return nil
}

// wait some time for some peers
func (d *NodesManager) Stop() (err error) {
	log.Debug("Signaling stop for discovery")
	// TODO
	return nil
}

func (nm *NodesManager) Join(nodes []string) {
	// Join an existing cluster by specifying at least one known member.
	n, err := nm.members.Join(nodes)
	if err != nil {
		log.Fatalf("Failed to join cluster: " + err.Error())
	} else {
		log.Info("Joined %d peers", n)
	}
}

// wait some time for some peers
func (this *NodesManager) WaitForNodesTime(seconds time.Duration) (err error) {
	if this.members.NumMembers() > 0 {
		return nil
	} else {
		log.Info("Waiting for %d seconds for peers to join...", seconds)
		select {
		case <-this.newNodesChan:
			err = nil
			break
		case <-time.After(seconds * time.Second):
			err = fmt.Errorf("no valid peers found in %d seconds", seconds)
		}
		return err
	}
}

// Wait for some peers
func (this *NodesManager) WaitForNodes() error {
	if this.members.NumMembers() > 0 {
		return nil
	} else {
		log.Debug("Waiting forever for new peers to join...")
		<-this.newNodesChan
		return nil
	}
}

// Sends some data to some other node
func (this *NodesManager) SendTo(packet []byte, node *Node) error {
	addr := net.IPAddr{node.Addr, ""}
	err := this.members.SendTo(&addr, packet)
	if err != nil {
		return fmt.Errorf("error when sending data to peer %s", node)
	}
	return nil
}

// NodeMeta is used to retrieve meta-data about the current node
// when broadcasting an alive message. It's length is limited to
// the given byte size. This metadata is available in the Node structure.
func (p *NodesManager) NodeMeta(limit int) []byte {
	res := make([]byte, 0)
	return res
}

// NotifyMsg is called when a user-data message is received.
// Care should be taken that this method does not block, since doing
// so would block the entire UDP packet receive loop. Additionally, the byte
// slice may be modified after the call returns, so it should be copied if needed.
func (p *NodesManager) NotifyMsg([]byte) {

}

// GetBroadcasts is called when user data messages can be broadcast.
// It can return a list of buffers to send. Each buffer should assume an
// overhead as provided with a limit on the total byte size allowed.
// The total byte size of the resulting data to send must not exceed
// the limit.
func (p *NodesManager) GetBroadcasts(overhead, limit int) [][]byte {
	res := make([][]byte, 0)
	return res
}

// LocalState is used for a TCP Push/Pull. This is sent to
// the remote side in addition to the membership information. Any
// data can be sent here. See MergeRemoteState as well. The `join`
// boolean indicates this is for a join instead of a push/pull.
func (p *NodesManager) LocalState(join bool) []byte {
	res := make([]byte, 0)
	return res
}

// MergeRemoteState is invoked after a TCP Push/Pull. This is the
// state received from the remote side and is the result of the
// remote side's LocalState call. The 'join'
// boolean indicates this is for a join instead of a push/pull.
func (p *NodesManager) MergeRemoteState(buf []byte, join bool) {

}
<|endoftext|>


<commit_before>package specs

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-rc3"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<commit_msg>version: master back to -dev<commit_after>package specs

import "fmt"

const (
	// VersionMajor is for an API incompatible changes
	VersionMajor = 1
	// VersionMinor is for functionality in a backwards-compatible manner
	VersionMinor = 0
	// VersionPatch is for backwards-compatible bug fixes
	VersionPatch = 0

	// VersionDev indicates development branch. Releases will be empty string.
	VersionDev = "-rc3-dev"
)

// Version is the specification version that the package types support.
var Version = fmt.Sprintf("%d.%d.%d%s", VersionMajor, VersionMinor, VersionPatch, VersionDev)
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"github.com/square/spincycle/proto"
	"github.com/square/spincycle/spinc/app"
)

type Status struct {
	ctx   app.Context
	reqId string
}

func NewStatus(ctx app.Context) *Status {
	return &Status{
		ctx: ctx,
	}
}

func (c *Status) Prepare() error {
	if len(c.ctx.Command.Args) == 0 {
		return fmt.Errorf("Usage: spinc status <id>\n")
	}
	c.reqId = c.ctx.Command.Args[0]
	return nil
}

func (c *Status) Run() error {
	status, err := c.ctx.RMClient.RequestStatus(c.reqId)
	if err != nil {
		return err
	}
	if c.ctx.Options.Debug {
		app.Debug("status: %#v", status)
	}

	if c.ctx.Hooks.CommandRunResult != nil {
		c.ctx.Hooks.CommandRunResult(status, err)
		return nil
	}

	running := map[string]string{}
	js := status.JobChainStatus.JobStatuses
	for _, job := range js {
		if job.State == proto.STATE_RUNNING {
			running[job.Name] = job.Status
		}
	}

	fmt.Printf("state:      %s\n", proto.StateName[status.State])
	fmt.Printf("running jobs: \n")
	for j, _ := range running {
		fmt.Printf("\t%s\n", j)
	}
	fmt.Printf("jobs done:  %d\n", status.FinishedJobs)
	fmt.Printf("jobs total: %d\n", status.TotalJobs)
	fmt.Printf("created:    %s\n", status.CreatedAt)
	fmt.Printf("started:    %s\n", status.StartedAt)
	fmt.Printf("finished:   %s\n", status.FinishedAt)
	fmt.Printf("user:       %s\n", status.User)
	fmt.Printf("type:       %s\n", status.Type)
	fmt.Printf("id:         %s\n", status.Id)

	if c.ctx.Options.Verbose {
		fmt.Printf("live status:\n")
		for j, s := range running {
			fmt.Printf("            %s: %s\n", j, s)
		}
	}

	return nil
}
<commit_msg>fix spinc real time job status<commit_after>package cmd

import (
	"fmt"

	"github.com/square/spincycle/proto"
	"github.com/square/spincycle/spinc/app"
)

type Status struct {
	ctx   app.Context
	reqId string
}

func NewStatus(ctx app.Context) *Status {
	return &Status{
		ctx: ctx,
	}
}

func (c *Status) Prepare() error {
	if len(c.ctx.Command.Args) == 0 {
		return fmt.Errorf("Usage: spinc status <id>\n")
	}
	c.reqId = c.ctx.Command.Args[0]
	return nil
}

func (c *Status) Run() error {
	status, err := c.ctx.RMClient.RequestStatus(c.reqId)
	if err != nil {
		return err
	}
	if c.ctx.Options.Debug {
		app.Debug("status: %#v", status)
	}

	if c.ctx.Hooks.CommandRunResult != nil {
		c.ctx.Hooks.CommandRunResult(status, err)
		return nil
	}

	jobStatuses := status.JobChainStatus.JobStatuses
	running := []proto.JobStatus{}
	for _, js := range jobStatuses {
		if js.State == proto.STATE_RUNNING {
			running = append(running, js)
		}
	}

	fmt.Printf("state:      %s\n", proto.StateName[status.State])
	fmt.Printf("running jobs: \n")
	for _, js := range running {
		fmt.Printf("\t%s\n", js.Name)
	}
	fmt.Printf("jobs done:  %d\n", status.FinishedJobs)
	fmt.Printf("jobs total: %d\n", status.TotalJobs)
	fmt.Printf("created:    %s\n", status.CreatedAt)
	fmt.Printf("started:    %s\n", status.StartedAt)
	fmt.Printf("finished:   %s\n", status.FinishedAt)
	fmt.Printf("user:       %s\n", status.User)
	fmt.Printf("type:       %s\n", status.Type)
	fmt.Printf("id:         %s\n", status.Id)

	if c.ctx.Options.Verbose {
		fmt.Printf("live status:\n")
		for _, js := range running {
			fmt.Printf("            %s: %s\n", js.Name, js.Status)
		}
	}

	return nil
}
<|endoftext|>