<commit_before>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package swarm

import (
	"math/rand"

	"crypto/tls"
	"crypto/x509"
	"github.com/fsouza/go-dockerclient"
	"github.com/pkg/errors"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/storage"
	"github.com/tsuru/tsuru/provision"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type notFoundError struct{ error }

func (e notFoundError) NotFound() bool {
	return true
}

var errNoSwarmNode = notFoundError{errors.New("no swarm nodes available")}

const (
	uniqueDocumentID       = "swarm"
	swarmCollectionName    = "swarmnodes"
	swarmSecCollectionName = "swarmsec"
	nodeRetryCount         = 3
)

type NodeAddrs struct {
	UniqueID  string `bson:"_id"`
	Addresses []string
}

func chooseDBSwarmNode() (*docker.Client, error) {
	coll, err := nodeAddrCollection()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var addrs NodeAddrs
	err = coll.FindId(uniqueDocumentID).One(&addrs)
	if err != nil && err != mgo.ErrNotFound {
		return nil, errors.WithStack(err)
	}
	if len(addrs.Addresses) == 0 {
		return nil, errors.Wrap(errNoSwarmNode, "")
	}
	var client *docker.Client
	initialIdx := rand.Intn(len(addrs.Addresses))
	var i int
	for ; i < nodeRetryCount; i++ {
		idx := (initialIdx + i) % len(addrs.Addresses)
		addr := addrs.Addresses[idx]
		client, err = newClient(addr)
		if err != nil {
			return nil, err
		}
		err = client.Ping()
		if err == nil {
			break
		}
	}
	if err != nil {
		return nil, errors.WithStack(err)
	}
	if i > 0 {
		updateDBSwarmNodes(client)
	}
	return client, nil
}

func updateDBSwarmNodes(client *docker.Client) error {
	nodes, err := client.ListNodes(docker.ListNodesOptions{})
	if err != nil {
		return errors.WithStack(err)
	}
	var addrs []string
	for _, n := range nodes {
		if n.ManagerStatus == nil {
			continue
		}
		addr := n.Spec.Annotations.Labels[labelNodeDockerAddr.String()]
		if addr == "" {
			continue
		}
		addrs = append(addrs, addr)
	}
	coll, err := nodeAddrCollection()
	if err != nil {
		return err
	}
	defer coll.Close()
	_, err = coll.UpsertId(uniqueDocumentID, bson.M{"$set": bson.M{"addresses": addrs}})
	if err != nil {
		return errors.WithStack(err)
	}
	return nil
}

type NodeSec struct {
	Address    string `bson:"_id"`
	CaCert     []byte
	ClientCert []byte
	ClientKey  []byte
}

func addNodeCredentials(opts provision.AddNodeOptions) error {
	if opts.CaCert == nil && opts.ClientCert == nil && opts.ClientKey == nil {
		return nil
	}
	secColl, err := nodeSecurityCollection()
	if err != nil {
		return err
	}
	defer secColl.Close()
	data := NodeSec{
		Address:    opts.Address,
		CaCert:     opts.CaCert,
		ClientCert: opts.ClientCert,
		ClientKey:  opts.ClientKey,
	}
	_, err = secColl.UpsertId(data.Address, data)
	return errors.WithStack(err)
}

func getNodeCredentials(address string) (*tls.Config, error) {
	secColl, err := nodeSecurityCollection()
	if err != nil {
		return nil, err
	}
	var data NodeSec
	err = secColl.FindId(address).One(&data)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, errors.WithStack(err)
	}
	tlsCert, err := tls.X509KeyPair(data.ClientCert, data.ClientKey)
	if err != nil {
		return nil, err
	}
	caPool := x509.NewCertPool()
	if !caPool.AppendCertsFromPEM(data.CaCert) {
		return nil, errors.New("could not add RootCA pem")
	}
	return &tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		RootCAs:      caPool,
	}, nil
}

func nodeAddrCollection() (*storage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return conn.Collection(swarmCollectionName), nil
}

func nodeSecurityCollection() (*storage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return conn.Collection(swarmSecCollectionName), nil
}
<commit_msg>provision/swarm: fix import ordering<commit_after>// Copyright 2016 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package swarm

import (
	"crypto/tls"
	"crypto/x509"
	"math/rand"

	"github.com/fsouza/go-dockerclient"
	"github.com/pkg/errors"
	"github.com/tsuru/tsuru/db"
	"github.com/tsuru/tsuru/db/storage"
	"github.com/tsuru/tsuru/provision"
	"gopkg.in/mgo.v2"
	"gopkg.in/mgo.v2/bson"
)

type notFoundError struct{ error }

func (e notFoundError) NotFound() bool {
	return true
}

var errNoSwarmNode = notFoundError{errors.New("no swarm nodes available")}

const (
	uniqueDocumentID       = "swarm"
	swarmCollectionName    = "swarmnodes"
	swarmSecCollectionName = "swarmsec"
	nodeRetryCount         = 3
)

type NodeAddrs struct {
	UniqueID  string `bson:"_id"`
	Addresses []string
}

func chooseDBSwarmNode() (*docker.Client, error) {
	coll, err := nodeAddrCollection()
	if err != nil {
		return nil, err
	}
	defer coll.Close()
	var addrs NodeAddrs
	err = coll.FindId(uniqueDocumentID).One(&addrs)
	if err != nil && err != mgo.ErrNotFound {
		return nil, errors.WithStack(err)
	}
	if len(addrs.Addresses) == 0 {
		return nil, errors.Wrap(errNoSwarmNode, "")
	}
	var client *docker.Client
	initialIdx := rand.Intn(len(addrs.Addresses))
	var i int
	for ; i < nodeRetryCount; i++ {
		idx := (initialIdx + i) % len(addrs.Addresses)
		addr := addrs.Addresses[idx]
		client, err = newClient(addr)
		if err != nil {
			return nil, err
		}
		err = client.Ping()
		if err == nil {
			break
		}
	}
	if err != nil {
		return nil, errors.WithStack(err)
	}
	if i > 0 {
		updateDBSwarmNodes(client)
	}
	return client, nil
}

func updateDBSwarmNodes(client *docker.Client) error {
	nodes, err := client.ListNodes(docker.ListNodesOptions{})
	if err != nil {
		return errors.WithStack(err)
	}
	var addrs []string
	for _, n := range nodes {
		if n.ManagerStatus == nil {
			continue
		}
		addr := n.Spec.Annotations.Labels[labelNodeDockerAddr.String()]
		if addr == "" {
			continue
		}
		addrs = append(addrs, addr)
	}
	coll, err := nodeAddrCollection()
	if err != nil {
		return err
	}
	defer coll.Close()
	_, err = coll.UpsertId(uniqueDocumentID, bson.M{"$set": bson.M{"addresses": addrs}})
	if err != nil {
		return errors.WithStack(err)
	}
	return nil
}

type NodeSec struct {
	Address    string `bson:"_id"`
	CaCert     []byte
	ClientCert []byte
	ClientKey  []byte
}

func addNodeCredentials(opts provision.AddNodeOptions) error {
	if opts.CaCert == nil && opts.ClientCert == nil && opts.ClientKey == nil {
		return nil
	}
	secColl, err := nodeSecurityCollection()
	if err != nil {
		return err
	}
	defer secColl.Close()
	data := NodeSec{
		Address:    opts.Address,
		CaCert:     opts.CaCert,
		ClientCert: opts.ClientCert,
		ClientKey:  opts.ClientKey,
	}
	_, err = secColl.UpsertId(data.Address, data)
	return errors.WithStack(err)
}

func getNodeCredentials(address string) (*tls.Config, error) {
	secColl, err := nodeSecurityCollection()
	if err != nil {
		return nil, err
	}
	var data NodeSec
	err = secColl.FindId(address).One(&data)
	if err != nil {
		if err == mgo.ErrNotFound {
			return nil, nil
		}
		return nil, errors.WithStack(err)
	}
	tlsCert, err := tls.X509KeyPair(data.ClientCert, data.ClientKey)
	if err != nil {
		return nil, err
	}
	caPool := x509.NewCertPool()
	if !caPool.AppendCertsFromPEM(data.CaCert) {
		return nil, errors.New("could not add RootCA pem")
	}
	return &tls.Config{
		Certificates: []tls.Certificate{tlsCert},
		RootCAs:      caPool,
	}, nil
}

func nodeAddrCollection() (*storage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return conn.Collection(swarmCollectionName), nil
}

func nodeSecurityCollection() (*storage.Collection, error) {
	conn, err := db.Conn()
	if err != nil {
		return nil, errors.WithStack(err)
	}
	return conn.Collection(swarmSecCollectionName), nil
}
<|endoftext|>


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

package control

import (
	"errors"

	"fmt"

	clientv2 "github.com/coreos/etcd/client"
	"github.com/coreos/etcd/clientv3"
	consulapi "github.com/hashicorp/consul/api"
	"github.com/samuel/go-zookeeper/zk"
	"golang.org/x/net/context"
)

type ReqHandler func(ctx context.Context, req *request) error

func newPutEtcd2(conn clientv2.KeysAPI) ReqHandler {
	return func(ctx context.Context, req *request) error {
		op := req.etcdv2Op
		_, err := conn.Set(context.Background(), op.key, op.value, nil)
		return err
	}
}

func newPutEtcd3(conn clientv3.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		_, err := conn.Do(ctx, req.etcdv3Op)
		return err
	}
}

func newPutOverwriteZK(conn *zk.Conn) ReqHandler {
	return func(ctx context.Context, req *request) error {
		op := req.zkOp
		_, err := conn.Set(op.key, op.value, int32(-1))
		return err
	}
}

func newPutCreateZK(conn *zk.Conn) ReqHandler {
	// samekey
	return func(ctx context.Context, req *request) error {
		op := req.zkOp
		_, err := conn.Create(op.key, op.value, zkCreateFlags, zkCreateAcl)
		return err
	}
}

func newPutConsul(conn *consulapi.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		op := req.consulOp
		_, err := conn.Put(&consulapi.KVPair{Key: op.key, Value: op.value}, nil)
		return err
	}
}

