<commit_before>package unit

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"reflect"
	"strings"

	"github.com/coreos/go-systemd/unit"
)

var supported = map[string]bool{
	"simple": true,
}

const MAXLENGTH = 2048

// Struct representing the unit
type Unit struct {
	*Definition
	*exec.Cmd
	Loaded bool
	Status status
	//Name      string
	//ShortName string
}

// Definition as found in the unit specification file
type Definition struct {
	Unit struct {
		Description, After, Wants string
	}
	Service struct {
		//Type                        ServiceType
		Type, ExecStart, WorkingDirectory string
	}
	Install struct {
		// WIP
	}
}

//type ServiceType string

//const (
//Simple  ServiceType = ""
//Oneshot             = "oneshot"
//Forking             = "forking"
//)

// Execution status of a unit
type status int

const (
	Inactive status = iota
	Active
	Exited
	Failed
)

// Looks for unit files in paths given and adds to 'Units' map
func ParseDir(paths ...string) (map[string]*Unit, error) {
	units := map[string]*Unit{}
	for _, path := range paths {
		var err error
		var file *os.File
		var files []os.FileInfo

		if file, err = os.Open(path); err != nil {
			return nil, err
		}
		if files, err = file.Readdir(0); err != nil {
			if err == io.EOF {
				log.Println(path + " is empty")
				err = nil
				break
			}
			return nil, err
		}

		for _, f := range files {
			var def *Definition

			fpath := path + "/" + f.Name()

			if f.IsDir() {
				//return ParseDir(fpath)
				//			return nil, errors.New("not implemented yet") //TODO
				log.Println("directory parsing not implemented yet")
				continue
			}
			if file, err = os.Open(fpath); err != nil {
				return nil, err
			}
			if def, err = ParseUnit(file); err != nil {
				//return nil, errors.New("error parsing " + fpath + ": " + err.Error())
				log.Println("error parsing " + fpath + ": " + err.Error())
				continue
			}

			units[f.Name()] = &Unit{Definition: def}
		}
	}
	return units, nil
}

// Attempts to parse a specification of a unit
func ParseUnit(specification io.Reader) (*Definition, error) {
	var err error
	var opts []*unit.UnitOption
	definition := &Definition{}
	def := reflect.ValueOf(definition).Elem()

	if opts, err = unit.Deserialize(specification); err != nil {
		return nil, err
	}

	for _, opt := range opts {
		if v := def.FieldByName(opt.Section); v.IsValid() {
			if v := v.FieldByName(opt.Name); v.IsValid() {
				v.SetString(opt.Value)
			} else {
				return nil, errors.New("field " + opt.Name + " does not exist")
			}
		} else {
			return nil, errors.New("section " + opt.Section + " does not exist")
		}
	}

	if definition.Service.ExecStart == "" {
		return nil, errors.New("'ExecStart' field not set")
	}

	switch definition.Service.Type {
	case "":
		definition.Service.Type = "simple"
	default:
		if !supported[definition.Service.Type] {
			return nil, errors.New("service type " + definition.Service.Type + " does not exist or is not supported yet")
		}
	}

	return definition, nil
}

// Starts execution of the unit's specified command
func (u *Unit) Start() error {
	cmd := strings.Split(u.Service.ExecStart, " ")
	u.Cmd = exec.Command(cmd[0], strings.Join(cmd[1:], " "))

	u.Loaded = true
	return u.Cmd.Start()
}

// Stops execution of the unit's specified command
func (u *Unit) Stop() error {
	if u.Loaded != true {
		return errors.New("unit not loaded")
	}

	u.Loaded = false
	return u.Process.Kill()
}
<commit_msg>unit: reload<commit_after>package unit

import (
	"errors"
	"io"
	"log"
	"os"
	"os/exec"
	"reflect"
	"strings"

	"github.com/coreos/go-systemd/unit"
)

var supported = map[string]bool{
	"simple": true,
}

const MAXLENGTH = 2048

// Struct representing the unit
type Unit struct {
	*Definition
	*exec.Cmd
	Loaded bool
	Status status
	//Name      string
	//ShortName string
}

// Definition as found in the unit specification file
type Definition struct {
	Unit struct {
		Description, After, Wants string
	}
	Service struct {
		//Type                        ServiceType
		Type, ExecStart, ExecReload, WorkingDirectory string
	}
	Install struct {
		// WIP
	}
}

//type ServiceType string

//const (
//Simple  ServiceType = ""
//Oneshot             = "oneshot"
//Forking             = "forking"
//)

// Execution status of a unit
type status int

const (
	Inactive status = iota
	Active
	Exited
	Failed
)

// Looks for unit files in paths given and adds to 'Units' map
func ParseDir(paths ...string) (map[string]*Unit, error) {
	units := map[string]*Unit{}
	for _, path := range paths {
		var err error
		var file *os.File
		var files []os.FileInfo

		if file, err = os.Open(path); err != nil {
			return nil, err
		}
		if files, err = file.Readdir(0); err != nil {
			if err == io.EOF {
				log.Println(path + " is empty")
				err = nil
				break
			}
			return nil, err
		}

		for _, f := range files {
			var def *Definition

			fpath := path + "/" + f.Name()

			if f.IsDir() {
				//return ParseDir(fpath)
				//			return nil, errors.New("not implemented yet") //TODO
				log.Println("directory parsing not implemented yet")
				continue
			}
			if file, err = os.Open(fpath); err != nil {
				return nil, err
			}
			if def, err = ParseUnit(file); err != nil {
				//return nil, errors.New("error parsing " + fpath + ": " + err.Error())
				log.Println("error parsing " + fpath + ": " + err.Error())
				continue
			}

			units[f.Name()] = &Unit{Definition: def}
		}
	}
	return units, nil
}

// Attempts to parse a specification of a unit
func ParseUnit(specification io.Reader) (*Definition, error) {
	var err error
	var opts []*unit.UnitOption
	definition := &Definition{}
	def := reflect.ValueOf(definition).Elem()

	if opts, err = unit.Deserialize(specification); err != nil {
		return nil, err
	}

	for _, opt := range opts {
		if v := def.FieldByName(opt.Section); v.IsValid() {
			if v := v.FieldByName(opt.Name); v.IsValid() {
				v.SetString(opt.Value)
			} else {
				return nil, errors.New("field " + opt.Name + " does not exist")
			}
		} else {
			return nil, errors.New("section " + opt.Section + " does not exist")
		}
	}

	if definition.Service.ExecStart == "" {
		return nil, errors.New("'ExecStart' field not set")
	}

	switch definition.Service.Type {
	case "":
		definition.Service.Type = "simple"
	default:
		if !supported[definition.Service.Type] {
			return nil, errors.New("service type " + definition.Service.Type + " does not exist or is not supported yet")
		}
	}

	return definition, nil
}

// Starts execution of the unit's specified command
func (u *Unit) Start() error {
	cmd := strings.Split(u.Service.ExecStart, " ")
	u.Cmd = exec.Command(cmd[0], strings.Join(cmd[1:], " "))

	u.Loaded = true
	return u.Cmd.Start()
}

// Stops execution of the unit's specified command
func (u *Unit) Stop() error {
	if !u.Loaded {
		return errors.New("unit not loaded")
	}

	u.Loaded = false
	return u.Process.Kill()
}

func (u *Unit) Reload() error {
	if err := u.Stop(); err != nil {
		return err
	}

	var cmd []string
	if u.Service.ExecReload == "" {
		cmd = strings.Split(u.Service.ExecStart, " ")
	} else {
		cmd = strings.Split(u.Service.ExecReload, " ")
	}
	u.Cmd = exec.Command(cmd[0], strings.Join(cmd[1:], " "))

	u.Loaded = true
	return u.Cmd.Start()
}
<|endoftext|>


<commit_before>//
// go-sonos
// ========
//
// Copyright (c) 2012, Ian T. Richards <ianr@panix.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package upnp

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	_ "log"
	"net/http"
	_ "os"
	"strings"
)

const (
	soapEncodingStyle  = "http://schemas.xmlsoap.org/soap/encoding/"
	soapEnvelopeSchema = "http://schemas.xmlsoap.org/soap/envelope/"
	soapContentType    = "text/xml; charset=\"utf-8\""
	soapUserAgent      = "Linux UPnP/1.1 crash"
)

type Arg struct {
	Key   string
	Value interface{}
}

type Args []Arg

type ErrorResponse struct {
	FaultCode   string `xml:"faultcode"`
	FaultString string `xml:"faultstring"`
	Detail      struct {
		XMLName   xml.Name
		UPnPError struct {
			XMLName   xml.Name
			ErrorCode string `xml:"errorCode"`
		} `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"`
	} `xml:"detail"`
}

func (this *ErrorResponse) Error() (err error) {
	if 0 < len(this.FaultCode) {
		err = errors.New(this.Detail.UPnPError.ErrorCode)
	}
	return
}

type soapRequestAction struct {
	XMLName  xml.Name
	XMLNS_u  string `xml:"xmlns:u,attr"`
	Argument []soapRequestArgument
}

func soapBuildNamespace(svc *Service) (ns string) {
	return fmt.Sprintf("urn:%s:service:%s:%s", svc.serviceURI, svc.serviceType, svc.serviceVersion)
}

func soapNewRequestAction(action string, svc *Service, args Args) (ra soapRequestAction) {
	ns := soapBuildNamespace(svc)
	ra = soapRequestAction{}
	ra.XMLName.Local = "u:" + action
	ra.XMLNS_u = ns
	for _, arg := range args {
		ra.Argument = append(ra.Argument, soapNewRequestArgument(arg.Key, fmt.Sprintf("%v", arg.Value)))
	}
	return
}

type soapRequestArgument struct {
	XMLName xml.Name
	Value   string `xml:",chardata"`
}

func soapNewRequestArgument(name, value string) (ar soapRequestArgument) {
	ar = soapRequestArgument{}
	ar.XMLName.Local = name
	ar.Value = value
	return
}

type soapRequestBody struct {
	XMLName xml.Name `xml:"s:Body"`
	Action  soapRequestAction
}

type soapResponseBody struct {
	Data string `xml:",innerxml"`
}

type soapRequestEnvelope struct {
	XMLName       xml.Name `xml:"s:Envelope"`
	Body          soapRequestBody
	XMLNS_s       string `xml:"xmlns:s,attr"`
	EncodingStyle string `xml:"s:encodingStyle,attr"`
}

type soapResponseEnvelope struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Body    soapResponseBody
}

type soapRequest struct {
	Envelope soapRequestEnvelope
}

func soapNewRequest(action string, svc *Service, args Args) (req *soapRequest) {
	req = &soapRequest{}
	req.Envelope.XMLNS_s = soapEnvelopeSchema
	req.Envelope.EncodingStyle = soapEncodingStyle
	req.Envelope.Body.Action = soapNewRequestAction(action, svc, args)
	return
}

func upnpBuildRequest(svc *Service, action string, args Args) (msg []byte) {
	if act, err := svc.findAction(action); nil != err {
		panic(err)
	} else {
		req := soapNewRequest(act.name, svc, args)
		var err error
		if msg, err = xml.MarshalIndent(req.Envelope, "", "  "); nil != err {
			panic(err)
		}
	}
	return
}

func (this *Service) Call(action string, args Args) (response string) {
	client := &http.Client{}
	r := upnpBuildRequest(this, action, args)
	body := strings.NewReader(xml.Header + string(r))
	req, err := http.NewRequest("POST", this.controlURL.String(), body)
	req.Header.Set("CONTENT-TYPE", soapContentType)
	req.Header.Set("USER-AGENT", soapUserAgent)
	req.Header.Set("SOAPACTION", fmt.Sprintf("\"%s#%s\"", soapBuildNamespace(this), action))
	//req.Write(os.Stdout)
	//body.Seek(0, 0)
	if nil != err {
		panic(err)
	}
	if resp, err := client.Do(req); nil != err {
		panic(err)
	} else {
		defer resp.Body.Close()
		var body []byte
		if body, err = ioutil.ReadAll(resp.Body); nil != err {
			panic(err)
		}
		doc := soapResponseEnvelope{}
		xml.Unmarshal(body, &doc)
		response = doc.Body.Data
	}
	return
}

