<commit_before>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 db

import (
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"sync"

	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"github.com/tecbot/gorocksdb"
)

var dbLogger = logging.MustGetLogger("db")

const blockchainCF = "blockchainCF"
const stateCF = "stateCF"
const stateDeltaCF = "stateDeltaCF"
const indexesCF = "indexesCF"
const persistCF = "persistCF"

var columnfamilies = []string{
	blockchainCF, // blocks of the block chain
	stateCF,      // world state
	stateDeltaCF, // open transaction state
	indexesCF,    // tx uuid -> blockno
	persistCF,    // persistent per-peer state (consensus)
}

type dbState int32

const (
	closed dbState = iota
	opened
)

// OpenchainDB encapsulates rocksdb's structures
type OpenchainDB struct {
	DB           *gorocksdb.DB
	BlockchainCF *gorocksdb.ColumnFamilyHandle
	StateCF      *gorocksdb.ColumnFamilyHandle
	StateDeltaCF *gorocksdb.ColumnFamilyHandle
	IndexesCF    *gorocksdb.ColumnFamilyHandle
	PersistCF    *gorocksdb.ColumnFamilyHandle
	dbState      dbState
	mux          sync.Mutex
}

var openchainDB *OpenchainDB
var once sync.Once

// Create create an openchainDB instance
func Create() *OpenchainDB {
	return &OpenchainDB{dbState: closed}
}

// GetDBHandle get an opened openchainDB singleton
func GetDBHandle() *OpenchainDB {
	once.Do(func() {
		openchainDB = Create()
	})
	openchainDB.Open()

	return openchainDB
}

// GetFromBlockchainCF get value for given key from column family - blockchainCF
func (openchainDB *OpenchainDB) GetFromBlockchainCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.BlockchainCF, key)
}

// GetFromBlockchainCFSnapshot get value for given key from column family in a DB snapshot - blockchainCF
func (openchainDB *OpenchainDB) GetFromBlockchainCFSnapshot(snapshot *gorocksdb.Snapshot, key []byte) ([]byte, error) {
	return openchainDB.getFromSnapshot(snapshot, openchainDB.BlockchainCF, key)
}

// GetFromStateCF get value for given key from column family - stateCF
func (openchainDB *OpenchainDB) GetFromStateCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.StateCF, key)
}

// GetFromStateDeltaCF get value for given key from column family - stateDeltaCF
func (openchainDB *OpenchainDB) GetFromStateDeltaCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.StateDeltaCF, key)
}

// GetFromIndexesCF get value for given key from column family - indexCF
func (openchainDB *OpenchainDB) GetFromIndexesCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.IndexesCF, key)
}

// GetBlockchainCFIterator get iterator for column family - blockchainCF
func (openchainDB *OpenchainDB) GetBlockchainCFIterator() *gorocksdb.Iterator {
	return openchainDB.GetIterator(openchainDB.BlockchainCF)
}

// GetStateCFIterator get iterator for column family - stateCF
func (openchainDB *OpenchainDB) GetStateCFIterator() *gorocksdb.Iterator {
	return openchainDB.GetIterator(openchainDB.StateCF)
}

// GetStateCFSnapshotIterator get iterator for column family - stateCF. This iterator
// is based on a snapshot and should be used for long running scans, such as
// reading the entire state. Remember to call iterator.Close() when you are done.
func (openchainDB *OpenchainDB) GetStateCFSnapshotIterator(snapshot *gorocksdb.Snapshot) *gorocksdb.Iterator {
	return openchainDB.getSnapshotIterator(snapshot, openchainDB.StateCF)
}

// GetStateDeltaCFIterator get iterator for column family - stateDeltaCF
func (openchainDB *OpenchainDB) GetStateDeltaCFIterator() *gorocksdb.Iterator {
	return openchainDB.GetIterator(openchainDB.StateDeltaCF)
}

// GetSnapshot returns a point-in-time view of the DB. You MUST call snapshot.Release()
// when you are done with the snapshot.
func (openchainDB *OpenchainDB) GetSnapshot() *gorocksdb.Snapshot {
	return openchainDB.DB.NewSnapshot()
}

func getDBPath() string {
	dbPath := viper.GetString("peer.fileSystemPath")
	if dbPath == "" {
		panic("DB path not specified in configuration file. Please check that property 'peer.fileSystemPath' is set")
	}
	if !strings.HasSuffix(dbPath, "/") {
		dbPath = dbPath + "/"
	}
	return dbPath + "db"
}

// Open open underlying rocksdb
func (openchainDB *OpenchainDB) Open() {
	openchainDB.mux.Lock()
	if openchainDB.dbState == opened {
		openchainDB.mux.Unlock()
		return
	}

	defer openchainDB.mux.Unlock()

	dbPath := getDBPath()
	missing, err := dirMissingOrEmpty(dbPath)
	if err != nil {
		panic(fmt.Sprintf("Error while trying to open DB: %s", err))
	}
	dbLogger.Debugf("Is db path [%s] empty [%t]", dbPath, missing)

	if missing {
		err = os.MkdirAll(path.Dir(dbPath), 0755)
		if err != nil {
			panic(fmt.Sprintf("Error making directory path [%s]: %s", dbPath, err))
		}
	}

	opts := gorocksdb.NewDefaultOptions()
	defer opts.Destroy()

	opts.SetCreateIfMissing(missing)
	opts.SetCreateIfMissingColumnFamilies(true)

	cfNames := []string{"default"}
	cfNames = append(cfNames, columnfamilies...)
	var cfOpts []*gorocksdb.Options
	for range cfNames {
		cfOpts = append(cfOpts, opts)
	}

	db, cfHandlers, err := gorocksdb.OpenDbColumnFamilies(opts, dbPath, cfNames, cfOpts)

	if err != nil {
		panic(fmt.Sprintf("Error opening DB: %s", err))
	}

	openchainDB.DB = db
	openchainDB.BlockchainCF = cfHandlers[1]
	openchainDB.StateCF = cfHandlers[2]
	openchainDB.StateDeltaCF = cfHandlers[3]
	openchainDB.IndexesCF = cfHandlers[4]
	openchainDB.PersistCF = cfHandlers[5]
	openchainDB.dbState = opened
}

// Close releases all column family handles and closes rocksdb
func (openchainDB *OpenchainDB) Close() {
	openchainDB.mux.Lock()
	if openchainDB.dbState == closed {
		openchainDB.mux.Unlock()
		return
	}

	defer openchainDB.mux.Unlock()
	openchainDB.BlockchainCF.Destroy()
	openchainDB.StateCF.Destroy()
	openchainDB.StateDeltaCF.Destroy()
	openchainDB.IndexesCF.Destroy()
	openchainDB.PersistCF.Destroy()
	openchainDB.DB.Close()
	openchainDB.dbState = closed
}

// DeleteState delets ALL state keys/values from the DB. This is generally
// only used during state synchronization when creating a new state from
// a snapshot.
func (openchainDB *OpenchainDB) DeleteState() error {
	err := openchainDB.DB.DropColumnFamily(openchainDB.StateCF)
	if err != nil {
		dbLogger.Errorf("Error dropping state CF: %s", err)
		return err
	}
	err = openchainDB.DB.DropColumnFamily(openchainDB.StateDeltaCF)
	if err != nil {
		dbLogger.Errorf("Error dropping state delta CF: %s", err)
		return err
	}
	opts := gorocksdb.NewDefaultOptions()
	defer opts.Destroy()
	openchainDB.StateCF, err = openchainDB.DB.CreateColumnFamily(opts, stateCF)
	if err != nil {
		dbLogger.Errorf("Error creating state CF: %s", err)
		return err
	}
	openchainDB.StateDeltaCF, err = openchainDB.DB.CreateColumnFamily(opts, stateDeltaCF)
	if err != nil {
		dbLogger.Errorf("Error creating state delta CF: %s", err)
		return err
	}
	return nil
}

// Get returns the valud for the given column family and key
func (openchainDB *OpenchainDB) Get(cfHandler *gorocksdb.ColumnFamilyHandle, key []byte) ([]byte, error) {
	opt := gorocksdb.NewDefaultReadOptions()
	defer opt.Destroy()
	slice, err := openchainDB.DB.GetCF(opt, cfHandler, key)
	if err != nil {
		fmt.Println("Error while trying to retrieve key:", key)
		return nil, err
	}
	defer slice.Free()
	if slice.Data() == nil {
		return nil, nil
	}
	data := makeCopy(slice.Data())
	return data, nil
}

// Put saves the key/value in the given column family
func (openchainDB *OpenchainDB) Put(cfHandler *gorocksdb.ColumnFamilyHandle, key []byte, value []byte) error {
	opt := gorocksdb.NewDefaultWriteOptions()
	defer opt.Destroy()
	err := openchainDB.DB.PutCF(opt, cfHandler, key, value)
	if err != nil {
		fmt.Println("Error while trying to write key:", key)
		return err
	}
	return nil
}

// Delete delets the given key in the specified column family
func (openchainDB *OpenchainDB) Delete(cfHandler *gorocksdb.ColumnFamilyHandle, key []byte) error {
	opt := gorocksdb.NewDefaultWriteOptions()
	defer opt.Destroy()
	err := openchainDB.DB.DeleteCF(opt, cfHandler, key)
	if err != nil {
		fmt.Println("Error while trying to delete key:", key)
		return err
	}
	return nil
}

func (openchainDB *OpenchainDB) getFromSnapshot(snapshot *gorocksdb.Snapshot, cfHandler *gorocksdb.ColumnFamilyHandle, key []byte) ([]byte, error) {
	opt := gorocksdb.NewDefaultReadOptions()
	defer opt.Destroy()
	opt.SetSnapshot(snapshot)
	slice, err := openchainDB.DB.GetCF(opt, cfHandler, key)
	if err != nil {
		fmt.Println("Error while trying to retrieve key:", key)
		return nil, err
	}
	defer slice.Free()
	data := append([]byte(nil), slice.Data()...)
	return data, nil
}

// GetIterator returns an iterator for the given column family
func (openchainDB *OpenchainDB) GetIterator(cfHandler *gorocksdb.ColumnFamilyHandle) *gorocksdb.Iterator {
	opt := gorocksdb.NewDefaultReadOptions()
	opt.SetFillCache(true)
	defer opt.Destroy()
	return openchainDB.DB.NewIteratorCF(opt, cfHandler)
}

