<commit_before>package evolve

import (
	"fmt"
	"math/rand"
	"runtime"
	"time"

	"github.com/aurelien-rainone/evolve/framework"
	"github.com/aurelien-rainone/evolve/worker"
)

// Stepper is the interface implemented by objects having a NextEvolutionStep
// method.
type Stepper interface {

	// NextEvolutionStep performs a single step/iteration of the evolutionary process.
	//
	// - evaluatedPopulation is the population at the beginning of the process.
	// - eliteCount is the number of the fittest individuals that must be
	// preserved.
	//
	// Returns the updated population after the evolutionary process has
	// proceeded by one step/iteration.
	NextEvolutionStep(
		evaluatedPopulation framework.EvaluatedPopulation,
		eliteCount int,
		rng *rand.Rand) framework.EvaluatedPopulation
}

// AbstractEvolutionEngine is a base struc for EvolutionEngine implementations.
type AbstractEvolutionEngine struct {
	// A single multi-threaded worker is shared among multiple evolution engine instances.
	pool                           *worker.Pool
	observers                      map[framework.EvolutionObserver]struct{}
	rng                            *rand.Rand
	candidateFactory               framework.CandidateFactory
	fitnessEvaluator               framework.FitnessEvaluator
	singleThreaded                 bool
	satisfiedTerminationConditions []framework.TerminationCondition
	Stepper
}

// NewAbstractEvolutionEngine creates a new evolution engine by specifying the
// various components required by an evolutionary algorithm.
//
// - candidateFactory is the factory used to create the initial population that
// is iteratively evolved.
// - fitnessEvaluator is a function for assigning fitness scores to candidate
// solutions.
// - rng is the source of randomness used by all stochastic processes (including
// evolutionary operators and selection strategies).
func NewAbstractEvolutionEngine(candidateFactory framework.CandidateFactory,
	fitnessEvaluator framework.FitnessEvaluator,
	rng *rand.Rand,
	stepper Stepper) *AbstractEvolutionEngine {

	return &AbstractEvolutionEngine{
		candidateFactory: candidateFactory,
		fitnessEvaluator: fitnessEvaluator,
		rng:              rng,
		observers:        make(map[framework.EvolutionObserver]struct{}),
		Stepper:          stepper,
	}
}

// Evolve executes the evolutionary algorithm until one of the termination
// conditions is met, then return the fittest candidate from the final
// generation.
//
// To return the entire population rather than just the fittest candidate,
// use the EvolvePopulation method instead.
//
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount is the number of candidates preserved via elitism. In
// elitism, a sub-set of the population with the best fitness scores are
// preserved unchanged in the subsequent generation. Candidate solutions
// that are preserved unchanged through elitism remain eligible for
// selection for breeding the remainder of the next generation. This value
// must be non-negative and less than the population size. A value of zero
// means that no elitism will be applied.
// - conditions is a slice of conditions that may cause the evolution to
// terminate.
//
// Return the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) Evolve(populationSize, eliteCount int,
	conditions ...framework.TerminationCondition) framework.Candidate {

	return e.EvolveWithSeedCandidates(populationSize,
		eliteCount,
		[]framework.Candidate{},
		conditions...)
}

// EvolveWithSeedCandidates executes the evolutionary algorithm until one of
// the termination conditions is met, then return the fittest candidate from
// the final generation. Provide a set of candidates to seed the starting
// population with.
//
// To return the entire population rather than just the fittest candidate,
// use the EvolvePopulationWithSeedCandidates method instead.
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount is the number of candidates preserved via elitism. In
// elitism, a sub-set of the population with the best fitness scores are
// preserved unchanged in the subsequent generation. Candidate solutions
// that are preserved unchanged through elitism remain eligible for
// selection for breeding the remainder of the next generation.  This value
// must be non-negative and less than the population size. A value of zero
// means that no elitism will be applied.
// - seedCandidates is a set of candidates to seed the population with. The
// size of this collection must be no greater than the specified population
// size.
// - conditions is a slice of conditions that may cause the evolution to
// terminate.
//
// Returns the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) EvolveWithSeedCandidates(populationSize, eliteCount int,
	seedCandidates []framework.Candidate,
	conditions ...framework.TerminationCondition) framework.Candidate {

	return e.EvolvePopulationWithSeedCandidates(populationSize,
		eliteCount,
		seedCandidates,
		conditions...)[0].Candidate()
}

// EvolvePopulation executes the evolutionary algorithm until one of the
// termination conditions is met, then return all of the candidates from the
// final generation.
//
// To return just the fittest candidate rather than the entire population,
// use the Evolve method instead.
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount is the number of candidates preserved via elitism. In
// elitism, a sub-set of the population with the best fitness scores are
// preserved unchanged in the subsequent generation. Candidate solutions
// that are preserved unchanged through elitism remain eligible for
// selection for breeding the remainder of the next generation.  This value
// must be non-negative and less than the population size. A value of zero
// means that no elitism will be applied.
// -  conditions is a slice of conditions that may cause the evolution to
// terminate.
//
// Return the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) EvolvePopulation(populationSize, eliteCount int,
	conditions ...framework.TerminationCondition) framework.EvaluatedPopulation {

	return e.EvolvePopulationWithSeedCandidates(populationSize,
		eliteCount,
		[]framework.Candidate{},
		conditions...)
}

// EvolvePopulationWithSeedCandidates executes the evolutionary algorithm
// until one of the termination conditions is met, then return all of the
// candidates from the final generation.
//
// To return just the fittest candidate rather than the entire population,
// use the EvolveWithSeedCandidates method instead.
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount The number of candidates preserved via elitism.  In elitism,
// a sub-set of the population with the best fitness scores are preserved
// unchanged in the subsequent generation.  Candidate solutions that are
// preserved unchanged through elitism remain eligible for selection for
// breeding the remainder of the next generation.  This value must be
// non-negative and less than the population size.  A value of zero means
// that no elitism will be applied.
// - seedCandidates A set of candidates to seed the population with.  The
// size of this collection must be no greater than the specified population
// size.
// - conditions One or more conditions that may cause the evolution to
// terminate.
//
// Return the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) EvolvePopulationWithSeedCandidates(
	populationSize, eliteCount int,
	seedCandidates []framework.Candidate,
	conditions ...framework.TerminationCondition) framework.EvaluatedPopulation {

	if eliteCount < 0 || eliteCount >= populationSize {
		panic("Elite count must be non-negative and less than population size.")
	}
	if len(conditions) == 0 {
		panic("At least one TerminationCondition must be specified.")
	}

	e.satisfiedTerminationConditions = nil
	var currentGenerationIndex int
	startTime := time.Now()

	population := e.candidateFactory.SeedInitialPopulation(populationSize,
		seedCandidates,
		e.rng)

	// Calculate the fitness scores for each member of the initial population.
	evaluatedPopulation := e.evaluatePopulation(population)

	SortEvaluatedPopulation(evaluatedPopulation, e.fitnessEvaluator.IsNatural())
	data := ComputePopulationData(evaluatedPopulation,
		e.fitnessEvaluator.IsNatural(),
		eliteCount,
		currentGenerationIndex,
		startTime)

	// Notify observers of the state of the population.
	e.notifyPopulationChange(data)

	satisfiedConditions := ShouldContinue(data, conditions...)
	for satisfiedConditions == nil {
		currentGenerationIndex++
		evaluatedPopulation = e.NextEvolutionStep(evaluatedPopulation, eliteCount, e.rng)
		SortEvaluatedPopulation(evaluatedPopulation, e.fitnessEvaluator.IsNatural())
		data = ComputePopulationData(evaluatedPopulation,
			e.fitnessEvaluator.IsNatural(),
			eliteCount,
			currentGenerationIndex,
			startTime)
		// Notify observers of the state of the population.
		e.notifyPopulationChange(data)
		satisfiedConditions = ShouldContinue(data, conditions...)
	}
	e.satisfiedTerminationConditions = satisfiedConditions
	return evaluatedPopulation
}

// Takes a population, assigns a fitness score to each member and returns
// the members with their scores attached, sorted in descending order of
// fitness (descending order of fitness score for natural scores, ascending
// order of scores for non-natural scores).
// - population is the population to evaluate (each candidate is assigned a
// fitness score).
//
// Returns the evaluated population (a list of candidates with attached fitness
// scores).
func (e *AbstractEvolutionEngine) evaluatePopulation(population []framework.Candidate) framework.EvaluatedPopulation {
	var evaluatedPopulation framework.EvaluatedPopulation
	// Do fitness evaluations
	var err error
	if e.singleThreaded {
		evaluatedPopulation = make(framework.EvaluatedPopulation, len(population))
		for i, candidate := range population {
			evaluatedPopulation[i], err = framework.NewEvaluatedCandidate(candidate, e.fitnessEvaluator.Fitness(candidate, population))
			if err != nil {
				panic(fmt.Sprintf("Can't evaluate candidate %v: %v", candidate, err))
			}
		}
	} else {
		// Divide the required number of fitness evaluations equally among the
		// available goroutines and coordinate them so that we do not proceed
		// until all of them have finished processing.
		workers := make([]worker.Worker, len(population))
		evaluatedPopulation = make(framework.EvaluatedPopulation, len(population))
		var err error
		for i, candidate := range population {
			func(i int, candidate framework.Candidate) {
				workers[i] = worker.WorkWith(func() interface{} {
					evaluatedPopulation[i], err = framework.NewEvaluatedCandidate(candidate,
						e.fitnessEvaluator.Fitness(candidate, population))
					if err != nil {
						panic(fmt.Sprintf("Error during fitness computation of candidate %v: %v", candidate, err))
					}
					return struct{}{}
				})
			}(i, candidate) // forces the closure on current value of i and candidate
		}

		_, err = e.workerPool().Submit(workers)
		if err != nil {
			panic(fmt.Sprintf("Error while submitting workers to the pool: %v", err))
		}

		// TODO: handle goroutine termination
		/*
		   catch (InterruptedException ex)
		   {
		       // Restore the interrupted status, allows methods further up the call-stack
		       // to abort processing if appropriate.
		       Thread.currentThread().interrupt();
		   }
		*/
	}

	return evaluatedPopulation
}