func (this *Service) CallVa(action string, va_list ...interface{}) (response string) {
	var args Args
	for i := 0; i < len(va_list); i += 2 {
		args = append(args, Arg{va_list[i].(string), va_list[i+1]})
	}
	return this.Call(action, args)
}
<commit_msg>Adding debug (commented out) ...<commit_after>//
// go-sonos
// ========
//
// Copyright (c) 2012, Ian T. Richards <ianr@panix.com>
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions
// are met:
//
//   * Redistributions of source code must retain the above copyright notice,
//     this list of conditions and the following disclaimer.
//   * Redistributions in binary form must reproduce the above copyright
//     notice, this list of conditions and the following disclaimer in the
//     documentation and/or other materials provided with the distribution.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
// TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
// PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
// LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
// SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//

package upnp

import (
	"encoding/xml"
	"errors"
	"fmt"
	"io/ioutil"
	_ "log"
	"net/http"
	_ "os"
	"strings"
)

const (
	soapEncodingStyle  = "http://schemas.xmlsoap.org/soap/encoding/"
	soapEnvelopeSchema = "http://schemas.xmlsoap.org/soap/envelope/"
	soapContentType    = "text/xml; charset=\"utf-8\""
	soapUserAgent      = "Linux UPnP/1.1 crash"
)

type Arg struct {
	Key   string
	Value interface{}
}

type Args []Arg

type ErrorResponse struct {
	FaultCode   string `xml:"faultcode"`
	FaultString string `xml:"faultstring"`
	Detail      struct {
		XMLName   xml.Name
		UPnPError struct {
			XMLName   xml.Name
			ErrorCode string `xml:"errorCode"`
		} `xml:"urn:schemas-upnp-org:control-1-0 UPnPError"`
	} `xml:"detail"`
}

func (this *ErrorResponse) Error() (err error) {
	if 0 < len(this.FaultCode) {
		err = errors.New(this.Detail.UPnPError.ErrorCode)
	}
	return
}

type soapRequestAction struct {
	XMLName  xml.Name
	XMLNS_u  string `xml:"xmlns:u,attr"`
	Argument []soapRequestArgument
}

func soapBuildNamespace(svc *Service) (ns string) {
	return fmt.Sprintf("urn:%s:service:%s:%s", svc.serviceURI, svc.serviceType, svc.serviceVersion)
}

func soapNewRequestAction(action string, svc *Service, args Args) (ra soapRequestAction) {
	ns := soapBuildNamespace(svc)
	ra = soapRequestAction{}
	ra.XMLName.Local = "u:" + action
	ra.XMLNS_u = ns
	for _, arg := range args {
		ra.Argument = append(ra.Argument, soapNewRequestArgument(arg.Key, fmt.Sprintf("%v", arg.Value)))
	}
	return
}

type soapRequestArgument struct {
	XMLName xml.Name
	Value   string `xml:",chardata"`
}

func soapNewRequestArgument(name, value string) (ar soapRequestArgument) {
	ar = soapRequestArgument{}
	ar.XMLName.Local = name
	ar.Value = value
	return
}

type soapRequestBody struct {
	XMLName xml.Name `xml:"s:Body"`
	Action  soapRequestAction
}

type soapResponseBody struct {
	Data string `xml:",innerxml"`
}

type soapRequestEnvelope struct {
	XMLName       xml.Name `xml:"s:Envelope"`
	Body          soapRequestBody
	XMLNS_s       string `xml:"xmlns:s,attr"`
	EncodingStyle string `xml:"s:encodingStyle,attr"`
}

type soapResponseEnvelope struct {
	XMLName xml.Name `xml:"http://schemas.xmlsoap.org/soap/envelope/ Envelope"`
	Body    soapResponseBody
}

type soapRequest struct {
	Envelope soapRequestEnvelope
}

func soapNewRequest(action string, svc *Service, args Args) (req *soapRequest) {
	req = &soapRequest{}
	req.Envelope.XMLNS_s = soapEnvelopeSchema
	req.Envelope.EncodingStyle = soapEncodingStyle
	req.Envelope.Body.Action = soapNewRequestAction(action, svc, args)
	return
}

func upnpBuildRequest(svc *Service, action string, args Args) (msg []byte) {
	if act, err := svc.findAction(action); nil != err {
		panic(err)
	} else {
		req := soapNewRequest(act.name, svc, args)
		var err error
		if msg, err = xml.MarshalIndent(req.Envelope, "", "  "); nil != err {
			panic(err)
		}
	}
	return
}

func (this *Service) Call(action string, args Args) (response string) {
	client := &http.Client{}
	r := upnpBuildRequest(this, action, args)
	body := strings.NewReader(xml.Header + string(r))
	req, err := http.NewRequest("POST", this.controlURL.String(), body)
	req.Header.Set("CONTENT-TYPE", soapContentType)
	req.Header.Set("USER-AGENT", soapUserAgent)
	req.Header.Set("SOAPACTION", fmt.Sprintf("\"%s#%s\"", soapBuildNamespace(this), action))
	//req.Write(os.Stdout)
	//body.Seek(0, 0)
	if nil != err {
		panic(err)
	}
	if resp, err := client.Do(req); nil != err {
		panic(err)
	} else {
		defer resp.Body.Close()
		var body []byte
		if body, err = ioutil.ReadAll(resp.Body); nil != err {
			panic(err)
		}
		doc := soapResponseEnvelope{}
		//log.Printf("%v", string(body))
		xml.Unmarshal(body, &doc)
		response = doc.Body.Data
	}
	return
}

func (this *Service) CallVa(action string, va_list ...interface{}) (response string) {
	var args Args
	for i := 0; i < len(va_list); i += 2 {
		args = append(args, Arg{va_list[i].(string), va_list[i+1]})
	}
	return this.Call(action, args)
}
<|endoftext|>


<commit_before>package util

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
)

// Debug is a global flag for debugging.
var Debug bool

// ErrNotImplemented signifies a function has not been implemented yet.
var ErrNotImplemented = errors.New("Error: not implemented yet.")

// ErrTimeout implies that a timeout has been triggered
var ErrTimeout = errors.New("Error: Call timed out.")

// ErrSeErrSearchIncomplete implies that a search type operation didnt
// find the expected node, but did find 'a' node.
var ErrSearchIncomplete = errors.New("Error: Search Incomplete.")

// ErrNotFound is returned when a search fails to find anything
var ErrNotFound = errors.New("Error: Not Found.")

// Key is a string representation of multihash for use with maps.
type Key string

// Pretty returns Key in a b58 encoded string
func (k Key) Pretty() string {
	return b58.Encode([]byte(k))
}

// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits
func Hash(data []byte) (mh.Multihash, error) {
	return mh.Sum(data, mh.SHA2_256, -1)
}

// TildeExpansion expands a filename, which may begin with a tilde.
func TildeExpansion(filename string) (string, error) {
	if strings.HasPrefix(filename, "~/") {
		usr, err := user.Current()
		if err != nil {
			return "", err
		}

		dir := usr.HomeDir + "/"
		filename = strings.Replace(filename, "~/", dir, 1)
	}
	return filename, nil
}