func (openchainDB *OpenchainDB) getSnapshotIterator(snapshot *gorocksdb.Snapshot, cfHandler *gorocksdb.ColumnFamilyHandle) *gorocksdb.Iterator {
	opt := gorocksdb.NewDefaultReadOptions()
	defer opt.Destroy()
	opt.SetSnapshot(snapshot)
	iter := openchainDB.DB.NewIteratorCF(opt, cfHandler)
	return iter
}

func dirMissingOrEmpty(path string) (bool, error) {
	dirExists, err := dirExists(path)
	if err != nil {
		return false, err
	}
	if !dirExists {
		return true, nil
	}

	dirEmpty, err := dirEmpty(path)
	if err != nil {
		return false, err
	}
	if dirEmpty {
		return true, nil
	}
	return false, nil
}

func dirExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func dirEmpty(path string) (bool, error) {
	f, err := os.Open(path)
	if err != nil {
		return false, err
	}
	defer f.Close()

	_, err = f.Readdir(1)
	if err == io.EOF {
		return true, nil
	}
	return false, err
}

func makeCopy(src []byte) []byte {
	dest := make([]byte, len(src))
	copy(dest, src)
	return dest
}
<commit_msg>remove sync.once, just init when declare<commit_after>/*
Copyright IBM Corp. 2016 All Rights Reserved.

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 db

import (
	"fmt"
	"io"
	"os"
	"path"
	"strings"
	"sync"

	"github.com/op/go-logging"
	"github.com/spf13/viper"
	"github.com/tecbot/gorocksdb"
)

var dbLogger = logging.MustGetLogger("db")

const blockchainCF = "blockchainCF"
const stateCF = "stateCF"
const stateDeltaCF = "stateDeltaCF"
const indexesCF = "indexesCF"
const persistCF = "persistCF"

var columnfamilies = []string{
	blockchainCF, // blocks of the block chain
	stateCF,      // world state
	stateDeltaCF, // open transaction state
	indexesCF,    // tx uuid -> blockno
	persistCF,    // persistent per-peer state (consensus)
}

type dbState int32

const (
	closed dbState = iota
	opened
)

// OpenchainDB encapsulates rocksdb's structures
type OpenchainDB struct {
	DB           *gorocksdb.DB
	BlockchainCF *gorocksdb.ColumnFamilyHandle
	StateCF      *gorocksdb.ColumnFamilyHandle
	StateDeltaCF *gorocksdb.ColumnFamilyHandle
	IndexesCF    *gorocksdb.ColumnFamilyHandle
	PersistCF    *gorocksdb.ColumnFamilyHandle
	dbState      dbState
	mux          sync.Mutex
}

var openchainDB = Create()

// Create create an openchainDB instance
func Create() *OpenchainDB {
	return &OpenchainDB{dbState: closed}
}

// GetDBHandle get an opened openchainDB singleton
func GetDBHandle() *OpenchainDB {
	openchainDB.Open()
	return openchainDB
}

// GetFromBlockchainCF get value for given key from column family - blockchainCF
func (openchainDB *OpenchainDB) GetFromBlockchainCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.BlockchainCF, key)
}

// GetFromBlockchainCFSnapshot get value for given key from column family in a DB snapshot - blockchainCF
func (openchainDB *OpenchainDB) GetFromBlockchainCFSnapshot(snapshot *gorocksdb.Snapshot, key []byte) ([]byte, error) {
	return openchainDB.getFromSnapshot(snapshot, openchainDB.BlockchainCF, key)
}

// GetFromStateCF get value for given key from column family - stateCF
func (openchainDB *OpenchainDB) GetFromStateCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.StateCF, key)
}

// GetFromStateDeltaCF get value for given key from column family - stateDeltaCF
func (openchainDB *OpenchainDB) GetFromStateDeltaCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.StateDeltaCF, key)
}

// GetFromIndexesCF get value for given key from column family - indexCF
func (openchainDB *OpenchainDB) GetFromIndexesCF(key []byte) ([]byte, error) {
	return openchainDB.Get(openchainDB.IndexesCF, key)
}

// GetBlockchainCFIterator get iterator for column family - blockchainCF
func (openchainDB *OpenchainDB) GetBlockchainCFIterator() *gorocksdb.Iterator {
	return openchainDB.GetIterator(openchainDB.BlockchainCF)
}

// GetStateCFIterator get iterator for column family - stateCF
func (openchainDB *OpenchainDB) GetStateCFIterator() *gorocksdb.Iterator {
	return openchainDB.GetIterator(openchainDB.StateCF)
}

// GetStateCFSnapshotIterator get iterator for column family - stateCF. This iterator
// is based on a snapshot and should be used for long running scans, such as
// reading the entire state. Remember to call iterator.Close() when you are done.
func (openchainDB *OpenchainDB) GetStateCFSnapshotIterator(snapshot *gorocksdb.Snapshot) *gorocksdb.Iterator {
	return openchainDB.getSnapshotIterator(snapshot, openchainDB.StateCF)
}

// GetStateDeltaCFIterator get iterator for column family - stateDeltaCF
func (openchainDB *OpenchainDB) GetStateDeltaCFIterator() *gorocksdb.Iterator {
	return openchainDB.GetIterator(openchainDB.StateDeltaCF)
}

// GetSnapshot returns a point-in-time view of the DB. You MUST call snapshot.Release()
// when you are done with the snapshot.
func (openchainDB *OpenchainDB) GetSnapshot() *gorocksdb.Snapshot {
	return openchainDB.DB.NewSnapshot()
}

func getDBPath() string {
	dbPath := viper.GetString("peer.fileSystemPath")
	if dbPath == "" {
		panic("DB path not specified in configuration file. Please check that property 'peer.fileSystemPath' is set")
	}
	if !strings.HasSuffix(dbPath, "/") {
		dbPath = dbPath + "/"
	}
	return dbPath + "db"
}

// Open open underlying rocksdb
func (openchainDB *OpenchainDB) Open() {
	openchainDB.mux.Lock()
	if openchainDB.dbState == opened {
		openchainDB.mux.Unlock()
		return
	}

	defer openchainDB.mux.Unlock()

	dbPath := getDBPath()
	missing, err := dirMissingOrEmpty(dbPath)
	if err != nil {
		panic(fmt.Sprintf("Error while trying to open DB: %s", err))
	}
	dbLogger.Debugf("Is db path [%s] empty [%t]", dbPath, missing)

	if missing {
		err = os.MkdirAll(path.Dir(dbPath), 0755)
		if err != nil {
			panic(fmt.Sprintf("Error making directory path [%s]: %s", dbPath, err))
		}
	}

	opts := gorocksdb.NewDefaultOptions()
	defer opts.Destroy()

	opts.SetCreateIfMissing(missing)
	opts.SetCreateIfMissingColumnFamilies(true)

	cfNames := []string{"default"}
	cfNames = append(cfNames, columnfamilies...)
	var cfOpts []*gorocksdb.Options
	for range cfNames {
		cfOpts = append(cfOpts, opts)
	}

	db, cfHandlers, err := gorocksdb.OpenDbColumnFamilies(opts, dbPath, cfNames, cfOpts)

	if err != nil {
		panic(fmt.Sprintf("Error opening DB: %s", err))
	}

	openchainDB.DB = db
	openchainDB.BlockchainCF = cfHandlers[1]
	openchainDB.StateCF = cfHandlers[2]
	openchainDB.StateDeltaCF = cfHandlers[3]
	openchainDB.IndexesCF = cfHandlers[4]
	openchainDB.PersistCF = cfHandlers[5]
	openchainDB.dbState = opened
}

// Close releases all column family handles and closes rocksdb
func (openchainDB *OpenchainDB) Close() {
	openchainDB.mux.Lock()
	if openchainDB.dbState == closed {
		openchainDB.mux.Unlock()
		return
	}

	defer openchainDB.mux.Unlock()
	openchainDB.BlockchainCF.Destroy()
	openchainDB.StateCF.Destroy()
	openchainDB.StateDeltaCF.Destroy()
	openchainDB.IndexesCF.Destroy()
	openchainDB.PersistCF.Destroy()
	openchainDB.DB.Close()
	openchainDB.dbState = closed
}

// DeleteState delets ALL state keys/values from the DB. This is generally
// only used during state synchronization when creating a new state from
// a snapshot.
func (openchainDB *OpenchainDB) DeleteState() error {
	err := openchainDB.DB.DropColumnFamily(openchainDB.StateCF)
	if err != nil {
		dbLogger.Errorf("Error dropping state CF: %s", err)
		return err
	}
	err = openchainDB.DB.DropColumnFamily(openchainDB.StateDeltaCF)
	if err != nil {
		dbLogger.Errorf("Error dropping state delta CF: %s", err)
		return err
	}
	opts := gorocksdb.NewDefaultOptions()
	defer opts.Destroy()
	openchainDB.StateCF, err = openchainDB.DB.CreateColumnFamily(opts, stateCF)
	if err != nil {
		dbLogger.Errorf("Error creating state CF: %s", err)
		return err
	}
	openchainDB.StateDeltaCF, err = openchainDB.DB.CreateColumnFamily(opts, stateDeltaCF)
	if err != nil {
		dbLogger.Errorf("Error creating state delta CF: %s", err)
		return err
	}
	return nil
}

// Get returns the valud for the given column family and key
func (openchainDB *OpenchainDB) Get(cfHandler *gorocksdb.ColumnFamilyHandle, key []byte) ([]byte, error) {
	opt := gorocksdb.NewDefaultReadOptions()
	defer opt.Destroy()
	slice, err := openchainDB.DB.GetCF(opt, cfHandler, key)
	if err != nil {
		fmt.Println("Error while trying to retrieve key:", key)
		return nil, err
	}
	defer slice.Free()
	if slice.Data() == nil {
		return nil, nil
	}
	data := makeCopy(slice.Data())
	return data, nil
}

// Put saves the key/value in the given column family
func (openchainDB *OpenchainDB) Put(cfHandler *gorocksdb.ColumnFamilyHandle, key []byte, value []byte) error {
	opt := gorocksdb.NewDefaultWriteOptions()
	defer opt.Destroy()
	err := openchainDB.DB.PutCF(opt, cfHandler, key, value)
	if err != nil {
		fmt.Println("Error while trying to write key:", key)
		return err
	}
	return nil
}

// Delete delets the given key in the specified column family
func (openchainDB *OpenchainDB) Delete(cfHandler *gorocksdb.ColumnFamilyHandle, key []byte) error {
	opt := gorocksdb.NewDefaultWriteOptions()
	defer opt.Destroy()
	err := openchainDB.DB.DeleteCF(opt, cfHandler, key)
	if err != nil {
		fmt.Println("Error while trying to delete key:", key)
		return err
	}
	return nil
}

func (openchainDB *OpenchainDB) getFromSnapshot(snapshot *gorocksdb.Snapshot, cfHandler *gorocksdb.ColumnFamilyHandle, key []byte) ([]byte, error) {
	opt := gorocksdb.NewDefaultReadOptions()
	defer opt.Destroy()
	opt.SetSnapshot(snapshot)
	slice, err := openchainDB.DB.GetCF(opt, cfHandler, key)
	if err != nil {
		fmt.Println("Error while trying to retrieve key:", key)
		return nil, err
	}
	defer slice.Free()
	data := append([]byte(nil), slice.Data()...)
	return data, nil
}

// GetIterator returns an iterator for the given column family
func (openchainDB *OpenchainDB) GetIterator(cfHandler *gorocksdb.ColumnFamilyHandle) *gorocksdb.Iterator {
	opt := gorocksdb.NewDefaultReadOptions()
	opt.SetFillCache(true)
	defer opt.Destroy()
	return openchainDB.DB.NewIteratorCF(opt, cfHandler)
}

func (openchainDB *OpenchainDB) getSnapshotIterator(snapshot *gorocksdb.Snapshot, cfHandler *gorocksdb.ColumnFamilyHandle) *gorocksdb.Iterator {
	opt := gorocksdb.NewDefaultReadOptions()
	defer opt.Destroy()
	opt.SetSnapshot(snapshot)
	iter := openchainDB.DB.NewIteratorCF(opt, cfHandler)
	return iter
}

func dirMissingOrEmpty(path string) (bool, error) {
	dirExists, err := dirExists(path)
	if err != nil {
		return false, err
	}
	if !dirExists {
		return true, nil
	}

	dirEmpty, err := dirEmpty(path)
	if err != nil {
		return false, err
	}
	if dirEmpty {
		return true, nil
	}
	return false, nil
}

func dirExists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}

func dirEmpty(path string) (bool, error) {
	f, err := os.Open(path)
	if err != nil {
		return false, err
	}
	defer f.Close()

	_, err = f.Readdir(1)
	if err == io.EOF {
		return true, nil
	}
	return false, err
}

func makeCopy(src []byte) []byte {
	dest := make([]byte, len(src))
	copy(dest, src)
	return dest
}
<|endoftext|>


<commit_before>// Debug functions.
package core

import (
	"encoding/json"
	"fmt"
	"github.com/DanielRenne/GoCore/core/extensions"
	"github.com/DanielRenne/GoCore/core/serverSettings"
	"github.com/davidrenne/reflections"
	"github.com/go-errors/errors"
	"log"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

type core_debug struct{}

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

var TransactionLog string
var Debug = core_debug{}
var Logger = core_logger
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
}

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
}

func (self *core_debug) PrintCallStack() {
	debug.PrintStack()
}

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))
}

func (self *core_debug) FormatSkip(skip int, value interface{}) string {
	return self.formatValue(value) + self.formatCallstack(skip+1)
}

func (self *core_debug) Format(value interface{}) string {
	return self.FormatSkip(2, value)
}

func (self *core_debug) DumpQuiet(values ...interface{}) {
	// uncomment below to find your callers to quiet
	self.Print("Silently not dumping " + extensions.IntToString(len(values)) + " values")
	//Logger.Println("DumpQuiet has " + extensions.IntToString(len(values)) + " parameters called")
	//Logger.Println("")
	//self.ThrowAndPrintError()
}

func IsZeroOfUnderlyingType(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

func IsZeroOfUnderlyingType2(x interface{}) bool {
	return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}

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 ""
}

func (self *core_debug) Dump(valuesOriginal ...interface{}) {
	t := time.Now()
	l := "!!!!!!!!!!!!! DEBUG " + t.String() + "!!!!!!!!!!!!!\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.String() + "!!!!!!!!!!!!!"
	Logger.Println(l)
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
}

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) DumpBase(values ...interface{}) (output string) {
	//golog "github.com/DanielRenne/GoCore/core/log"
	//defer golog.TimeTrack(time.Now(), "Dump")
	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 {
				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 {
					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 {
						output += "#### + " + kind + " " + stringVal + "[len:" + extensions.IntToString(len(stringVal)) + "]####" + "\n"
						// for _, tmp := range strings.Split(stringVal, "\\n") {
						// 	output += "\n" + tmp
						// }
					} else {
						output += stringVal[6:] + " --> "
					}
				} else {
					output += fmt.Sprintf("#### %-39s ####\n%+v", kind, value)
				}
			}
		}
	}
	return output
}

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")
		filePathSplit := strings.Split(stack[7], ".go:")
		filePaths := strings.Split(filePathSplit[0], "/")
		fileName := filePaths[len(filePaths)-1] + ".go"
		lineParts := strings.Split(filePathSplit[1], "(")
		lineNumber := strings.TrimSpace(lineParts[0])

		finalLineOfCode := strings.TrimSpace(stack[8])

		if strings.Index(finalLineOfCode, "Desc->Caller for Query") == -1 {
			output += "\nDump Caller (" + fileName + ":" + lineNumber + "):"
			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---------------"
			output += "\n"
			output += "\n"
		}
	}
	return output
}

func (self *core_debug) ThrowError() *errors.Error {
	return errors.Errorf("Debug Dump")
}

func (self *core_debug) Print(values ...interface{}) {
	if Logger != nil {
		Logger.Print(values...)
	}
}

func (self *core_debug) Printf(format string, values ...interface{}) {
	if Logger != nil {
		Logger.Printf(format, values...)
	}
}
<commit_msg>couple of helpers for returning output of GetDump<commit_after>// Debug functions.
package core

import (
	"encoding/json"
	"fmt"
	"github.com/DanielRenne/GoCore/core/extensions"
	"github.com/DanielRenne/GoCore/core/serverSettings"
	"github.com/davidrenne/reflections"
	"github.com/go-errors/errors"
	"log"
	"os"
	"reflect"
	"runtime"
	"runtime/debug"
	"strings"
	"sync"
	"time"
)

type core_debug struct{}

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

var TransactionLog string
var Debug = core_debug{}
var Logger = core_logger
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
}

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
}

func (self *core_debug) PrintCallStack() {
	debug.PrintStack()
}

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))
}

func (self *core_debug) FormatSkip(skip int, value interface{}) string {
	return self.formatValue(value) + self.formatCallstack(skip+1)
}

func (self *core_debug) Format(value interface{}) string {
	return self.FormatSkip(2, value)
}

func (self *core_debug) DumpQuiet(values ...interface{}) {
	// uncomment below to find your callers to quiet
	self.Print("Silently not dumping " + extensions.IntToString(len(values)) + " values")
	//Logger.Println("DumpQuiet has " + extensions.IntToString(len(values)) + " parameters called")
	//Logger.Println("")
	//self.ThrowAndPrintError()
}

func IsZeroOfUnderlyingType(x interface{}) bool {
	return reflect.DeepEqual(x, reflect.Zero(reflect.TypeOf(x)).Interface())
}

func IsZeroOfUnderlyingType2(x interface{}) bool {
	return x == reflect.Zero(reflect.TypeOf(x)).Interface()
}

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 ""
}

func (self *core_debug) Dump(valuesOriginal ...interface{}) {
	t := time.Now()
	l := "!!!!!!!!!!!!! DEBUG " + t.String() + "!!!!!!!!!!!!!\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.String() + "!!!!!!!!!!!!!"
	Logger.Println(l)
	serverSettings.WebConfigMutex.RLock()
	if serverSettings.WebConfig.Application.ReleaseMode == "development" {
		TransactionLogMutex.Lock()
		TransactionLog += l
		TransactionLogMutex.Unlock()
	}
	serverSettings.WebConfigMutex.RUnlock()
}

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()
	l := "\n!!!!!!!!!!!!! DEBUG " + t.String() + "!!!!!!!!!!!!!\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)
	}

	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 " + t.String() + "!!!!!!!!!!!!!\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) {
	//golog "github.com/DanielRenne/GoCore/core/log"
	//defer golog.TimeTrack(time.Now(), "Dump")
	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 {
				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 {
					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 {
						output += "#### + " + kind + " " + stringVal + "[len:" + extensions.IntToString(len(stringVal)) + "]####" + "\n"
						// for _, tmp := range strings.Split(stringVal, "\\n") {
						// 	output += "\n" + tmp
						// }
					} else {
						output += stringVal[6:] + " --> "
					}
				} else {
					output += fmt.Sprintf("#### %-39s ####\n%+v", kind, value)
				}
			}
		}
	}
	return output
}

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")
		filePathSplit := strings.Split(stack[7], ".go:")
		filePaths := strings.Split(filePathSplit[0], "/")
		fileName := filePaths[len(filePaths)-1] + ".go"
		lineParts := strings.Split(filePathSplit[1], "(")
		lineNumber := strings.TrimSpace(lineParts[0])

		finalLineOfCode := strings.TrimSpace(stack[8])

		if strings.Index(finalLineOfCode, "Desc->Caller for Query") == -1 {
			output += "\nDump Caller (" + fileName + ":" + lineNumber + "):"
			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])
			output += "\n---------------"
			output += "\n"
			output += "\n"
		}
	}
	return output
}

func (self *core_debug) ThrowError() *errors.Error {
	return errors.Errorf("Debug Dump")
}

func (self *core_debug) Print(values ...interface{}) {
	if Logger != nil {
		Logger.Print(values...)
	}
}

func (self *core_debug) Printf(format string, values ...interface{}) {
	if Logger != nil {
		Logger.Printf(format, values...)
	}
}
<|endoftext|>


<commit_before>package core

/*
#cgo CFLAGS: -I./src/include
#include "lwip/pbuf.h"
#include "lwip/timeouts.h"
#include "lwip/tcp.h"

err_t
input(struct pbuf *p)
{
	return (*netif_list).input(p, netif_list);
}
*/
import "C"
import (
	"unsafe"
)

