<commit_before>package aws

import (
	"log"
	"strings"

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

func resourceAwsSsmDocument() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmDocumentCreate,
		Read:   resourceAwsSsmDocumentRead,
		Update: resourceAwsSsmDocumentUpdate,
		Delete: resourceAwsSsmDocumentDelete,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"content": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"owner": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"platform_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"parameter": {
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"default_value": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"description": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"type": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"permissions": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
						},
						"account_ids": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsSsmDocumentCreate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Creating SSM Document: %s", d.Get("name").(string))

	docInput := &ssm.CreateDocumentInput{
		Name:    aws.String(d.Get("name").(string)),
		Content: aws.String(d.Get("content").(string)),
	}

	resp, err := ssmconn.CreateDocument(docInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error creating SSM document: {{err}}", err)
	}

	d.SetId(*resp.DocumentDescription.Name)

	if v, ok := d.GetOk("permissions"); ok && v != nil {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting permissions for %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

func resourceAwsSsmDocumentRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Document: %s", d.Id())

	docInput := &ssm.DescribeDocumentInput{
		Name: aws.String(d.Get("name").(string)),
	}

	resp, err := ssmconn.DescribeDocument(docInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error describing SSM document: {{err}}", err)
	}

	doc := resp.Document
	d.Set("created_date", doc.CreatedDate)
	d.Set("description", doc.Description)
	d.Set("hash", doc.Hash)
	d.Set("hash_type", doc.HashType)
	d.Set("name", doc.Name)
	d.Set("owner", doc.Owner)
	d.Set("platform_type", doc.PlatformTypes[0])
	d.Set("status", doc.Status)

	gp, err := getDocumentPermissions(d, meta)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error reading SSM document permissions: {{err}}", err)
	}

	d.Set("permissions", gp)

	params := make([]map[string]interface{}, 0)
	for i := 0; i < len(doc.Parameters); i++ {

		dp := doc.Parameters[i]
		param := make(map[string]interface{})

		if dp.DefaultValue != nil {
			param["default_value"] = *dp.DefaultValue
		}
		param["description"] = *dp.Description
		param["name"] = *dp.Name
		param["type"] = *dp.Type
		params = append(params, param)
	}

	if len(params) == 0 {
		params = make([]map[string]interface{}, 1)
	}

	if err := d.Set("parameter", params); err != nil {
		return err
	}

	return nil
}

func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error {

	if _, ok := d.GetOk("permissions"); ok {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting document permissions on %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

func resourceAwsSsmDocumentDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	if err := deleteDocumentPermissions(d, meta); err != nil {
		return err
	}

	log.Printf("[INFO] Deleting SSM Document: %s", d.Id())

	params := &ssm.DeleteDocumentInput{
		Name: aws.String(d.Get("name").(string)),
	}

	_, err := ssmconn.DeleteDocument(params)

	if err != nil {
		return err
	}

	return nil
}

func setDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Setting permissions for document: %s", d.Id())
	permission := d.Get("permissions").(map[string]interface{})

	ids := aws.StringSlice([]string{permission["account_ids"].(string)})

	if strings.Contains(permission["account_ids"].(string), ",") {
		ids = aws.StringSlice(strings.Split(permission["account_ids"].(string), ","))
	}

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:            aws.String(d.Get("name").(string)),
		PermissionType:  aws.String(permission["type"].(string)),
		AccountIdsToAdd: ids,
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	return nil
}

func getDocumentPermissions(d *schema.ResourceData, meta interface{}) (map[string]interface{}, error) {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Getting permissions for document: %s", d.Id())

	//How to get from nested scheme resource?
	permissionType := "Share"

	permInput := &ssm.DescribeDocumentPermissionInput{
		Name:           aws.String(d.Get("name").(string)),
		PermissionType: aws.String(permissionType),
	}

	resp, err := ssmconn.DescribeDocumentPermission(permInput)

	if err != nil {
		return nil, errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	var account_ids = make([]string, len(resp.AccountIds))
	for i := 0; i < len(resp.AccountIds); i++ {
		account_ids[i] = *resp.AccountIds[i]
	}

	var ids = ""
	if len(account_ids) == 1 {
		ids = account_ids[0]
	} else if len(account_ids) > 1 {
		ids = strings.Join(account_ids, ",")
	} else {
		ids = ""
	}

	if ids == "" {
		return nil, nil
	}

	perms := make(map[string]interface{})
	perms["type"] = permissionType
	perms["account_ids"] = ids

	return perms, nil
}

func deleteDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Removing permissions from document: %s", d.Id())

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:               aws.String(d.Get("name").(string)),
		PermissionType:     aws.String("Share"),
		AccountIdsToRemove: aws.StringSlice(strings.Split("all", ",")),
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error removing permissions for SSM document: {{err}}", err)
	}

	return nil
}
<commit_msg>provider/aws: Add retry to the `aws_ssm_document` delete func<commit_after>package aws

import (
	"fmt"
	"log"
	"strings"
	"time"

	"github.com/aws/aws-sdk-go/aws"
	"github.com/aws/aws-sdk-go/aws/awserr"
	"github.com/aws/aws-sdk-go/service/ssm"
	"github.com/hashicorp/errwrap"
	"github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/helper/schema"
)

func resourceAwsSsmDocument() *schema.Resource {
	return &schema.Resource{
		Create: resourceAwsSsmDocumentCreate,
		Read:   resourceAwsSsmDocumentRead,
		Update: resourceAwsSsmDocumentUpdate,
		Delete: resourceAwsSsmDocumentDelete,

		Schema: map[string]*schema.Schema{
			"name": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"content": {
				Type:     schema.TypeString,
				ForceNew: true,
				Required: true,
			},
			"created_date": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"description": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"hash_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"owner": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"status": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"platform_type": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"parameter": {
				Type:     schema.TypeList,
				Computed: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"name": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"default_value": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"description": {
							Type:     schema.TypeString,
							Optional: true,
						},
						"type": {
							Type:     schema.TypeString,
							Optional: true,
						},
					},
				},
			},
			"permissions": {
				Type:     schema.TypeMap,
				Optional: true,
				Elem: &schema.Resource{
					Schema: map[string]*schema.Schema{
						"type": {
							Type:     schema.TypeString,
							Required: true,
						},
						"account_ids": {
							Type:     schema.TypeString,
							Required: true,
						},
					},
				},
			},
		},
	}
}

func resourceAwsSsmDocumentCreate(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Creating SSM Document: %s", d.Get("name").(string))

	docInput := &ssm.CreateDocumentInput{
		Name:    aws.String(d.Get("name").(string)),
		Content: aws.String(d.Get("content").(string)),
	}

	resp, err := ssmconn.CreateDocument(docInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error creating SSM document: {{err}}", err)
	}

	d.SetId(*resp.DocumentDescription.Name)

	if v, ok := d.GetOk("permissions"); ok && v != nil {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting permissions for %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

func resourceAwsSsmDocumentRead(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[DEBUG] Reading SSM Document: %s", d.Id())

	docInput := &ssm.DescribeDocumentInput{
		Name: aws.String(d.Get("name").(string)),
	}

	resp, err := ssmconn.DescribeDocument(docInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error describing SSM document: {{err}}", err)
	}

	doc := resp.Document
	d.Set("created_date", doc.CreatedDate)
	d.Set("description", doc.Description)
	d.Set("hash", doc.Hash)
	d.Set("hash_type", doc.HashType)
	d.Set("name", doc.Name)
	d.Set("owner", doc.Owner)
	d.Set("platform_type", doc.PlatformTypes[0])
	d.Set("status", doc.Status)

	gp, err := getDocumentPermissions(d, meta)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error reading SSM document permissions: {{err}}", err)
	}

	d.Set("permissions", gp)

	params := make([]map[string]interface{}, 0)
	for i := 0; i < len(doc.Parameters); i++ {

		dp := doc.Parameters[i]
		param := make(map[string]interface{})

		if dp.DefaultValue != nil {
			param["default_value"] = *dp.DefaultValue
		}
		param["description"] = *dp.Description
		param["name"] = *dp.Name
		param["type"] = *dp.Type
		params = append(params, param)
	}

	if len(params) == 0 {
		params = make([]map[string]interface{}, 1)
	}

	if err := d.Set("parameter", params); err != nil {
		return err
	}

	return nil
}

func resourceAwsSsmDocumentUpdate(d *schema.ResourceData, meta interface{}) error {

	if _, ok := d.GetOk("permissions"); ok {
		if err := setDocumentPermissions(d, meta); err != nil {
			return err
		}
	} else {
		log.Printf("[DEBUG] Not setting document permissions on %q", d.Id())
	}

	return resourceAwsSsmDocumentRead(d, meta)
}

func resourceAwsSsmDocumentDelete(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	if err := deleteDocumentPermissions(d, meta); err != nil {
		return err
	}

	log.Printf("[INFO] Deleting SSM Document: %s", d.Id())

	params := &ssm.DeleteDocumentInput{
		Name: aws.String(d.Get("name").(string)),
	}

	_, err := ssmconn.DeleteDocument(params)
	if err != nil {
		return err
	}

	log.Printf("[DEBUG] Waiting for SSM Document %q to be deleted", d.Get("name").(string))
	err = resource.Retry(10*time.Minute, func() *resource.RetryError {
		_, err := ssmconn.DescribeDocument(&ssm.DescribeDocumentInput{
			Name: aws.String(d.Get("name").(string)),
		})

		if err != nil {
			awsErr, ok := err.(awserr.Error)
			if !ok {
				return resource.NonRetryableError(err)
			}

			if awsErr.Code() == "InvalidDocument" {
				return nil
			}

			return resource.NonRetryableError(err)
		}

		return resource.RetryableError(
			fmt.Errorf("%q: Timeout while waiting for the document to be deleted", d.Id()))
	})
	if err != nil {
		return err
	}

	d.SetId("")

	return nil
}

func setDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Setting permissions for document: %s", d.Id())
	permission := d.Get("permissions").(map[string]interface{})

	ids := aws.StringSlice([]string{permission["account_ids"].(string)})

	if strings.Contains(permission["account_ids"].(string), ",") {
		ids = aws.StringSlice(strings.Split(permission["account_ids"].(string), ","))
	}

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:            aws.String(d.Get("name").(string)),
		PermissionType:  aws.String(permission["type"].(string)),
		AccountIdsToAdd: ids,
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	return nil
}

func getDocumentPermissions(d *schema.ResourceData, meta interface{}) (map[string]interface{}, error) {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Getting permissions for document: %s", d.Id())

	//How to get from nested scheme resource?
	permissionType := "Share"

	permInput := &ssm.DescribeDocumentPermissionInput{
		Name:           aws.String(d.Get("name").(string)),
		PermissionType: aws.String(permissionType),
	}

	resp, err := ssmconn.DescribeDocumentPermission(permInput)

	if err != nil {
		return nil, errwrap.Wrapf("[ERROR] Error setting permissions for SSM document: {{err}}", err)
	}

	var account_ids = make([]string, len(resp.AccountIds))
	for i := 0; i < len(resp.AccountIds); i++ {
		account_ids[i] = *resp.AccountIds[i]
	}

	var ids = ""
	if len(account_ids) == 1 {
		ids = account_ids[0]
	} else if len(account_ids) > 1 {
		ids = strings.Join(account_ids, ",")
	} else {
		ids = ""
	}

	if ids == "" {
		return nil, nil
	}

	perms := make(map[string]interface{})
	perms["type"] = permissionType
	perms["account_ids"] = ids

	return perms, nil
}