// PErr is a shorthand printing function to output to Stderr.
func PErr(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// POut is a shorthand printing function to output to Stdout.
func POut(format string, a ...interface{}) {
	fmt.Fprintf(os.Stdout, format, a...)
}

// DErr is a shorthand debug printing function to output to Stderr.
// Will only print if Debug is true.
func DErr(format string, a ...interface{}) {
	if Debug {
		PErr(format, a...)
	}
}

// DOut is a shorthand debug printing function to output to Stdout.
// Will only print if Debug is true.
func DOut(format string, a ...interface{}) {
	if Debug {
		POut(format, a...)
	}
}

// ExpandPathnames takes a set of paths and turns them into absolute paths
func ExpandPathnames(paths []string) ([]string, error) {
	var out []string
	for _, p := range paths {
		abspath, err := filepath.Abs(p)
		if err != nil {
			return nil, err
		}
		out = append(out, abspath)
	}
	return out, nil
}
<commit_msg>set util.ErrNotFound = ds.ErrNotFound<commit_after>package util

import (
	"errors"
	"fmt"
	"os"
	"os/user"
	"path/filepath"
	"strings"

	ds "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/datastore.go"
	b58 "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-base58"
	mh "github.com/jbenet/go-ipfs/Godeps/_workspace/src/github.com/jbenet/go-multihash"
)

// Debug is a global flag for debugging.
var Debug bool

// ErrNotImplemented signifies a function has not been implemented yet.
var ErrNotImplemented = errors.New("Error: not implemented yet.")

// ErrTimeout implies that a timeout has been triggered
var ErrTimeout = errors.New("Error: Call timed out.")

// ErrSeErrSearchIncomplete implies that a search type operation didnt
// find the expected node, but did find 'a' node.
var ErrSearchIncomplete = errors.New("Error: Search Incomplete.")

// ErrNotFound is returned when a search fails to find anything
var ErrNotFound = ds.ErrNotFound

// Key is a string representation of multihash for use with maps.
type Key string

// Pretty returns Key in a b58 encoded string
func (k Key) Pretty() string {
	return b58.Encode([]byte(k))
}

// Hash is the global IPFS hash function. uses multihash SHA2_256, 256 bits
func Hash(data []byte) (mh.Multihash, error) {
	return mh.Sum(data, mh.SHA2_256, -1)
}

// TildeExpansion expands a filename, which may begin with a tilde.
func TildeExpansion(filename string) (string, error) {
	if strings.HasPrefix(filename, "~/") {
		usr, err := user.Current()
		if err != nil {
			return "", err
		}

		dir := usr.HomeDir + "/"
		filename = strings.Replace(filename, "~/", dir, 1)
	}
	return filename, nil
}

// PErr is a shorthand printing function to output to Stderr.
func PErr(format string, a ...interface{}) {
	fmt.Fprintf(os.Stderr, format, a...)
}

// POut is a shorthand printing function to output to Stdout.
func POut(format string, a ...interface{}) {
	fmt.Fprintf(os.Stdout, format, a...)
}

// DErr is a shorthand debug printing function to output to Stderr.
// Will only print if Debug is true.
func DErr(format string, a ...interface{}) {
	if Debug {
		PErr(format, a...)
	}
}

// DOut is a shorthand debug printing function to output to Stdout.
// Will only print if Debug is true.
func DOut(format string, a ...interface{}) {
	if Debug {
		POut(format, a...)
	}
}

// ExpandPathnames takes a set of paths and turns them into absolute paths
func ExpandPathnames(paths []string) ([]string, error) {
	var out []string
	for _, p := range paths {
		abspath, err := filepath.Abs(p)
		if err != nil {
			return nil, err
		}
		out = append(out, abspath)
	}
	return out, nil
}
<|endoftext|>


<commit_before>package util

import (
	"math/rand"
)

var letters = []rune("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789")

func GetUID(n int) string {
	b := make([]rune, n)
	for i := range b {
		b[i] = letters[rand.Intn(len(letters))]
	}
	return string(b)
}
<commit_msg>id-generator ausgetauscht gegen einen viel cooleren :P<commit_after>package util

import (
	"github.com/FabianWilms/GoReadableID/readableId"
)

func GetUID(n int) string {
	return readableId.GetRandomID()
}
<|endoftext|>


<commit_before>package ow_stats

import "testing"

var keyTests = []struct {
	n        string // input
	expected string // expected result
}{
	{"Multikill - Best", "multikill_best"},
	{"Soldier: 76", "soldier76"},
	{"Lúcio", "lucio"},
}

var valueTests = []struct {
	n        string  // input
	expected float32 // expected result
}{
	{"5", 5},
	{"50%", 0.5},
	{"50 %", 0.5},
	{"01:00:00", 1},
	{"00:30:00", 0.5},
	{"30:36", 0.51},
	{"02:22", 0.0394444465637207},
	{"1 hour", 1},
	{"5 hours", 5},
	{"30 minutes", 0.5},
	{"1 minute", 1 / 60.},
	{"50 seconds", 50 / 3600.},
	{"2.8 seconds", 2.8 / 3600.},
	{"1 second", 1 / 3600.},
	{"2.8", 2.8},
}

var pluralizerTests = []struct {
	n        string // input
	expected string // expected result
}{
	{"solo_kill", "solo_kills"},
	{"solo_kills", "solo_kills"},
	{"final_blow", "final_blows"},
	{"final_blows", "final_blows"},
	{"nano_boost_applied", "nano_boosts_applied"},
	{"nano_boosts_applied", "nano_boosts_applied"},
	{"final_blows_most_in_game", "final_blows_most_in_game"},
	{"final_blow_most_in_game", "final_blows_most_in_game"},
	{"projected_barriers_applied", "projected_barriers_applied"},
	{"projected_barrier_applied", "projected_barriers_applied"},
	{"multikills", "multikills"},
	{"multikill", "multikills"},
	{"eliminations_most_in_game", "eliminations_most_in_game"},
	{"elimination_most_in_game", "eliminations_most_in_game"},
	{"testblow_test", "testblow_test"},
	{"kill_streak_best", "kill_streak_best"},
}

func TestSanitizeKey(t *testing.T) {
	for _, tt := range keyTests {
		actual := SanitizeKey(tt.n)
		if actual != tt.expected {
			t.Errorf("SanitizeKey(%s): expected %s, actual %s", tt.n, tt.expected, actual)
		}
	}
}

func TestSanitizeValue(t *testing.T) {
	for _, tt := range valueTests {
		actual := SanitizeValue(tt.n)
		if actual != tt.expected {
			t.Errorf("SanitizeValue(%s): expected %f, actual %f", tt.n, tt.expected, actual)
		}
	}
}

func TestPluralizer(t *testing.T) {
	for _, tt := range pluralizerTests {
		actual := Pluralizer(tt.n)
		if actual != tt.expected {
			t.Errorf("Pluralizer(%s): expected %s, actual %s", tt.n, tt.expected, actual)
		}
	}
}
<commit_msg>Added more unit tests<commit_after>package ow_stats

import "testing"

var keyTests = []struct {
	n        string // input
	expected string // expected result
}{
	{"Multikill - Best", "multikill_best"},
	{"Soldier: 76", "soldier76"},
	{"Lúcio", "lucio"},
}

var valueTests = []struct {
	n        string  // input
	expected float32 // expected result
}{
	{"5", 5},
	{"11,996", 11996},
	{"50%", 0.5},
	{"50 %", 0.5},
	{"01:00:00", 1},
	{"00:30:00", 0.5},
	{"30:36", 0.51},
	{"02:22", 0.0394444465637207},
	{"1 hour", 1},
	{"5 hours", 5},
	{"14 hours", 14},
	{"30 minutes", 0.5},
	{"1 minute", 1 / 60.},
	{"50 seconds", 50 / 3600.},
	{"2.8 seconds", 2.8 / 3600.},
	{"1 second", 1 / 3600.},
	{"2.8", 2.8},
}

var pluralizerTests = []struct {
	n        string // input
	expected string // expected result
}{
	{"solo_kill", "solo_kills"},
	{"solo_kills", "solo_kills"},
	{"final_blow", "final_blows"},
	{"final_blows", "final_blows"},
	{"nano_boost_applied", "nano_boosts_applied"},
	{"nano_boosts_applied", "nano_boosts_applied"},
	{"final_blows_most_in_game", "final_blows_most_in_game"},
	{"final_blow_most_in_game", "final_blows_most_in_game"},
	{"projected_barriers_applied", "projected_barriers_applied"},
	{"projected_barrier_applied", "projected_barriers_applied"},
	{"multikills", "multikills"},
	{"multikill", "multikills"},
	{"eliminations_most_in_game", "eliminations_most_in_game"},
	{"elimination_most_in_game", "eliminations_most_in_game"},
	{"testblow_test", "testblow_test"},
	{"kill_streak_best", "kill_streak_best"},
}

func TestSanitizeKey(t *testing.T) {
	for _, tt := range keyTests {
		actual := SanitizeKey(tt.n)
		if actual != tt.expected {
			t.Errorf("SanitizeKey(%s): expected %s, actual %s", tt.n, tt.expected, actual)
		}
	}
}

func TestSanitizeValue(t *testing.T) {
	for _, tt := range valueTests {
		actual := SanitizeValue(tt.n)
		if actual != tt.expected {
			t.Errorf("SanitizeValue(%s): expected %f, actual %f", tt.n, tt.expected, actual)
		}
	}
}

func TestPluralizer(t *testing.T) {
	for _, tt := range pluralizerTests {
		actual := Pluralizer(tt.n)
		if actual != tt.expected {
			t.Errorf("Pluralizer(%s): expected %s, actual %s", tt.n, tt.expected, actual)
		}
	}
}
<|endoftext|>


<commit_before>package neptulon

import (
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"testing"
)

func TestRandString(t *testing.T) {
	l := 12304
	str := randString(l)

	if len(str) != l {
		t.Fatalf("Expected a random string of length %v but got %v", l, len(str))
	}
	if str[1] == str[2] && str[3] == str[4] && str[5] == str[6] && str[7] == str[8] {
		t.Fatal("Expected a random string, got repeated characters")
	}
}

func TestGetID(t *testing.T) {
	for i := 0; i < 50; i++ {
		id, err := getID()

		if err != nil {
			t.Fatalf("Error while generating unique ID: %v", err)
		}
		if len(id) != 26 {
			t.Fatalf("Expected a string of length 26 but got %v", len(id))
		}
		if id[3] == id[4] && id[5] == id[6] && id[7] == id[8] && id[9] == id[10] {
			t.Fatal("Expected a random string, got repeated characters")
		}
	}
}

func TestGenCert(t *testing.T) {
	// keyLength := 0 // used for internal test cert generation
	keyLength := 512

	caCert, caKey, clientCert, clientKey, err := genTestCertPair(keyLength)
	if err != nil {
		t.Fatal(err)
	}

	if keyLength == 0 {
		fmt.Println("CA cert:")
		fmt.Println(string(caCert))
		fmt.Println(string(caKey))
		fmt.Println("Client cert:")
		fmt.Println(string(clientCert))
		fmt.Println(string(clientKey))
	}
}

func genTestCertPair(keyLength int) (caCert, caKey, clientCert, clientKey []byte, err error) {
	// CA certificate
	caCert, caKey, err = genCert("127.0.0.1", 0, nil, nil, keyLength, "127.0.0.1", "devastator")

	if err != nil {
		err = fmt.Errorf("Failed to generate CA certificate or key: %v", err)
		return
	}
	if caCert == nil || caKey == nil {
		err = fmt.Errorf("Generated empty CA certificate or key")
		return
	}

	tlsCert, err := tls.X509KeyPair(caCert, caKey)

	if err != nil {
		err = fmt.Errorf("Generated invalid CA certificate or key: %v", err)
		return
	}
	if &tlsCert == nil {
		err = fmt.Errorf("Generated invalid CA certificate or key")
		return
	}

	// client certificate
	pub, err := x509.ParseCertificate(tlsCert.Certificate[0])
	if err != nil {
		err = fmt.Errorf("Failed to parse x509 certificate of CA cert to sign client-cert: %v", err)
		return
	}

	clientCert, clientKey, err = genCert("client.127.0.0.1", 0, pub, tlsCert.PrivateKey.(*rsa.PrivateKey), keyLength, "client.127.0.0.1", "devastator")
	if err != nil {
		err = fmt.Errorf("Failed to generate client-certificate or key: %v", err)
		return
	}

	tlsCert2, err := tls.X509KeyPair(clientCert, clientKey)

	if err != nil {
		err = fmt.Errorf("Generated invalid client-certificate or key: %v", err)
		return
	}
	if &tlsCert2 == nil {
		err = fmt.Errorf("Generated invalid client-certificate or key")
		return
	}

	return
}
<commit_msg>fix build<commit_after>package neptulon

import (
	"crypto/rsa"
	"crypto/tls"
	"crypto/x509"
	"fmt"
	"testing"
)

func TestRandString(t *testing.T) {
	l := 12304
	str := randString(l)

	if len(str) != l {
		t.Fatalf("Expected a random string of length %v but got %v", l, len(str))
	}
	if str[1] == str[2] && str[3] == str[4] && str[5] == str[6] && str[7] == str[8] {
		t.Fatal("Expected a random string, got repeated characters")
	}
}

func TestGenUID(t *testing.T) {
	for i := 0; i < 50; i++ {
		id, err := GenUID()

		if err != nil {
			t.Fatalf("Error while generating unique ID: %v", err)
		}
		if len(id) != 26 {
			t.Fatalf("Expected a string of length 26 but got %v", len(id))
		}
		if id[3] == id[4] && id[5] == id[6] && id[7] == id[8] && id[9] == id[10] {
			t.Fatal("Expected a random string, got repeated characters")
		}
	}
}

func TestGenCert(t *testing.T) {
	// keyLength := 0 // used for internal test cert generation
	keyLength := 512

	caCert, caKey, clientCert, clientKey, err := genTestCertPair(keyLength)
	if err != nil {
		t.Fatal(err)
	}

	if keyLength == 0 {
		fmt.Println("CA cert:")
		fmt.Println(string(caCert))
		fmt.Println(string(caKey))
		fmt.Println("Client cert:")
		fmt.Println(string(clientCert))
		fmt.Println(string(clientKey))
	}
}

func genTestCertPair(keyLength int) (caCert, caKey, clientCert, clientKey []byte, err error) {
	// CA certificate
	caCert, caKey, err = genCert("127.0.0.1", 0, nil, nil, keyLength, "127.0.0.1", "devastator")

	if err != nil {
		err = fmt.Errorf("Failed to generate CA certificate or key: %v", err)
		return
	}
	if caCert == nil || caKey == nil {
		err = fmt.Errorf("Generated empty CA certificate or key")
		return
	}

	tlsCert, err := tls.X509KeyPair(caCert, caKey)

	if err != nil {
		err = fmt.Errorf("Generated invalid CA certificate or key: %v", err)
		return
	}
	if &tlsCert == nil {
		err = fmt.Errorf("Generated invalid CA certificate or key")
		return
	}

	// client certificate
	pub, err := x509.ParseCertificate(tlsCert.Certificate[0])
	if err != nil {
		err = fmt.Errorf("Failed to parse x509 certificate of CA cert to sign client-cert: %v", err)
		return
	}

	clientCert, clientKey, err = genCert("client.127.0.0.1", 0, pub, tlsCert.PrivateKey.(*rsa.PrivateKey), keyLength, "client.127.0.0.1", "devastator")
	if err != nil {
		err = fmt.Errorf("Failed to generate client-certificate or key: %v", err)
		return
	}

	tlsCert2, err := tls.X509KeyPair(clientCert, clientKey)

	if err != nil {
		err = fmt.Errorf("Generated invalid client-certificate or key: %v", err)
		return
	}
	if &tlsCert2 == nil {
		err = fmt.Errorf("Generated invalid client-certificate or key")
		return
	}

	return
}
<|endoftext|>


<commit_before>package utils

import "os/exec"

// GitRepo type manages a VCS repository with Git
type GitRepo struct {
	// Local path to the repository
	Path string

	// Upstream URL of the Git repository
	Upstream string

	// Path to the Git tool
	git string
}

// NewGitRepo creates a new Git repository
func NewGitRepo(path, upstream string) (*GitRepo, error) {
	cmd, err := exec.LookPath("git")
	if err != nil {
		return nil, err
	}

	repo := &GitRepo{
		Path:     path,
		Upstream: upstream,
		git:      cmd,
	}

	return repo, nil
}

// Fetch fetches from the given remote
func (gr *GitRepo) Fetch(remote string) ([]byte, error) {
	return exec.Command(gr.git, "--git-dir", gr.Path, "fetch", remote).CombinedOutput()
}

// Pull pulls from the given remote and merges changes into the
// local branch
func (gr *GitRepo) Pull(remote, branch string) ([]byte, error) {
	out, err := gr.Checkout(branch)
	if err != nil {
		return out, err
	}

	return exec.Command(gr.git, "--git-dir", gr.Path, "pull", remote).CombinedOutput()
}

// Checkout checks out a given local branch
func (gr *GitRepo) Checkout(branch string) ([]byte, error) {
	return exec.Command(gr.git, "--git-dir", gr.Path, "checkout", branch).CombinedOutput()
}

// CheckoutDetached checks out a given local branch in detached mode
func (gr *GitRepo) CheckoutDetached(branch string) ([]byte, error) {
	return exec.Command(gr.git, "--git-dir", gr.Path, "checkout", "--detach", branch).CombinedOutput()
}

// Clone clones the upstream repository
func (gr *GitRepo) Clone() ([]byte, error) {
	return exec.Command(gr.git, "clone", gr.Upstream, gr.Path).CombinedOutput()
}

// Head returns the SHA1 commit id at HEAD
func (gr *GitRepo) Head() ([]byte, error) {
	return exec.Command(gr.git, "--git-dir", gr.Path, "rev-parse", "--short", "HEAD").CombinedOutput()
}

// IsGitRepo checks if the repository is a valid Git repository
func (gr *GitRepo) IsGitRepo() bool {
	err := exec.Command(gr.git, "--git-dir", gr.Path, "rev-parse").Run()
	if err != nil {
		return false
	}

	return true
}
<commit_msg>utils: switch to the git directory before running git commands<commit_after>package utils

import (
	"os/exec"
	"strings"
)

// GitRepo type manages a VCS repository with Git
type GitRepo struct {
	// Local path to the repository
	Path string

	// Upstream URL of the Git repository
	Upstream string

	// Path to the Git tool
	git string
}

// NewGitRepo creates a new Git repository
func NewGitRepo(path, upstream string) (*GitRepo, error) {
	cmd, err := exec.LookPath("git")
	if err != nil {
		return nil, err
	}

	repo := &GitRepo{
		Path:     path,
		Upstream: upstream,
		git:      cmd,
	}

	return repo, nil
}

// Fetch fetches from the given remote
func (gr *GitRepo) Fetch(remote string) ([]byte, error) {
	return exec.Command(gr.git, "-C", gr.Path, "fetch", remote).CombinedOutput()
}

// Pull pulls from the given remote and merges changes into the
// local branch
func (gr *GitRepo) Pull(remote, branch string) ([]byte, error) {
	out, err := gr.Checkout(branch)
	if err != nil {
		return out, err
	}

	return exec.Command(gr.git, "-C", gr.Path, "pull", remote).CombinedOutput()
}

// Checkout checks out a given local branch
func (gr *GitRepo) Checkout(branch string) ([]byte, error) {
	return exec.Command(gr.git, "-C", gr.Path, "checkout", branch).CombinedOutput()
}

// CheckoutDetached checks out a given local branch in detached mode
func (gr *GitRepo) CheckoutDetached(branch string) ([]byte, error) {
	return exec.Command(gr.git, "-C", gr.Path, "checkout", "--detach", branch).CombinedOutput()
}

// Clone clones the upstream repository
func (gr *GitRepo) Clone() ([]byte, error) {
	return exec.Command(gr.git, "clone", gr.Upstream, gr.Path).CombinedOutput()
}

// Head returns the SHA1 commit id at HEAD
func (gr *GitRepo) Head() (string, error) {
	head, err := exec.Command(gr.git, "-C", gr.Path, "rev-parse", "--short", "HEAD").CombinedOutput()
	if err != nil {
		return "", err
	}

	return strings.Trim(string(head), "\n"), nil
}

// IsGitRepo checks if the repository is a valid Git repository
func (gr *GitRepo) IsGitRepo() bool {
	err := exec.Command(gr.git, "-C", gr.Path, "rev-parse").Run()
	if err != nil {
		return false
	}

	return true
}
<|endoftext|>


<commit_before>package utils

import (
	"log"
	"os"
	"runtime"
	"fmt"
)

const(
	LEVEL_DEBUG=1
	LEVEL_INFO=2
	LEVEL_WARN=3
	LEVEL_ERROR=4
)
var LEVEL_NAMES=[]string{"debug","info","warn","error"}
type LogConfig struct {
	IsDebug bool
	FileName string
}
type LogFactory struct {
	loglevel int
	logfile *os.File
	l *log.Logger
}

func (this *LogFactory)SetConfig(config LogConfig) {
	logFile,err:= os.OpenFile(config.FileName,os.O_RDWR|os.O_APPEND|os.O_CREATE, os.ModeAppend)
	log.Println("open log file",config.FileName)
	if err!=nil {
		log.Fatal("openfile error!",err.Error())
	}else {
		this.logfile = logFile
		this.l = log.New(this.logfile, "", log.LstdFlags)
	}

}

func (this *LogFactory)GetLog(name string)Log {
	return &logImp{name,this}
}

func (this *LogFactory)Close(){
	if this .logfile!=nil {
		this.logfile.Close()
	}
}

func (this *LogFactory)Write(level int,prefix string,fmt string,obj ...interface{}) {
	   if level >= this.loglevel { //判断loglevel是否大于指定的level，如果大于则输出，否则直接抛弃
		   content := this.formatHeader(prefix, level) + fmt + "\n"
		   if this.l == nil {
			   go log.Printf(content, obj...)
		   } else {
			   go this.l.Printf(content, obj...)
		   }
	   }
}

func (this *LogFactory) formatHeader(prefix string,level int) string {
	_, thefile, line, _ := runtime.Caller(3)
	short := thefile
	for i := len(thefile) - 1; i > 0; i-- {
		if thefile[i] == '/' {
			short = thefile[i+1:]
			break
		}
	}
	return fmt.Sprintf("[%s][%s][%s(%d)]- ",LEVEL_NAMES[level-1],prefix,short,line)
}

type Log interface {
	Info(msg string,obj ...interface{})
	Debug(msg string,obj ...interface{})
	Error(msg string,obj ...interface{})
	Warning(msg string,obj ...interface{})
}

type logImp struct {
	module string
	factory *LogFactory
}
func (this *logImp)Info(msg string,obj ...interface{}){
   this.factory.Write(LEVEL_INFO,this.module,msg,obj...)
}

func (this *logImp)Debug(msg string,obj ...interface{}){
	this.factory.Write(LEVEL_DEBUG,this.module,msg,obj...)
}
func (this *logImp)Error(msg string,obj ...interface{}){
	this.factory.Write(LEVEL_ERROR,this.module,msg,obj...)
}
func (this *logImp)Warning(msg string,obj ...interface{}){
	this.factory.Write(LEVEL_WARN,this.module,msg,obj...)
}<commit_msg>允许没有设置log文件的时候使用console输出<commit_after>package utils

import (
	"log"
	"os"
	"runtime"
	"fmt"
)

const(
	LEVEL_DEBUG=1
	LEVEL_INFO=2
	LEVEL_WARN=3
	LEVEL_ERROR=4
)
var LEVEL_NAMES=[]string{"debug","info","warn","error"}
type LogConfig struct {
	IsDebug bool
	FileName string
}
type LogFactory struct {
	loglevel int
	logfile *os.File
	l *log.Logger
}

func (this *LogFactory)SetConfig(config LogConfig) {

	logFile,err:= os.OpenFile(config.FileName,os.O_RDWR|os.O_APPEND|os.O_CREATE, os.ModeAppend)
	log.Println("open log file",config.FileName)
	if err!=nil {
		log.Println("open log file error!",err.Error())
	}else {
		this.logfile = logFile
		this.l = log.New(this.logfile, "", log.LstdFlags)
	}

}

func (this *LogFactory)GetLog(name string)Log {
	return &logImp{name,this}
}

func (this *LogFactory)Close(){
	if this .logfile!=nil {
		this.logfile.Close()
	}
}

func (this *LogFactory)Write(level int,prefix string,fmt string,obj ...interface{}) {
	   if level >= this.loglevel { //判断loglevel是否大于指定的level，如果大于则输出，否则直接抛弃
		   content := this.formatHeader(prefix, level) + fmt + "\n"
		   if this.l == nil {
			   go log.Printf(content, obj...)
		   } else {
			   go this.l.Printf(content, obj...)
		   }
	   }
}

func (this *LogFactory) formatHeader(prefix string,level int) string {
	_, thefile, line, _ := runtime.Caller(3)
	short := thefile
	for i := len(thefile) - 1; i > 0; i-- {
		if thefile[i] == '/' {
			short = thefile[i+1:]
			break
		}
	}
	return fmt.Sprintf("[%s][%s][%s(%d)]- ",LEVEL_NAMES[level-1],prefix,short,line)
}

type Log interface {
	Info(msg string,obj ...interface{})
	Debug(msg string,obj ...interface{})
	Error(msg string,obj ...interface{})
	Warning(msg string,obj ...interface{})
}

type logImp struct {
	module string
	factory *LogFactory
}
func (this *logImp)Info(msg string,obj ...interface{}){
   this.factory.Write(LEVEL_INFO,this.module,msg,obj...)
}

func (this *logImp)Debug(msg string,obj ...interface{}){
	this.factory.Write(LEVEL_DEBUG,this.module,msg,obj...)
}
func (this *logImp)Error(msg string,obj ...interface{}){
	this.factory.Write(LEVEL_ERROR,this.module,msg,obj...)
}
func (this *logImp)Warning(msg string,obj ...interface{}){
	this.factory.Write(LEVEL_WARN,this.module,msg,obj...)
}<|endoftext|>


<commit_before>package machinery

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/RichardKnop/machinery/v1/backends/amqp"
	"github.com/RichardKnop/machinery/v1/brokers/errs"
	"github.com/RichardKnop/machinery/v1/log"
	"github.com/RichardKnop/machinery/v1/retry"
	"github.com/RichardKnop/machinery/v1/tasks"
	"github.com/RichardKnop/machinery/v1/tracing"
	"github.com/opentracing/opentracing-go"
)