// SatisfiedTerminationConditions returns a slice of all TerminationCondition's
// that are satisfied by the current state of the evolution engine.
//
// Usually this slice will contain only one item, but it is possible that
// mutliple termination conditions will become satisfied at the same time. In
// this case the condition objects in the slice will be in the same order that
// they were specified when passed to the engine.
//
// If the evolution has not yet terminated (either because it is still in
// progress or because it hasn't even been started) then
// framework.ErrIllegalState is returned.
//
// If the evolution terminated because the request thread was interrupted before
// any termination conditions were satisfied then this method will return an
// empty slice.
//
// The slice is guaranteed to be non-null. The slice may be empty because it is
// possible for evolution to terminate without any conditions being matched.
// The only situation in which this occurs is when the request thread is
// interrupted.
func (e *AbstractEvolutionEngine) SatisfiedTerminationConditions() ([]framework.TerminationCondition, error) {
	if e.satisfiedTerminationConditions == nil {
		//throw new IllegalStateException("EvolutionEngine has not terminated.");
		return nil, framework.ErrIllegalState("evolution engine has not terminated")
	}
	satisfiedTerminationConditions := make([]framework.TerminationCondition, len(e.satisfiedTerminationConditions))
	copy(satisfiedTerminationConditions, e.satisfiedTerminationConditions)
	return satisfiedTerminationConditions, nil
}

// AddEvolutionObserver adds a listener to receive status updates on the
// evolution progress.
//
// Updates are dispatched synchronously on the request thread. Observers should
// complete their processing and return in a timely manner to avoid holding up
// the evolution.
func (e *AbstractEvolutionEngine) AddEvolutionObserver(observer framework.EvolutionObserver) {
	e.observers[observer] = struct{}{}
}

// RemoveEvolutionObserver removes an evolution progress listener.
func (e *AbstractEvolutionEngine) RemoveEvolutionObserver(observer framework.EvolutionObserver) {
	delete(e.observers, observer)
}

// notifyPopulationChange sends the population data to all registered observers.
func (e *AbstractEvolutionEngine) notifyPopulationChange(data *framework.PopulationData) {
	for observer := range e.observers {
		observer.PopulationUpdate(data)
	}
}

// SetSingleThreaded forces evaluation to occur synchronously on the request
// goroutine.
//
// By default, fitness evaluations are performed on separate goroutines (as many
// as there are available cores/processors). This is useful in restricted
// environments where programs are not permitted to start or control threads. It
// might also lead to better performance for programs that have extremely
// lightweight/trivial fitness evaluations.
func (e *AbstractEvolutionEngine) SetSingleThreaded(singleThreaded bool) {
	e.singleThreaded = singleThreaded
}

// workerPool lazily creates the fitness evaluations goroutine pool.
func (e *AbstractEvolutionEngine) workerPool() *worker.Pool {
	if e.pool == nil {
		// create a worker pool and set the maximum number of concurrent
		// goroutines to the number of logical CPUs usable by the current
		// process.
		e.pool = worker.NewPool(runtime.NumCPU())
	}
	return e.pool
}
<commit_msg>Use worker fitness evaluation<commit_after>package evolve

import (
	"fmt"
	"math/rand"
	"runtime"
	"time"

	"github.com/aurelien-rainone/evolve/framework"
	"github.com/aurelien-rainone/evolve/worker"
)

// Stepper is the interface implemented by objects having a NextEvolutionStep
// method.
type Stepper interface {

	// NextEvolutionStep performs a single step/iteration of the evolutionary process.
	//
	// - evaluatedPopulation is the population at the beginning of the process.
	// - eliteCount is the number of the fittest individuals that must be
	// preserved.
	//
	// Returns the updated population after the evolutionary process has
	// proceeded by one step/iteration.
	NextEvolutionStep(
		evaluatedPopulation framework.EvaluatedPopulation,
		eliteCount int,
		rng *rand.Rand) framework.EvaluatedPopulation
}

// AbstractEvolutionEngine is a base struc for EvolutionEngine implementations.
type AbstractEvolutionEngine struct {
	// A single multi-threaded worker is shared among multiple evolution engine instances.
	pool                           *worker.Pool
	observers                      map[framework.EvolutionObserver]struct{}
	rng                            *rand.Rand
	candidateFactory               framework.CandidateFactory
	fitnessEvaluator               framework.FitnessEvaluator
	singleThreaded                 bool
	satisfiedTerminationConditions []framework.TerminationCondition
	Stepper
}

// NewAbstractEvolutionEngine creates a new evolution engine by specifying the
// various components required by an evolutionary algorithm.
//
// - candidateFactory is the factory used to create the initial population that
// is iteratively evolved.
// - fitnessEvaluator is a function for assigning fitness scores to candidate
// solutions.
// - rng is the source of randomness used by all stochastic processes (including
// evolutionary operators and selection strategies).
func NewAbstractEvolutionEngine(candidateFactory framework.CandidateFactory,
	fitnessEvaluator framework.FitnessEvaluator,
	rng *rand.Rand,
	stepper Stepper) *AbstractEvolutionEngine {

	return &AbstractEvolutionEngine{
		candidateFactory: candidateFactory,
		fitnessEvaluator: fitnessEvaluator,
		rng:              rng,
		observers:        make(map[framework.EvolutionObserver]struct{}),
		Stepper:          stepper,
	}
}

// Evolve executes the evolutionary algorithm until one of the termination
// conditions is met, then return the fittest candidate from the final
// generation.
//
// To return the entire population rather than just the fittest candidate,
// use the EvolvePopulation method instead.
//
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount is the number of candidates preserved via elitism. In
// elitism, a sub-set of the population with the best fitness scores are
// preserved unchanged in the subsequent generation. Candidate solutions
// that are preserved unchanged through elitism remain eligible for
// selection for breeding the remainder of the next generation. This value
// must be non-negative and less than the population size. A value of zero
// means that no elitism will be applied.
// - conditions is a slice of conditions that may cause the evolution to
// terminate.
//
// Return the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) Evolve(populationSize, eliteCount int,
	conditions ...framework.TerminationCondition) framework.Candidate {

	return e.EvolveWithSeedCandidates(populationSize,
		eliteCount,
		[]framework.Candidate{},
		conditions...)
}

// EvolveWithSeedCandidates executes the evolutionary algorithm until one of
// the termination conditions is met, then return the fittest candidate from
// the final generation. Provide a set of candidates to seed the starting
// population with.
//
// To return the entire population rather than just the fittest candidate,
// use the EvolvePopulationWithSeedCandidates method instead.
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount is the number of candidates preserved via elitism. In
// elitism, a sub-set of the population with the best fitness scores are
// preserved unchanged in the subsequent generation. Candidate solutions
// that are preserved unchanged through elitism remain eligible for
// selection for breeding the remainder of the next generation.  This value
// must be non-negative and less than the population size. A value of zero
// means that no elitism will be applied.
// - seedCandidates is a set of candidates to seed the population with. The
// size of this collection must be no greater than the specified population
// size.
// - conditions is a slice of conditions that may cause the evolution to
// terminate.
//
// Returns the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) EvolveWithSeedCandidates(populationSize, eliteCount int,
	seedCandidates []framework.Candidate,
	conditions ...framework.TerminationCondition) framework.Candidate {

	return e.EvolvePopulationWithSeedCandidates(populationSize,
		eliteCount,
		seedCandidates,
		conditions...)[0].Candidate()
}

// EvolvePopulation executes the evolutionary algorithm until one of the
// termination conditions is met, then return all of the candidates from the
// final generation.
//
// To return just the fittest candidate rather than the entire population,
// use the Evolve method instead.
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount is the number of candidates preserved via elitism. In
// elitism, a sub-set of the population with the best fitness scores are
// preserved unchanged in the subsequent generation. Candidate solutions
// that are preserved unchanged through elitism remain eligible for
// selection for breeding the remainder of the next generation.  This value
// must be non-negative and less than the population size. A value of zero
// means that no elitism will be applied.
// -  conditions is a slice of conditions that may cause the evolution to
// terminate.
//
// Return the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) EvolvePopulation(populationSize, eliteCount int,
	conditions ...framework.TerminationCondition) framework.EvaluatedPopulation {

	return e.EvolvePopulationWithSeedCandidates(populationSize,
		eliteCount,
		[]framework.Candidate{},
		conditions...)
}

// EvolvePopulationWithSeedCandidates executes the evolutionary algorithm
// until one of the termination conditions is met, then return all of the
// candidates from the final generation.
//
// To return just the fittest candidate rather than the entire population,
// use the EvolveWithSeedCandidates method instead.
// - populationSize is the number of candidate solutions present in the
// population at any point in time.
// - eliteCount The number of candidates preserved via elitism.  In elitism,
// a sub-set of the population with the best fitness scores are preserved
// unchanged in the subsequent generation.  Candidate solutions that are
// preserved unchanged through elitism remain eligible for selection for
// breeding the remainder of the next generation.  This value must be
// non-negative and less than the population size.  A value of zero means
// that no elitism will be applied.
// - seedCandidates A set of candidates to seed the population with.  The
// size of this collection must be no greater than the specified population
// size.
// - conditions One or more conditions that may cause the evolution to
// terminate.
//
// Return the fittest solution found by the evolutionary process.
func (e *AbstractEvolutionEngine) EvolvePopulationWithSeedCandidates(
	populationSize, eliteCount int,
	seedCandidates []framework.Candidate,
	conditions ...framework.TerminationCondition) framework.EvaluatedPopulation {

	if eliteCount < 0 || eliteCount >= populationSize {
		panic("Elite count must be non-negative and less than population size.")
	}
	if len(conditions) == 0 {
		panic("At least one TerminationCondition must be specified.")
	}

	e.satisfiedTerminationConditions = nil
	var currentGenerationIndex int
	startTime := time.Now()

	population := e.candidateFactory.SeedInitialPopulation(populationSize,
		seedCandidates,
		e.rng)

	// Calculate the fitness scores for each member of the initial population.
	evaluatedPopulation := e.evaluatePopulation(population)

	SortEvaluatedPopulation(evaluatedPopulation, e.fitnessEvaluator.IsNatural())
	data := ComputePopulationData(evaluatedPopulation,
		e.fitnessEvaluator.IsNatural(),
		eliteCount,
		currentGenerationIndex,
		startTime)

	// Notify observers of the state of the population.
	e.notifyPopulationChange(data)

	satisfiedConditions := ShouldContinue(data, conditions...)
	for satisfiedConditions == nil {
		currentGenerationIndex++
		evaluatedPopulation = e.NextEvolutionStep(evaluatedPopulation, eliteCount, e.rng)
		SortEvaluatedPopulation(evaluatedPopulation, e.fitnessEvaluator.IsNatural())
		data = ComputePopulationData(evaluatedPopulation,
			e.fitnessEvaluator.IsNatural(),
			eliteCount,
			currentGenerationIndex,
			startTime)
		// Notify observers of the state of the population.
		e.notifyPopulationChange(data)
		satisfiedConditions = ShouldContinue(data, conditions...)
	}
	e.satisfiedTerminationConditions = satisfiedConditions
	return evaluatedPopulation
}

