<commit_before>package cfg

import (
	"bytes"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Config is a goroutine safe configuration store, with a map of values
// set from a config Provider.
type Config struct {
	m  map[string]string
	mu sync.RWMutex
}

// Provider is implemented by the user to provide the configuration as a map.
// There are currently two Providers implemented, EnvProvider and MapProvider.
type Provider interface {
	Provide() (map[string]string, error)
}

//==============================================================================

// New populates a new Config from a Provider. It will return an error if there
// was any problem reading from the Provider.
func New(p Provider) (*Config, error) {
	m, err := p.Provide()
	if err != nil {
		return &Config{}, err
	}

	c := &Config{m: m}

	return c, nil
}

// Log returns a string to help with logging your configuration. It excludes
// any values whose key contains the string "PASS".
func (c *Config) Log() string {
	c.mu.RLock()
	defer c.mu.RUnlock()

	var buf bytes.Buffer
	for k, v := range c.m {
		if !strings.Contains(k, "PASS") {
			buf.WriteString(k + "=" + v + "\n")
		}
	}

	return buf.String()
}

// String returns the value of the given key as a string. It will return an
// error if key was not found.
func (c *Config) String(key string) (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return "", fmt.Errorf("Unknown key %s !", key)
	}

	return value, nil
}

// MustString returns the value of the given key as a string. It will panic if
// the key was not found.
func (c *Config) MustString(key string) string {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown key %s !", key))
	}

	return value
}

// SetString adds or modifies the configuration for the specified key and
// value.
func (c *Config) SetString(key string, value string) {
	c.mu.RLock()
	{
		c.m[key] = value
	}
	c.mu.RUnlock()
}

// Int returns the value of the given key as an int. It will return an error if
// the key was not found or the value can't be converted to an int.
func (c *Config) Int(key string) (int, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return 0, fmt.Errorf("Unknown Key %s !", key)
	}

	iv, err := strconv.Atoi(value)
	if err != nil {
		return 0, err
	}

	return iv, nil
}

// MustInt returns the value of the given key as an int. It will panic if the
// key was not found or the value can't be converted to an int.
func (c *Config) MustInt(key string) int {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	iv, err := strconv.Atoi(value)
	if err != nil {
		panic(fmt.Sprintf("Key %q value is not an int", key))
	}

	return iv
}

// SetInt adds or modifies the configuration for the specified key and value.
func (c *Config) SetInt(key string, value int) {
	c.mu.RLock()
	{
		c.m[key] = strconv.Itoa(value)
	}
	c.mu.RUnlock()
}

// Time returns the value of the given key as a Time. It will return an error
// if the key was not found or the value can't be converted to a Time.
func (c *Config) Time(key string) (time.Time, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return time.Time{}, fmt.Errorf("Unknown Key %s !", key)
	}

	tv, err := time.Parse(time.UnixDate, value)
	if err != nil {
		return tv, err
	}

	return tv, nil
}

// MustTime returns the value of the given key as a Time. It will panic if the
// key was not found or the value can't be converted to a Time.
func (c *Config) MustTime(key string) time.Time {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	tv, err := time.Parse(time.UnixDate, value)
	if err != nil {
		panic(fmt.Sprintf("Key %q value is not a Time", key))
	}

	return tv
}

// SetTime adds or modifies the configuration for the specified key and value.
func (c *Config) SetTime(key string, value time.Time) {
	c.mu.RLock()
	{
		c.m[key] = value.Format(time.UnixDate)
	}
	c.mu.RUnlock()
}

// Bool returns the bool value of a given key as a bool. It will return an
// error if the key was not found or the value can't be converted to a bool.
func (c *Config) Bool(key string) (bool, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return false, fmt.Errorf("Unknown Key %s !", key)
	}

	if value == "on" || value == "yes" {
		value = "true"
	} else if value == "off" || value == "no" {
		value = "false"
	}

	val, err := strconv.ParseBool(value)
	if err != nil {
		return false, err
	}

	return val, nil
}

// MustBool returns the bool value of a given key as a bool. It will panic if
// the key was not found or the value can't be converted to a bool.
func (c *Config) MustBool(key string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	if value == "on" || value == "yes" {
		value = "true"
	} else if value == "off" || value == "no" {
		value = "false"
	}

	val, err := strconv.ParseBool(value)
	if err != nil {
		return false
	}

	return val
}

// SetBool adds or modifies the configuration for the specified key and value.
func (c *Config) SetBool(key string, value bool) {
	str := "false"
	if value {
		str = "true"
	}

	c.mu.RLock()
	{
		c.m[key] = str
	}
	c.mu.RUnlock()
}

// URL returns the value of the given key as a URL. It will return an error if
// the key was not found or the value can't be converted to a URL.
func (c *Config) URL(key string) (*url.URL, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return nil, fmt.Errorf("Unknown Key %s !", key)
	}

	u, err := url.Parse(value)
	if err != nil {
		return u, err
	}

	return u, nil
}

// MustURL returns the value of the given key as a URL. It will panic if the
// key was not found or the value can't be converted to a URL.
func (c *Config) MustURL(key string) *url.URL {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	u, err := url.Parse(value)
	if err != nil {
		panic(fmt.Sprintf("Key %q value is not a URL", key))
	}

	return u
}

// SetURL adds or modifies the configuration for the specified key and value.
func (c *Config) SetURL(key string, value *url.URL) {
	c.mu.RLock()
	{
		c.m[key] = value.String()
	}
	c.mu.RUnlock()
}
<commit_msg>Minor change to return zero value on error.<commit_after>package cfg

import (
	"bytes"
	"fmt"
	"net/url"
	"strconv"
	"strings"
	"sync"
	"time"
)

// Config is a goroutine safe configuration store, with a map of values
// set from a config Provider.
type Config struct {
	m  map[string]string
	mu sync.RWMutex
}

// Provider is implemented by the user to provide the configuration as a map.
// There are currently two Providers implemented, EnvProvider and MapProvider.
type Provider interface {
	Provide() (map[string]string, error)
}

//==============================================================================

// New populates a new Config from a Provider. It will return an error if there
// was any problem reading from the Provider.
func New(p Provider) (*Config, error) {
	m, err := p.Provide()
	if err != nil {
		return nil, err
	}

	c := &Config{m: m}

	return c, nil
}

// Log returns a string to help with logging your configuration. It excludes
// any values whose key contains the string "PASS".
func (c *Config) Log() string {
	c.mu.RLock()
	defer c.mu.RUnlock()

	var buf bytes.Buffer
	for k, v := range c.m {
		if !strings.Contains(k, "PASS") {
			buf.WriteString(k + "=" + v + "\n")
		}
	}

	return buf.String()
}

// String returns the value of the given key as a string. It will return an
// error if key was not found.
func (c *Config) String(key string) (string, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return "", fmt.Errorf("Unknown key %s !", key)
	}

	return value, nil
}

// MustString returns the value of the given key as a string. It will panic if
// the key was not found.
func (c *Config) MustString(key string) string {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown key %s !", key))
	}

	return value
}

// SetString adds or modifies the configuration for the specified key and
// value.
func (c *Config) SetString(key string, value string) {
	c.mu.RLock()
	{
		c.m[key] = value
	}
	c.mu.RUnlock()
}

// Int returns the value of the given key as an int. It will return an error if
// the key was not found or the value can't be converted to an int.
func (c *Config) Int(key string) (int, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return 0, fmt.Errorf("Unknown Key %s !", key)
	}

	iv, err := strconv.Atoi(value)
	if err != nil {
		return 0, err
	}

	return iv, nil
}

// MustInt returns the value of the given key as an int. It will panic if the
// key was not found or the value can't be converted to an int.
func (c *Config) MustInt(key string) int {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	iv, err := strconv.Atoi(value)
	if err != nil {
		panic(fmt.Sprintf("Key %q value is not an int", key))
	}

	return iv
}

// SetInt adds or modifies the configuration for the specified key and value.
func (c *Config) SetInt(key string, value int) {
	c.mu.RLock()
	{
		c.m[key] = strconv.Itoa(value)
	}
	c.mu.RUnlock()
}

// Time returns the value of the given key as a Time. It will return an error
// if the key was not found or the value can't be converted to a Time.
func (c *Config) Time(key string) (time.Time, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return time.Time{}, fmt.Errorf("Unknown Key %s !", key)
	}

	tv, err := time.Parse(time.UnixDate, value)
	if err != nil {
		return tv, err
	}

	return tv, nil
}

// MustTime returns the value of the given key as a Time. It will panic if the
// key was not found or the value can't be converted to a Time.
func (c *Config) MustTime(key string) time.Time {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	tv, err := time.Parse(time.UnixDate, value)
	if err != nil {
		panic(fmt.Sprintf("Key %q value is not a Time", key))
	}

	return tv
}

// SetTime adds or modifies the configuration for the specified key and value.
func (c *Config) SetTime(key string, value time.Time) {
	c.mu.RLock()
	{
		c.m[key] = value.Format(time.UnixDate)
	}
	c.mu.RUnlock()
}

// Bool returns the bool value of a given key as a bool. It will return an
// error if the key was not found or the value can't be converted to a bool.
func (c *Config) Bool(key string) (bool, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return false, fmt.Errorf("Unknown Key %s !", key)
	}

	if value == "on" || value == "yes" {
		value = "true"
	} else if value == "off" || value == "no" {
		value = "false"
	}

	val, err := strconv.ParseBool(value)
	if err != nil {
		return false, err
	}

	return val, nil
}

// MustBool returns the bool value of a given key as a bool. It will panic if
// the key was not found or the value can't be converted to a bool.
func (c *Config) MustBool(key string) bool {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	if value == "on" || value == "yes" {
		value = "true"
	} else if value == "off" || value == "no" {
		value = "false"
	}

	val, err := strconv.ParseBool(value)
	if err != nil {
		return false
	}

	return val
}

// SetBool adds or modifies the configuration for the specified key and value.
func (c *Config) SetBool(key string, value bool) {
	str := "false"
	if value {
		str = "true"
	}

	c.mu.RLock()
	{
		c.m[key] = str
	}
	c.mu.RUnlock()
}

// URL returns the value of the given key as a URL. It will return an error if
// the key was not found or the value can't be converted to a URL.
func (c *Config) URL(key string) (*url.URL, error) {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		return nil, fmt.Errorf("Unknown Key %s !", key)
	}

	u, err := url.Parse(value)
	if err != nil {
		return u, err
	}

	return u, nil
}

// MustURL returns the value of the given key as a URL. It will panic if the
// key was not found or the value can't be converted to a URL.
func (c *Config) MustURL(key string) *url.URL {
	c.mu.RLock()
	defer c.mu.RUnlock()

	value, found := c.m[key]
	if !found {
		panic(fmt.Sprintf("Unknown Key %s !", key))
	}

	u, err := url.Parse(value)
	if err != nil {
		panic(fmt.Sprintf("Key %q value is not a URL", key))
	}

	return u
}

// SetURL adds or modifies the configuration for the specified key and value.
func (c *Config) SetURL(key string, value *url.URL) {
	c.mu.RLock()
	{
		c.m[key] = value.String()
	}
	c.mu.RUnlock()
}
<|endoftext|>


<commit_before>package services

import (
    "encoding/json"
)

// Git structs

type User struct {
    Name         string
    Email        string
    Username     string
    Display_name string
}

type GitRepository struct {
    Id          int
    Name        string
    Full_name   string
    Url         string
    AbsoluteUrl string
    Owner       User
    Pusher      User
}

type GitCommit struct {
    Id        string
    Message   string
    Timestamp string
    Url       string
    Author    User
    Committer User
    Modified  []string
}

type GitPayload struct {
    Ref        string
    Compare    string
    Repository GitRepository
    Commits    []GitCommit
}

