<commit_before>package haaasd

import (
	"bytes"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"os/exec"
	"time"
)

func NewHaproxy(properties *Config, application string, platform string, version string) *Haproxy {
	if version == "" {
		version = "1.4.22"
	}
	return &Haproxy{
		Application: application,
		Platform:    platform,
		properties:  properties,
		Version:     version,
	}
}

type Haproxy struct {
	Application string
	Platform    string
	Version     string
	properties  *Config
	State       int
}

const (
	SUCCESS int = iota
	UNCHANGED int = iota
	ERR_SYSLOG int = iota
	ERR_CONF int = iota
	ERR_RELOAD int = iota
)

// ApplyConfiguration write the new configuration and reload
// A rollback is called on failure
func (hap *Haproxy) ApplyConfiguration(data *EventMessage) (int, error) {
	hap.createSkeleton(data.Correlationid)

	newConf := data.Conf
	path := hap.confPath()

	// Check conf diff
	oldConf, err := ioutil.ReadFile(path)

	if err != nil {
		log.WithFields(log.Fields{
			"correlationId" : data.Correlationid,
			"path": path,
		}).Error("Cannot read old configuration")
		return ERR_CONF, err
	}

	if bytes.Equal(oldConf, newConf) {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"application": data.Application,
			"plateform":   data.Platform,
		}).Info("Ignore unchanged configuration")
		return UNCHANGED, nil
	}

	// Archive previous configuration
	archivePath := hap.confArchivePath()
	os.Rename(path, archivePath)
	log.WithFields(
		log.Fields{
			"correlationId": data.Correlationid,
			"application": data.Application,
			"plateform":   data.Platform,
			"archivePath": archivePath,
		}).Info("Old configuration saved")
	err = ioutil.WriteFile(path, newConf, 0644)
	if err != nil {
		return ERR_CONF, err
	}
	log.WithField("path", path).Info("New configuration written")

	// Reload haproxy
	err = hap.reload(data.Correlationid)
	if err != nil {
		log.WithFields(log.Fields{
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Reload failed")
		hap.dumpConfiguration(newConf, data)
		err = hap.rollback(data.Correlationid)
		return ERR_RELOAD, err
	}
	// Write syslog fragment
	fragmentPath := hap.syslogFragmentPath()
	err = ioutil.WriteFile(fragmentPath, data.SyslogFragment, 0644)
	if err != nil {
		log.WithFields(log.Fields{
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Failed to write syslog fragment")
		// TODO Should we rollback on syslog error ?
		return ERR_SYSLOG, err
	}

	return SUCCESS, nil
}

// dumpConfiguration dumps the new configuration file with context for debugging purpose
func (hap *Haproxy) dumpConfiguration(newConf []byte, data *EventMessage) {
	errorFilename := hap.NewErrorPath()
	f, err2 := os.Create(errorFilename)
	defer f.Close()
	if err2 == nil {
		f.WriteString("================================================================\n")
		f.WriteString(fmt.Sprintf("application: %s\n", data.Application))
		f.WriteString(fmt.Sprintf("platform: %s\n", data.Platform))
		f.WriteString(fmt.Sprintf("correlationid: %s\n", data.Correlationid))
		f.WriteString("================================================================\n")
		f.Write(newConf)
		f.Sync()

		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"filename": errorFilename,
			"application": data.Application,
			"platform": data.Platform,
		}).Info("Invalid conf logged into %s")
	}
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) confPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/Config"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// confPath give the path of the archived configuration file given an application context
func (hap *Haproxy) confArchivePath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/version-1"
	// It returns the absolute path to the file
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// NewErrorPath gives a unique path the error file given the hap context
// It returns the full path to the file
func (hap *Haproxy) NewErrorPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/errors"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

// reload calls external shell script to reload haproxy
// It returns error if the reload fails
func (hap *Haproxy) reload(correlationId string) error {

	reloadScript := hap.getReloadScript()
	cmd, err := exec.Command("sh", reloadScript, "reload", "-y").Output()
	if err != nil {
		log.WithError(err).Error("Error reloading")
	}
	log.WithFields(log.Fields{
		"correlationId" : correlationId,
		"application": hap.Application,
		"platform": hap.Platform,
		"reloadScript": reloadScript,
	}).WithField("cmd", cmd).Debug("Reload succeeded")
	return err
}

// rollbac reverts configuration files and call for reload
func (hap *Haproxy) rollback(correlationId string) error {
	lastConf := hap.confArchivePath()
	if _, err := os.Stat(lastConf); os.IsNotExist(err) {
		return errors.New("No configuration file to rollback")
	}
	os.Rename(lastConf, hap.confPath())
	hap.reload(correlationId)
	return nil
}

// createSkeleton creates the directory tree for a new haproxy context
func (hap *Haproxy) createSkeleton(correlationId string) error {
	baseDir := hap.properties.HapHome + "/" + hap.Application

	createDirectory(correlationId, baseDir + "/Config")
	createDirectory(correlationId, baseDir + "/logs/" + hap.Application + hap.Platform)
	createDirectory(correlationId, baseDir + "/scripts")
	createDirectory(correlationId, baseDir + "/version-1")

	updateSymlink(correlationId, hap.getHapctlFilename(), hap.getReloadScript())
	updateSymlink(correlationId, hap.getHapBinary(), baseDir + "/Config/haproxy")

	log.WithFields(log.Fields{
		"correlationId" : correlationId,
		"dir": baseDir,
	}).Info("Skeleton created")

	return nil
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) syslogFragmentPath() string {
	baseDir := hap.properties.HapHome + "/SYSLOG/Config/syslog.conf.d"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/syslog" + hap.Application + hap.Platform + ".conf"
}

// updateSymlink create or update a symlink
func updateSymlink(correlationId, oldname string, newname string) {
	if _, err := os.Stat(newname); err == nil {
		os.Remove(newname)
	}
	err := os.Symlink(oldname, newname)
	if err != nil {
		log.WithError(err).WithFields(log.Fields{
			"correlationId" : correlationId,
			"path": newname,
		}).Error("Failed to create symlink")
	}
}

// createDirectory recursively creates directory if it doesn't exists
func createDirectory(correlationId string, dir string) {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.WithError(err).WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Error("Failed to create")
		} else {
			log.WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Info("Directory created")
		}
	}
}

// getHapctlFilename return the path to the vsc hapctl shell script
// This script is provided
func (hap *Haproxy) getHapctlFilename() string {
	return "/HOME/uxwadm/scripts/hapctl_unif"
}

// getReloadScript calculates reload script path given the hap context
// It returns the full script path
func (hap *Haproxy) getReloadScript() string {
	return fmt.Sprintf("%s/%s/scripts/hapctl%s%s", hap.properties.HapHome, hap.Application, hap.Application, hap.Platform)
}

// getHapBinary calculates the haproxy binary to use given the expected version
// It returns the full path to the haproxy binary
func (hap *Haproxy) getHapBinary() string {
	return fmt.Sprintf("/export/product/haproxy/product/%s/bin/haproxy", hap.Version)
}
<commit_msg>Add dump debug<commit_after>package haaasd

import (
	"bytes"
	"errors"
	"fmt"
	log "github.com/Sirupsen/logrus"
	"io/ioutil"
	"os"
	"os/exec"
	"time"
)

func NewHaproxy(properties *Config, application string, platform string, version string) *Haproxy {
	if version == "" {
		version = "1.4.22"
	}
	return &Haproxy{
		Application: application,
		Platform:    platform,
		properties:  properties,
		Version:     version,
	}
}

type Haproxy struct {
	Application string
	Platform    string
	Version     string
	properties  *Config
	State       int
}

const (
	SUCCESS int = iota
	UNCHANGED int = iota
	ERR_SYSLOG int = iota
	ERR_CONF int = iota
	ERR_RELOAD int = iota
)

// ApplyConfiguration write the new configuration and reload
// A rollback is called on failure
func (hap *Haproxy) ApplyConfiguration(data *EventMessage) (int, error) {
	hap.createSkeleton(data.Correlationid)

	newConf := data.Conf
	path := hap.confPath()

	// Check conf diff
	oldConf, err := ioutil.ReadFile(path)

	if err != nil {
		log.WithFields(log.Fields{
			"correlationId" : data.Correlationid,
			"path": path,
		}).Error("Cannot read old configuration")
		return ERR_CONF, err
	}
	if log.GetLevel() == log.DebugLevel {
		hap.dumpConfiguration(hap.NewDebugPath(), newConf, data)
	}
	if bytes.Equal(oldConf, newConf) {
		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"application": data.Application,
			"plateform":   data.Platform,
		}).Info("Ignore unchanged configuration")
		return UNCHANGED, nil
	}

	// Archive previous configuration
	archivePath := hap.confArchivePath()
	os.Rename(path, archivePath)
	log.WithFields(
		log.Fields{
			"correlationId": data.Correlationid,
			"application": data.Application,
			"plateform":   data.Platform,
			"archivePath": archivePath,
		}).Info("Old configuration saved")
	err = ioutil.WriteFile(path, newConf, 0644)
	if err != nil {
		return ERR_CONF, err
	}
	log.WithField("path", path).Info("New configuration written")

	// Reload haproxy
	err = hap.reload(data.Correlationid)
	if err != nil {
		log.WithFields(log.Fields{
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Reload failed")
		hap.dumpConfiguration(hap.NewErrorPath(), newConf, data)
		err = hap.rollback(data.Correlationid)
		return ERR_RELOAD, err
	}
	// Write syslog fragment
	fragmentPath := hap.syslogFragmentPath()
	err = ioutil.WriteFile(fragmentPath, data.SyslogFragment, 0644)
	if err != nil {
		log.WithFields(log.Fields{
			"application": data.Application,
			"plateform":   data.Platform,
		}).WithError(err).Error("Failed to write syslog fragment")
		// TODO Should we rollback on syslog error ?
		return ERR_SYSLOG, err
	}

	return SUCCESS, nil
}

// dumpConfiguration dumps the new configuration file with context for debugging purpose
func (hap *Haproxy) dumpConfiguration(filename string, newConf []byte, data *EventMessage) {

	f, err2 := os.Create(filename)
	defer f.Close()
	if err2 == nil {
		f.WriteString("================================================================\n")
		f.WriteString(fmt.Sprintf("application: %s\n", data.Application))
		f.WriteString(fmt.Sprintf("platform: %s\n", data.Platform))
		f.WriteString(fmt.Sprintf("correlationid: %s\n", data.Correlationid))
		f.WriteString("================================================================\n")
		f.Write(newConf)
		f.Sync()

		log.WithFields(log.Fields{
			"correlationId": data.Correlationid,
			"filename": filename,
			"application": data.Application,
			"platform": data.Platform,
		}).Info("Dump configuration")
	}
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) confPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/Config"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// confPath give the path of the archived configuration file given an application context
func (hap *Haproxy) confArchivePath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/version-1"
	// It returns the absolute path to the file
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/hap" + hap.Application + hap.Platform + ".conf"
}

// NewErrorPath gives a unique path the error file given the hap context
// It returns the full path to the file
func (hap *Haproxy) NewErrorPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/errors"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

func (hap *Haproxy) NewDebugPath() string {
	baseDir := hap.properties.HapHome + "/" + hap.Application + "/dump"
	os.MkdirAll(baseDir, 0755)
	prefix := time.Now().Format("20060102150405")
	return baseDir + "/" + prefix + "_" + hap.Application + hap.Platform + ".log"
}