// Takes a population, assigns a fitness score to each member and returns
// the members with their scores attached, sorted in descending order of
// fitness (descending order of fitness score for natural scores, ascending
// order of scores for non-natural scores).
// - population is the population to evaluate (each candidate is assigned a
// fitness score).
//
// Returns the evaluated population (a list of candidates with attached fitness
// scores).
func (e *AbstractEvolutionEngine) evaluatePopulation(population []framework.Candidate) framework.EvaluatedPopulation {

	// Do fitness evaluations
	evaluatedPopulation := make(framework.EvaluatedPopulation, len(population))

	if e.singleThreaded {

		var err error
		for i, candidate := range population {
			evaluatedPopulation[i], err = framework.NewEvaluatedCandidate(candidate, e.fitnessEvaluator.Fitness(candidate, population))
			if err != nil {
				panic(fmt.Sprintf("Can't evaluate candidate %v: %v", candidate, err))
			}
		}

	} else {

		// Create a worker pool that will divides the required number of fitness
		// evaluations equally among the available goroutines and coordinate
		// them so that we do not proceed until all of them have finished
		// processing.
		workers := make([]worker.Worker, len(population))
		for i := range population {
			workers[i] = &fitnessEvaluationWorker{
				idx:       i,
				pop:       population,
				evaluator: e.fitnessEvaluator,
			}
		}

		results, err := e.workerPool().Submit(workers)
		if err != nil {
			panic(fmt.Sprintf("Error while submitting workers to the pool: %v", err))
		}

		for i, result := range results {
			evaluatedPopulation[i] = result.(*framework.EvaluatedCandidate)
		}
		// TODO: handle goroutine termination
		/*
		   catch (InterruptedException ex)
		   {
		       // Restore the interrupted status, allows methods further up the call-stack
		       // to abort processing if appropriate.
		       Thread.currentThread().interrupt();
		   }
		*/
	}

	return evaluatedPopulation
}

type fitnessEvaluationWorker struct {
	idx       int                   // index of candidate to evaluate
	pop       []framework.Candidate // full population
	evaluator framework.FitnessEvaluator
}

func (w *fitnessEvaluationWorker) Work() (interface{}, error) {
	return framework.NewEvaluatedCandidate(w.pop[w.idx],
		w.evaluator.Fitness(w.pop[w.idx], w.pop))
}

// SatisfiedTerminationConditions returns a slice of all TerminationCondition's
// that are satisfied by the current state of the evolution engine.
//
// Usually this slice will contain only one item, but it is possible that
// mutliple termination conditions will become satisfied at the same time. In
// this case the condition objects in the slice will be in the same order that
// they were specified when passed to the engine.
//
// If the evolution has not yet terminated (either because it is still in
// progress or because it hasn't even been started) then
// framework.ErrIllegalState is returned.
//
// If the evolution terminated because the request thread was interrupted before
// any termination conditions were satisfied then this method will return an
// empty slice.
//
// The slice is guaranteed to be non-null. The slice may be empty because it is
// possible for evolution to terminate without any conditions being matched.
// The only situation in which this occurs is when the request thread is
// interrupted.
func (e *AbstractEvolutionEngine) SatisfiedTerminationConditions() ([]framework.TerminationCondition, error) {
	if e.satisfiedTerminationConditions == nil {
		//throw new IllegalStateException("EvolutionEngine has not terminated.");
		return nil, framework.ErrIllegalState("evolution engine has not terminated")
	}
	satisfiedTerminationConditions := make([]framework.TerminationCondition, len(e.satisfiedTerminationConditions))
	copy(satisfiedTerminationConditions, e.satisfiedTerminationConditions)
	return satisfiedTerminationConditions, nil
}

// AddEvolutionObserver adds a listener to receive status updates on the
// evolution progress.
//
// Updates are dispatched synchronously on the request thread. Observers should
// complete their processing and return in a timely manner to avoid holding up
// the evolution.
func (e *AbstractEvolutionEngine) AddEvolutionObserver(observer framework.EvolutionObserver) {
	e.observers[observer] = struct{}{}
}

// RemoveEvolutionObserver removes an evolution progress listener.
func (e *AbstractEvolutionEngine) RemoveEvolutionObserver(observer framework.EvolutionObserver) {
	delete(e.observers, observer)
}

// notifyPopulationChange sends the population data to all registered observers.
func (e *AbstractEvolutionEngine) notifyPopulationChange(data *framework.PopulationData) {
	for observer := range e.observers {
		observer.PopulationUpdate(data)
	}
}

// SetSingleThreaded forces evaluation to occur synchronously on the request
// goroutine.
//
// By default, fitness evaluations are performed on separate goroutines (as many
// as there are available cores/processors). This is useful in restricted
// environments where programs are not permitted to start or control threads. It
// might also lead to better performance for programs that have extremely
// lightweight/trivial fitness evaluations.
func (e *AbstractEvolutionEngine) SetSingleThreaded(singleThreaded bool) {
	e.singleThreaded = singleThreaded
}

// workerPool lazily creates the fitness evaluations goroutine pool.
func (e *AbstractEvolutionEngine) workerPool() *worker.Pool {
	if e.pool == nil {
		// create a worker pool and set the maximum number of concurrent
		// goroutines to the number of logical CPUs usable by the current
		// process.
		e.pool = worker.NewPool(runtime.NumCPU())
	}
	return e.pool
}
<|endoftext|>


<commit_before>package keystore

import (
	"encoding/json"
	"log"

	"github.com/coreos/go-etcd/etcd"
)

// Structs used for decoding json received by watch command.

type Port struct {
	ContainerPort int    `json:"containerPort,omitempty"`
	Protocol      string `json:"protocol,omitempty"`
}

type Condition struct {
	Status string `json:"status,omitempty"`
	Type   string `json:"type,omitempty"`
}

type Container struct {
	Image string `json:"image,omitempty"`
	Name  string `json:"name,omitempty"`
	Ports []Port `json:"ports,omitempty"`
}

type Pod struct {
	Spec struct {
		Containers []Container `json:"containers,omitempty"`
		Host       string      `json:"host,omitempty"`
	} `json:"spec,omitempty"`

	//
	Status_ struct {
		Conditions []Condition `json:"Condition,omitempty"`
		Phase      string      `json:"phase"`
		PodIP      string      `json:"podIP,omitempty"`
	} `json:"status"`
}

func (pod *Pod) Status() string {
	return pod.Status_.Phase
}

func (pod *Pod) PodIP() string {
	return pod.Status_.PodIP
}

func (pod *Pod) Host() string {
	return pod.Spec.Host
}

func (pod *Pod) String() string {
	if b, err := json.MarshalIndent(pod, "", "\t"); err != nil {
		return "{ERROR}"
	} else {
		return string(b)
	}
}

// etcd.Watch is blocking so use this helper function to monitor.
func watch(client *etcd.Client, receiver chan *etcd.Response, stop chan bool) {
	if _, err := client.Watch("/registry/pods", 0, true, receiver, stop); err != nil {
		log.Fatal(err)
	}
}

//  Connect to master etcd instance and wait for pods to come online/offline.
func Watch(master string) error {

	machines := []string{master}
	client := etcd.NewClient(machines)

	receiver := make(chan *etcd.Response, 1)
	stop := make(chan bool, 1)

	go watch(client, receiver, stop)

	log.Println("Watching and waiting for pods to come online...")
	for {
		select {
		case resp := <-receiver:
			if resp == nil {
				log.Printf("Got nil resp in watch channel.")
			} else {

				var pod Pod
				json.Unmarshal([]byte(resp.Node.Value), &pod)

				switch resp.Action {
				case "create":
					log.Printf("> Pod %s created.", resp.Node.Key)
				case "compareAndSwap":
					if pod.Status() == "Running" {
						log.Printf("> Pod %s status changed to %s with ip %s", resp.Node.Key, pod.Status(), pod.PodIP())
					} else {
						log.Printf("> Pod %s status changed to %s", resp.Node.Key, pod.Status())
					}
					// log.Printf("\n> %s\n\t%s\n\t%s", resp.Action, resp.Pod.Value, resp.Pod.Key)
				case "delete":
					log.Printf("> Pod %s offline.", resp.Node.Key)
				}

				log.Printf(pod.String())
			}
		case <-stop:
			log.Printf("Exiting!")
			return nil
		}
	}
}
<commit_msg>Remove unnecessary debug<commit_after>package keystore

import (
	"encoding/json"
	"log"

	"github.com/coreos/go-etcd/etcd"
)

// Structs used for decoding json received by watch command.

type Port struct {
	ContainerPort int    `json:"containerPort,omitempty"`
	Protocol      string `json:"protocol,omitempty"`
}

type Condition struct {
	Status string `json:"status,omitempty"`
	Type   string `json:"type,omitempty"`
}