func newGetEtcd2(conn clientv2.KeysAPI) ReqHandler {
	return func(ctx context.Context, req *request) error {
		_, err := conn.Get(ctx, req.etcdv2Op.key, nil)
		return err
	}
}

func newGetEtcd3(conn clientv3.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		_, err := conn.Do(ctx, req.etcdv3Op)
		return err
	}
}

func newGetZK(conn *zk.Conn) ReqHandler {
	return func(ctx context.Context, req *request) error {
		errt := ""
		if !req.zkOp.staleRead {
			_, err := conn.Sync("/" + req.zkOp.key)
			if err != nil {
				errt += err.Error()
			}
		}
		_, _, err := conn.Get("/" + req.zkOp.key)
		if err != nil {
			if errt != "" {
				errt += "; "
			}
			errt += fmt.Sprintf("%q while getting %q", err.Error(), "/"+req.zkOp.key)
		}
		if errt != "" {
			return errors.New(errt)
		}
		return nil
	}
}

func newGetConsul(conn *consulapi.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		opt := &consulapi.QueryOptions{
			AllowStale: req.consulOp.staleRead,
		}
		if !req.consulOp.staleRead {
			opt.RequireConsistent = true
		}
		_, _, err := conn.Get(req.consulOp.key, opt)
		return err
	}
}
<commit_msg>control: make Consul opt more explicit<commit_after>// Copyright 2016 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package control

import (
	"errors"

	"fmt"

	clientv2 "github.com/coreos/etcd/client"
	"github.com/coreos/etcd/clientv3"
	consulapi "github.com/hashicorp/consul/api"
	"github.com/samuel/go-zookeeper/zk"
	"golang.org/x/net/context"
)

type ReqHandler func(ctx context.Context, req *request) error

func newPutEtcd2(conn clientv2.KeysAPI) ReqHandler {
	return func(ctx context.Context, req *request) error {
		op := req.etcdv2Op
		_, err := conn.Set(context.Background(), op.key, op.value, nil)
		return err
	}
}

func newPutEtcd3(conn clientv3.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		_, err := conn.Do(ctx, req.etcdv3Op)
		return err
	}
}

func newPutOverwriteZK(conn *zk.Conn) ReqHandler {
	return func(ctx context.Context, req *request) error {
		op := req.zkOp
		_, err := conn.Set(op.key, op.value, int32(-1))
		return err
	}
}

func newPutCreateZK(conn *zk.Conn) ReqHandler {
	// samekey
	return func(ctx context.Context, req *request) error {
		op := req.zkOp
		_, err := conn.Create(op.key, op.value, zkCreateFlags, zkCreateAcl)
		return err
	}
}

func newPutConsul(conn *consulapi.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		op := req.consulOp
		_, err := conn.Put(&consulapi.KVPair{Key: op.key, Value: op.value}, nil)
		return err
	}
}

func newGetEtcd2(conn clientv2.KeysAPI) ReqHandler {
	return func(ctx context.Context, req *request) error {
		_, err := conn.Get(ctx, req.etcdv2Op.key, nil)
		return err
	}
}

func newGetEtcd3(conn clientv3.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		_, err := conn.Do(ctx, req.etcdv3Op)
		return err
	}
}

func newGetZK(conn *zk.Conn) ReqHandler {
	return func(ctx context.Context, req *request) error {
		errt := ""
		if !req.zkOp.staleRead {
			_, err := conn.Sync("/" + req.zkOp.key)
			if err != nil {
				errt += err.Error()
			}
		}
		_, _, err := conn.Get("/" + req.zkOp.key)
		if err != nil {
			if errt != "" {
				errt += "; "
			}
			errt += fmt.Sprintf("%q while getting %q", err.Error(), "/"+req.zkOp.key)
		}
		if errt != "" {
			return errors.New(errt)
		}
		return nil
	}
}

func newGetConsul(conn *consulapi.KV) ReqHandler {
	return func(ctx context.Context, req *request) error {
		opt := &consulapi.QueryOptions{}
		if req.consulOp.staleRead {
			opt.AllowStale = true
			opt.RequireConsistent = false
		}
		if !req.consulOp.staleRead {
			opt.AllowStale = false
			opt.RequireConsistent = true
		}
		_, _, err := conn.Get(req.consulOp.key, opt)
		return err
	}
}
<|endoftext|>


<commit_before>// Copyright 2016 HeadwindFly. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package clevergo

type ControllerInterface interface {
	Handle(next Handler) Handler

	DELETE(ctx *Context)
	GET(ctx *Context)
	HEAD(ctx *Context)
	OPTIONS(ctx *Context)
	PATCH(ctx *Context)
	POST(ctx *Context)
	PUT(ctx *Context)
}

type Controller struct {
}

func (c Controller) Handle(next Handler) Handler {
	return HandlerFunc(func(ctx *Context) {
		// Invoke the request handler.
		next.Handle(ctx)
	})
}

func (c Controller) DELETE(ctx *Context) {
	ctx.ResponseForbidden()
}

func (c Controller) GET(ctx *Context) {
	ctx.ResponseForbidden()
}

func (c Controller) HEAD(ctx *Context) {
	ctx.ResponseForbidden()
}

func (c Controller) OPTIONS(ctx *Context) {
	ctx.ResponseForbidden()
}

func (c Controller) PATCH(ctx *Context) {
	ctx.ResponseForbidden()
}

func (c Controller) POST(ctx *Context) {
	ctx.ResponseForbidden()
}

func (c Controller) PUT(ctx *Context) {
	ctx.ResponseForbidden()
}
<commit_msg>Modified Controller default response to Not Found<commit_after>// Copyright 2016 HeadwindFly. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package clevergo

type ControllerInterface interface {
	Handle(next Handler) Handler

	DELETE(ctx *Context)
	GET(ctx *Context)
	HEAD(ctx *Context)
	OPTIONS(ctx *Context)
	PATCH(ctx *Context)
	POST(ctx *Context)
	PUT(ctx *Context)
}

type Controller struct {
}

func (c Controller) Handle(next Handler) Handler {
	return HandlerFunc(func(ctx *Context) {
		// Invoke the request handler.
		next.Handle(ctx)
	})
}

func (c Controller) DELETE(ctx *Context) {
	ctx.NotFound()
}

func (c Controller) GET(ctx *Context) {
	ctx.NotFound()
}

func (c Controller) HEAD(ctx *Context) {
	ctx.NotFound()
}

func (c Controller) OPTIONS(ctx *Context) {
	ctx.NotFound()
}

func (c Controller) PATCH(ctx *Context) {
	ctx.NotFound()
}

func (c Controller) POST(ctx *Context) {
	ctx.NotFound()
}

func (c Controller) PUT(ctx *Context) {
	ctx.NotFound()
}
<|endoftext|>


<commit_before>package funcmock

import "reflect"

type mockController struct {
	originalFunc reflect.Value
	targetFunc   reflect.Value
	// we need map, not slice, to set call before it is called
	callStack map[int]*call
	// we need it to set call, before it is called
	counter int
	// the default call which shall be used for mint calls
	defaultYield []reflect.Value

	// Flag indicating the default return has been set
	yieldSet bool
}

func (this *mockController) CallCount() int {
	return this.counter
}

func (this *mockController) CallNth(nth int) (c *call) {
	c = this.callStack[nth]
	if c == nil {
		c = new(call)
		this.callStack[nth] = c
	}
	return c
}

func (this *mockController) incrementCounter() {
	this.counter++
	return
}

func (this *mockController) SetDefaultReturn(args ...interface{}) {
	if this.targetFunc == reflect.Zero(this.targetFunc.Type()) {
		panic("Internal Error: Target Function should prior to calling SetDefaultReturn")
	}
	fnType := this.targetFunc.Type()
	typeNumOut := fnType.NumOut()
	if len(args) == typeNumOut && !this.yieldSet {
		this.defaultYield = this.defaultYield[:0]
		for i := 0; i < typeNumOut; i++ {
			if args[i] == nil {
				// kind of return param, eg. ptr, slice, etc.
				kind := fnType.Out(i).Kind()
				switch kind {
				case reflect.Ptr:
				default:
					panic("Cannot set nil to not-pointer type")
				}
				v := reflect.Zero(fnType.Out(i))
				this.defaultYield = append(this.defaultYield, v)
			} else {
				this.defaultYield = append(this.defaultYield,
					reflect.ValueOf(args[i]))
			}
		}
		this.yieldSet = true
	} else if this.yieldSet {
		panic("Can only call SetDefaultReturn once")
	} else {
		panic("The number of returns should be the same as that of the function")
	}

}

func (this *mockController) add(c *call) {
	this.callStack[this.CallCount()-1] = c
}

func (this *mockController) Called() bool {
	return this.CallCount() > 0
}

func (this *mockController) Restore() {
	this.targetFunc.Set(this.originalFunc)
}
<commit_msg>mockController made public (issue 2)<commit_after>package funcmock

import "reflect"

type MockController struct {
	originalFunc reflect.Value
	targetFunc   reflect.Value
	// we need map, not slice, to set call before it is called
	callStack map[int]*call
	// we need it to set call, before it is called
	counter int
	// the default call which shall be used for mint calls
	defaultYield []reflect.Value

	// Flag indicating the default return has been set
	yieldSet bool
}

func (this *mockController) CallCount() int {
	return this.counter
}

func (this *mockController) CallNth(nth int) (c *call) {
	c = this.callStack[nth]
	if c == nil {
		c = new(call)
		this.callStack[nth] = c
	}
	return c
}

func (this *mockController) incrementCounter() {
	this.counter++
	return
}