func Input(pkt []byte) (int, error) {
	buf := C.pbuf_alloc_reference(unsafe.Pointer(&pkt[0]), C.u16_t(len(pkt)), C.PBUF_ROM)
	lwipMutex.Lock()
	C.input(buf)
	C.sys_check_timeouts()
	lwipMutex.Unlock()
	return len(pkt), nil
}
<commit_msg>has a chance failed here<commit_after>package core

/*
#cgo CFLAGS: -I./src/include
#include "lwip/pbuf.h"
#include "lwip/timeouts.h"
#include "lwip/tcp.h"

err_t
input(struct pbuf *p)
{
	return (*netif_list).input(p, netif_list);
}
*/
import "C"
import (
	"unsafe"
)

func Input(pkt []byte) (int, error) {
	buf := C.pbuf_alloc_reference(unsafe.Pointer(&pkt[0]), C.u16_t(len(pkt)), C.PBUF_ROM)
	lwipMutex.Lock()
	err := C.input(buf)
	if err != C.ERR_OK {
		C.pbuf_free(buf)
		// TODO
		panic("why failed!?")
	}
	C.sys_check_timeouts()
	lwipMutex.Unlock()
	return len(pkt), nil
}
<|endoftext|>


<commit_before>// Package core provides Blocks, Sources and the means to connect them together. Blocks communicate with
// one another by passing Messages.
package core