func deleteDocumentPermissions(d *schema.ResourceData, meta interface{}) error {
	ssmconn := meta.(*AWSClient).ssmconn

	log.Printf("[INFO] Removing permissions from document: %s", d.Id())

	permInput := &ssm.ModifyDocumentPermissionInput{
		Name:               aws.String(d.Get("name").(string)),
		PermissionType:     aws.String("Share"),
		AccountIdsToRemove: aws.StringSlice(strings.Split("all", ",")),
	}

	_, err := ssmconn.ModifyDocumentPermission(permInput)

	if err != nil {
		return errwrap.Wrapf("[ERROR] Error removing permissions for SSM document: {{err}}", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package yiigo

import (
	"errors"
	"path/filepath"
	"sync"

	toml "github.com/pelletier/go-toml"
)

type env struct {
	tree  *toml.Tree
	mutex sync.RWMutex
}

// Env enviroment
var Env *env

// ErrNil returned when config not found.
var ErrNil = errors.New("config not found")

// loadEnv load env file
func loadEnv(path string) {
	abs, _ := filepath.Abs(path)
	tomlTree, err := toml.LoadFile(abs)

	if err != nil {
		panic(err)
	}

	Env = &env{tree: tomlTree}
}

// String return string
func (e *env) String(key string, defaultValue ...string) string {
	dv := ""

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	if v, ok := i.(string); ok {
		return v
	}

	return dv
}

// Int return int
func (e *env) Int(key string, defaultValue ...int) int {
	dv := 0

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	if v, ok := i.(int64); ok {
		return int(v)
	}

	return dv
}

// Int64 return int64
func (e *env) Int64(key string, defaultValue ...int64) int64 {
	var dv int64

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	if v, ok := i.(int64); ok {
		return v
	}

	return dv
}

// Float64 return float64
func (e *env) Float64(key string, defaultValue ...float64) float64 {
	var dv float64

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	if v, ok := i.(float64); ok {
		return v
	}

	return dv
}

// Bool return bool
func (e *env) Bool(key string, defaultValue ...bool) bool {
	var dv bool

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	if v, ok := i.(bool); ok {
		return v
	}

	return dv
}

// ToMap return map[string]interface{}
func (e *env) ToMap(key string) map[string]interface{} {
	i := e.Get(key)

	if v, ok := i.(*toml.Tree); ok {
		return v.ToMap()
	}

	return nil
}

// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by dest
func (e *env) Unmarshal(key string, dest interface{}) error {
	i := e.Get(key)

	if i == nil {
		return ErrNil
	}

	if v, ok := i.(*toml.Tree); ok {
		err := v.Unmarshal(dest)

		return err
	}

	return errors.New("value is not a tree of toml")
}

// Get return interface{}
func (e *env) Get(key string) interface{} {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	i := e.tree.Get(key)

	return i
}
<commit_msg>update env<commit_after>package yiigo

import (
	"errors"
	"path/filepath"
	"strconv"
	"sync"
	"time"

	toml "github.com/pelletier/go-toml"
)

type env struct {
	tree  *toml.Tree
	mutex sync.RWMutex
}

// Env enviroment
var Env *env

// ErrNil returned when config not found.
var ErrNil = errors.New("config not found")

// loadEnv load env file
func loadEnv(path string) {
	abs, _ := filepath.Abs(path)
	tomlTree, err := toml.LoadFile(abs)

	if err != nil {
		panic(err)
	}

	Env = &env{tree: tomlTree}
}

// String return string
func (e *env) String(key string, defaultValue ...string) string {
	dv := ""

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	switch t := i.(type) {
	case string:
		return t
	case int64:
		return strconv.FormatInt(t, 10)
	case uint64:
		return strconv.FormatInt(int64(t), 10)
	case float64:
		return strconv.FormatFloat(t, 'f', -1, 64)
	case bool:
		return strconv.FormatBool(t)
	default:
		return dv
	}
}

// Int return int
func (e *env) Int(key string, defaultValue ...int) int {
	dv := 0

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	switch t := i.(type) {
	case int64:
		return int(t)
	case uint64:
		return int(t)
	case float64:
		return int(t)
	case string:
		if v, err := strconv.ParseInt(t, 0, 0); err == nil {
			return int(v)
		}

		return dv
	case bool:
		if t {
			return 1
		}

		return 0
	default:
		return dv
	}
}

// Int64 return int64
func (e *env) Int64(key string, defaultValue ...int64) int64 {
	var dv int64

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	switch t := i.(type) {
	case int64:
		return t
	case uint64:
		return int64(t)
	case float64:
		return int64(t)
	case string:
		if v, err := strconv.ParseInt(t, 0, 0); err == nil {
			return v
		}

		return dv
	case bool:
		if t {
			return 1
		}

		return 0
	default:
		return dv
	}
}

// Float64 return float64
func (e *env) Float64(key string, defaultValue ...float64) float64 {
	var dv float64

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	switch t := i.(type) {
	case float64:
		return t
	case int64:
		return float64(t)
	case uint64:
		return float64(t)
	case string:
		if v, err := strconv.ParseFloat(t, 64); err == nil {
			return v
		}

		return dv
	case bool:
		if t {
			return 1
		}

		return 0
	default:
		return dv
	}
}

// Bool return bool
func (e *env) Bool(key string, defaultValue ...bool) bool {
	var dv bool

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	switch t := i.(type) {
	case bool:
		return t
	case int64:
		if t != 0 {
			return true
		}

		return false
	case uint64:
		if t != 0 {
			return true
		}

		return false
	case string:
		if v, err := strconv.ParseBool(t); err == nil {
			return v
		}

		return dv
	default:
		return dv
	}
}

// Time return time.Time
func (e *env) Time(key string, defaultValue ...time.Time) time.Time {
	var dv time.Time

	if len(defaultValue) > 0 {
		dv = defaultValue[0]
	}

	i := e.Get(key)

	switch t := i.(type) {
	case time.Time:
		return t
	case string:
		if v, err := time.Parse("2006-01-02 15:04:05", t); err == nil {
			return v
		}

		return dv
	case int64:
		return time.Unix(t, 0)
	case uint64:
		return time.Unix(int64(t), 0)
	default:
		return dv
	}
}

// ToMap return map[string]interface{}
func (e *env) ToMap(key string) map[string]interface{} {
	i := e.Get(key)

	if v, ok := i.(*toml.Tree); ok {
		return v.ToMap()
	}

	return nil
}

// Unmarshal attempts to unmarshal the Tree into a Go struct pointed by dest
func (e *env) Unmarshal(key string, dest interface{}) error {
	i := e.Get(key)

	if i == nil {
		return ErrNil
	}

	if v, ok := i.(*toml.Tree); ok {
		err := v.Unmarshal(dest)

		return err
	}

	return errors.New("value is not a tree of toml")
}

// Get return interface{}
func (e *env) Get(key string) interface{} {
	e.mutex.RLock()
	defer e.mutex.RUnlock()

	i := e.tree.Get(key)

	return i
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package main

import (
	"os"

	"bazil.org/fuse"
)

type dir struct {
}

func (d *dir) Attr() fuse.Attr {
	return fuse.Attr{
		// TODO(jacobsa): Expose ACLs from GCS?
		Mode: os.ModeDir | 0700,
	}
}
<commit_msg>Clarified the purpose of the dir struct.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
// Author: jacobsa@google.com (Aaron Jacobs)

package main

import (
	"os"

	"bazil.org/fuse"
)

const dirSeparator = '/'

// A "directory" in GCS, defined by an object name prefix. All prefixes end
// with dirSeparator except for the special case of the root directory, where
// the prefix is the empty string.
type dir struct {
	prefix string
}

func (d *dir) Attr() fuse.Attr {
	return fuse.Attr{
		// TODO(jacobsa): Expose ACLs from GCS?
		Mode: os.ModeDir | 0700,
	}
}
<|endoftext|>


<commit_before>package deps

import (
	"fmt"
	"os"
	"os/exec"
)

// Downloads (but does not install) the package at the given import path to the first GOPATH tree.
// Works like `go get -d`.
func (c *Context) Download(importPath string) error {
	return c.GoGet(importPath, DownloadOnly)
}

type GetMode int

const (
	DownloadOnly GetMode = iota
	Update
	Verbose
)

// Runs `go get` on importPath with the options specified in mode.
func (c *Context) GoGet(importPath string, mode GetMode) error {
	args := []string{"get"}
	if mode&DownloadOnly > 0 {
		args = append(args, "-d")
	}
	if mode&Update > 0 {
		args = append(args, "-u")
	}
	if mode&Verbose > 0 {
		args = append(args, "-x", "-v")
	}
	args = append(args, "--", string(importPath))
	return c.gocmd(args)
}

func (c *Context) gocmd(args []string) error {
	fmt.Fprintf(os.Stderr, "go %v", args)
	cmd := exec.Command("go", args...)
	cmd.Env = []string{"GOROOT=" + c.GOROOT, "GOPATH=" + c.GOPATH, "GIT_ASKPASS=echo", "PATH=" + os.Getenv("PATH")}
	if out, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("error: `go get` with args %v: %s: %s", args, err, string(out))
	}
	return nil
}

// Calls Download with the go/build.Default build context.
func Download(importPath string) error {
	return Default.Download(importPath)
}

// Calls GoGet with the go/build.Default build context.
func GoGet(importPath string, mode GetMode) error {
	return Default.GoGet(importPath, mode)
}
<commit_msg>Add newline in log<commit_after>package deps

import (
	"fmt"
	"os"
	"os/exec"
)

// Downloads (but does not install) the package at the given import path to the first GOPATH tree.
// Works like `go get -d`.
func (c *Context) Download(importPath string) error {
	return c.GoGet(importPath, DownloadOnly)
}

type GetMode int

const (
	DownloadOnly GetMode = iota
	Update
	Verbose
)

// Runs `go get` on importPath with the options specified in mode.
func (c *Context) GoGet(importPath string, mode GetMode) error {
	args := []string{"get"}
	if mode&DownloadOnly > 0 {
		args = append(args, "-d")
	}
	if mode&Update > 0 {
		args = append(args, "-u")
	}
	if mode&Verbose > 0 {
		args = append(args, "-x", "-v")
	}
	args = append(args, "--", string(importPath))
	return c.gocmd(args)
}

func (c *Context) gocmd(args []string) error {
	fmt.Fprintf(os.Stderr, "go %v\n", args)
	cmd := exec.Command("go", args...)
	cmd.Env = []string{"GOROOT=" + c.GOROOT, "GOPATH=" + c.GOPATH, "GIT_ASKPASS=echo", "PATH=" + os.Getenv("PATH")}
	if out, err := cmd.CombinedOutput(); err != nil {
		return fmt.Errorf("error: `go get` with args %v: %s: %s", args, err, string(out))
	}
	return nil
}

// Calls Download with the go/build.Default build context.
func Download(importPath string) error {
	return Default.Download(importPath)
}

// Calls GoGet with the go/build.Default build context.
func GoGet(importPath string, mode GetMode) error {
	return Default.GoGet(importPath, mode)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/henvic/uiprogress"
)

func resolveGitRepositories(uniqueGitModules map[string]GitModule) {
	defer timeTrack(time.Now(), funcName())
	if len(uniqueGitModules) <= 0 {
		Debugf("uniqueGitModules[] is empty, skipping...")
		return
	}
	bar := uiprogress.AddBar(len(uniqueGitModules)).AppendCompleted().PrependElapsed()
	bar.PrependFunc(func(b *uiprogress.Bar) string {
		return fmt.Sprintf("Resolving Git modules (%d/%d)", b.Current(), len(uniqueGitModules))
	})
	// Dummy channel to coordinate the number of concurrent goroutines.
	// This channel should be buffered otherwise we will be immediately blocked
	// when trying to fill it.

	Debugf("Resolving " + strconv.Itoa(len(uniqueGitModules)) + " Git modules with " + strconv.Itoa(config.Maxworker) + " workers")
	concurrentGoroutines := make(chan struct{}, config.Maxworker)
	// Fill the dummy channel with config.Maxworker empty struct.
	for i := 0; i < config.Maxworker; i++ {
		concurrentGoroutines <- struct{}{}
	}

	// The done channel indicates when a single goroutine has
	// finished its job.
	done := make(chan bool)
	// The waitForAllJobs channel allows the main program
	// to wait until we have indeed done all the jobs.
	waitForAllJobs := make(chan bool)
	// Collect all the jobs, and since the job is finished, we can
	// release another spot for a goroutine.
	go func() {
		for _, gm := range uniqueGitModules {
			go func(gm GitModule) {
				<-done
				// Say that another goroutine can now start.
				concurrentGoroutines <- struct{}{}
			}(gm)
		}
		// We have collected all the jobs, the program
		// can now terminate  8.6s with git (13.7s sync, I/O 1.2s)
		waitForAllJobs <- true
	}()
	wg := sync.WaitGroup{}
	wg.Add(len(uniqueGitModules))

	for url, gm := range uniqueGitModules {
		Debugf("git repo url " + url)
		privateKey := gm.privateKey
		go func(url string, privateKey string, gm GitModule, bar *uiprogress.Bar) {
			// Try to receive from the concurrentGoroutines channel. When we have something,
			// it means we can start a new goroutine because another one finished.
			// Otherwise, it will block the execution until an execution
			// spot is available.
			<-concurrentGoroutines
			defer bar.Incr()
			defer wg.Done()

			if len(gm.privateKey) > 0 {
				Debugf("git repo url " + url + " with ssh key " + privateKey)
			} else {
				Debugf("git repo url " + url + " without ssh key")
			}

			//log.Println(config)
			// create save directory name from Git repo name
			repoDir := strings.Replace(strings.Replace(url, "/", "_", -1), ":", "-", -1)
			workDir := config.ModulesCacheDir + repoDir

			doMirrorOrUpdate(url, workDir, privateKey, gm.ignoreUnreachable, 1)
			//	doCloneOrPull(source, workDir, targetDir, sa.Remote, branch, sa.PrivateKey)
		}(url, privateKey, gm, bar)
		done <- true
	}

	// Wait for all jobs to finish
	<-waitForAllJobs
	wg.Wait()
}

func doMirrorOrUpdate(url string, workDir string, sshPrivateKey string, allowFail bool, retryCount int) bool {
	needSSHKey := true
	if strings.Contains(url, "github.com") || len(sshPrivateKey) == 0 {
		needSSHKey = false
	}

	er := ExecResult{}
	gitCmd := "git clone --mirror " + url + " " + workDir
	if isDir(workDir) {
		gitCmd = "git --git-dir " + workDir + " remote update --prune"
	}

	if needSSHKey {
		er = executeCommand("ssh-agent bash -c 'ssh-add "+sshPrivateKey+"; "+gitCmd+"'", config.Timeout, allowFail)
	} else {
		er = executeCommand(gitCmd, config.Timeout, allowFail)
	}

	if er.returnCode != 0 {
		if config.UseCacheFallback {
			Warnf("WARN: git repository " + url + " does not exist or is unreachable at this moment!")
			Warnf("WARN: Trying to use cache for " + url + " git repository")
		} else if config.RetryGitCommands && retryCount > 0 {
			Warnf("WARN: git command failed: " + gitCmd + " deleting local cached repository and retrying...")
			purgeDir(workDir, "doMirrorOrUpdate, because git command failed, retrying")
			return doMirrorOrUpdate(url, workDir, sshPrivateKey, false, retryCount-1)
		}
		Warnf("WARN: git repository " + url + " does not exist or is unreachable at this moment!")
		return false
	}
	return true
}

func syncToModuleDir(srcDir string, targetDir string, tree string, allowFail bool, ignoreUnreachable bool) bool {
	mutex.Lock()
	syncGitCount++
	mutex.Unlock()
	if !isDir(srcDir) {
		if config.UseCacheFallback {
			Fatalf("Could not find cached git module " + srcDir)
		}
	}
	logCmd := "git --git-dir " + srcDir + " rev-parse --verify '" + tree + "'"
	er := executeCommand(logCmd, config.Timeout, allowFail)
	hashFile := targetDir + "/.latest_commit"
	needToSync := true
	if er.returnCode != 0 {
		if allowFail && ignoreUnreachable {
			Infof("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
			purgeDir(targetDir, "syncToModuleDir, because ignore-unreachable is set for this module")
		}
		return false
	}

	if len(er.output) > 0 {
		targetHash, _ := ioutil.ReadFile(hashFile)
		if string(targetHash) == er.output {
			needToSync = false
			//Debugf("Skipping, because no diff found between " + srcDir + "(" + er.output + ") and " + targetDir + "(" + string(targetHash) + ")")
		}

	}
	if needToSync && er.returnCode == 0 {
		Infof("Need to sync " + targetDir)
		mutex.Lock()
		needSyncGitCount++
		mutex.Unlock()
		if !dryRun {
			createOrPurgeDir(targetDir, "syncToModuleDir()")
			gitArchiveArgs := []string{"--git-dir", srcDir, "archive", tree}
			cmd := exec.Command("git", gitArchiveArgs...)
			Debugf("Executing git --git-dir " + srcDir + " archive " + tree)
			cmdOut, err := cmd.StdoutPipe()
			if err != nil {
				if !allowFail {
					Infof("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
				} else {
					return false
				}
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + tree + " Error: " + err.Error())
			}
			cmd.Start()

			before := time.Now()
			unTar(cmdOut, targetDir)
			duration := time.Since(before).Seconds()
			mutex.Lock()
			ioGitTime += duration
			mutex.Unlock()
			Verbosef("syncToModuleDir(): Executing git --git-dir " + srcDir + " archive " + tree + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")

			er = executeCommand(logCmd, config.Timeout, false)
			if len(er.output) > 0 {
				Debugf("Writing hash " + er.output + " from command " + logCmd + " to " + hashFile)
				f, _ := os.Create(hashFile)
				defer f.Close()
				f.WriteString(er.output)
				f.Sync()
			}
		}
	}
	return true
}
<commit_msg>More robust git failure detection (#92)<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/henvic/uiprogress"
)

func resolveGitRepositories(uniqueGitModules map[string]GitModule) {
	defer timeTrack(time.Now(), funcName())
	if len(uniqueGitModules) <= 0 {
		Debugf("uniqueGitModules[] is empty, skipping...")
		return
	}
	bar := uiprogress.AddBar(len(uniqueGitModules)).AppendCompleted().PrependElapsed()
	bar.PrependFunc(func(b *uiprogress.Bar) string {
		return fmt.Sprintf("Resolving Git modules (%d/%d)", b.Current(), len(uniqueGitModules))
	})
	// Dummy channel to coordinate the number of concurrent goroutines.
	// This channel should be buffered otherwise we will be immediately blocked
	// when trying to fill it.

	Debugf("Resolving " + strconv.Itoa(len(uniqueGitModules)) + " Git modules with " + strconv.Itoa(config.Maxworker) + " workers")
	concurrentGoroutines := make(chan struct{}, config.Maxworker)
	// Fill the dummy channel with config.Maxworker empty struct.
	for i := 0; i < config.Maxworker; i++ {
		concurrentGoroutines <- struct{}{}
	}

	// The done channel indicates when a single goroutine has
	// finished its job.
	done := make(chan bool)
	// The waitForAllJobs channel allows the main program
	// to wait until we have indeed done all the jobs.
	waitForAllJobs := make(chan bool)
	// Collect all the jobs, and since the job is finished, we can
	// release another spot for a goroutine.
	go func() {
		for _, gm := range uniqueGitModules {
			go func(gm GitModule) {
				<-done
				// Say that another goroutine can now start.
				concurrentGoroutines <- struct{}{}
			}(gm)
		}
		// We have collected all the jobs, the program
		// can now terminate  8.6s with git (13.7s sync, I/O 1.2s)
		waitForAllJobs <- true
	}()
	wg := sync.WaitGroup{}
	wg.Add(len(uniqueGitModules))

	for url, gm := range uniqueGitModules {
		Debugf("git repo url " + url)
		privateKey := gm.privateKey
		go func(url string, privateKey string, gm GitModule, bar *uiprogress.Bar) {
			// Try to receive from the concurrentGoroutines channel. When we have something,
			// it means we can start a new goroutine because another one finished.
			// Otherwise, it will block the execution until an execution
			// spot is available.
			<-concurrentGoroutines
			defer bar.Incr()
			defer wg.Done()

			if len(gm.privateKey) > 0 {
				Debugf("git repo url " + url + " with ssh key " + privateKey)
			} else {
				Debugf("git repo url " + url + " without ssh key")
			}

			//log.Println(config)
			// create save directory name from Git repo name
			repoDir := strings.Replace(strings.Replace(url, "/", "_", -1), ":", "-", -1)
			workDir := config.ModulesCacheDir + repoDir

			doMirrorOrUpdate(url, workDir, privateKey, gm.ignoreUnreachable, 1)
			//	doCloneOrPull(source, workDir, targetDir, sa.Remote, branch, sa.PrivateKey)
		}(url, privateKey, gm, bar)
		done <- true
	}

	// Wait for all jobs to finish
	<-waitForAllJobs
	wg.Wait()
}

func doMirrorOrUpdate(url string, workDir string, sshPrivateKey string, allowFail bool, retryCount int) bool {
	needSSHKey := true
	if strings.Contains(url, "github.com") || len(sshPrivateKey) == 0 {
		needSSHKey = false
	}

	er := ExecResult{}
	gitCmd := "git clone --mirror " + url + " " + workDir
	if isDir(workDir) {
		gitCmd = "git --git-dir " + workDir + " remote update --prune"
	}

	if needSSHKey {
		er = executeCommand("ssh-agent bash -c 'ssh-add "+sshPrivateKey+"; "+gitCmd+"'", config.Timeout, allowFail)
	} else {
		er = executeCommand(gitCmd, config.Timeout, allowFail)
	}

	if er.returnCode != 0 {
		if config.UseCacheFallback {
			Warnf("WARN: git repository " + url + " does not exist or is unreachable at this moment!")
			Warnf("WARN: Trying to use cache for " + url + " git repository")
		} else if config.RetryGitCommands && retryCount > 0 {
			Warnf("WARN: git command failed: " + gitCmd + " deleting local cached repository and retrying...")
			purgeDir(workDir, "doMirrorOrUpdate, because git command failed, retrying")
			return doMirrorOrUpdate(url, workDir, sshPrivateKey, false, retryCount-1)
		}
		Warnf("WARN: git repository " + url + " does not exist or is unreachable at this moment!")
		return false
	}
	return true
}

func syncToModuleDir(srcDir string, targetDir string, tree string, allowFail bool, ignoreUnreachable bool) bool {
	mutex.Lock()
	syncGitCount++
	mutex.Unlock()
	if !isDir(srcDir) {
		if config.UseCacheFallback {
			Fatalf("Could not find cached git module " + srcDir)
		}
	}
	logCmd := "git --git-dir " + srcDir + " rev-parse --verify '" + tree + "'"
	er := executeCommand(logCmd, config.Timeout, allowFail)
	hashFile := targetDir + "/.latest_commit"
	needToSync := true
	if er.returnCode != 0 {
		if allowFail && ignoreUnreachable {
			Infof("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
			purgeDir(targetDir, "syncToModuleDir, because ignore-unreachable is set for this module")
		}
		return false
	}

	if len(er.output) > 0 {
		targetHash, _ := ioutil.ReadFile(hashFile)
		if string(targetHash) == er.output {
			needToSync = false
			//Debugf("Skipping, because no diff found between " + srcDir + "(" + er.output + ") and " + targetDir + "(" + string(targetHash) + ")")
		}

	}
	if needToSync && er.returnCode == 0 {
		Infof("Need to sync " + targetDir)
		mutex.Lock()
		needSyncGitCount++
		mutex.Unlock()
		if !dryRun {
			createOrPurgeDir(targetDir, "syncToModuleDir()")
			gitArchiveArgs := []string{"--git-dir", srcDir, "archive", tree}
			cmd := exec.Command("git", gitArchiveArgs...)
			Debugf("Executing git --git-dir " + srcDir + " archive " + tree)
			cmdOut, err := cmd.StdoutPipe()
			if err != nil {
				if !allowFail {
					Infof("Failed to populate module " + targetDir + " but ignore-unreachable is set. Continuing...")
				} else {
					return false
				}
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + tree + " Error: " + err.Error())
			}
			cmd.Start()

			before := time.Now()
			unTar(cmdOut, targetDir)
			duration := time.Since(before).Seconds()
			mutex.Lock()
			ioGitTime += duration
			mutex.Unlock()

			err = cmd.Wait()
			if err != nil {
				Fatalf("syncToModuleDir(): Failed to execute command: git --git-dir " + srcDir + " archive " + tree + " Error: " + err.Error())
			}

			Verbosef("syncToModuleDir(): Executing git --git-dir " + srcDir + " archive " + tree + " took " + strconv.FormatFloat(duration, 'f', 5, 64) + "s")

			er = executeCommand(logCmd, config.Timeout, false)
			if len(er.output) > 0 {
				Debugf("Writing hash " + er.output + " from command " + logCmd + " to " + hashFile)
				f, _ := os.Create(hashFile)
				defer f.Close()
				f.WriteString(er.output)
				f.Sync()
			}
		}
	}
	return true
}
<|endoftext|>


<commit_before>package game

import (
	"math/rand"
	"time"

	"github.com/pkg/errors"
	"github.com/verath/archipelago/lib/game/model"
)

func createPlayers(numPlayers int) ([]*model.Player, *model.Player, error) {
	playerNeutral, err := model.NewPlayer()
	if err != nil {
		return nil, nil, errors.Wrap(err, "error creating playerNeutral")
	}
	players := make([]*model.Player, numPlayers)
	for i := 0; i < numPlayers; i++ {
		players[i], err = model.NewPlayer()
		if err != nil {
			return nil, nil, errors.Wrap(err, "error creating player")
		}
	}
	return players, playerNeutral, nil
}

func createIslands(players []*model.Player, playerNeutral *model.Player, size model.Coordinate, gameRand *rand.Rand) ([]*model.Island, error) {
	type islandData struct {
		Size            model.IslandSize
		Strength        int64
		Player          *model.Player
		GrowthRemainder time.Duration
	}
	islandMap := make(map[model.Coordinate]islandData)
	// Randomize the neutral islands
	neutralSizes := []model.IslandSize{model.IslandSizeTiny, model.IslandSizeSmall, model.IslandSizeMedium}
	for x := 0; x < size.X; x++ {
		for y := 0; y < size.Y; y++ {
			// 25% tiles should be islands.
			if gameRand.Intn(100) >= 25 {
				continue
			}
			pos := model.Coordinate{x, y}
			size := neutralSizes[gameRand.Intn(len(neutralSizes))]
			strength := gameRand.Int63n(int64(size*model.IslandGrowthCap)) + 10
			growthRemainder := time.Millisecond * time.Duration(gameRand.Int63n(1000))
			islandMap[pos] = islandData{size, strength, playerNeutral, growthRemainder}
		}
	}
	// Randomize player island positions.
	// TODO: Evenly distribute on board, prevent close positions.
	boardPerm := gameRand.Perm(size.X * size.Y)
	for i := range players {
		x, y := boardPerm[i]%size.X, boardPerm[i]/size.X
		pos := model.Coordinate{x, y}
		islandMap[pos] = islandData{model.IslandSizeLarge, 20, players[i], 0}
	}
	// Transform the map to a slice of islands.
	var islands []*model.Island
	for pos, data := range islandMap {
		island, err := model.NewIsland(pos, data.Size, data.Strength, data.Player)
		if err != nil {
			return nil, errors.Wrapf(err, "Error creating island (data: %v)", data)
		}
		island.SetGrowthRemainder(data.GrowthRemainder)
		islands = append(islands, island)
	}
	return islands, nil
}

// createBasicGame creates a new instance of a Game model with numPlayers
// players.
func createBasicGame(numPlayers int) (*model.Game, error) {
	size := model.Coordinate{7, 7}
	players, playerNeutral, err := createPlayers(numPlayers)
	if err != nil {
		return nil, errors.Wrap(err, "error creating players")
	}
	seed := time.Now().UnixNano()
	gameRand := rand.New(rand.NewSource(seed))
	islands, err := createIslands(players, playerNeutral, size, gameRand)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating islands")
	}
	gameBuilder := model.NewGameBuilder(size, playerNeutral)
	for _, player := range players {
		gameBuilder.AddPlayer(player)
	}
	for _, island := range islands {
		gameBuilder.AddIsland(island)
	}
	return gameBuilder.Build()
}
<commit_msg>Increase board size when > 5 players<commit_after>package game

import (
	"math/rand"
	"time"

	"github.com/pkg/errors"
	"github.com/verath/archipelago/lib/game/model"
)

func createPlayers(numPlayers int) ([]*model.Player, *model.Player, error) {
	playerNeutral, err := model.NewPlayer()
	if err != nil {
		return nil, nil, errors.Wrap(err, "error creating playerNeutral")
	}
	players := make([]*model.Player, numPlayers)
	for i := 0; i < numPlayers; i++ {
		players[i], err = model.NewPlayer()
		if err != nil {
			return nil, nil, errors.Wrap(err, "error creating player")
		}
	}
	return players, playerNeutral, nil
}

func createIslands(players []*model.Player, playerNeutral *model.Player, size model.Coordinate, gameRand *rand.Rand) ([]*model.Island, error) {
	type islandData struct {
		Size            model.IslandSize
		Strength        int64
		Player          *model.Player
		GrowthRemainder time.Duration
	}
	islandMap := make(map[model.Coordinate]islandData)
	// Randomize the neutral islands
	neutralSizes := []model.IslandSize{model.IslandSizeTiny, model.IslandSizeSmall, model.IslandSizeMedium}
	for x := 0; x < size.X; x++ {
		for y := 0; y < size.Y; y++ {
			// 25% tiles should be islands.
			if gameRand.Intn(100) >= 25 {
				continue
			}
			pos := model.Coordinate{x, y}
			size := neutralSizes[gameRand.Intn(len(neutralSizes))]
			strength := gameRand.Int63n(int64(size*model.IslandGrowthCap)) + 10
			growthRemainder := time.Millisecond * time.Duration(gameRand.Int63n(1000))
			islandMap[pos] = islandData{size, strength, playerNeutral, growthRemainder}
		}
	}
	// Randomize player island positions.
	// TODO: Evenly distribute on board, prevent close positions.
	boardPerm := gameRand.Perm(size.X * size.Y)
	for i := range players {
		x, y := boardPerm[i]%size.X, boardPerm[i]/size.X
		pos := model.Coordinate{x, y}
		islandMap[pos] = islandData{model.IslandSizeLarge, 20, players[i], 0}
	}
	// Transform the map to a slice of islands.
	var islands []*model.Island
	for pos, data := range islandMap {
		island, err := model.NewIsland(pos, data.Size, data.Strength, data.Player)
		if err != nil {
			return nil, errors.Wrapf(err, "Error creating island (data: %v)", data)
		}
		island.SetGrowthRemainder(data.GrowthRemainder)
		islands = append(islands, island)
	}
	return islands, nil
}

// createBasicGame creates a new instance of a Game model with numPlayers
// players.
func createBasicGame(numPlayers int) (*model.Game, error) {
	size := model.Coordinate{7, 7}
	if numPlayers > 5 {
		size.X += 2
		size.Y += 2
	}
	players, playerNeutral, err := createPlayers(numPlayers)
	if err != nil {
		return nil, errors.Wrap(err, "error creating players")
	}
	seed := time.Now().UnixNano()
	gameRand := rand.New(rand.NewSource(seed))
	islands, err := createIslands(players, playerNeutral, size, gameRand)
	if err != nil {
		return nil, errors.Wrap(err, "Error creating islands")
	}
	gameBuilder := model.NewGameBuilder(size, playerNeutral)
	for _, player := range players {
		gameBuilder.AddPlayer(player)
	}
	for _, island := range islands {
		gameBuilder.AddIsland(island)
	}
	return gameBuilder.Build()
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Extended and bugfixes by Miek Gieben. Copyright 2010-2012.

// DOMAIN NAME SYSTEM
//
// Package dns implements a full featured interface to the Domain Name System.
// Server- and client-side programming is supported.
// The package allows complete control over what is send out to the DNS. The package
// API follows the less-is-more principle, by presenting a small, clean interface.
//
// The package dns supports (asynchronous) querying/replying, incoming/outgoing AXFR/IXFR,
// TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
// Note that domain names MUST be fully qualified, before sending them, unqualified
// names in a message will result in a packing failure.
//
// Resource records are native types. They are not stored in wire format.
// Basic usage pattern for creating a new resource record:
//
//      r := new(dns.RR_MX)
//      r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}
//      r.Pref = 10
//      r.Mx = "mx.miek.nl."
//
// Or directly from a string:
//
//      mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
//
// Or when the default TTL (3600) and class (IN) suit you:
//
//      mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
//
// Or even:
//
//      mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
//
// In the DNS messages are exchanged, these messages contain resource
// records (sets).  Use pattern for creating a message:
//
//      m := dns.new(Msg)
//      m.SetQuestion("miek.nl.", dns.TypeMX)
//
// Or when not certain if the domain name is fully qualified:
//
//	m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
//
// The message m is now a message with the question section set to ask
// the MX records for the miek.nl. zone.
//
// The following is slightly more verbose, but more flexible:
//
//      m1 := new(dns.Msg)
//      m1.MsgHdr.Id = Id()
//      m1.MsgHdr.RecursionDesired = true
//      m1.Question = make([]Question, 1)
//      m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
//
// After creating a message it can be send.
// Basic use pattern for synchronous querying the DNS at a
// server configured on 127.0.0.1 and port 53:
//
//      c := new(Client)
//      in, err := c.Exchange(m1, "127.0.0.1:53")
//
// An asynchronous query is also possible, see client.Do and client.DoRtt.
//
// From a birds eye view a dns message consists out of four sections.
// The question section: in.Question, the answer section: in.Answer,
// the authority section: in.Ns and the additional section: in.Extra.
//
// Each of these sections (except the Question section) contain a []RR. Basic
// use pattern for accessing the rdata of a TXT RR as the first RR in
// the Answer section:
//
//	if t, ok := in.Answer[0].(*RR_TXT); ok {
//		// do something with t.Txt
//	}
package dns

import (
	"net"
	"strconv"
)

const (
	year68         = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
	DefaultMsgSize = 4096    // Standard default for larger than 512 packets.
	udpMsgSize     = 512     // Default buffer size for servers receiving UDP packets.
	MaxMsgSize     = 65536   // Largest possible DNS packet.
	defaultTtl     = 3600    // Default TTL.
)

// Error represents a DNS error
type Error struct {
	Err     string
	Name    string
	Server  net.Addr
	Timeout bool
}

func (e *Error) Error() string {
	if e == nil {
		return "dns: <nil>"
	}
	if e.Name == "" {
		return e.Err
	}
	return e.Name + ": " + e.Err

}

// An RR represents a resource record.
type RR interface {
	// Header returns the header of an resource record. The header contains
	// everything up to the rdata.
	Header() *RR_Header
	// String returns the text representation of the resource record.
	String() string
	// Len returns the length (in octects) of the uncompressed RR in wire format.
	Len() int
	// Copy returns a copy of the RR
	Copy() RR
}

// DNS resource records.
// There are many types of RRs,
// but they all share the same header.
type RR_Header struct {
	Name     string `dns:"cdomain-name"`
	Rrtype   uint16
	Class    uint16
	Ttl      uint32
	Rdlength uint16 // length of data after header
}

func (h *RR_Header) Header() *RR_Header {
	return h
}

func (h *RR_Header) CopyHeader() *RR_Header {
	r := new(RR_Header)
	r.Name = h.Name
	r.Rrtype = h.Rrtype
	r.Class = h.Class
	r.Ttl = h.Ttl
	r.Rdlength = h.Rdlength
	return r
}

// Just to imlement the RR interface
func (h *RR_Header) Copy() RR {
	return nil
}

func (h *RR_Header) String() string {
	var s string

	if h.Rrtype == TypeOPT {
		s = ";"
		// and maybe other things
	}

	if len(h.Name) == 0 {
		s += ".\t"
	} else {
		s += h.Name + "\t"
	}
	s = s + strconv.FormatInt(int64(h.Ttl), 10) + "\t"

	if _, ok := Class_str[h.Class]; ok {
		s += Class_str[h.Class] + "\t"
	} else {
		s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
	}

	if _, ok := Rr_str[h.Rrtype]; ok {
		s += Rr_str[h.Rrtype] + "\t"
	} else {
		s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
	}
	return s
}

func (h *RR_Header) Len() int {
	l := len(h.Name) + 1
	l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
	return l
}

// find best matching pattern for zone
func zoneMatch(pattern, zone string) (ok bool) {
	if len(pattern) == 0 {
		return
	}
	if len(zone) == 0 {
		zone = "."
	}
	// pattern = Fqdn(pattern) // should already be a fqdn
	zone = Fqdn(zone)
	i := 0
	for {
		ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i]
		i++

		if !ok {
			break
		}
		if len(pattern)-1-i < 0 || len(zone)-1-i < 0 {
			break
		}

	}
	return
}
<commit_msg>doc tweaks<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
// Extended and bugfixes by Miek Gieben. Copyright 2010-2012.

// Package dns implements a full featured interface to the Domain Name System.
// Server- and client-side programming is supported.
// The package allows complete control over what is send out to the DNS. The package
// API follows the less-is-more principle, by presenting a small, clean interface.
//
// The package dns supports (asynchronous) querying/replying, incoming/outgoing AXFR/IXFR,
// TSIG, EDNS0, dynamic updates, notifies and DNSSEC validation/signing.
// Note that domain names MUST be fully qualified, before sending them, unqualified
// names in a message will result in a packing failure.
//
// Resource records are native types. They are not stored in wire format.
// Basic usage pattern for creating a new resource record:
//
//      r := new(dns.RR_MX)
//      r.Hdr = dns.RR_Header{Name: "miek.nl.", Rrtype: dns.TypeMX, Class: dns.ClassINET, Ttl: 3600}
//      r.Pref = 10
//      r.Mx = "mx.miek.nl."
//
// Or directly from a string:
//
//      mx, err := dns.NewRR("miek.nl. 3600 IN MX 10 mx.miek.nl.")
//
// Or when the default TTL (3600) and class (IN) suit you:
//
//      mx, err := dns.NewRR("miek.nl. MX 10 mx.miek.nl.")
//
// Or even:
//
//      mx, err := dns.NewRR("$ORIGIN nl.\nmiek 1H IN MX 10 mx.miek")
//
// In the DNS messages are exchanged, these messages contain resource
// records (sets).  Use pattern for creating a message:
//
//      m := dns.new(Msg)
//      m.SetQuestion("miek.nl.", dns.TypeMX)
//
// Or when not certain if the domain name is fully qualified:
//
//	m.SetQuestion(dns.Fqdn("miek.nl"), dns.TypeMX)
//
// The message m is now a message with the question section set to ask
// the MX records for the miek.nl. zone.
//
// The following is slightly more verbose, but more flexible:
//
//      m1 := new(dns.Msg)
//      m1.MsgHdr.Id = Id()
//      m1.MsgHdr.RecursionDesired = true
//      m1.Question = make([]Question, 1)
//      m1.Question[0] = dns.Question{"miek.nl.", dns.TypeMX, dns.ClassINET}
//
// After creating a message it can be send.
// Basic use pattern for synchronous querying the DNS at a
// server configured on 127.0.0.1 and port 53:
//
//      c := new(Client)
//      in, err := c.Exchange(m1, "127.0.0.1:53")
//
// An asynchronous query is also possible, see client.Do and client.DoRtt.
//
// From a birds eye view a dns message consists out of four sections.
// The question section: in.Question, the answer section: in.Answer,
// the authority section: in.Ns and the additional section: in.Extra.
//
// Each of these sections (except the Question section) contain a []RR. Basic
// use pattern for accessing the rdata of a TXT RR as the first RR in
// the Answer section:
//
//	if t, ok := in.Answer[0].(*RR_TXT); ok {
//		// do something with t.Txt
//	}
package dns

import (
	"net"
	"strconv"
)

const (
	year68         = 1 << 31 // For RFC1982 (Serial Arithmetic) calculations in 32 bits.
	DefaultMsgSize = 4096    // Standard default for larger than 512 packets.
	udpMsgSize     = 512     // Default buffer size for servers receiving UDP packets.
	MaxMsgSize     = 65536   // Largest possible DNS packet.
	defaultTtl     = 3600    // Default TTL.
)

// Error represents a DNS error
type Error struct {
	Err     string
	Name    string
	Server  net.Addr
	Timeout bool
}

func (e *Error) Error() string {
	if e == nil {
		return "dns: <nil>"
	}
	if e.Name == "" {
		return e.Err
	}
	return e.Name + ": " + e.Err

}

// An RR represents a resource record.
type RR interface {
	// Header returns the header of an resource record. The header contains
	// everything up to the rdata.
	Header() *RR_Header
	// String returns the text representation of the resource record.
	String() string
	// Len returns the length (in octects) of the uncompressed RR in wire format.
	Len() int
	// Copy returns a copy of the RR
	Copy() RR
}

// DNS resource records.
// There are many types of RRs,
// but they all share the same header.
type RR_Header struct {
	Name     string `dns:"cdomain-name"`
	Rrtype   uint16
	Class    uint16
	Ttl      uint32
	Rdlength uint16 // length of data after header
}

func (h *RR_Header) Header() *RR_Header {
	return h
}

func (h *RR_Header) CopyHeader() *RR_Header {
	r := new(RR_Header)
	r.Name = h.Name
	r.Rrtype = h.Rrtype
	r.Class = h.Class
	r.Ttl = h.Ttl
	r.Rdlength = h.Rdlength
	return r
}

// Just to imlement the RR interface
func (h *RR_Header) Copy() RR {
	return nil
}

func (h *RR_Header) String() string {
	var s string

	if h.Rrtype == TypeOPT {
		s = ";"
		// and maybe other things
	}

	if len(h.Name) == 0 {
		s += ".\t"
	} else {
		s += h.Name + "\t"
	}
	s = s + strconv.FormatInt(int64(h.Ttl), 10) + "\t"

	if _, ok := Class_str[h.Class]; ok {
		s += Class_str[h.Class] + "\t"
	} else {
		s += "CLASS" + strconv.Itoa(int(h.Class)) + "\t"
	}

	if _, ok := Rr_str[h.Rrtype]; ok {
		s += Rr_str[h.Rrtype] + "\t"
	} else {
		s += "TYPE" + strconv.Itoa(int(h.Rrtype)) + "\t"
	}
	return s
}

func (h *RR_Header) Len() int {
	l := len(h.Name) + 1
	l += 10 // rrtype(2) + class(2) + ttl(4) + rdlength(2)
	return l
}

// find best matching pattern for zone
func zoneMatch(pattern, zone string) (ok bool) {
	if len(pattern) == 0 {
		return
	}
	if len(zone) == 0 {
		zone = "."
	}
	// pattern = Fqdn(pattern) // should already be a fqdn
	zone = Fqdn(zone)
	i := 0
	for {
		ok = pattern[len(pattern)-1-i] == zone[len(zone)-1-i]
		i++

		if !ok {
			break
		}
		if len(pattern)-1-i < 0 || len(zone)-1-i < 0 {
			break
		}

	}
	return
}
<|endoftext|>


<commit_before>package dalga

// TODO refactor publisher wait/notify
// TODO update readme

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net"
	"strconv"
	"time"

	"github.com/cenkalti/dalga/vendor/github.com/go-sql-driver/mysql"
	"github.com/cenkalti/dalga/vendor/github.com/streadway/amqp"
)

const createTableSQL = "" +
	"CREATE TABLE `%s` (" +
	"  `id`          VARCHAR(255)       NOT NULL," +
	"  `routing_key` VARCHAR(255)    NOT NULL," +
	"  `interval`    INT UNSIGNED    NOT NULL," +
	"  `next_run`    DATETIME        NOT NULL," +
	"" +
	"  PRIMARY KEY (`id`, `routing_key`)," +
	"  KEY `idx_next_run` (`next_run`)" +
	") ENGINE=InnoDB DEFAULT CHARSET=utf8"

var debugging = flag.Bool("debug", false, "turn on debug messages")

func debug(args ...interface{}) {
	if *debugging {
		log.Println(args...)
	}
}

type Dalga struct {
	Config            Config
	db                *sql.DB
	rabbit            *amqp.Connection
	channel           *amqp.Channel
	listener          net.Listener
	newJobs           chan *Job
	canceledJobs      chan *Job
	quitPublisher     chan bool
	publisherFinished chan bool
}

func New(config Config) *Dalga {
	return &Dalga{
		Config:            config,
		newJobs:           make(chan *Job),
		canceledJobs:      make(chan *Job),
		quitPublisher:     make(chan bool),
		publisherFinished: make(chan bool),
	}
}

// Start starts the publisher and http server goroutines.
func (d *Dalga) Start() error {
	if err := d.connectDB(); err != nil {
		return err
	}

	if err := d.connectMQ(); err != nil {
		return err
	}

	server, err := d.makeServer()
	if err != nil {
		return err
	}

	go d.publisher()
	go server()

	return nil
}

// Run starts the dalga and waits until Shutdown() is called.
func (d *Dalga) Run() error {
	err := d.Start()
	if err != nil {
		return err
	}

	debug("Waiting a message from publisherFinished channel")
	<-d.publisherFinished
	debug("Received message from publisherFinished channel")

	return nil
}

func (d *Dalga) Shutdown() error {
	// TODO stop goroutines
	return d.listener.Close()
}

func (d *Dalga) connectDB() error {
	var err error
	d.db, err = d.newMySQLConnection()
	return err
}

func (d *Dalga) newMySQLConnection() (*sql.DB, error) {
	db, err := sql.Open("mysql", d.Config.MySQL.DSN())
	if err != nil {
		return nil, err
	}

	err = db.Ping()
	if err != nil {
		return nil, err
	}

	log.Println("Connected to MySQL")
	return db, nil
}

func (d *Dalga) connectMQ() error {
	var err error

	d.rabbit, err = amqp.Dial(d.Config.RabbitMQ.URL())
	if err != nil {
		return err
	}

	d.channel, err = d.rabbit.Channel()
	log.Println("Connected to RabbitMQ")
	return err
}

func (d *Dalga) CreateTable() error {
	db, err := d.newMySQLConnection()
	if err != nil {
		return err
	}
	defer db.Close()

	sql := fmt.Sprintf(createTableSQL, d.Config.MySQL.Table)
	_, err = db.Exec(sql)
	if err != nil {
		return err
	}

	return nil
}

func (d *Dalga) Schedule(id, routingKey string, interval uint32) error {
	job := NewJob(id, routingKey, interval)

	err := d.insert(job)
	if err != nil {
		return err
	}

	// Wake up the publisher.
	//
	// publisher() may be sleeping for the next job on the queue
	// at the time we schedule a new Job. Let it wake up so it can
	// re-fetch the new Job from the front of the queue.
	//
	// The code below is an idiom for non-blocking send to a channel.
	select {
	case d.newJobs <- job:
		debug("Sent new job signal")
	default:
		debug("Did not send new job signal")
	}

	debug("Job is scheduled:", job)
	return nil
}

func (d *Dalga) Cancel(id, routingKey string) error {
	err := d.delete(id, routingKey)
	if err != nil {
		return err
	}

	job := Job{primaryKey: primaryKey{id, routingKey}}

	select {
	case d.canceledJobs <- &job:
		debug("Sent cancel signal")
	default:
		debug("Did not send cancel signal")
	}

	debug("Job is cancelled:", job)
	return nil
}

// front returns the first job to be run in the queue.
func (d *Dalga) front() (*Job, error) {
	var interval uint32
	var j Job

	row := d.db.QueryRow("SELECT id, routing_key, `interval`, next_run " +
		"FROM " + d.Config.MySQL.Table + " " +
		"ORDER BY next_run ASC LIMIT 1")

	err := row.Scan(&j.ID, &j.RoutingKey, &interval, &j.NextRun)
	if err != nil {
		return nil, err
	}

	j.Interval = time.Duration(interval) * time.Second
	return &j, nil
}

// publish sends a message to exchange defined in the config and
// updates the Job's next run time on the database.
func (d *Dalga) publish(j *Job) error {
	debug("publish", *j)

	// Update next run time
	_, err := d.db.Exec("UPDATE "+d.Config.MySQL.Table+" "+
		"SET next_run=? "+
		"WHERE id=? AND routing_key=?",
		time.Now().UTC().Add(j.Interval), j.ID, j.RoutingKey)
	if err != nil {
		return err
	}

	// Send a message to RabbitMQ
	err = d.channel.Publish(d.Config.RabbitMQ.Exchange, j.RoutingKey, false, false, amqp.Publishing{
		Headers: amqp.Table{
			"interval":     j.Interval.Seconds(),
			"published_at": time.Now().UTC().String(),
		},
		ContentType:  "application/octet-stream",
		Body:         []byte(j.ID),
		DeliveryMode: amqp.Persistent,
		Priority:     0,
		Expiration:   strconv.FormatUint(uint64(j.Interval.Seconds()), 10) + "000",
	})

	if err == nil { // Published successfully
		return nil
	}

	log.Println(err)

	// Revert next run time
	_, err = d.db.Exec("UPDATE "+d.Config.MySQL.Table+" "+
		"SET next_run=? "+
		"WHERE id=? AND routing_key=?",
		j.NextRun, j.ID, j.RoutingKey)
	return err
}

// insert puts the job to the waiting queue.
func (d *Dalga) insert(j *Job) error {
	interval := j.Interval.Seconds()
	_, err := d.db.Exec("INSERT INTO "+d.Config.MySQL.Table+" "+
		"(id, routing_key, `interval`, next_run) "+
		"VALUES(?, ?, ?, ?) "+
		"ON DUPLICATE KEY UPDATE "+
		"next_run=DATE_ADD(next_run, INTERVAL (? - `interval`) SECOND), "+
		"`interval`=?",
		j.ID, j.RoutingKey, interval, j.NextRun, interval, interval)
	return err
}

// delete removes the job from the waiting queue.
func (d *Dalga) delete(id, routingKey string) error {
	_, err := d.db.Exec("DELETE FROM "+d.Config.MySQL.Table+" "+
		"WHERE id=? AND routing_key=?", id, routingKey)
	return err
}

// publisher runs a loop that reads the next Job from the queue and publishes it.
func (d *Dalga) publisher() {
	publish := func(j *Job) {
		err := d.publish(j)
		if err != nil {
			log.Println(err)
			time.Sleep(time.Duration(1) * time.Second)
		}
	}

	for {
		debug("---")

		select {
		case <-d.quitPublisher:
			debug("Came message from channel 1: quitPublisher")
			goto end
		default:
		}

		job, err := d.front()
		if err != nil {
			if err != sql.ErrNoRows {
				log.Println(err)
				time.Sleep(time.Duration(1) * time.Second)
				continue
			} else if myErr, ok := err.(*mysql.MySQLError); ok && myErr.Number == 1146 { // Table doesn't exist
				log.Fatal(myErr)
			}

			debug("No waiting jobs in the queue")
			debug("Waiting for new job signal")
			select {
			case job = <-d.newJobs:
				debug("Got new job signal")
			case <-d.quitPublisher:
				debug("Came message from channel 2: quitPublisher")
				goto end
			}
		}

	checkNextRun:
		remaining := job.Remaining()
		debug("Next job:", job, "Remaining:", remaining)

		now := time.Now().UTC()
		if !job.NextRun.After(now) {
			publish(job)
			continue
		}

		// Wait until the next Job time or
		// the webserver's /schedule handler wakes us up
		debug("Sleeping for job:", remaining)
		select {
		case <-time.After(remaining):
			debug("Job sleep time finished")
			publish(job)
		case newJob := <-d.newJobs:
			debug("A new job has been scheduled")
			if newJob.NextRun.Before(job.NextRun) {
				debug("The new job comes before out current job")
				job = newJob // Process the new job next
			}
			// Continue processing the current job without fetching from database
			goto checkNextRun
		case canceledJob := <-d.canceledJobs:
			debug("A job has been cancelled")
			if job.Equal(canceledJob) {
				// The job we are waiting for has been canceled.
				// We need to fetch the next job in the queue.
				debug("The cancelled job is our current job")
				continue
			}
			// Continue to process our current job
			goto checkNextRun
		case <-d.quitPublisher:
			debug("Came message from channel 3: quitPublisher")
			goto end
		}
	}
end:
	d.publisherFinished <- true
}
<commit_msg>exit program when amqp disconnects<commit_after>package dalga

// TODO refactor publisher wait/notify
// TODO update readme

import (
	"database/sql"
	"flag"
	"fmt"
	"log"
	"net"
	"strconv"
	"time"

	"github.com/cenkalti/dalga/vendor/github.com/go-sql-driver/mysql"
	"github.com/cenkalti/dalga/vendor/github.com/streadway/amqp"
)

const createTableSQL = "" +
	"CREATE TABLE `%s` (" +
	"  `id`          VARCHAR(255)       NOT NULL," +
	"  `routing_key` VARCHAR(255)    NOT NULL," +
	"  `interval`    INT UNSIGNED    NOT NULL," +
	"  `next_run`    DATETIME        NOT NULL," +
	"" +
	"  PRIMARY KEY (`id`, `routing_key`)," +
	"  KEY `idx_next_run` (`next_run`)" +
	") ENGINE=InnoDB DEFAULT CHARSET=utf8"

var debugging = flag.Bool("debug", false, "turn on debug messages")

func debug(args ...interface{}) {
	if *debugging {
		log.Println(args...)
	}
}

type Dalga struct {
	Config            Config
	db                *sql.DB
	rabbit            *amqp.Connection
	channel           *amqp.Channel
	listener          net.Listener
	newJobs           chan *Job
	canceledJobs      chan *Job
	quitPublisher     chan bool
	publisherFinished chan bool
}

func New(config Config) *Dalga {
	return &Dalga{
		Config:            config,
		newJobs:           make(chan *Job),
		canceledJobs:      make(chan *Job),
		quitPublisher:     make(chan bool),
		publisherFinished: make(chan bool),
	}
}

// Start starts the publisher and http server goroutines.
func (d *Dalga) Start() error {
	if err := d.connectDB(); err != nil {
		return err
	}

	if err := d.connectMQ(); err != nil {
		return err
	}

	server, err := d.makeServer()
	if err != nil {
		return err
	}

	go d.publisher()
	go server()

	return nil
}

// Run starts the dalga and waits until Shutdown() is called.
func (d *Dalga) Run() error {
	err := d.Start()
	if err != nil {
		return err
	}

	debug("Waiting a message from publisherFinished channel")
	<-d.publisherFinished
	debug("Received message from publisherFinished channel")

	return nil
}

func (d *Dalga) Shutdown() error {
	// TODO stop goroutines
	return d.listener.Close()
}

func (d *Dalga) connectDB() error {
	var err error
	d.db, err = d.newMySQLConnection()
	return err
}

func (d *Dalga) newMySQLConnection() (*sql.DB, error) {
	db, err := sql.Open("mysql", d.Config.MySQL.DSN())
	if err != nil {
		return nil, err
	}

	err = db.Ping()
	if err != nil {
		return nil, err
	}

	log.Println("Connected to MySQL")
	return db, nil
}

func (d *Dalga) connectMQ() error {
	var err error

	d.rabbit, err = amqp.Dial(d.Config.RabbitMQ.URL())
	if err != nil {
		return err
	}

	d.channel, err = d.rabbit.Channel()
	if err != nil {
		return err
	}

	// Exit program when AMQP connection is closed.
	connectionClosed := make(chan *amqp.Error)
	d.rabbit.NotifyClose(connectionClosed)
	go func() {
		err, ok := <-connectionClosed
		if ok {
			log.Fatal(err)
		}
	}()

	log.Println("Connected to RabbitMQ")
	return err
}

func (d *Dalga) CreateTable() error {
	db, err := d.newMySQLConnection()
	if err != nil {
		return err
	}
	defer db.Close()

	sql := fmt.Sprintf(createTableSQL, d.Config.MySQL.Table)
	_, err = db.Exec(sql)
	return err
}

func (d *Dalga) Schedule(id, routingKey string, interval uint32) error {
	job := NewJob(id, routingKey, interval)

	err := d.insert(job)
	if err != nil {
		return err
	}

	// Wake up the publisher.
	//
	// publisher() may be sleeping for the next job on the queue
	// at the time we schedule a new Job. Let it wake up so it can
	// re-fetch the new Job from the front of the queue.
	//
	// The code below is an idiom for non-blocking send to a channel.
	select {
	case d.newJobs <- job:
		debug("Sent new job signal")
	default:
		debug("Did not send new job signal")
	}

	debug("Job is scheduled:", job)
	return nil
}

func (d *Dalga) Cancel(id, routingKey string) error {
	err := d.delete(id, routingKey)
	if err != nil {
		return err
	}

	job := Job{primaryKey: primaryKey{id, routingKey}}

	select {
	case d.canceledJobs <- &job:
		debug("Sent cancel signal")
	default:
		debug("Did not send cancel signal")
	}

	debug("Job is cancelled:", job)
	return nil
}

// front returns the first job to be run in the queue.
func (d *Dalga) front() (*Job, error) {
	var interval uint32
	var j Job

	row := d.db.QueryRow("SELECT id, routing_key, `interval`, next_run " +
		"FROM " + d.Config.MySQL.Table + " " +
		"ORDER BY next_run ASC LIMIT 1")

	err := row.Scan(&j.ID, &j.RoutingKey, &interval, &j.NextRun)
	if err != nil {
		return nil, err
	}

	j.Interval = time.Duration(interval) * time.Second
	return &j, nil
}

// publish sends a message to exchange defined in the config and
// updates the Job's next run time on the database.
func (d *Dalga) publish(j *Job) error {
	debug("publish", *j)

	// Update next run time
	_, err := d.db.Exec("UPDATE "+d.Config.MySQL.Table+" "+
		"SET next_run=? "+
		"WHERE id=? AND routing_key=?",
		time.Now().UTC().Add(j.Interval), j.ID, j.RoutingKey)
	if err != nil {
		return err
	}

	// Send a message to RabbitMQ
	err = d.channel.Publish(d.Config.RabbitMQ.Exchange, j.RoutingKey, true, false, amqp.Publishing{
		Body:         []byte(j.ID),
		DeliveryMode: amqp.Persistent,
		Expiration:   strconv.FormatFloat(j.Interval.Seconds(), 'f', 0, 64) + "000",
	})

	if err == nil { // Published successfully
		return nil
	}

	log.Println(err)

	// Revert next run time
	_, err = d.db.Exec("UPDATE "+d.Config.MySQL.Table+" "+
		"SET next_run=? "+
		"WHERE id=? AND routing_key=?",
		j.NextRun, j.ID, j.RoutingKey)
	return err
}

// insert puts the job to the waiting queue.
func (d *Dalga) insert(j *Job) error {
	interval := j.Interval.Seconds()
	_, err := d.db.Exec("INSERT INTO "+d.Config.MySQL.Table+" "+
		"(id, routing_key, `interval`, next_run) "+
		"VALUES(?, ?, ?, ?) "+
		"ON DUPLICATE KEY UPDATE "+
		"next_run=DATE_ADD(next_run, INTERVAL (? - `interval`) SECOND), "+
		"`interval`=?",
		j.ID, j.RoutingKey, interval, j.NextRun, interval, interval)
	return err
}

// delete removes the job from the waiting queue.
func (d *Dalga) delete(id, routingKey string) error {
	_, err := d.db.Exec("DELETE FROM "+d.Config.MySQL.Table+" "+
		"WHERE id=? AND routing_key=?", id, routingKey)
	return err
}

// publisher runs a loop that reads the next Job from the queue and publishes it.
func (d *Dalga) publisher() {
	publish := func(j *Job) {
		err := d.publish(j)
		if err != nil {
			log.Println(err)
			time.Sleep(time.Second)
		}
	}

	for {
		debug("---")

		select {
		case <-d.quitPublisher:
			debug("Came message from channel 1: quitPublisher")
			goto end
		default:
		}

		job, err := d.front()
		if err != nil {
			if err != sql.ErrNoRows {
				log.Println(err)
				time.Sleep(time.Second)
				continue
			} else if myErr, ok := err.(*mysql.MySQLError); ok && myErr.Number == 1146 { // Table doesn't exist
				log.Fatal(myErr)
			}

			debug("No waiting jobs in the queue")
			debug("Waiting for new job signal")
			select {
			case job = <-d.newJobs:
				debug("Got new job signal")
			case <-d.quitPublisher:
				debug("Came message from channel 2: quitPublisher")
				goto end
			}
		}

	checkNextRun:
		remaining := job.Remaining()
		debug("Next job:", job, "Remaining:", remaining)

		now := time.Now().UTC()
		if !job.NextRun.After(now) {
			publish(job)
			continue
		}

		// Wait until the next Job time or
		// the webserver's /schedule handler wakes us up
		debug("Sleeping for job:", remaining)
		select {
		case <-time.After(remaining):
			debug("Job sleep time finished")
			publish(job)
		case newJob := <-d.newJobs:
			debug("A new job has been scheduled")
			if newJob.NextRun.Before(job.NextRun) {
				debug("The new job comes before out current job")
				job = newJob // Process the new job next
			}
			// Continue processing the current job without fetching from database
			goto checkNextRun
		case canceledJob := <-d.canceledJobs:
			debug("A job has been cancelled")
			if job.Equal(canceledJob) {
				// The job we are waiting for has been canceled.
				// We need to fetch the next job in the queue.
				debug("The cancelled job is our current job")
				continue
			}
			// Continue to process our current job
			goto checkNextRun
		case <-d.quitPublisher:
			debug("Came message from channel 3: quitPublisher")
			goto end
		}
	}
end:
	d.publisherFinished <- true
}
<|endoftext|>


<commit_before>package daslog

import (
	"bytes"
	"testing"
)

// almost dummy test
// TODO : add tests
func TestMain(t *testing.T) {
	var buf bytes.Buffer

	o := Options{
		Destination: &buf,
		Prefix:      "{{.O}} [{{.Q}}]: ",
		LogLevel:    UrgencyLevelCritical,
	}

	l, err := New(o)
	if err != nil {
		t.Fatalf("%v", err)
	}

	// notice in Log style
	l.Log(UrgencyLevelNotice, "test notice message")

	// info
	l.Info("test info message")

	// error
	l.Errorf("%s %s %s", "test", "error", "message")
}
<commit_msg>added some tests<commit_after>package daslog

import (
	"bytes"
	"fmt"
	"io"
	"path/filepath"
	"runtime"
	"strings"
	"testing"
	"time"
)

func chkerr(e error, t *testing.T, m string) {
	if e != nil {
		_, fn, line, _ := runtime.Caller(1)
		fnb := filepath.Base(fn)
		t.Fatalf("%s\ncall: %s:%d\nerr : %v\n", m, fnb, line, e)
	}
}

type prfxTmplTest struct {
	E string  // expected
	O Options // options
}

type outTest struct {
	N string // name
	E string // expected
}

// TODO : add more tests
func TestMain(t *testing.T) {
	var buf bytes.Buffer

	l, err := New(Options{
		Destination: &buf,
		LogLevel:    UrgencyLevelCritical,
	})
	chkerr(err, t, "fail to init Daslog")

	l.Notice("Notice")
	l.Noticef("Noticef")
	l.Info("Info")
	l.Infof("Infof")
	l.Error("Error")
	l.Errorf("Errorf")
	l.Critical("Critical")
	l.Criticalf("Criticalf")

	outTests := []outTest{
		outTest{N: "Notice", E: "Notice"},
		outTest{N: "Noticef", E: "Noticef"},
		outTest{N: "Info", E: "Info"},
		outTest{N: "Infof", E: "Infof"},
		outTest{N: "Error", E: "Error"},
		outTest{N: "Errorf", E: "Errorf"},
		outTest{N: "Critical", E: "Critical"},
		outTest{N: "Criticalf", E: "Criticalf"},
	}

	outTestsBuf := strings.Split(buf.String(), "\n")

	for i, tst := range outTestsBuf {
		if i == len(outTestsBuf)-1 {
			continue
		}

		if tst != outTests[i].E {
			t.Fatalf("'%s' != '%s' (out test: #%d, name: %s)\n", outTests[i], tst, i, outTests[i].N)
		}
	}

	prfxTests := []prfxTmplTest{
		prfxTmplTest{
			E: time.Now().Local().Format("2006-01-02"),
			O: Options{Prefix: "{{.F}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("2006"),
			O: Options{Prefix: "{{.Y}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("06"),
			O: Options{Prefix: "{{.y}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("01"),
			O: Options{Prefix: "{{.m}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("Jan"),
			O: Options{Prefix: "{{.b}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("January"),
			O: Options{Prefix: "{{.B}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("02"),
			O: Options{Prefix: "{{.d}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("Mon"),
			O: Options{Prefix: "{{.a}} ", LogLevel: UrgencyLevelCritical},
		},
		prfxTmplTest{
			E: time.Now().Local().Format("PM"),
			O: Options{Prefix: "{{.p}} ", LogLevel: UrgencyLevelCritical},
		},
	}

	for i, tst := range prfxTests {
		buf.Reset()
		tst.O.Destination = &buf

		l2, e := New(tst.O)
		chkerr(e, t, fmt.Sprintf("fail to init Daslog (date test #%d)", i))

		l2.Info("test")

		out := strings.Replace(buf.String(), "\n", "", -1)
		exp := tst.E + " test"

		if out != exp {
			t.Fatalf("'%s' != '%s' (date test #%d)\n", out, exp, i)
		}
	}

	buf.Reset()
	var buf2 bytes.Buffer

	l3, err := New(Options{
		Destinations: []io.Writer{&buf, &buf2},
		LogLevel:     UrgencyLevelCritical,
	})
	chkerr(err, t, "fail to init Daslog")

	l3.Info("test")

	if buf.String() != "test\n" || buf2.String() != "test\n" {
		t.Fatalf("destinations test fail\n")
	}
}
<|endoftext|>


<commit_before>package backoff

import (
	"runtime"
	"time"
)

// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
//
// Usage:
// 	operation := func() error {
// 		// An operation that may fail
// 	}
//
//	b := backoff.NewExponentialBackOff()
//	ticker := backoff.NewTicker(b)
//
// 	var err error
//	for _ = range ticker.C {
//		if err = operation(); err != nil {
//			log.Println(err, "will retry...")
//			continue
//		}
//
//		break
//	}
//
// 	if err != nil {
// 		// Operation has failed.
// 	}
//
// 	// Operation is successfull.
//
type Ticker struct {
	C    <-chan time.Time
	c    chan time.Time
	b    BackOff
	stop chan struct{}
}

// NewTicker returns a new Ticker containing a channel that will send the time at times
// specified by the BackOff argument. Ticker is guaranteed to tick at least once.
// The channel is closed when Stop method is called or BackOff stops.
func NewTicker(b BackOff) *Ticker {
	c := make(chan time.Time)
	t := &Ticker{
		C:    c,
		c:    c,
		b:    b,
		stop: make(chan struct{}, 1),
	}
	go t.run()
	runtime.SetFinalizer(t, func(x *Ticker) { x.Stop() })
	return t
}

// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
	select {
	case t.stop <- struct{}{}:
	default:
	}
}

func (t *Ticker) run() {
	var next time.Duration
	var afterC <-chan time.Time

	defer close(t.c)

	first := make(chan time.Time, 1)
	first <- time.Now()

	for {
		var tick time.Time

		select {
		case tick = <-first:
			select {
			case t.c <- tick:
			case <-t.stop:
				return
			}
			t.b.Reset()
			next = t.b.NextBackOff()
			if next == Stop {
				return
			}
			afterC = time.After(next)
		case tick = <-afterC:
			select {
			case t.c <- tick:
			case <-t.stop:
				return
			}
			next = t.b.NextBackOff()
			if next == Stop {
				return
			}
			afterC = time.After(next)
		case <-t.stop:
			return
		}
	}
}
<commit_msg>refactor<commit_after>package backoff

import (
	"runtime"
	"time"
)

// Ticker holds a channel that delivers `ticks' of a clock at times reported by a BackOff.
//
// Usage:
// 	operation := func() error {
// 		// An operation that may fail
// 	}
//
//	b := backoff.NewExponentialBackOff()
//	ticker := backoff.NewTicker(b)
//
// 	var err error
//	for _ = range ticker.C {
//		if err = operation(); err != nil {
//			log.Println(err, "will retry...")
//			continue
//		}
//
//		break
//	}
//
// 	if err != nil {
// 		// Operation has failed.
// 	}
//
// 	// Operation is successfull.
//
type Ticker struct {
	C    <-chan time.Time
	c    chan time.Time
	b    BackOff
	stop chan struct{}
}

// NewTicker returns a new Ticker containing a channel that will send the time at times
// specified by the BackOff argument. Ticker is guaranteed to tick at least once.
// The channel is closed when Stop method is called or BackOff stops.
func NewTicker(b BackOff) *Ticker {
	c := make(chan time.Time)
	t := &Ticker{
		C:    c,
		c:    c,
		b:    b,
		stop: make(chan struct{}, 1),
	}
	go t.run()
	runtime.SetFinalizer(t, func(x *Ticker) { x.Stop() })
	return t
}

// Stop turns off a ticker. After Stop, no more ticks will be sent.
func (t *Ticker) Stop() {
	select {
	case t.stop <- struct{}{}:
	default:
	}
}

func (t *Ticker) run() {
	var (
		next   time.Duration
		afterC <-chan time.Time
		tick   time.Time
	)

	defer close(t.c)
	t.b.Reset()

	send := func() {
		select {
		case t.c <- tick:
		case <-t.stop:
			return
		}

		next = t.b.NextBackOff()
		if next == Stop {
			t.Stop()
			return
		}
		afterC = time.After(next)
	}

	send() // Ticker is guaranteed to tick at least once.
	for {
		select {
		case tick = <-afterC:
			send()
		case <-t.stop:
			return
		}
	}
}
<|endoftext|>


<commit_before>package hilbert

// Peano represents a 2D Peano curve of order N for mapping to and from.
// Implements SpaceFilling interface.
type Peano struct {
	N int // Always a power of three, and is the width/height of the space.
}

// isPow3 returns true if n is a power of 3.
func isPow3(n float64) bool {
	// I wanted to do the following, but due to subtle floating point issues it didn't work
	// const ln3 = 1.098612288668109691395245236922525704647490557822749451734694333637494 // https://oeis.org/A002391
	//return n == math.Pow(3, math.Trunc(math.Log(n) / ln3))
	for n >= 1 {
		if n == 1 {
			return true
		}
		n = n / 3
	}
	return false
}

// NewPeano returns a new Peano space filling curve which maps integers to and from the curve.
// n must be a power of three.
func NewPeano(n int) (*Peano, error) {
	if n <= 0 {
		return nil, ErrNotPositive
	}

	if !isPow3(float64(n)) {
		return nil, ErrNotPowerOfThree
	}

	return &Peano{
		N: n,
	}, nil
}

// GetDimensions returns the width and height of the 2D space.
func (p *Peano) GetDimensions() (int, int) {
	return p.N, p.N
}

// Map transforms a one dimension value, t, in the range [0, n^3-1] to coordinates on the Peano
// curve in the two-dimension space, where x and y are within [0,n-1].
func (p *Peano) Map(t int) (x, y int, err error) {
	if t < 0 || t >= p.N*p.N {
		return -1, -1, ErrOutOfRange
	}

	for i := 1; i < p.N; i = i * 3 {
		s := t % 9

		// rx/ry are the coordinates in the 3x3 grid
		rx := int(s / 3)
		ry := int(s % 3)
		if rx == 1 {
			ry = 2 - ry
		}

		// now based on depth rotate our points
		if i > 1 {
			x, y = p.rotate(i, x, y, s)
		}

		x += rx * i
		y += ry * i

		t /= 9
	}

	return x, y, nil
}

// rotate rotates the x and y coordinates depending on the current n depth.
func (p *Peano) rotate(n, x, y, s int) (int, int) {

	if n == 1 {
		// Special case
		return x, y
	}

	n = n - 1
	switch s {
	case 0:
		return x, y // normal
	case 1:
		return n - x, y // fliph
	case 2:
		return x, y // normal
	case 3:
		return x, n - y // flipv
	case 4:
		return n - x, n - y // flipv and fliph
	case 5:
		return x, n - y // flipv
	case 6:
		return x, y // normal
	case 7:
		return n - x, y // fliph
	case 8:
		return x, y // normal
	}

	panic("assertation failure: this line should never be reached")
}

// MapInverse transform coordinates on the Peano curve from (x,y) to t.
// NOTE IMPLEMENTED YET
func (p *Peano) MapInverse(x, y int) (t int, err error) {
	if x < 0 || x >= p.N || y < 0 || y >= p.N {
		return -1, ErrOutOfRange
	}

	panic("Not finished")
	return -1, nil
}
<commit_msg>Fixed typo.<commit_after>package hilbert

// Peano represents a 2D Peano curve of order N for mapping to and from.
// Implements SpaceFilling interface.
type Peano struct {
	N int // Always a power of three, and is the width/height of the space.
}

// isPow3 returns true if n is a power of 3.
func isPow3(n float64) bool {
	// I wanted to do the following, but due to subtle floating point issues it didn't work
	// const ln3 = 1.098612288668109691395245236922525704647490557822749451734694333637494 // https://oeis.org/A002391
	//return n == math.Pow(3, math.Trunc(math.Log(n) / ln3))
	for n >= 1 {
		if n == 1 {
			return true
		}
		n = n / 3
	}
	return false
}

// NewPeano returns a new Peano space filling curve which maps integers to and from the curve.
// n must be a power of three.
func NewPeano(n int) (*Peano, error) {
	if n <= 0 {
		return nil, ErrNotPositive
	}

	if !isPow3(float64(n)) {
		return nil, ErrNotPowerOfThree
	}

	return &Peano{
		N: n,
	}, nil
}

// GetDimensions returns the width and height of the 2D space.
func (p *Peano) GetDimensions() (int, int) {
	return p.N, p.N
}

// Map transforms a one dimension value, t, in the range [0, n^3-1] to coordinates on the Peano
// curve in the two-dimension space, where x and y are within [0,n-1].
func (p *Peano) Map(t int) (x, y int, err error) {
	if t < 0 || t >= p.N*p.N {
		return -1, -1, ErrOutOfRange
	}

	for i := 1; i < p.N; i = i * 3 {
		s := t % 9

		// rx/ry are the coordinates in the 3x3 grid
		rx := int(s / 3)
		ry := int(s % 3)
		if rx == 1 {
			ry = 2 - ry
		}

		// now based on depth rotate our points
		if i > 1 {
			x, y = p.rotate(i, x, y, s)
		}

		x += rx * i
		y += ry * i

		t /= 9
	}

	return x, y, nil
}

// rotate rotates the x and y coordinates depending on the current n depth.
func (p *Peano) rotate(n, x, y, s int) (int, int) {

	if n == 1 {
		// Special case
		return x, y
	}

	n = n - 1
	switch s {
	case 0:
		return x, y // normal
	case 1:
		return n - x, y // fliph
	case 2:
		return x, y // normal
	case 3:
		return x, n - y // flipv
	case 4:
		return n - x, n - y // flipv and fliph
	case 5:
		return x, n - y // flipv
	case 6:
		return x, y // normal
	case 7:
		return n - x, y // fliph
	case 8:
		return x, y // normal
	}

	panic("assertion failure: this line should never be reached")
}

// MapInverse transform coordinates on the Peano curve from (x,y) to t.
// NOTE IMPLEMENTED YET
func (p *Peano) MapInverse(x, y int) (t int, err error) {
	if x < 0 || x >= p.N || y < 0 || y >= p.N {
		return -1, ErrOutOfRange
	}

	panic("Not finished")
	return -1, nil
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package main

/*
#cgo LDFLAGS: -lxname
#include <xname.h>
*/
import "C"

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

type statusbarItem struct {
	Name   string            `yaml:"name"`
	Period int               `yaml:"period"`
	Type   string            `yaml:"type"`
	Args   map[string]string `yaml:"args"`
}

type gsdConfig struct {
	Separator string          `yaml:"separator"`
	Items     []statusbarItem `yaml:"items"`
}

var config gsdConfig
var statusLock sync.Mutex
var statusbar []string

func updateStatusbar(pos int, text string) {
	statusLock.Lock()
	defer statusLock.Unlock()

	statusbar[pos] = text
	C.xname(C.CString(strings.Join(statusbar, config.Separator)))
}

func spawnUpdater(pos, period int, args map[string]string, f func(map[string]string) string) {
	go func() {
		for {
			time.Sleep(time.Duration(period) * time.Second)
			updateStatusbar(pos, f(args))
		}
	}()
}

func timestamp(args map[string]string) string {
	return "placeholder"
}

func fileReader(args map[string]string) string {
	return "placeholder"
}

func main() {
	if len(os.Args) < 2 {
		log.Fatalln("Usage:", os.Args[0], "<configuration file>")
	}

	data, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		log.Fatalln("Error reading configuration file:", err)
	}

	if err := yaml.Unmarshal(data, &config); err != nil {
		log.Fatalln("Error parsing configuration file:", err)
	}

}
<commit_msg>We're totally not exploding now.<commit_after>// Copyright 2015 Robert S. Gerus. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package main

/*
#cgo LDFLAGS: -lxname
#include <xname.h>
*/
import "C"

import (
	"gopkg.in/yaml.v2"
	"io/ioutil"
	"log"
	"os"
	"strings"
	"sync"
	"time"
)

type statusbarItem struct {
	Name   string            `yaml:"name"`
	Period int               `yaml:"period"`
	Type   string            `yaml:"type"`
	Args   map[string]string `yaml:"args"`
}

type gsdConfig struct {
	Separator string          `yaml:"separator"`
	Items     []statusbarItem `yaml:"items"`
}

var config gsdConfig
var statusLock sync.Mutex
var statusbar []string

func updateStatusbar(pos int, text string) {
	statusLock.Lock()
	defer statusLock.Unlock()

	statusbar[pos] = text
	C.xname(C.CString(strings.Join(statusbar, config.Separator)))
}

func spawnUpdater(pos, period int, args map[string]string, f func(map[string]string) string) {
	go func() {
		for {
			time.Sleep(time.Duration(period) * time.Second)
			updateStatusbar(pos, f(args))
		}
	}()
}

func timestamp(args map[string]string) string {
	return "placeholderStamp"
}

func fileReader(args map[string]string) string {
	return "placeholderFile"
}

func main() {
	if len(os.Args) < 2 {
		log.Fatalln("Usage:", os.Args[0], "<configuration file>")
	}

	data, err := ioutil.ReadFile(os.Args[1])
	if err != nil {
		log.Fatalln("Error reading configuration file:", err)
	}

	if err := yaml.Unmarshal(data, &config); err != nil {
		log.Fatalln("Error parsing configuration file:", err)
	}

	statusbar = make([]string, len(config.Items))

	for i, item := range config.Items {
		spawnUpdater(i, item.Period, item.Args, fileReader)
	}

	time.Sleep(time.Duration(10) * time.Second)
}
<|endoftext|>


<commit_before>package utils

import (
	"fmt"
	"os"
	"time"
)

type timer struct {
	action     string
	start      time.Time
	depth      int
	duration   time.Duration
	subActions []*timer
}

func (t *timer) String() string {
	this := fmt.Sprintf("%.3fs %*s%s\n", t.duration.Seconds(), t.depth, "", t.action)
	for _, sub := range t.subActions {
		this += sub.String()
	}
	return this
}

var stack []*timer

// Start a timer, used for tracking time spent.
// Generally used with either defer, as in:
//  defer utils.Timeit("my func")()
// Which will track how much time is spent in your function. Or
// if you want to track the time spent in a function you are calling
// then you would use:
//  toc := utils.Timeit("anotherFunc()")
//  anotherFunc()
//  toc()
// This tracks nested calls by indenting the output, and will print out the
// full stack of timing when we reach the top of the stack.
func Timeit(action string) func() {
	cur := &timer{action: action, start: time.Now(), depth: len(stack)}
	if len(stack) != 0 {
		tip := stack[len(stack)-1]
		tip.subActions = append(tip.subActions, cur)
	}
	stack = append(stack, cur)
	return func() {
		cur.duration = time.Since(cur.start)
		if cur == stack[0] {
			fmt.Fprint(os.Stderr, cur)
			stack = nil
		} else {
			stack = stack[0 : len(stack)-1]
		}
	}
}
<commit_msg>handle non-linear pushing and popping of timeit calls<commit_after>package utils

import (
	"fmt"
	"os"
	"time"
)

type timer struct {
	action     string
	start      time.Time
	depth      int
	duration   time.Duration
	subActions []*timer
}

func (t *timer) String() string {
	this := fmt.Sprintf("%.3fs %*s%s\n", t.duration.Seconds(), t.depth, "", t.action)
	for _, sub := range t.subActions {
		this += sub.String()
	}
	return this
}

var stack []*timer

// Start a timer, used for tracking time spent.
// Generally used with either defer, as in:
//  defer utils.Timeit("my func")()
// Which will track how much time is spent in your function. Or
// if you want to track the time spent in a function you are calling
// then you would use:
//  toc := utils.Timeit("anotherFunc()")
//  anotherFunc()
//  toc()
// This tracks nested calls by indenting the output, and will print out the
// full stack of timing when we reach the top of the stack.
func Timeit(action string) func() {
	cur := &timer{action: action, start: time.Now(), depth: len(stack)}
	if len(stack) != 0 {
		tip := stack[len(stack)-1]
		tip.subActions = append(tip.subActions, cur)
	}
	stack = append(stack, cur)
	return func() {
		cur.duration = time.Since(cur.start)
		if len(stack) == 0 || cur == stack[0] {
			fmt.Fprint(os.Stderr, cur)
			stack = nil
		} else {
			stack = stack[0 : len(stack)-1]
		}
	}
}
<|endoftext|>