// reload calls external shell script to reload haproxy
// It returns error if the reload fails
func (hap *Haproxy) reload(correlationId string) error {

	reloadScript := hap.getReloadScript()
	cmd, err := exec.Command("sh", reloadScript, "reload", "-y").Output()
	if err != nil {
		log.WithError(err).Error("Error reloading")
	}
	log.WithFields(log.Fields{
		"correlationId" : correlationId,
		"application": hap.Application,
		"platform": hap.Platform,
		"reloadScript": reloadScript,
	}).WithField("cmd", cmd).Debug("Reload succeeded")
	return err
}

// rollbac reverts configuration files and call for reload
func (hap *Haproxy) rollback(correlationId string) error {
	lastConf := hap.confArchivePath()
	if _, err := os.Stat(lastConf); os.IsNotExist(err) {
		return errors.New("No configuration file to rollback")
	}
	os.Rename(lastConf, hap.confPath())
	hap.reload(correlationId)
	return nil
}

// createSkeleton creates the directory tree for a new haproxy context
func (hap *Haproxy) createSkeleton(correlationId string) error {
	baseDir := hap.properties.HapHome + "/" + hap.Application

	createDirectory(correlationId, baseDir + "/Config")
	createDirectory(correlationId, baseDir + "/logs/" + hap.Application + hap.Platform)
	createDirectory(correlationId, baseDir + "/scripts")
	createDirectory(correlationId, baseDir + "/version-1")

	updateSymlink(correlationId, hap.getHapctlFilename(), hap.getReloadScript())
	updateSymlink(correlationId, hap.getHapBinary(), baseDir + "/Config/haproxy")

	log.WithFields(log.Fields{
		"correlationId" : correlationId,
		"dir": baseDir,
	}).Info("Skeleton created")

	return nil
}

// confPath give the path of the configuration file given an application context
// It returns the absolute path to the file
func (hap *Haproxy) syslogFragmentPath() string {
	baseDir := hap.properties.HapHome + "/SYSLOG/Config/syslog.conf.d"
	os.MkdirAll(baseDir, 0755)
	return baseDir + "/syslog" + hap.Application + hap.Platform + ".conf"
}

// updateSymlink create or update a symlink
func updateSymlink(correlationId, oldname string, newname string) {
	if _, err := os.Stat(newname); err == nil {
		os.Remove(newname)
	}
	err := os.Symlink(oldname, newname)
	if err != nil {
		log.WithError(err).WithFields(log.Fields{
			"correlationId" : correlationId,
			"path": newname,
		}).Error("Failed to create symlink")
	}
}

// createDirectory recursively creates directory if it doesn't exists
func createDirectory(correlationId string, dir string) {
	if _, err := os.Stat(dir); os.IsNotExist(err) {
		err := os.MkdirAll(dir, 0755)
		if err != nil {
			log.WithError(err).WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Error("Failed to create")
		} else {
			log.WithFields(log.Fields{
				"correlationId" : correlationId,
				"dir": dir,
			}).Info("Directory created")
		}
	}
}

// getHapctlFilename return the path to the vsc hapctl shell script
// This script is provided
func (hap *Haproxy) getHapctlFilename() string {
	return "/HOME/uxwadm/scripts/hapctl_unif"
}

// getReloadScript calculates reload script path given the hap context
// It returns the full script path
func (hap *Haproxy) getReloadScript() string {
	return fmt.Sprintf("%s/%s/scripts/hapctl%s%s", hap.properties.HapHome, hap.Application, hap.Application, hap.Platform)
}

// getHapBinary calculates the haproxy binary to use given the expected version
// It returns the full path to the haproxy binary
func (hap *Haproxy) getHapBinary() string {
	return fmt.Sprintf("/export/product/haproxy/product/%s/bin/haproxy", hap.Version)
}
<|endoftext|>


<commit_before>// Copyright © 2009 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package gospec

import (
	"container/vector";
	"fmt";
)

// Context coordinates the spec execution

type Context struct {
	targetPath []int;
	currentSpec *specification;
	specs *vector.Vector;
}

func newInitialContext() *Context {
	return newExplicitContext([]int{})
}

func newExplicitContext(targetPath []int) *Context {
	c := new(Context);
	c.targetPath = targetPath;
	c.currentSpec = nil;
	c.specs = vector.New(0);
	return c
}

func (c *Context) Specify(name string, closure func()) {
	c.enterSpec(name, closure);
	if c.shouldExecuteCurrentSpec() {
		c.executeCurrentSpec();
	}
	c.exitSpec();
}

func (c *Context) enterSpec(name string, closure func()) {
	spec := newSpecification(name, closure, c.currentSpec);
	c.specs.Push(spec);
	c.currentSpec = spec;
}

func (c *Context) shouldExecuteCurrentSpec() bool {
//	fmt.Println();
//	fmt.Println("targetPath:", c.targetPath);
//	fmt.Println("currentSpec:", c.currentSpec);

	isBelowTargetPath := currentIsBelowTargetPath(c.currentSpec.path, c.targetPath);
	isUnseen := len(c.currentSpec.path) > len(c.targetPath);
	isFirstChild := c.currentSpec.lastPathIndex() == 0;

	return isBelowTargetPath || (isUnseen && isFirstChild)
}

func currentIsBelowTargetPath(current []int, target []int) bool {
	if len(current) > len(target) {
		return false
	}
	for i := 0; i < len(current); i++ {
		if current[i] != target[i] {
			return false
		}
	}
	return true
}

func (c *Context) executeCurrentSpec() {
	c.currentSpec.execute();
}

func (c *Context) exitSpec() {
	c.currentSpec = c.currentSpec.parent;
}

// Represents a spec in a tree of specs

type specification struct {
	name string;
	closure func();
	parent *specification;
	numberOfChildren int;
	path []int;
}

func newSpecification(name string, closure func(), parent *specification) *specification {
	path := []int{};
	if parent != nil {
		path = pathForChildOf(parent.path, parent.numberOfChildren);
		parent.numberOfChildren++;
	}
	return &specification{name, closure, parent, 0, path}
}

func pathForChildOf(parentPath []int, childIndex int) []int {
	childPath := make([]int, len(parentPath) + 1);
	for i, v := range parentPath {
		childPath[i] = v
	}
	childPath[len(parentPath)] = childIndex;
	return childPath
}

func (s *specification) execute() {
	s.closure();
}

func (s *specification) lastPathIndex() int {
	if len(s.path) == 0 {
		return 0	// root specification
	}
	return s.path[len(s.path) - 1]
}

func (s *specification) String() string {
	return fmt.Sprintf("specification{%v @ %v}", s.name, s.path)
}

// Starts the spec execution

type RootSpecRunner struct {
	specName string;
	specClosure func(*Context);
}

func NewRootSpecRunner(specName string, specClosure func(*Context)) *RootSpecRunner {
	return &RootSpecRunner{specName, specClosure};
}

func (r *RootSpecRunner) runInContext(c *Context) {
	c.Specify(r.specName, func() { r.specClosure(c) });

	//fmt.Println(c.specs);
}

<commit_msg>Refactored<commit_after>// Copyright © 2009 Esko Luontola <www.orfjackal.net>
// This software is released under the Apache License 2.0.
// The license text is at http://www.apache.org/licenses/LICENSE-2.0

package gospec

import (
	"container/vector";
	"fmt";
)

// Context coordinates the spec execution

type Context struct {
	targetPath path;
	currentSpec *specification;
	specs *vector.Vector;
}

func newInitialContext() *Context {
	return newExplicitContext(emptyPath())
}

func newExplicitContext(targetPath path) *Context {
	c := new(Context);
	c.targetPath = targetPath;
	c.currentSpec = nil;
	c.specs = vector.New(0);
	return c
}

func (c *Context) Specify(name string, closure func()) {
	c.enterSpec(name, closure);
	if c.shouldExecuteCurrentSpec() {
		c.executeCurrentSpec();
	}
	c.exitSpec();
}

func (c *Context) enterSpec(name string, closure func()) {
	spec := newSpecification(name, closure, c.currentSpec);
	c.specs.Push(spec);
	c.currentSpec = spec;
}

func (c *Context) shouldExecuteCurrentSpec() bool {
//	fmt.Println();
//	fmt.Println("targetPath:", c.targetPath);
//	fmt.Println("currentSpec:", c.currentSpec);

	currentPath := c.currentSpec.path;
	targetPath := c.targetPath;

	isOnTargetPath := currentPath.isOn(targetPath);
	isUnseen := currentPath.isBeyond(targetPath);
	isFirstChild := currentPath.lastIndex() == 0;

	return isOnTargetPath || (isUnseen && isFirstChild)
}

func (c *Context) executeCurrentSpec() {
	c.currentSpec.execute();
}

func (c *Context) exitSpec() {
	c.currentSpec = c.currentSpec.parent;
}

// Spec paths

type path []int;

func emptyPath() path {
	return []int{};
}

func (parent path) append(index int) path {
	result := make([]int, len(parent) + 1);
	for i, v := range parent {
		result[i] = v
	}
	result[len(parent)] = index;
	return result
}

func (current path) isOn(target path) bool {
	if current.isBeyond(target) {
		return false
	}
	for i := 0; i < len(current); i++ {
		if current[i] != target[i] {
			return false
		}
	}
	return true
}

func (current path) isBeyond(target path) bool {
	return len(current) > len(target)
}

func (path path) lastIndex() int {
	if len(path) == 0 {
		return -1	// empty path, i.e. root specification
	}
	return path[len(path) - 1]
}

// Represents a spec in a tree of specs

type specification struct {
	name string;
	closure func();
	parent *specification;
	numberOfChildren int;
	path path;
}

func newSpecification(name string, closure func(), parent *specification) *specification {
	path := emptyPath();
	if parent != nil {
		currentIndex := parent.numberOfChildren;
		path = parent.path.append(currentIndex);
		parent.numberOfChildren++;
	}
	return &specification{name, closure, parent, 0, path}
}

func (s *specification) execute() {
	s.closure();
}

func (s *specification) String() string {
	return fmt.Sprintf("specification{%v @ %v}", s.name, s.path)
}

// Starts the spec execution

type RootSpecRunner struct {
	specName string;
	specClosure func(*Context);
}

func NewRootSpecRunner(specName string, specClosure func(*Context)) *RootSpecRunner {
	return &RootSpecRunner{specName, specClosure};
}

func (r *RootSpecRunner) runInContext(c *Context) {
	c.Specify(r.specName, func() { r.specClosure(c) });

	//fmt.Println(c.specs);
}

<|endoftext|>


<commit_before>package nodes

import (
	"fmt"
	"io"
	"os"

	"github.com/freeconf/gconf/meta"
	"github.com/freeconf/gconf/node"
	"github.com/freeconf/gconf/val"
)

const padding = "                                                                                "

type dump struct {
	Out io.Writer
}

// DumpBin sends useful information to string when written to
func DumpBin(out io.Writer) node.Node {
	return Dump(Null(), out)
}

// Dumpf will send useful information to file while delegating reads/writes to the given
// node
func Dumpf(n node.Node, fname string) node.Node {
	f, ferr := os.Create(fname)
	if ferr != nil {
		panic(ferr)
	}
	return dump{
		Out: f,
	}.Node(0, n)
}

// Dump will send useful information to writer while delegating reads/writes to the given
// node
func Dump(n node.Node, out io.Writer) node.Node {
	return dump{
		Out: out,
	}.Node(0, n)
}

func (self dump) Close() {
	if closer, ok := self.Out.(io.ReadCloser); ok {
		closer.Close()
	}
}

func (self dump) write(s string, args ...interface{}) {
	self.Out.Write([]byte(fmt.Sprintf(s, args...)))
}