import "sync"

const (
	NONE = iota
	KEY_VALUE
	STREAM
	LIST
	VALUE_PRIMITIVE
	PRIORITY
)

// MessageMap maps a block's inbound routes onto the Messages they contain
type MessageMap map[RouteIndex]Message

// Message is the container for data sent between blocks
type Message interface{}

// RouteIndex is the index into a MessageMap. The 0th index corresponds to that block's 0th Input or Output
type RouteIndex int

// SourceType is used to indicate what kind of source a block can connect to
type SourceType int

// Connections are used to connect blocks together
type Connection chan Message

// Interrupt is a function that interrupts a running block in order to change its state.
// If the interrupt returns false, the block will quit.
type Interrupt func() bool

// Kernel is a block's core function that operates on an inbound message. It works by populating
// the outbound MessageMap, and can be interrupted on its Interrupt channel.
type Kernel func(MessageMap, MessageMap, MessageMap, Source, chan Interrupt) Interrupt

// A Pin contains information about a particular input or output
type Pin struct {
	Name string
}

// A Spec defines a block's input and output Pins, and the block's Kernel.
type Spec struct {
	Name    string
	Inputs  []Pin
	Outputs []Pin
	Source  SourceType
	Kernel  Kernel
}

// Input is an inbound route to a block. A Input holds the channel that allows Messages
// to be passed into the block. A Input's Path is applied to the inbound Message before populating the
// MessageMap and calling the Kernel. A Input can be set to a Value, instead of waiting for an inbound message.
type Input struct {
	Name string
	C    chan Message
}

// An Output holds a set of Connections. Each Connection refers to a Input.C. Every outbound
// mesage is sent on every Connection in the Connections set.
type Output struct {
	Name        string                  `json:"name"`
	Connections map[Connection]struct{} `json:"-"`
}

// A SourceSpec defines a source's name and type
type SourceSpec struct {
	Name string
	Type int
	New  SourceFunc
}

// A function that creates a source
type SourceFunc func() Source

// A ManifestPair is a unique reference to an Output/Connection pair
type ManifestPair struct {
	int
	Connection
}

// A block's Manifest is the set of Connections
type Manifest map[ManifestPair]struct{}

// A block's BlockState is the pair of input/output MessageMaps, and the Manifest
type BlockState struct {
	inputValues    MessageMap
	outputValues   MessageMap
	internalValues MessageMap
	manifest       Manifest
	Processed      bool
}

// a Source is esssentially a lockable piece of memory that can be accessed safely by mulitple blocks.
// The Lock and Unlock methods are usually implemented using a sync.Mutex
// TODO Source -> Source
type Source interface {
	Lock()
	Unlock()
	Describe() map[string]string
	Serve()
	Stop()
	SetSourceParameter(key, value string)
	GetType() SourceType
}

// A block's BlockRouting is the set of Input and Output routes, and the Interrupt channel
type BlockRouting struct {
	Inputs        []Input
	Outputs       []Output
	Source        Source
	InterruptChan chan Interrupt
	sync.RWMutex
}

// A Block is comprised of a state, a set of routes, and a kernel
type Block struct {
	state      BlockState
	routing    BlockRouting
	kernel     Kernel
	sourceType SourceType
}
<commit_msg>fixing types for source/interface/store change<commit_after>// Package core provides Blocks, Sources and the means to connect them together. Blocks communicate with
// one another by passing Messages.
package core

import "sync"

const (
	NONE = iota
	KEY_VALUE
	STREAM
	LIST
	VALUE_PRIMITIVE
	PRIORITY
)

// MessageMap maps a block's inbound routes onto the Messages they contain
type MessageMap map[RouteIndex]Message

// Message is the container for data sent between blocks
type Message interface{}

// RouteIndex is the index into a MessageMap. The 0th index corresponds to that block's 0th Input or Output
type RouteIndex int

// SourceType is used to indicate what kind of source a block can connect to
type SourceType int

// Connections are used to connect blocks together
type Connection chan Message

// Interrupt is a function that interrupts a running block in order to change its state.
// If the interrupt returns false, the block will quit.
type Interrupt func() bool

// Kernel is a block's core function that operates on an inbound message. It works by populating
// the outbound MessageMap, and can be interrupted on its Interrupt channel.
type Kernel func(MessageMap, MessageMap, MessageMap, Source, chan Interrupt) Interrupt

// A Pin contains information about a particular input or output
type Pin struct {
	Name string
}

// A Spec defines a block's input and output Pins, and the block's Kernel.
type Spec struct {
	Name    string
	Inputs  []Pin
	Outputs []Pin
	Source  SourceType
	Kernel  Kernel
}

// Input is an inbound route to a block. A Input holds the channel that allows Messages
// to be passed into the block. A Input's Path is applied to the inbound Message before populating the
// MessageMap and calling the Kernel. A Input can be set to a Value, instead of waiting for an inbound message.
type Input struct {
	Name string
	C    chan Message
}

// An Output holds a set of Connections. Each Connection refers to a Input.C. Every outbound
// mesage is sent on every Connection in the Connections set.
type Output struct {
	Name        string                  `json:"name"`
	Connections map[Connection]struct{} `json:"-"`
}

// A SourceSpec defines a source's name and type
type SourceSpec struct {
	Name string
	Type int
	New  SourceFunc
}

// A function that creates a source
type SourceFunc func() Source

// A ManifestPair is a unique reference to an Output/Connection pair
type ManifestPair struct {
	int
	Connection
}

// A block's Manifest is the set of Connections
type Manifest map[ManifestPair]struct{}

// A block's BlockState is the pair of input/output MessageMaps, and the Manifest
type BlockState struct {
	inputValues    MessageMap
	outputValues   MessageMap
	internalValues MessageMap
	manifest       Manifest
	Processed      bool
}

// a Source is esssentially a lockable piece of memory that can be accessed safely by mulitple blocks.
// The Lock and Unlock methods are usually implemented using a sync.Mutex
// TODO Source -> Source
type Source interface {
	Lock()
	Unlock()
	GetType() SourceType
}

type Interface interface {
	Source
	Describe() map[string]string
	Serve()
	Stop()
	SetSourceParameter(key, value string)
}

type Store interface {
	Source
	Get() interface{}
	Set() interface{}
}

// A block's BlockRouting is the set of Input and Output routes, and the Interrupt channel
type BlockRouting struct {
	Inputs        []Input
	Outputs       []Output
	Source        Source
	InterruptChan chan Interrupt
	sync.RWMutex
}

// A Block is comprised of a state, a set of routes, and a kernel
type Block struct {
	state      BlockState
	routing    BlockRouting
	kernel     Kernel
	sourceType SourceType
}
<|endoftext|>


<commit_before>package cli

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

const (
	AppName = "dockward"
	Version = "0.0.2"
	Usage   = `Usage: dockward [options...] <port> [<container port> <filter>] [endpoints...]
try 'dockward --help' for more.
`
	FullUsage = `Usage: dockward [options...] <port> [<container port> <filter>] [endpoints...]

options:
  --host=false         Host mode, forward to host endpoints instead of container.
  --docker-host=""     Ip address of docker host if this machine is not docker host.
                       For monitoring and adding/removing filtered containers.
  --policy="random"    Load balancer scheduling policy. One of "random", "round_robin".
  -h, --help=false     Show this help.
  -v, --version=false  Show version.

port:
  port to listen on. e.g. 8080

container port:
  port to forward to inside in the container. Defaults to <port>.

filter:
  Containers' filter. Supports id, name, label.
  e.g. id=749bdeaf6920, name=amazing_leavitt, label=com.myorg.key=value.

endpoints:
  port, ip/host or ip/host:port to forward to. Requires --host.

`
)

type cliConf struct {
	HostPort      int
	ContainerPort int
	Filter        string
	FilterValue   string
	Host          bool
	Endpoints     []string
	DockerHost    string
	Policy        string

	Help         bool
	Version      bool
	remoteClient bool
}