// Return github data.
func getGithubData(decoder *json.Decoder, header string) (string, string) {
    var gEvent GitPayload
    decoder.Decode(&gEvent)
    event := gEvent.Repository.Name + " --> " + header + " event"
    repo := gEvent.Repository
    desc := repo.Name + ": \n" +
        "\nName: " + repo.Name +
        "\nUrl: " + repo.Url +
        "\nOwner: " + repo.Owner.Email +
        "\nCompare: " + gEvent.Compare +
        "\nRef: " + gEvent.Ref +
        "\nModified files\n"
    for i := 0; i < len(gEvent.Commits); i++ {
        commit := gEvent.Commits[i]
        desc += "\n* " + commit.Message + " (" + commit.Timestamp + ")"
        for j := 0; j < len(commit.Modified); j++ {
            desc += "\n * " + commit.Modified[j]
        }
    }
    return event, desc
}
<commit_msg>pull request start<commit_after>package services

import (
    "encoding/json"
)

// Git structs

type User struct {
    Name         string
    Email        string
    Username     string
    Display_name string
}

type GitRepository struct {
    Id          int
    Name        string
    Full_name   string
    Url         string
    AbsoluteUrl string
    Owner       User
    Pusher      User
}

type GitCommit struct {
    Id        string
    Message   string
    Timestamp string
    Url       string
    Author    User
    Committer User
    Modified  []string
}

type GitUser struct {
    Login string
    Id int
    Avatar_url string
    Type string
    Site_admin bool
}

type GitPullRequest struct {
    Url string
    Id int
    State string
    Title string
    User GitUser
    Body string
    Repo GitRepository
}

type GitPayload struct {
    Ref        string
    Compare    string
    Repository GitRepository
    Commits    []GitCommit
    Action string
    Number int
    Pull_request GitPullRequest
}

// Return github data.
func getGithubData(decoder *json.Decoder, header string) (string, string) {
    var gEvent GitPayload
    decoder.Decode(&gEvent)
    event := gEvent.Repository.Name + " --> " + header + " event"
    repo := gEvent.Repository
    desc := repo.Name + ": \n" +
        "\nName: " + repo.Name +
        "\nUrl: " + repo.Url +
        "\nOwner: " + repo.Owner.Email +
        "\nCompare: " + gEvent.Compare +
        "\nRef: " + gEvent.Ref +
        "\nModified files\n"
    for i := 0; i < len(gEvent.Commits); i++ {
        commit := gEvent.Commits[i]
        desc += "\n* " + commit.Message + " (" + commit.Timestamp + ")"
        for j := 0; j < len(commit.Modified); j++ {
            desc += "\n * " + commit.Modified[j]
        }
    }
    return event, desc
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"github.com/mitchellh/packer/packer"
	"time"
)

type Builder struct {
	config config
	cancel chan int
	done   bool
	ui     packer.Ui
}

type config struct {
	period   float64
	duration float64
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
	b.config.period = 1
	b.config.duration = 5

	for _, e := range raws {
		var m map[string]interface{} = *e.(*map[string]interface{})

		if v, ok := m["period"]; ok {
			b.config.period = v.(float64)
		}

		if v, ok := m["duration"]; ok {
			b.config.duration = v.(float64)
		}
	}

	b.cancel = make(chan int)

	return nil, nil
}

func (b *Builder) Run(ui packer.Ui, _ packer.Hook, _ packer.Cache) (packer.Artifact, error) {
	ui.Say(fmt.Sprintf("Running(%3.0f, %3.0f)...", b.config.period, b.config.duration))

	b.ui = ui

	tick := time.Tick(time.Duration(b.config.period) * time.Second)
	stop := time.After(time.Duration(b.config.duration) * time.Second)
	start := time.Now()

	for !b.done {
		select {
		case <-tick:
			ui.Say(fmt.Sprintf("Building... %s", time.Since(start)))
		case <-stop:
			ui.Say("Done! Stopping...")
			b.done = true
		case <-b.cancel:
			ui.Say("Cancelled! Stopping...")
			b.done = true
		}
	}

	return nil, nil
}

func (b *Builder) Cancel() {
	if b.done {
		return
	}

	b.ui.Say("Cancelling...")
	b.cancel <- 1
}
<commit_msg>Update Prepare; buffer cancel semaphore channel<commit_after>package main

import (
	"fmt"
	"github.com/mitchellh/packer/packer"
	"time"
)

type Builder struct {
	config config
	cancel chan int
	done   bool
	ui     packer.Ui
}

type config struct {
	period   float64
	duration float64
}

func (b *Builder) Prepare(raws ...interface{}) ([]string, error) {
	b.config.period = 1
	b.config.duration = 5

	for _, e := range raws {
		var m map[string]interface{} = *e.(*map[string]interface{})

		if v, ok := m["period"]; ok {
			b.config.period = v.(float64)
		}

		if v, ok := m["duration"]; ok {
			b.config.duration = v.(float64)
		}
	}

	b.cancel = make(chan int, 1)

	return nil, nil
}

func (b *Builder) Run(ui packer.Ui, _ packer.Hook, _ packer.Cache) (packer.Artifact, error) {
	ui.Say(fmt.Sprintf("Running(%3.0f, %3.0f)...", b.config.period, b.config.duration))

	b.ui = ui

	tick := time.Tick(time.Duration(b.config.period) * time.Second)
	stop := time.After(time.Duration(b.config.duration) * time.Second)
	start := time.Now()

	for !b.done {
		select {
		case <-tick:
			ui.Say(fmt.Sprintf("Building... %s", time.Since(start)))
		case <-stop:
			ui.Say("Done! Stopping...")
			b.done = true
		case <-b.cancel:
			ui.Say("Cancelled! Stopping...")
			b.done = true
		}
	}

	return nil, nil
}

func (b *Builder) Cancel() {
	if b.done {
		return
	}

	b.ui.Say("Cancelling...")
	b.cancel <- 1
}
<|endoftext|>


<commit_before>package sfcli

import (
	"errors"
	"fmt"

	"github.com/alecthomas/units"
	"github.com/codegangsta/cli"
	"github.com/solidfire/solidfire-docker-driver/sfapi"
	"strconv"
	"strings"
)

var (
	volumeCmd = cli.Command{
		Name:  "volume",
		Usage: "volume related commands",
		Subcommands: []cli.Command{
			volumeCreateCmd,
			volumeCloneCmd,
			volumeDeleteCmd,
			volumeListCmd,
			volumeAttachCmd,
			volumeDetachCmd,
			volumeAddToVag,
			volumeRollbackCmd,
		},
	}

	volumeCreateCmd = cli.Command{
		Name:  "create",
		Usage: "create a new volume: `create [options] NAME`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "size",
				Usage: "size of volume in bytes or GiB: `[--size 1073741824|1GiB]`",
			},
			cli.StringFlag{
				Name:  "account",
				Usage: "account id to assign volume: `[--account 488]`",
			},
			cli.StringFlag{
				Name:  "qos",
				Usage: "min,max and burst qos settings for new volume: `[--qos 1000,5000,15000]`",
			},
			cli.StringFlag{
				Name:  "vag",
				Usage: "Volume Access Group to add volume to on create: `[--vag 8]`",
			},
			cli.StringFlag{
				Name:  "type",
				Usage: "Specify a volume type as defined in a SolidFire config file: `[--type Gold]`",
			},
		},
		Action: cmdVolumeCreate,
	}

	volumeCloneCmd = cli.Command{
		Name:   "clone",
		Usage:  "create a clone of an existing volume: `clone [options] EXISTING_VOLID NAME`",
		Action: cmdVolumeClone,
	}

	volumeRollbackCmd = cli.Command{
		Name:   "rollback",
		Usage:  "rollback a volume to a previously taken snapshot `rollback [options] VOLUME_ID SNAPSHOT_ID`",
		Action: cmdVolumeRollback,
	}

	volumeDeleteCmd = cli.Command{
		Name:  "delete",
		Usage: "delete an existing volume: `delete VOLUME-ID`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "range",
				Value: "",
				Usage: ": deletes a range of volume ID's `[--range <startID-endID>]`",
			},
		},
		Action: cmdVolumeDelete,
	}

	volumeListCmd = cli.Command{
		Name:  "list",
		Usage: "list existing volumes: `list`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "startID, s",
				Value: "",
				Usage: ": list a range of volume ID's `[--startid <startID>]`",
			},
			cli.StringFlag{
				Name:  "limit, l",
				Value: "",
				Usage: ": limit the number of volumes returned to `[--limit <limit>]`",
			},
			cli.StringFlag{
				Name:  "account, a",
				Value: "",
				Usage: ": only retrieve volumes for the specified accountID `[--account <accountID>]` (not compatible with other options)",
			},
		},
		Action: cmdVolumeList,
	}

	volumeAttachCmd = cli.Command{
		Name:  "attach",
		Usage: "iscsi attach volume to host (requires permissions and iscsiadm): `attach VOLUME-ID`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "iface",
				Usage: "Device file of iSCSI interface (initiator): `[--iface /dev/p1p2]`",
			},
		},
		Action: cmdVolumeAttach,
	}

	volumeDetachCmd = cli.Command{
		Name:   "detach",
		Usage:  "iscsi detach volume from host (requires permissions and iscsiadm): `detach VOLUME-ID`",
		Action: cmdVolumeDetach,
	}

	volumeAddToVag = cli.Command{
		Name:   "addtovag",
		Usage:  "Add existing Volume to existing Volume Access Group: `addtovag VOLUME-ID VAG-ID`",
		Action: cmdVolumeAddToVag,
	}
)

func cmdVolumeAttach(c *cli.Context) (err error) {
	id := c.Args().First()
	volID, _ := strconv.ParseInt(id, 10, 64)
	v, err := client.GetVolumeByID(volID)
	if err != nil {
		err = errors.New("Failed to find volume for attach")
		return err
	}
	netDev := c.String("iface")
	if c.String("iface") == "" {
		netDev = "default"
	}
	path, device, err := client.AttachVolume(&v, netDev)
	if err != nil {
		fmt.Println("Error encountered while performing iSCSI attach on Volume: ", volID)
		fmt.Println(err)
		return err

	}

	fmt.Println("-------------------------------------------")
	fmt.Println("Succesfully iSCSI Attached Volume:")
	fmt.Println("-------------------------------------------")
	fmt.Println("ID:         ", volID)
	fmt.Println("Path:       ", path)
	fmt.Println("Device:     ", device)
	fmt.Println("-------------------------------------------")
	return err

}

func cmdVolumeDetach(c *cli.Context) (err error) {
	id := c.Args().First()
	volID, _ := strconv.ParseInt(id, 10, 64)
	v, err := client.GetVolumeByID(volID)
	err = client.DetachVolume(v)
	if err != nil {
		fmt.Println("Error encountered while performing iSCSI detach of Volume: ", volID)
		fmt.Println(err)
		return err

	}
	return err
}

func cmdVolumeAddToVag(c *cli.Context) (err error) {
	vID, _ := strconv.ParseInt(c.Args().First(), 10, 64)
	vagID, _ := strconv.ParseInt(c.Args()[1], 10, 64)

	var volIDs []int64
	volIDs = append(volIDs, vID)
	err = client.AddVolumeToAccessGroup(vagID, volIDs)
	if err != nil {
		fmt.Printf("Failed to add volume to VAG ID: %d\n", vagID)
		return err
	}
	fmt.Printf("Succesfully added volume to VAG ID: %d\n", vagID)
	return err
}

func cmdVolumeRollback(c *cli.Context) (err error) {
	var req sfapi.RollbackToSnapshotRequest
	vid, _ := strconv.ParseInt(c.Args().First(), 10, 64)
	sid, _ := strconv.ParseInt(c.Args()[1], 10, 64)

	req.VolumeID = vid
	req.SnapshotID = sid
	_, err = client.RollbackToSnapshot(&req)
	if err != nil {
		fmt.Errorf("failed rollback to snapshot: %+v\n", err)
	}
	return err
}