// Worker represents a single worker process
type Worker struct {
	server          *Server
	ConsumerTag     string
	Concurrency     int
	Queue           string
	errorHandler    func(err error)
	preTaskHandler  func(*tasks.Signature)
	postTaskHandler func(*tasks.Signature)
}

// Launch starts a new worker process. The worker subscribes
// to the default queue and processes incoming registered tasks
func (worker *Worker) Launch() error {
	errorsChan := make(chan error)

	worker.LaunchAsync(errorsChan)

	return <-errorsChan
}

// LaunchAsync is a non blocking version of Launch
func (worker *Worker) LaunchAsync(errorsChan chan<- error) {
	cnf := worker.server.GetConfig()
	broker := worker.server.GetBroker()

	// Log some useful information about worker configuration
	log.INFO.Printf("Launching a worker with the following settings:")
	log.INFO.Printf("- Broker: %s", cnf.Broker)
	if worker.Queue == "" {
		log.INFO.Printf("- DefaultQueue: %s", cnf.DefaultQueue)
	} else {
		log.INFO.Printf("- CustomQueue: %s", worker.Queue)
	}
	log.INFO.Printf("- ResultBackend: %s", cnf.ResultBackend)
	if cnf.AMQP != nil {
		log.INFO.Printf("- AMQP: %s", cnf.AMQP.Exchange)
		log.INFO.Printf("  - Exchange: %s", cnf.AMQP.Exchange)
		log.INFO.Printf("  - ExchangeType: %s", cnf.AMQP.ExchangeType)
		log.INFO.Printf("  - BindingKey: %s", cnf.AMQP.BindingKey)
		log.INFO.Printf("  - PrefetchCount: %d", cnf.AMQP.PrefetchCount)
	}

	// Goroutine to start broker consumption and handle retries when broker connection dies
	go func() {
		for {
			retry, err := broker.StartConsuming(worker.ConsumerTag, worker.Concurrency, worker)

			if retry {
				if worker.errorHandler != nil {
					worker.errorHandler(err)
				} else {
					log.WARNING.Printf("Broker failed with error: %s", err)
				}
			} else {
				errorsChan <- err // stop the goroutine
				return
			}
		}
	}()
	if !cnf.NoUnixSignals {
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
		var signalsReceived uint

		// Goroutine Handle SIGINT and SIGTERM signals
		go func() {
			for {
				select {
				case s := <-sig:
					log.WARNING.Printf("Signal received: %v", s)
					signalsReceived++

					if signalsReceived < 2 {
						// After first Ctrl+C start quitting the worker gracefully
						log.WARNING.Print("Waiting for running tasks to finish before shutting down")
						go func() {
							worker.Quit()
							errorsChan <- errors.New("Worker quit gracefully")
						}()
					} else {
						// Abort the program when user hits Ctrl+C second time in a row
						errorsChan <- errors.New("Worker quit abruptly")
					}
				}
			}
		}()
	}
}

// CustomQueue returns Custom Queue of the running worker process
func (worker *Worker) CustomQueue() string {
	return worker.Queue
}

// Quit tears down the running worker process
func (worker *Worker) Quit() {
	worker.server.GetBroker().StopConsuming()
}