func (this *mockController) SetDefaultReturn(args ...interface{}) {
	if this.targetFunc == reflect.Zero(this.targetFunc.Type()) {
		panic("Internal Error: Target Function should prior to calling SetDefaultReturn")
	}
	fnType := this.targetFunc.Type()
	typeNumOut := fnType.NumOut()
	if len(args) == typeNumOut && !this.yieldSet {
		this.defaultYield = this.defaultYield[:0]
		for i := 0; i < typeNumOut; i++ {
			if args[i] == nil {
				// kind of return param, eg. ptr, slice, etc.
				kind := fnType.Out(i).Kind()
				switch kind {
				case reflect.Ptr:
				default:
					panic("Cannot set nil to not-pointer type")
				}
				v := reflect.Zero(fnType.Out(i))
				this.defaultYield = append(this.defaultYield, v)
			} else {
				this.defaultYield = append(this.defaultYield,
					reflect.ValueOf(args[i]))
			}
		}
		this.yieldSet = true
	} else if this.yieldSet {
		panic("Can only call SetDefaultReturn once")
	} else {
		panic("The number of returns should be the same as that of the function")
	}

}

func (this *mockController) add(c *call) {
	this.callStack[this.CallCount()-1] = c
}

func (this *mockController) Called() bool {
	return this.CallCount() > 0
}

func (this *mockController) Restore() {
	this.targetFunc.Set(this.originalFunc)
}
<|endoftext|>


<commit_before>// Package core is the main package contents of the GoCore application collection of packages and utilities
// Also the root contains some debugging/dumping variable functions
package core

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"github.com/DanielRenne/GoCore/core/extensions"
	"github.com/DanielRenne/GoCore/core/serverSettings"
	"github.com/davidrenne/reflections"
	"github.com/go-errors/errors"
)

type core_debug struct{}

var core_logger = log.New(os.Stdout, "", 0)

// TransactionLog provides a thread-safe buffer/string if you have serverSettings.WebConfig.Application.ReleaseMode == "development" you can call something like core.Debug.GetDump in many places and then read the TransactionLog when you need to (note you must manually clear it as it will just increase your memory usage the more logs are sent)
var TransactionLog string

// Debug is a base struct for all debug functions.
var Debug = core_debug{}

// Logger can be overridden with log.New(os.Stdout, "", 0) to log to stdout or some other writer
var Logger = core_logger

// TransactionLogMutex is a mutex for the TransactionLog which should be used on your end to clear the value safely
var TransactionLogMutex *sync.RWMutex

func init() {
	TransactionLogMutex = &sync.RWMutex{}
}

// Nop is a dummy function that can be called in source files where
// other debug functions are constantly added and removed.
// That way import "github.com/ungerik/go-start/debug" won't cause an error when
// no other debug function is currently used.
// Arbitrary objects can be passed as arguments to avoid "declared and not used"
// error messages when commenting code out and in.
// The result is a nil interface{} dummy value.
func (self *core_debug) Nop(dummiesIn ...interface{}) (dummyOut interface{}) {
	return nil
}

// CallStackInfo returns a string with the call stack info.
func (self *core_debug) CallStackInfo(skip int) (info string) {
	pc, file, line, ok := runtime.Caller(skip)
	if ok {
		funcName := runtime.FuncForPC(pc).Name()
		info += fmt.Sprintf("In function %s()", funcName)
	}
	for i := 0; ok; i++ {
		info += fmt.Sprintf("\n%s:%d", file, line)
		_, file, line, ok = runtime.Caller(skip + i)
	}
	return info
}

// PrintCallStack prints the call stack info.
func (self *core_debug) PrintCallStack() {
	debug.PrintStack()
}

// LogCallStack logs the call stack info.
func (self *core_debug) LogCallStack() {
	log.Print(self.Stack())
}

func (self *core_debug) Stack() string {
	return string(debug.Stack())
}

func (self *core_debug) formatValue(value interface{}) string {
	return fmt.Sprintf("\n     Type: %T\n    Value: %v\nGo Syntax: %#v", value, value, value)
}

func (self *core_debug) formatCallstack(skip int) string {
	return fmt.Sprintf("\nCallstack: %s", self.CallStackInfo(skip+1))
}

// FormatSkip formats a value with callstack info.
func (self *core_debug) FormatSkip(skip int, value interface{}) string {
	return self.formatValue(value) + self.formatCallstack(skip+1)
}

// Format formats a value with callstack info.
func (self *core_debug) Format(value interface{}) string {
	return self.FormatSkip(2, value)
}