func (self dump) eol() {
	self.Out.Write([]byte("\n"))
}

func (self dump) check(e error) {
	if e != nil {
		self.write(",! ! ! err=%s ! ! !", e.Error())
	}
}

func (self dump) Node(level int, target node.Node) node.Node {
	n := &Basic{}
	n.OnAction = target.Action
	n.OnNotify = target.Notify
	n.OnChoose = func(sel node.Selection, choice *meta.Choice) (choosen *meta.ChoiceCase, err error) {
		self.write("%schoose %s=", padding[:level], choice.Ident())
		choosen, err = target.Choose(sel, choice)
		if choosen != nil {
			self.write(choosen.Ident())
		} else {
			self.write("nil")
		}
		self.check(err)
		self.eol()
		return choosen, err
	}
	n.OnChild = func(r node.ChildRequest) (child node.Node, err error) {
		self.write("%s{%s", padding[:level], r.Meta.Ident())
		if r.New {
			self.write(", new")
		}
		child, err = target.Child(r)
		if child != nil {
			self.write(", found")
		} else {
			self.write(", !found")
		}
		self.check(err)
		self.eol()
		if child == nil {
			return nil, err
		}
		return self.Node(level+1, child), nil
	}
	n.OnField = func(r node.FieldRequest, hnd *node.ValueHandle) (err error) {
		if r.Write {
			self.write("%s->%s=%s(", padding[:level], r.Meta.Ident(), hnd.Val.Format())
			err = target.Field(r, hnd)
			self.write("%v)", hnd.Val.String())
			self.check(err)
			self.eol()
		} else {
			self.write("%s<-%s=", padding[:level], r.Meta.Ident())
			err = target.Field(r, hnd)
			if hnd.Val != nil {
				self.write("%s(%v)", hnd.Val.Format(), hnd.Val.String())
			} else {
				self.write("nil")
			}
			self.check(err)
			self.eol()
		}
		return
	}
	n.OnNext = func(r node.ListRequest) (next node.Node, key []val.Value, err error) {
		self.write("%s[%s, row=%d", padding[:level], r.Meta.Ident(), r.Row)
		if r.New {
			self.write(", new")
		}
		if r.First {
			self.write(", first")
		}
		next, key, err = target.Next(r)
		if next != nil {
			self.write(", found key=%v", key)
		} else {
			self.write(", !found")
		}
		self.check(err)
		self.eol()
		if next == nil {
			return nil, key, err
		}
		return self.Node(level, next), key, err
	}
	onNodeRequest := func(r node.NodeRequest, entry string) {
		self.write("%s%s, new=%v, src=%s", padding[:level], entry, r.New, r.Source.Path.String())
		self.eol()
	}
	n.OnBeginEdit = func(r node.NodeRequest) (err error) {
		onNodeRequest(r, "BeginEdit")
		return target.BeginEdit(r)
	}
	n.OnEndEdit = func(r node.NodeRequest) (err error) {
		onNodeRequest(r, "EndEdit")
		return target.EndEdit(r)
	}
	n.OnDelete = func(r node.NodeRequest) (err error) {
		onNodeRequest(r, "Delete")
		return target.Delete(r)
	}
	return n
}
<commit_msg>Dump to proxy peeking as well<commit_after>package nodes

import (
	"fmt"
	"io"
	"os"

	"github.com/freeconf/gconf/meta"
	"github.com/freeconf/gconf/node"
	"github.com/freeconf/gconf/val"
)

const padding = "                                                                                "

type dump struct {
	Out io.Writer
}

// DumpBin sends useful information to string when written to
func DumpBin(out io.Writer) node.Node {
	return Dump(Null(), out)
}

// Dumpf will send useful information to file while delegating reads/writes to the given
// node
func Dumpf(n node.Node, fname string) node.Node {
	f, ferr := os.Create(fname)
	if ferr != nil {
		panic(ferr)
	}
	return dump{
		Out: f,
	}.Node(0, n)
}

// Dump will send useful information to writer while delegating reads/writes to the given
// node
func Dump(n node.Node, out io.Writer) node.Node {
	return dump{
		Out: out,
	}.Node(0, n)
}

func (self dump) Close() {
	if closer, ok := self.Out.(io.ReadCloser); ok {
		closer.Close()
	}
}

func (self dump) write(s string, args ...interface{}) {
	self.Out.Write([]byte(fmt.Sprintf(s, args...)))
}

func (self dump) eol() {
	self.Out.Write([]byte("\n"))
}

func (self dump) check(e error) {
	if e != nil {
		self.write(",! ! ! err=%s ! ! !", e.Error())
	}
}

func (self dump) Node(level int, target node.Node) node.Node {
	n := &Basic{}
	n.OnPeek = target.Peek
	n.OnAction = target.Action
	n.OnNotify = target.Notify
	n.OnChoose = func(sel node.Selection, choice *meta.Choice) (choosen *meta.ChoiceCase, err error) {
		self.write("%schoose %s=", padding[:level], choice.Ident())
		choosen, err = target.Choose(sel, choice)
		if choosen != nil {
			self.write(choosen.Ident())
		} else {
			self.write("nil")
		}
		self.check(err)
		self.eol()
		return choosen, err
	}
	n.OnChild = func(r node.ChildRequest) (child node.Node, err error) {
		self.write("%s{%s", padding[:level], r.Meta.Ident())
		if r.New {
			self.write(", new")
		}
		child, err = target.Child(r)
		if child != nil {
			self.write(", found")
		} else {
			self.write(", !found")
		}
		self.check(err)
		self.eol()
		if child == nil {
			return nil, err
		}
		return self.Node(level+1, child), nil
	}
	n.OnField = func(r node.FieldRequest, hnd *node.ValueHandle) (err error) {
		if r.Write {
			self.write("%s->%s=%s(", padding[:level], r.Meta.Ident(), hnd.Val.Format())
			err = target.Field(r, hnd)
			self.write("%v)", hnd.Val.String())
			self.check(err)
			self.eol()
		} else {
			self.write("%s<-%s=", padding[:level], r.Meta.Ident())
			err = target.Field(r, hnd)
			if hnd.Val != nil {
				self.write("%s(%v)", hnd.Val.Format(), hnd.Val.String())
			} else {
				self.write("nil")
			}
			self.check(err)
			self.eol()
		}
		return
	}
	n.OnNext = func(r node.ListRequest) (next node.Node, key []val.Value, err error) {
		self.write("%s[%s, row=%d", padding[:level], r.Meta.Ident(), r.Row)
		if r.New {
			self.write(", new")
		}
		if r.First {
			self.write(", first")
		}
		next, key, err = target.Next(r)
		if next != nil {
			self.write(", found key=%v", key)
		} else {
			self.write(", !found")
		}
		self.check(err)
		self.eol()
		if next == nil {
			return nil, key, err
		}
		return self.Node(level, next), key, err
	}
	onNodeRequest := func(r node.NodeRequest, entry string) {
		self.write("%s%s, new=%v, src=%s", padding[:level], entry, r.New, r.Source.Path.String())
		self.eol()
	}
	n.OnBeginEdit = func(r node.NodeRequest) (err error) {
		onNodeRequest(r, "BeginEdit")
		return target.BeginEdit(r)
	}
	n.OnEndEdit = func(r node.NodeRequest) (err error) {
		onNodeRequest(r, "EndEdit")
		return target.EndEdit(r)
	}
	n.OnDelete = func(r node.NodeRequest) (err error) {
		onNodeRequest(r, "Delete")
		return target.Delete(r)
	}
	return n
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"os"

	"github.com/pgombola/gomad/client"
)

var (
	nomad string
	hosts bool
	jobs  bool
)

func init() {
	flag.StringVar(&nomad, "nomad", "", "Host address and port of nomad server")
	flag.BoolVar(&hosts, "hosts", false, "Retrieve the status of the hosts")
	flag.BoolVar(&jobs, "jobs", false, "Retrieve the status of the jobs")
}

func main() {
	flag.Parse()

	if nomad == "" {
		fmt.Print("nomad flag must be set.")
		os.Exit(-1)
	}

	if hosts {
		client.PopulateHosts("http://" + nomad)
		fmt.Println(client.HostsToString())
	}

	if jobs {
		client.PopulateJobs("http://" + nomad)
		client.PrintJobs()
	}
}
<commit_msg>Removed nodestatus.go; example no longer needed<commit_after><|endoftext|>


<commit_before>package hexmap

import (
	"testing"
	"fmt"
	"encoding/json"
	"strings"
)

func TestHexMapConstructor(t *testing.T) {
	hm0 := HexMap{Name: "testmap", Size: HexVector{3, 5}, Origin: ORIGIN}

	if hm0.Name != "testmap" {
		t.Error("HexMap.Name - expected: 'testmap', actual: '" +  hm0.Name + "'")
	}

	if ! hm0.Size.Equal(HexVector{3, 5}) {
		t.Error(fmt.Sprintf("HexMap.Size - expected: {3, 5}, actual: {%d, %d}", hm0.Size.Hx(), hm0.Size.Hy()))
	}

	if ! hm0.Origin.Equal(ORIGIN) {
		t.Error(fmt.Sprintf("HexMap.Size - expected: {0, 0}, actual: {%d, %d}", hm0.Origin.Hx(), hm0.Origin.Hy()))
	}

}

func TestHexMapMarshal(t *testing.T) {

	hm := HexMap{Name: "TestMap", Size: HexVector{22, 14}, Origin: ORIGIN}

	jhm, err := json.Marshal(hm)

	if err != nil {
		t.Error(fmt.Sprintf("HexMap.Marshal() - error: %s", err))
	}

	if strings.Compare(string(jhm), "{\"name\":\"TestMap\",\"size\":{\"hx\":22,\"hy\":14},\"origin\":{\"hx\":0,\"hy\":0}}") != 0 {
		t.Error(fmt.Sprintf("HexMap.Marshal(), - result: %s", jhm))
	}
}

// test containment and bias

func TestYbias(t *testing.T) {
	hm0 := HexMap{Name: "ybiastest", Size: HexVector{22,14}, Origin: ORIGIN}

	values := [][]int{{-3,-2}, {-2,-1}, {-1,-1}, {0,0}, {1,0}, {2,1}, {3,1}, {4,2}}

	for _, pair := range values {
		if hm0.ybias(pair[0]) != pair[1] {
			t.Error(fmt.Sprintf("HexMap.ybias() - %d -> expect: %d, actual: %d", pair[0], pair[1], hm0.ybias(pair[0])))

		}
	}
}

func TestContains(t *testing.T) {
	hm0 := HexMap{Name: "containmenttest", Size: HexVector{14,22}, Origin: ORIGIN}

	var values = []struct {
		hv HexVector
		contains bool
	}{
		{HexVector{0, 0}, true},
		{HexVector{-1,0}, false},
		{HexVector{13, 7}, true},
		{HexVector{14, 7}, false},
		{HexVector{0, 21}, true},
		{HexVector{0, 22}, false},
		{HexVector{13,5}, false},
		{HexVector{13,6}, true},
		{HexVector{13,21+6}, true},
		{HexVector{13,21+7}, false},
	}

	for _, testvalue := range values {
		if hm0.Contains(testvalue.hv) != testvalue.contains {
			t.Error(fmt.Sprintf("test contains is wrong: %s", testvalue.hv))
		}
	}

}
<commit_msg>fix null terrains in map test<commit_after>package hexmap

import (
	"testing"
	"fmt"
	"encoding/json"
	"strings"
)