// Process handles received tasks and triggers success/error callbacks
func (worker *Worker) Process(signature *tasks.Signature) error {
	// If the task is not registered with this worker, do not continue
	// but only return nil as we do not want to restart the worker process
	if !worker.server.IsTaskRegistered(signature.Name) {
		return nil
	}

	taskFunc, err := worker.server.GetRegisteredTask(signature.Name)
	if err != nil {
		return nil
	}

	// Update task state to RECEIVED
	if err = worker.server.GetBackend().SetStateReceived(signature); err != nil {
		return fmt.Errorf("Set state to 'received' for task %s returned error: %s", signature.UUID, err)
	}

	// Prepare task for processing
	task, err := tasks.NewWithSignature(taskFunc, signature)
	// if this failed, it means the task is malformed, probably has invalid
	// signature, go directly to task failed without checking whether to retry
	if err != nil {
		worker.taskFailed(signature, err)
		return err
	}

	// try to extract trace span from headers and add it to the function context
	// so it can be used inside the function if it has context.Context as the first
	// argument. Start a new span if it isn't found.
	taskSpan := tracing.StartSpanFromHeaders(signature.Headers, signature.Name)
	tracing.AnnotateSpanWithSignatureInfo(taskSpan, signature)
	task.Context = opentracing.ContextWithSpan(task.Context, taskSpan)

	// Update task state to STARTED
	if err = worker.server.GetBackend().SetStateStarted(signature); err != nil {
		return fmt.Errorf("Set state to 'started' for task %s returned error: %s", signature.UUID, err)
	}

	//Run handler before the task is called
	if worker.preTaskHandler != nil {
		worker.preTaskHandler(signature)
	}

	//Defer run handler for the end of the task
	if worker.postTaskHandler != nil {
		defer worker.postTaskHandler(signature)
	}

	// Call the task
	results, err := task.Call()
	if err != nil {
		// If a tasks.ErrRetryTaskLater was returned from the task,
		// retry the task after specified duration
		retriableErr, ok := interface{}(err).(tasks.ErrRetryTaskLater)
		if ok {
			return worker.retryTaskIn(signature, retriableErr.RetryIn())
		}

		// Otherwise, execute default retry logic based on signature.RetryCount
		// and signature.RetryTimeout values
		if signature.RetryCount > 0 {
			return worker.taskRetry(signature)
		}

		return worker.taskFailed(signature, err)
	}

	return worker.taskSucceeded(signature, results)
}

// retryTask decrements RetryCount counter and republishes the task to the queue
func (worker *Worker) taskRetry(signature *tasks.Signature) error {
	// Update task state to RETRY
	if err := worker.server.GetBackend().SetStateRetry(signature); err != nil {
		return fmt.Errorf("Set state to 'retry' for task %s returned error: %s", signature.UUID, err)
	}

	// Decrement the retry counter, when it reaches 0, we won't retry again
	signature.RetryCount--

	// Increase retry timeout
	signature.RetryTimeout = retry.FibonacciNext(signature.RetryTimeout)

	// Delay task by signature.RetryTimeout seconds
	eta := time.Now().UTC().Add(time.Second * time.Duration(signature.RetryTimeout))
	signature.ETA = &eta

	log.WARNING.Printf("Task %s failed. Going to retry in %d seconds.", signature.UUID, signature.RetryTimeout)

	// Send the task back to the queue
	_, err := worker.server.SendTask(signature)
	return err
}

// taskRetryIn republishes the task to the queue with ETA of now + retryIn.Seconds()
func (worker *Worker) retryTaskIn(signature *tasks.Signature, retryIn time.Duration) error {
	// Update task state to RETRY
	if err := worker.server.GetBackend().SetStateRetry(signature); err != nil {
		return fmt.Errorf("Set state to 'retry' for task %s returned error: %s", signature.UUID, err)
	}

	// Delay task by retryIn duration
	eta := time.Now().UTC().Add(retryIn)
	signature.ETA = &eta

	log.WARNING.Printf("Task %s failed. Going to retry in %.0f seconds.", signature.UUID, retryIn.Seconds())

	// Send the task back to the queue
	_, err := worker.server.SendTask(signature)
	return err
}

// taskSucceeded updates the task state and triggers success callbacks or a
// chord callback if this was the last task of a group with a chord callback
func (worker *Worker) taskSucceeded(signature *tasks.Signature, taskResults []*tasks.TaskResult) error {
	// Update task state to SUCCESS
	if err := worker.server.GetBackend().SetStateSuccess(signature, taskResults); err != nil {
		return fmt.Errorf("Set state to 'success' for task %s returned error: %s", signature.UUID, err)
	}

	// Log human readable results of the processed task
	var debugResults = "[]"
	results, err := tasks.ReflectTaskResults(taskResults)
	if err != nil {
		log.WARNING.Print(err)
	} else {
		debugResults = tasks.HumanReadableResults(results)
	}
	log.DEBUG.Printf("Processed task %s. Results = %s", signature.UUID, debugResults)

	// Trigger success callbacks

	for _, successTask := range signature.OnSuccess {
		if signature.Immutable == false {
			// Pass results of the task to success callbacks
			for _, taskResult := range taskResults {
				successTask.Args = append(successTask.Args, tasks.Arg{
					Type:  taskResult.Type,
					Value: taskResult.Value,
				})
			}
		}

		worker.server.SendTask(successTask)
	}

	// If the task was not part of a group, just return
	if signature.GroupUUID == "" {
		return nil
	}

	// Check if all task in the group has completed
	groupCompleted, err := worker.server.GetBackend().GroupCompleted(
		signature.GroupUUID,
		signature.GroupTaskCount,
	)
	if err != nil {
		return fmt.Errorf("Completed check for group %s returned error: %s", signature.GroupUUID, err)
	}

	// If the group has not yet completed, just return
	if !groupCompleted {
		return nil
	}

	// Defer purging of group meta queue if we are using AMQP backend
	if worker.hasAMQPBackend() {
		defer worker.server.GetBackend().PurgeGroupMeta(signature.GroupUUID)
	}

	// There is no chord callback, just return
	if signature.ChordCallback == nil {
		return nil
	}

	// Trigger chord callback
	shouldTrigger, err := worker.server.GetBackend().TriggerChord(signature.GroupUUID)
	if err != nil {
		return fmt.Errorf("Triggering chord for group %s returned error: %s", signature.GroupUUID, err)
	}

	// Chord has already been triggered
	if !shouldTrigger {
		return nil
	}

	// Get task states
	taskStates, err := worker.server.GetBackend().GroupTaskStates(
		signature.GroupUUID,
		signature.GroupTaskCount,
	)
	if err != nil {
		return nil
	}

	// Append group tasks' return values to chord task if it's not immutable
	for _, taskState := range taskStates {
		if !taskState.IsSuccess() {
			return nil
		}

		if signature.ChordCallback.Immutable == false {
			// Pass results of the task to the chord callback
			for _, taskResult := range taskState.Results {
				signature.ChordCallback.Args = append(signature.ChordCallback.Args, tasks.Arg{
					Type:  taskResult.Type,
					Value: taskResult.Value,
				})
			}
		}
	}

	// Send the chord task
	_, err = worker.server.SendTask(signature.ChordCallback)
	if err != nil {
		return err
	}

	return nil
}

// taskFailed updates the task state and triggers error callbacks
func (worker *Worker) taskFailed(signature *tasks.Signature, taskErr error) error {
	// Update task state to FAILURE
	if err := worker.server.GetBackend().SetStateFailure(signature, taskErr.Error()); err != nil {
		return fmt.Errorf("Set state to 'failure' for task %s returned error: %s", signature.UUID, err)
	}

	if worker.errorHandler != nil {
		worker.errorHandler(taskErr)
	} else {
		log.ERROR.Printf("Failed processing task %s. Error = %v", signature.UUID, taskErr)
	}

	// Trigger error callbacks
	for _, errorTask := range signature.OnError {
		// Pass error as a first argument to error callbacks
		args := append([]tasks.Arg{{
			Type:  "string",
			Value: taskErr.Error(),
		}}, errorTask.Args...)
		errorTask.Args = args
		worker.server.SendTask(errorTask)
	}

	if signature.StopTaskDeletionOnError {
		return errs.ErrStopTaskDeletion
	}

	return nil
}

// Returns true if the worker uses AMQP backend
func (worker *Worker) hasAMQPBackend() bool {
	_, ok := worker.server.GetBackend().(*amqp.Backend)
	return ok
}

// SetErrorHandler sets a custom error handler for task errors
// A default behavior is just to log the error after all the retry attempts fail
func (worker *Worker) SetErrorHandler(handler func(err error)) {
	worker.errorHandler = handler
}

//SetPreTaskHandler sets a custom handler func before a job is started
func (worker *Worker) SetPreTaskHandler(handler func(*tasks.Signature)) {
	worker.preTaskHandler = handler
}

//SetPostTaskHandler sets a custom handler for the end of a job
func (worker *Worker) SetPostTaskHandler(handler func(*tasks.Signature)) {
	worker.postTaskHandler = handler
}

//GetServer returns server
func (worker *Worker) GetServer() *Server {
	return worker.server
}
<commit_msg>Fix #445 - Define worker errors as variables (#475)<commit_after>package machinery

import (
	"errors"
	"fmt"
	"os"
	"os/signal"
	"syscall"
	"time"

	"github.com/RichardKnop/machinery/v1/backends/amqp"
	"github.com/RichardKnop/machinery/v1/brokers/errs"
	"github.com/RichardKnop/machinery/v1/log"
	"github.com/RichardKnop/machinery/v1/retry"
	"github.com/RichardKnop/machinery/v1/tasks"
	"github.com/RichardKnop/machinery/v1/tracing"
	"github.com/opentracing/opentracing-go"
)

// Worker represents a single worker process
type Worker struct {
	server          *Server
	ConsumerTag     string
	Concurrency     int
	Queue           string
	errorHandler    func(err error)
	preTaskHandler  func(*tasks.Signature)
	postTaskHandler func(*tasks.Signature)
}

var (
	// ErrWorkerQuitGracefully is return when worker quit gracefully
	ErrWorkerQuitGracefully = errors.New("Worker quit gracefully")
	// ErrWorkerQuitGracefully is return when worker quit abruptly
	ErrWorkerQuitAbruptly = errors.New("Worker quit abruptly")
)

// Launch starts a new worker process. The worker subscribes
// to the default queue and processes incoming registered tasks
func (worker *Worker) Launch() error {
	errorsChan := make(chan error)

	worker.LaunchAsync(errorsChan)

	return <-errorsChan
}

// LaunchAsync is a non blocking version of Launch
func (worker *Worker) LaunchAsync(errorsChan chan<- error) {
	cnf := worker.server.GetConfig()
	broker := worker.server.GetBroker()

	// Log some useful information about worker configuration
	log.INFO.Printf("Launching a worker with the following settings:")
	log.INFO.Printf("- Broker: %s", cnf.Broker)
	if worker.Queue == "" {
		log.INFO.Printf("- DefaultQueue: %s", cnf.DefaultQueue)
	} else {
		log.INFO.Printf("- CustomQueue: %s", worker.Queue)
	}
	log.INFO.Printf("- ResultBackend: %s", cnf.ResultBackend)
	if cnf.AMQP != nil {
		log.INFO.Printf("- AMQP: %s", cnf.AMQP.Exchange)
		log.INFO.Printf("  - Exchange: %s", cnf.AMQP.Exchange)
		log.INFO.Printf("  - ExchangeType: %s", cnf.AMQP.ExchangeType)
		log.INFO.Printf("  - BindingKey: %s", cnf.AMQP.BindingKey)
		log.INFO.Printf("  - PrefetchCount: %d", cnf.AMQP.PrefetchCount)
	}

	// Goroutine to start broker consumption and handle retries when broker connection dies
	go func() {
		for {
			retry, err := broker.StartConsuming(worker.ConsumerTag, worker.Concurrency, worker)

			if retry {
				if worker.errorHandler != nil {
					worker.errorHandler(err)
				} else {
					log.WARNING.Printf("Broker failed with error: %s", err)
				}
			} else {
				errorsChan <- err // stop the goroutine
				return
			}
		}
	}()
	if !cnf.NoUnixSignals {
		sig := make(chan os.Signal, 1)
		signal.Notify(sig, os.Interrupt, syscall.SIGTERM)
		var signalsReceived uint

		// Goroutine Handle SIGINT and SIGTERM signals
		go func() {
			for {
				select {
				case s := <-sig:
					log.WARNING.Printf("Signal received: %v", s)
					signalsReceived++

					if signalsReceived < 2 {
						// After first Ctrl+C start quitting the worker gracefully
						log.WARNING.Print("Waiting for running tasks to finish before shutting down")
						go func() {
							worker.Quit()
							errorsChan <- ErrWorkerQuitGracefully
						}()
					} else {
						// Abort the program when user hits Ctrl+C second time in a row
						errorsChan <- ErrWorkerQuitAbruptly
					}
				}
			}
		}()
	}
}

// CustomQueue returns Custom Queue of the running worker process
func (worker *Worker) CustomQueue() string {
	return worker.Queue
}

// Quit tears down the running worker process
func (worker *Worker) Quit() {
	worker.server.GetBroker().StopConsuming()
}