// IsZeroOfUnderlyingType returns true if the value is the zero value (nil) for its type.
func IsZeroOfUnderlyingType(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

// IsZeroOfUnderlyingType2 returns true if the value is the zero value (nil) for its type.
func IsZeroOfUnderlyingType2(x interface{}) bool {
	return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}

// HandleError is a helper function that will log an error and return it with the callers line and file.
func (self *core_debug) HandleError(err error) (s string) {
	if err != nil {
		// notice that we're using 1, so it will actually log the where
		// the error happened, 0 = this function, we don't want that.
		_, fn, line, _ := runtime.Caller(1)
		fileNameParts := strings.Split(fn, "/")
		return fmt.Sprintf("  Error Info: %s Line %d. ErrorType: %v", fileNameParts[len(fileNameParts)-1], line, err)
	}
	return ""
}

// ErrLineAndFile returns the line and file of the error.
func (self *core_debug) ErrLineAndFile(err error) (s string) {
	if err != nil {
		// notice that we're using 1, so it will actually log the where
		// the error happened, 0 = this function, we don't want that.
		_, fn, line, _ := runtime.Caller(1)
		fileNameParts := strings.Split(fn, "/")
		return fmt.Sprintf("%s Line %d", fileNameParts[len(fileNameParts)-1], line)
	}
	return ""
}

// Dump is a helper function that will log unlimited values to print to stdout or however you have log setup if you overload core/Logger
func (self *core_debug) Dump(valuesOriginal ...interface{}) {
	t := time.Now()
	l := "!!!!!!!!!!!!! DEBUG " + t.Format("2006-01-02 15:04:05.000000") + "!!!!!!!!!!!!!\n\n"
	Logger.Println(l)

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	for _, value := range valuesOriginal {
		l := self.dumpBase(value)
		Logger.Print(l)
		serverSettings.WebConfigMutex.RLock()
		if serverSettings.WebConfig.Application.ReleaseMode == "development" {
			TransactionLogMutex.Lock()
			TransactionLog += l
			TransactionLogMutex.Unlock()
		}
		serverSettings.WebConfigMutex.RUnlock()
	}
	l = self.ThrowAndPrintError()
	Logger.Print(l)

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	l = "!!!!!!!!!!!!! ENDDEBUG " + t.Format("2006-01-02 15:04:05.000000") + "!!!!!!!!!!!!!"
	Logger.Println(l)
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
}

// GetDump is a helper function that will log unlimited values which will return a string representation of what was logged
func (self *core_debug) GetDump(valuesOriginal ...interface{}) (output string) {
	for _, value := range valuesOriginal {
		output += self.dumpBase(value)
	}
	//output += self.ThrowAndPrintError()
	return output
}

func (self *core_debug) GetDumpWithInfo(valuesOriginal ...interface{}) (output string) {
	t := time.Now()
	return self.GetDumpWithInfoAndTimeString(t.String(), valuesOriginal...)
}

// GetDumpWithInfoAndTimeString is a helper function that will log unlimited values which will return a string representation of what was logged but allows you to pass your own time string in a case of timezone offsets
func (self *core_debug) GetDumpWithInfoAndTimeString(timeStr string, valuesOriginal ...interface{}) (output string) {
	l := "\n!!!!!!!!!!!!! DEBUG " + timeStr + "!!!!!!!!!!!!!\n\n"
	output += l

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()

	for _, value := range valuesOriginal {
		output += self.dumpBase(value) + "\n"
	}

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += output
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()

	l = self.ThrowAndPrintError()
	output += l
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	l = "!!!!!!!!!!!!! ENDDEBUG " + timeStr + "!!!!!!!!!!!!!\n"
	output += l
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	return output
}

func (self *core_debug) dumpBase(values ...interface{}) (output string) {
	var jsonString string
	var err error
	var structKeys []string
	if Logger != nil {
		for _, value := range values {
			isAllJSON := true
			var kind string
			kind = strings.TrimSpace(fmt.Sprintf("%T", value))
			var pieces = strings.Split(kind, " ")
			if pieces[0] == "struct" || strings.Index(pieces[0], "model.") != -1 || strings.Index(pieces[0], "viewModel.") != -1 {
				// if !IsZeroOfUnderlyingType(value) {
				kind = reflections.ReflectKind(value)
				structKeys, err = reflections.FieldsDeep(value)
				if err == nil {
					for _, field := range structKeys {
						jsonString, err = reflections.GetFieldTag(value, field, "json")
						if err != nil {
							isAllJSON = false
						}
						if jsonString == "" {
							isAllJSON = false
						}
					}
				} else {
					isAllJSON = false
				}
			} else {
				isAllJSON = false
			}

			if isAllJSON || kind == "map" || kind == "bson.M" || kind == "slice" {
				var rawBytes []byte
				rawBytes, err = json.MarshalIndent(value, "", "\t")
				if err == nil {
					if kind == "slice" || kind[:2] == "[]" {
						valReflected := reflect.ValueOf(value)
						output += fmt.Sprintf("#### %-39s [len:%s]####\n%+v", kind, extensions.IntToString(valReflected.Len()), string(rawBytes[:]))
					} else {
						output += fmt.Sprintf("#### %-39s ####\n%+v", kind, string(rawBytes[:]))
					}
				}
			} else {
				if strings.TrimSpace(kind) == "string" {
					var stringVal = value.(string)
					position := strings.Index(stringVal, "Desc->")
					if position == -1 {
						if !extensions.IsPrintable(stringVal) {
							kind += " (non printables -> dump hex)"
							stringVal = hex.Dump([]byte(stringVal))
						}
						valReflected := reflect.ValueOf(value)
						output += fmt.Sprintf("#### %-39s [len:%s]####\n%s", kind, extensions.IntToString(valReflected.Len()), stringVal)
					} else {
						output += stringVal[6:] + " --> "
					}
				} else if kind[:2] == "[]" || strings.TrimSpace(kind) == "array" {
					valReflected := reflect.ValueOf(value)
					output += fmt.Sprintf("#### %-39s [len:%s]####\n%+v", kind, extensions.IntToString(valReflected.Len()), value)
				} else {
					output += fmt.Sprintf("#### %-39s ####\n%+v", kind, value)
				}
			}
		}
	}
	return output
}

// ThrowAndPrintError is a helper function that will throw a fake error and get the callstack and return it as a string (you probably shouldnt use this)
func (self *core_debug) ThrowAndPrintError() (output string) {

	serverSettings.WebConfigMutex.RLock()
	ok := serverSettings.WebConfig.Application.CoreDebugStackTrace
	serverSettings.WebConfigMutex.RUnlock()
	if ok {
		output += "\n"
		errorInfo := self.ThrowError()
		stack := strings.Split(errorInfo.ErrorStack(), "\n")
		if len(stack) >= 8 {
			output += "\nDump Caller:"
			output += "\n---------------"
			//output += strings.Join(stack, ",")
			output += "\n golines ==> " + strings.TrimSpace(stack[6])
			output += "\n         ==> " + strings.TrimSpace(stack[7])
			output += "\n         ==> " + strings.TrimSpace(stack[8])
			output += "\n         ==> " + strings.TrimSpace(stack[9])
			output += "\n         ==> " + strings.TrimSpace(stack[10])
			if len(stack) >= 12 {
				output += "\n         ==> " + strings.TrimSpace(stack[11])
			}
			if len(stack) >= 13 {
				output += "\n         ==> " + strings.TrimSpace(stack[12])
			}
			if len(stack) >= 14 {
				output += "\n         ==> " + strings.TrimSpace(stack[13])
			}
			if len(stack) >= 15 {
				output += "\n         ==> " + strings.TrimSpace(stack[14])
			}
			if len(stack) >= 16 {
				output += "\n         ==> " + strings.TrimSpace(stack[15])
			}
			output += "\n---------------"
			output += "\n"
			output += "\n"
		}
	}
	return output
}

// ThrowError is a helper function that will throw a fake error and get the callstack and return it as an error (you probably shouldnt use this)
func (self *core_debug) ThrowError() *errors.Error {
	return errors.Errorf("Debug Dump")
}

// GetDump is a helper function that will return a string of the dump of the values passed in
func GetDump(valuesOriginal ...interface{}) string {
	return Debug.GetDump(valuesOriginal...)
}

// Dump is a helper function that will dump the values passed to it
func Dump(valuesOriginal ...interface{}) {
	Debug.Dump(valuesOriginal...)
}
<commit_msg>fmt.Sprintf<commit_after>// Package core is the main package contents of the GoCore application collection of packages and utilities
// Also the root contains some debugging/dumping variable functions
package core

import (
	"encoding/hex"
	"encoding/json"
	"fmt"
	"log"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"time"

	"github.com/DanielRenne/GoCore/core/extensions"
	"github.com/DanielRenne/GoCore/core/serverSettings"
	"github.com/davidrenne/reflections"
	"github.com/go-errors/errors"
)

type core_debug struct{}

var core_logger = log.New(os.Stdout, "", 0)

// TransactionLog provides a thread-safe buffer/string if you have serverSettings.WebConfig.Application.ReleaseMode == "development" you can call something like core.Debug.GetDump in many places and then read the TransactionLog when you need to (note you must manually clear it as it will just increase your memory usage the more logs are sent)
var TransactionLog string

// Debug is a base struct for all debug functions.
var Debug = core_debug{}

// Logger can be overridden with log.New(os.Stdout, "", 0) to log to stdout or some other writer
var Logger = core_logger

// TransactionLogMutex is a mutex for the TransactionLog which should be used on your end to clear the value safely
var TransactionLogMutex *sync.RWMutex

func init() {
	TransactionLogMutex = &sync.RWMutex{}
}

// Nop is a dummy function that can be called in source files where
// other debug functions are constantly added and removed.
// That way import "github.com/ungerik/go-start/debug" won't cause an error when
// no other debug function is currently used.
// Arbitrary objects can be passed as arguments to avoid "declared and not used"
// error messages when commenting code out and in.
// The result is a nil interface{} dummy value.
func (self *core_debug) Nop(dummiesIn ...interface{}) (dummyOut interface{}) {
	return nil
}

// CallStackInfo returns a string with the call stack info.
func (self *core_debug) CallStackInfo(skip int) (info string) {
	pc, file, line, ok := runtime.Caller(skip)
	if ok {
		funcName := runtime.FuncForPC(pc).Name()
		info += fmt.Sprintf("In function %s()", funcName)
	}
	for i := 0; ok; i++ {
		info += fmt.Sprintf("\n%s:%d", file, line)
		_, file, line, ok = runtime.Caller(skip + i)
	}
	return info
}

// PrintCallStack prints the call stack info.
func (self *core_debug) PrintCallStack() {
	debug.PrintStack()
}

// LogCallStack logs the call stack info.
func (self *core_debug) LogCallStack() {
	log.Print(self.Stack())
}

func (self *core_debug) Stack() string {
	return string(debug.Stack())
}

func (self *core_debug) formatValue(value interface{}) string {
	return fmt.Sprintf("\n     Type: %T\n    Value: %v\nGo Syntax: %#v", value, value, value)
}

func (self *core_debug) formatCallstack(skip int) string {
	return fmt.Sprintf("\nCallstack: %s", self.CallStackInfo(skip+1))
}

// FormatSkip formats a value with callstack info.
func (self *core_debug) FormatSkip(skip int, value interface{}) string {
	return self.formatValue(value) + self.formatCallstack(skip+1)
}

// Format formats a value with callstack info.
func (self *core_debug) Format(value interface{}) string {
	return self.FormatSkip(2, value)
}

// IsZeroOfUnderlyingType returns true if the value is the zero value (nil) for its type.
func IsZeroOfUnderlyingType(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

// IsZeroOfUnderlyingType2 returns true if the value is the zero value (nil) for its type.
func IsZeroOfUnderlyingType2(x interface{}) bool {
	return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}

// HandleError is a helper function that will log an error and return it with the callers line and file.
func (self *core_debug) HandleError(err error) (s string) {
	if err != nil {
		// notice that we're using 1, so it will actually log the where
		// the error happened, 0 = this function, we don't want that.
		_, fn, line, _ := runtime.Caller(1)
		fileNameParts := strings.Split(fn, "/")
		return fmt.Sprintf("  Error Info: %s Line %d. ErrorType: %v", fileNameParts[len(fileNameParts)-1], line, err)
	}
	return ""
}

// ErrLineAndFile returns the line and file of the error.
func (self *core_debug) ErrLineAndFile(err error) (s string) {
	if err != nil {
		// notice that we're using 1, so it will actually log the where
		// the error happened, 0 = this function, we don't want that.
		_, fn, line, _ := runtime.Caller(1)
		fileNameParts := strings.Split(fn, "/")
		return fmt.Sprintf("%s Line %d", fileNameParts[len(fileNameParts)-1], line)
	}
	return ""
}

// Dump is a helper function that will log unlimited values to print to stdout or however you have log setup if you overload core/Logger
func (self *core_debug) Dump(valuesOriginal ...interface{}) {
	t := time.Now()
	l := "!!!!!!!!!!!!! DEBUG " + t.Format("2006-01-02 15:04:05.000000") + "!!!!!!!!!!!!!\n\n"
	Logger.Println(l)

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	for _, value := range valuesOriginal {
		l := self.dumpBase(value)
		Logger.Print(l)
		serverSettings.WebConfigMutex.RLock()
		if serverSettings.WebConfig.Application.ReleaseMode == "development" {
			TransactionLogMutex.Lock()
			TransactionLog += l
			TransactionLogMutex.Unlock()
		}
		serverSettings.WebConfigMutex.RUnlock()
	}
	l = self.ThrowAndPrintError()
	Logger.Print(l)

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	l = "!!!!!!!!!!!!! ENDDEBUG " + t.Format("2006-01-02 15:04:05.000000") + "!!!!!!!!!!!!!"
	Logger.Println(l)
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
}

// GetDump is a helper function that will log unlimited values which will return a string representation of what was logged
func (self *core_debug) GetDump(valuesOriginal ...interface{}) (output string) {
	for _, value := range valuesOriginal {
		output += self.dumpBase(value)
	}
	//output += self.ThrowAndPrintError()
	return output
}

func (self *core_debug) GetDumpWithInfo(valuesOriginal ...interface{}) (output string) {
	t := time.Now()
	return self.GetDumpWithInfoAndTimeString(t.String(), valuesOriginal...)
}

// GetDumpWithInfoAndTimeString is a helper function that will log unlimited values which will return a string representation of what was logged but allows you to pass your own time string in a case of timezone offsets
func (self *core_debug) GetDumpWithInfoAndTimeString(timeStr string, valuesOriginal ...interface{}) (output string) {
	l := "\n!!!!!!!!!!!!! DEBUG " + timeStr + "!!!!!!!!!!!!!\n\n"
	output += l

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()

	for _, value := range valuesOriginal {
		output += self.dumpBase(value) + "\n"
	}

	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += output
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()

	l = self.ThrowAndPrintError()
	output += l
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	l = "!!!!!!!!!!!!! ENDDEBUG " + timeStr + "!!!!!!!!!!!!!\n"
	output += l
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
	return output
}

func (self *core_debug) dumpBase(values ...interface{}) (output string) {
	var jsonString string
	var err error
	var structKeys []string
	if Logger != nil {
		for _, value := range values {
			isAllJSON := true
			var kind string
			kind = strings.TrimSpace(fmt.Sprintf("%T", value))
			// var pieces = strings.Split(kind, " ")
			// if pieces[0] == "struct" || strings.Index(pieces[0], "model.") != -1 || strings.Index(pieces[0], "viewModel.") != -1 {
			if !IsZeroOfUnderlyingType(value) {
				kind = reflections.ReflectKind(value)
				structKeys, err = reflections.FieldsDeep(value)
				if err == nil {
					for _, field := range structKeys {
						jsonString, err = reflections.GetFieldTag(value, field, "json")
						if err != nil {
							isAllJSON = false
						}
						if jsonString == "" {
							isAllJSON = false
						}
					}
				} else {
					isAllJSON = false
				}
			} else {
				isAllJSON = false
			}

			if isAllJSON || kind == "map" || kind == "bson.M" || kind == "slice" {
				var rawBytes []byte
				rawBytes, err = json.MarshalIndent(value, "", "\t")
				if err == nil {
					if kind == "slice" || kind[:2] == "[]" {
						valReflected := reflect.ValueOf(value)
						output += fmt.Sprintf("#### %-39s [len:%s]####\n%+v", kind, extensions.IntToString(valReflected.Len()), string(rawBytes[:]))
					} else {
						output += fmt.Sprintf("#### %-39s ####\n%+v", kind, string(rawBytes[:]))
					}
				}
			} else {
				if strings.TrimSpace(kind) == "string" {
					var stringVal = fmt.Sprintf("%+v", value)
					position := strings.Index(stringVal, "Desc->")
					if position == -1 {
						if !extensions.IsPrintable(stringVal) {
							kind += " (non printables -> dump hex)"
							stringVal = hex.Dump([]byte(stringVal))
						}
						valReflected := reflect.ValueOf(value)
						output += fmt.Sprintf("#### %-39s [len:%s]####\n%s", kind, extensions.IntToString(valReflected.Len()), stringVal)
					} else {
						output += stringVal[6:] + " --> "
					}
				} else if kind[:2] == "[]" || strings.TrimSpace(kind) == "array" {
					valReflected := reflect.ValueOf(value)
					output += fmt.Sprintf("#### %-39s [len:%s]####\n%+v", kind, extensions.IntToString(valReflected.Len()), value)
				} else {
					output += fmt.Sprintf("#### %-39s ####\n%+v", kind, value)
				}
			}
		}
	}
	return output
}

// ThrowAndPrintError is a helper function that will throw a fake error and get the callstack and return it as a string (you probably shouldnt use this)
func (self *core_debug) ThrowAndPrintError() (output string) {

	serverSettings.WebConfigMutex.RLock()
	ok := serverSettings.WebConfig.Application.CoreDebugStackTrace
	serverSettings.WebConfigMutex.RUnlock()
	if ok {
		output += "\n"
		errorInfo := self.ThrowError()
		stack := strings.Split(errorInfo.ErrorStack(), "\n")
		if len(stack) >= 8 {
			output += "\nDump Caller:"
			output += "\n---------------"
			//output += strings.Join(stack, ",")
			output += "\n golines ==> " + strings.TrimSpace(stack[6])
			output += "\n         ==> " + strings.TrimSpace(stack[7])
			output += "\n         ==> " + strings.TrimSpace(stack[8])
			output += "\n         ==> " + strings.TrimSpace(stack[9])
			output += "\n         ==> " + strings.TrimSpace(stack[10])
			if len(stack) >= 12 {
				output += "\n         ==> " + strings.TrimSpace(stack[11])
			}
			if len(stack) >= 13 {
				output += "\n         ==> " + strings.TrimSpace(stack[12])
			}
			if len(stack) >= 14 {
				output += "\n         ==> " + strings.TrimSpace(stack[13])
			}
			if len(stack) >= 15 {
				output += "\n         ==> " + strings.TrimSpace(stack[14])
			}
			if len(stack) >= 16 {
				output += "\n         ==> " + strings.TrimSpace(stack[15])
			}
			output += "\n---------------"
			output += "\n"
			output += "\n"
		}
	}
	return output
}

// ThrowError is a helper function that will throw a fake error and get the callstack and return it as an error (you probably shouldnt use this)
func (self *core_debug) ThrowError() *errors.Error {
	return errors.Errorf("Debug Dump")
}

// GetDump is a helper function that will return a string of the dump of the values passed in
func GetDump(valuesOriginal ...interface{}) string {
	return Debug.GetDump(valuesOriginal...)
}

// Dump is a helper function that will dump the values passed to it
func Dump(valuesOriginal ...interface{}) {
	Debug.Dump(valuesOriginal...)
}
<|endoftext|>


<commit_before>// Your very own Gobot
// "More man than machine"
package core

import (
	"flag"
	"github.com/sdstrowes/gesture/rewrite"
	irc "github.com/fluffle/goirc/client"
	"log"
	"regexp"
)

type Response struct {
	Status Status
	Error  error
}

type Status int

const (
	Stop Status = iota
	KeepGoing
)

type Gobot struct {
	Name      string
	Config    *Config
	client    *irc.Conn
	quitter   chan bool
	listeners []listener
}

// -----------------------------------------------------------------------------
// Tell Gobot how to be a Real Boy

// Create a new Gobot from the given gesture config
func CreateGobot(config *Config) *Gobot {
	bot := &Gobot{config.BotName, config, nil, make(chan bool), nil}

	flag.Parse()
	bot.client = irc.SimpleClient(config.BotName)
	bot.client.EnableStateTracking()

	bot.client.HandleFunc(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Println("Connected to", config.Hostname, "!")
			for _, channel := range config.Channels {
				conn.Join(channel)
			}
		})

	bot.client.HandleFunc(irc.JOIN,
		func(conn *irc.Conn, line *irc.Line) {
			if line.Nick == bot.Name {
				log.Printf("Joined %+v\n", line.Args)
			}
		})

	bot.client.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			bot.quitter <- true
		})

	bot.client.HandleFunc(irc.PRIVMSG,
		func(conn *irc.Conn, line *irc.Line) {
			bot.messageReceived(conn, line)
		})

	return bot
}