func TestHexMapConstructor(t *testing.T) {
	hm0 := HexMap{Name: "testmap", Size: HexVector{3, 5}, Origin: ORIGIN}

	if hm0.Name != "testmap" {
		t.Error("HexMap.Name - expected: 'testmap', actual: '" +  hm0.Name + "'")
	}

	if ! hm0.Size.Equal(HexVector{3, 5}) {
		t.Error(fmt.Sprintf("HexMap.Size - expected: {3, 5}, actual: {%d, %d}", hm0.Size.Hx(), hm0.Size.Hy()))
	}

	if ! hm0.Origin.Equal(ORIGIN) {
		t.Error(fmt.Sprintf("HexMap.Size - expected: {0, 0}, actual: {%d, %d}", hm0.Origin.Hx(), hm0.Origin.Hy()))
	}

}

func TestHexMapMarshal(t *testing.T) {

	hm := HexMap{Name: "TestMap", Size: HexVector{22, 14}, Origin: ORIGIN}

	jhm, err := json.Marshal(hm)

	if err != nil {
		t.Error(fmt.Sprintf("HexMap.Marshal() - error: %s", err))
	}

	if strings.Compare(string(jhm), "{\"name\":\"TestMap\",\"size\":{\"hx\":22,\"hy\":14},\"origin\":{\"hx\":0,\"hy\":0},\"terrains\":null}") != 0 {
		t.Error(fmt.Sprintf("HexMap.Marshal(), - result: %s", jhm))
	}
}

// test containment and bias

func TestYbias(t *testing.T) {
	hm0 := HexMap{Name: "ybiastest", Size: HexVector{22,14}, Origin: ORIGIN}

	values := [][]int{{-3,-2}, {-2,-1}, {-1,-1}, {0,0}, {1,0}, {2,1}, {3,1}, {4,2}}

	for _, pair := range values {
		if hm0.ybias(pair[0]) != pair[1] {
			t.Error(fmt.Sprintf("HexMap.ybias() - %d -> expect: %d, actual: %d", pair[0], pair[1], hm0.ybias(pair[0])))

		}
	}
}

func TestContains(t *testing.T) {
	hm0 := HexMap{Name: "containmenttest", Size: HexVector{14,22}, Origin: ORIGIN}

	var values = []struct {
		hv HexVector
		contains bool
	}{
		{HexVector{0, 0}, true},
		{HexVector{-1,0}, false},
		{HexVector{13, 7}, true},
		{HexVector{14, 7}, false},
		{HexVector{0, 21}, true},
		{HexVector{0, 22}, false},
		{HexVector{13,5}, false},
		{HexVector{13,6}, true},
		{HexVector{13,21+6}, true},
		{HexVector{13,21+7}, false},
	}

	for _, testvalue := range values {
		if hm0.Contains(testvalue.hv) != testvalue.contains {
			t.Error(fmt.Sprintf("test contains is wrong: %s", testvalue.hv))
		}
	}

}
<|endoftext|>


<commit_before>package pgstorage

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"strings"

	"github.com/jacksontj/dataman/src/datamantype"
)

func DoQuery(ctx context.Context, db *sql.DB, query string, args ...interface{}) ([]map[string]interface{}, error) {
	rows, err := db.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("Error running query=%s Err=%v", query, err)
	}

	results := make([]map[string]interface{}, 0)

	// Get the list of column names
	cols, err := rows.Columns()
	if err != nil {
		return nil, err
	}
	// If there aren't any rows, we return a nil result
	for rows.Next() {
		columns := make([]interface{}, len(cols))
		columnPointers := make([]interface{}, len(cols))
		for i, _ := range columns {
			columnPointers[i] = &columns[i]
		}

		// Scan the result into the column pointers...
		if err := rows.Scan(columnPointers...); err != nil {
			return nil, err
		}

		// Create our map, and retrieve the value for each column from the pointers slice,
		// storing it in the map with the name of the column as the key.
		data := make(map[string]interface{})
		for i, colName := range cols {
			val := columnPointers[i].(*interface{})
			data[colName] = *val
		}
		results = append(results, data)
	}
	return results, nil
}

// Normalize field names. This takes a string such as "(data ->> 'created'::text)"
// and converts it to "data.created"
func normalizeFieldName(in string) string {
	if in[0] != '(' || in[len(in)-1] != ')' {
		return in
	}
	in = in[1 : len(in)-1]

	var output string

	for _, part := range strings.Split(in, " ") {
		if sepIdx := strings.Index(part, "'::"); sepIdx > -1 {
			part = part[1:sepIdx]
		}
		if part == "->>" {
			output += "."
		} else {
			output += part
		}
	}

	return output
}

func serializeValue(t datamantype.DatamanType, v interface{}) (string, error) {
	switch t {
	case datamantype.DateTime:
		return fmt.Sprintf("'%v'", v.(time.Time).Format(datamantype.DateTimeFormatStr)), nil
	default:
		return fmt.Sprintf("'%v'", v), nil
	}
}
<commit_msg>Change order in error message<commit_after>package pgstorage

import (
	"context"
	"database/sql"
	"fmt"
	"time"

	"strings"

	"github.com/jacksontj/dataman/src/datamantype"
)

func DoQuery(ctx context.Context, db *sql.DB, query string, args ...interface{}) ([]map[string]interface{}, error) {
	rows, err := db.QueryContext(ctx, query, args...)
	if err != nil {
		return nil, fmt.Errorf("Error running query: Err=%v query=%s ", err, query)
	}

	results := make([]map[string]interface{}, 0)

	// Get the list of column names
	cols, err := rows.Columns()
	if err != nil {
		return nil, err
	}
	// If there aren't any rows, we return a nil result
	for rows.Next() {
		columns := make([]interface{}, len(cols))
		columnPointers := make([]interface{}, len(cols))
		for i, _ := range columns {
			columnPointers[i] = &columns[i]
		}

		// Scan the result into the column pointers...
		if err := rows.Scan(columnPointers...); err != nil {
			return nil, err
		}

		// Create our map, and retrieve the value for each column from the pointers slice,
		// storing it in the map with the name of the column as the key.
		data := make(map[string]interface{})
		for i, colName := range cols {
			val := columnPointers[i].(*interface{})
			data[colName] = *val
		}
		results = append(results, data)
	}
	return results, nil
}

// Normalize field names. This takes a string such as "(data ->> 'created'::text)"
// and converts it to "data.created"
func normalizeFieldName(in string) string {
	if in[0] != '(' || in[len(in)-1] != ')' {
		return in
	}
	in = in[1 : len(in)-1]

	var output string

	for _, part := range strings.Split(in, " ") {
		if sepIdx := strings.Index(part, "'::"); sepIdx > -1 {
			part = part[1:sepIdx]
		}
		if part == "->>" {
			output += "."
		} else {
			output += part
		}
	}

	return output
}

func serializeValue(t datamantype.DatamanType, v interface{}) (string, error) {
	switch t {
	case datamantype.DateTime:
		return fmt.Sprintf("'%v'", v.(time.Time).Format(datamantype.DateTimeFormatStr)), nil
	default:
		return fmt.Sprintf("'%v'", v), nil
	}
}
<|endoftext|>


<commit_before>package fixer

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/flynn/flynn/controller/utils"
	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
	sirenia "github.com/flynn/flynn/pkg/sirenia/client"
	state "github.com/flynn/flynn/pkg/sirenia/state"
)

func (f *ClusterFixer) FixSirenia(svc string) error {
	log := f.l.New("fn", "FixSirenia", "service", svc)
	log.Info("checking sirenia cluster status")
	service := discoverd.NewService(svc)
	leader, _ := service.Leader()
	if leader == nil || leader.Addr == "" {
		log.Info("no running leader")
		leader = nil
	} else {
		log.Info("found running leader")
	}
	instances, _ := service.Instances()
	log.Info("found running instances", "count", len(instances))

	log.Info("getting sirenia status")
	var status *sirenia.Status
	if leader != nil && leader.Addr != "" {
		client := sirenia.NewClient(leader.Addr)
		var err error
		status, err = client.Status()
		if err != nil {
			log.Error("error getting status from leader", "error", err)
		}
	}
	if status != nil && status.Database != nil && status.Database.ReadWrite {
		log.Info("cluster claims to be read-write")
		return nil
	}

	log.Info("getting service metadata")
	meta, err := discoverd.NewService(svc).GetMeta()
	if err != nil {
		return fmt.Errorf("error getting sirenia state from discoverd: %s", err)
	}

	var state state.State
	if err := json.Unmarshal(meta.Data, &state); err != nil {
		return fmt.Errorf("error decoding state: %s", err)
	}
	if state.Primary == nil {
		return fmt.Errorf("no primary in sirenia state")
	}

	log.Info("getting primary job info", "job.id", state.Primary.Meta["FLYNN_JOB_ID"])
	primaryJob, primaryHost, err := f.GetJob(state.Primary.Meta["FLYNN_JOB_ID"])
	if err != nil {
		log.Error("unable to get primary job info")
	}
	var syncJob *host.Job
	var syncHost *cluster.Host
	if state.Sync != nil {
		log.Info("getting sync job info", "job.id", state.Sync.Meta["FLYNN_JOB_ID"])
		syncJob, syncHost, err = f.GetJob(state.Sync.Meta["FLYNN_JOB_ID"])
		if err != nil {
			log.Error("unable to get sync job info")
		}
	}

	waitForInstance := func(jobID string) (func() (string, error), error) {
		watchCh := make(chan *discoverd.Event)
		upCh := make(chan string)
		stream, err := service.Watch(watchCh)
		if err != nil {
			return nil, fmt.Errorf("error watching discoverd service: %s", err)
		}
		go func() {
			var current bool
			for event := range watchCh {
				if event.Kind == discoverd.EventKindCurrent {
					current = true
					continue
				}
				if !current || event.Kind != discoverd.EventKindUp {
					continue
				}
				if event.Instance.Meta["FLYNN_JOB_ID"] == jobID {
					upCh <- event.Instance.Addr
				}
			}
		}()
		return func() (string, error) {
			log.Info("waiting for instance to start", "job.id", jobID)
			defer stream.Close()
			select {
			case addr := <-upCh:
				return addr, nil
			case <-time.After(time.Minute):
				return "", fmt.Errorf("timed out waiting for sirenia instance to come up")
			}
		}, nil
	}

	log.Info("terminating unassigned sirenia instances")
outer:
	for _, i := range instances {
		if i.Addr == state.Primary.Addr || i.Addr == state.Sync.Addr {
			continue
		}
		for _, a := range state.Async {
			if i.Addr == a.Addr {
				continue outer
			}
		}
		// job not assigned in state, attempt to terminate it
		if jobID, ok := i.Meta["FLYNN_JOB_ID"]; ok {
			hostID, err := cluster.ExtractHostID(jobID)
			if err != nil {
				log.Error("error extracting host id from jobID", "jobID", jobID, "err", err)
			}
			h := f.Host(hostID)
			if h != nil {
				if err := h.StopJob(jobID); err != nil {
					log.Error("error stopping unassigned sirenia job", "jobID", jobID)
				}
			} else {
				log.Error("host not found", "hostID", hostID)
			}
		}
	}

	isRunning := func(addr string) bool {
		for _, i := range instances {
			if i.Addr == addr {
				return true
			}
		}
		return false
	}

	// if the leader isn't currently running then start it using primaryJob/primaryHost
	var wait func() (string, error)
	if !isRunning(state.Primary.Addr) {
		// if we don't have info about the primary job attempt to promote the sync
		if primaryJob == nil {
			if syncJob != nil {
				// set primary job to sync
				primaryJob = syncJob
				primaryHost = syncHost

				// nil out sync job now so we can re-allocate it.
				syncJob = nil
				syncHost = nil
			} else {
				return fmt.Errorf("neither primary or sync job info available")
			}
		}

		primaryJob.ID = cluster.GenerateJobID(primaryHost.ID(), "")
		f.FixJobEnv(primaryJob)
		log.Info("starting primary job", "job.id", primaryJob.ID)
		wait, err = waitForInstance(primaryJob.ID)
		if err != nil {
			return err
		}
		if err := primaryHost.AddJob(primaryJob); err != nil {
			return fmt.Errorf("error starting primary job on %s: %s", primaryHost.ID(), err)
		}
	}
	if !state.Singleton && !isRunning(state.Sync.Addr) {
		if syncHost == nil {
			for _, h := range f.hosts {
				if h.ID() != primaryHost.ID() {
					syncHost = h
					break
				}
			}
			if syncHost == nil {
				// if there are no other hosts, use the same one we put the primary on
				syncHost = primaryHost
			}
		}
		// if we don't have a sync job then copy the primary job
		// and provision a new volume
		if syncJob == nil {
			syncJob = primaryJob
			if err := utils.ProvisionVolume(syncHost, syncJob); err != nil {
				return fmt.Errorf("error creating volume on %s: %s", syncHost.ID(), err)
			}
		}
		syncJob.ID = cluster.GenerateJobID(syncHost.ID(), "")
		f.FixJobEnv(syncJob)
		log.Info("starting sync job", "job.id", syncJob.ID)
		if wait == nil {
			wait, err = waitForInstance(syncJob.ID)
			if err != nil {
				return err
			}
		}
		if err := syncHost.AddJob(syncJob); err != nil {
			return fmt.Errorf("error starting additional job on %s: %s", syncHost.ID(), err)
		}
	}

	if wait != nil {
		addr, err := wait()
		if err != nil {
			return err
		}
		if leader != nil {
			addr = leader.Addr
		}
		log.Info("waiting for cluster to come up read-write")
		return sirenia.NewClient(addr).WaitForReadWrite(5 * time.Minute)
	}
	return nil
}
<commit_msg>host/fixer: Check for nil Sync when terminating unassigned instances<commit_after>package fixer