func parseCli() cliConf {
	conf := cliConf{Policy: "random"}

	fs := flag.FlagSet{}
	fs.SetOutput(ioutil.Discard)

	fs.BoolVar(&conf.Host, "host", conf.Host, "")
	fs.StringVar(&conf.DockerHost, "docker-host", conf.DockerHost, "")
	fs.BoolVar(&conf.Help, "h", conf.Help, "")
	fs.BoolVar(&conf.Help, "help", conf.Help, "")
	fs.BoolVar(&conf.Version, "v", conf.Version, "")
	fs.BoolVar(&conf.Version, "version", conf.Version, "")
	fs.StringVar(&conf.Policy, "policy", conf.Policy, "")
	fs.BoolVar(&conf.remoteClient, "remote-client", conf.remoteClient, "")

	err := fs.Parse(os.Args[1:])
	exitIfErr(err)

	if conf.Help {
		fmt.Println(FullUsage)
		exit(nil)
	}
	if conf.Version {
		fmt.Println("dockward version", Version)
		exit(nil)
	}

	switch fs.NArg() {
	case 0:
		usageErr(fmt.Errorf("port missing."))
	case 1:
		if !conf.Host {
			// docker mode
			usageErr(fmt.Errorf("filter missing."))
		} else if !conf.remoteClient {
			// host mode
			usageErr(fmt.Errorf("endpoint(s) missing."))
		}

	}

	args := fs.Args()

	conf.HostPort, err = strconv.Atoi(args[0])
	if err != nil {
		usageErr(err)
	}

	// if not host mode, require one container param.
	if !conf.Host {
		var filterArg string
		if len(args) > 2 {
			conf.ContainerPort, err = strconv.Atoi(args[1])
			if err != nil {
				usageErr(err)
			}
			filterArg = args[2]
		} else {
			conf.ContainerPort = conf.HostPort
			filterArg = args[1]
		}

		str := strings.SplitN(filterArg, "=", 2)
		if len(str) != 2 {
			usageErr(fmt.Errorf("Invalid filter."))
		}

		conf.Filter, conf.FilterValue = str[0], str[1]

		switch conf.Filter {
		case "id", "name", "label":
		default:
			usageErr(fmt.Errorf("Invalid filter %s", conf.Filter))
		}

	} else {
		// if host mode, load endpoints.
		conf.Endpoints = args[1:]
	}

	return conf
}

func usageErr(err error) {
	exit(fmt.Errorf("dockward: %v\n%v", err, Usage))
}

func exitIfErr(err error) {
	if err != nil {
		exit(err)
	}
}

func exit(err error) {
	if err == nil {
		os.Exit(0)
	}
	fmt.Println(err)
	os.Exit(1)
}
<commit_msg>0.0.3<commit_after>package cli

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"strconv"
	"strings"
)

const (
	AppName = "dockward"
	Version = "0.0.3"
	Usage   = `Usage: dockward [options...] <port> [<container port> <filter>] [endpoints...]
try 'dockward --help' for more.
`
	FullUsage = `Usage: dockward [options...] <port> [<container port> <filter>] [endpoints...]

options:
  --host=false         Host mode, forward to host endpoints instead of container.
  --docker-host=""     Ip address of docker host if this machine is not docker host.
                       For monitoring and adding/removing filtered containers.
  --policy="random"    Load balancer scheduling policy. One of "random", "round_robin".
  -h, --help=false     Show this help.
  -v, --version=false  Show version.

port:
  port to listen on. e.g. 8080

container port:
  port to forward to inside in the container. Defaults to <port>.

filter:
  Containers' filter. Supports id, name, label.
  e.g. id=749bdeaf6920, name=amazing_leavitt, label=com.myorg.key=value.

endpoints:
  port, ip/host or ip/host:port to forward to. Requires --host.

`
)

type cliConf struct {
	HostPort      int
	ContainerPort int
	Filter        string
	FilterValue   string
	Host          bool
	Endpoints     []string
	DockerHost    string
	Policy        string

	Help         bool
	Version      bool
	remoteClient bool
}

func parseCli() cliConf {
	conf := cliConf{Policy: "random"}

	fs := flag.FlagSet{}
	fs.SetOutput(ioutil.Discard)

	fs.BoolVar(&conf.Host, "host", conf.Host, "")
	fs.StringVar(&conf.DockerHost, "docker-host", conf.DockerHost, "")
	fs.BoolVar(&conf.Help, "h", conf.Help, "")
	fs.BoolVar(&conf.Help, "help", conf.Help, "")
	fs.BoolVar(&conf.Version, "v", conf.Version, "")
	fs.BoolVar(&conf.Version, "version", conf.Version, "")
	fs.StringVar(&conf.Policy, "policy", conf.Policy, "")
	fs.BoolVar(&conf.remoteClient, "remote-client", conf.remoteClient, "")

	err := fs.Parse(os.Args[1:])
	exitIfErr(err)

	if conf.Help {
		fmt.Println(FullUsage)
		exit(nil)
	}
	if conf.Version {
		fmt.Println(AppName, "version", Version)
		exit(nil)
	}

	switch fs.NArg() {
	case 0:
		usageErr(fmt.Errorf("port missing."))
	case 1:
		if !conf.Host {
			// docker mode
			usageErr(fmt.Errorf("filter missing."))
		} else if !conf.remoteClient {
			// host mode
			usageErr(fmt.Errorf("endpoint(s) missing."))
		}

	}

	args := fs.Args()

	conf.HostPort, err = strconv.Atoi(args[0])
	if err != nil {
		usageErr(err)
	}

	// if not host mode, require one container param.
	if !conf.Host {
		var filterArg string
		if len(args) > 2 {
			conf.ContainerPort, err = strconv.Atoi(args[1])
			if err != nil {
				usageErr(err)
			}
			filterArg = args[2]
		} else {
			conf.ContainerPort = conf.HostPort
			filterArg = args[1]
		}

		str := strings.SplitN(filterArg, "=", 2)
		if len(str) != 2 {
			usageErr(fmt.Errorf("Invalid filter."))
		}

		conf.Filter, conf.FilterValue = str[0], str[1]

		switch conf.Filter {
		case "id", "name", "label":
		default:
			usageErr(fmt.Errorf("Invalid filter %s", conf.Filter))
		}

	} else {
		// if host mode, load endpoints.
		conf.Endpoints = args[1:]
	}

	return conf
}

func usageErr(err error) {
	exit(fmt.Errorf("dockward: %v\n%v", err, Usage))
}

func exitIfErr(err error) {
	if err != nil {
		exit(err)
	}
}

func exit(err error) {
	if err == nil {
		os.Exit(0)
	}
	fmt.Println(err)
	os.Exit(1)
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"

	"os"

	"strings"

	"github.com/dutchcoders/transfer.sh/server"
	"github.com/fatih/color"
	"github.com/minio/cli"
)

var Version = "0.1"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
	`{{ "\n"}}`

var globalFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "listener",
		Usage: "127.0.0.1:8080",
		Value: "127.0.0.1:8080",
	},
	// redirect to https?
	// hostnames
	cli.StringFlag{
		Name:  "profile-listener",
		Usage: "127.0.0.1:6060",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "force-https",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-listener",
		Usage: "127.0.0.1:8443",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-cert-file",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-private-key",
		Value: "",
	},
	cli.StringFlag{
		Name:  "temp-path",
		Usage: "path to temp files",
		Value: os.TempDir(),
	},
	cli.StringFlag{
		Name:  "web-path",
		Usage: "path to static web files",
		Value: "",
	},
	cli.StringFlag{
		Name:  "provider",
		Usage: "s3|gdrive|local",
		Value: "",
	},
	cli.StringFlag{
		Name: "s3-endpoint",
		Usage: "",
		Value: "http://s3-eu-west-1.amazonaws.com",
		EnvVar: "S3_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "aws-access-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_ACCESS_KEY",
	},
	cli.StringFlag{
		Name:   "aws-secret-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_SECRET_KEY",
	},
	cli.StringFlag{
		Name:   "bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "BUCKET",
	},
	cli.StringFlag{
		Name:   "gdrive-client-json-filepath",
		Usage:  "",
		Value:  "",
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "gdrive-local-config-path",
		Usage:  "",
		Value:  "",
		EnvVar: "",
	},
	cli.IntFlag{
		Name:   "rate-limit",
		Usage:  "requests per minute",
		Value:  0,
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "lets-encrypt-hosts",
		Usage:  "host1, host2",
		Value:  "",
		EnvVar: "HOSTS",
	},
	cli.StringFlag{
		Name:  "log",
		Usage: "/var/log/transfersh.log",
		Value: "",
	},
	cli.StringFlag{
		Name:  "basedir",
		Usage: "path to storage",
		Value: "",
	},
	cli.StringFlag{
		Name:   "clamav-host",
		Usage:  "clamav-host",
		Value:  "",
		EnvVar: "CLAMAV_HOST",
	},
	cli.StringFlag{
		Name:   "virustotal-key",
		Usage:  "virustotal-key",
		Value:  "",
		EnvVar: "VIRUSTOTAL_KEY",
	},
	cli.BoolFlag{
		Name:  "profiler",
		Usage: "enable profiling",
	},
}

type Cmd struct {
	*cli.App
}