type Container struct {
	Image string `json:"image,omitempty"`
	Name  string `json:"name,omitempty"`
	Ports []Port `json:"ports,omitempty"`
}

type Pod struct {
	Spec struct {
		Containers []Container `json:"containers,omitempty"`
		Host       string      `json:"host,omitempty"`
	} `json:"spec,omitempty"`

	//
	Status_ struct {
		Conditions []Condition `json:"Condition,omitempty"`
		Phase      string      `json:"phase"`
		PodIP      string      `json:"podIP,omitempty"`
	} `json:"status"`
}

func (pod *Pod) Status() string {
	return pod.Status_.Phase
}

func (pod *Pod) PodIP() string {
	return pod.Status_.PodIP
}

func (pod *Pod) Host() string {
	return pod.Spec.Host
}

func (pod *Pod) String() string {
	if b, err := json.MarshalIndent(pod, "", "\t"); err != nil {
		return "{ERROR}"
	} else {
		return string(b)
	}
}

// etcd.Watch is blocking so use this helper function to monitor.
func watch(client *etcd.Client, receiver chan *etcd.Response, stop chan bool) {
	if _, err := client.Watch("/registry/pods", 0, true, receiver, stop); err != nil {
		log.Fatal(err)
	}
}

//  Connect to master etcd instance and wait for pods to come online/offline.
func Watch(master string) error {

	machines := []string{master}
	client := etcd.NewClient(machines)

	receiver := make(chan *etcd.Response, 1)
	stop := make(chan bool, 1)

	go watch(client, receiver, stop)

	for {
		select {
		case resp := <-receiver:
			if resp == nil {
				log.Printf("Got nil resp in watch channel.")
			} else {

				var pod Pod
				json.Unmarshal([]byte(resp.Node.Value), &pod)

				switch resp.Action {
				case "create":
					log.Printf("> Pod %s created.", resp.Node.Key)
				case "compareAndSwap":
					if pod.Status() == "Running" {
						log.Printf("> Pod %s status changed to %s with ip %s", resp.Node.Key, pod.Status(), pod.PodIP())
					} else {
						log.Printf("> Pod %s status changed to %s", resp.Node.Key, pod.Status())
					}
					// log.Printf("\n> %s\n\t%s\n\t%s", resp.Action, resp.Pod.Value, resp.Pod.Key)
				case "delete":
					log.Printf("> Pod %s offline.", resp.Node.Key)
				}

				log.Printf(pod.String())
			}
		case <-stop:
			log.Printf("Exiting!")
			return nil
		}
	}
}
<|endoftext|>


<commit_before>package client

import (
	"crypto/tls"
	"errors"
	"io"
	"log"
	"net/http"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
)

// BlobstoreClient encapsulates interacting with an S3 compatible blobstore
type BlobstoreClient struct {
	s3Client *s3.S3
	config   BlobstoreClientConfig
}

// New returns a BlobstoreClient if the configuration file backing configFile is valid
func New(configFile io.Reader) (BlobstoreClient, error) {
	config, err := newConfig(configFile)
	if err != nil {
		return BlobstoreClient{}, err
	}

	err = config.validate()
	if err != nil {
		return BlobstoreClient{}, err
	}

	transport := *http.DefaultTransport.(*http.Transport)
	transport.TLSClientConfig = &tls.Config{
		InsecureSkipVerify: !config.SSLVerifyPeer,
	}

	httpClient := &http.Client{Transport: &transport}

	s3Config := aws.NewConfig().
		WithRegion(config.Region).
		WithS3ForcePathStyle(true).
		WithLogLevel(aws.LogOff).
		WithDisableSSL(!config.UseSSL).
		WithHTTPClient(httpClient)

	if config.Region != "" && config.Host == "" {
		s3Config = s3Config.WithRegion(config.Region)
	} else if config.Host != "" && config.Region == "" {
		s3Config = s3Config.WithRegion("").WithEndpoint(config.s3Endpoint())
	} else {
		return BlobstoreClient{}, errors.New("Unable to handle both region and host being set")
	}

	if config.CredentialsSource == credentialsSourceStatic {
		s3Config = s3Config.WithCredentials(credentials.NewStaticCredentials(config.AccessKeyID, config.SecretAccessKey, ""))
	}

	s3Client := s3.New(s3Config)

	if config.UseV2SigningMethod {
		setv2Handlers(s3Client)
	}

	return BlobstoreClient{s3Client: s3Client, config: config}, nil
}

// Get fetches a blob from an S3 compatible blobstore
// Destination will be overwritten if exists
func (c *BlobstoreClient) Get(src string, dest io.WriterAt) error {
	downloader := s3manager.NewDownloader(&s3manager.DownloadOptions{S3: c.s3Client})

	_, err := downloader.Download(dest, &s3.GetObjectInput{
		Bucket: aws.String(c.config.BucketName),
		Key:    aws.String(src),
	})

	if err != nil {
		return err
	}

	return nil
}

// Put uploads a blob to an S3 compatible blobstore
func (c *BlobstoreClient) Put(src io.ReadSeeker, dest string) error {
	uploader := s3manager.NewUploader(&s3manager.UploadOptions{S3: c.s3Client})
	putResult, err := uploader.Upload(&s3manager.UploadInput{
		Body:   src,
		Bucket: aws.String(c.config.BucketName),
		Key:    aws.String(dest),
	})

	if err != nil {
		return err
	}

	log.Println("Successfully uploaded file to", putResult.Location)
	return nil
}
<commit_msg>See if region information can be fudged when host is provided.<commit_after>package client

import (
	"crypto/tls"
	"errors"
	"io"
	"log"
	"net/http"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/credentials"
	"github.com/aws/aws-sdk-go/service/s3"
	"github.com/aws/aws-sdk-go/service/s3/s3manager"
)

// BlobstoreClient encapsulates interacting with an S3 compatible blobstore
type BlobstoreClient struct {
	s3Client *s3.S3
	config   BlobstoreClientConfig
}

// New returns a BlobstoreClient if the configuration file backing configFile is valid
func New(configFile io.Reader) (BlobstoreClient, error) {
	config, err := newConfig(configFile)
	if err != nil {
		return BlobstoreClient{}, err
	}

	err = config.validate()
	if err != nil {
		return BlobstoreClient{}, err
	}

	transport := *http.DefaultTransport.(*http.Transport)
	transport.TLSClientConfig = &tls.Config{
		InsecureSkipVerify: !config.SSLVerifyPeer,
	}

	httpClient := &http.Client{Transport: &transport}

	s3Config := aws.NewConfig().
		WithRegion(config.Region).
		WithS3ForcePathStyle(true).
		WithLogLevel(aws.LogOff).
		WithDisableSSL(!config.UseSSL).
		WithHTTPClient(httpClient)

	if config.Region != "" && config.Host == "" {
		s3Config = s3Config.WithRegion(config.Region)
	} else if config.Host != "" && config.Region == "" {
		s3Config = s3Config.WithRegion(" ").WithEndpoint(config.s3Endpoint())
	} else {
		return BlobstoreClient{}, errors.New("Unable to handle both region and host being set")
	}

	if config.CredentialsSource == credentialsSourceStatic {
		s3Config = s3Config.WithCredentials(credentials.NewStaticCredentials(config.AccessKeyID, config.SecretAccessKey, ""))
	}

	s3Client := s3.New(s3Config)

	if config.UseV2SigningMethod {
		setv2Handlers(s3Client)
	}

	return BlobstoreClient{s3Client: s3Client, config: config}, nil
}

// Get fetches a blob from an S3 compatible blobstore
// Destination will be overwritten if exists
func (c *BlobstoreClient) Get(src string, dest io.WriterAt) error {
	downloader := s3manager.NewDownloader(&s3manager.DownloadOptions{S3: c.s3Client})

	_, err := downloader.Download(dest, &s3.GetObjectInput{
		Bucket: aws.String(c.config.BucketName),
		Key:    aws.String(src),
	})

	if err != nil {
		return err
	}

	return nil
}

// Put uploads a blob to an S3 compatible blobstore
func (c *BlobstoreClient) Put(src io.ReadSeeker, dest string) error {
	uploader := s3manager.NewUploader(&s3manager.UploadOptions{S3: c.s3Client})
	putResult, err := uploader.Upload(&s3manager.UploadInput{
		Body:   src,
		Bucket: aws.String(c.config.BucketName),
		Key:    aws.String(dest),
	})

	if err != nil {
		return err
	}

	log.Println("Successfully uploaded file to", putResult.Location)
	return nil
}
<|endoftext|>


<commit_before>package connectors

import (
	"bytes"
	"log"
	"os/exec"
	"strings"
	"time"

	"github.com/projectjane/jane/models"
	"github.com/projectjane/jane/parse"
	"golang.org/x/crypto/ssh"
)

type Exec struct {
}

func (x Exec) Listen(commandMsgs chan<- models.Message, connector models.Connector) {
	defer Recovery(connector)
	for _, command := range connector.Commands {
		if command.RunCheck {
			if connector.Debug {
				log.Print("Starting Listener for " + connector.ID + " " + command.Name)
			}
			go check(commandMsgs, command, connector)
		}
	}
}

func (x Exec) Command(message models.Message, publishMsgs chan<- models.Message, connector models.Connector) {
	for _, command := range connector.Commands {
		if match, tokens := parse.Match(command.Match, message.In.Text); match {
			args := parse.Substitute(command.Args, tokens)
			tokens["STDOUT"] = callCmd(command.Cmd, args, connector)
			var color = "NONE"
			var match = false
			if match, _ = parse.Match(command.Green, tokens["STDOUT"]); match {
				color = "SUCCESS"
			}
			if match, _ = parse.Match(command.Yellow, tokens["STDOUT"]); match {
				color = "WARN"
			}
			if match, _ = parse.Match(command.Red, tokens["STDOUT"]); match {
				color = "FAIL"
			}
			message.In.Tags = parse.TagAppend(message.In.Tags, connector.Tags)
			message.Out.Text = connector.ID + " " + command.Name
			message.Out.Detail = parse.Substitute(command.Output, tokens)
			message.Out.Status = color
			publishMsgs <- message
		}
	}
}