import (
	"encoding/json"
	"fmt"
	"time"

	"github.com/flynn/flynn/controller/utils"
	"github.com/flynn/flynn/discoverd/client"
	"github.com/flynn/flynn/host/types"
	"github.com/flynn/flynn/pkg/cluster"
	sirenia "github.com/flynn/flynn/pkg/sirenia/client"
	state "github.com/flynn/flynn/pkg/sirenia/state"
)

func (f *ClusterFixer) FixSirenia(svc string) error {
	log := f.l.New("fn", "FixSirenia", "service", svc)
	log.Info("checking sirenia cluster status")
	service := discoverd.NewService(svc)
	leader, _ := service.Leader()
	if leader == nil || leader.Addr == "" {
		log.Info("no running leader")
		leader = nil
	} else {
		log.Info("found running leader")
	}
	instances, _ := service.Instances()
	log.Info("found running instances", "count", len(instances))

	log.Info("getting sirenia status")
	var status *sirenia.Status
	if leader != nil && leader.Addr != "" {
		client := sirenia.NewClient(leader.Addr)
		var err error
		status, err = client.Status()
		if err != nil {
			log.Error("error getting status from leader", "error", err)
		}
	}
	if status != nil && status.Database != nil && status.Database.ReadWrite {
		log.Info("cluster claims to be read-write")
		return nil
	}

	log.Info("getting service metadata")
	meta, err := discoverd.NewService(svc).GetMeta()
	if err != nil {
		return fmt.Errorf("error getting sirenia state from discoverd: %s", err)
	}

	var state state.State
	if err := json.Unmarshal(meta.Data, &state); err != nil {
		return fmt.Errorf("error decoding state: %s", err)
	}
	if state.Primary == nil {
		return fmt.Errorf("no primary in sirenia state")
	}

	log.Info("getting primary job info", "job.id", state.Primary.Meta["FLYNN_JOB_ID"])
	primaryJob, primaryHost, err := f.GetJob(state.Primary.Meta["FLYNN_JOB_ID"])
	if err != nil {
		log.Error("unable to get primary job info")
	}
	var syncJob *host.Job
	var syncHost *cluster.Host
	if state.Sync != nil {
		log.Info("getting sync job info", "job.id", state.Sync.Meta["FLYNN_JOB_ID"])
		syncJob, syncHost, err = f.GetJob(state.Sync.Meta["FLYNN_JOB_ID"])
		if err != nil {
			log.Error("unable to get sync job info")
		}
	}

	waitForInstance := func(jobID string) (func() (string, error), error) {
		watchCh := make(chan *discoverd.Event)
		upCh := make(chan string)
		stream, err := service.Watch(watchCh)
		if err != nil {
			return nil, fmt.Errorf("error watching discoverd service: %s", err)
		}
		go func() {
			var current bool
			for event := range watchCh {
				if event.Kind == discoverd.EventKindCurrent {
					current = true
					continue
				}
				if !current || event.Kind != discoverd.EventKindUp {
					continue
				}
				if event.Instance.Meta["FLYNN_JOB_ID"] == jobID {
					upCh <- event.Instance.Addr
				}
			}
		}()
		return func() (string, error) {
			log.Info("waiting for instance to start", "job.id", jobID)
			defer stream.Close()
			select {
			case addr := <-upCh:
				return addr, nil
			case <-time.After(time.Minute):
				return "", fmt.Errorf("timed out waiting for sirenia instance to come up")
			}
		}, nil
	}

	log.Info("terminating unassigned sirenia instances")
outer:
	for _, i := range instances {
		if i.Addr == state.Primary.Addr || (state.Sync != nil && i.Addr == state.Sync.Addr) {
			continue
		}
		for _, a := range state.Async {
			if i.Addr == a.Addr {
				continue outer
			}
		}
		// job not assigned in state, attempt to terminate it
		if jobID, ok := i.Meta["FLYNN_JOB_ID"]; ok {
			hostID, err := cluster.ExtractHostID(jobID)
			if err != nil {
				log.Error("error extracting host id from jobID", "jobID", jobID, "err", err)
			}
			h := f.Host(hostID)
			if h != nil {
				if err := h.StopJob(jobID); err != nil {
					log.Error("error stopping unassigned sirenia job", "jobID", jobID)
				}
			} else {
				log.Error("host not found", "hostID", hostID)
			}
		}
	}

	isRunning := func(addr string) bool {
		for _, i := range instances {
			if i.Addr == addr {
				return true
			}
		}
		return false
	}

	// if the leader isn't currently running then start it using primaryJob/primaryHost
	var wait func() (string, error)
	if !isRunning(state.Primary.Addr) {
		// if we don't have info about the primary job attempt to promote the sync
		if primaryJob == nil {
			if syncJob != nil {
				// set primary job to sync
				primaryJob = syncJob
				primaryHost = syncHost

				// nil out sync job now so we can re-allocate it.
				syncJob = nil
				syncHost = nil
			} else {
				return fmt.Errorf("neither primary or sync job info available")
			}
		}

		primaryJob.ID = cluster.GenerateJobID(primaryHost.ID(), "")
		f.FixJobEnv(primaryJob)
		log.Info("starting primary job", "job.id", primaryJob.ID)
		wait, err = waitForInstance(primaryJob.ID)
		if err != nil {
			return err
		}
		if err := primaryHost.AddJob(primaryJob); err != nil {
			return fmt.Errorf("error starting primary job on %s: %s", primaryHost.ID(), err)
		}
	}
	if !state.Singleton && !isRunning(state.Sync.Addr) {
		if syncHost == nil {
			for _, h := range f.hosts {
				if h.ID() != primaryHost.ID() {
					syncHost = h
					break
				}
			}
			if syncHost == nil {
				// if there are no other hosts, use the same one we put the primary on
				syncHost = primaryHost
			}
		}
		// if we don't have a sync job then copy the primary job
		// and provision a new volume
		if syncJob == nil {
			syncJob = primaryJob
			if err := utils.ProvisionVolume(syncHost, syncJob); err != nil {
				return fmt.Errorf("error creating volume on %s: %s", syncHost.ID(), err)
			}
		}
		syncJob.ID = cluster.GenerateJobID(syncHost.ID(), "")
		f.FixJobEnv(syncJob)
		log.Info("starting sync job", "job.id", syncJob.ID)
		if wait == nil {
			wait, err = waitForInstance(syncJob.ID)
			if err != nil {
				return err
			}
		}
		if err := syncHost.AddJob(syncJob); err != nil {
			return fmt.Errorf("error starting additional job on %s: %s", syncHost.ID(), err)
		}
	}

	if wait != nil {
		addr, err := wait()
		if err != nil {
			return err
		}
		if leader != nil {
			addr = leader.Addr
		}
		log.Info("waiting for cluster to come up read-write")
		return sirenia.NewClient(addr).WaitForReadWrite(5 * time.Minute)
	}
	return nil
}
<|endoftext|>


<commit_before>package sudoku

import (
	"log"
	"testing"
)