func cmdVolumeClone(c *cli.Context) (err error) {
	var req sfapi.CloneVolumeRequest
	id, _ := strconv.ParseInt(c.Args().First(), 10, 64)
	name := c.Args()[1]
	if id == 0 || name == "" {
		fmt.Printf("Error, missing arguments to clone cmd")
		// TODO(jdg): Create error object
		return err
	}
	req.VolumeID = id
	req.Name = name
	v, err := client.CloneVolume(&req)
	if err != nil {
		fmt.Println("Error cloning volume: ", err)
		return err
	}
	fmt.Println("-------------------------------------------")
	fmt.Println("Succesfully Cloned Volume:")
	fmt.Println("-------------------------------------------")
	fmt.Println("ID:         ", v.VolumeID)
	fmt.Println("Name:       ", v.Name)
	fmt.Println("Size (GiB): ", v.TotalSize/int64(units.GiB))
	fmt.Println("QoS :       ", "minIOPS:", v.Qos.MinIOPS, "maxIOPS:", v.Qos.MaxIOPS, "burstIOPS:", v.Qos.BurstIOPS)
	fmt.Println("Account:    ", v.AccountID)
	fmt.Println("-------------------------------------------")
	return err
}

func cmdVolumeCreate(c *cli.Context) (err error) {
	var req sfapi.CreateVolumeRequest
	var qos sfapi.QoS
	req.Name = c.Args().First()

	sz := int64(0)
	if c.String("size") == "" && client.DefaultVolSize != 0 {
		sz = client.DefaultVolSize
	} else if c.String("size") != "" {
		sz, _ = units.ParseStrictBytes(c.String("size"))
	} else {
		fmt.Println("You must specify size for volumeCreate")
		return
	}

	account := int64(0)
	if c.String("account") == "" && client.DefaultAccountID != 0 {
		account = client.DefaultAccountID
	} else if c.String("account") != "" {
		account, _ = strconv.ParseInt(c.String("account"), 10, 64)
	} else {
		fmt.Println("You must specify an account for volumeCreate")
		return
	}

	req.TotalSize = sz
	req.AccountID = account
	if c.String("qos") != "" {
		iops := strings.Split(c.String("qos"), ",")
		qos.MinIOPS, _ = strconv.ParseInt(iops[0], 10, 64)
		qos.MaxIOPS, _ = strconv.ParseInt(iops[2], 10, 64)
		qos.BurstIOPS, _ = strconv.ParseInt(iops[2], 10, 64)
		req.Qos = qos
	} else if c.String("type") != "" {
		for _, t := range *client.Config.Types {
			if t.Type == c.String("type") {
				req.Qos = t.QOS
			}
		}
	} else {
	}

	v, err := client.CreateVolume(&req)
	if err != nil {
		fmt.Println("Error creating volume: ", err)
	}
	fmt.Println("-------------------------------------------")
	fmt.Println("Succesfully Created Volume:")
	fmt.Println("-------------------------------------------")
	fmt.Println("ID:         ", v.VolumeID)
	fmt.Println("Name:       ", v.Name)
	fmt.Println("Size (GiB): ", v.TotalSize/int64(units.GiB))
	fmt.Println("QoS :       ", "minIOPS:", v.Qos.MinIOPS, "maxIOPS:", v.Qos.MaxIOPS, "burstIOPS:", v.Qos.BurstIOPS)
	fmt.Println("Account:    ", v.AccountID)
	fmt.Println("-------------------------------------------")

	if c.String("vag") != "" {
		vagID, _ := strconv.ParseInt(c.String("vag"), 10, 64)
		var volIDs []int64
		volIDs = append(volIDs, v.VolumeID)
		err := client.AddVolumeToAccessGroup(vagID, volIDs)
		if err != nil {
			fmt.Printf("Failed to add volume to VAG ID: %d\n", vagID)
			return err
		}
		fmt.Printf("Succesfully added volume to VAG ID: %d\n", vagID)
	}
	return nil
}

func cmdVolumeDelete(c *cli.Context) (err error) {
	volumes := c.String("range")
	if volumes != "" {
		ids := strings.Split(volumes, "-")
		fmt.Println("You've selected to delete volumes: ", ids[0], " through ", ids[1])
		fmt.Print("Are you sure you want to do this [yes/no]: ")
		if confirm() {
			startID, _ := strconv.ParseInt(ids[0], 10, 64)
			endID, _ := strconv.ParseInt(ids[1], 10, 64)
			client.DeleteRange(startID, endID)
		}

	} else {
		for _, arg := range c.Args() {
			vID, _ := strconv.ParseInt(arg, 10, 64)
			client.DeleteVolume(vID)
		}
	}
	return nil
}

func listForAccount(acctID int64) (vols []sfapi.Volume, err error) {
	var req sfapi.ListVolumesForAccountRequest
	req.AccountID = acctID
	return client.ListVolumesForAccount(&req)
}

func listActiveVolumes(req sfapi.ListActiveVolumesRequest) (vols []sfapi.Volume, err error) {
	return client.ListActiveVolumes(&req)
}

func cmdVolumeList(c *cli.Context) (err error) {
	var req sfapi.ListActiveVolumesRequest
	var volumes []sfapi.Volume

	if c.String("account") != "" {
		acctID, _ := strconv.ParseInt(c.String("account"), 10, 64)
		volumes, err = listForAccount(acctID)
	} else {
		if c.String("startID") != "" {
			stID, _ := strconv.ParseInt(c.String("startID"), 10, 64)
			req.StartVolumeID = stID
		}
		if c.String("limit") != "" {
			limit, _ := strconv.ParseInt(c.String("limit"), 10, 64)
			req.Limit = limit
		}
		volumes, err = client.ListActiveVolumes(&req)
	}

	if err != nil {
		fmt.Println(err)
	} else {
		printVolList(volumes)
	}
	return err

}
<commit_msg>Spellcheck<commit_after>package sfcli

import (
	"errors"
	"fmt"

	"github.com/alecthomas/units"
	"github.com/codegangsta/cli"
	"github.com/solidfire/solidfire-docker-driver/sfapi"
	"strconv"
	"strings"
)

var (
	volumeCmd = cli.Command{
		Name:  "volume",
		Usage: "volume related commands",
		Subcommands: []cli.Command{
			volumeCreateCmd,
			volumeCloneCmd,
			volumeDeleteCmd,
			volumeListCmd,
			volumeAttachCmd,
			volumeDetachCmd,
			volumeAddToVag,
			volumeRollbackCmd,
		},
	}

	volumeCreateCmd = cli.Command{
		Name:  "create",
		Usage: "create a new volume: `create [options] NAME`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "size",
				Usage: "size of volume in bytes or GiB: `[--size 1073741824|1GiB]`",
			},
			cli.StringFlag{
				Name:  "account",
				Usage: "account id to assign volume: `[--account 488]`",
			},
			cli.StringFlag{
				Name:  "qos",
				Usage: "min,max and burst qos settings for new volume: `[--qos 1000,5000,15000]`",
			},
			cli.StringFlag{
				Name:  "vag",
				Usage: "Volume Access Group to add volume to on create: `[--vag 8]`",
			},
			cli.StringFlag{
				Name:  "type",
				Usage: "Specify a volume type as defined in a SolidFire config file: `[--type Gold]`",
			},
		},
		Action: cmdVolumeCreate,
	}

	volumeCloneCmd = cli.Command{
		Name:   "clone",
		Usage:  "create a clone of an existing volume: `clone [options] EXISTING_VOLID NAME`",
		Action: cmdVolumeClone,
	}

	volumeRollbackCmd = cli.Command{
		Name:   "rollback",
		Usage:  "rollback a volume to a previously taken snapshot `rollback [options] VOLUME_ID SNAPSHOT_ID`",
		Action: cmdVolumeRollback,
	}

	volumeDeleteCmd = cli.Command{
		Name:  "delete",
		Usage: "delete an existing volume: `delete VOLUME-ID`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "range",
				Value: "",
				Usage: ": deletes a range of volume ID's `[--range <startID-endID>]`",
			},
		},
		Action: cmdVolumeDelete,
	}

	volumeListCmd = cli.Command{
		Name:  "list",
		Usage: "list existing volumes: `list`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "startID, s",
				Value: "",
				Usage: ": list a range of volume ID's `[--startid <startID>]`",
			},
			cli.StringFlag{
				Name:  "limit, l",
				Value: "",
				Usage: ": limit the number of volumes returned to `[--limit <limit>]`",
			},
			cli.StringFlag{
				Name:  "account, a",
				Value: "",
				Usage: ": only retrieve volumes for the specified accountID `[--account <accountID>]` (not compatible with other options)",
			},
		},
		Action: cmdVolumeList,
	}

	volumeAttachCmd = cli.Command{
		Name:  "attach",
		Usage: "iscsi attach volume to host (requires permissions and iscsiadm): `attach VOLUME-ID`",
		Flags: []cli.Flag{
			cli.StringFlag{
				Name:  "iface",
				Usage: "Device file of iSCSI interface (initiator): `[--iface /dev/p1p2]`",
			},
		},
		Action: cmdVolumeAttach,
	}

	volumeDetachCmd = cli.Command{
		Name:   "detach",
		Usage:  "iscsi detach volume from host (requires permissions and iscsiadm): `detach VOLUME-ID`",
		Action: cmdVolumeDetach,
	}

	volumeAddToVag = cli.Command{
		Name:   "addtovag",
		Usage:  "Add existing Volume to existing Volume Access Group: `addtovag VOLUME-ID VAG-ID`",
		Action: cmdVolumeAddToVag,
	}
)

func cmdVolumeAttach(c *cli.Context) (err error) {
	id := c.Args().First()
	volID, _ := strconv.ParseInt(id, 10, 64)
	v, err := client.GetVolumeByID(volID)
	if err != nil {
		err = errors.New("Failed to find volume for attach")
		return err
	}
	netDev := c.String("iface")
	if c.String("iface") == "" {
		netDev = "default"
	}
	path, device, err := client.AttachVolume(&v, netDev)
	if err != nil {
		fmt.Println("Error encountered while performing iSCSI attach on Volume: ", volID)
		fmt.Println(err)
		return err

	}

	fmt.Println("-------------------------------------------")
	fmt.Println("Successfully iSCSI Attached Volume:")
	fmt.Println("-------------------------------------------")
	fmt.Println("ID:         ", volID)
	fmt.Println("Path:       ", path)
	fmt.Println("Device:     ", device)
	fmt.Println("-------------------------------------------")
	return err

}

func cmdVolumeDetach(c *cli.Context) (err error) {
	id := c.Args().First()
	volID, _ := strconv.ParseInt(id, 10, 64)
	v, err := client.GetVolumeByID(volID)
	err = client.DetachVolume(v)
	if err != nil {
		fmt.Println("Error encountered while performing iSCSI detach of Volume: ", volID)
		fmt.Println(err)
		return err

	}
	return err
}

func cmdVolumeAddToVag(c *cli.Context) (err error) {
	vID, _ := strconv.ParseInt(c.Args().First(), 10, 64)
	vagID, _ := strconv.ParseInt(c.Args()[1], 10, 64)

	var volIDs []int64
	volIDs = append(volIDs, vID)
	err = client.AddVolumeToAccessGroup(vagID, volIDs)
	if err != nil {
		fmt.Printf("Failed to add volume to VAG ID: %d\n", vagID)
		return err
	}
	fmt.Printf("Successfully added volume to VAG ID: %d\n", vagID)
	return err
}

func cmdVolumeRollback(c *cli.Context) (err error) {
	var req sfapi.RollbackToSnapshotRequest
	vid, _ := strconv.ParseInt(c.Args().First(), 10, 64)
	sid, _ := strconv.ParseInt(c.Args()[1], 10, 64)

	req.VolumeID = vid
	req.SnapshotID = sid
	_, err = client.RollbackToSnapshot(&req)
	if err != nil {
		fmt.Errorf("failed rollback to snapshot: %+v\n", err)
	}
	return err
}