// Process handles received tasks and triggers success/error callbacks
func (worker *Worker) Process(signature *tasks.Signature) error {
	// If the task is not registered with this worker, do not continue
	// but only return nil as we do not want to restart the worker process
	if !worker.server.IsTaskRegistered(signature.Name) {
		return nil
	}

	taskFunc, err := worker.server.GetRegisteredTask(signature.Name)
	if err != nil {
		return nil
	}

	// Update task state to RECEIVED
	if err = worker.server.GetBackend().SetStateReceived(signature); err != nil {
		return fmt.Errorf("Set state to 'received' for task %s returned error: %s", signature.UUID, err)
	}

	// Prepare task for processing
	task, err := tasks.NewWithSignature(taskFunc, signature)
	// if this failed, it means the task is malformed, probably has invalid
	// signature, go directly to task failed without checking whether to retry
	if err != nil {
		worker.taskFailed(signature, err)
		return err
	}

	// try to extract trace span from headers and add it to the function context
	// so it can be used inside the function if it has context.Context as the first
	// argument. Start a new span if it isn't found.
	taskSpan := tracing.StartSpanFromHeaders(signature.Headers, signature.Name)
	tracing.AnnotateSpanWithSignatureInfo(taskSpan, signature)
	task.Context = opentracing.ContextWithSpan(task.Context, taskSpan)

	// Update task state to STARTED
	if err = worker.server.GetBackend().SetStateStarted(signature); err != nil {
		return fmt.Errorf("Set state to 'started' for task %s returned error: %s", signature.UUID, err)
	}

	//Run handler before the task is called
	if worker.preTaskHandler != nil {
		worker.preTaskHandler(signature)
	}

	//Defer run handler for the end of the task
	if worker.postTaskHandler != nil {
		defer worker.postTaskHandler(signature)
	}

	// Call the task
	results, err := task.Call()
	if err != nil {
		// If a tasks.ErrRetryTaskLater was returned from the task,
		// retry the task after specified duration
		retriableErr, ok := interface{}(err).(tasks.ErrRetryTaskLater)
		if ok {
			return worker.retryTaskIn(signature, retriableErr.RetryIn())
		}

		// Otherwise, execute default retry logic based on signature.RetryCount
		// and signature.RetryTimeout values
		if signature.RetryCount > 0 {
			return worker.taskRetry(signature)
		}

		return worker.taskFailed(signature, err)
	}

	return worker.taskSucceeded(signature, results)
}

// retryTask decrements RetryCount counter and republishes the task to the queue
func (worker *Worker) taskRetry(signature *tasks.Signature) error {
	// Update task state to RETRY
	if err := worker.server.GetBackend().SetStateRetry(signature); err != nil {
		return fmt.Errorf("Set state to 'retry' for task %s returned error: %s", signature.UUID, err)
	}

	// Decrement the retry counter, when it reaches 0, we won't retry again
	signature.RetryCount--

	// Increase retry timeout
	signature.RetryTimeout = retry.FibonacciNext(signature.RetryTimeout)

	// Delay task by signature.RetryTimeout seconds
	eta := time.Now().UTC().Add(time.Second * time.Duration(signature.RetryTimeout))
	signature.ETA = &eta

	log.WARNING.Printf("Task %s failed. Going to retry in %d seconds.", signature.UUID, signature.RetryTimeout)

	// Send the task back to the queue
	_, err := worker.server.SendTask(signature)
	return err
}

// taskRetryIn republishes the task to the queue with ETA of now + retryIn.Seconds()
func (worker *Worker) retryTaskIn(signature *tasks.Signature, retryIn time.Duration) error {
	// Update task state to RETRY
	if err := worker.server.GetBackend().SetStateRetry(signature); err != nil {
		return fmt.Errorf("Set state to 'retry' for task %s returned error: %s", signature.UUID, err)
	}

	// Delay task by retryIn duration
	eta := time.Now().UTC().Add(retryIn)
	signature.ETA = &eta

	log.WARNING.Printf("Task %s failed. Going to retry in %.0f seconds.", signature.UUID, retryIn.Seconds())

	// Send the task back to the queue
	_, err := worker.server.SendTask(signature)
	return err
}

// taskSucceeded updates the task state and triggers success callbacks or a
// chord callback if this was the last task of a group with a chord callback
func (worker *Worker) taskSucceeded(signature *tasks.Signature, taskResults []*tasks.TaskResult) error {
	// Update task state to SUCCESS
	if err := worker.server.GetBackend().SetStateSuccess(signature, taskResults); err != nil {
		return fmt.Errorf("Set state to 'success' for task %s returned error: %s", signature.UUID, err)
	}

	// Log human readable results of the processed task
	var debugResults = "[]"
	results, err := tasks.ReflectTaskResults(taskResults)
	if err != nil {
		log.WARNING.Print(err)
	} else {
		debugResults = tasks.HumanReadableResults(results)
	}
	log.DEBUG.Printf("Processed task %s. Results = %s", signature.UUID, debugResults)

	// Trigger success callbacks

	for _, successTask := range signature.OnSuccess {
		if signature.Immutable == false {
			// Pass results of the task to success callbacks
			for _, taskResult := range taskResults {
				successTask.Args = append(successTask.Args, tasks.Arg{
					Type:  taskResult.Type,
					Value: taskResult.Value,
				})
			}
		}

		worker.server.SendTask(successTask)
	}

	// If the task was not part of a group, just return
	if signature.GroupUUID == "" {
		return nil
	}

	// Check if all task in the group has completed
	groupCompleted, err := worker.server.GetBackend().GroupCompleted(
		signature.GroupUUID,
		signature.GroupTaskCount,
	)
	if err != nil {
		return fmt.Errorf("Completed check for group %s returned error: %s", signature.GroupUUID, err)
	}

	// If the group has not yet completed, just return
	if !groupCompleted {
		return nil
	}

	// Defer purging of group meta queue if we are using AMQP backend
	if worker.hasAMQPBackend() {
		defer worker.server.GetBackend().PurgeGroupMeta(signature.GroupUUID)
	}

	// There is no chord callback, just return
	if signature.ChordCallback == nil {
		return nil
	}

	// Trigger chord callback
	shouldTrigger, err := worker.server.GetBackend().TriggerChord(signature.GroupUUID)
	if err != nil {
		return fmt.Errorf("Triggering chord for group %s returned error: %s", signature.GroupUUID, err)
	}

	// Chord has already been triggered
	if !shouldTrigger {
		return nil
	}

	// Get task states
	taskStates, err := worker.server.GetBackend().GroupTaskStates(
		signature.GroupUUID,
		signature.GroupTaskCount,
	)
	if err != nil {
		return nil
	}

	// Append group tasks' return values to chord task if it's not immutable
	for _, taskState := range taskStates {
		if !taskState.IsSuccess() {
			return nil
		}

		if signature.ChordCallback.Immutable == false {
			// Pass results of the task to the chord callback
			for _, taskResult := range taskState.Results {
				signature.ChordCallback.Args = append(signature.ChordCallback.Args, tasks.Arg{
					Type:  taskResult.Type,
					Value: taskResult.Value,
				})
			}
		}
	}

	// Send the chord task
	_, err = worker.server.SendTask(signature.ChordCallback)
	if err != nil {
		return err
	}

	return nil
}

// taskFailed updates the task state and triggers error callbacks
func (worker *Worker) taskFailed(signature *tasks.Signature, taskErr error) error {
	// Update task state to FAILURE
	if err := worker.server.GetBackend().SetStateFailure(signature, taskErr.Error()); err != nil {
		return fmt.Errorf("Set state to 'failure' for task %s returned error: %s", signature.UUID, err)
	}

	if worker.errorHandler != nil {
		worker.errorHandler(taskErr)
	} else {
		log.ERROR.Printf("Failed processing task %s. Error = %v", signature.UUID, taskErr)
	}

	// Trigger error callbacks
	for _, errorTask := range signature.OnError {
		// Pass error as a first argument to error callbacks
		args := append([]tasks.Arg{{
			Type:  "string",
			Value: taskErr.Error(),
		}}, errorTask.Args...)
		errorTask.Args = args
		worker.server.SendTask(errorTask)
	}

	if signature.StopTaskDeletionOnError {
		return errs.ErrStopTaskDeletion
	}

	return nil
}

// Returns true if the worker uses AMQP backend
func (worker *Worker) hasAMQPBackend() bool {
	_, ok := worker.server.GetBackend().(*amqp.Backend)
	return ok
}

// SetErrorHandler sets a custom error handler for task errors
// A default behavior is just to log the error after all the retry attempts fail
func (worker *Worker) SetErrorHandler(handler func(err error)) {
	worker.errorHandler = handler
}

//SetPreTaskHandler sets a custom handler func before a job is started
func (worker *Worker) SetPreTaskHandler(handler func(*tasks.Signature)) {
	worker.preTaskHandler = handler
}

//SetPostTaskHandler sets a custom handler for the end of a job
func (worker *Worker) SetPostTaskHandler(handler func(*tasks.Signature)) {
	worker.postTaskHandler = handler
}

//GetServer returns server
func (worker *Worker) GetServer() *Server {
	return worker.server
}
<|endoftext|>


<commit_before>/**
 * Package validator
 *
 * MISC:
 * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank
 *
 */

package validator

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"time"
	"unicode"
)

const (
	utf8HexComma        = "0x2C"
	utf8Pipe            = "0x7C"
	tagSeparator        = ","
	orSeparator         = "|"
	tagKeySeparator     = "="
	structOnlyTag       = "structonly"
	omitempty           = "omitempty"
	skipValidationTag   = "-"
	diveTag             = "dive"
	fieldErrMsg         = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag"
	arrayIndexFieldName = "%s[%d]"
	mapIndexFieldName   = "%s[%v]"
	invalidValidation   = "Invalid validation tag on field %s"
	undefinedValidation = "Undefined validation function on field %s"
)

var (
	timeType    = reflect.TypeOf(time.Time{})
	timePtrType = reflect.TypeOf(&time.Time{})
)

// Validate implements the Validate Struct
// NOTE: Fields within are not thread safe and that is on purpose
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// or make it thread safe on your end
type Validate struct {
	config Config
}

// Config contains the options that Validator with use
// passed to the New function
type Config struct {
	TagName         string
	ValidationFuncs map[string]Func
}

// Func accepts all values needed for file and cross field validation
// topStruct     = top level struct when validating by struct otherwise nil
// currentStruct = current level struct when validating by struct otherwise optional comparison value
// field         = field value for validation
// param         = parameter used in validation i.e. gt=0 param would be 0
type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool

// ValidationErrors is a type of map[string]*FieldError
// it exists to allow for multiple errors passed from this library
// and yet still comply to the error interface
type ValidationErrors map[string]*FieldError

// This is intended for use in development + debugging and not intended to be a production error message.
// It allows ValidationErrors to subscribe to the Error interface.
// All information to create an error message specific to your application is contained within
// the FieldError found in the ValidationErrors
func (ve ValidationErrors) Error() string {

	buff := bytes.NewBufferString("")

	for key, err := range ve {
		buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag))
	}

	return strings.TrimSpace(buff.String())
}

// FieldError contains a single field's validation error along
// with other properties that may be needed for error message creation
type FieldError struct {
	Field string
	Tag   string
	Kind  reflect.Kind
	Type  reflect.Type
	Param string
	Value interface{}
	// IsPlaceholderErr bool
	// IsSliceOrArray   bool
	// IsMap            bool
	// SliceOrArrayErrs map[int]error         // counld be FieldError, StructErrors
	// MapErrs          map[interface{}]error // counld be FieldError, StructErrors
}

// New creates a new Validate instance for use.
func New(config Config) *Validate {

	// structPool = &sync.Pool{New: newStructErrors}

	return &Validate{config: config}
}

// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
// NOTE: this method is not thread-safe
func (v *Validate) RegisterValidation(key string, f Func) error {

	if len(key) == 0 {
		return errors.New("Function Key cannot be empty")
	}

	if f == nil {
		return errors.New("Function cannot be empty")
	}

	v.config.ValidationFuncs[key] = f

	return nil
}

// Field allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validate) Field(field interface{}, tag string) ValidationErrors {

	errs := map[string]*FieldError{}
	fieldVal := reflect.ValueOf(field)

	v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "")

	if len(errs) == 0 {
		return nil
	}

	return errs
}

// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors {

	errs := map[string]*FieldError{}
	topVal := reflect.ValueOf(val)

	v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "")

	if len(errs) == 0 {
		return nil
	}

	return errs
}

// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// the Array or Map.
func (v *Validate) Struct(current interface{}) ValidationErrors {

	errs := map[string]*FieldError{}
	sv := reflect.ValueOf(current)

	v.tranverseStruct(sv, sv, sv, "", errs, true)

	if len(errs) == 0 {
		return nil
	}

	return errs
}