// Attempt to connect to IRC!
func (bot *Gobot) Connect(hostname string) (chan bool, error) {
	err := bot.client.Connect(bot.Config.Hostname)
	if err != nil {
		return nil, err
	}
	return bot.quitter, nil
}

// Send a disconnect message to your robot
func (bot *Gobot) Disconnect() {
	bot.quitter <- true
}

// Add a listener that matches incoming messages based on the given regexp.
// Matched messages and any submatches are returned to the callback.
func (bot *Gobot) ListenFor(pattern string, cb func(Message, []string) Response) {
	re := regexp.MustCompile(pattern)
	bot.listeners = append(bot.listeners, listener{re, cb})
}

func (msg *Gobot) Stop() Response {
	return Response{Stop, nil}
}

func (msg *Gobot) KeepGoing() Response {
	return Response{KeepGoing, nil}
}

func (msg *Gobot) Error(err error) Response {
	return Response{Stop, err}
}

// TODO:
// - OnEnter/Leave
// - OnTopicChange

// -------------------------------------------------------------------
// GOBOT'S ROOM, KEEP OUT

func (bot *Gobot) messageReceived(conn *irc.Conn, line *irc.Line) {
	if len(line.Args) > 1 {
		msg := messageFrom(conn, line)
		log.Printf(">> %s (%s): %s\n", msg.User, msg.Channel, msg.Text)

		matched := false
		for _, listener := range bot.listeners {
			response := listener.listen(msg)
			if response != nil {
				if response.Error != nil {
					log.Print(response.Error)
					msg.Reply(response.Error.Error())
					matched = true
					break
				}
				if response.Status == Stop {
					matched = true
					break
				}
			}
		}
		if !matched {
			// try to expand any links
			for _, token := range rewrite.GetRewrittenLinks(msg.Text) {
				msg.Ftfy(token)
			}
		}
	}
}

func messageFrom(conn *irc.Conn, line *irc.Line) Message {
	return Message{conn, line, line.Nick, line.Args[0], line.Args[1]}
}

// -------------------------------------------------------------------
// PICK UP THE DAMN PHONE

type listener struct {
	re *regexp.Regexp
	cb func(Message, []string) Response
}

// Try to match the given message. If it matches, fire the callback and returns
// true. Returns false otherwise.
func (listener *listener) listen(msg Message) *Response {
	if matches := listener.re.FindStringSubmatch(msg.Text); matches != nil {
		response := listener.cb(msg, matches)
		return &response
	}
	return nil
}
<commit_msg>s/Connect/ConnectTo/<commit_after>// Your very own Gobot
// "More man than machine"
package core

import (
	"flag"
	"github.com/sdstrowes/gesture/rewrite"
	irc "github.com/fluffle/goirc/client"
	"log"
	"regexp"
)

type Response struct {
	Status Status
	Error  error
}

type Status int

const (
	Stop Status = iota
	KeepGoing
)

type Gobot struct {
	Name      string
	Config    *Config
	client    *irc.Conn
	quitter   chan bool
	listeners []listener
}

// -----------------------------------------------------------------------------
// Tell Gobot how to be a Real Boy

// Create a new Gobot from the given gesture config
func CreateGobot(config *Config) *Gobot {
	bot := &Gobot{config.BotName, config, nil, make(chan bool), nil}

	flag.Parse()
	bot.client = irc.SimpleClient(config.BotName)
	bot.client.EnableStateTracking()

	bot.client.HandleFunc(irc.CONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			log.Println("Connected to", config.Hostname, "!")
			for _, channel := range config.Channels {
				conn.Join(channel)
			}
		})

	bot.client.HandleFunc(irc.JOIN,
		func(conn *irc.Conn, line *irc.Line) {
			if line.Nick == bot.Name {
				log.Printf("Joined %+v\n", line.Args)
			}
		})

	bot.client.HandleFunc(irc.DISCONNECTED,
		func(conn *irc.Conn, line *irc.Line) {
			bot.quitter <- true
		})

	bot.client.HandleFunc(irc.PRIVMSG,
		func(conn *irc.Conn, line *irc.Line) {
			bot.messageReceived(conn, line)
		})

	return bot
}

// Attempt to connect to IRC!
func (bot *Gobot) Connect(hostname string) (chan bool, error) {
	err := bot.client.ConnectTo(bot.Config.Hostname)
	if err != nil {
		return nil, err
	}
	return bot.quitter, nil
}