func (x Exec) Publish(publishMsgs <-chan models.Message, connector models.Connector) {
	return
}

func (x Exec) Help(connector models.Connector) (help []string) {
	help = make([]string, 0)
	for _, command := range connector.Commands {
		if !command.HideHelp {
			if command.Help != "" {
				help = append(help, command.Help)
			} else {
				help = append(help, command.Match)
			}
		}
	}
	return help
}

func check(commandMsgs chan<- models.Message, command models.Command, connector models.Connector) {
	var state = command.Green
	var interval = 1
	var remind = 0
	if command.Interval > 0 {
		interval = command.Interval
	}
	if command.Remind > 0 {
		remind = command.Remind
	}
	var counter = 0
	for {
		var color = "NONE"
		var match = false
		var newstate = ""
		counter += 1
		out := callCmd(command.Cmd, command.Args, connector)
		if match, _ = parse.Match(command.Green, out); match {
			newstate = command.Green
			color = "SUCCESS"
		}
		if match, _ = parse.Match(command.Yellow, out); match {
			newstate = command.Yellow
			color = "WARN"
		}
		if match, _ = parse.Match(command.Red, out); match {
			newstate = command.Red
			color = "FAIL"
		}
		if newstate != state || (newstate != command.Green && counter == remind && remind != 0) {
			var message models.Message
			message.In.ConnectorType = connector.Type
			message.In.ConnectorID = connector.ID
			message.In.Tags = connector.Tags
			message.In.Process = false
			message.Out.Text = connector.ID + " " + command.Name
			message.Out.Detail = strings.Replace(command.Output, "${STDOUT}", out, -1)
			message.Out.Status = color
			commandMsgs <- message
			state = newstate
		}
		if counter >= remind {
			counter = 0
		}
		time.Sleep(time.Duration(interval) * time.Minute)
	}
}

func callCmd(cmd string, args string, connector models.Connector) (out string) {
	if connector.Server != "" {
		out = callRemote(cmd, args, connector)
	} else {
		out = callLocal(cmd, args, connector)
	}
	return out
}

func callLocal(cmd string, args string, connector models.Connector) (out string) {
	ca := cmd + " " + args
	if connector.Debug {
		log.Print("Executing: " + cmd + " " + args)
	}
	var o bytes.Buffer
	var e bytes.Buffer
	c := exec.Command("/bin/sh", "-c", ca)
	c.Stdout = &o
	c.Stderr = &e
	err := c.Run()
	if err != nil {
		log.Print(cmd + " " + args)
		log.Print(err)
		log.Print(e.String())
	}
	out = o.String()
	if connector.Debug {
		log.Print(out)
	}
	return out
}

func callRemote(cmd string, args string, connector models.Connector) (out string) {
	serverconn := true
	clientconn := &ssh.ClientConfig{
		User: connector.Login,
		Auth: []ssh.AuthMethod{
			ssh.Password(connector.Pass),
		},
	}
	port := "22"
	if connector.Port != "" {
		port = connector.Port
	}
	if connector.Debug {
		log.Print("Starting ssh connection for " + connector.Server + ":" + port)
	}
	client, err := ssh.Dial("tcp", connector.Server+":"+port, clientconn)
	if err != nil {
		log.Print(err)
	}
	if client == nil {
		serverconn = false
	} else {
		defer client.Close()
		session, err := client.NewSession()
		if err != nil {
			log.Print(err)
		}
		if session == nil {
			serverconn = false
		} else {
			defer session.Close()
			b, err := session.CombinedOutput(cmd + " " + args)
			if err != nil && connector.Debug {
				log.Print(err)
			}
			out = string(b[:])
			if connector.Debug {
				log.Print("Exec results for " + connector.Server + " " + cmd + " " + args + ": " + out)
			}
		}
	}
	if !serverconn {
		if connector.Debug {
			log.Print("Cannot connect to server " + connector.Server)
		}
		out = "ERROR - Cannot connect to server " + connector.Server
	}
	return out
}
<commit_msg>adding remote server retry<commit_after>package connectors

import (
	"bytes"
	"log"
	"os/exec"
	"strconv"
	"strings"
	"time"

	"github.com/projectjane/jane/models"
	"github.com/projectjane/jane/parse"
	"golang.org/x/crypto/ssh"
)

type Exec struct {
}

func (x Exec) Listen(commandMsgs chan<- models.Message, connector models.Connector) {
	defer Recovery(connector)
	for _, command := range connector.Commands {
		if command.RunCheck {
			if connector.Debug {
				log.Print("Starting Listener for " + connector.ID + " " + command.Name)
			}
			go check(commandMsgs, command, connector)
		}
	}
}

func (x Exec) Command(message models.Message, publishMsgs chan<- models.Message, connector models.Connector) {
	for _, command := range connector.Commands {
		if match, tokens := parse.Match(command.Match, message.In.Text); match {
			args := parse.Substitute(command.Args, tokens)
			tokens["STDOUT"] = callCmd(command.Cmd, args, connector)
			var color = "NONE"
			var match = false
			if match, _ = parse.Match(command.Green, tokens["STDOUT"]); match {
				color = "SUCCESS"
			}
			if match, _ = parse.Match(command.Yellow, tokens["STDOUT"]); match {
				color = "WARN"
			}
			if match, _ = parse.Match(command.Red, tokens["STDOUT"]); match {
				color = "FAIL"
			}
			message.In.Tags = parse.TagAppend(message.In.Tags, connector.Tags)
			message.Out.Text = connector.ID + " " + command.Name
			message.Out.Detail = parse.Substitute(command.Output, tokens)
			message.Out.Status = color
			publishMsgs <- message
		}
	}
}

func (x Exec) Publish(publishMsgs <-chan models.Message, connector models.Connector) {
	return
}

func (x Exec) Help(connector models.Connector) (help []string) {
	help = make([]string, 0)
	for _, command := range connector.Commands {
		if !command.HideHelp {
			if command.Help != "" {
				help = append(help, command.Help)
			} else {
				help = append(help, command.Match)
			}
		}
	}
	return help
}

func check(commandMsgs chan<- models.Message, command models.Command, connector models.Connector) {
	var state = command.Green
	var interval = 1
	var remind = 0
	if command.Interval > 0 {
		interval = command.Interval
	}
	if command.Remind > 0 {
		remind = command.Remind
	}
	var counter = 0
	for {
		var color = "NONE"
		var match = false
		var newstate = ""
		counter += 1
		out := callCmd(command.Cmd, command.Args, connector)
		if match, _ = parse.Match(command.Green, out); match {
			newstate = command.Green
			color = "SUCCESS"
		}
		if match, _ = parse.Match(command.Yellow, out); match {
			newstate = command.Yellow
			color = "WARN"
		}
		if match, _ = parse.Match(command.Red, out); match {
			newstate = command.Red
			color = "FAIL"
		}
		if newstate != state || (newstate != command.Green && counter == remind && remind != 0) {
			var message models.Message
			message.In.ConnectorType = connector.Type
			message.In.ConnectorID = connector.ID
			message.In.Tags = connector.Tags
			message.In.Process = false
			message.Out.Text = connector.ID + " " + command.Name
			message.Out.Detail = strings.Replace(command.Output, "${STDOUT}", out, -1)
			message.Out.Status = color
			commandMsgs <- message
			state = newstate
		}
		if counter >= remind {
			counter = 0
		}
		time.Sleep(time.Duration(interval) * time.Minute)
	}
}

func callCmd(cmd string, args string, connector models.Connector) (out string) {
	if connector.Server != "" {
		out = callRemote(cmd, args, connector)
	} else {
		out = callLocal(cmd, args, connector)
	}
	return out
}

func callLocal(cmd string, args string, connector models.Connector) (out string) {
	ca := cmd + " " + args
	if connector.Debug {
		log.Print("Executing: " + cmd + " " + args)
	}
	var o bytes.Buffer
	var e bytes.Buffer
	c := exec.Command("/bin/sh", "-c", ca)
	c.Stdout = &o
	c.Stderr = &e
	err := c.Run()
	if err != nil {
		log.Print(cmd + " " + args)
		log.Print(err)
		log.Print(e.String())
	}
	out = o.String()
	if connector.Debug {
		log.Print(out)
	}
	return out
}

func callRemote(cmd string, args string, connector models.Connector) (out string) {
	serverconn := true
	clientconn := &ssh.ClientConfig{
		User: connector.Login,
		Auth: []ssh.AuthMethod{
			ssh.Password(connector.Pass),
		},
	}
	port := "22"
	if connector.Port != "" {
		port = connector.Port
	}
	if connector.Debug {
		log.Print("Starting ssh connection for " + connector.Server + ":" + port)
	}
	retries := 3
	retryCounter := retries
	for retryCounter > 0 {
		client, err := ssh.Dial("tcp", connector.Server+":"+port, clientconn)
		if err != nil {
			log.Print(err)
		}
		if client == nil {
			serverconn = false
		} else {
			defer client.Close()
			session, err := client.NewSession()
			if err != nil {
				log.Print(err)
			}
			if session == nil {
				serverconn = false
			} else {
				defer session.Close()
				b, err := session.CombinedOutput(cmd + " " + args)
				if err != nil && connector.Debug {
					log.Print(err)
				}
				out = string(b[:])
				if connector.Debug {
					log.Print("Exec results for " + connector.Server + " " + cmd + " " + args + ": " + out)
				}
			}
		}
		if serverconn {
			retryCounter = 0
		} else {
			if connector.Debug {
				log.Print("Cannot connect to server " + connector.Server + " (try #" + strconv.Itoa(retries-retryCounter) + ")")
			}
			retryCounter -= 1
			time.Sleep(3 * time.Second)
		}
	}
	if !serverconn {
		out = "ERROR - Cannot connect to server " + connector.Server
	}
	return out
}
<|endoftext|>


<commit_before>// Copyright 2015 Reborndb Org. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package cachepool

import (
	"sync"
	"time"

	"container/list"

	"github.com/juju/errors"
	"github.com/reborndb/reborn/pkg/proxy/redispool"
)