func VersionAction(c *cli.Context) {
	fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
	app := cli.NewApp()
	app.Name = "transfer.sh"
	app.Author = ""
	app.Usage = "transfer.sh"
	app.Description = `Easy file sharing from the command line`
	app.Flags = globalFlags
	app.CustomAppHelpTemplate = helpTemplate
	app.Commands = []cli.Command{
		{
			Name:   "version",
			Action: VersionAction,
		},
	}

	app.Before = func(c *cli.Context) error {
		return nil
	}

	app.Action = func(c *cli.Context) {
		options := []server.OptionFn{}
		if v := c.String("listener"); v != "" {
			options = append(options, server.Listener(v))
		}

		if v := c.String("tls-listener"); v != "" {
			options = append(options, server.TLSListener(v))
		}

		if v := c.String("profile-listener"); v != "" {
			options = append(options, server.ProfileListener(v))
		}

		if v := c.String("web-path"); v != "" {
			options = append(options, server.WebPath(v))
		}

		if v := c.String("temp-path"); v != "" {
			options = append(options, server.TempPath(v))
		}

		if v := c.String("lets-encrypt-hosts"); v != "" {
			options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
		}

		if v := c.String("virustotal-key"); v != "" {
			options = append(options, server.VirustotalKey(v))
		}

		if v := c.String("clamav-host"); v != "" {
			options = append(options, server.ClamavHost(v))
		}

		if v := c.Int("rate-limit"); v > 0 {
			options = append(options, server.RateLimit(v))
		}

		if cert := c.String("tls-cert-file"); cert == "" {
		} else if pk := c.String("tls-private-key"); pk == "" {
		} else {
			options = append(options, server.TLSConfig(cert, pk))
		}

		if c.Bool("profiler") {
			options = append(options, server.EnableProfiler())
		}

		if c.Bool("force-https") {
			options = append(options, server.ForceHTTPs())
		}

		switch provider := c.String("provider"); provider {
		case "s3":
			if accessKey := c.String("aws-access-key"); accessKey == "" {
				panic("access-key not set.")
			} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
				panic("secret-key not set.")
			} else if bucket := c.String("bucket"); bucket == "" {
				panic("bucket not set.")
			} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-endpoint")); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "gdrive":
			if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
				panic("client-json-filepath not set.")
			} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
				panic("local-config-path not set.")
			} else if basedir := c.String("basedir"); basedir == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "local":
			if v := c.String("basedir"); v == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewLocalStorage(v); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		default:
			panic("Provider not set or invalid.")
		}

		srvr, err := server.New(
			options...,
		)

		if err != nil {
			fmt.Println(color.RedString("Error starting server: %s", err.Error()))
			return
		}

		srvr.Run()
	}

	return &Cmd{
		App: app,
	}
}
<commit_msg>all: gofmt (#119)<commit_after>package cmd

import (
	"fmt"

	"os"

	"strings"

	"github.com/dutchcoders/transfer.sh/server"
	"github.com/fatih/color"
	"github.com/minio/cli"
)

var Version = "0.1"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
	`{{ "\n"}}`

var globalFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "listener",
		Usage: "127.0.0.1:8080",
		Value: "127.0.0.1:8080",
	},
	// redirect to https?
	// hostnames
	cli.StringFlag{
		Name:  "profile-listener",
		Usage: "127.0.0.1:6060",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "force-https",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-listener",
		Usage: "127.0.0.1:8443",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-cert-file",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-private-key",
		Value: "",
	},
	cli.StringFlag{
		Name:  "temp-path",
		Usage: "path to temp files",
		Value: os.TempDir(),
	},
	cli.StringFlag{
		Name:  "web-path",
		Usage: "path to static web files",
		Value: "",
	},
	cli.StringFlag{
		Name:  "provider",
		Usage: "s3|gdrive|local",
		Value: "",
	},
	cli.StringFlag{
		Name:   "s3-endpoint",
		Usage:  "",
		Value:  "http://s3-eu-west-1.amazonaws.com",
		EnvVar: "S3_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "aws-access-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_ACCESS_KEY",
	},
	cli.StringFlag{
		Name:   "aws-secret-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_SECRET_KEY",
	},
	cli.StringFlag{
		Name:   "bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "BUCKET",
	},
	cli.StringFlag{
		Name:   "gdrive-client-json-filepath",
		Usage:  "",
		Value:  "",
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "gdrive-local-config-path",
		Usage:  "",
		Value:  "",
		EnvVar: "",
	},
	cli.IntFlag{
		Name:   "rate-limit",
		Usage:  "requests per minute",
		Value:  0,
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "lets-encrypt-hosts",
		Usage:  "host1, host2",
		Value:  "",
		EnvVar: "HOSTS",
	},
	cli.StringFlag{
		Name:  "log",
		Usage: "/var/log/transfersh.log",
		Value: "",
	},
	cli.StringFlag{
		Name:  "basedir",
		Usage: "path to storage",
		Value: "",
	},
	cli.StringFlag{
		Name:   "clamav-host",
		Usage:  "clamav-host",
		Value:  "",
		EnvVar: "CLAMAV_HOST",
	},
	cli.StringFlag{
		Name:   "virustotal-key",
		Usage:  "virustotal-key",
		Value:  "",
		EnvVar: "VIRUSTOTAL_KEY",
	},
	cli.BoolFlag{
		Name:  "profiler",
		Usage: "enable profiling",
	},
}

type Cmd struct {
	*cli.App
}

func VersionAction(c *cli.Context) {
	fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
	app := cli.NewApp()
	app.Name = "transfer.sh"
	app.Author = ""
	app.Usage = "transfer.sh"
	app.Description = `Easy file sharing from the command line`
	app.Flags = globalFlags
	app.CustomAppHelpTemplate = helpTemplate
	app.Commands = []cli.Command{
		{
			Name:   "version",
			Action: VersionAction,
		},
	}

	app.Before = func(c *cli.Context) error {
		return nil
	}

	app.Action = func(c *cli.Context) {
		options := []server.OptionFn{}
		if v := c.String("listener"); v != "" {
			options = append(options, server.Listener(v))
		}

		if v := c.String("tls-listener"); v != "" {
			options = append(options, server.TLSListener(v))
		}

		if v := c.String("profile-listener"); v != "" {
			options = append(options, server.ProfileListener(v))
		}

		if v := c.String("web-path"); v != "" {
			options = append(options, server.WebPath(v))
		}

		if v := c.String("temp-path"); v != "" {
			options = append(options, server.TempPath(v))
		}

		if v := c.String("lets-encrypt-hosts"); v != "" {
			options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
		}

		if v := c.String("virustotal-key"); v != "" {
			options = append(options, server.VirustotalKey(v))
		}

		if v := c.String("clamav-host"); v != "" {
			options = append(options, server.ClamavHost(v))
		}

		if v := c.Int("rate-limit"); v > 0 {
			options = append(options, server.RateLimit(v))
		}

		if cert := c.String("tls-cert-file"); cert == "" {
		} else if pk := c.String("tls-private-key"); pk == "" {
		} else {
			options = append(options, server.TLSConfig(cert, pk))
		}

		if c.Bool("profiler") {
			options = append(options, server.EnableProfiler())
		}

		if c.Bool("force-https") {
			options = append(options, server.ForceHTTPs())
		}

		switch provider := c.String("provider"); provider {
		case "s3":
			if accessKey := c.String("aws-access-key"); accessKey == "" {
				panic("access-key not set.")
			} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
				panic("secret-key not set.")
			} else if bucket := c.String("bucket"); bucket == "" {
				panic("bucket not set.")
			} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-endpoint")); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "gdrive":
			if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
				panic("client-json-filepath not set.")
			} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
				panic("local-config-path not set.")
			} else if basedir := c.String("basedir"); basedir == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "local":
			if v := c.String("basedir"); v == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewLocalStorage(v); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		default:
			panic("Provider not set or invalid.")
		}

		srvr, err := server.New(
			options...,
		)

		if err != nil {
			fmt.Println(color.RedString("Error starting server: %s", err.Error()))
			return
		}

		srvr.Run()
	}

	return &Cmd{
		App: app,
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/dutchcoders/transfer.sh/server"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"google.golang.org/api/googleapi"
)

var Version = "1.1.0"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
	`{{ "\n"}}`

var globalFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "listener",
		Usage: "127.0.0.1:8080",
		Value: "127.0.0.1:8080",
	},
	// redirect to https?
	// hostnames
	cli.StringFlag{
		Name:  "profile-listener",
		Usage: "127.0.0.1:6060",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "force-https",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-listener",
		Usage: "127.0.0.1:8443",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "tls-listener-only",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-cert-file",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-private-key",
		Value: "",
	},
	cli.StringFlag{
		Name:  "temp-path",
		Usage: "path to temp files",
		Value: os.TempDir(),
	},
	cli.StringFlag{
		Name:  "web-path",
		Usage: "path to static web files",
		Value: "",
	},
	cli.StringFlag{
		Name:  "proxy-path",
		Usage: "path prefix when service is run behind a proxy",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ga-key",
		Usage: "key for google analytics (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "uservoice-key",
		Usage: "key for user voice (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "provider",
		Usage: "s3|gdrive|local",
		Value: "",
	},
	cli.StringFlag{
		Name:   "s3-endpoint",
		Usage:  "",
		Value:  "",
		EnvVar: "S3_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "s3-region",
		Usage:  "",
		Value:  "eu-west-1",
		EnvVar: "S3_REGION",
	},
	cli.StringFlag{
		Name:   "aws-access-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_ACCESS_KEY",
	},
	cli.StringFlag{
		Name:   "aws-secret-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_SECRET_KEY",
	},
	cli.StringFlag{
		Name:   "bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "BUCKET",
	},
	cli.BoolFlag{
		Name:  "s3-no-multipart",
		Usage: "Disables S3 Multipart Puts",
	},
	cli.BoolFlag{
		Name:  "s3-path-style",
		Usage: "Forces path style URLs, required for Minio.",
	},
	cli.StringFlag{
		Name:  "gdrive-client-json-filepath",
		Usage: "",
		Value: "",
	},
	cli.StringFlag{
		Name:  "gdrive-local-config-path",
		Usage: "",
		Value: "",
	},
	cli.IntFlag{
		Name:  "gdrive-chunk-size",
		Usage: "",
		Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
	},
	cli.IntFlag{
		Name:   "rate-limit",
		Usage:  "requests per minute",
		Value:  0,
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "lets-encrypt-hosts",
		Usage:  "host1, host2",
		Value:  "",
		EnvVar: "HOSTS",
	},
	cli.StringFlag{
		Name:  "log",
		Usage: "/var/log/transfersh.log",
		Value: "",
	},
	cli.StringFlag{
		Name:  "basedir",
		Usage: "path to storage",
		Value: "",
	},
	cli.StringFlag{
		Name:   "clamav-host",
		Usage:  "clamav-host",
		Value:  "",
		EnvVar: "CLAMAV_HOST",
	},
	cli.StringFlag{
		Name:   "virustotal-key",
		Usage:  "virustotal-key",
		Value:  "",
		EnvVar: "VIRUSTOTAL_KEY",
	},
	cli.BoolFlag{
		Name:  "profiler",
		Usage: "enable profiling",
	},
	cli.StringFlag{
		Name:  "http-auth-user",
		Usage: "user for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "http-auth-pass",
		Usage: "pass for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-whitelist",
		Usage: "comma separated list of ips allowed to connect to the service",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-blacklist",
		Usage: "comma separated list of ips not allowed to connect to the service",
		Value: "",
	},
}

type Cmd struct {
	*cli.App
}