// Send a disconnect message to your robot
func (bot *Gobot) Disconnect() {
	bot.quitter <- true
}

// Add a listener that matches incoming messages based on the given regexp.
// Matched messages and any submatches are returned to the callback.
func (bot *Gobot) ListenFor(pattern string, cb func(Message, []string) Response) {
	re := regexp.MustCompile(pattern)
	bot.listeners = append(bot.listeners, listener{re, cb})
}

func (msg *Gobot) Stop() Response {
	return Response{Stop, nil}
}

func (msg *Gobot) KeepGoing() Response {
	return Response{KeepGoing, nil}
}

func (msg *Gobot) Error(err error) Response {
	return Response{Stop, err}
}

// TODO:
// - OnEnter/Leave
// - OnTopicChange

// -------------------------------------------------------------------
// GOBOT'S ROOM, KEEP OUT

func (bot *Gobot) messageReceived(conn *irc.Conn, line *irc.Line) {
	if len(line.Args) > 1 {
		msg := messageFrom(conn, line)
		log.Printf(">> %s (%s): %s\n", msg.User, msg.Channel, msg.Text)

		matched := false
		for _, listener := range bot.listeners {
			response := listener.listen(msg)
			if response != nil {
				if response.Error != nil {
					log.Print(response.Error)
					msg.Reply(response.Error.Error())
					matched = true
					break
				}
				if response.Status == Stop {
					matched = true
					break
				}
			}
		}
		if !matched {
			// try to expand any links
			for _, token := range rewrite.GetRewrittenLinks(msg.Text) {
				msg.Ftfy(token)
			}
		}
	}
}

func messageFrom(conn *irc.Conn, line *irc.Line) Message {
	return Message{conn, line, line.Nick, line.Args[0], line.Args[1]}
}

// -------------------------------------------------------------------
// PICK UP THE DAMN PHONE

type listener struct {
	re *regexp.Regexp
	cb func(Message, []string) Response
}

// Try to match the given message. If it matches, fire the callback and returns
// true. Returns false otherwise.
func (listener *listener) listen(msg Message) *Response {
	if matches := listener.re.FindStringSubmatch(msg.Text); matches != nil {
		response := listener.cb(msg, matches)
		return &response
	}
	return nil
}
<|endoftext|>


<commit_before>package refmt_test

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/polydawn/refmt"
	"github.com/polydawn/refmt/cbor"
	"github.com/polydawn/refmt/json"
	"github.com/polydawn/refmt/obj/atlas"
)

func TestRoundTrip(t *testing.T) {
	t.Run("empty []interface{}", func(t *testing.T) {
		testRoundTripAllEncodings(t, []interface{}{}, atlas.MustBuild())
	})
	t.Run("nil nil", func(t *testing.T) {
		testRoundTripAllEncodings(t, nil, atlas.MustBuild())
	})
	t.Run("nil []interface{}", func(t *testing.T) {
		testRoundTripAllEncodings(t, []interface{}(nil), atlas.MustBuild())
	})
}

func testRoundTripAllEncodings(
	t *testing.T,
	value interface{},
	atl atlas.Atlas,
) {
	t.Run("cbor", func(t *testing.T) {
		roundTrip(t, value, cbor.EncodeOptions{}, cbor.DecodeOptions{}, atl)
	})
	t.Run("json", func(t *testing.T) {
		roundTrip(t, value, json.EncodeOptions{}, json.DecodeOptions{}, atl)
	})
}

func roundTrip(
	t *testing.T,
	value interface{},
	encodeOptions refmt.EncodeOptions,
	decodeOptions refmt.DecodeOptions,
	atl atlas.Atlas,
) {
	// Encode.
	var buf bytes.Buffer
	encoder := refmt.NewMarshallerAtlased(encodeOptions, &buf, atl)
	if err := encoder.Marshal(value); err != nil {
		t.Fatalf("failed encoding: %s", err)
	}

	// Decode back to obj.
	decoder := refmt.NewUnmarshallerAtlased(decodeOptions, bytes.NewBuffer(buf.Bytes()), atl)
	var slot interface{}
	if err := decoder.Unmarshal(&slot); err != nil {
		t.Fatalf("failed decoding: %s", err)
	}
	t.Logf("%#T -- %#v", slot, slot)

	// Re-encode.  Expect to get same encoded form.
	var buf2 bytes.Buffer
	encoder2 := refmt.NewMarshallerAtlased(encodeOptions, &buf2, atl)
	if err := encoder2.Marshal(slot); err != nil {
		t.Fatalf("failed re-encoding: %s", err)
	}

	// Stringify.  (Plain "%q" escapes unprintables quite nicely.)
	str1 := fmt.Sprintf("%q", buf.String())
	str2 := fmt.Sprintf("%q", buf2.String())
	if str1 != str2 {
		t.Errorf("%q != %q", str1, str2)
	}
	t.Logf("%#v == %q", value, str1)
}
<commit_msg>refmt: test empty/nil map roundtrips.<commit_after>package refmt_test

import (
	"bytes"
	"fmt"
	"testing"

	"github.com/polydawn/refmt"
	"github.com/polydawn/refmt/cbor"
	"github.com/polydawn/refmt/json"
	"github.com/polydawn/refmt/obj/atlas"
)

func TestRoundTrip(t *testing.T) {
	t.Run("nil nil", func(t *testing.T) {
		testRoundTripAllEncodings(t, nil, atlas.MustBuild())
	})
	t.Run("empty []interface{}", func(t *testing.T) {
		testRoundTripAllEncodings(t, []interface{}{}, atlas.MustBuild())
	})
	t.Run("nil []interface{}", func(t *testing.T) {
		testRoundTripAllEncodings(t, []interface{}(nil), atlas.MustBuild())
	})
	t.Run("empty map[string]interface{}", func(t *testing.T) {
		testRoundTripAllEncodings(t, map[string]interface{}(nil), atlas.MustBuild())
	})
	t.Run("nil map[string]interface{}", func(t *testing.T) {
		testRoundTripAllEncodings(t, map[string]interface{}(nil), atlas.MustBuild())
	})
}

func testRoundTripAllEncodings(
	t *testing.T,
	value interface{},
	atl atlas.Atlas,
) {
	t.Run("cbor", func(t *testing.T) {
		roundTrip(t, value, cbor.EncodeOptions{}, cbor.DecodeOptions{}, atl)
	})
	t.Run("json", func(t *testing.T) {
		roundTrip(t, value, json.EncodeOptions{}, json.DecodeOptions{}, atl)
	})
}

func roundTrip(
	t *testing.T,
	value interface{},
	encodeOptions refmt.EncodeOptions,
	decodeOptions refmt.DecodeOptions,
	atl atlas.Atlas,
) {
	// Encode.
	var buf bytes.Buffer
	encoder := refmt.NewMarshallerAtlased(encodeOptions, &buf, atl)
	if err := encoder.Marshal(value); err != nil {
		t.Fatalf("failed encoding: %s", err)
	}

	// Decode back to obj.
	decoder := refmt.NewUnmarshallerAtlased(decodeOptions, bytes.NewBuffer(buf.Bytes()), atl)
	var slot interface{}
	if err := decoder.Unmarshal(&slot); err != nil {
		t.Fatalf("failed decoding: %s", err)
	}
	t.Logf("%#T -- %#v", slot, slot)

	// Re-encode.  Expect to get same encoded form.
	var buf2 bytes.Buffer
	encoder2 := refmt.NewMarshallerAtlased(encodeOptions, &buf2, atl)
	if err := encoder2.Marshal(slot); err != nil {
		t.Fatalf("failed re-encoding: %s", err)
	}

	// Stringify.  (Plain "%q" escapes unprintables quite nicely.)
	str1 := fmt.Sprintf("%q", buf.String())
	str2 := fmt.Sprintf("%q", buf2.String())
	if str1 != str2 {
		t.Errorf("%q != %q", str1, str2)
	}
	t.Logf("%#v == %q", value, str1)
}
<|endoftext|>


<commit_before>//go:generate go run gen.go

package version

// Version tells us the app version string
const Version = "4.0.0-alpha2"
<commit_msg>Bump version to v4.0.0<commit_after>//go:generate go run gen.go

package version

// Version tells us the app version string
const Version = "4.0.0"
<|endoftext|>


<commit_before>package stitch

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"sync"

	. "github.com/polydawn/go-errcat"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	"go.polydawn.net/rio/cache"
	"go.polydawn.net/rio/config"
	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/stitch/placer"
)

/*
	Struct to gather the args for a single rio.Unpack func call.
	(The context object and monitors are handled in a different band.)

	Note the similar name to a structure in the go-timeless-api packages;
	this one is not serializable, is internal, and
	contains the literal set of warehouses already resolved,
	as well as the path inline rather than in a map key, so we can sort slices.
*/
type UnpackSpec struct {
	Path       fs.AbsolutePath
	WareID     api.WareID
	Filters    api.FilesetFilters
	Warehouses []api.WarehouseAddr
}

// Cast slices to this type to sort by target path (which is effectively mountability order).
type UnpackSpecByPath []UnpackSpec

func (a UnpackSpecByPath) Len() int           { return len(a) }
func (a UnpackSpecByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a UnpackSpecByPath) Less(i, j int) bool { return a[i].Path.String() < a[j].Path.String() }

type unpackResult struct {
	Path  fs.AbsolutePath // cache path or mount source path
	Error error
}

type Assembler struct {
	cache          fs.FS
	unpackTool     rio.UnpackFunc
	placerTool     placer.Placer
	fillerDirProps fs.Metadata
}

func NewAssembler(unpackTool rio.UnpackFunc) (*Assembler, error) {
	placerTool, err := placer.GetMountPlacer()
	if err != nil {
		return nil, err
	}
	return &Assembler{
		cache:      osfs.New(config.GetCacheBasePath()),
		unpackTool: unpackTool,
		placerTool: placerTool,
		fillerDirProps: fs.Metadata{
			Type: fs.Type_Dir, Perms: 0755, Uid: 0, Gid: 0, Mtime: fs.DefaultAtime,
		},
	}, nil
}