func cmdVolumeClone(c *cli.Context) (err error) {
	var req sfapi.CloneVolumeRequest
	id, _ := strconv.ParseInt(c.Args().First(), 10, 64)
	name := c.Args()[1]
	if id == 0 || name == "" {
		fmt.Printf("Error, missing arguments to clone cmd")
		// TODO(jdg): Create error object
		return err
	}
	req.VolumeID = id
	req.Name = name
	v, err := client.CloneVolume(&req)
	if err != nil {
		fmt.Println("Error cloning volume: ", err)
		return err
	}
	fmt.Println("-------------------------------------------")
	fmt.Println("Successfully Cloned Volume:")
	fmt.Println("-------------------------------------------")
	fmt.Println("ID:         ", v.VolumeID)
	fmt.Println("Name:       ", v.Name)
	fmt.Println("Size (GiB): ", v.TotalSize/int64(units.GiB))
	fmt.Println("QoS :       ", "minIOPS:", v.Qos.MinIOPS, "maxIOPS:", v.Qos.MaxIOPS, "burstIOPS:", v.Qos.BurstIOPS)
	fmt.Println("Account:    ", v.AccountID)
	fmt.Println("-------------------------------------------")
	return err
}

func cmdVolumeCreate(c *cli.Context) (err error) {
	var req sfapi.CreateVolumeRequest
	var qos sfapi.QoS
	req.Name = c.Args().First()

	sz := int64(0)
	if c.String("size") == "" && client.DefaultVolSize != 0 {
		sz = client.DefaultVolSize
	} else if c.String("size") != "" {
		sz, _ = units.ParseStrictBytes(c.String("size"))
	} else {
		fmt.Println("You must specify size for volumeCreate")
		return
	}

	account := int64(0)
	if c.String("account") == "" && client.DefaultAccountID != 0 {
		account = client.DefaultAccountID
	} else if c.String("account") != "" {
		account, _ = strconv.ParseInt(c.String("account"), 10, 64)
	} else {
		fmt.Println("You must specify an account for volumeCreate")
		return
	}

	req.TotalSize = sz
	req.AccountID = account
	if c.String("qos") != "" {
		iops := strings.Split(c.String("qos"), ",")
		qos.MinIOPS, _ = strconv.ParseInt(iops[0], 10, 64)
		qos.MaxIOPS, _ = strconv.ParseInt(iops[2], 10, 64)
		qos.BurstIOPS, _ = strconv.ParseInt(iops[2], 10, 64)
		req.Qos = qos
	} else if c.String("type") != "" {
		for _, t := range *client.Config.Types {
			if t.Type == c.String("type") {
				req.Qos = t.QOS
			}
		}
	} else {
	}

	v, err := client.CreateVolume(&req)
	if err != nil {
		fmt.Println("Error creating volume: ", err)
	}
	fmt.Println("-------------------------------------------")
	fmt.Println("Successfully Created Volume:")
	fmt.Println("-------------------------------------------")
	fmt.Println("ID:         ", v.VolumeID)
	fmt.Println("Name:       ", v.Name)
	fmt.Println("Size (GiB): ", v.TotalSize/int64(units.GiB))
	fmt.Println("QoS :       ", "minIOPS:", v.Qos.MinIOPS, "maxIOPS:", v.Qos.MaxIOPS, "burstIOPS:", v.Qos.BurstIOPS)
	fmt.Println("Account:    ", v.AccountID)
	fmt.Println("-------------------------------------------")

	if c.String("vag") != "" {
		vagID, _ := strconv.ParseInt(c.String("vag"), 10, 64)
		var volIDs []int64
		volIDs = append(volIDs, v.VolumeID)
		err := client.AddVolumeToAccessGroup(vagID, volIDs)
		if err != nil {
			fmt.Printf("Failed to add volume to VAG ID: %d\n", vagID)
			return err
		}
		fmt.Printf("Successfully added volume to VAG ID: %d\n", vagID)
	}
	return nil
}

func cmdVolumeDelete(c *cli.Context) (err error) {
	volumes := c.String("range")
	if volumes != "" {
		ids := strings.Split(volumes, "-")
		fmt.Println("You've selected to delete volumes: ", ids[0], " through ", ids[1])
		fmt.Print("Are you sure you want to do this [yes/no]: ")
		if confirm() {
			startID, _ := strconv.ParseInt(ids[0], 10, 64)
			endID, _ := strconv.ParseInt(ids[1], 10, 64)
			client.DeleteRange(startID, endID)
		}

	} else {
		for _, arg := range c.Args() {
			vID, _ := strconv.ParseInt(arg, 10, 64)
			client.DeleteVolume(vID)
		}
	}
	return nil
}

func listForAccount(acctID int64) (vols []sfapi.Volume, err error) {
	var req sfapi.ListVolumesForAccountRequest
	req.AccountID = acctID
	return client.ListVolumesForAccount(&req)
}

func listActiveVolumes(req sfapi.ListActiveVolumesRequest) (vols []sfapi.Volume, err error) {
	return client.ListActiveVolumes(&req)
}

func cmdVolumeList(c *cli.Context) (err error) {
	var req sfapi.ListActiveVolumesRequest
	var volumes []sfapi.Volume

	if c.String("account") != "" {
		acctID, _ := strconv.ParseInt(c.String("account"), 10, 64)
		volumes, err = listForAccount(acctID)
	} else {
		if c.String("startID") != "" {
			stID, _ := strconv.ParseInt(c.String("startID"), 10, 64)
			req.StartVolumeID = stID
		}
		if c.String("limit") != "" {
			limit, _ := strconv.ParseInt(c.String("limit"), 10, 64)
			req.Limit = limit
		}
		volumes, err = client.ListActiveVolumes(&req)
	}

	if err != nil {
		fmt.Println(err)
	} else {
		printVolList(volumes)
	}
	return err

}
<|endoftext|>


<commit_before>package golog

// All of Golog's builtin, foreign-implemented predicates
// are defined here.

import "fmt"
import "strings"
import "github.com/mndrix/golog/term"

// !/0
func BuiltinCut(m Machine, args []term.Term) ForeignReturn {
    // if were anything to cut, !/0 would have already been
    // replaced with '$cut_to/1'  Since this goal wasn't there
    // must be nothing cut, so treat it as an alias for "true/0"
    return ForeignTrue()
}

// $cut_to/1
func BuiltinCutTo(m Machine, args []term.Term) ForeignReturn {
    barrierId := args[0].(*term.Integer).Value().Int64()
    return m.CutTo(barrierId)
}

// ,/2
func BuiltinComma(m Machine, args []term.Term) ForeignReturn {
    return m.PushConj(args[1]).PushConj(args[0])
}

// ->/2
func BuiltinIfThen(m Machine, args []term.Term) ForeignReturn {
    cond := args[0]
    then := args[1]

    cut := term.NewTerm("!")
    goal := term.NewTerm(",", cond, term.NewTerm(",", cut, then))
    m1 := m.PushCutBarrier().PushConj(goal)
    return m1
}

// ;/2
func BuiltinSemicolon(m Machine, args []term.Term) ForeignReturn {
    cp := NewSimpleChoicePoint(m, args[1])
    m = m.PushDisj(cp)

    return m.PushConj(args[0])
}

// =/2
func BuiltinUnify(m Machine, args []term.Term) ForeignReturn {
    return ForeignUnify(args[0], args[1])
}

// atom_codes/2 see §8.16.5
func BuiltinAtomCodes2(m Machine, args []term.Term) ForeignReturn {
    atom := args[0]
    list := args[1]

    if !term.IsVariable(atom) {
        list = term.NewCodeList(atom.Functor())
        return ForeignUnify(args[1], list)
    } else if !term.IsVariable(list) {
        runes := make([]rune, 0)
        for {
            switch list.Indicator() {
                case "./2":
                    args := list.Arguments()
                    code := args[0].(*term.Integer)
                    runes = append(runes, code.Code())
                    list = args[1]
                case "[]/0":
                    atom = term.NewTerm(string(runes))
                    return ForeignUnify(args[0], atom)
                default:
                    msg := fmt.Sprintf("unexpected code list %s", args[1])
                    panic(msg)
            }
        }
    }

    msg := fmt.Sprintf("atom_codes/2: error with the arguments %s and %s", args[0], args[1])
    panic(msg)
}

// call/*
func BuiltinCall(m Machine, args []term.Term) ForeignReturn {
    // which goal is being called?
    bodyTerm := args[0]
    if term.IsVariable(bodyTerm) {
        bindings := m.Bindings()
        bodyTerm = bindings.Resolve_(bodyTerm.(*term.Variable))
    }

    // build a new goal with extra arguments attached
    functor := bodyTerm.Functor()
    newArgs := make([]term.Term, 0)
    newArgs = append(newArgs, bodyTerm.Arguments()...)
    newArgs = append(newArgs, args[1:]...)
    goal := term.NewTerm(functor, newArgs...)

    // construct a machine that will prove this goal next
    return m.PushCutBarrier().PushConj(goal)
}

// downcase_atom(+AnyCase, -LowerCase)
//
// Converts the characters of AnyCase into lowercase and unifies the
// lowercase atom with LowerCase.
func BuiltinDowncaseAtom2(m Machine, args []term.Term) ForeignReturn {
    anycase := args[0]
    if term.IsVariable(anycase) {
        panic("downcase_atom/2: instantiation_error")
    }
    if anycase.Arity() != 0 {
        msg := fmt.Sprintf("downcase_atom/2: type_error(atom, %s)", anycase)
        panic(msg)
    }

    lowercase := term.NewAtom(strings.ToLower(anycase.Functor()))
    return ForeignUnify(args[1], lowercase)
}

// fail/0
func BuiltinFail(m Machine, args []term.Term) ForeignReturn {
    return ForeignFail()
}

// findall/3
func BuiltinFindall3(m Machine, args []term.Term) ForeignReturn {
    template := args[0]
    goal := args[1]

    // call(Goal), X=Template
    x := term.NewVar("_")
    call := term.NewTerm("call", goal)
    unify := term.NewTerm("=", x, template)
    prove := term.NewTerm(",", call, unify)
    proofs := m.ProveAll(prove)

    // build a list from the results
    instances := make([]term.Term, 0)
    for _, proof := range proofs {
        t, err := proof.Resolve(x)
        maybePanic(err)
        instances = append(instances, t)
    }

    return ForeignUnify(args[2], term.NewTermList(instances))
}

// listing/0
// This should be implemented in pure Prolog, but for debugging purposes,
// I'm doing it for now as a foreign predicate.
func BuiltinListing0(m Machine, args []term.Term) ForeignReturn {
    fmt.Println(m.String())
    return ForeignTrue()
}
<commit_msg>Minor built in cleanups<commit_after>package golog

// All of Golog's builtin, foreign-implemented predicates
// are defined here.

import "fmt"
import "strings"
import "github.com/mndrix/golog/term"

// !/0
func BuiltinCut(m Machine, args []term.Term) ForeignReturn {
    // if were anything to cut, !/0 would have already been
    // replaced with '$cut_to/1'  Since this goal wasn't there
    // must be nothing cut, so treat it as an alias for "true/0"
    return ForeignTrue()
}

// $cut_to/1
func BuiltinCutTo(m Machine, args []term.Term) ForeignReturn {
    barrierId := args[0].(*term.Integer).Value().Int64()
    return m.CutTo(barrierId)
}

// ,/2
func BuiltinComma(m Machine, args []term.Term) ForeignReturn {
    return m.PushConj(args[1]).PushConj(args[0])
}

// ->/2
func BuiltinIfThen(m Machine, args []term.Term) ForeignReturn {
    cond := args[0]
    then := args[1]

    // CUT_BARRIER, (cond, !, then)
    cut := term.NewTerm("!")
    goal := term.NewTerm(",", cond, term.NewTerm(",", cut, then))
    return m.PushCutBarrier().PushConj(goal)
}

// ;/2
func BuiltinSemicolon(m Machine, args []term.Term) ForeignReturn {
    cp := NewSimpleChoicePoint(m, args[1])
    return m.PushDisj(cp).PushConj(args[0])
}

// =/2
func BuiltinUnify(m Machine, args []term.Term) ForeignReturn {
    return ForeignUnify(args[0], args[1])
}