func VersionAction(c *cli.Context) {
	fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
	logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)

	app := cli.NewApp()
	app.Name = "transfer.sh"
	app.Author = ""
	app.Usage = "transfer.sh"
	app.Description = `Easy file sharing from the command line`
	app.Version = Version
	app.Flags = globalFlags
	app.CustomAppHelpTemplate = helpTemplate
	app.Commands = []cli.Command{
		{
			Name:   "version",
			Action: VersionAction,
		},
	}

	app.Before = func(c *cli.Context) error {
		return nil
	}

	app.Action = func(c *cli.Context) {
		options := []server.OptionFn{}
		if v := c.String("listener"); v != "" {
			options = append(options, server.Listener(v))
		}

		if v := c.String("tls-listener"); v == "" {
		} else if c.Bool("tls-listener-only") {
			options = append(options, server.TLSListener(v, true))
		} else {
			options = append(options, server.TLSListener(v, false))
		}

		if v := c.String("profile-listener"); v != "" {
			options = append(options, server.ProfileListener(v))
		}

		if v := c.String("web-path"); v != "" {
			options = append(options, server.WebPath(v))
		}

		if v := c.String("proxy-path"); v != "" {
			options = append(options, server.ProxyPath(v))
		}

		if v := c.String("ga-key"); v != "" {
			options = append(options, server.GoogleAnalytics(v))
		}

		if v := c.String("uservoice-key"); v != "" {
			options = append(options, server.UserVoice(v))
		}

		if v := c.String("temp-path"); v != "" {
			options = append(options, server.TempPath(v))
		}

		if v := c.String("log"); v != "" {
			options = append(options, server.LogFile(logger, v))
		} else {
			options = append(options, server.Logger(logger))
		}

		if v := c.String("lets-encrypt-hosts"); v != "" {
			options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
		}

		if v := c.String("virustotal-key"); v != "" {
			options = append(options, server.VirustotalKey(v))
		}

		if v := c.String("clamav-host"); v != "" {
			options = append(options, server.ClamavHost(v))
		}

		if v := c.Int("rate-limit"); v > 0 {
			options = append(options, server.RateLimit(v))
		}

		if cert := c.String("tls-cert-file"); cert == "" {
		} else if pk := c.String("tls-private-key"); pk == "" {
		} else {
			options = append(options, server.TLSConfig(cert, pk))
		}

		if c.Bool("profiler") {
			options = append(options, server.EnableProfiler())
		}

		if c.Bool("force-https") {
			options = append(options, server.ForceHTTPs())
		}

		if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
		} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
		} else {
			options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
		}

		applyIPFilter := false
		ipFilterOptions := server.IPFilterOptions{}
		if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
			applyIPFilter = true
			ipFilterOptions.AllowedIPs = strings.Split(ipWhitelist, ",")
			ipFilterOptions.BlockByDefault = true
		}

		if ipBlacklist := c.String("ip-blacklist"); ipBlacklist != "" {
			applyIPFilter = true
			ipFilterOptions.BlockedIPs = strings.Split(ipBlacklist, ",")
		}

		if applyIPFilter {
			options = append(options, server.FilterOptions(ipFilterOptions))
		}

		switch provider := c.String("provider"); provider {
		case "s3":
			if accessKey := c.String("aws-access-key"); accessKey == "" {
				panic("access-key not set.")
			} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
				panic("secret-key not set.")
			} else if bucket := c.String("bucket"); bucket == "" {
				panic("bucket not set.")
			} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "gdrive":
			chunkSize := c.Int("gdrive-chunk-size")

			if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
				panic("client-json-filepath not set.")
			} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
				panic("local-config-path not set.")
			} else if basedir := c.String("basedir"); basedir == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "local":
			if v := c.String("basedir"); v == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewLocalStorage(v, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		default:
			panic("Provider not set or invalid.")
		}

		srvr, err := server.New(
			options...,
		)

		if err != nil {
			logger.Println(color.RedString("Error starting server: %s", err.Error()))
			return
		}

		srvr.Run()
	}

	return &Cmd{
		App: app,
	}
}
<commit_msg>v1.1.2<commit_after>package cmd

import (
	"fmt"
	"log"
	"os"
	"strings"

	"github.com/dutchcoders/transfer.sh/server"
	"github.com/fatih/color"
	"github.com/urfave/cli"
	"google.golang.org/api/googleapi"
)

var Version = "1.1.2"
var helpTemplate = `NAME:
{{.Name}} - {{.Usage}}

DESCRIPTION:
{{.Description}}

USAGE:
{{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]

COMMANDS:
{{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
{{end}}{{if .Flags}}
FLAGS:
{{range .Flags}}{{.}}
{{end}}{{end}}
VERSION:
` + Version +
	`{{ "\n"}}`

var globalFlags = []cli.Flag{
	cli.StringFlag{
		Name:  "listener",
		Usage: "127.0.0.1:8080",
		Value: "127.0.0.1:8080",
	},
	// redirect to https?
	// hostnames
	cli.StringFlag{
		Name:  "profile-listener",
		Usage: "127.0.0.1:6060",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "force-https",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-listener",
		Usage: "127.0.0.1:8443",
		Value: "",
	},
	cli.BoolFlag{
		Name:  "tls-listener-only",
		Usage: "",
	},
	cli.StringFlag{
		Name:  "tls-cert-file",
		Value: "",
	},
	cli.StringFlag{
		Name:  "tls-private-key",
		Value: "",
	},
	cli.StringFlag{
		Name:  "temp-path",
		Usage: "path to temp files",
		Value: os.TempDir(),
	},
	cli.StringFlag{
		Name:  "web-path",
		Usage: "path to static web files",
		Value: "",
	},
	cli.StringFlag{
		Name:  "proxy-path",
		Usage: "path prefix when service is run behind a proxy",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ga-key",
		Usage: "key for google analytics (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "uservoice-key",
		Usage: "key for user voice (front end)",
		Value: "",
	},
	cli.StringFlag{
		Name:  "provider",
		Usage: "s3|gdrive|local",
		Value: "",
	},
	cli.StringFlag{
		Name:   "s3-endpoint",
		Usage:  "",
		Value:  "",
		EnvVar: "S3_ENDPOINT",
	},
	cli.StringFlag{
		Name:   "s3-region",
		Usage:  "",
		Value:  "eu-west-1",
		EnvVar: "S3_REGION",
	},
	cli.StringFlag{
		Name:   "aws-access-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_ACCESS_KEY",
	},
	cli.StringFlag{
		Name:   "aws-secret-key",
		Usage:  "",
		Value:  "",
		EnvVar: "AWS_SECRET_KEY",
	},
	cli.StringFlag{
		Name:   "bucket",
		Usage:  "",
		Value:  "",
		EnvVar: "BUCKET",
	},
	cli.BoolFlag{
		Name:  "s3-no-multipart",
		Usage: "Disables S3 Multipart Puts",
	},
	cli.BoolFlag{
		Name:  "s3-path-style",
		Usage: "Forces path style URLs, required for Minio.",
	},
	cli.StringFlag{
		Name:  "gdrive-client-json-filepath",
		Usage: "",
		Value: "",
	},
	cli.StringFlag{
		Name:  "gdrive-local-config-path",
		Usage: "",
		Value: "",
	},
	cli.IntFlag{
		Name:  "gdrive-chunk-size",
		Usage: "",
		Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
	},
	cli.IntFlag{
		Name:   "rate-limit",
		Usage:  "requests per minute",
		Value:  0,
		EnvVar: "",
	},
	cli.StringFlag{
		Name:   "lets-encrypt-hosts",
		Usage:  "host1, host2",
		Value:  "",
		EnvVar: "HOSTS",
	},
	cli.StringFlag{
		Name:  "log",
		Usage: "/var/log/transfersh.log",
		Value: "",
	},
	cli.StringFlag{
		Name:  "basedir",
		Usage: "path to storage",
		Value: "",
	},
	cli.StringFlag{
		Name:   "clamav-host",
		Usage:  "clamav-host",
		Value:  "",
		EnvVar: "CLAMAV_HOST",
	},
	cli.StringFlag{
		Name:   "virustotal-key",
		Usage:  "virustotal-key",
		Value:  "",
		EnvVar: "VIRUSTOTAL_KEY",
	},
	cli.BoolFlag{
		Name:  "profiler",
		Usage: "enable profiling",
	},
	cli.StringFlag{
		Name:  "http-auth-user",
		Usage: "user for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "http-auth-pass",
		Usage: "pass for http basic auth",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-whitelist",
		Usage: "comma separated list of ips allowed to connect to the service",
		Value: "",
	},
	cli.StringFlag{
		Name:  "ip-blacklist",
		Usage: "comma separated list of ips not allowed to connect to the service",
		Value: "",
	},
}

type Cmd struct {
	*cli.App
}

func VersionAction(c *cli.Context) {
	fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line")))
}