func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool) {

	if current.Kind() == reflect.Ptr && !current.IsNil() {
		current = current.Elem()
	}

	if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface {
		panic("value passed for validation is not a struct")
	}

	typ := current.Type()

	if useStructName {
		errPrefix += typ.Name() + "."
	}

	numFields := current.NumField()

	var fld reflect.StructField

	for i := 0; i < numFields; i++ {
		fld = typ.Field(i)

		if !unicode.IsUpper(rune(fld.Name[0])) {
			continue
		}

		v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.config.TagName), fld.Name)
	}
}

func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) {

	if tag == skipValidationTag {
		return
	}

	kind := current.Kind()

	if kind == reflect.Ptr && !current.IsNil() {
		current = current.Elem()
		kind = current.Kind()
	}

	typ := current.Type()

	// this also allows for tags 'required' and 'omitempty' to be used on
	// nested struct fields because when len(tags) > 0 below and the value is nil
	// then required failes and we check for omitempty just before that
	if (kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil() {

		if strings.Contains(tag, omitempty) {
			return
		}

		tags := strings.Split(tag, tagSeparator)

		if len(tags) > 0 {

			var param string
			vals := strings.SplitN(tags[0], tagKeySeparator, 2)

			if len(vals) > 1 {
				param = vals[1]
			}

			errs[errPrefix+name] = &FieldError{
				Field: name,
				Tag:   vals[0],
				Param: param,
				Value: current.Interface(),
				Kind:  kind,
				Type:  typ,
			}

			return
		}
	}

	switch kind {
	case reflect.Struct, reflect.Interface:

		if kind == reflect.Interface {

			current = current.Elem()
			kind = current.Kind()

			if kind == reflect.Ptr && !current.IsNil() {
				current = current.Elem()
				kind = current.Kind()
			}

			// changed current, so have to get inner type again
			typ = current.Type()

			if kind != reflect.Struct {
				goto FALLTHROUGH
			}
		}

		if typ != timeType && typ != timePtrType {

			if kind == reflect.Struct {

				// required passed validationa above so stop here
				// if only validating the structs existance.
				if strings.Contains(tag, structOnlyTag) {
					return
				}

				v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false)
				return
			}
		}
	FALLTHROUGH:
		fallthrough
	default:
		if len(tag) == 0 {
			return
		}
	}

	var dive bool
	var diveSubTag string

	for _, t := range strings.Split(tag, tagSeparator) {

		if t == diveTag {

			dive = true
			diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",")
			break
		}

		// no use in checking tags if it's empty and is ok to be
		// omitempty needs to be the first tag if you wish to use it
		if t == omitempty {

			if !hasValue(topStruct, currentStruct, current, typ, kind, "") {
				return
			}
			continue
		}

		var key string
		var param string

		// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
		if strings.Index(t, orSeparator) == -1 {
			vals := strings.SplitN(t, tagKeySeparator, 2)
			key = vals[0]

			if len(key) == 0 {
				panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name)))
			}

			if len(vals) > 1 {
				param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
			}
		} else {
			key = t
		}

		if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, key, param, name) {
			return
		}
	}

	if dive {
		// traverse slice or map here
		// or panic ;)
		switch kind {
		case reflect.Slice, reflect.Array:
			v.traverseSlice(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name)
		case reflect.Map:
			v.traverseMap(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name)
		default:
			// throw error, if not a slice or map then should not have gotten here
			// bad dive tag
			panic("dive error! can't dive on a non slice or map")
		}
	}
}

func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) {

	for i := 0; i < current.Len(); i++ {

		idxField := current.Index(i)

		if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
			idxField = idxField.Elem()
		}

		v.traverseField(topStruct, currentStruct, idxField, errPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i))
	}
}

func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) {

	for _, key := range current.MapKeys() {

		idxField := current.MapIndex(key)

		if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
			idxField = idxField.Elem()
		}

		v.traverseField(topStruct, currentStruct, idxField, errPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface()))
	}
}

// validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok
func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool {

	// check if key is orVals, it could be!
	orVals := strings.Split(key, orSeparator)

	if len(orVals) > 1 {

		var errTag string

		for _, val := range orVals {
			vals := strings.SplitN(val, tagKeySeparator, 2)

			if len(vals[0]) == 0 {
				panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name)))
			}

			param := ""
			if len(vals) > 1 {
				param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
			}

			// validate and keep track!
			valFunc, ok := v.config.ValidationFuncs[vals[0]]
			if !ok {
				panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
			}

			if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) {
				return false
			}

			errTag += orSeparator + vals[0]
		}

		errs[errPrefix+name] = &FieldError{
			Field: name,
			Tag:   errTag[1:],
			Value: current.Interface(),
			Param: param,
			Type:  currentType,
			Kind:  currentKind,
		}

		return true
	}

	valFunc, ok := v.config.ValidationFuncs[key]
	if !ok {
		panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
	}

	if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) {
		return false
	}

	errs[errPrefix+name] = &FieldError{
		Field: name,
		Tag:   key,
		Value: current.Interface(),
		Param: param,
		Type:  currentType,
		Kind:  currentKind,
	}

	return true
}
<commit_msg>Add ValidationErrs Caching<commit_after>/**
 * Package validator
 *
 * MISC:
 * - anonymous structs - they don't have names so expect the Struct name within StructErrors to be blank
 *
 */

package validator

import (
	"bytes"
	"errors"
	"fmt"
	"reflect"
	"strings"
	"sync"
	"time"
	"unicode"
)

const (
	utf8HexComma        = "0x2C"
	utf8Pipe            = "0x7C"
	tagSeparator        = ","
	orSeparator         = "|"
	tagKeySeparator     = "="
	structOnlyTag       = "structonly"
	omitempty           = "omitempty"
	skipValidationTag   = "-"
	diveTag             = "dive"
	fieldErrMsg         = "Key: \"%s\" Error:Field validation for \"%s\" failed on the \"%s\" tag"
	arrayIndexFieldName = "%s[%d]"
	mapIndexFieldName   = "%s[%v]"
	invalidValidation   = "Invalid validation tag on field %s"
	undefinedValidation = "Undefined validation function on field %s"
)

var (
	timeType    = reflect.TypeOf(time.Time{})
	timePtrType = reflect.TypeOf(&time.Time{})
	errsPool    = &sync.Pool{New: newValidationErrors}
)

// returns new ValidationErrors to the pool
func newValidationErrors() interface{} {
	return map[string]*FieldError{}
}

// Validate implements the Validate Struct
// NOTE: Fields within are not thread safe and that is on purpose
// Functions and Tags should all be predifined before use, so subscribe to the philosiphy
// or make it thread safe on your end
type Validate struct {
	config Config
}

// Config contains the options that Validator with use
// passed to the New function
type Config struct {
	TagName         string
	ValidationFuncs map[string]Func
}

// Func accepts all values needed for file and cross field validation
// topStruct     = top level struct when validating by struct otherwise nil
// currentStruct = current level struct when validating by struct otherwise optional comparison value
// field         = field value for validation
// param         = parameter used in validation i.e. gt=0 param would be 0
type Func func(topStruct reflect.Value, currentStruct reflect.Value, field reflect.Value, fieldtype reflect.Type, fieldKind reflect.Kind, param string) bool

// ValidationErrors is a type of map[string]*FieldError
// it exists to allow for multiple errors passed from this library
// and yet still comply to the error interface
type ValidationErrors map[string]*FieldError

// This is intended for use in development + debugging and not intended to be a production error message.
// It allows ValidationErrors to subscribe to the Error interface.
// All information to create an error message specific to your application is contained within
// the FieldError found in the ValidationErrors
func (ve ValidationErrors) Error() string {

	buff := bytes.NewBufferString("")

	for key, err := range ve {
		buff.WriteString(fmt.Sprintf(fieldErrMsg, key, err.Field, err.Tag))
	}

	return strings.TrimSpace(buff.String())
}

// FieldError contains a single field's validation error along
// with other properties that may be needed for error message creation
type FieldError struct {
	Field string
	Tag   string
	Kind  reflect.Kind
	Type  reflect.Type
	Param string
	Value interface{}
}

// New creates a new Validate instance for use.
func New(config Config) *Validate {
	return &Validate{config: config}
}

// RegisterValidation adds a validation Func to a Validate's map of validators denoted by the key
// NOTE: if the key already exists, it will get replaced.
// NOTE: this method is not thread-safe
func (v *Validate) RegisterValidation(key string, f Func) error {

	if len(key) == 0 {
		return errors.New("Function Key cannot be empty")
	}

	if f == nil {
		return errors.New("Function cannot be empty")
	}

	v.config.ValidationFuncs[key] = f

	return nil
}

// Field allows validation of a single field, still using tag style validation to check multiple errors
func (v *Validate) Field(field interface{}, tag string) ValidationErrors {

	errs := errsPool.Get().(map[string]*FieldError)
	fieldVal := reflect.ValueOf(field)

	v.traverseField(fieldVal, fieldVal, fieldVal, "", errs, false, tag, "")

	if len(errs) == 0 {
		errsPool.Put(errs)
		return nil
	}

	return errs
}

// FieldWithValue allows validation of a single field, possibly even against another fields value, still using tag style validation to check multiple errors
func (v *Validate) FieldWithValue(val interface{}, field interface{}, tag string) ValidationErrors {

	errs := errsPool.Get().(map[string]*FieldError)
	topVal := reflect.ValueOf(val)

	v.traverseField(topVal, topVal, reflect.ValueOf(field), "", errs, false, tag, "")

	if len(errs) == 0 {
		errsPool.Put(errs)
		return nil
	}

	return errs
}

// Struct validates a struct, even it's nested structs, and returns a struct containing the errors
// NOTE: Nested Arrays, or Maps of structs do not get validated only the Array or Map itself; the reason is that there is no good
// way to represent or report which struct within the array has the error, besides can validate the struct prior to adding it to
// the Array or Map.
func (v *Validate) Struct(current interface{}) ValidationErrors {

	errs := errsPool.Get().(map[string]*FieldError)
	sv := reflect.ValueOf(current)

	v.tranverseStruct(sv, sv, sv, "", errs, true)

	if len(errs) == 0 {
		errsPool.Put(errs)
		return nil
	}

	return errs
}

func (v *Validate) tranverseStruct(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, useStructName bool) {

	if current.Kind() == reflect.Ptr && !current.IsNil() {
		current = current.Elem()
	}

	if current.Kind() != reflect.Struct && current.Kind() != reflect.Interface {
		panic("value passed for validation is not a struct")
	}

	typ := current.Type()

	if useStructName {
		errPrefix += typ.Name() + "."
	}

	numFields := current.NumField()

	var fld reflect.StructField

	for i := 0; i < numFields; i++ {
		fld = typ.Field(i)

		if !unicode.IsUpper(rune(fld.Name[0])) {
			continue
		}

		v.traverseField(topStruct, currentStruct, current.Field(i), errPrefix, errs, true, fld.Tag.Get(v.config.TagName), fld.Name)
	}
}