func TestSubsetIndexes(t *testing.T) {
	result := subsetIndexes(3, 1)
	expectedResult := [][]int{[]int{0}, []int{1}, []int{2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(3, 2)
	expectedResult = [][]int{[]int{0, 1}, []int{0, 2}, []int{1, 2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(5, 3)
	expectedResult = [][]int{[]int{0, 1, 2}, []int{0, 1, 3}, []int{0, 1, 4}, []int{0, 2, 3}, []int{0, 2, 4}, []int{0, 3, 4}, []int{1, 2, 3}, []int{1, 2, 4}, []int{1, 3, 4}, []int{2, 3, 4}}
	subsetIndexHelper(t, result, expectedResult)

	if subsetIndexes(1, 2) != nil {
		t.Log("Subset indexes returned a subset where the length is greater than the len")
		t.Fail()
	}

}

func subsetIndexHelper(t *testing.T, result [][]int, expectedResult [][]int) {
	if len(result) != len(expectedResult) {
		t.Log("subset indexes returned wrong number of results for: ", result, " :", expectedResult)
		t.FailNow()
	}
	for i, item := range result {
		if len(item) != len(expectedResult[0]) {
			t.Log("subset indexes returned a result with wrong numbrer of items ", i, " : ", result, " : ", expectedResult)
			t.FailNow()
		}
		for j, value := range item {
			if value != expectedResult[i][j] {
				t.Log("Subset indexes had wrong number at ", i, ",", j, " : ", result, " : ", expectedResult)
				t.Fail()
			}
		}
	}
}

type solveTechniqueMatchMode int

const (
	solveTechniqueMatchModeAll = iota
	solveTechniqueMatchModeAny
)

type solveTechniqueTestHelperOptions struct {
	transpose bool
	//Whether the descriptions of cells are a list of legal possible individual values, or must all match.
	matchMode    solveTechniqueMatchMode
	targetCells  []cellRef
	pointerCells []cellRef
	targetNums   IntSlice
	pointerNums  IntSlice
	targetSame   cellGroupType
	targetGroup  int
	description  string
	descriptions []string
	debugPrint   bool
}

func humanSolveTechniqueTestHelper(t *testing.T, puzzleName string, techniqueName string, options solveTechniqueTestHelperOptions) {
	//TODO: test for col and block as well
	grid := NewGrid()
	grid.LoadFromFile(puzzlePath(puzzleName))

	if options.transpose {
		grid = grid.transpose()
	}

	solver := techniquesByName[techniqueName]

	if solver == nil {
		t.Fatal("Couldn't find technique object: ", techniqueName)
	}

	steps := solver.Find(grid)

	if len(steps) == 0 {
		t.Fatal(techniqueName, " didn't find a cell it should have.")
	}

	step := steps[0]

	if options.debugPrint {
		log.Println(step)
	}

	if options.matchMode == solveTechniqueMatchModeAll {

		//All must match

		if options.targetCells != nil {
			if !step.TargetCells.sameAsRefs(options.targetCells) {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			if !step.PointerCells.sameAsRefs(options.pointerCells) {
				t.Error(techniqueName, " had the wrong pointer cells: ", step.PointerCells)
			}
		}

		switch options.targetSame {
		case GROUP_ROW:
			if !step.TargetCells.SameRow() || step.TargetCells.Row() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong row :", step.TargetCells.Row())
			}
		case GROUP_BLOCK:
			if !step.TargetCells.SameBlock() || step.TargetCells.Block() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong block :", step.TargetCells.Block())
			}
		case GROUP_COL:
			if !step.TargetCells.SameCol() || step.TargetCells.Col() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong col :", step.TargetCells.Col())
			}
		case GROUP_NONE:
			//Do nothing
		default:
			t.Error("human solve technique helper error: unsupported group type: ", options.targetSame)
		}

		if options.targetNums != nil {
			if !step.TargetNums.SameContentAs(options.targetNums) {
				t.Error(techniqueName, " found the wrong numbers: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			if !step.PointerNums.SameContentAs(options.pointerNums) {
				t.Error(techniqueName, "found the wrong numbers:", step.PointerNums)
			}
		}
	} else if options.matchMode == solveTechniqueMatchModeAny {

		foundMatch := false

		if options.targetCells != nil {
			foundMatch = false
			for _, ref := range options.targetCells {
				for _, cell := range step.TargetCells {
					if ref.Cell(grid) == cell {
						//TODO: break out early
						foundMatch = true
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			t.Error("Pointer cells in match mode any not yet supported.")
		}

		if options.targetSame != GROUP_NONE {
			t.Error("Target Same in match mode any not yet supported.")
		}

		if options.targetNums != nil {
			foundMatch = false
			for _, targetNum := range options.targetNums {
				for _, num := range step.TargetNums {
					if targetNum == num {
						foundMatch = true
						//TODO: break early here.
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target nums: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			t.Error("Pointer nums in match mode any not yet supported.")
		}
	}

	if options.description != "" {
		//Normalize the step so that the description will be stable for the test.
		step.normalize()
		description := solver.Description(step)
		if description != options.description {
			t.Error("Wrong description for ", techniqueName, ". Got:*", description, "* expected: *", options.description, "*")
		}
	} else if options.descriptions != nil {
		foundMatch := false
		step.normalize()
		description := solver.Description(step)
		for _, targetDescription := range options.descriptions {
			if description == targetDescription {
				foundMatch = true
			}
		}
		if !foundMatch {
			t.Error("No descriptions matched for ", techniqueName, ". Got:*", description)
		}
	}

	//TODO: we should do exhaustive testing of SolveStep application. We used to test it here, but as long as targetCells and targetNums are correct it should be fine.

	grid.Done()
}
<commit_msg>Documented options.descriptions<commit_after>package sudoku

import (
	"log"
	"testing"
)

func TestSubsetIndexes(t *testing.T) {
	result := subsetIndexes(3, 1)
	expectedResult := [][]int{[]int{0}, []int{1}, []int{2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(3, 2)
	expectedResult = [][]int{[]int{0, 1}, []int{0, 2}, []int{1, 2}}
	subsetIndexHelper(t, result, expectedResult)

	result = subsetIndexes(5, 3)
	expectedResult = [][]int{[]int{0, 1, 2}, []int{0, 1, 3}, []int{0, 1, 4}, []int{0, 2, 3}, []int{0, 2, 4}, []int{0, 3, 4}, []int{1, 2, 3}, []int{1, 2, 4}, []int{1, 3, 4}, []int{2, 3, 4}}
	subsetIndexHelper(t, result, expectedResult)

	if subsetIndexes(1, 2) != nil {
		t.Log("Subset indexes returned a subset where the length is greater than the len")
		t.Fail()
	}

}

func subsetIndexHelper(t *testing.T, result [][]int, expectedResult [][]int) {
	if len(result) != len(expectedResult) {
		t.Log("subset indexes returned wrong number of results for: ", result, " :", expectedResult)
		t.FailNow()
	}
	for i, item := range result {
		if len(item) != len(expectedResult[0]) {
			t.Log("subset indexes returned a result with wrong numbrer of items ", i, " : ", result, " : ", expectedResult)
			t.FailNow()
		}
		for j, value := range item {
			if value != expectedResult[i][j] {
				t.Log("Subset indexes had wrong number at ", i, ",", j, " : ", result, " : ", expectedResult)
				t.Fail()
			}
		}
	}
}

type solveTechniqueMatchMode int

const (
	solveTechniqueMatchModeAll = iota
	solveTechniqueMatchModeAny
)

type solveTechniqueTestHelperOptions struct {
	transpose bool
	//Whether the descriptions of cells are a list of legal possible individual values, or must all match.
	matchMode    solveTechniqueMatchMode
	targetCells  []cellRef
	pointerCells []cellRef
	targetNums   IntSlice
	pointerNums  IntSlice
	targetSame   cellGroupType
	targetGroup  int
	//If description provided, the description MUST match.
	description string
	//If descriptions provided, ONE of the descriptions must match.
	//generally used in conjunction with solveTechniqueMatchModeAny.
	descriptions []string
	debugPrint   bool
}

func humanSolveTechniqueTestHelper(t *testing.T, puzzleName string, techniqueName string, options solveTechniqueTestHelperOptions) {
	//TODO: test for col and block as well
	grid := NewGrid()
	grid.LoadFromFile(puzzlePath(puzzleName))

	if options.transpose {
		grid = grid.transpose()
	}

	solver := techniquesByName[techniqueName]

	if solver == nil {
		t.Fatal("Couldn't find technique object: ", techniqueName)
	}

	steps := solver.Find(grid)

	if len(steps) == 0 {
		t.Fatal(techniqueName, " didn't find a cell it should have.")
	}

	step := steps[0]

	if options.debugPrint {
		log.Println(step)
	}

	if options.matchMode == solveTechniqueMatchModeAll {

		//All must match

		if options.targetCells != nil {
			if !step.TargetCells.sameAsRefs(options.targetCells) {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			if !step.PointerCells.sameAsRefs(options.pointerCells) {
				t.Error(techniqueName, " had the wrong pointer cells: ", step.PointerCells)
			}
		}

		switch options.targetSame {
		case GROUP_ROW:
			if !step.TargetCells.SameRow() || step.TargetCells.Row() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong row :", step.TargetCells.Row())
			}
		case GROUP_BLOCK:
			if !step.TargetCells.SameBlock() || step.TargetCells.Block() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong block :", step.TargetCells.Block())
			}
		case GROUP_COL:
			if !step.TargetCells.SameCol() || step.TargetCells.Col() != options.targetGroup {
				t.Error("The target cells in the ", techniqueName, " were wrong col :", step.TargetCells.Col())
			}
		case GROUP_NONE:
			//Do nothing
		default:
			t.Error("human solve technique helper error: unsupported group type: ", options.targetSame)
		}

		if options.targetNums != nil {
			if !step.TargetNums.SameContentAs(options.targetNums) {
				t.Error(techniqueName, " found the wrong numbers: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			if !step.PointerNums.SameContentAs(options.pointerNums) {
				t.Error(techniqueName, "found the wrong numbers:", step.PointerNums)
			}
		}
	} else if options.matchMode == solveTechniqueMatchModeAny {

		foundMatch := false

		if options.targetCells != nil {
			foundMatch = false
			for _, ref := range options.targetCells {
				for _, cell := range step.TargetCells {
					if ref.Cell(grid) == cell {
						//TODO: break out early
						foundMatch = true
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target cells: ", step.TargetCells)
			}
		}
		if options.pointerCells != nil {
			t.Error("Pointer cells in match mode any not yet supported.")
		}

		if options.targetSame != GROUP_NONE {
			t.Error("Target Same in match mode any not yet supported.")
		}

		if options.targetNums != nil {
			foundMatch = false
			for _, targetNum := range options.targetNums {
				for _, num := range step.TargetNums {
					if targetNum == num {
						foundMatch = true
						//TODO: break early here.
					}
				}
			}
			if !foundMatch {
				t.Error(techniqueName, " had the wrong target nums: ", step.TargetNums)
			}
		}

		if options.pointerNums != nil {
			t.Error("Pointer nums in match mode any not yet supported.")
		}
	}

	if options.description != "" {
		//Normalize the step so that the description will be stable for the test.
		step.normalize()
		description := solver.Description(step)
		if description != options.description {
			t.Error("Wrong description for ", techniqueName, ". Got:*", description, "* expected: *", options.description, "*")
		}
	} else if options.descriptions != nil {
		foundMatch := false
		step.normalize()
		description := solver.Description(step)
		for _, targetDescription := range options.descriptions {
			if description == targetDescription {
				foundMatch = true
			}
		}
		if !foundMatch {
			t.Error("No descriptions matched for ", techniqueName, ". Got:*", description)
		}
	}

	//TODO: we should do exhaustive testing of SolveStep application. We used to test it here, but as long as targetCells and targetNums are correct it should be fine.

	grid.Done()
}
<|endoftext|>


<commit_before>package sudoku

import (
	"testing"
)

//TODO: test a few more puzzles to make sure I'm exercising it correctly.

func TestSwordfishCol(t *testing.T) {

	techniqueVariantsTestHelper(t, "Swordfish")

	grid := NewGrid()

	puzzleName := "swordfish_example.sdk"

	if !grid.LoadFromFile(puzzlePath(puzzleName)) {
		t.Fatal("Couldn't load puzzle ", puzzleName)
	}

	//Set up the grid correctly for the Swordfish technique to work. The
	//example we use is a grid that has other work done to exclude
	//possibilities from certain cells.

	//TODO: it's a smell that there's no way to serialize and load up a grid
	//with extra excludes set.
	excludedConfig := map[cellRef]IntSlice{
		cellRef{0, 0}: IntSlice{1, 8},
		cellRef{1, 3}: IntSlice{1},
		cellRef{1, 4}: IntSlice{1, 8},
		cellRef{2, 3}: IntSlice{1},
		cellRef{2, 5}: IntSlice{1, 8},
		cellRef{3, 0}: IntSlice{2, 8},
		cellRef{4, 0}: IntSlice{7},
		cellRef{4, 1}: IntSlice{7},
		cellRef{7, 3}: IntSlice{1, 6},
		cellRef{7, 5}: IntSlice{1},
	}

	for ref, ints := range excludedConfig {
		cell := ref.Cell(grid)
		for _, exclude := range ints {
			cell.SetExcluded(exclude, true)
		}
	}

	options := solveTechniqueTestHelperOptions{
		targetCells:  []cellRef{{1, 1}, {5, 4}},
		pointerCells: []cellRef{{1, 0}, {1, 5}, {5, 3}, {5, 5}, {8, 0}, {8, 3}},
		targetNums:   IntSlice{1},
		//TODO: test description
	}
	options.stepsToCheck.grid = grid

	humanSolveTechniqueTestHelper(t, "NOOP", "Swordfish", options)

}

//TODO: TestSwordfishRow (and implement Row!)
<commit_msg>TESTS FAIL. Fixed the test so it actually runs swordfish. Now it's returning the wrong steps (which is a step in the right direction!)<commit_after>package sudoku

import (
	"testing"
)

//TODO: test a few more puzzles to make sure I'm exercising it correctly.

func TestSwordfishCol(t *testing.T) {

	techniqueVariantsTestHelper(t, "Swordfish")

	grid := NewGrid()

	puzzleName := "swordfish_example.sdk"

	if !grid.LoadFromFile(puzzlePath(puzzleName)) {
		t.Fatal("Couldn't load puzzle ", puzzleName)
	}

	//Set up the grid correctly for the Swordfish technique to work. The
	//example we use is a grid that has other work done to exclude
	//possibilities from certain cells.

	//TODO: it's a smell that there's no way to serialize and load up a grid
	//with extra excludes set.
	excludedConfig := map[cellRef]IntSlice{
		cellRef{0, 0}: IntSlice{1, 8},
		cellRef{1, 3}: IntSlice{1},
		cellRef{1, 4}: IntSlice{1, 8},
		cellRef{2, 3}: IntSlice{1},
		cellRef{2, 5}: IntSlice{1, 8},
		cellRef{3, 0}: IntSlice{2, 8},
		cellRef{4, 0}: IntSlice{7},
		cellRef{4, 1}: IntSlice{7},
		cellRef{7, 3}: IntSlice{1, 6},
		cellRef{7, 5}: IntSlice{1},
	}

	for ref, ints := range excludedConfig {
		cell := ref.Cell(grid)
		for _, exclude := range ints {
			cell.SetExcluded(exclude, true)
		}
	}

	options := solveTechniqueTestHelperOptions{
		targetCells:  []cellRef{{1, 1}, {5, 4}},
		pointerCells: []cellRef{{1, 0}, {1, 5}, {5, 3}, {5, 5}, {8, 0}, {8, 3}},
		targetNums:   IntSlice{1},
		//TODO: test description
	}
	options.stepsToCheck.grid = grid

	//TODO: it's not possible to just pass in an override grid to humanSolveTechniqueTestHelper as
	//is, because we're overloading passing it to stepsToCheck. That's a smell.
	grid, solver, steps := humanSolveTechniqueTestHelperStepGenerator(t, "NOOP", "Swordfish", options)

	options.stepsToCheck.grid = grid
	options.stepsToCheck.solver = solver
	options.stepsToCheck.steps = steps

	humanSolveTechniqueTestHelper(t, "NOOP", "Swordfish", options)

}

//TODO: TestSwordfishRow (and implement Row!)
<|endoftext|>


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

package orm

import (
	"context"
	"reflect"
	"strings"

	"github.com/astaxie/beego/orm"
	"github.com/goharbor/harbor/src/common/dao"
	"github.com/goharbor/harbor/src/lib/q"
)

// QuerySetter generates the query setter according to the query. "ignoredCols" is used to set the
// columns that will not be queried
func QuerySetter(ctx context.Context, model interface{}, query *q.Query, ignoredCols ...string) (orm.QuerySeter, error) {
	ormer, err := FromContext(ctx)
	if err != nil {
		return nil, err
	}
	qs := ormer.QueryTable(model)
	if query == nil {
		return qs, nil
	}

	// the program will panic when querying the columns that doesn't exist
	// list the supported columns first to avoid the panic
	cols := listQueriableCols(model, ignoredCols...)
	for k, v := range query.Keywords {
		col := strings.SplitN(k, orm.ExprSep, 2)[0]
		if _, exist := cols[col]; !exist {
			continue
		}

		// fuzzy match
		f, ok := v.(*q.FuzzyMatchValue)
		if ok {
			qs = qs.Filter(k+"__icontains", f.Value)
			continue
		}

		// range
		r, ok := v.(*q.Range)
		if ok {
			if r.Min != nil {
				qs = qs.Filter(k+"__gte", r.Min)
			}
			if r.Max != nil {
				qs = qs.Filter(k+"__lte", r.Max)
			}
			continue
		}

		// or list
		ol, ok := v.(*q.OrList)
		if ok {
			qs = qs.Filter(k+"__in", ol.Values...)
			continue
		}

		// and list
		_, ok = v.(*q.AndList)
		if ok {
			// do nothing as and list needs to be handled by the logic of DAO
			continue
		}

		// exact match
		qs = qs.Filter(k, v)
	}
	if query.PageSize > 0 {
		qs = qs.Limit(query.PageSize)
		if query.PageNumber > 0 {
			qs = qs.Offset(query.PageSize * (query.PageNumber - 1))
		}
	}
	return qs, nil
}

// list the columns that can be queried
// e.g. for the following model the columns that can be queried are:
// "Field2", "customized_field2", "Field3" and "field3"
// type model struct{
//   Field1 string `orm:"-"`
//   Field2 string `orm:"column(customized_field2)"`
//   Field3 string
// }
//
// set "ignoredCols" to ignore the specified columns
func listQueriableCols(model interface{}, ignoredCols ...string) map[string]struct{} {
	if model == nil {
		return nil
	}
	ignored := map[string]struct{}{}
	for _, ig := range ignoredCols {
		ignored[ig] = struct{}{}
	}
	cols := map[string]struct{}{}
	t := reflect.Indirect(reflect.ValueOf(model)).Type()
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		orm := field.Tag.Get("orm")
		if orm == "-" {
			continue
		}
		colName := ""
		for _, str := range strings.Split(orm, ";") {
			if strings.HasPrefix(str, "column") {
				str = strings.TrimPrefix(str, "column(")
				str = strings.TrimSuffix(str, ")")
				if len(str) > 0 {
					colName = str
					break
				}
			}
		}
		if len(colName) == 0 {
			// TODO convert the field.Name to snake_case
		}
		if _, exist := ignored[colName]; exist {
			continue
		}
		if _, exist := ignored[field.Name]; exist {
			continue
		}
		if len(colName) != 0 {
			cols[colName] = struct{}{}
		}
		cols[field.Name] = struct{}{}
	}
	return cols
}

// ParamPlaceholderForIn returns a string that contains placeholders for sql keyword "in"
// e.g. n=3, returns "?,?,?"
func ParamPlaceholderForIn(n int) string {
	placeholders := []string{}
	for i := 0; i < n; i++ {
		placeholders = append(placeholders, "?")
	}
	return strings.Join(placeholders, ",")
}

// Escape special characters
func Escape(str string) string {
	return dao.Escape(str)
}
<commit_msg>Handle empty orlist in orm query (#11270)<commit_after>// Copyright Project Harbor Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//    http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package orm

import (
	"context"
	"reflect"
	"strings"

	"github.com/astaxie/beego/orm"
	"github.com/goharbor/harbor/src/common/dao"
	"github.com/goharbor/harbor/src/lib/q"
)

// QuerySetter generates the query setter according to the query. "ignoredCols" is used to set the
// columns that will not be queried
func QuerySetter(ctx context.Context, model interface{}, query *q.Query, ignoredCols ...string) (orm.QuerySeter, error) {
	ormer, err := FromContext(ctx)
	if err != nil {
		return nil, err
	}
	qs := ormer.QueryTable(model)
	if query == nil {
		return qs, nil
	}

	// the program will panic when querying the columns that doesn't exist
	// list the supported columns first to avoid the panic
	cols := listQueriableCols(model, ignoredCols...)
	for k, v := range query.Keywords {
		col := strings.SplitN(k, orm.ExprSep, 2)[0]
		if _, exist := cols[col]; !exist {
			continue
		}

		// fuzzy match
		f, ok := v.(*q.FuzzyMatchValue)
		if ok {
			qs = qs.Filter(k+"__icontains", f.Value)
			continue
		}

		// range
		r, ok := v.(*q.Range)
		if ok {
			if r.Min != nil {
				qs = qs.Filter(k+"__gte", r.Min)
			}
			if r.Max != nil {
				qs = qs.Filter(k+"__lte", r.Max)
			}
			continue
		}

		// or list
		ol, ok := v.(*q.OrList)
		if ok && len(ol.Values) > 0 {
			qs = qs.Filter(k+"__in", ol.Values...)
			continue
		}

		// and list
		_, ok = v.(*q.AndList)
		if ok {
			// do nothing as and list needs to be handled by the logic of DAO
			continue
		}

		// exact match
		qs = qs.Filter(k, v)
	}
	if query.PageSize > 0 {
		qs = qs.Limit(query.PageSize)
		if query.PageNumber > 0 {
			qs = qs.Offset(query.PageSize * (query.PageNumber - 1))
		}
	}
	return qs, nil
}

// list the columns that can be queried
// e.g. for the following model the columns that can be queried are:
// "Field2", "customized_field2", "Field3" and "field3"
// type model struct{
//   Field1 string `orm:"-"`
//   Field2 string `orm:"column(customized_field2)"`
//   Field3 string
// }
//
// set "ignoredCols" to ignore the specified columns
func listQueriableCols(model interface{}, ignoredCols ...string) map[string]struct{} {
	if model == nil {
		return nil
	}
	ignored := map[string]struct{}{}
	for _, ig := range ignoredCols {
		ignored[ig] = struct{}{}
	}
	cols := map[string]struct{}{}
	t := reflect.Indirect(reflect.ValueOf(model)).Type()
	for i := 0; i < t.NumField(); i++ {
		field := t.Field(i)
		orm := field.Tag.Get("orm")
		if orm == "-" {
			continue
		}
		colName := ""
		for _, str := range strings.Split(orm, ";") {
			if strings.HasPrefix(str, "column") {
				str = strings.TrimPrefix(str, "column(")
				str = strings.TrimSuffix(str, ")")
				if len(str) > 0 {
					colName = str
					break
				}
			}
		}
		if len(colName) == 0 {
			// TODO convert the field.Name to snake_case
		}
		if _, exist := ignored[colName]; exist {
			continue
		}
		if _, exist := ignored[field.Name]; exist {
			continue
		}
		if len(colName) != 0 {
			cols[colName] = struct{}{}
		}
		cols[field.Name] = struct{}{}
	}
	return cols
}

// ParamPlaceholderForIn returns a string that contains placeholders for sql keyword "in"
// e.g. n=3, returns "?,?,?"
func ParamPlaceholderForIn(n int) string {
	placeholders := []string{}
	for i := 0; i < n; i++ {
		placeholders = append(placeholders, "?")
	}
	return strings.Join(placeholders, ",")
}

// Escape special characters
func Escape(str string) string {
	return dao.Escape(str)
}
<|endoftext|>


<commit_before>package registry

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"sync"
	"time"

	consul "github.com/hashicorp/consul/api"
	hash "github.com/mitchellh/hashstructure"
)

type consulRegistry struct {
	Address string
	Client  *consul.Client
	Options Options

	sync.Mutex
	register map[string]uint64
}

func newTransport(config *tls.Config) *http.Transport {
	if config == nil {
		config = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     config,
	}
	runtime.SetFinalizer(&t, func(tr **http.Transport) {
		(*tr).CloseIdleConnections()
	})
	return t
}

func newConsulRegistry(opts ...Option) Registry {
	var options Options
	for _, o := range opts {
		o(&options)
	}

	// use default config
	config := consul.DefaultConfig()
	if options.Context != nil {
		// Use the consul config passed in the options, if available
		if c, ok := options.Context.Value("consul_config").(*consul.Config); ok {
			config = c
		}
	}

	// set timeout
	if options.Timeout > 0 {
		config.HttpClient.Timeout = options.Timeout
	}

	// check if there are any addrs
	if len(options.Addrs) > 0 {
		addr, port, err := net.SplitHostPort(options.Addrs[0])
		if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
			port = "8500"
			addr = options.Addrs[0]
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		} else if err == nil {
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		}
	}

	// requires secure connection?
	if options.Secure || options.TLSConfig != nil {
		config.Scheme = "https"
		// We're going to support InsecureSkipVerify
		config.HttpClient.Transport = newTransport(options.TLSConfig)
	}

	// create the client
	client, _ := consul.NewClient(config)

	cr := &consulRegistry{
		Address:  config.Address,
		Client:   client,
		Options:  options,
		register: make(map[string]uint64),
	}

	return cr
}

func (c *consulRegistry) Deregister(s *Service) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	// delete our hash of the service
	c.Lock()
	delete(c.register, s.Name)
	c.Unlock()

	node := s.Nodes[0]
	return c.Client.Agent().ServiceDeregister(node.Id)
}

func (c *consulRegistry) Register(s *Service, opts ...RegisterOption) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	var options RegisterOptions
	for _, o := range opts {
		o(&options)
	}

	// create hash of service; uint64
	h, err := hash.Hash(s, nil)
	if err != nil {
		return err
	}

	// use first node
	node := s.Nodes[0]

	// get existing hash
	c.Lock()
	v, ok := c.register[s.Name]
	c.Unlock()

	// if it's already registered and matches then just pass the check
	if ok && v == h {
		// if the err is nil we're all good, bail out
		// if not, we don't know what the state is, so full re-register
		if err := c.Client.Agent().PassTTL("service:"+node.Id, ""); err == nil {
			return nil
		}
	}

	// encode the tags
	tags := encodeMetadata(node.Metadata)
	tags = append(tags, encodeEndpoints(s.Endpoints)...)
	tags = append(tags, encodeVersion(s.Version)...)

	var check *consul.AgentServiceCheck

	// if the TTL is greater than 0 create an associated check
	if options.TTL > time.Duration(0) {
		// splay slightly for the watcher?
		splay := time.Second * 5
		deregTTL := options.TTL + splay
		// consul has a minimum timeout on deregistration of 1 minute.
		if options.TTL < time.Minute {
			deregTTL = time.Minute + splay
		}

		check = &consul.AgentServiceCheck{
			TTL: fmt.Sprintf("%v", options.TTL),
			DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
		}
	}

	// register the service
	if err := c.Client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		ID:      node.Id,
		Name:    s.Name,
		Tags:    tags,
		Port:    node.Port,
		Address: node.Address,
		Check:   check,
	}); err != nil {
		return err
	}

	// save our hash of the service
	c.Lock()
	c.register[s.Name] = h
	c.Unlock()

	// if the TTL is 0 we don't mess with the checks
	if options.TTL == time.Duration(0) {
		return nil
	}

	// pass the healthcheck
	return c.Client.Agent().PassTTL("service:"+node.Id, "")
}

func (c *consulRegistry) GetService(name string) ([]*Service, error) {
	rsp, _, err := c.Client.Health().Service(name, "", false, nil)
	if err != nil {
		return nil, err
	}

	serviceMap := map[string]*Service{}

	for _, s := range rsp {
		if s.Service.Service != name {
			continue
		}

		// version is now a tag
		version, found := decodeVersion(s.Service.Tags)
		// service ID is now the node id
		id := s.Service.ID
		// key is always the version
		key := version
		// address is service address
		address := s.Service.Address

		// if we can't get the version we bail
		// use old the old ways
		if !found {
			continue
		}

		svc, ok := serviceMap[key]
		if !ok {
			svc = &Service{
				Endpoints: decodeEndpoints(s.Service.Tags),
				Name:      s.Service.Service,
				Version:   version,
			}
			serviceMap[key] = svc
		}

		var del bool
		for _, check := range s.Checks {
			// delete the node if the status is critical
			if check.Status == "critical" {
				del = true
				break
			}
		}

		// if delete then skip the node
		if del {
			continue
		}

		svc.Nodes = append(svc.Nodes, &Node{
			Id:       id,
			Address:  address,
			Port:     s.Service.Port,
			Metadata: decodeMetadata(s.Service.Tags),
		})
	}

	var services []*Service
	for _, service := range serviceMap {
		services = append(services, service)
	}
	return services, nil
}

func (c *consulRegistry) ListServices() ([]*Service, error) {
	rsp, _, err := c.Client.Catalog().Services(nil)
	if err != nil {
		return nil, err
	}

	var services []*Service

	for service := range rsp {
		services = append(services, &Service{Name: service})
	}

	return services, nil
}

func (c *consulRegistry) Watch() (Watcher, error) {
	return newConsulWatcher(c)
}

func (c *consulRegistry) String() string {
	return "consul"
}
<commit_msg>Fixes nil pointer dereferencing for Consul TLS transport<commit_after>package registry

import (
	"crypto/tls"
	"errors"
	"fmt"
	"net"
	"net/http"
	"runtime"
	"sync"
	"time"

	consul "github.com/hashicorp/consul/api"
	hash "github.com/mitchellh/hashstructure"
)

type consulRegistry struct {
	Address string
	Client  *consul.Client
	Options Options

	sync.Mutex
	register map[string]uint64
}

func newTransport(config *tls.Config) *http.Transport {
	if config == nil {
		config = &tls.Config{
			InsecureSkipVerify: true,
		}
	}

	t := &http.Transport{
		Proxy: http.ProxyFromEnvironment,
		Dial: (&net.Dialer{
			Timeout:   30 * time.Second,
			KeepAlive: 30 * time.Second,
		}).Dial,
		TLSHandshakeTimeout: 10 * time.Second,
		TLSClientConfig:     config,
	}
	runtime.SetFinalizer(&t, func(tr **http.Transport) {
		(*tr).CloseIdleConnections()
	})
	return t
}

func newConsulRegistry(opts ...Option) Registry {
	var options Options
	for _, o := range opts {
		o(&options)
	}

	// use default config
	config := consul.DefaultConfig()
	if options.Context != nil {
		// Use the consul config passed in the options, if available
		if c, ok := options.Context.Value("consul_config").(*consul.Config); ok {
			config = c
		}
	}

	// set timeout
	if options.Timeout > 0 {
		config.HttpClient.Timeout = options.Timeout
	}

	// check if there are any addrs
	if len(options.Addrs) > 0 {
		addr, port, err := net.SplitHostPort(options.Addrs[0])
		if ae, ok := err.(*net.AddrError); ok && ae.Err == "missing port in address" {
			port = "8500"
			addr = options.Addrs[0]
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		} else if err == nil {
			config.Address = fmt.Sprintf("%s:%s", addr, port)
		}
	}

	// requires secure connection?
	if options.Secure || options.TLSConfig != nil {
		config.Scheme = "https"
		// We're going to support InsecureSkipVerify
		if config.HttpClient == nil {
			config.HttpClient = new(http.Client)
		}
		config.HttpClient.Transport = newTransport(options.TLSConfig)
	}

	// create the client
	client, _ := consul.NewClient(config)

	cr := &consulRegistry{
		Address:  config.Address,
		Client:   client,
		Options:  options,
		register: make(map[string]uint64),
	}

	return cr
}

func (c *consulRegistry) Deregister(s *Service) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	// delete our hash of the service
	c.Lock()
	delete(c.register, s.Name)
	c.Unlock()

	node := s.Nodes[0]
	return c.Client.Agent().ServiceDeregister(node.Id)
}

func (c *consulRegistry) Register(s *Service, opts ...RegisterOption) error {
	if len(s.Nodes) == 0 {
		return errors.New("Require at least one node")
	}

	var options RegisterOptions
	for _, o := range opts {
		o(&options)
	}

	// create hash of service; uint64
	h, err := hash.Hash(s, nil)
	if err != nil {
		return err
	}

	// use first node
	node := s.Nodes[0]

	// get existing hash
	c.Lock()
	v, ok := c.register[s.Name]
	c.Unlock()

	// if it's already registered and matches then just pass the check
	if ok && v == h {
		// if the err is nil we're all good, bail out
		// if not, we don't know what the state is, so full re-register
		if err := c.Client.Agent().PassTTL("service:"+node.Id, ""); err == nil {
			return nil
		}
	}

	// encode the tags
	tags := encodeMetadata(node.Metadata)
	tags = append(tags, encodeEndpoints(s.Endpoints)...)
	tags = append(tags, encodeVersion(s.Version)...)

	var check *consul.AgentServiceCheck

	// if the TTL is greater than 0 create an associated check
	if options.TTL > time.Duration(0) {
		// splay slightly for the watcher?
		splay := time.Second * 5
		deregTTL := options.TTL + splay
		// consul has a minimum timeout on deregistration of 1 minute.
		if options.TTL < time.Minute {
			deregTTL = time.Minute + splay
		}

		check = &consul.AgentServiceCheck{
			TTL: fmt.Sprintf("%v", options.TTL),
			DeregisterCriticalServiceAfter: fmt.Sprintf("%v", deregTTL),
		}
	}

	// register the service
	if err := c.Client.Agent().ServiceRegister(&consul.AgentServiceRegistration{
		ID:      node.Id,
		Name:    s.Name,
		Tags:    tags,
		Port:    node.Port,
		Address: node.Address,
		Check:   check,
	}); err != nil {
		return err
	}

	// save our hash of the service
	c.Lock()
	c.register[s.Name] = h
	c.Unlock()

	// if the TTL is 0 we don't mess with the checks
	if options.TTL == time.Duration(0) {
		return nil
	}

	// pass the healthcheck
	return c.Client.Agent().PassTTL("service:"+node.Id, "")
}

func (c *consulRegistry) GetService(name string) ([]*Service, error) {
	rsp, _, err := c.Client.Health().Service(name, "", false, nil)
	if err != nil {
		return nil, err
	}

	serviceMap := map[string]*Service{}

	for _, s := range rsp {
		if s.Service.Service != name {
			continue
		}

		// version is now a tag
		version, found := decodeVersion(s.Service.Tags)
		// service ID is now the node id
		id := s.Service.ID
		// key is always the version
		key := version
		// address is service address
		address := s.Service.Address

		// if we can't get the version we bail
		// use old the old ways
		if !found {
			continue
		}

		svc, ok := serviceMap[key]
		if !ok {
			svc = &Service{
				Endpoints: decodeEndpoints(s.Service.Tags),
				Name:      s.Service.Service,
				Version:   version,
			}
			serviceMap[key] = svc
		}

		var del bool
		for _, check := range s.Checks {
			// delete the node if the status is critical
			if check.Status == "critical" {
				del = true
				break
			}
		}

		// if delete then skip the node
		if del {
			continue
		}

		svc.Nodes = append(svc.Nodes, &Node{
			Id:       id,
			Address:  address,
			Port:     s.Service.Port,
			Metadata: decodeMetadata(s.Service.Tags),
		})
	}

	var services []*Service
	for _, service := range serviceMap {
		services = append(services, service)
	}
	return services, nil
}

func (c *consulRegistry) ListServices() ([]*Service, error) {
	rsp, _, err := c.Client.Catalog().Services(nil)
	if err != nil {
		return nil, err
	}

	var services []*Service

	for service := range rsp {
		services = append(services, &Service{Name: service})
	}

	return services, nil
}

func (c *consulRegistry) Watch() (Watcher, error) {
	return newConsulWatcher(c)
}

func (c *consulRegistry) String() string {
	return "consul"
}
<|endoftext|>