// atom_codes/2 see §8.16.5
func BuiltinAtomCodes2(m Machine, args []term.Term) ForeignReturn {
    atom := args[0]
    list := args[1]

    if !term.IsVariable(atom) {
        list = term.NewCodeList(atom.Functor())
        return ForeignUnify(args[1], list)
    } else if !term.IsVariable(list) {
        runes := make([]rune, 0)
        for {
            switch list.Indicator() {
                case "./2":
                    args := list.Arguments()
                    code := args[0].(*term.Integer)
                    runes = append(runes, code.Code())
                    list = args[1]
                case "[]/0":
                    atom = term.NewTerm(string(runes))
                    return ForeignUnify(args[0], atom)
                default:
                    msg := fmt.Sprintf("unexpected code list %s", args[1])
                    panic(msg)
            }
        }
    }

    msg := fmt.Sprintf("atom_codes/2: error with the arguments %s and %s", args[0], args[1])
    panic(msg)
}

// call/*
func BuiltinCall(m Machine, args []term.Term) ForeignReturn {
    // which goal is being called?
    bodyTerm := args[0]
    if term.IsVariable(bodyTerm) {
        bindings := m.Bindings()
        bodyTerm = bindings.Resolve_(bodyTerm.(*term.Variable))
    }

    // build a new goal with extra arguments attached
    functor := bodyTerm.Functor()
    newArgs := make([]term.Term, 0)
    newArgs = append(newArgs, bodyTerm.Arguments()...)
    newArgs = append(newArgs, args[1:]...)
    goal := term.NewTerm(functor, newArgs...)

    // construct a machine that will prove this goal next
    return m.PushCutBarrier().PushConj(goal)
}

// downcase_atom(+AnyCase, -LowerCase)
//
// Converts the characters of AnyCase into lowercase and unifies the
// lowercase atom with LowerCase.
func BuiltinDowncaseAtom2(m Machine, args []term.Term) ForeignReturn {
    anycase := args[0]
    if term.IsVariable(anycase) {
        panic("downcase_atom/2: instantiation_error")
    }
    if anycase.Arity() != 0 {
        msg := fmt.Sprintf("downcase_atom/2: type_error(atom, %s)", anycase)
        panic(msg)
    }

    lowercase := term.NewAtom(strings.ToLower(anycase.Functor()))
    return ForeignUnify(args[1], lowercase)
}

// fail/0
func BuiltinFail(m Machine, args []term.Term) ForeignReturn {
    return ForeignFail()
}

// findall/3
func BuiltinFindall3(m Machine, args []term.Term) ForeignReturn {
    template := args[0]
    goal := args[1]

    // call(Goal), X=Template
    x := term.NewVar("_")
    call := term.NewTerm("call", goal)
    unify := term.NewTerm("=", x, template)
    prove := term.NewTerm(",", call, unify)
    proofs := m.ProveAll(prove)

    // build a list from the results
    instances := make([]term.Term, 0)
    for _, proof := range proofs {
        t, err := proof.Resolve(x)
        maybePanic(err)
        instances = append(instances, t)
    }

    return ForeignUnify(args[2], term.NewTermList(instances))
}

// listing/0
// This should be implemented in pure Prolog, but for debugging purposes,
// I'm doing it for now as a foreign predicate.
func BuiltinListing0(m Machine, args []term.Term) ForeignReturn {
    fmt.Println(m.String())
    return ForeignTrue()
}
<|endoftext|>


<commit_before>package templates

func ScopeTemplateContent() string {
	return `{{ .Name}}:
  {{ range .Services }}{{ .GetName }}:
    {{ range $key, $value := .GetParameters }}{{ $key }}: {{ $value }}
    {{ end }}{{ end }}
`
}
<commit_msg>Add line break after each service on template<commit_after>package templates

func ScopeTemplateContent() string {
	return `#cloud-config

{{ .Name}}:
  {{ range .Services }}{{ .GetName }}:
    {{ range $key, $value := .GetParameters }}{{ $key }}: {{ $value }}
    {{ end }}
  {{ end }}
`
}
<|endoftext|>


<commit_before>package network

import (
	"fmt"
	. "gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
	"runtime"
)

const clientError = -100

type GitLabClient struct {
	clients map[string]*client
}

func (n *GitLabClient) getClient(runner RunnerCredentials) (c *client, err error) {
	if n.clients == nil {
		n.clients = make(map[string]*client)
	}
	key := fmt.Sprintf("%s_%s", runner.URL, runner.TLSCAFile)
	c = n.clients[key]
	if c == nil {
		c, err = newClient(runner)
		if err != nil {
			return
		}
		n.clients[key] = c
	}
	return
}

func (n *GitLabClient) getRunnerVersion(config RunnerConfig) VersionInfo {
	info := VersionInfo{
		Name:         NAME,
		Version:      VERSION,
		Revision:     REVISION,
		Platform:     runtime.GOOS,
		Architecture: runtime.GOARCH,
		Executor:     config.Executor,
	}

	if executor := GetExecutor(config.Executor); executor != nil {
		executor.GetFeatures(&info.Features)
	}

	if config.Shell != nil {
		if shell := GetShell(*config.Shell); shell != nil {
			shell.GetFeatures(&info.Features)
		}
	}

	return info
}

func (n *GitLabClient) do(runner RunnerCredentials, method, uri string, statusCode int, request interface{}, response interface{}) (int, string, string) {
	c, err := n.getClient(runner)
	if err != nil {
		return clientError, err.Error(), ""
	}

	return c.do(uri, method, statusCode, request, response)
}

func (n *GitLabClient) GetBuild(config RunnerConfig) (*GetBuildResponse, bool) {
	request := GetBuildRequest{
		Info:  n.getRunnerVersion(config),
		Token: config.Token,
	}

	var response GetBuildResponse
	result, statusText, certificates := n.do(config.RunnerCredentials, "POST", "builds/register.json", 201, &request, &response)

	switch result {
	case 201:
		config.Log().Println("Checking for builds...", "received")
		response.TLSCAChain = certificates
		return &response, true
	case 403:
		config.Log().Errorln("Checking for builds...", "forbidden")
		return nil, false
	case 404:
		config.Log().Debugln("Checking for builds...", "nothing")
		return nil, true
	case clientError:
		config.Log().WithField("status", statusText).Errorln("Checking for builds...", "error")
		return nil, false
	default:
		config.Log().WithField("status", statusText).Warningln("Checking for builds...", "failed")
		return nil, true
	}
}

func (n *GitLabClient) RegisterRunner(runner RunnerCredentials, description, tags string) *RegisterRunnerResponse {
	// TODO: pass executor
	request := RegisterRunnerRequest{
		Info:        n.getRunnerVersion(RunnerConfig{}),
		Token:       runner.Token,
		Description: description,
		Tags:        tags,
	}

	var response RegisterRunnerResponse
	result, statusText, _ := n.do(runner, "POST", "runners/register.json", 201, &request, &response)

	switch result {
	case 201:
		runner.Log().Println("Registering runner...", "succeeded")
		return &response
	case 403:
		runner.Log().Errorln("Registering runner...", "forbidden (check registration token)")
		return nil
	case clientError:
		runner.Log().WithField("status", statusText).Errorln("Registering runner...", "error")
		return nil
	default:
		runner.Log().WithField("status", statusText).Errorln("Registering runner...", "failed")
		return nil
	}
}

func (n *GitLabClient) DeleteRunner(runner RunnerCredentials) bool {
	request := DeleteRunnerRequest{
		Token: runner.Token,
	}

	result, statusText, _ := n.do(runner, "DELETE", "runners/delete", 200, &request, nil)

	switch result {
	case 200:
		runner.Log().Println("Deleting runner...", "succeeded")
		return true
	case 403:
		runner.Log().Errorln("Deleting runner...", "forbidden")
		return false
	case clientError:
		runner.Log().WithField("status", statusText).Errorln("Deleting runner...", "error")
		return false
	default:
		runner.Log().WithField("status", statusText).Errorln("Deleting runner...", "failed")
		return false
	}
}

func (n *GitLabClient) VerifyRunner(runner RunnerCredentials) bool {
	request := VerifyRunnerRequest{
		Token: runner.Token,
	}

	// HACK: we use non-existing build id to check if receive forbidden or not found
	result, statusText, _ := n.do(runner, "PUT", fmt.Sprintf("builds/%d", -1), 200, &request, nil)

	switch result {
	case 404:
		// this is expected due to fact that we ask for non-existing job
		runner.Log().Println("Veryfing runner...", "is alive")
		return true
	case 403:
		runner.Log().Errorln("Veryfing runner...", "is removed")
		return false
	case clientError:
		runner.Log().WithField("status", statusText).Errorln("Veryfing runner...", "error")
		return false
	default:
		runner.Log().WithField("status", statusText).Errorln("Veryfing runner...", "failed")
		return true
	}
}

func (n *GitLabClient) UpdateBuild(config RunnerConfig, id int, state BuildState, trace string) UpdateState {
	request := UpdateBuildRequest{
		Info:  n.getRunnerVersion(config),
		Token: config.Token,
		State: state,
		Trace: trace,
	}

	result, statusText, _ := n.do(config.RunnerCredentials, "PUT", fmt.Sprintf("builds/%d.json", id), 200, &request, nil)
	switch result {
	case 200:
		config.Log().Println(id, "Submitting build to coordinator...", "ok")
		return UpdateSucceeded
	case 404:
		config.Log().Warningln(id, "Submitting build to coordinator...", "aborted")
		return UpdateAbort
	case 403:
		config.Log().Errorln(id, "Submitting build to coordinator...", "forbidden")
		return UpdateAbort
	case clientError:
		config.Log().WithField("status", statusText).Errorln(id, "Submitting build to coordinator...", "error")
		return UpdateAbort
	default:
		config.Log().WithField("status", statusText).Warningln(id, "Submitting build to coordinator...", "failed")
		return UpdateFailed
	}
}

func (n *GitLabClient) GetArtifactsUploadURL(config RunnerCredentials, id int) string {
	c, err := n.getClient(config)
	if err != nil {
		return ""
	}
	return c.fullUrl("builds/%d/artifacts", id)
}
<commit_msg>Accept error code 204 (no content) if no jobs in queue<commit_after>package network

import (
	"fmt"
	. "gitlab.com/gitlab-org/gitlab-ci-multi-runner/common"
	"runtime"
)

const clientError = -100

type GitLabClient struct {
	clients map[string]*client
}

func (n *GitLabClient) getClient(runner RunnerCredentials) (c *client, err error) {
	if n.clients == nil {
		n.clients = make(map[string]*client)
	}
	key := fmt.Sprintf("%s_%s", runner.URL, runner.TLSCAFile)
	c = n.clients[key]
	if c == nil {
		c, err = newClient(runner)
		if err != nil {
			return
		}
		n.clients[key] = c
	}
	return
}

func (n *GitLabClient) getRunnerVersion(config RunnerConfig) VersionInfo {
	info := VersionInfo{
		Name:         NAME,
		Version:      VERSION,
		Revision:     REVISION,
		Platform:     runtime.GOOS,
		Architecture: runtime.GOARCH,
		Executor:     config.Executor,
	}

	if executor := GetExecutor(config.Executor); executor != nil {
		executor.GetFeatures(&info.Features)
	}

	if config.Shell != nil {
		if shell := GetShell(*config.Shell); shell != nil {
			shell.GetFeatures(&info.Features)
		}
	}

	return info
}

func (n *GitLabClient) do(runner RunnerCredentials, method, uri string, statusCode int, request interface{}, response interface{}) (int, string, string) {
	c, err := n.getClient(runner)
	if err != nil {
		return clientError, err.Error(), ""
	}

	return c.do(uri, method, statusCode, request, response)
}