func New() *Cmd {
	logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)

	app := cli.NewApp()
	app.Name = "transfer.sh"
	app.Author = ""
	app.Usage = "transfer.sh"
	app.Description = `Easy file sharing from the command line`
	app.Version = Version
	app.Flags = globalFlags
	app.CustomAppHelpTemplate = helpTemplate
	app.Commands = []cli.Command{
		{
			Name:   "version",
			Action: VersionAction,
		},
	}

	app.Before = func(c *cli.Context) error {
		return nil
	}

	app.Action = func(c *cli.Context) {
		options := []server.OptionFn{}
		if v := c.String("listener"); v != "" {
			options = append(options, server.Listener(v))
		}

		if v := c.String("tls-listener"); v == "" {
		} else if c.Bool("tls-listener-only") {
			options = append(options, server.TLSListener(v, true))
		} else {
			options = append(options, server.TLSListener(v, false))
		}

		if v := c.String("profile-listener"); v != "" {
			options = append(options, server.ProfileListener(v))
		}

		if v := c.String("web-path"); v != "" {
			options = append(options, server.WebPath(v))
		}

		if v := c.String("proxy-path"); v != "" {
			options = append(options, server.ProxyPath(v))
		}

		if v := c.String("ga-key"); v != "" {
			options = append(options, server.GoogleAnalytics(v))
		}

		if v := c.String("uservoice-key"); v != "" {
			options = append(options, server.UserVoice(v))
		}

		if v := c.String("temp-path"); v != "" {
			options = append(options, server.TempPath(v))
		}

		if v := c.String("log"); v != "" {
			options = append(options, server.LogFile(logger, v))
		} else {
			options = append(options, server.Logger(logger))
		}

		if v := c.String("lets-encrypt-hosts"); v != "" {
			options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
		}

		if v := c.String("virustotal-key"); v != "" {
			options = append(options, server.VirustotalKey(v))
		}

		if v := c.String("clamav-host"); v != "" {
			options = append(options, server.ClamavHost(v))
		}

		if v := c.Int("rate-limit"); v > 0 {
			options = append(options, server.RateLimit(v))
		}

		if cert := c.String("tls-cert-file"); cert == "" {
		} else if pk := c.String("tls-private-key"); pk == "" {
		} else {
			options = append(options, server.TLSConfig(cert, pk))
		}

		if c.Bool("profiler") {
			options = append(options, server.EnableProfiler())
		}

		if c.Bool("force-https") {
			options = append(options, server.ForceHTTPs())
		}

		if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
		} else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
		} else {
			options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
		}

		applyIPFilter := false
		ipFilterOptions := server.IPFilterOptions{}
		if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
			applyIPFilter = true
			ipFilterOptions.AllowedIPs = strings.Split(ipWhitelist, ",")
			ipFilterOptions.BlockByDefault = true
		}

		if ipBlacklist := c.String("ip-blacklist"); ipBlacklist != "" {
			applyIPFilter = true
			ipFilterOptions.BlockedIPs = strings.Split(ipBlacklist, ",")
		}

		if applyIPFilter {
			options = append(options, server.FilterOptions(ipFilterOptions))
		}

		switch provider := c.String("provider"); provider {
		case "s3":
			if accessKey := c.String("aws-access-key"); accessKey == "" {
				panic("access-key not set.")
			} else if secretKey := c.String("aws-secret-key"); secretKey == "" {
				panic("secret-key not set.")
			} else if bucket := c.String("bucket"); bucket == "" {
				panic("bucket not set.")
			} else if storage, err := server.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"), c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "gdrive":
			chunkSize := c.Int("gdrive-chunk-size")

			if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
				panic("client-json-filepath not set.")
			} else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
				panic("local-config-path not set.")
			} else if basedir := c.String("basedir"); basedir == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		case "local":
			if v := c.String("basedir"); v == "" {
				panic("basedir not set.")
			} else if storage, err := server.NewLocalStorage(v, logger); err != nil {
				panic(err)
			} else {
				options = append(options, server.UseStorage(storage))
			}
		default:
			panic("Provider not set or invalid.")
		}

		srvr, err := server.New(
			options...,
		)

		if err != nil {
			logger.Println(color.RedString("Error starting server: %s", err.Error()))
			return
		}

		srvr.Run()
	}

	return &Cmd{
		App: app,
	}
}
<|endoftext|>


<commit_before>package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/anchore/syft/syft/presenter"
	"github.com/anchore/syft/syft/scope"

	"github.com/anchore/stereoscope"
	"github.com/anchore/syft/internal/config"
	"github.com/anchore/syft/internal/format"
	"github.com/anchore/syft/internal/log"
	"github.com/anchore/syft/internal/logger"
	"github.com/anchore/syft/syft"
	"github.com/spf13/viper"
	"github.com/wagoodman/go-partybus"
	"gopkg.in/yaml.v2"
)

var appConfig *config.Application
var eventBus *partybus.Bus
var eventSubscription *partybus.Subscription
var cliOpts = config.CliOnlyOptions{}

func init() {
	setGlobalCliOptions()

	cobra.OnInitialize(
		initAppConfig,
		initLogging,
		logAppConfig,
		initEventBus,
	)
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		log.Errorf(err.Error())
		os.Exit(1)
	}
}

func setGlobalCliOptions() {
	rootCmd.PersistentFlags().StringVarP(&cliOpts.ConfigPath, "config", "c", "", "application config file")

	// scan options
	flag := "scope"
	rootCmd.Flags().StringP(
		"scope", "s", scope.SquashedScope.String(),
		fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options))
	if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
		fmt.Printf("unable to bind flag '%s': %+v", flag, err)
		os.Exit(1)
	}

	// output & formatting options
	flag = "output"
	rootCmd.Flags().StringP(
		flag, "o", presenter.TablePresenter.String(),
		fmt.Sprintf("report output formatter, options=%v", presenter.Options),
	)
	if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
		fmt.Printf("unable to bind flag '%s': %+v", flag, err)
		os.Exit(1)
	}

	flag = "quiet"
	rootCmd.Flags().BoolP(
		flag, "q", false,
		"suppress all logging output",
	)
	if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
		fmt.Printf("unable to bind flag '%s': %+v", flag, err)
		os.Exit(1)
	}

	rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
}

func initAppConfig() {
	cfg, err := config.LoadConfigFromFile(viper.GetViper(), &cliOpts)
	if err != nil {
		fmt.Printf("failed to load application config: \n\t%+v\n", err)
		os.Exit(1)
	}
	appConfig = cfg
}

func initLogging() {
	cfg := logger.LogrusConfig{
		EnableConsole: (appConfig.Log.FileLocation == "" || appConfig.CliOptions.Verbosity > 0) && !appConfig.Quiet,
		EnableFile:    appConfig.Log.FileLocation != "",
		Level:         appConfig.Log.LevelOpt,
		Structured:    appConfig.Log.Structured,
		FileLocation:  appConfig.Log.FileLocation,
	}

	logWrapper := logger.NewLogrusLogger(cfg)
	syft.SetLogger(logWrapper)
	stereoscope.SetLogger(&logger.LogrusNestedLogger{
		Logger: logWrapper.Logger.WithField("from-lib", "steroscope"),
	})
}

func logAppConfig() {
	appCfgStr, err := yaml.Marshal(&appConfig)

	if err != nil {
		log.Debugf("Could not display application config: %+v", err)
	} else {
		log.Debugf("Application config:\n%+v", format.Magenta.Format(string(appCfgStr)))
	}
}

func initEventBus() {
	eventBus = partybus.NewBus()
	eventSubscription = eventBus.Subscribe()

	stereoscope.SetBus(eventBus)
	syft.SetBus(eventBus)
}
<commit_msg>fix panic on top-level log (#125)<commit_after>package cmd

import (
	"fmt"
	"os"

	"github.com/spf13/cobra"

	"github.com/anchore/syft/syft/presenter"
	"github.com/anchore/syft/syft/scope"

	"github.com/anchore/stereoscope"
	"github.com/anchore/syft/internal/config"
	"github.com/anchore/syft/internal/format"
	"github.com/anchore/syft/internal/log"
	"github.com/anchore/syft/internal/logger"
	"github.com/anchore/syft/syft"
	"github.com/spf13/viper"
	"github.com/wagoodman/go-partybus"
	"gopkg.in/yaml.v2"
)

var appConfig *config.Application
var eventBus *partybus.Bus
var eventSubscription *partybus.Subscription
var cliOpts = config.CliOnlyOptions{}

func init() {
	setGlobalCliOptions()

	cobra.OnInitialize(
		initAppConfig,
		initLogging,
		logAppConfig,
		initEventBus,
	)
}

func Execute() {
	if err := rootCmd.Execute(); err != nil {
		fmt.Fprintln(os.Stderr, err.Error())
		os.Exit(1)
	}
}

func setGlobalCliOptions() {
	rootCmd.PersistentFlags().StringVarP(&cliOpts.ConfigPath, "config", "c", "", "application config file")

	// scan options
	flag := "scope"
	rootCmd.Flags().StringP(
		"scope", "s", scope.SquashedScope.String(),
		fmt.Sprintf("selection of layers to catalog, options=%v", scope.Options))
	if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
		fmt.Printf("unable to bind flag '%s': %+v", flag, err)
		os.Exit(1)
	}

	// output & formatting options
	flag = "output"
	rootCmd.Flags().StringP(
		flag, "o", presenter.TablePresenter.String(),
		fmt.Sprintf("report output formatter, options=%v", presenter.Options),
	)
	if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
		fmt.Printf("unable to bind flag '%s': %+v", flag, err)
		os.Exit(1)
	}

	flag = "quiet"
	rootCmd.Flags().BoolP(
		flag, "q", false,
		"suppress all logging output",
	)
	if err := viper.BindPFlag(flag, rootCmd.Flags().Lookup(flag)); err != nil {
		fmt.Printf("unable to bind flag '%s': %+v", flag, err)
		os.Exit(1)
	}

	rootCmd.Flags().CountVarP(&cliOpts.Verbosity, "verbose", "v", "increase verbosity (-v = info, -vv = debug)")
}

func initAppConfig() {
	cfg, err := config.LoadConfigFromFile(viper.GetViper(), &cliOpts)
	if err != nil {
		fmt.Printf("failed to load application config: \n\t%+v\n", err)
		os.Exit(1)
	}
	appConfig = cfg
}

func initLogging() {
	cfg := logger.LogrusConfig{
		EnableConsole: (appConfig.Log.FileLocation == "" || appConfig.CliOptions.Verbosity > 0) && !appConfig.Quiet,
		EnableFile:    appConfig.Log.FileLocation != "",
		Level:         appConfig.Log.LevelOpt,
		Structured:    appConfig.Log.Structured,
		FileLocation:  appConfig.Log.FileLocation,
	}

	logWrapper := logger.NewLogrusLogger(cfg)
	syft.SetLogger(logWrapper)
	stereoscope.SetLogger(&logger.LogrusNestedLogger{
		Logger: logWrapper.Logger.WithField("from-lib", "steroscope"),
	})
}

func logAppConfig() {
	appCfgStr, err := yaml.Marshal(&appConfig)

	if err != nil {
		log.Debugf("Could not display application config: %+v", err)
	} else {
		log.Debugf("Application config:\n%+v", format.Magenta.Format(string(appCfgStr)))
	}
}

func initEventBus() {
	eventBus = partybus.NewBus()
	eventSubscription = eventBus.Subscribe()

	stereoscope.SetBus(eventBus)
	syft.SetBus(eventBus)
}
<|endoftext|>