func (a *Assembler) Run(ctx context.Context, targetFs fs.FS, parts []UnpackSpec) (func() error, error) {
	sort.Sort(UnpackSpecByPath(parts))

	// Unpacking either wares or more mounts into paths under mounts is seriously illegal.
	//  It's a massive footgun, entirely strange, and just No.
	//  Doing it into paths under other wares is fine because it's not *leaving* our zone.
	var mounts map[fs.AbsolutePath]struct{}
	for _, part := range parts {
		for mount := range mounts {
			if strings.HasPrefix(part.Path.String(), mount.String()) {
				return nil, Errorf(rio.ErrAssemblyInvalid, "invalid inputs config: "+
					"cannot stitch additional inputs under a mount (%q is under mount at %q)",
					part.Path, mount)
			}
		}
		// If this one is a mount, mark it for the rest.
		//  (Paths under it must come after it, due to the sort.)
		if part.WareID.Type == "mount" {
			mounts[part.Path] = struct{}{}
		}
	}

	// Fan out materialization into cache paths.
	unpackResults := make([]unpackResult, len(parts))
	var wg sync.WaitGroup
	wg.Add(len(parts))
	for i, part := range parts {
		go func(i int, part UnpackSpec) {
			defer wg.Done()
			res := &unpackResults[i]
			// If it's a mount, shortcut.
			if part.WareID.Type == "mount" {
				res.Path, res.Error = fs.ParseAbsolutePath(part.WareID.Hash)
				return
			}
			// Unpack with placement=none to populate cache.
			resultWareID, err := a.unpackTool(
				ctx, // TODO fork em out
				part.WareID,
				"-",
				part.Filters,
				rio.Placement_None,
				part.Warehouses,
				rio.Monitor{},
			)
			// Yield the cache path.
			res.Path = config.GetCacheBasePath().Join(cache.ShelfFor(resultWareID))
			res.Error = err
			// TODO if any error, fan out cancellations
		}(i, part)
	}
	wg.Wait()
	// Yield up any errors from individual unpacks.
	for _, result := range unpackResults {
		if result.Error != nil {
			return nil, result.Error
		}
	}

	// Zip up all placements, in order.
	//  Parent dirs are made as necessary along the way.
	hk := &housekeeping{}
	for i, part := range parts {
		path := part.Path.CoerceRelative()

		// Ensure parent dirs.
		for _, parentPath := range path.Dir().Split() {
			target, isSymlink, err := targetFs.Readlink(parentPath)
			if isSymlink {
				// Future hackers: if you ever try to make this check cleverer,
				//  also make sure you include a check for host mount crossings.
				return nil, fs.NewBreakoutError(
					targetFs.BasePath(),
					path,
					parentPath,
					target,
				)
			} else if err == nil {
				continue
			} else if Category(err) == fs.ErrNotExists {
				// Make the parent dir if it does not exist.
				a.fillerDirProps.Name = parentPath
				// Could be cleaner: this PlaceFile call rechecks the symlink thing, but it's the shortest call for "make all props right plz".
				if err := fsOp.PlaceFile(targetFs, a.fillerDirProps, nil, false); err != nil {
					return nil, err
				}
			} else {
				// Halt assembly attempt for any unhandlable errors that come up during parent path establishment.
				return nil, err
			}
		}

		// Invoke placer.
		//  Accumulate the individual cleanup funcs into a mega func we'll return.
		//  If errors occur during any placement, fire the cleanups so far before returning.
		var janitor placer.Janitor
		var err error
		switch part.WareID.Type {
		case "mount":
			janitor, err = placer.BindPlacer(unpackResults[i].Path, part.Path, false)
		default:
			janitor, err = a.placerTool(unpackResults[i].Path, part.Path, false)
		}
		if err != nil {
			hk.Teardown()
			return nil, err
		}
		hk.append(janitor)
	}
	return hk.Teardown, nil
}

type housekeeping struct {
	CleanupStack []placer.Janitor
}

func (hk *housekeeping) append(janitor placer.Janitor) {
	hk.CleanupStack = append(hk.CleanupStack, janitor)
}

func (hk housekeeping) Teardown() error {
	progress := make([]string, len(hk.CleanupStack))
	var firstError error
	for i := len(hk.CleanupStack) - 1; i >= 0; i-- {
		janitor := hk.CleanupStack[i]
		if firstError != nil && !janitor.AlwaysTry() {
			progress[i] = "\tskipped: " + janitor.Description()
			continue
		}
		err := hk.CleanupStack[i].Teardown()
		if err != nil {
			if firstError == nil {
				firstError = err
			}
			progress[i] = "\tfailed:  " + janitor.Description()
			continue
		}
		progress[i] = "\tsuccess: " + janitor.Description()
	}
	if firstError != nil {
		// Keep the category of the first one, but also fold in
		//  the string of everything that did or did not get cleaned up.
		cleanupReport := strings.Join(progress, "\n")
		firstError = ErrorDetailed(
			Category(firstError),
			fmt.Sprintf("%s.  The following cleanups were attempted:\n%s", firstError, cleanupReport),
			map[string]string{"cleanupReport": cleanupReport},
		)
	}
	return firstError
}
<commit_msg>stitch: define mount rw/ro flags.<commit_after>package stitch

import (
	"context"
	"fmt"
	"sort"
	"strings"
	"sync"

	. "github.com/polydawn/go-errcat"

	"go.polydawn.net/go-timeless-api"
	"go.polydawn.net/go-timeless-api/rio"
	"go.polydawn.net/rio/cache"
	"go.polydawn.net/rio/config"
	"go.polydawn.net/rio/fs"
	"go.polydawn.net/rio/fs/osfs"
	"go.polydawn.net/rio/fsOp"
	"go.polydawn.net/rio/stitch/placer"
)

/*
	Struct to gather the args for a single rio.Unpack func call.
	(The context object and monitors are handled in a different band.)

	Note the similar name to a structure in the go-timeless-api packages;
	this one is not serializable, is internal, and
	contains the literal set of warehouses already resolved,
	as well as the path inline rather than in a map key, so we can sort slices.
*/
type UnpackSpec struct {
	Path       fs.AbsolutePath
	WareID     api.WareID
	Filters    api.FilesetFilters
	Warehouses []api.WarehouseAddr
}

// Cast slices to this type to sort by target path (which is effectively mountability order).
type UnpackSpecByPath []UnpackSpec

func (a UnpackSpecByPath) Len() int           { return len(a) }
func (a UnpackSpecByPath) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
func (a UnpackSpecByPath) Less(i, j int) bool { return a[i].Path.String() < a[j].Path.String() }

type unpackResult struct {
	Path     fs.AbsolutePath // cache path or mount source path
	Writable bool
	Error    error
}

type Assembler struct {
	cache          fs.FS
	unpackTool     rio.UnpackFunc
	placerTool     placer.Placer
	fillerDirProps fs.Metadata
}

func NewAssembler(unpackTool rio.UnpackFunc) (*Assembler, error) {
	placerTool, err := placer.GetMountPlacer()
	if err != nil {
		return nil, err
	}
	return &Assembler{
		cache:      osfs.New(config.GetCacheBasePath()),
		unpackTool: unpackTool,
		placerTool: placerTool,
		fillerDirProps: fs.Metadata{
			Type: fs.Type_Dir, Perms: 0755, Uid: 0, Gid: 0, Mtime: fs.DefaultAtime,
		},
	}, nil
}

func (a *Assembler) Run(ctx context.Context, targetFs fs.FS, parts []UnpackSpec) (func() error, error) {
	sort.Sort(UnpackSpecByPath(parts))

	// Unpacking either wares or more mounts into paths under mounts is seriously illegal.
	//  It's a massive footgun, entirely strange, and just No.
	//  Doing it into paths under other wares is fine because it's not *leaving* our zone.
	var mounts map[fs.AbsolutePath]struct{}
	for _, part := range parts {
		for mount := range mounts {
			if strings.HasPrefix(part.Path.String(), mount.String()) {
				return nil, Errorf(rio.ErrAssemblyInvalid, "invalid inputs config: "+
					"cannot stitch additional inputs under a mount (%q is under mount at %q)",
					part.Path, mount)
			}
		}
		// If this one is a mount, mark it for the rest.
		//  (Paths under it must come after it, due to the sort.)
		if part.WareID.Type == "mount" {
			mounts[part.Path] = struct{}{}
		}
	}

	// Fan out materialization into cache paths.
	unpackResults := make([]unpackResult, len(parts))
	var wg sync.WaitGroup
	wg.Add(len(parts))
	for i, part := range parts {
		go func(i int, part UnpackSpec) {
			defer wg.Done()
			res := &unpackResults[i]
			// If it's a mount, do some parsing, and that's it for prep work.
			if part.WareID.Type == "mount" {
				ss := strings.SplitN(part.WareID.Hash, ":", 2)
				if len(ss) != 2 {
					res.Error = Errorf(rio.ErrAssemblyInvalid, "invalid inputs config: mounts must specify mode (e.g. \"ro:/path\" or \"rw:/path\"")
					return
				}
				switch ss[0] {
				case "rw":
					res.Writable = true
				case "ro":
					res.Writable = false
				default:
					res.Error = Errorf(rio.ErrAssemblyInvalid, "invalid inputs config: mounts must specify mode (e.g. \"ro:/path\" or \"rw:/path\"")
					return
				}
				res.Path, res.Error = fs.ParseAbsolutePath(ss[1])
				return
			}
			// Unpack with placement=none to populate cache.
			resultWareID, err := a.unpackTool(
				ctx, // TODO fork em out
				part.WareID,
				"-",
				part.Filters,
				rio.Placement_None,
				part.Warehouses,
				rio.Monitor{},
			)
			// Yield the cache path.
			res.Path = config.GetCacheBasePath().Join(cache.ShelfFor(resultWareID))
			res.Writable = true
			res.Error = err
			// TODO if any error, fan out cancellations
		}(i, part)
	}
	wg.Wait()
	// Yield up any errors from individual unpacks.
	for _, result := range unpackResults {
		if result.Error != nil {
			return nil, result.Error
		}
	}

	// Zip up all placements, in order.
	//  Parent dirs are made as necessary along the way.
	hk := &housekeeping{}
	for i, part := range parts {
		path := part.Path.CoerceRelative()

		// Ensure parent dirs.
		for _, parentPath := range path.Dir().Split() {
			target, isSymlink, err := targetFs.Readlink(parentPath)
			if isSymlink {
				// Future hackers: if you ever try to make this check cleverer,
				//  also make sure you include a check for host mount crossings.
				return nil, fs.NewBreakoutError(
					targetFs.BasePath(),
					path,
					parentPath,
					target,
				)
			} else if err == nil {
				continue
			} else if Category(err) == fs.ErrNotExists {
				// Make the parent dir if it does not exist.
				a.fillerDirProps.Name = parentPath
				// Could be cleaner: this PlaceFile call rechecks the symlink thing, but it's the shortest call for "make all props right plz".
				if err := fsOp.PlaceFile(targetFs, a.fillerDirProps, nil, false); err != nil {
					return nil, err
				}
			} else {
				// Halt assembly attempt for any unhandlable errors that come up during parent path establishment.
				return nil, err
			}
		}

		// Invoke placer.
		//  Accumulate the individual cleanup funcs into a mega func we'll return.
		//  If errors occur during any placement, fire the cleanups so far before returning.
		var janitor placer.Janitor
		var err error
		switch part.WareID.Type {
		case "mount":
			janitor, err = placer.BindPlacer(unpackResults[i].Path, part.Path, unpackResults[i].Writable)
		default:
			janitor, err = a.placerTool(unpackResults[i].Path, part.Path, unpackResults[i].Writable)
		}
		if err != nil {
			hk.Teardown()
			return nil, err
		}
		hk.append(janitor)
	}
	return hk.Teardown, nil
}