func (n *GitLabClient) GetBuild(config RunnerConfig) (*GetBuildResponse, bool) {
	request := GetBuildRequest{
		Info:  n.getRunnerVersion(config),
		Token: config.Token,
	}

	var response GetBuildResponse
	result, statusText, certificates := n.do(config.RunnerCredentials, "POST", "builds/register.json", 201, &request, &response)

	switch result {
	case 201:
		config.Log().Println("Checking for builds...", "received")
		response.TLSCAChain = certificates
		return &response, true
	case 403:
		config.Log().Errorln("Checking for builds...", "forbidden")
		return nil, false
	case 204, 404:
		config.Log().Debugln("Checking for builds...", "nothing")
		return nil, true
	case clientError:
		config.Log().WithField("status", statusText).Errorln("Checking for builds...", "error")
		return nil, false
	default:
		config.Log().WithField("status", statusText).Warningln("Checking for builds...", "failed")
		return nil, true
	}
}

func (n *GitLabClient) RegisterRunner(runner RunnerCredentials, description, tags string) *RegisterRunnerResponse {
	// TODO: pass executor
	request := RegisterRunnerRequest{
		Info:        n.getRunnerVersion(RunnerConfig{}),
		Token:       runner.Token,
		Description: description,
		Tags:        tags,
	}

	var response RegisterRunnerResponse
	result, statusText, _ := n.do(runner, "POST", "runners/register.json", 201, &request, &response)

	switch result {
	case 201:
		runner.Log().Println("Registering runner...", "succeeded")
		return &response
	case 403:
		runner.Log().Errorln("Registering runner...", "forbidden (check registration token)")
		return nil
	case clientError:
		runner.Log().WithField("status", statusText).Errorln("Registering runner...", "error")
		return nil
	default:
		runner.Log().WithField("status", statusText).Errorln("Registering runner...", "failed")
		return nil
	}
}

func (n *GitLabClient) DeleteRunner(runner RunnerCredentials) bool {
	request := DeleteRunnerRequest{
		Token: runner.Token,
	}

	result, statusText, _ := n.do(runner, "DELETE", "runners/delete", 200, &request, nil)

	switch result {
	case 200:
		runner.Log().Println("Deleting runner...", "succeeded")
		return true
	case 403:
		runner.Log().Errorln("Deleting runner...", "forbidden")
		return false
	case clientError:
		runner.Log().WithField("status", statusText).Errorln("Deleting runner...", "error")
		return false
	default:
		runner.Log().WithField("status", statusText).Errorln("Deleting runner...", "failed")
		return false
	}
}

func (n *GitLabClient) VerifyRunner(runner RunnerCredentials) bool {
	request := VerifyRunnerRequest{
		Token: runner.Token,
	}

	// HACK: we use non-existing build id to check if receive forbidden or not found
	result, statusText, _ := n.do(runner, "PUT", fmt.Sprintf("builds/%d", -1), 200, &request, nil)

	switch result {
	case 404:
		// this is expected due to fact that we ask for non-existing job
		runner.Log().Println("Veryfing runner...", "is alive")
		return true
	case 403:
		runner.Log().Errorln("Veryfing runner...", "is removed")
		return false
	case clientError:
		runner.Log().WithField("status", statusText).Errorln("Veryfing runner...", "error")
		return false
	default:
		runner.Log().WithField("status", statusText).Errorln("Veryfing runner...", "failed")
		return true
	}
}

func (n *GitLabClient) UpdateBuild(config RunnerConfig, id int, state BuildState, trace string) UpdateState {
	request := UpdateBuildRequest{
		Info:  n.getRunnerVersion(config),
		Token: config.Token,
		State: state,
		Trace: trace,
	}

	result, statusText, _ := n.do(config.RunnerCredentials, "PUT", fmt.Sprintf("builds/%d.json", id), 200, &request, nil)
	switch result {
	case 200:
		config.Log().Println(id, "Submitting build to coordinator...", "ok")
		return UpdateSucceeded
	case 404:
		config.Log().Warningln(id, "Submitting build to coordinator...", "aborted")
		return UpdateAbort
	case 403:
		config.Log().Errorln(id, "Submitting build to coordinator...", "forbidden")
		return UpdateAbort
	case clientError:
		config.Log().WithField("status", statusText).Errorln(id, "Submitting build to coordinator...", "error")
		return UpdateAbort
	default:
		config.Log().WithField("status", statusText).Warningln(id, "Submitting build to coordinator...", "failed")
		return UpdateFailed
	}
}

func (n *GitLabClient) GetArtifactsUploadURL(config RunnerCredentials, id int) string {
	c, err := n.getClient(config)
	if err != nil {
		return ""
	}
	return c.fullUrl("builds/%d/artifacts", id)
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/pkg/errors"
	"log"
	"net/http/httptest"
	"testing"
)

type nilWriter struct{}