type SimpleConnectionPool struct {
	createTs time.Time
	sync.Mutex
	fact  redispool.CreateConnectionFunc
	conns *list.List
}

func NewSimpleConnectionPool() *SimpleConnectionPool {
	return &SimpleConnectionPool{
		createTs: time.Now(),
	}
}

func (s *SimpleConnectionPool) Put(conn redispool.PoolConnection) {
	if conn != nil {
		s.Lock()
		s.conns.PushBack(conn)
		s.Unlock()
	}

}

func (s *SimpleConnectionPool) Get() (redispool.PoolConnection, error) {
	s.Lock()
	if s.conns.Len() == 0 {
		c, err := s.fact(s)
		s.Unlock()
		return c, err
	}

	e := s.conns.Front()
	s.conns.Remove(e)

	s.Unlock()
	return e.Value.(redispool.PoolConnection), nil
}

func (s *SimpleConnectionPool) Open(fact redispool.CreateConnectionFunc) {
	s.Lock()
	defer s.Unlock()
	s.fact = fact
	s.conns = list.New()
}

func (s *SimpleConnectionPool) Close() {
	s.Lock()
	defer s.Unlock()
	for e := s.conns.Front(); e != nil; e = e.Next() {
		e.Value.(redispool.PoolConnection).Close()
	}
}

type LivePool struct {
	pool redispool.IPool
}

type CachePool struct {
	mu    sync.RWMutex
	pools map[string]*LivePool
}

func NewCachePool() *CachePool {
	return &CachePool{
		pools: make(map[string]*LivePool),
	}
}

func (cp *CachePool) GetConn(key string) (redispool.PoolConnection, error) {
	cp.mu.RLock()

	pool, ok := cp.pools[key]
	if !ok {
		cp.mu.RUnlock()
		return nil, errors.Errorf("pool %s not exist", key)
	}

	cp.mu.RUnlock()
	c, err := pool.pool.Get()

	return c, err
}

func (cp *CachePool) ReleaseConn(pc redispool.PoolConnection) {
	pc.Recycle()
}

func (cp *CachePool) AddPool(key string) error {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	pool, ok := cp.pools[key]
	if ok {
		return nil
	}
	pool = &LivePool{
		//pool: redispool.NewConnectionPool("redis conn pool", 50, 120*time.Second),
		pool: NewSimpleConnectionPool(),
	}

	pool.pool.Open(redispool.ConnectionCreator(key))

	cp.pools[key] = pool

	return nil
}

func (cp *CachePool) RemovePool(key string) error {
	cp.mu.Lock()

	pool, ok := cp.pools[key]
	if !ok {
		cp.mu.Unlock()
		return errors.Errorf("pool %s not exist", key)
	}
	delete(cp.pools, key)
	cp.mu.Unlock()

	go pool.pool.Close()
	return nil
}
<commit_msg>pkg/proxy/cachepool: minor cleanup<commit_after>// Copyright 2015 Reborndb Org. All Rights Reserved.
// Licensed under the MIT (MIT-LICENSE.txt) license.

package cachepool

import (
	"sync"
	"time"

	"container/list"

	"github.com/juju/errors"
	"github.com/reborndb/reborn/pkg/proxy/redispool"
)

type SimpleConnectionPool struct {
	createTs time.Time

	mu    sync.Mutex // guards fact and conns
	fact  redispool.CreateConnectionFunc
	conns *list.List
}

func NewSimpleConnectionPool() *SimpleConnectionPool {
	return &SimpleConnectionPool{
		createTs: time.Now(),
	}
}

func (s *SimpleConnectionPool) Put(conn redispool.PoolConnection) {
	if conn == nil {
		return
	}

	s.mu.Lock()
	s.conns.PushBack(conn)
	s.mu.Unlock()
}

func (s *SimpleConnectionPool) Get() (redispool.PoolConnection, error) {
	s.mu.Lock()

	if s.conns.Len() == 0 {
		c, err := s.fact(s)
		s.mu.Unlock()
		return c, err
	}

	e := s.conns.Front()
	s.conns.Remove(e)

	s.mu.Unlock()
	return e.Value.(redispool.PoolConnection), nil
}

func (s *SimpleConnectionPool) Open(fact redispool.CreateConnectionFunc) {
	s.mu.Lock()
	defer s.mu.Unlock()

	s.fact = fact
	s.conns = list.New()
}

func (s *SimpleConnectionPool) Close() {
	s.mu.Lock()
	defer s.mu.Unlock()

	for e := s.conns.Front(); e != nil; e = e.Next() {
		e.Value.(redispool.PoolConnection).Close()
	}
}

type LivePool struct {
	pool redispool.IPool
}

type CachePool struct {
	mu    sync.RWMutex // guard pools
	pools map[string]*LivePool
}

func NewCachePool() *CachePool {
	return &CachePool{
		pools: make(map[string]*LivePool),
	}
}

func (cp *CachePool) GetConn(key string) (redispool.PoolConnection, error) {
	cp.mu.RLock()

	pool, ok := cp.pools[key]
	if !ok {
		cp.mu.RUnlock()
		return nil, errors.Errorf("cachepool: pool %s does not exist", key)
	}

	cp.mu.RUnlock()
	c, err := pool.pool.Get()

	return c, err
}

func (cp *CachePool) ReleaseConn(pc redispool.PoolConnection) {
	pc.Recycle()
}

func (cp *CachePool) AddPool(key string) error {
	cp.mu.Lock()
	defer cp.mu.Unlock()

	pool, ok := cp.pools[key]
	if ok {
		return nil
	}
	pool = &LivePool{
		//pool: redispool.NewConnectionPool("redis conn pool", 50, 120*time.Second),
		pool: NewSimpleConnectionPool(),
	}

	pool.pool.Open(redispool.ConnectionCreator(key))

	cp.pools[key] = pool

	return nil
}

func (cp *CachePool) RemovePool(key string) error {
	cp.mu.Lock()

	pool, ok := cp.pools[key]
	if !ok {
		cp.mu.Unlock()
		return errors.Errorf("cachepool: pool %s does not exist", key)
	}

	delete(cp.pools, key)
	cp.mu.Unlock()

	go pool.pool.Close()

	return nil
}
<|endoftext|>


<commit_before>package plugin

import (
	"fmt"
	"io/ioutil"
	"net"
	"os"
	"strings"
	"time"

	"github.com/golang/glog"

	osapi "github.com/openshift/origin/pkg/sdn/api"
	"github.com/openshift/origin/pkg/util/ipcmd"
	"github.com/openshift/origin/pkg/util/netutils"

	kapierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilwait "k8s.io/apimachinery/pkg/util/wait"
	kapi "k8s.io/kubernetes/pkg/api"
	utildbus "k8s.io/kubernetes/pkg/util/dbus"
	kexec "k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/iptables"
	"k8s.io/kubernetes/pkg/util/sysctl"
)

func (plugin *OsdnNode) getLocalSubnet() (string, error) {
	var subnet *osapi.HostSubnet
	backoff := utilwait.Backoff{
		Duration: 100 * time.Millisecond,
		Factor:   2,
		Steps:    8,
	}
	err := utilwait.ExponentialBackoff(backoff, func() (bool, error) {
		var err error
		subnet, err = plugin.osClient.HostSubnets().Get(plugin.hostName, metav1.GetOptions{})
		if err == nil {
			return true, nil
		} else if kapierrors.IsNotFound(err) {
			glog.Warningf("Could not find an allocated subnet for node: %s, Waiting...", plugin.hostName)
			return false, nil
		} else {
			return false, err
		}
	})
	if err != nil {
		return "", fmt.Errorf("failed to get subnet for this host: %s, error: %v", plugin.hostName, err)
	}

	if err = plugin.networkInfo.validateNodeIP(subnet.HostIP); err != nil {
		return "", fmt.Errorf("failed to validate own HostSubnet: %v", err)
	}

	return subnet.Subnet, nil
}

func (plugin *OsdnNode) alreadySetUp(localSubnetGatewayCIDR, clusterNetworkCIDR string) bool {
	var found bool

	exec := kexec.New()
	itx := ipcmd.NewTransaction(exec, TUN)
	addrs, err := itx.GetAddresses()
	itx.EndTransaction()
	if err != nil {
		return false
	}
	found = false
	for _, addr := range addrs {
		if strings.Contains(addr, localSubnetGatewayCIDR) {
			found = true
			break
		}
	}
	if !found {
		return false
	}

	itx = ipcmd.NewTransaction(exec, TUN)
	routes, err := itx.GetRoutes()
	itx.EndTransaction()
	if err != nil {
		return false
	}
	found = false
	for _, route := range routes {
		if strings.Contains(route, clusterNetworkCIDR+" ") {
			found = true
			break
		}
	}
	if !found {
		return false
	}

	if !plugin.oc.AlreadySetUp() {
		return false
	}

	return true
}

func deleteLocalSubnetRoute(device, localSubnetCIDR string) {
	backoff := utilwait.Backoff{
		Duration: 100 * time.Millisecond,
		Factor:   1.25,
		Steps:    6,
	}
	err := utilwait.ExponentialBackoff(backoff, func() (bool, error) {
		itx := ipcmd.NewTransaction(kexec.New(), device)
		routes, err := itx.GetRoutes()
		if err != nil {
			return false, fmt.Errorf("could not get routes: %v", err)
		}
		for _, route := range routes {
			if strings.Contains(route, localSubnetCIDR) {
				itx.DeleteRoute(localSubnetCIDR)
				err = itx.EndTransaction()
				if err != nil {
					return false, fmt.Errorf("could not delete route: %v", err)
				}
				return true, nil
			}
		}
		return false, nil
	})

	if err != nil {
		glog.Errorf("Error removing %s route from dev %s: %v; if the route appears later it will not be deleted.", localSubnetCIDR, device, err)
	}
}