type housekeeping struct {
	CleanupStack []placer.Janitor
}

func (hk *housekeeping) append(janitor placer.Janitor) {
	hk.CleanupStack = append(hk.CleanupStack, janitor)
}

func (hk housekeeping) Teardown() error {
	progress := make([]string, len(hk.CleanupStack))
	var firstError error
	for i := len(hk.CleanupStack) - 1; i >= 0; i-- {
		janitor := hk.CleanupStack[i]
		if firstError != nil && !janitor.AlwaysTry() {
			progress[i] = "\tskipped: " + janitor.Description()
			continue
		}
		err := hk.CleanupStack[i].Teardown()
		if err != nil {
			if firstError == nil {
				firstError = err
			}
			progress[i] = "\tfailed:  " + janitor.Description()
			continue
		}
		progress[i] = "\tsuccess: " + janitor.Description()
	}
	if firstError != nil {
		// Keep the category of the first one, but also fold in
		//  the string of everything that did or did not get cleaned up.
		cleanupReport := strings.Join(progress, "\n")
		firstError = ErrorDetailed(
			Category(firstError),
			fmt.Sprintf("%s.  The following cleanups were attempted:\n%s", firstError, cleanupReport),
			map[string]string{"cleanupReport": cleanupReport},
		)
	}
	return firstError
}
<|endoftext|>


<commit_before>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

import (
	"errors"
	"strings"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	// ControllerConfigName is the name of config map for the controller.
	ControllerConfigName = "config-controller"

	queueSidecarImageKey           = "queueSidecarImage"
	registriesSkippingTagResolving = "registriesSkippingTagResolving"
)

// NewControllerConfigFromMap creates a Controller from the supplied Map
func NewControllerConfigFromMap(configMap map[string]string) (*Controller, error) {
	nc := &Controller{}

	if qsideCarImage, ok := configMap[queueSidecarImageKey]; !ok {
		return nil, errors.New("Queue sidecar image is missing")
	} else {
		nc.QueueSidecarImage = qsideCarImage
	}

	if registries, ok := configMap[registriesSkippingTagResolving]; !ok {
		// It is ok if registries are missing.
		nc.RegistriesSkippingTagResolving = sets.NewString("ko.local", "dev.local")
	} else {
		nc.RegistriesSkippingTagResolving = sets.NewString(strings.Split(registries, ",")...)
	}
	return nc, nil
}

// NewControllerConfigFromConfigMap creates a Controller from the supplied configMap
func NewControllerConfigFromConfigMap(config *corev1.ConfigMap) (*Controller, error) {
	return NewControllerConfigFromMap(config.Data)
}

// Controller includes the configurations for the controller.
type Controller struct {
	// QueueSidecarImage is the name of the image used for the queue sidecar
	// injected into the revision pod
	QueueSidecarImage string

	// Repositories for which tag to digest resolving should be skipped
	RegistriesSkippingTagResolving sets.String
}
<commit_msg>Drop unnecessary else (#3542)<commit_after>/*
Copyright 2018 The Knative Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    https://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package config

import (
	"errors"
	"strings"

	corev1 "k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/util/sets"
)

const (
	// ControllerConfigName is the name of config map for the controller.
	ControllerConfigName = "config-controller"

	queueSidecarImageKey           = "queueSidecarImage"
	registriesSkippingTagResolving = "registriesSkippingTagResolving"
)

// NewControllerConfigFromMap creates a Controller from the supplied Map
func NewControllerConfigFromMap(configMap map[string]string) (*Controller, error) {
	nc := &Controller{}
	qsideCarImage, ok := configMap[queueSidecarImageKey]
	if !ok {
		return nil, errors.New("Queue sidecar image is missing")
	}
	nc.QueueSidecarImage = qsideCarImage

	if registries, ok := configMap[registriesSkippingTagResolving]; !ok {
		// It is ok if registries are missing.
		nc.RegistriesSkippingTagResolving = sets.NewString("ko.local", "dev.local")
	} else {
		nc.RegistriesSkippingTagResolving = sets.NewString(strings.Split(registries, ",")...)
	}
	return nc, nil
}

// NewControllerConfigFromConfigMap creates a Controller from the supplied configMap
func NewControllerConfigFromConfigMap(config *corev1.ConfigMap) (*Controller, error) {
	return NewControllerConfigFromMap(config.Data)
}

// Controller includes the configurations for the controller.
type Controller struct {
	// QueueSidecarImage is the name of the image used for the queue sidecar
	// injected into the revision pod
	QueueSidecarImage string

	// Repositories for which tag to digest resolving should be skipped
	RegistriesSkippingTagResolving sets.String
}
<|endoftext|>


<commit_before>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package spec

import (
	"reflect"
)

// -----------------------------------------------------------------------------

// A ConstKind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type ConstKind = reflect.Kind

const (
	// BigInt - bound type - bigint
	BigInt = ConstKind(reflect.UnsafePointer + 1)
	// BigRat - bound type - bigrat
	BigRat = ConstKind(reflect.UnsafePointer + 2)
	// BigFloat - bound type - bigfloat
	BigFloat = ConstKind(reflect.UnsafePointer + 3)
	// ConstBoundRune - bound type: rune
	ConstBoundRune = reflect.Int32
	// ConstBoundString - bound type: string
	ConstBoundString = reflect.String
	// ConstUnboundInt - unbound int type
	ConstUnboundInt = ConstKind(reflect.UnsafePointer + 4)
	// ConstUnboundFloat - unbound float type
	ConstUnboundFloat = ConstKind(reflect.UnsafePointer + 5)
	// ConstUnboundComplex - unbound complex type
	ConstUnboundComplex = ConstKind(reflect.UnsafePointer + 6)
	// ConstUnboundPtr - nil: unbound ptr
	ConstUnboundPtr = ConstKind(reflect.UnsafePointer + 7)
)

// IsConstBound checks a const is bound or not.
func IsConstBound(kind ConstKind) bool {
	return kind <= BigFloat
}

// -----------------------------------------------------------------------------
<commit_msg>ast/spec: add KindName for dump<commit_after>/*
 Copyright 2020 The GoPlus Authors (goplus.org)

 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this file except in compliance with the License.
 You may obtain a copy of the License at

     http://www.apache.org/licenses/LICENSE-2.0

 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
*/

package spec

import (
	"reflect"
)

// -----------------------------------------------------------------------------

// A ConstKind represents the specific kind of type that a Type represents.
// The zero Kind is not a valid kind.
type ConstKind = reflect.Kind

const (
	// BigInt - bound type - bigint
	BigInt = ConstKind(reflect.UnsafePointer + 1)
	// BigRat - bound type - bigrat
	BigRat = ConstKind(reflect.UnsafePointer + 2)
	// BigFloat - bound type - bigfloat
	BigFloat = ConstKind(reflect.UnsafePointer + 3)
	// ConstBoundRune - bound type: rune
	ConstBoundRune = reflect.Int32
	// ConstBoundString - bound type: string
	ConstBoundString = reflect.String
	// ConstUnboundInt - unbound int type
	ConstUnboundInt = ConstKind(reflect.UnsafePointer + 4)
	// ConstUnboundFloat - unbound float type
	ConstUnboundFloat = ConstKind(reflect.UnsafePointer + 5)
	// ConstUnboundComplex - unbound complex type
	ConstUnboundComplex = ConstKind(reflect.UnsafePointer + 6)
	// ConstUnboundPtr - nil: unbound ptr
	ConstUnboundPtr = ConstKind(reflect.UnsafePointer + 7)
)

// IsConstBound checks a const is bound or not.
func IsConstBound(kind ConstKind) bool {
	return kind <= BigFloat
}

func KindName(kind ConstKind) string {
	switch kind {
	case BigInt:
		return "bigint"
	case BigRat:
		return "bigrat"
	case BigFloat:
		return "bigfloat"
	case ConstUnboundInt:
		return "unbound int"
	case ConstUnboundFloat:
		return "unbound float"
	case ConstUnboundComplex:
		return "unbound complex"
	case ConstUnboundPtr:
		return "unbound ptr"
	}
	return kind.String()
}

// -----------------------------------------------------------------------------
<|endoftext|>