func (v *Validate) traverseField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, isStructField bool, tag string, name string) {

	if tag == skipValidationTag {
		return
	}

	kind := current.Kind()

	if kind == reflect.Ptr && !current.IsNil() {
		current = current.Elem()
		kind = current.Kind()
	}

	typ := current.Type()

	// this also allows for tags 'required' and 'omitempty' to be used on
	// nested struct fields because when len(tags) > 0 below and the value is nil
	// then required failes and we check for omitempty just before that
	if (kind == reflect.Ptr || kind == reflect.Interface) && current.IsNil() {

		if strings.Contains(tag, omitempty) {
			return
		}

		tags := strings.Split(tag, tagSeparator)

		if len(tags) > 0 {

			var param string
			vals := strings.SplitN(tags[0], tagKeySeparator, 2)

			if len(vals) > 1 {
				param = vals[1]
			}

			errs[errPrefix+name] = &FieldError{
				Field: name,
				Tag:   vals[0],
				Param: param,
				Value: current.Interface(),
				Kind:  kind,
				Type:  typ,
			}

			return
		}
	}

	switch kind {
	case reflect.Struct, reflect.Interface:

		if kind == reflect.Interface {

			current = current.Elem()
			kind = current.Kind()

			if kind == reflect.Ptr && !current.IsNil() {
				current = current.Elem()
				kind = current.Kind()
			}

			// changed current, so have to get inner type again
			typ = current.Type()

			if kind != reflect.Struct {
				goto FALLTHROUGH
			}
		}

		if typ != timeType && typ != timePtrType {

			if kind == reflect.Struct {

				// required passed validationa above so stop here
				// if only validating the structs existance.
				if strings.Contains(tag, structOnlyTag) {
					return
				}

				v.tranverseStruct(topStruct, current, current, errPrefix+name+".", errs, false)
				return
			}
		}
	FALLTHROUGH:
		fallthrough
	default:
		if len(tag) == 0 {
			return
		}
	}

	var dive bool
	var diveSubTag string

	for _, t := range strings.Split(tag, tagSeparator) {

		if t == diveTag {

			dive = true
			diveSubTag = strings.TrimLeft(strings.SplitN(tag, diveTag, 2)[1], ",")
			break
		}

		// no use in checking tags if it's empty and is ok to be
		// omitempty needs to be the first tag if you wish to use it
		if t == omitempty {

			if !hasValue(topStruct, currentStruct, current, typ, kind, "") {
				return
			}
			continue
		}

		var key string
		var param string

		// if a pipe character is needed within the param you must use the utf8Pipe representation "0x7C"
		if strings.Index(t, orSeparator) == -1 {
			vals := strings.SplitN(t, tagKeySeparator, 2)
			key = vals[0]

			if len(key) == 0 {
				panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name)))
			}

			if len(vals) > 1 {
				param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
			}
		} else {
			key = t
		}

		if v.validateField(topStruct, currentStruct, current, typ, kind, errPrefix, errs, key, param, name) {
			return
		}
	}

	if dive {
		// traverse slice or map here
		// or panic ;)
		switch kind {
		case reflect.Slice, reflect.Array:
			v.traverseSlice(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name)
		case reflect.Map:
			v.traverseMap(topStruct, currentStruct, current, errPrefix, errs, diveSubTag, name)
		default:
			// throw error, if not a slice or map then should not have gotten here
			// bad dive tag
			panic("dive error! can't dive on a non slice or map")
		}
	}
}

func (v *Validate) traverseSlice(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) {

	for i := 0; i < current.Len(); i++ {

		idxField := current.Index(i)

		if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
			idxField = idxField.Elem()
		}

		v.traverseField(topStruct, currentStruct, idxField, errPrefix, errs, false, tag, fmt.Sprintf(arrayIndexFieldName, name, i))
	}
}

func (v *Validate) traverseMap(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, errPrefix string, errs ValidationErrors, tag string, name string) {

	for _, key := range current.MapKeys() {

		idxField := current.MapIndex(key)

		if idxField.Kind() == reflect.Ptr && !idxField.IsNil() {
			idxField = idxField.Elem()
		}

		v.traverseField(topStruct, currentStruct, idxField, errPrefix, errs, false, tag, fmt.Sprintf(mapIndexFieldName, name, key.Interface()))
	}
}

// validateField validates a field based on the provided key tag and param and return true if there is an error false if all ok
func (v *Validate) validateField(topStruct reflect.Value, currentStruct reflect.Value, current reflect.Value, currentType reflect.Type, currentKind reflect.Kind, errPrefix string, errs ValidationErrors, key string, param string, name string) bool {

	// check if key is orVals, it could be!
	orVals := strings.Split(key, orSeparator)

	if len(orVals) > 1 {

		var errTag string

		for _, val := range orVals {
			vals := strings.SplitN(val, tagKeySeparator, 2)

			if len(vals[0]) == 0 {
				panic(strings.TrimSpace(fmt.Sprintf(invalidValidation, name)))
			}

			param := ""
			if len(vals) > 1 {
				param = strings.Replace(strings.Replace(vals[1], utf8HexComma, ",", -1), utf8Pipe, "|", -1)
			}

			// validate and keep track!
			valFunc, ok := v.config.ValidationFuncs[vals[0]]
			if !ok {
				panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
			}

			if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) {
				return false
			}

			errTag += orSeparator + vals[0]
		}

		errs[errPrefix+name] = &FieldError{
			Field: name,
			Tag:   errTag[1:],
			Value: current.Interface(),
			Param: param,
			Type:  currentType,
			Kind:  currentKind,
		}

		return true
	}

	valFunc, ok := v.config.ValidationFuncs[key]
	if !ok {
		panic(strings.TrimSpace(fmt.Sprintf(undefinedValidation, name)))
	}

	if valFunc(topStruct, currentStruct, current, currentType, currentKind, param) {
		return false
	}

	errs[errPrefix+name] = &FieldError{
		Field: name,
		Tag:   key,
		Value: current.Interface(),
		Param: param,
		Type:  currentType,
		Kind:  currentKind,
	}

	return true
}
<|endoftext|>


<commit_before>package main

import (
	"database/sql"

	"github.com/1and1/soma/internal/tree"
	uuid "github.com/satori/go.uuid"
)

func (tk *treeKeeper) startupSystemProperties(stMap map[string]*sql.Stmt) {
	if tk.broken {
		return
	}

	var (
		err                                                       error
		instanceId, srcInstanceId, objectId, view, systemProperty string
		inInstanceId, inObjectType, inObjId, sourceType, value    string
		inheritance, childrenOnly                                 bool
		rows, instance_rows                                       *sql.Rows
	)

	for loopType, loopStmt := range map[string]string{
		`repository`: `LoadPropRepoSystem`,
		`bucket`:     `LoadPropBuckSystem`,
		`group`:      `LoadPropGrpSystem`,
		`cluster`:    `LoadPropClrSystem`,
		`node`:       `LoadPropNodeSystem`,
	} {

		tk.startLog.Printf("TK[%s]: loading %s system properties\n", tk.repoName, loopType)
		rows, err = stMap[loopStmt].Query(tk.repoId)
		if err != nil {
			tk.startLog.Printf("TK[%s] Error loading %s system properties: %s", tk.repoName, loopType, err.Error())
			tk.broken = true
			return
		}
		defer rows.Close()

	systemloop:
		// load all system properties defined directly on objects
		for rows.Next() {
			err = rows.Scan(
				&instanceId,
				&srcInstanceId,
				&objectId,
				&view,
				&systemProperty,
				&sourceType,
				&inheritance,
				&childrenOnly,
				&value,
			)
			if err != nil {
				if err == sql.ErrNoRows {
					break systemloop
				}
				tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
				tk.broken = true
				return
			}

			// build the property
			prop := tree.PropertySystem{
				Inheritance:  inheritance,
				ChildrenOnly: childrenOnly,
				View:         view,
				Key:          systemProperty,
				Value:        value,
			}
			prop.Id, _ = uuid.FromString(instanceId)
			prop.Instances = make([]tree.PropertyInstance, 0)

			instance_rows, err = stMap[`LoadPropSystemInstance`].Query(
				tk.repoId,
				srcInstanceId,
			)
			if err != nil {
				tk.startLog.Printf("TK[%s] Error loading %s system properties: %s", tk.repoName, loopType, err.Error())
				tk.broken = true
				return
			}
			defer instance_rows.Close()

		inproploop:
			// load all all ids for properties that were inherited from the
			// current group system property so the IDs can be set correctly
			for instance_rows.Next() {
				err = instance_rows.Scan(
					&inInstanceId,
					&inObjectType,
					&inObjId,
				)
				if err != nil {
					if err == sql.ErrNoRows {
						break inproploop
					}
					tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
					tk.broken = true
					return
				}

				var propObjectId, propInstanceId uuid.UUID
				if propObjectId, err = uuid.FromString(inObjId); err != nil {
					tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
					tk.broken = true
					return
				}
				if propInstanceId, err = uuid.FromString(inInstanceId); err != nil {
					tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
					tk.broken = true
					return
				}
				if uuid.Equal(uuid.Nil, propObjectId) || uuid.Equal(uuid.Nil, propInstanceId) {
					continue inproploop
				}
				if inObjectType == "MAGIC_NO_RESULT_VALUE" {
					continue inproploop
				}

				pi := tree.PropertyInstance{
					ObjectId:   propObjectId,
					ObjectType: inObjectType,
					InstanceId: propInstanceId,
				}
				prop.Instances = append(prop.Instances, pi)
			}

			// lookup the group and set the prepared property
			tk.tree.Find(tree.FindRequest{
				ElementId: objectId,
			}, true).SetProperty(&prop)

			// throw away all generated actions, we do this for every
			// property since with inheritance this can create a lot of
			// actions
			for i := len(tk.actionChan); i > 0; i-- {
				<-tk.actionChan
			}
			for i := len(tk.errChan); i > 0; i-- {
				<-tk.errChan
			}
		}
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<commit_msg>Treekeeper: use drain to empty channels<commit_after>package main

import (
	"database/sql"

	"github.com/1and1/soma/internal/tree"
	uuid "github.com/satori/go.uuid"
)

func (tk *treeKeeper) startupSystemProperties(stMap map[string]*sql.Stmt) {
	if tk.broken {
		return
	}

	var (
		err                                                       error
		instanceId, srcInstanceId, objectId, view, systemProperty string
		inInstanceId, inObjectType, inObjId, sourceType, value    string
		inheritance, childrenOnly                                 bool
		rows, instance_rows                                       *sql.Rows
	)

	for loopType, loopStmt := range map[string]string{
		`repository`: `LoadPropRepoSystem`,
		`bucket`:     `LoadPropBuckSystem`,
		`group`:      `LoadPropGrpSystem`,
		`cluster`:    `LoadPropClrSystem`,
		`node`:       `LoadPropNodeSystem`,
	} {

		tk.startLog.Printf("TK[%s]: loading %s system properties\n", tk.repoName, loopType)
		rows, err = stMap[loopStmt].Query(tk.repoId)
		if err != nil {
			tk.startLog.Printf("TK[%s] Error loading %s system properties: %s", tk.repoName, loopType, err.Error())
			tk.broken = true
			return
		}
		defer rows.Close()

	systemloop:
		// load all system properties defined directly on objects
		for rows.Next() {
			err = rows.Scan(
				&instanceId,
				&srcInstanceId,
				&objectId,
				&view,
				&systemProperty,
				&sourceType,
				&inheritance,
				&childrenOnly,
				&value,
			)
			if err != nil {
				if err == sql.ErrNoRows {
					break systemloop
				}
				tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
				tk.broken = true
				return
			}

			// build the property
			prop := tree.PropertySystem{
				Inheritance:  inheritance,
				ChildrenOnly: childrenOnly,
				View:         view,
				Key:          systemProperty,
				Value:        value,
			}
			prop.Id, _ = uuid.FromString(instanceId)
			prop.Instances = make([]tree.PropertyInstance, 0)

			instance_rows, err = stMap[`LoadPropSystemInstance`].Query(
				tk.repoId,
				srcInstanceId,
			)
			if err != nil {
				tk.startLog.Printf("TK[%s] Error loading %s system properties: %s", tk.repoName, loopType, err.Error())
				tk.broken = true
				return
			}
			defer instance_rows.Close()

		inproploop:
			// load all all ids for properties that were inherited from the
			// current group system property so the IDs can be set correctly
			for instance_rows.Next() {
				err = instance_rows.Scan(
					&inInstanceId,
					&inObjectType,
					&inObjId,
				)
				if err != nil {
					if err == sql.ErrNoRows {
						break inproploop
					}
					tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
					tk.broken = true
					return
				}

				var propObjectId, propInstanceId uuid.UUID
				if propObjectId, err = uuid.FromString(inObjId); err != nil {
					tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
					tk.broken = true
					return
				}
				if propInstanceId, err = uuid.FromString(inInstanceId); err != nil {
					tk.startLog.Printf("TK[%s] Error: %s\n", tk.repoName, err.Error())
					tk.broken = true
					return
				}
				if uuid.Equal(uuid.Nil, propObjectId) || uuid.Equal(uuid.Nil, propInstanceId) {
					continue inproploop
				}
				if inObjectType == "MAGIC_NO_RESULT_VALUE" {
					continue inproploop
				}

				pi := tree.PropertyInstance{
					ObjectId:   propObjectId,
					ObjectType: inObjectType,
					InstanceId: propInstanceId,
				}
				prop.Instances = append(prop.Instances, pi)
			}

			// lookup the group and set the prepared property
			tk.tree.Find(tree.FindRequest{
				ElementId: objectId,
			}, true).SetProperty(&prop)

			// throw away all generated actions, we do this for every
			// property since with inheritance this can create a lot of
			// actions
			tk.drain(`action`)
			tk.drain(`error`)
		}
	}
}

// vim: ts=4 sw=4 sts=4 noet fenc=utf-8 ffs=unix
<|endoftext|>