func (plugin *OsdnNode) SetupSDN() (bool, error) {
	clusterNetworkCIDR := plugin.networkInfo.ClusterNetwork.String()
	serviceNetworkCIDR := plugin.networkInfo.ServiceNetwork.String()

	localSubnetCIDR := plugin.localSubnetCIDR
	_, ipnet, err := net.ParseCIDR(localSubnetCIDR)
	localSubnetMaskLength, _ := ipnet.Mask.Size()
	localSubnetGateway := netutils.GenerateDefaultGateway(ipnet).String()

	glog.V(5).Infof("[SDN setup] node pod subnet %s gateway %s", ipnet.String(), localSubnetGateway)

	exec := kexec.New()

	if plugin.clearLbr0IptablesRule {
		// Delete docker's left-over lbr0 rule; cannot do this from
		// NewNodePlugin (where docker is cleaned up) because we need
		// localSubnetCIDR which is only valid after plugin start
		ipt := iptables.New(exec, utildbus.New(), iptables.ProtocolIpv4)
		ipt.DeleteRule(iptables.TableNAT, iptables.ChainPostrouting, "-s", localSubnetCIDR, "!", "-o", "lbr0", "-j", "MASQUERADE")
	}

	gwCIDR := fmt.Sprintf("%s/%d", localSubnetGateway, localSubnetMaskLength)
	if plugin.alreadySetUp(gwCIDR, clusterNetworkCIDR) {
		glog.V(5).Infof("[SDN setup] no SDN setup required")
		return false, nil
	}
	glog.V(5).Infof("[SDN setup] full SDN setup required")

	if err := os.MkdirAll("/run/openshift-sdn", 0700); err != nil {
		return false, err
	}
	config := fmt.Sprintf("export OPENSHIFT_CLUSTER_SUBNET=%s", clusterNetworkCIDR)
	err = ioutil.WriteFile("/run/openshift-sdn/config.env", []byte(config), 0644)
	if err != nil {
		return false, err
	}

	err = plugin.oc.SetupOVS(clusterNetworkCIDR, serviceNetworkCIDR, localSubnetCIDR, localSubnetGateway)
	if err != nil {
		return false, err
	}

	itx := ipcmd.NewTransaction(exec, TUN)
	itx.AddAddress(gwCIDR)
	defer deleteLocalSubnetRoute(TUN, localSubnetCIDR)
	itx.SetLink("mtu", fmt.Sprint(plugin.mtu))
	itx.SetLink("up")
	itx.AddRoute(clusterNetworkCIDR, "proto", "kernel", "scope", "link")
	itx.AddRoute(serviceNetworkCIDR)
	err = itx.EndTransaction()
	if err != nil {
		return false, err
	}

	sysctl := sysctl.New()

	// Enable IP forwarding for ipv4 packets
	err = sysctl.SetSysctl("net/ipv4/ip_forward", 1)
	if err != nil {
		return false, fmt.Errorf("could not enable IPv4 forwarding: %s", err)
	}
	err = sysctl.SetSysctl(fmt.Sprintf("net/ipv4/conf/%s/forwarding", TUN), 1)
	if err != nil {
		return false, fmt.Errorf("could not enable IPv4 forwarding on %s: %s", TUN, err)
	}

	return true, nil
}

func (plugin *OsdnNode) updateEgressNetworkPolicyRules(vnid uint32) {
	policies := plugin.egressPolicies[vnid]
	namespaces := plugin.policy.GetNamespaces(vnid)
	if err := plugin.oc.UpdateEgressNetworkPolicyRules(policies, vnid, namespaces, plugin.egressDNS); err != nil {
		glog.Errorf("Error updating OVS flows for EgressNetworkPolicy: %v", err)
	}
}

func (plugin *OsdnNode) AddHostSubnetRules(subnet *osapi.HostSubnet) {
	glog.Infof("AddHostSubnetRules for %s", hostSubnetToString(subnet))
	if err := plugin.oc.AddHostSubnetRules(subnet); err != nil {
		glog.Errorf("Error adding OVS flows for subnet %q: %v", subnet.Subnet, err)
	}
}

func (plugin *OsdnNode) DeleteHostSubnetRules(subnet *osapi.HostSubnet) {
	glog.Infof("DeleteHostSubnetRules for %s", hostSubnetToString(subnet))
	if err := plugin.oc.DeleteHostSubnetRules(subnet); err != nil {
		glog.Errorf("Error deleting OVS flows for subnet %q: %v", subnet.Subnet, err)
	}
}

func (plugin *OsdnNode) AddServiceRules(service *kapi.Service, netID uint32) {
	glog.V(5).Infof("AddServiceRules for %v", service)
	if err := plugin.oc.AddServiceRules(service, netID); err != nil {
		glog.Errorf("Error adding OVS flows for service %v, netid %d: %v", service, netID, err)
	}
}

func (plugin *OsdnNode) DeleteServiceRules(service *kapi.Service) {
	glog.V(5).Infof("DeleteServiceRules for %v", service)
	if err := plugin.oc.DeleteServiceRules(service); err != nil {
		glog.Errorf("Error deleting OVS flows for service %v: %v", service, err)
	}
}
<commit_msg>Remove stale SDN code: Writing cluster network CIDR to config.env<commit_after>package plugin

import (
	"fmt"
	"net"
	"strings"
	"time"

	"github.com/golang/glog"

	osapi "github.com/openshift/origin/pkg/sdn/api"
	"github.com/openshift/origin/pkg/util/ipcmd"
	"github.com/openshift/origin/pkg/util/netutils"

	kapierrors "k8s.io/apimachinery/pkg/api/errors"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	utilwait "k8s.io/apimachinery/pkg/util/wait"
	kapi "k8s.io/kubernetes/pkg/api"
	utildbus "k8s.io/kubernetes/pkg/util/dbus"
	kexec "k8s.io/kubernetes/pkg/util/exec"
	"k8s.io/kubernetes/pkg/util/iptables"
	"k8s.io/kubernetes/pkg/util/sysctl"
)

func (plugin *OsdnNode) getLocalSubnet() (string, error) {
	var subnet *osapi.HostSubnet
	backoff := utilwait.Backoff{
		Duration: 100 * time.Millisecond,
		Factor:   2,
		Steps:    8,
	}
	err := utilwait.ExponentialBackoff(backoff, func() (bool, error) {
		var err error
		subnet, err = plugin.osClient.HostSubnets().Get(plugin.hostName, metav1.GetOptions{})
		if err == nil {
			return true, nil
		} else if kapierrors.IsNotFound(err) {
			glog.Warningf("Could not find an allocated subnet for node: %s, Waiting...", plugin.hostName)
			return false, nil
		} else {
			return false, err
		}
	})
	if err != nil {
		return "", fmt.Errorf("failed to get subnet for this host: %s, error: %v", plugin.hostName, err)
	}

	if err = plugin.networkInfo.validateNodeIP(subnet.HostIP); err != nil {
		return "", fmt.Errorf("failed to validate own HostSubnet: %v", err)
	}

	return subnet.Subnet, nil
}

func (plugin *OsdnNode) alreadySetUp(localSubnetGatewayCIDR, clusterNetworkCIDR string) bool {
	var found bool

	exec := kexec.New()
	itx := ipcmd.NewTransaction(exec, TUN)
	addrs, err := itx.GetAddresses()
	itx.EndTransaction()
	if err != nil {
		return false
	}
	found = false
	for _, addr := range addrs {
		if strings.Contains(addr, localSubnetGatewayCIDR) {
			found = true
			break
		}
	}
	if !found {
		return false
	}

	itx = ipcmd.NewTransaction(exec, TUN)
	routes, err := itx.GetRoutes()
	itx.EndTransaction()
	if err != nil {
		return false
	}
	found = false
	for _, route := range routes {
		if strings.Contains(route, clusterNetworkCIDR+" ") {
			found = true
			break
		}
	}
	if !found {
		return false
	}

	if !plugin.oc.AlreadySetUp() {
		return false
	}

	return true
}

func deleteLocalSubnetRoute(device, localSubnetCIDR string) {
	backoff := utilwait.Backoff{
		Duration: 100 * time.Millisecond,
		Factor:   1.25,
		Steps:    6,
	}
	err := utilwait.ExponentialBackoff(backoff, func() (bool, error) {
		itx := ipcmd.NewTransaction(kexec.New(), device)
		routes, err := itx.GetRoutes()
		if err != nil {
			return false, fmt.Errorf("could not get routes: %v", err)
		}
		for _, route := range routes {
			if strings.Contains(route, localSubnetCIDR) {
				itx.DeleteRoute(localSubnetCIDR)
				err = itx.EndTransaction()
				if err != nil {
					return false, fmt.Errorf("could not delete route: %v", err)
				}
				return true, nil
			}
		}
		return false, nil
	})

	if err != nil {
		glog.Errorf("Error removing %s route from dev %s: %v; if the route appears later it will not be deleted.", localSubnetCIDR, device, err)
	}
}