func (w *nilWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func TestNewRestServer(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	_, err := newNginxAuth("nginxauth.conf", ".")
	if err != nil {
		t.Error("expected success", err)
	}
}

func TestNewEmailer(t *testing.T) {
	n := authConf{
		VerifyEmailTemplate:     "testTemplates/verifyEmail.html",
		VerifyEmailSubject:      "verifyEmailSubject",
		WelcomeTemplate:         "testTemplates/welcomeEmail.html",
		WelcomeSubject:          "welcomeSubject",
		NewLoginTemplate:        "testTemplates/newLogin.html",
		NewLoginSubject:         "newLoginSubject",
		LockedOutTemplate:       "testTemplates/lockedOut.html",
		LockedOutSubject:        "lockedOutSubject",
		EmailChangedTemplate:    "testTemplates/emailChanged.html",
		EmailChangedSubject:     "emailChangedSubject",
		PasswordChangedTemplate: "testTemplates/passwordChanged.html",
		PasswordChangedSubject:  "passwordChangedSubject",
	}
	n.NewEmailer()
}

func TestAuth(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	auth(storer, w, nil)
	if w.Body.String() != "Authentication required: failed\n" || storer.LastRun != "GetSession" {
		t.Error("expected auth to fail", w.Body.String(), storer.LastRun)
	}

	w = httptest.NewRecorder()
	storer = &mockAuthStorer{SessionReturn: &loginSession{UserID: 1, Email: "test@test.com", FullName: "Name"}}
	auth(storer, w, nil)
	if w.Header().Get("X-User") != `{"UserID":1,"Email":"test@test.com","FullName":"Name"}` || storer.LastRun != "GetSession" {
		t.Error("expected User header to be set", w.Header().Get("X-User"), storer.LastRun)
	}
}

func TestAuthBasic(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	authBasic(storer, w, nil)
	if w.Body.String() != "Authentication required: failed\n" || storer.LastRun != "GetBasicAuth" {
		t.Error("expected auth to fail", w.Body.String(), storer.LastRun)
	}

	w = httptest.NewRecorder()
	storer = &mockAuthStorer{SessionReturn: &loginSession{Email: "test@test.com"}}
	authBasic(storer, w, nil)
	if w.Header().Get("X-User") != `{"UserID":0,"Email":"test@test.com","FullName":""}` || storer.LastRun != "GetBasicAuth" {
		t.Error("expected User header to be set", w.Header().Get("X-User"), storer.LastRun)
	}
}

func TestLogin(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	login(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "Login" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}

	w = httptest.NewRecorder()
	storer = &mockAuthStorer{SessionReturn: &loginSession{}}
	login(storer, w, nil)
	if w.Body.String() != `{ "result": "Success" }` || storer.LastRun != "Login" {
		t.Error("expected success", w.Body.String(), storer.LastRun)
	}
}

func TestRegister(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	register(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "Register" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestCreateProfile(t *testing.T) {
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	createProfile(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "CreateProfile" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestUpdateEmail(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	updateEmail(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "UpdateEmail" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestUpdatePassword(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	updatePassword(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "UpdatePassword" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestVerifyEmail(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	verifyEmail(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "VerifyEmail" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestAddUserHeader(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	addUserHeader(`{"name": "value"}`, w)
	if w.Header().Get("X-User") != `{"name": "value"}` {
		t.Error("expected halfauth header", w.Header())
	}
}

/*******************************************************/
type mockAuthStorer struct {
	SessionReturn        *loginSession
	DestinationURLReturn string
	ErrReturn            error
	LastRun              string
}

func (s *mockAuthStorer) GetSession() (*loginSession, error) {
	s.LastRun = "GetSession"
	return s.SessionReturn, s.ErrReturn
}

func (s *mockAuthStorer) GetBasicAuth() (*loginSession, error) {
	s.LastRun = "GetBasicAuth"
	return s.SessionReturn, s.ErrReturn
}
func (s *mockAuthStorer) OAuthLogin() error {
	s.LastRun = "OAuthLogin"
	return s.ErrReturn
}
func (s *mockAuthStorer) Login() error {
	s.LastRun = "Login"
	return s.ErrReturn
}
func (s *mockAuthStorer) Register() error {
	s.LastRun = "Register"
	return s.ErrReturn
}
func (s *mockAuthStorer) CreateProfile() error {
	s.LastRun = "CreateProfile"
	return s.ErrReturn
}
func (s *mockAuthStorer) VerifyEmail() (string, error) {
	s.LastRun = "VerifyEmail"
	return s.DestinationURLReturn, s.ErrReturn
}
func (s *mockAuthStorer) UpdateEmail() error {
	s.LastRun = "UpdateEmail"
	return s.ErrReturn
}
func (s *mockAuthStorer) UpdatePassword() error {
	s.LastRun = "UpdatePassword"
	return s.ErrReturn
}
<commit_msg>fix filename<commit_after>package main

import (
	"log"
	"net/http/httptest"
	"testing"

	"github.com/pkg/errors"
)

type nilWriter struct{}

func (w *nilWriter) Write(p []byte) (n int, err error) {
	return len(p), nil
}

func TestNewRestServer(t *testing.T) {
	if testing.Short() {
		t.SkipNow()
	}
	_, err := newNginxAuth("nginxauth.conf", "auth.log")
	if err != nil {
		t.Error("expected success", err)
	}
}

func TestNewEmailer(t *testing.T) {
	n := authConf{
		VerifyEmailTemplate:     "testTemplates/verifyEmail.html",
		VerifyEmailSubject:      "verifyEmailSubject",
		WelcomeTemplate:         "testTemplates/welcomeEmail.html",
		WelcomeSubject:          "welcomeSubject",
		NewLoginTemplate:        "testTemplates/newLogin.html",
		NewLoginSubject:         "newLoginSubject",
		LockedOutTemplate:       "testTemplates/lockedOut.html",
		LockedOutSubject:        "lockedOutSubject",
		EmailChangedTemplate:    "testTemplates/emailChanged.html",
		EmailChangedSubject:     "emailChangedSubject",
		PasswordChangedTemplate: "testTemplates/passwordChanged.html",
		PasswordChangedSubject:  "passwordChangedSubject",
	}
	n.NewEmailer()
}

func TestAuth(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	auth(storer, w, nil)
	if w.Body.String() != "Authentication required: failed\n" || storer.LastRun != "GetSession" {
		t.Error("expected auth to fail", w.Body.String(), storer.LastRun)
	}

	w = httptest.NewRecorder()
	storer = &mockAuthStorer{SessionReturn: &loginSession{UserID: 1, Email: "test@test.com", FullName: "Name"}}
	auth(storer, w, nil)
	if w.Header().Get("X-User") != `{"UserID":1,"Email":"test@test.com","FullName":"Name"}` || storer.LastRun != "GetSession" {
		t.Error("expected User header to be set", w.Header().Get("X-User"), storer.LastRun)
	}
}

func TestAuthBasic(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	authBasic(storer, w, nil)
	if w.Body.String() != "Authentication required: failed\n" || storer.LastRun != "GetBasicAuth" {
		t.Error("expected auth to fail", w.Body.String(), storer.LastRun)
	}

	w = httptest.NewRecorder()
	storer = &mockAuthStorer{SessionReturn: &loginSession{Email: "test@test.com"}}
	authBasic(storer, w, nil)
	if w.Header().Get("X-User") != `{"UserID":0,"Email":"test@test.com","FullName":""}` || storer.LastRun != "GetBasicAuth" {
		t.Error("expected User header to be set", w.Header().Get("X-User"), storer.LastRun)
	}
}

func TestLogin(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	login(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "Login" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}

	w = httptest.NewRecorder()
	storer = &mockAuthStorer{SessionReturn: &loginSession{}}
	login(storer, w, nil)
	if w.Body.String() != `{ "result": "Success" }` || storer.LastRun != "Login" {
		t.Error("expected success", w.Body.String(), storer.LastRun)
	}
}

func TestRegister(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	register(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "Register" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestCreateProfile(t *testing.T) {
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	createProfile(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "CreateProfile" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestUpdateEmail(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	updateEmail(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "UpdateEmail" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestUpdatePassword(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	updatePassword(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "UpdatePassword" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestVerifyEmail(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	storer := &mockAuthStorer{ErrReturn: errors.New("failed")}
	verifyEmail(storer, w, nil)
	if w.Body.String() != "failed\n" || storer.LastRun != "VerifyEmail" {
		t.Error("expected to fail", w.Body.String(), storer.LastRun)
	}
}

func TestAddUserHeader(t *testing.T) {
	log.SetOutput(&nilWriter{})
	w := httptest.NewRecorder()
	addUserHeader(`{"name": "value"}`, w)
	if w.Header().Get("X-User") != `{"name": "value"}` {
		t.Error("expected halfauth header", w.Header())
	}
}

/*******************************************************/
type mockAuthStorer struct {
	SessionReturn        *loginSession
	DestinationURLReturn string
	ErrReturn            error
	LastRun              string
}

func (s *mockAuthStorer) GetSession() (*loginSession, error) {
	s.LastRun = "GetSession"
	return s.SessionReturn, s.ErrReturn
}

func (s *mockAuthStorer) GetBasicAuth() (*loginSession, error) {
	s.LastRun = "GetBasicAuth"
	return s.SessionReturn, s.ErrReturn
}
func (s *mockAuthStorer) OAuthLogin() error {
	s.LastRun = "OAuthLogin"
	return s.ErrReturn
}
func (s *mockAuthStorer) Login() error {
	s.LastRun = "Login"
	return s.ErrReturn
}
func (s *mockAuthStorer) Register() error {
	s.LastRun = "Register"
	return s.ErrReturn
}
func (s *mockAuthStorer) CreateProfile() error {
	s.LastRun = "CreateProfile"
	return s.ErrReturn
}
func (s *mockAuthStorer) VerifyEmail() (string, error) {
	s.LastRun = "VerifyEmail"
	return s.DestinationURLReturn, s.ErrReturn
}
func (s *mockAuthStorer) UpdateEmail() error {
	s.LastRun = "UpdateEmail"
	return s.ErrReturn
}
func (s *mockAuthStorer) UpdatePassword() error {
	s.LastRun = "UpdatePassword"
	return s.ErrReturn
}
<|endoftext|>


<commit_before>package controller

// Internal Run Guard Module

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/fsnotify/fsnotify"
	"github.com/pkg/errors"
	"github.com/shirou/gopsutil/process"
)

func (w *Worker) runProtect(ctx context.Context, insp *types.ContainerJSON, pid int, timelim uint64, outputlim int64, outputfile string) (info runinfo, err error) {
	starttime := time.Now().UnixNano()
	curtime := time.Now().UnixNano()

	// Use run protect to protect the running instance
	// It will start right after the execmd =w=
	info.outputexceed = false
	info.usedmem = 0
	info.usedtime = 0
	info.timeexceed = false
	p, err := process.NewProcess(int32(pid))
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot attach process")
		return
	}
	m, err := p.MemoryInfoEx()
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot get memory info")
	}
	var f os.FileInfo
	if outputfile != "" {
		f, err = os.Stat(outputfile)
		if err != nil && !os.IsNotExist(err) {
			err = errors.Wrap(err, "run protect error: cannot get output file")
			return
		}
	}
	wt, er := fsnotify.NewWatcher()
	if er != nil {
		err = errors.Wrap(er, "run protect error: cannot create watcher")
		return
	}
	defer wt.Close()
	err = wt.Add(w.WorkDir)
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot add watchpoint")
		return
	}
	log.Debugf("Add watch to %s", w.WorkDir)
Loop:
	for {
		select {
		case ev := <-wt.Events:
			log.Debugf("%s", ev.String())
			println(ev.String())
			if ev.Op == fsnotify.Create && strings.HasSuffix(ev.Name, "done.lck") {
				curtime = time.Now().UnixNano()
				break Loop
			}
		default:
			curtime = time.Now().UnixNano()
			// Only when output file is not empty, check the file size
			if outputfile != "" {
				f, err = os.Stat(outputfile)
				if err != nil && !os.IsNotExist(err) {
					err = errors.Wrap(err, "run protect error: cannot stat outputfile")
					break Loop
				}
				// Output Limit exceed
				if err == nil && f.Size() > outputlim {
					info.outputexceed = true
					break Loop
				}
			}
			// Collect memory used
			if info.usedmem < m.Dirty {
				info.usedmem = m.Dirty
			}
			// Time limit exceed
			if curtime-starttime > int64(timelim*1000000000) {
				info.timeexceed = true
				log.Debugf("Program exceed hard time limit(used %d, hardlim %d), terminated now", curtime-starttime, timelim*1000000000)
				// Killed the program
				err = p.Terminate()
				if err != nil {
					p.Kill()
				}
				break Loop
			}
			// done.lck create too quick, then just get it and exit
			_, err = os.Stat(filepath.Join(w.WorkDir, "done.lck"))
			if err == nil {
				break Loop
			}

		}
	}
	info.usedtime = curtime - starttime
	info.memexceed = insp.State.OOMKilled
	println("NOW WILL REMOVE DONE.LCK")
	time.Sleep(5 * time.Second)
	err = os.RemoveAll(filepath.Join(w.WorkDir, "done.lck"))
	if err != nil {
		err = errors.Wrap(err, "cannot remove done.lck [ABORT!]")
		return
	}
	println("DONE.LCK SHOULD BE REOMVED")
	time.Sleep(5 * time.Second)

	return
}

func (w *Worker) execcmd(ctx context.Context, cli *client.Client, user string, cmd string) (info types.ContainerExecInspect, err error) {
	ec := types.ExecConfig{}
	ec.Detach = true
	ec.Cmd = strings.Split(cmd, " ")
	excmd := strings.Join(ec.Cmd[2:], " ")
	ec.Cmd[2] = excmd
	ec.User = user
	eresp, er := cli.ContainerExecCreate(ctx, w.containerID, ec)
	if er != nil {
		err = errors.Wrap(er, "exec command in container error")
		return
	}
	sc := types.ExecStartCheck{}
	log.Infof("exec ID = %s", eresp.ID)
	err = cli.ContainerExecStart(ctx, eresp.ID, sc)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	//insp, err := cli.ContainerExecAttach(ctx, eresp.ID, ec)
	//if err != nil {
	//	err = errors.Wrap(err, "exec command in container error")
	//}
	//defer insp.Close()
	log.Infof("Executing exec ID = %s", eresp.ID)
	info, err = cli.ContainerExecInspect(ctx, eresp.ID)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
	}
	//buf := bytes.Buffer{}
	//buf.ReadFrom(insp.Reader)
	return
}
<commit_msg>Guard is still buggy<commit_after>package controller

// Internal Run Guard Module

import (
	"context"
	"os"
	"path/filepath"
	"strings"
	"time"

	log "github.com/Sirupsen/logrus"
	"github.com/docker/engine-api/client"
	"github.com/docker/engine-api/types"
	"github.com/fsnotify/fsnotify"
	"github.com/pkg/errors"
	"github.com/shirou/gopsutil/process"
)

func (w *Worker) runProtect(ctx context.Context, insp *types.ContainerJSON, pid int, timelim uint64, outputlim int64, outputfile string) (info runinfo, err error) {
	starttime := time.Now().UnixNano()
	curtime := time.Now().UnixNano()

	// Use run protect to protect the running instance
	// It will start right after the execmd =w=
	info.outputexceed = false
	info.usedmem = 0
	info.usedtime = 0
	info.timeexceed = false
	p, err := process.NewProcess(int32(pid))
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot attach process")
		return
	}
	m, err := p.MemoryInfoEx()
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot get memory info")
	}
	var f os.FileInfo
	if outputfile != "" {
		f, err = os.Stat(outputfile)
		if err != nil && !os.IsNotExist(err) {
			err = errors.Wrap(err, "run protect error: cannot get output file")
			return
		}
	}
	wt, er := fsnotify.NewWatcher()
	if er != nil {
		err = errors.Wrap(er, "run protect error: cannot create watcher")
		return
	}
	defer wt.Close()
	err = wt.Add(w.WorkDir)
	if err != nil {
		err = errors.Wrap(err, "run protect error: cannot add watchpoint")
		return
	}
	log.Debugf("Add watch to %s", w.WorkDir)
Loop:
	for {
		select {
		case ev := <-wt.Events:
			log.Debugf("%s", ev.String())
			println(ev.String())
			if ev.Op == fsnotify.Create && strings.HasSuffix(ev.Name, "done.lck") {
				curtime = time.Now().UnixNano()
				break Loop
			}
		default:
			curtime = time.Now().UnixNano()
			// Only when output file is not empty, check the file size
			if outputfile != "" {
				f, err = os.Stat(outputfile)
				if err != nil && !os.IsNotExist(err) {
					err = errors.Wrap(err, "run protect error: cannot stat outputfile")
					break Loop
				}
				// Output Limit exceed
				if err == nil && f.Size() > outputlim {
					info.outputexceed = true
					break Loop
				}
			}
			// Collect memory used
			if info.usedmem < m.Dirty {
				info.usedmem = m.Dirty
			}
			// Time limit exceed
			if curtime-starttime > int64(timelim*1000000000) {
				info.timeexceed = true
				log.Debugf("Program exceed hard time limit(used %d, hardlim %d), terminated now", curtime-starttime, timelim*1000000000)
				// Killed the program
				err = p.Terminate()
				if err != nil {
					p.Kill()
				}
				break Loop
			}
			// done.lck create too quick, then just get it and exit
			_, err = os.Stat(filepath.Join(w.WorkDir, "done.lck"))
			if err == nil {
				break Loop
			}

		}
	}
	info.usedtime = curtime - starttime
	info.memexceed = insp.State.OOMKilled
	err = os.RemoveAll(filepath.Join(w.WorkDir, "done.lck"))
	if err != nil {
		err = errors.Wrap(err, "cannot remove done.lck [ABORT!]")
		return
	}
	println("DONE.LCK SHOULD BE REOMVED")

	return
}

func (w *Worker) execcmd(ctx context.Context, cli *client.Client, user string, cmd string) (info types.ContainerExecInspect, err error) {
	ec := types.ExecConfig{}
	ec.Detach = false
	ec.Tty = false
	ec.Cmd = make([]string, 3)
	ec.Cmd[0] = "/bin/bash"
	ec.Cmd[1] = "-c"
	ec.Cmd[2] = cmd
	//ec.Cmd = strings.Split(cmd, " ")
	//excmd := strings.Join(ec.Cmd[2:], " ")
	//ec.Cmd[2] = excmd
	ec.User = user
	//log.Infof("ONLY FOR NOW DEBUG ec %v", ec.Cmd)
	eresp, er := cli.ContainerExecCreate(ctx, w.containerID, ec)
	if er != nil {
		err = errors.Wrap(er, "exec command in container error")
		return
	}
	sc := types.ExecStartCheck{}
	log.Infof("ExecStartCheck %+v", sc)
	sc.Tty = true
	sc.Detach = false
	//log.Infof("exec ID = %s", eresp.ID)
	err = cli.ContainerExecStart(ctx, eresp.ID, sc)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
		return
	}
	//insp, err := cli.ContainerExecAttach(ctx, eresp.ID, ec)
	//if err != nil {
	//	err = errors.Wrap(err, "exec command in container error")
	//}
	//defer insp.Close()
	log.Debugf("Executing exec ID = %s", eresp.ID)
	//time.Sleep(1 * time.Second)
	info, err = cli.ContainerExecInspect(ctx, eresp.ID)
	ins, er := cli.ContainerInspect(ctx, w.containerID)
	log.Infof("ONLY FOR CURRENT DEBUG ins.State.ExitCode = %d", ins.State.ExitCode)
	log.Infof("ONLY FOR CURRENT DEBUG info.ExitCode = %d", info.ExitCode)
	if err != nil {
		err = errors.Wrap(err, "exec command in container error")
	}
	//buf := bytes.Buffer{}
	//buf.ReadFrom(insp.Reader)
	return
}
<|endoftext|>


<commit_before>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"fmt"
	"log"
	"reflect"
	"runtime"
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	indexes  map[string]int
	stop     chan struct{}
	add      chan *Entry
	snapshot chan []*Entry
	running  bool
	ErrorLog *log.Logger
	location *time.Location
}

// Job is an interface for submitted cron jobs.
type Job interface {
	GetID() string
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The ID is unique for Entry
	ID string

	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner, in the Local time zone.
func New() *Cron {
	return NewWithLocation(time.Now().Location())
}

// NewWithLocation returns a new Cron job runner.
func NewWithLocation(location *time.Location) *Cron {
	return &Cron{
		entries:  nil,
		indexes:  make(map[string]int),
		add:      make(chan *Entry),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		ErrorLog: nil,
		location: location,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) GetID() string {
	return fmt.Sprintf("pointer[%v]", reflect.ValueOf(f).Pointer())
}
func (f FuncJob) Run() { f() }

// AddFunc adds a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) error {
	return c.AddJob(spec, FuncJob(cmd))
}

// AddJob adds a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) error {
	schedule, err := Parse(spec)
	if err != nil {
		return err
	}
	c.Schedule(schedule, cmd)
	return nil
}

// Schedule adds a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job) {
	entry := &Entry{
		ID:       cmd.GetID(),
		Schedule: schedule,
		Job:      cmd,
	}
	if !c.running {
		if index, ok := c.indexes[entry.ID]; ok {
			c.entries[index] = entry
			return
		}
		c.entries, c.indexes[entry.ID] = append(c.entries, entry), len(c.entries)
		return
	}

	c.add <- entry
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Location gets the time zone location
func (c *Cron) Location() *time.Location {
	return c.location
}

// Start the cron scheduler in its own go-routine, or no-op if already started.
func (c *Cron) Start() {
	if c.running {
		return
	}
	c.running = true
	go c.run()
}

func (c *Cron) runWithRecovery(j Job) {
	defer func() {
		if r := recover(); r != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			c.logf("cron: panic running job: %v\n%s", r, buf)
		}
	}()
	j.Run()
}

// Rebuild the indexes
func (c *Cron) reIndex() {
	for i, count := 0, len(c.entries); i < count; i++ {
		c.indexes[c.entries[i].ID] = i
	}
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().In(c.location)
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	timer := time.NewTimer(time.Minute)
	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))
		c.reIndex()

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		timer.Reset(effective.Sub(now))
		select {
		case now = <-timer.C:
			now = now.In(c.location)
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				go c.runWithRecovery(e.Job)
				e.Prev = e.Next
				e.Next = e.Schedule.Next(now)
			}
			continue

		case newEntry := <-c.add:
			if index, ok := c.indexes[newEntry.ID]; ok {
				c.entries[index] = newEntry
			} else {
				c.entries, c.indexes[newEntry.ID] = append(c.entries, newEntry), len(c.entries)
			}
			newEntry.Next = newEntry.Schedule.Next(time.Now().In(c.location))

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			timer.Stop()
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().In(c.location)
	}
}

// Logs an error to stderr or to the configured error log
func (c *Cron) logf(format string, args ...interface{}) {
	if c.ErrorLog != nil {
		c.ErrorLog.Printf(format, args...)
	} else {
		log.Printf(format, args...)
	}
}

// Stop stops the cron scheduler if it is running; otherwise it does nothing.
func (c *Cron) Stop() {
	if !c.running {
		return
	}
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
		})
	}
	return entries
}
<commit_msg>cron: 支持删除 job<commit_after>// This library implements a cron spec parser and runner.  See the README for
// more details.
package cron

import (
	"fmt"
	"log"
	"reflect"
	"runtime"
	"sort"
	"time"
)

// Cron keeps track of any number of entries, invoking the associated func as
// specified by the schedule. It may be started, stopped, and the entries may
// be inspected while running.
type Cron struct {
	entries  []*Entry
	indexes  map[string]int
	stop     chan struct{}
	add      chan *Entry
	del      chan string
	snapshot chan []*Entry
	running  bool
	ErrorLog *log.Logger
	location *time.Location
}

// Job is an interface for submitted cron jobs.
type Job interface {
	GetID() string
	Run()
}

// The Schedule describes a job's duty cycle.
type Schedule interface {
	// Return the next activation time, later than the given time.
	// Next is invoked initially, and then each time the job is run.
	Next(time.Time) time.Time
}

// Entry consists of a schedule and the func to execute on that schedule.
type Entry struct {
	// The ID is unique for Entry
	ID string

	// The schedule on which this job should be run.
	Schedule Schedule

	// The next time the job will run. This is the zero time if Cron has not been
	// started or this entry's schedule is unsatisfiable
	Next time.Time

	// The last time this job was run. This is the zero time if the job has never
	// been run.
	Prev time.Time

	// The Job to run.
	Job Job
}

// byTime is a wrapper for sorting the entry array by time
// (with zero time at the end).
type byTime []*Entry

func (s byTime) Len() int      { return len(s) }
func (s byTime) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
func (s byTime) Less(i, j int) bool {
	// Two zero times should return false.
	// Otherwise, zero is "greater" than any other time.
	// (To sort it at the end of the list.)
	if s[i].Next.IsZero() {
		return false
	}
	if s[j].Next.IsZero() {
		return true
	}
	return s[i].Next.Before(s[j].Next)
}

// New returns a new Cron job runner, in the Local time zone.
func New() *Cron {
	return NewWithLocation(time.Now().Location())
}

// NewWithLocation returns a new Cron job runner.
func NewWithLocation(location *time.Location) *Cron {
	return &Cron{
		entries:  nil,
		indexes:  make(map[string]int),
		add:      make(chan *Entry),
		del:      make(chan string),
		stop:     make(chan struct{}),
		snapshot: make(chan []*Entry),
		running:  false,
		ErrorLog: nil,
		location: location,
	}
}

// A wrapper that turns a func() into a cron.Job
type FuncJob func()

func (f FuncJob) GetID() string {
	return fmt.Sprintf("pointer[%v]", reflect.ValueOf(f).Pointer())
}
func (f FuncJob) Run() { f() }

// AddFunc adds or updates a func to the Cron to be run on the given schedule.
func (c *Cron) AddFunc(spec string, cmd func()) error {
	return c.AddJob(spec, FuncJob(cmd))
}

// AddJob adds or updates a Job to the Cron to be run on the given schedule.
func (c *Cron) AddJob(spec string, cmd Job) error {
	schedule, err := Parse(spec)
	if err != nil {
		return err
	}
	c.Schedule(schedule, cmd)
	return nil
}

// Schedule adds or updates a Job to the Cron to be run on the given schedule.
func (c *Cron) Schedule(schedule Schedule, cmd Job) {
	entry := &Entry{
		ID:       cmd.GetID(),
		Schedule: schedule,
		Job:      cmd,
	}
	if !c.running {
		if index, ok := c.indexes[entry.ID]; ok {
			c.entries[index] = entry
			return
		}
		c.entries, c.indexes[entry.ID] = append(c.entries, entry), len(c.entries)
		return
	}

	c.add <- entry
}

// DelFunc deletes a Job from the Cron.
func (c *Cron) DelFunc(cmd func()) {
	c.DelJob(FuncJob(cmd))
}

// DelJob deletes a Job from the Cron.
func (c *Cron) DelJob(cmd Job) {
	index, ok := c.indexes[cmd.GetID()]
	if !ok {
		return
	}

	if c.running {
		c.del <- cmd.GetID()
		return
	}

	c.entries = append(c.entries[:index], c.entries[index+1:]...)
	delete(c.indexes, cmd.GetID())
	return
}

// Entries returns a snapshot of the cron entries.
func (c *Cron) Entries() []*Entry {
	if c.running {
		c.snapshot <- nil
		x := <-c.snapshot
		return x
	}
	return c.entrySnapshot()
}

// Location gets the time zone location
func (c *Cron) Location() *time.Location {
	return c.location
}

// Start the cron scheduler in its own go-routine, or no-op if already started.
func (c *Cron) Start() {
	if c.running {
		return
	}
	c.running = true
	go c.run()
}

func (c *Cron) runWithRecovery(j Job) {
	defer func() {
		if r := recover(); r != nil {
			const size = 64 << 10
			buf := make([]byte, size)
			buf = buf[:runtime.Stack(buf, false)]
			c.logf("cron: panic running job: %v\n%s", r, buf)
		}
	}()
	j.Run()
}

// Rebuild the indexes
func (c *Cron) reIndex() {
	for i, count := 0, len(c.entries); i < count; i++ {
		c.indexes[c.entries[i].ID] = i
	}
}

// Run the scheduler.. this is private just due to the need to synchronize
// access to the 'running' state variable.
func (c *Cron) run() {
	// Figure out the next activation times for each entry.
	now := time.Now().In(c.location)
	for _, entry := range c.entries {
		entry.Next = entry.Schedule.Next(now)
	}

	timer := time.NewTimer(time.Minute)
	for {
		// Determine the next entry to run.
		sort.Sort(byTime(c.entries))
		c.reIndex()

		var effective time.Time
		if len(c.entries) == 0 || c.entries[0].Next.IsZero() {
			// If there are no entries yet, just sleep - it still handles new entries
			// and stop requests.
			effective = now.AddDate(10, 0, 0)
		} else {
			effective = c.entries[0].Next
		}

		timer.Reset(effective.Sub(now))
		select {
		case now = <-timer.C:
			now = now.In(c.location)
			// Run every entry whose next time was this effective time.
			for _, e := range c.entries {
				if e.Next != effective {
					break
				}
				go c.runWithRecovery(e.Job)
				e.Prev = e.Next
				e.Next = e.Schedule.Next(now)
			}
			continue

		case newEntry := <-c.add:
			if index, ok := c.indexes[newEntry.ID]; ok {
				c.entries[index] = newEntry
			} else {
				c.entries, c.indexes[newEntry.ID] = append(c.entries, newEntry), len(c.entries)
			}
			newEntry.Next = newEntry.Schedule.Next(time.Now().In(c.location))

		case id := <-c.del:
			index, ok := c.indexes[id]
			if !ok {
				continue
			}

			c.entries = append(c.entries[:index], c.entries[index+1:]...)
			delete(c.indexes, id)

		case <-c.snapshot:
			c.snapshot <- c.entrySnapshot()

		case <-c.stop:
			timer.Stop()
			return
		}

		// 'now' should be updated after newEntry and snapshot cases.
		now = time.Now().In(c.location)
	}
}

// Logs an error to stderr or to the configured error log
func (c *Cron) logf(format string, args ...interface{}) {
	if c.ErrorLog != nil {
		c.ErrorLog.Printf(format, args...)
	} else {
		log.Printf(format, args...)
	}
}

// Stop stops the cron scheduler if it is running; otherwise it does nothing.
func (c *Cron) Stop() {
	if !c.running {
		return
	}
	c.stop <- struct{}{}
	c.running = false
}

// entrySnapshot returns a copy of the current cron entry list.
func (c *Cron) entrySnapshot() []*Entry {
	entries := []*Entry{}
	for _, e := range c.entries {
		entries = append(entries, &Entry{
			Schedule: e.Schedule,
			Next:     e.Next,
			Prev:     e.Prev,
			Job:      e.Job,
		})
	}
	return entries
}
<|endoftext|>