func (plugin *OsdnNode) SetupSDN() (bool, error) {
	clusterNetworkCIDR := plugin.networkInfo.ClusterNetwork.String()
	serviceNetworkCIDR := plugin.networkInfo.ServiceNetwork.String()

	localSubnetCIDR := plugin.localSubnetCIDR
	_, ipnet, err := net.ParseCIDR(localSubnetCIDR)
	localSubnetMaskLength, _ := ipnet.Mask.Size()
	localSubnetGateway := netutils.GenerateDefaultGateway(ipnet).String()

	glog.V(5).Infof("[SDN setup] node pod subnet %s gateway %s", ipnet.String(), localSubnetGateway)

	exec := kexec.New()

	if plugin.clearLbr0IptablesRule {
		// Delete docker's left-over lbr0 rule; cannot do this from
		// NewNodePlugin (where docker is cleaned up) because we need
		// localSubnetCIDR which is only valid after plugin start
		ipt := iptables.New(exec, utildbus.New(), iptables.ProtocolIpv4)
		ipt.DeleteRule(iptables.TableNAT, iptables.ChainPostrouting, "-s", localSubnetCIDR, "!", "-o", "lbr0", "-j", "MASQUERADE")
	}

	gwCIDR := fmt.Sprintf("%s/%d", localSubnetGateway, localSubnetMaskLength)
	if plugin.alreadySetUp(gwCIDR, clusterNetworkCIDR) {
		glog.V(5).Infof("[SDN setup] no SDN setup required")
		return false, nil
	}
	glog.V(5).Infof("[SDN setup] full SDN setup required")

	err = plugin.oc.SetupOVS(clusterNetworkCIDR, serviceNetworkCIDR, localSubnetCIDR, localSubnetGateway)
	if err != nil {
		return false, err
	}

	itx := ipcmd.NewTransaction(exec, TUN)
	itx.AddAddress(gwCIDR)
	defer deleteLocalSubnetRoute(TUN, localSubnetCIDR)
	itx.SetLink("mtu", fmt.Sprint(plugin.mtu))
	itx.SetLink("up")
	itx.AddRoute(clusterNetworkCIDR, "proto", "kernel", "scope", "link")
	itx.AddRoute(serviceNetworkCIDR)
	err = itx.EndTransaction()
	if err != nil {
		return false, err
	}

	sysctl := sysctl.New()

	// Enable IP forwarding for ipv4 packets
	err = sysctl.SetSysctl("net/ipv4/ip_forward", 1)
	if err != nil {
		return false, fmt.Errorf("could not enable IPv4 forwarding: %s", err)
	}
	err = sysctl.SetSysctl(fmt.Sprintf("net/ipv4/conf/%s/forwarding", TUN), 1)
	if err != nil {
		return false, fmt.Errorf("could not enable IPv4 forwarding on %s: %s", TUN, err)
	}

	return true, nil
}

func (plugin *OsdnNode) updateEgressNetworkPolicyRules(vnid uint32) {
	policies := plugin.egressPolicies[vnid]
	namespaces := plugin.policy.GetNamespaces(vnid)
	if err := plugin.oc.UpdateEgressNetworkPolicyRules(policies, vnid, namespaces, plugin.egressDNS); err != nil {
		glog.Errorf("Error updating OVS flows for EgressNetworkPolicy: %v", err)
	}
}

func (plugin *OsdnNode) AddHostSubnetRules(subnet *osapi.HostSubnet) {
	glog.Infof("AddHostSubnetRules for %s", hostSubnetToString(subnet))
	if err := plugin.oc.AddHostSubnetRules(subnet); err != nil {
		glog.Errorf("Error adding OVS flows for subnet %q: %v", subnet.Subnet, err)
	}
}

func (plugin *OsdnNode) DeleteHostSubnetRules(subnet *osapi.HostSubnet) {
	glog.Infof("DeleteHostSubnetRules for %s", hostSubnetToString(subnet))
	if err := plugin.oc.DeleteHostSubnetRules(subnet); err != nil {
		glog.Errorf("Error deleting OVS flows for subnet %q: %v", subnet.Subnet, err)
	}
}

func (plugin *OsdnNode) AddServiceRules(service *kapi.Service, netID uint32) {
	glog.V(5).Infof("AddServiceRules for %v", service)
	if err := plugin.oc.AddServiceRules(service, netID); err != nil {
		glog.Errorf("Error adding OVS flows for service %v, netid %d: %v", service, netID, err)
	}
}

func (plugin *OsdnNode) DeleteServiceRules(service *kapi.Service) {
	glog.V(5).Infof("DeleteServiceRules for %v", service)
	if err := plugin.oc.DeleteServiceRules(service); err != nil {
		glog.Errorf("Error deleting OVS flows for service %v: %v", service, err)
	}
}
<|endoftext|>


<commit_before>package serialize

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"

	"github.com/xephonhq/xephon-b/pkg/common"

	"fmt"
	"time"
)

// test implementation satisfies the interface
func TestSerializerInterface(t *testing.T) {
	t.Parallel()
	var _ Serializer = (*DebugSerializer)(nil)
	var _ Serializer = (*JsonSerializer)(nil)
}

type SerializeTestSuite struct {
	suite.Suite
	iP *common.IntPointWithSeries
	dP *common.DoublePointWithSeries
	ts int64
}

func TestSerializeTestSuite(t *testing.T) {
	suite.Run(t, new(SerializeTestSuite))
}

func (suite *SerializeTestSuite) SetupTest() {
	name := "cpu.idle"
	s := common.NewSeries(name)
	s.AddTag("os", "ubuntu")
	s.AddTag("arch", "amd64")
	ts := time.Now().UnixNano()
	suite.ts = ts
	suite.iP = &common.IntPointWithSeries{Series: s}
	suite.iP.V = 123
	suite.iP.TimeNano = ts
	suite.dP = &common.DoublePointWithSeries{Series: s}
	suite.dP.V = 12.03
	suite.dP.TimeNano = ts

}

func (suite *SerializeTestSuite) TestDebugSerializer(t *testing.T) {
	assert := assert.New(t)
	ds := DebugSerializer{}

	o := fmt.Sprintf("cpu.idle:os=ubuntu,arch=amd64, %d %d", 123, suite.ts)
	w, _ := ds.WriteInt(suite.iP)
	assert.Equal(o, string(w))

	o = fmt.Sprintf("cpu.idle:os=ubuntu,arch=amd64, %0.2f %d", 12.03, suite.ts)
	w, _ = ds.WriteDouble(suite.dP)
	assert.Equal(o, string(w))
}
<commit_msg>[serialize][test] Fix #11 test suite panic<commit_after>package serialize

import (
	"testing"

	"github.com/stretchr/testify/assert"
	"github.com/stretchr/testify/suite"

	"github.com/xephonhq/xephon-b/pkg/common"

	"fmt"
	"time"
)

// test implementation satisfies the interface
func TestSerializerInterface(t *testing.T) {
	t.Parallel()
	var _ Serializer = (*DebugSerializer)(nil)
	var _ Serializer = (*JsonSerializer)(nil)
}

type SerializeTestSuite struct {
	suite.Suite
	iP *common.IntPointWithSeries
	dP *common.DoublePointWithSeries
	ts int64
}

func TestSerializeTestSuite(t *testing.T) {
	suite.Run(t, new(SerializeTestSuite))
}

func (suite *SerializeTestSuite) SetupTest() {
	name := "cpu.idle"
	s := common.NewSeries(name)
	s.AddTag("os", "ubuntu")
	s.AddTag("arch", "amd64")
	ts := time.Now().UnixNano()
	suite.ts = ts
	suite.iP = &common.IntPointWithSeries{Series: s}
	suite.iP.V = 123
	suite.iP.TimeNano = ts
	suite.dP = &common.DoublePointWithSeries{Series: s}
	suite.dP.V = 12.03
	suite.dP.TimeNano = ts

}

func (suite *SerializeTestSuite) TestDebugSerializer() {
	assert := assert.New(suite.T())
	ds := DebugSerializer{}

	o := fmt.Sprintf("cpu.idle:os=ubuntu,arch=amd64, %d %d", 123, suite.ts)
	w, _ := ds.WriteInt(suite.iP)
	assert.Equal(o, string(w))

	o = fmt.Sprintf("cpu.idle:os=ubuntu,arch=amd64, %0.2f %d", 12.03, suite.ts)
	w, _ = ds.WriteDouble(suite.dP)
	assert.Equal(o, string(w))
}
<|endoftext|>


<commit_before>package store

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/slinga/engine/actual"
	"github.com/Aptomi/aptomi/pkg/slinga/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"github.com/Aptomi/aptomi/pkg/slinga/object/store"
)

func (s *defaultStore) GetActualState() (*resolve.PolicyResolution, error) {
	// todo empty state temporarily
	actualState := resolve.NewPolicyResolution()

	return actualState, nil
}

func (s *defaultStore) ActualStateUpdater() actual.StateUpdater {
	return &defaultStateUpdater{s.store}
}

type defaultStateUpdater struct {
	store store.ObjectStore
}

func (u *defaultStateUpdater) Create(obj object.Base) error {
	return u.Update(obj)
}

func (u *defaultStateUpdater) Update(obj object.Base) error {
	if _, ok := obj.(*resolve.ComponentInstance); !ok {
		return fmt.Errorf("Only ComponentInstances could be updated using actual.StateUpdater, not: %T", obj)
	}

	_, err := u.store.Save(obj)
	return err
}

func (u *defaultStateUpdater) Delete(string) error {
	// todo
	panic("not implemented: defaultStateUpdater.Delete")
}
<commit_msg>Add support for loading actual state<commit_after>package store

import (
	"fmt"
	"github.com/Aptomi/aptomi/pkg/slinga/engine/actual"
	"github.com/Aptomi/aptomi/pkg/slinga/engine/resolve"
	"github.com/Aptomi/aptomi/pkg/slinga/object"
	"github.com/Aptomi/aptomi/pkg/slinga/object/store"
)

func (s *defaultStore) GetActualState() (*resolve.PolicyResolution, error) {
	// todo empty state temporarily
	actualState := resolve.NewPolicyResolution()

	instances, err := s.store.GetAll(object.SystemNS, resolve.ComponentInstanceObject.Kind)
	if err != nil {
		return nil, fmt.Errorf("Error while getting all component instances: ", err)
	}

	for _, instanceObj := range instances {
		if instance, ok := instanceObj.(*resolve.ComponentInstance); ok {
			key := instance.GetKey()
			actualState.ComponentInstanceMap[key] = instance
			actualState.ComponentProcessingOrder = append(actualState.ComponentProcessingOrder, key)
		}
	}

	return actualState, nil
}

func (s *defaultStore) ActualStateUpdater() actual.StateUpdater {
	return &defaultStateUpdater{s.store}
}

type defaultStateUpdater struct {
	store store.ObjectStore
}

func (u *defaultStateUpdater) Create(obj object.Base) error {
	return u.Update(obj)
}

func (u *defaultStateUpdater) Update(obj object.Base) error {
	if _, ok := obj.(*resolve.ComponentInstance); !ok {
		return fmt.Errorf("Only ComponentInstances could be updated using actual.StateUpdater, not: %T", obj)
	}

	_, err := u.store.Save(obj)
	return err
}

func (u *defaultStateUpdater) Delete(string) error {
	// todo
	panic("not implemented: defaultStateUpdater.Delete")
}
<|endoftext|>