<commit_before>package impl

import (
	"errors"
	"fmt"
	"time"

	"os"

	"github.com/ellcrys/util"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres" // gorm requires it
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/types"
	logging "github.com/op/go-logging"
)

var log = logging.MustGetLogger("postgres.store")

// ErrChainExist represents an error about an existing chain
var ErrChainExist = errors.New("chain already exists")

// LedgerTableName represents the name of the table where all
// known ledger info are stored.
const LedgerTableName = "ledgers"

// TransactionTableName represents the name of the table where all
// transactions are stored.
const TransactionTableName = "transactions"

// PostgresStore defines a store implementation
// on the postgres database. It implements the Store interface
type PostgresStore struct {
	db         *gorm.DB
	blockchain types.Blockchain
	locker     types.Lock
}

// SetBlockchainImplementation sets sets a reference of the blockchain implementation
func (s *PostgresStore) SetBlockchainImplementation(b types.Blockchain) {
	s.blockchain = b
}

// GetImplementationName returns the name of this store implementation
func (s *PostgresStore) GetImplementationName() string {
	return "postgres.store"
}

// Connect connects to a postgres server and returns a client
// or error if connection failed.
func (s *PostgresStore) Connect(dbAddr string) (interface{}, error) {

	var err error
	s.db, err = gorm.Open("postgres", dbAddr)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to store backend. %s", err)
	}

	s.db.LogMode(false)

	return s.db, nil
}

// MakeLegderHash takes a ledger and computes a hash
func (s *PostgresStore) MakeLegderHash(ledger *types.Ledger) string {
	return util.Sha256(fmt.Sprintf("%s;%t;%d", ledger.Name, ledger.Public, ledger.CreatedAt))
}

// Init initializes the types. Creates the necessary tables such as the
// the tables and public and private system ledgers
func (s *PostgresStore) Init(systemPublicLedgerName, systemPrivateLedgerName string) error {

	// create ledger table if not existing
	if !s.db.HasTable(LedgerTableName) {
		if err := s.db.CreateTable(&types.Ledger{}).Error; err != nil {
			return fmt.Errorf("failed to create `%s` table. %s", LedgerTableName, err)
		}
	}

	// create transaction table if not existing
	if !s.db.HasTable(TransactionTableName) {
		if err := s.db.CreateTable(&types.Transaction{}).
			AddIndex("idx_name_ledger_key_created_at", "ledger", "key", "created_at").Error; err != nil {
			return fmt.Errorf("failed to create `%s` table. %s", TransactionTableName, err)
		}
	}

	// create system ledgers
	var systemLedgers = [][]interface{}{
		[]interface{}{systemPublicLedgerName, true},   // public
		[]interface{}{systemPrivateLedgerName, false}, // private
	}
	for _, ledger := range systemLedgers {
		var c int
		if err := s.db.Model(&types.Ledger{}).Where("name = ?", ledger[0].(string)).Count(&c).Error; err != nil {
			return fmt.Errorf("failed to check existence of ledger named %s: %s", ledger[0].(string), err)
		}
		if c == 0 {
			_, err := s.CreateLedger(ledger[0].(string), true, ledger[1].(bool))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// Destroy removes the database tables.
// Will only work in a test environment (Test Only!!!)
func Destroy(dbAddr string) error {

	if os.Getenv("APP_ENV") != "test" {
		return fmt.Errorf("Cowardly refusing to do it! Can only call Destroy() in test environment")
	}

	db, err := gorm.Open("postgres", dbAddr)
	if err != nil {
		return fmt.Errorf("failed to connect to store backend")
	}

	return db.DropTable(types.Ledger{}, types.Transaction{}).Error
}

// Clear the database tables.
// Will only work in a test environment (Test Only!!!)
func Clear(dbAddr string) error {

	if os.Getenv("APP_ENV") != "test" {
		return fmt.Errorf("Cowardly refusing to do it! Can only call Destroy() in test environment")
	}

	db, err := gorm.Open("postgres", dbAddr)
	if err != nil {
		return fmt.Errorf("failed to connect to store backend")
	}

	err = db.Delete(types.Transaction{}).Error
	if err != nil {
		return err
	}

	err = db.Delete(types.Ledger{}).Error
	if err != nil {
		return err
	}

	return nil
}

// CreateLedgerThen creates a new ledger and accepts an additional operation (via the thenFunc) to be
// executed before the ledger creation transaction is committed. If the thenFunc returns an error, the
// transaction is rolled back and error returned
func (s *PostgresStore) CreateLedgerThen(name string, chained, public bool, thenFunc func() error) (*types.Ledger, error) {

	tx := s.db.Begin()

	err := tx.Exec(`SET TRANSACTION isolation level repeatable read`).Error
	if err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("failed to set transaction isolation level. %s", err)
	}

	newLedger := &types.Ledger{
		Name:      name,
		Public:    public,
		Chained:   chained,
		CreatedAt: time.Now().Unix(),
	}

	newLedger.Hash = s.MakeLegderHash(newLedger)

	if err := tx.Create(newLedger).Error; err != nil {
		tx.Rollback()
		if common.IsUniqueConstraintError(err, "name") {
			return nil, fmt.Errorf("ledger with matching name already exists")
		} else if common.IsUniqueConstraintError(err, "hash") {
			return nil, fmt.Errorf("hash is being used by another ledger")
		}
		return nil, err
	}

	// create chain
	if chained {
		_, err := s.blockchain.CreateChain(name, public)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	// run the companion functions and Rollback
	// the transaction if error was returned
	if thenFunc != nil {
		if err = thenFunc(); err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	tx.Commit()

	return newLedger, nil
}

// CreateLedger creates a new ledger.
func (s *PostgresStore) CreateLedger(name string, chained, public bool) (*types.Ledger, error) {
	return s.CreateLedgerThen(name, chained, public, nil)
}

// GetLedger fetches a ledger meta information
func (s *PostgresStore) GetLedger(name string) (*types.Ledger, error) {

	var l types.Ledger

	err := s.db.Where("name = ?", name).Last(&l).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, fmt.Errorf("failed to get ledger. %s", err)
	} else if err == gorm.ErrRecordNotFound {
		return nil, nil
	}

	return &l, nil
}

// makeTxLockKey constructs a lock key using a transaction key and ledger name
func makeTxLockKey(ledgerName, key string) string {
	return fmt.Sprintf("tx;key;%s;%s", ledgerName, key)
}

// PutThen adds transactions to the store and returns a list of transaction receipts.
// Any transaction that failed to be created will result in an error receipt being created
// and returned along with success receipts of they successfully added transactions.
// However, all transactions will be rolled back if the `thenFunc` returns error. Only the
// transaction that are successfully added will be passed to the thenFunc.
// Future work may allow the caller to determine the behaviour via an additional parameter.
func (s *PostgresStore) PutThen(ledgerName string, txs []*types.Transaction, thenFunc func(validTxss []*types.Transaction) error) ([]*types.TxReceipt, error) {

	var validTxs []*types.Transaction
	txReceipts := []*types.TxReceipt{}

	dbTx := s.db.Begin()
	err := dbTx.Exec(`SET TRANSACTION isolation level repeatable read`).Error
	if err != nil {
		dbTx.Rollback()
		return nil, fmt.Errorf("failed to set transaction isolation level. %s", err)
	}

	// create transactions and add transaction receipts for
	// successfully stored transactions
	for _, tx := range txs {

		var err error

		// acquire lock on the transaction via its key
		lock := common.NewLock(makeTxLockKey(tx.LedgerInternal, tx.Key))
		if err = lock.Acquire(); err != nil {
			if err == types.ErrLockAlreadyAcquired {
				txReceipts = append(txReceipts, &types.TxReceipt{
					ID:  tx.ID,
					Err: "failed to acquire lock. object has been locked by another process",
				})
			} else {
				txReceipts = append(txReceipts, &types.TxReceipt{
					ID:  tx.ID,
					Err: err.Error(),
				})
			}
			continue
		}

		// ensure the current transaction is not a stale transaction
		// when checked with the valid transactions. If this is not done, before we call the tx.Create
		// the entire transaction will not be committed by the postgres driver
		isStale := false
		for _, vTx := range validTxs {
			if vTx.KeyInternal == tx.KeyInternal && vTx.RevisionTo == tx.RevisionTo {
				isStale = true
				break
			}
		}
		if isStale {
			txReceipts = append(txReceipts, &types.TxReceipt{
				ID:  tx.ID,
				Err: "stale object",
			})
			lock.Release()
			continue
		}

		tx.Hash = tx.MakeHash()
		tx.Ledger = ledgerName
		txReceipt := &types.TxReceipt{ID: tx.ID}
		err = dbTx.Create(tx).Error
		if err != nil {
			txReceipt.Err = err.Error()
			if common.CompareErr(err, fmt.Errorf(`pq: duplicate key value violates unique constraint "idx_name_revision_to"`)) == 0 {
				txReceipt.Err = "stale object"
			}
		} else {
			validTxs = append(validTxs, tx)
		}

		if err = lock.Release(); err != nil {
			fmt.Println("Error releasing lock: ", err)
		}

		txReceipts = append(txReceipts, txReceipt)
	}

	// run the companion functions. Rollback
	// the transactions only if error was returned
	if thenFunc != nil {
		if err = thenFunc(validTxs); err != nil {
			dbTx.Rollback()
			return txReceipts, err
		}
	}

	if err := dbTx.Commit().Error; err != nil {
		return nil, err
	}

	return txReceipts, nil
}

// Put creates one or more transactions associated to a ledger.
// Returns a list of transaction receipts and a general error.
func (s *PostgresStore) Put(ledgerName string, txs []*types.Transaction) ([]*types.TxReceipt, error) {
	return s.PutThen(ledgerName, txs, nil)
}

// Get fetches a transaction by its ledger and key
func (s *PostgresStore) Get(ledger, key string) (*types.Transaction, error) {
	var tx types.Transaction

	// acquire lock on the transaction via its key
	lock := common.NewLock(makeTxLockKey(ledger, key))
	if err := lock.Acquire(); err != nil {
		if err == types.ErrLockAlreadyAcquired {
			return nil, fmt.Errorf("failed to acquire lock. object has been locked by another process")
		}
		return nil, err
	}

	defer lock.Release()

	err := s.db.Where("ledger = ? AND key = ?", ledger, key).Last(&tx).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, fmt.Errorf("failed to get transaction. %s", err)
	} else if err == gorm.ErrRecordNotFound {
		return nil, nil
	}

	return &tx, nil
}

// GetRange fetches transactions with keys included in a specified range.
// No lock is acquired in this operation.
func (s *PostgresStore) GetRange(ledger, startKey, endKey string, inclusive bool, limit, offset int) ([]*types.Transaction, error) {

	var err error
	var txs []*types.Transaction
	var q *gorm.DB

	if len(startKey) > 0 && len(endKey) > 0 {
		if !inclusive {
			q = s.db.Where("ledger = ? AND (key >= ? AND key < ?)", ledger, startKey, endKey)
		} else {
			q = s.db.Where("ledger = ? AND (key >= ? OR key <= ?)", ledger, startKey+"%", endKey+"%")
		}
	} else if len(startKey) > 0 && len(endKey) == 0 {
		q = s.db.Where("ledger = ? AND key like ?", ledger, startKey+"%")
	} else {
		// setting endKey only is a little tricky as the call code may construct
		// through a secondary process or rule, so add the '%' operator will most likely
		// result in wrong query. So we just let the external decide where to put it.
		q = s.db.Where("ledger = ? AND key like ?", ledger, endKey)
	}

	err = q.
		Limit(limit).
		Offset(offset).
		Select("DISTINCT ON (key) *").
		Order("key").
		Order("created_at desc").
		Find(&txs).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, fmt.Errorf("failed to get transactions. %s", err)
	} else if err == gorm.ErrRecordNotFound {
		return nil, nil
	}

	return txs, nil
}

// Close releases any resource held
func (s *PostgresStore) Close() error {
	if s.db != nil {
		return s.db.Close()
	}
	return nil
}
<commit_msg>Only perform  stale checks on transactions with a `revisionTo` value set<commit_after>package impl

import (
	"errors"
	"fmt"
	"time"

	"os"

	"github.com/ellcrys/util"
	"github.com/jinzhu/gorm"
	_ "github.com/jinzhu/gorm/dialects/postgres" // gorm requires it
	"github.com/ncodes/cocoon/core/common"
	"github.com/ncodes/cocoon/core/types"
	logging "github.com/op/go-logging"
)

var log = logging.MustGetLogger("postgres.store")

// ErrChainExist represents an error about an existing chain
var ErrChainExist = errors.New("chain already exists")

// LedgerTableName represents the name of the table where all
// known ledger info are stored.
const LedgerTableName = "ledgers"

// TransactionTableName represents the name of the table where all
// transactions are stored.
const TransactionTableName = "transactions"

// PostgresStore defines a store implementation
// on the postgres database. It implements the Store interface
type PostgresStore struct {
	db         *gorm.DB
	blockchain types.Blockchain
	locker     types.Lock
}

// SetBlockchainImplementation sets sets a reference of the blockchain implementation
func (s *PostgresStore) SetBlockchainImplementation(b types.Blockchain) {
	s.blockchain = b
}

// GetImplementationName returns the name of this store implementation
func (s *PostgresStore) GetImplementationName() string {
	return "postgres.store"
}

// Connect connects to a postgres server and returns a client
// or error if connection failed.
func (s *PostgresStore) Connect(dbAddr string) (interface{}, error) {

	var err error
	s.db, err = gorm.Open("postgres", dbAddr)
	if err != nil {
		return nil, fmt.Errorf("failed to connect to store backend. %s", err)
	}

	s.db.LogMode(false)

	return s.db, nil
}

// MakeLegderHash takes a ledger and computes a hash
func (s *PostgresStore) MakeLegderHash(ledger *types.Ledger) string {
	return util.Sha256(fmt.Sprintf("%s;%t;%d", ledger.Name, ledger.Public, ledger.CreatedAt))
}

// Init initializes the types. Creates the necessary tables such as the
// the tables and public and private system ledgers
func (s *PostgresStore) Init(systemPublicLedgerName, systemPrivateLedgerName string) error {

	// create ledger table if not existing
	if !s.db.HasTable(LedgerTableName) {
		if err := s.db.CreateTable(&types.Ledger{}).Error; err != nil {
			return fmt.Errorf("failed to create `%s` table. %s", LedgerTableName, err)
		}
	}

	// create transaction table if not existing
	if !s.db.HasTable(TransactionTableName) {
		if err := s.db.CreateTable(&types.Transaction{}).
			AddIndex("idx_name_ledger_key_created_at", "ledger", "key", "created_at").Error; err != nil {
			return fmt.Errorf("failed to create `%s` table. %s", TransactionTableName, err)
		}
	}

	// create system ledgers
	var systemLedgers = [][]interface{}{
		[]interface{}{systemPublicLedgerName, true},   // public
		[]interface{}{systemPrivateLedgerName, false}, // private
	}
	for _, ledger := range systemLedgers {
		var c int
		if err := s.db.Model(&types.Ledger{}).Where("name = ?", ledger[0].(string)).Count(&c).Error; err != nil {
			return fmt.Errorf("failed to check existence of ledger named %s: %s", ledger[0].(string), err)
		}
		if c == 0 {
			_, err := s.CreateLedger(ledger[0].(string), true, ledger[1].(bool))
			if err != nil {
				return err
			}
		}
	}

	return nil
}

// Destroy removes the database tables.
// Will only work in a test environment (Test Only!!!)
func Destroy(dbAddr string) error {

	if os.Getenv("APP_ENV") != "test" {
		return fmt.Errorf("Cowardly refusing to do it! Can only call Destroy() in test environment")
	}

	db, err := gorm.Open("postgres", dbAddr)
	if err != nil {
		return fmt.Errorf("failed to connect to store backend")
	}

	return db.DropTable(types.Ledger{}, types.Transaction{}).Error
}

// Clear the database tables.
// Will only work in a test environment (Test Only!!!)
func Clear(dbAddr string) error {

	if os.Getenv("APP_ENV") != "test" {
		return fmt.Errorf("Cowardly refusing to do it! Can only call Destroy() in test environment")
	}

	db, err := gorm.Open("postgres", dbAddr)
	if err != nil {
		return fmt.Errorf("failed to connect to store backend")
	}

	err = db.Delete(types.Transaction{}).Error
	if err != nil {
		return err
	}

	err = db.Delete(types.Ledger{}).Error
	if err != nil {
		return err
	}

	return nil
}

// CreateLedgerThen creates a new ledger and accepts an additional operation (via the thenFunc) to be
// executed before the ledger creation transaction is committed. If the thenFunc returns an error, the
// transaction is rolled back and error returned
func (s *PostgresStore) CreateLedgerThen(name string, chained, public bool, thenFunc func() error) (*types.Ledger, error) {

	tx := s.db.Begin()

	err := tx.Exec(`SET TRANSACTION isolation level repeatable read`).Error
	if err != nil {
		tx.Rollback()
		return nil, fmt.Errorf("failed to set transaction isolation level. %s", err)
	}

	newLedger := &types.Ledger{
		Name:      name,
		Public:    public,
		Chained:   chained,
		CreatedAt: time.Now().Unix(),
	}

	newLedger.Hash = s.MakeLegderHash(newLedger)

	if err := tx.Create(newLedger).Error; err != nil {
		tx.Rollback()
		if common.IsUniqueConstraintError(err, "name") {
			return nil, fmt.Errorf("ledger with matching name already exists")
		} else if common.IsUniqueConstraintError(err, "hash") {
			return nil, fmt.Errorf("hash is being used by another ledger")
		}
		return nil, err
	}

	// create chain
	if chained {
		_, err := s.blockchain.CreateChain(name, public)
		if err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	// run the companion functions and Rollback
	// the transaction if error was returned
	if thenFunc != nil {
		if err = thenFunc(); err != nil {
			tx.Rollback()
			return nil, err
		}
	}

	tx.Commit()

	return newLedger, nil
}

// CreateLedger creates a new ledger.
func (s *PostgresStore) CreateLedger(name string, chained, public bool) (*types.Ledger, error) {
	return s.CreateLedgerThen(name, chained, public, nil)
}

// GetLedger fetches a ledger meta information
func (s *PostgresStore) GetLedger(name string) (*types.Ledger, error) {

	var l types.Ledger

	err := s.db.Where("name = ?", name).Last(&l).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, fmt.Errorf("failed to get ledger. %s", err)
	} else if err == gorm.ErrRecordNotFound {
		return nil, nil
	}

	return &l, nil
}

// makeTxLockKey constructs a lock key using a transaction key and ledger name
func makeTxLockKey(ledgerName, key string) string {
	return fmt.Sprintf("tx;key;%s;%s", ledgerName, key)
}

// PutThen adds transactions to the store and returns a list of transaction receipts.
// Any transaction that failed to be created will result in an error receipt being created
// and returned along with success receipts of they successfully added transactions.
// However, all transactions will be rolled back if the `thenFunc` returns error. Only the
// transaction that are successfully added will be passed to the thenFunc.
// Future work may allow the caller to determine the behaviour via an additional parameter.
func (s *PostgresStore) PutThen(ledgerName string, txs []*types.Transaction, thenFunc func(validTxss []*types.Transaction) error) ([]*types.TxReceipt, error) {

	var validTxs []*types.Transaction
	txReceipts := []*types.TxReceipt{}

	dbTx := s.db.Begin()
	err := dbTx.Exec(`SET TRANSACTION isolation level repeatable read`).Error
	if err != nil {
		dbTx.Rollback()
		return nil, fmt.Errorf("failed to set transaction isolation level. %s", err)
	}

	// create transactions and add transaction receipts for
	// successfully stored transactions
	for _, tx := range txs {

		var err error

		// acquire lock on the transaction via its key
		lock := common.NewLock(makeTxLockKey(tx.LedgerInternal, tx.Key))
		if err = lock.Acquire(); err != nil {
			if err == types.ErrLockAlreadyAcquired {
				txReceipts = append(txReceipts, &types.TxReceipt{
					ID:  tx.ID,
					Err: "failed to acquire lock. object has been locked by another process",
				})
			} else {
				txReceipts = append(txReceipts, &types.TxReceipt{
					ID:  tx.ID,
					Err: err.Error(),
				})
			}
			continue
		}

		// ensure the current transaction is not a stale transaction
		// when checked with the valid transactions. If this is not done, before we call the tx.Create
		// the entire transaction will not be committed by the postgres driver
		isStale := false
		for _, vTx := range validTxs {
			if len(tx.RevisionTo) > 0 && tx.RevisionTo == vTx.RevisionTo && tx.KeyInternal == vTx.KeyInternal {
				isStale = true
				break
			}
		}
		if isStale {
			txReceipts = append(txReceipts, &types.TxReceipt{
				ID:  tx.ID,
				Err: "stale object",
			})
			lock.Release()
			continue
		}

		tx.Hash = tx.MakeHash()
		tx.Ledger = ledgerName
		txReceipt := &types.TxReceipt{ID: tx.ID}
		err = dbTx.Create(tx).Error
		if err != nil {
			txReceipt.Err = err.Error()
			if common.CompareErr(err, fmt.Errorf(`pq: duplicate key value violates unique constraint "idx_name_revision_to"`)) == 0 {
				txReceipt.Err = "stale object"
			}
		} else {
			validTxs = append(validTxs, tx)
		}

		if err = lock.Release(); err != nil {
			fmt.Println("Error releasing lock: ", err)
		}

		txReceipts = append(txReceipts, txReceipt)
	}

	// run the companion functions. Rollback
	// the transactions only if error was returned
	if thenFunc != nil {
		if err = thenFunc(validTxs); err != nil {
			dbTx.Rollback()
			return txReceipts, err
		}
	}

	if err := dbTx.Commit().Error; err != nil {
		return nil, err
	}

	return txReceipts, nil
}

// Put creates one or more transactions associated to a ledger.
// Returns a list of transaction receipts and a general error.
func (s *PostgresStore) Put(ledgerName string, txs []*types.Transaction) ([]*types.TxReceipt, error) {
	return s.PutThen(ledgerName, txs, nil)
}

// Get fetches a transaction by its ledger and key
func (s *PostgresStore) Get(ledger, key string) (*types.Transaction, error) {
	var tx types.Transaction

	// acquire lock on the transaction via its key
	lock := common.NewLock(makeTxLockKey(ledger, key))
	if err := lock.Acquire(); err != nil {
		if err == types.ErrLockAlreadyAcquired {
			return nil, fmt.Errorf("failed to acquire lock. object has been locked by another process")
		}
		return nil, err
	}

	defer lock.Release()

	err := s.db.Where("ledger = ? AND key = ?", ledger, key).Last(&tx).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, fmt.Errorf("failed to get transaction. %s", err)
	} else if err == gorm.ErrRecordNotFound {
		return nil, nil
	}

	return &tx, nil
}

// GetRange fetches transactions with keys included in a specified range.
// No lock is acquired in this operation.
func (s *PostgresStore) GetRange(ledger, startKey, endKey string, inclusive bool, limit, offset int) ([]*types.Transaction, error) {

	var err error
	var txs []*types.Transaction
	var q *gorm.DB

	if len(startKey) > 0 && len(endKey) > 0 {
		if !inclusive {
			q = s.db.Where("ledger = ? AND (key >= ? AND key < ?)", ledger, startKey, endKey)
		} else {
			q = s.db.Where("ledger = ? AND (key >= ? OR key <= ?)", ledger, startKey+"%", endKey+"%")
		}
	} else if len(startKey) > 0 && len(endKey) == 0 {
		q = s.db.Where("ledger = ? AND key like ?", ledger, startKey+"%")
	} else {
		// setting endKey only is a little tricky as the call code may construct
		// through a secondary process or rule, so add the '%' operator will most likely
		// result in wrong query. So we just let the external decide where to put it.
		q = s.db.Where("ledger = ? AND key like ?", ledger, endKey)
	}

	err = q.
		Limit(limit).
		Offset(offset).
		Select("DISTINCT ON (key) *").
		Order("key").
		Order("created_at desc").
		Find(&txs).Error
	if err != nil && err != gorm.ErrRecordNotFound {
		return nil, fmt.Errorf("failed to get transactions. %s", err)
	} else if err == gorm.ErrRecordNotFound {
		return nil, nil
	}

	return txs, nil
}

// Close releases any resource held
func (s *PostgresStore) Close() error {
	if s.db != nil {
		return s.db.Close()
	}
	return nil
}
<|endoftext|>


<commit_before>package kubeapi

/*
 Copyright 2017 - 2021 Crunchy Data Solutions, 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.
*/

import (
"flag"
"path/filepath"
"os"
log "github.com/sirupsen/logrus"
"k8s.io/client-go/kubernetes"
"k8s.io/client-go/tools/clientcmd"
"k8s.io/client-go/rest"
)

func GetClientConfig (oocFlag bool, namespaceFlag string)(* kubernetes.Clientset,  string, error) {

	var kubeconfig *string
	var config *rest.Config
	var err error

	namespace := getNamespace(oocFlag, namespaceFlag) // this may call os.Exit(non-zero)

	if !oocFlag {
		config, err = rest.InClusterConfig()

		if err != nil {
			log.Error(err.Error())
			log.Info("If running outside of container, use [ -r | --remote ] flag")
			os.Exit(-1)
		}

	} else if home := homeDir(); home != "" {
		kubeconfig = flag.String("kubeconfig", filepath.Join(home, ".kube", "config"), "")
		// use the current context in kubeconfig
		config, err = clientcmd.BuildConfigFromFlags("", *kubeconfig)

		if err != nil {
				panic(err.Error())
		}

	} else {
		panic("Unable to obtain a cluster configuration. Exiting.")
	}
	flag.Parse()

	// create the clientset
	clientset, err := kubernetes.NewForConfig(config)

	if err != nil {
		panic(err.Error())
	}

	return clientset, namespace, err
}

func homeDir() string {
	if h := os.Getenv("HOME"); h != "" {
		return h
	}
	return os.Getenv("USERPROFILE") // windows
}

func getNamespace(outOfContainer bool, namespaceFlag string) string {

	if namespaceFlag != "" {
		return namespaceFlag
	}

	if ns := os.Getenv("CCP_NAMESPACE"); ns != "" || outOfContainer {
		return ns
	}

	log.Error("CCP_NAMESPACE must be set.")
	// if namespace not set, exit
	os.Exit(-1)

	return "" // make compiler happy - never executed
}

<commit_msg>Remove orphaned Go code<commit_after><|endoftext|>


<commit_before>package open_falcon

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/qiniu/log"
	"github.com/qiniu/pandora-go-sdk/base/reqerr"

	"github.com/qiniu/logkit/conf"
	"github.com/qiniu/logkit/sender"
	. "github.com/qiniu/logkit/sender/config"
	. "github.com/qiniu/logkit/utils/models"
	utilsos "github.com/qiniu/logkit/utils/os"
)

type TransferSender struct {
	host       string
	url        string
	path       string
	client     *http.Client
	step       int
	tags       string
	extraInfo  map[string]string
	runnerName string
}

type TransferData struct {
	Metric      string  `json:"metric"`
	EndPoint    string  `json:"endpoint"`
	Tags        string  `json:"tags"`
	Value       float64 `json:"value"`
	Step        int     `json:"step"`        // 采集频率，秒
	CounterType string  `json:"counterType"` // 默认使用 GAUGE，原值
	TimeStamp   int64   `json:"timestamp"`   // 秒
}

type RespData struct {
	Message string `json:"message"`
	Total   int    `json:"total"`
	Invalid int    `json:"invalid"`
	Latency int    `json:"latency"`
}

type TransferResponse struct {
	Msg  string   `json:"msg"`
	Data RespData `json:"data"`
}

const (
	CounterTypeGauge   string = "GAUGE"
	CounterTypeCounter string = "COUNTER"
	Success            string = "success"
)

func init() {
	sender.RegisterConstructor(TypeOpenFalconTransfer, NewSender)
}

func NewSender(c conf.MapConf) (sender.Sender, error) {
	transferHost, err := c.GetString(KeyOpenFalconTransferHost)
	if err != nil {
		return nil, err
	}
	transferHost = strings.TrimSuffix(transferHost, "/")
	transferUrl, err := c.GetStringOr(KeyOpenFalconTransferURL, "/api/push")
	if err != nil {
		return nil, err
	}
	transferUrl = strings.TrimPrefix(transferUrl, "/")
	timeout, _ := c.GetStringOr(KeyHttpTimeout, "30s")
	step, err := c.GetInt(KeyCollectInterval)
	if err != nil {
		return nil, err
	}
	tags, err := c.GetStringOr(KeyTags, "")
	if err != nil {
		return nil, err
	}
	dur, err := time.ParseDuration(timeout)
	if err != nil {
		return nil, errors.New("timeout configure " + timeout + " is invalid")
	}
	name, _ := c.GetStringOr(KeyName, "")
	transferSender := &TransferSender{
		host:       transferHost,
		url:        transferUrl,
		path:       transferHost + "/" + transferUrl,
		step:       step,
		tags:       tags,
		extraInfo:  utilsos.GetExtraInfo(),
		client:     &http.Client{Timeout: dur},
		runnerName: name,
	}
	return transferSender, nil
}

func (ts *TransferSender) Name() string {
	return "open_falcon_transfer_" + ts.path + "_"
}

func (ts *TransferSender) Send(datas []Data) error {
	transferDatas := make([]TransferData, 0)
	var ok bool
	var vmap map[string]interface{}
	ste := &StatsError{
		StatsInfo: StatsInfo{
			Success: 0,
			Errors:  int64(len(datas)),
		},
	}
	timeStamp := time.Now().Unix()
	for _, d := range datas {
		for k, v := range d {
			log.Infof("k = %s, v = %+v", k, v)
			if vmap, ok = v.(map[string]interface{}); ok {
				for ik, iv := range vmap {
					if tmpData, success := ts.converToTransferData(ik, iv, timeStamp); success {
						transferDatas = append(transferDatas, tmpData)
						continue
					}
					log.Warnf("Runner[%v] Sender[%v] key(%s)'s value is %v, (%s) not float64", ts.runnerName, ts.Name(), ik, iv, reflect.TypeOf(iv))
				}
				continue
			}
			if tmpData, success := ts.converToTransferData(k, v, timeStamp); success {
				transferDatas = append(transferDatas, tmpData)
				continue
			}
			log.Warnf("Runner[%v] Sender[%v] key(%s)'s value is %v, (%s) not float64", ts.runnerName, ts.Name(), k, v, reflect.TypeOf(v))
		}
	}
	if len(transferDatas) == 0 {
		log.Warnf("Runner[%v] Sender[%v] send no data", ts.runnerName, ts.Name())
		ste.LastError = "no valid data to send"
		ste.SendError = reqerr.NewSendError("no valid data to send", sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	byteData, err := json.Marshal(transferDatas)
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] marshal transferDatas %+v failed: %v", ts.runnerName, ts.Name(), transferDatas, err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	req, err := http.NewRequest(http.MethodPost, ts.path, bytes.NewReader(byteData))
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] construct request failed, %v", ts.runnerName, ts.Name(), err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return err
	}
	resp, err := ts.client.Do(req)
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] post datas error %v\n", ts.runnerName, ts.Name(), err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	defer resp.Body.Close()
	var respBody []byte
	if resp.StatusCode != http.StatusOK {
		respBody, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Errorf("Runner[%v] Sender[%v] read response body error %v\n", ts.runnerName, ts.Name(), err)
			ste.LastError = err.Error()
			ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
			return ste
		}
		log.Errorf("Runner[%v] Sender[%v] response code is %v, response body is %v\n", ts.runnerName, ts.Name(), resp.StatusCode, string(respBody))
		ste.LastError = string(respBody)
		ste.SendError = reqerr.NewSendError(string(respBody), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	respBody, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] read response body error %v\n", ts.runnerName, ts.Name(), err)
		ste.LastError = string(respBody)
		ste.SendError = reqerr.NewSendError(string(respBody), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return err
	}
	if string(respBody) == Success {
		log.Infof("sender to open-falcon success: len(transferDatas) = %d", len(transferDatas))
		return nil
	}
	var respData TransferResponse
	if err = json.Unmarshal(respBody, &respData); err != nil {
		log.Errorf("Runner[%v] Sender[%v] unmarshal response body (%s) error %v\n", ts.runnerName, ts.Name(), string(respBody), err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	if respData.Msg != Success || respData.Data.Invalid != 0 {
		log.Warnf("Runner[%v] Sender[%v] send to transfer failed %v\n", ts.runnerName, ts.Name(), respData)
		if respData.Msg == Success {
			ste.Errors = int64(respData.Data.Invalid)
			ste.Success = int64(respData.Data.Total) - ste.Errors
		}
		ste.SendError = reqerr.NewSendError(string(respBody), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	log.Infof("sender to open-falcon success: %+v", respData)
	return nil
}

func (ts *TransferSender) Close() (err error) {
	return nil
}

func (ts *TransferSender) converToTransferData(key string, value interface{}, timeStamp int64) (TransferData, bool) {
	var valuef float64
	var valuei int
	var valuei64 int64
	var values string
	var valuej json.Number
	var ok bool
	var err error
	result := TransferData{
		Metric:      key,
		EndPoint:    ts.extraInfo[KeyHostName],
		Tags:        ts.tags,
		Step:        ts.step,
		CounterType: CounterTypeGauge,
		TimeStamp:   timeStamp,
	}
	if valuef, ok = value.(float64); ok {
		result.Value = valuef
		return result, true
	}
	if valuei, ok = value.(int); ok {
		result.Value = float64(valuei)
		return result, true
	}
	if valuei64, ok = value.(int64); ok {
		result.Value = float64(valuei64)
		return result, true
	}
	if valuej, ok = value.(json.Number); ok {
		if valuef, err = valuej.Float64(); err == nil {
			result.Value = valuef
			return result, true
		}
	}
	if values, ok = value.(string); ok {
		if valuef, err := strconv.ParseFloat(values, 64); err == nil {
			result.Value = valuef
			return result, true
		}
	}
	return TransferData{}, false
}
<commit_msg>retry ci<commit_after>package open_falcon

import (
	"bytes"
	"encoding/json"
	"errors"
	"io/ioutil"
	"net/http"
	"reflect"
	"strconv"
	"strings"
	"time"

	"github.com/qiniu/log"
	"github.com/qiniu/pandora-go-sdk/base/reqerr"

	"github.com/qiniu/logkit/conf"
	"github.com/qiniu/logkit/sender"
	. "github.com/qiniu/logkit/sender/config"
	. "github.com/qiniu/logkit/utils/models"
	utilsos "github.com/qiniu/logkit/utils/os"
)

type TransferSender struct {
	host       string
	url        string
	path       string
	client     *http.Client
	step       int
	tags       string
	extraInfo  map[string]string
	runnerName string
}

type TransferData struct {
	Metric      string  `json:"metric"`
	EndPoint    string  `json:"endpoint"`
	Tags        string  `json:"tags"`
	Value       float64 `json:"value"`
	Step        int     `json:"step"`        // 采集频率，秒
	CounterType string  `json:"counterType"` // 默认使用 GAUGE，原值
	TimeStamp   int64   `json:"timestamp"`   // 秒
}

type RespData struct {
	Message string `json:"message"`
	Total   int    `json:"total"`
	Invalid int    `json:"invalid"`
	Latency int    `json:"latency"`
}

type TransferResponse struct {
	Msg  string   `json:"msg"`
	Data RespData `json:"data"`
}

const (
	CounterTypeGauge   string = "GAUGE"
	CounterTypeCounter string = "COUNTER"
	Success            string = "success"
)

func init() {
	sender.RegisterConstructor(TypeOpenFalconTransfer, NewSender)
}

func NewSender(c conf.MapConf) (sender.Sender, error) {
	transferHost, err := c.GetString(KeyOpenFalconTransferHost)
	if err != nil {
		return nil, err
	}
	transferHost = strings.TrimSuffix(transferHost, "/")
	transferUrl, err := c.GetStringOr(KeyOpenFalconTransferURL, "/api/push")
	if err != nil {
		return nil, err
	}
	transferUrl = strings.TrimPrefix(transferUrl, "/")
	timeout, _ := c.GetStringOr(KeyHttpTimeout, "30s")
	step, err := c.GetInt(KeyCollectInterval)
	if err != nil {
		return nil, err
	}
	tags, err := c.GetStringOr(KeyTags, "")
	if err != nil {
		return nil, err
	}
	dur, err := time.ParseDuration(timeout)
	if err != nil {
		return nil, errors.New("timeout configure " + timeout + " is invalid")
	}
	name, _ := c.GetStringOr(KeyName, "")
	transferSender := &TransferSender{
		host:       transferHost,
		url:        transferUrl,
		path:       transferHost + "/" + transferUrl,
		step:       step,
		tags:       tags,
		extraInfo:  utilsos.GetExtraInfo(),
		client:     &http.Client{Timeout: dur},
		runnerName: name,
	}
	return transferSender, nil
}

func (ts *TransferSender) Name() string {
	return "open_falcon_transfer_" + ts.path + "_"
}

func (ts *TransferSender) Send(datas []Data) error {
	transferDatas := make([]TransferData, 0)
	var ok bool
	var vmap map[string]interface{}
	ste := &StatsError{
		StatsInfo: StatsInfo{
			Success: 0,
			Errors:  int64(len(datas)),
		},
	}
	timeStamp := time.Now().Unix()
	for _, d := range datas {
		for k, v := range d {
			log.Debugf("k = %s, v = %+v", k, v)
			if vmap, ok = v.(map[string]interface{}); ok {
				for ik, iv := range vmap {
					if tmpData, success := ts.converToTransferData(ik, iv, timeStamp); success {
						transferDatas = append(transferDatas, tmpData)
						continue
					}
					log.Warnf("Runner[%v] Sender[%v] key(%s)'s value is %v, (%s) not float64", ts.runnerName, ts.Name(), ik, iv, reflect.TypeOf(iv))
				}
				continue
			}
			if tmpData, success := ts.converToTransferData(k, v, timeStamp); success {
				transferDatas = append(transferDatas, tmpData)
				continue
			}
			log.Warnf("Runner[%v] Sender[%v] key(%s)'s value is %v, (%s) not float64", ts.runnerName, ts.Name(), k, v, reflect.TypeOf(v))
		}
	}
	if len(transferDatas) == 0 {
		log.Warnf("Runner[%v] Sender[%v] send no data", ts.runnerName, ts.Name())
		ste.LastError = "no valid data to send"
		ste.SendError = reqerr.NewSendError("no valid data to send", sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	byteData, err := json.Marshal(transferDatas)
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] marshal transferDatas %+v failed: %v", ts.runnerName, ts.Name(), transferDatas, err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	req, err := http.NewRequest(http.MethodPost, ts.path, bytes.NewReader(byteData))
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] construct request failed, %v", ts.runnerName, ts.Name(), err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return err
	}
	resp, err := ts.client.Do(req)
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] post datas error %v\n", ts.runnerName, ts.Name(), err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	defer resp.Body.Close()
	var respBody []byte
	if resp.StatusCode != http.StatusOK {
		respBody, err = ioutil.ReadAll(resp.Body)
		if err != nil {
			log.Errorf("Runner[%v] Sender[%v] read response body error %v\n", ts.runnerName, ts.Name(), err)
			ste.LastError = err.Error()
			ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
			return ste
		}
		log.Errorf("Runner[%v] Sender[%v] response code is %v, response body is %v\n", ts.runnerName, ts.Name(), resp.StatusCode, string(respBody))
		ste.LastError = string(respBody)
		ste.SendError = reqerr.NewSendError(string(respBody), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	respBody, err = ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Runner[%v] Sender[%v] read response body error %v\n", ts.runnerName, ts.Name(), err)
		ste.LastError = string(respBody)
		ste.SendError = reqerr.NewSendError(string(respBody), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return err
	}
	if string(respBody) == Success {
		log.Infof("sender to open-falcon success: len(transferDatas) = %d", len(transferDatas))
		return nil
	}
	var respData TransferResponse
	if err = json.Unmarshal(respBody, &respData); err != nil {
		log.Errorf("Runner[%v] Sender[%v] unmarshal response body (%s) error %v\n", ts.runnerName, ts.Name(), string(respBody), err)
		ste.LastError = err.Error()
		ste.SendError = reqerr.NewSendError(err.Error(), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	if respData.Msg != Success || respData.Data.Invalid != 0 {
		log.Warnf("Runner[%v] Sender[%v] send to transfer failed %v\n", ts.runnerName, ts.Name(), respData)
		if respData.Msg == Success {
			ste.Errors = int64(respData.Data.Invalid)
			ste.Success = int64(respData.Data.Total) - ste.Errors
		}
		ste.SendError = reqerr.NewSendError(string(respBody), sender.ConvertDatasBack(datas), reqerr.TypeDefault)
		return ste
	}
	log.Infof("sender to open-falcon success: %+v", respData)
	return nil
}

func (ts *TransferSender) Close() (err error) {
	return nil
}

func (ts *TransferSender) converToTransferData(key string, value interface{}, timeStamp int64) (TransferData, bool) {
	var valuef float64
	var valuei int
	var valuei64 int64
	var values string
	var valuej json.Number
	var ok bool
	var err error
	result := TransferData{
		Metric:      key,
		EndPoint:    ts.extraInfo[KeyHostName],
		Tags:        ts.tags,
		Step:        ts.step,
		CounterType: CounterTypeGauge,
		TimeStamp:   timeStamp,
	}
	if valuef, ok = value.(float64); ok {
		result.Value = valuef
		return result, true
	}
	if valuei, ok = value.(int); ok {
		result.Value = float64(valuei)
		return result, true
	}
	if valuei64, ok = value.(int64); ok {
		result.Value = float64(valuei64)
		return result, true
	}
	if valuej, ok = value.(json.Number); ok {
		if valuef, err = valuej.Float64(); err == nil {
			result.Value = valuef
			return result, true
		}
	}
	if values, ok = value.(string); ok {
		if valuef, err := strconv.ParseFloat(values, 64); err == nil {
			result.Value = valuef
			return result, true
		}
	}
	return TransferData{}, false
}
<|endoftext|>


<commit_before>package monitor

import "fmt"

// Target is a URL, which has to be polled for availability.
type Target struct {
	// Unique identifier of this target. Targets' IDs cannot intercept. Target's
	// ID must be constant between GetTargets() calls.
	ID uint
	// User-supplied target title, used purely for display.
	Title string
	// The HTTP URL to poll.
	URL string
}

func (t Target) String() string {
	return fmt.Sprintf("Target %v { %q, %q }", t.ID, t.Title, t.URL)
}

// TargetStatus simply connects target and its status in one structure.
type TargetStatus struct {
	Target Target
	Status Status
}

func (ts TargetStatus) String() string {
	return fmt.Sprintf("%v : %v", ts.Target, ts.Status)
}

// TargetsGetter is an interface of targets source. Monitor uses it to retrieve
// list targets on every polling iteration. External frontend may implement
// this interface to store targets in a DB or in a configuration file.
type TargetsGetter interface {
	GetTargets() ([]Target, error)
}
<commit_msg>:hammer: Add a basic slice implementation of TargetsGetter<commit_after>package monitor

import "fmt"

// Target is a URL, which has to be polled for availability.
type Target struct {
	// Unique identifier of this target. Targets' IDs cannot intercept. Target's
	// ID must be constant between GetTargets() calls.
	ID uint
	// User-supplied target title, used purely for display.
	Title string
	// The HTTP URL to poll.
	URL string
}

func (t Target) String() string {
	return fmt.Sprintf("Target %v { %q, %q }", t.ID, t.Title, t.URL)
}

// TargetStatus simply connects target and its status in one structure.
type TargetStatus struct {
	Target Target
	Status Status
}

func (ts TargetStatus) String() string {
	return fmt.Sprintf("%v : %v", ts.Target, ts.Status)
}

// TargetsGetter is an interface of targets source. Monitor uses it to retrieve
// list targets on every polling iteration. External frontend may implement
// this interface to store targets in a DB or in a configuration file.
type TargetsGetter interface {
	GetTargets() ([]Target, error)
}

// TargetsSlice - the most basic implementation on TargetsGetter.
type TargetsSlice []Target

// GetTargets implements TargetsGetter for TargetsSlice.
func (ts TargetsSlice) GetTargets() ([]Target, error) {
	return ts, nil
}

// NewTargetsSliceFromUrls constructs TargetsSlice from a list of urls.
// Each target is given an ID equal to the url's index, and the title of format
// "Target N".
func NewTargetsSliceFromUrls(urls []string) TargetsSlice {
	ts := TargetsSlice{}
	for i, url := range urls {
		ts = append(ts, Target{
			ID:    uint(i),
			Title: fmt.Sprintf("Target %v", i),
			URL:   url,
		})
	}
	return ts
}
<|endoftext|>


<commit_before>package snakes

import (
	"math/big"
	"testing"
)

func check(t *testing.T, a, b, c int) {
	bigA := big.NewInt(int64(a))
	bigB := big.NewInt(int64(b))
	bigC := big.NewInt(int64(c))
	z := new(big.Int).Exp(bigA, bigB, bigC)
	got := MontgomeryLadderExp(bigA, bigB, bigC)
	if got.Cmp(z) != 0 {
		t.Errorf("pow(%d, %d, %d) Expected: %s, got %s", a, b, c, z.String(), got.String())
	}
}

func TestMontgomeryLadderModExp(t *testing.T) {
	check(t, 0, 0, 1)
	check(t, 0, 1, 1)
	check(t, 1, 0, 1)
	check(t, 2, 10, 1)
	check(t, 4, 13, 1)
	check(t, 13, 4, 1)
	check(t, 2, 10, 15)
	check(t, 3, 10, 15)
	check(t, 13, 4, 7)
	check(t, 19, 65, 3)
	check(t, 1432, 432, 123)
}
<commit_msg>A failing test<commit_after>package snakes

import (
	"math/big"
	"testing"
)

func check(t *testing.T, a, b, c int) {
	bigA := big.NewInt(int64(a))
	bigB := big.NewInt(int64(b))
	bigC := big.NewInt(int64(c))
	z := new(big.Int).Exp(bigA, bigB, bigC)
	got := MontgomeryLadderExp(bigA, bigB, bigC)
	if got.Cmp(z) != 0 {
		t.Errorf("pow(%d, %d, %d) Expected: %s, got %s", a, b, c, z.String(), got.String())
	}
}

func TestMontgomeryLadderModExp(t *testing.T) {
	check(t, 0, 0, 1)
	check(t, 0, 1, 1)
	check(t, 0, 2, 1)
	check(t, 1, 0, 1)
	check(t, 2, 0, 1)
	check(t, 2, 10, 1)
	check(t, 4, 13, 1)
	check(t, 13, 4, 1)
	check(t, 2, 10, 15)
	check(t, 3, 10, 15)
	check(t, 13, 4, 7)
	check(t, 19, 65, 3)
	check(t, 1432, 432, 123)
}
<|endoftext|>


<commit_before>/*
   Copyright 2013 Juliano Martinez <juliano@martinez.io>

   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.

   @author: Juliano Martinez
*/

package main

import (
	"fmt"
	docopt "github.com/docopt/docopt.go"
	"github.com/fiorix/go-redis/redis"
	hpr_utils "github.com/ncode/hot-potato-router/utils"
	"strconv"
	"strings"
)

var (
	cfg = hpr_utils.NewConfig()
	rc  = redis.New(cfg.Options["redis"]["server_list"])
)

func main() {
	usage := `Hot Potato Router Control

Usage:
  hprctl add <vhost> <backend_ip:port> [--weight=<n>]
  hprctl del <vhost> <backend_ip:port> [--weight=<n>]
  hprctl show <vhost>
  hprctl list

Args:
  add           add a new vhost and backend
  dell          del a vhost and a backend
  show          show all backends from a given vhost
  list          list all vhosts

Options:
  -h --help     Show this screen.
  --version     Show version.
  --weight=<n>  Weight in wrr [default: 1].
`

	arguments, _ := docopt.Parse(usage, nil, true, "Hot Potato Router 0.3.0", false)
	if arguments["add"] == true {
		_, err := rc.ZAdd(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), arguments["--weight"], arguments["<backend_ip:port>"])
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		return
	}

	if arguments["del"] == true {
		_, err := rc.ZRem(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), arguments["--weight"], arguments["<backend_ip:port>"])
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		return
	}

	if arguments["show"] == true {
		bes, err := rc.ZRange(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), 0, -1, true)
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		fmt.Printf(":: vhost [ %s ]\n", arguments["<vhost>"])
		var url string
		for _, be := range bes {
			count, err := strconv.Atoi(be)
			if err != nil {
				url = be
				continue
			}
			fmt.Printf("-- backend %s weight=%d\n", url, count)
		}
		return
	}

	if arguments["list"] == true {
		keys, err := rc.Keys("hpr-backends::*")
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		for _, k := range keys {
			fmt.Printf(":: vhost [ %s ]\n", strings.TrimLeft(k, "hpr-backends::"))
		}
		return
	}
}
<commit_msg>removing wrr for now<commit_after>/*
   Copyright 2013 Juliano Martinez <juliano@martinez.io>

   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.

   @author: Juliano Martinez
*/

package main

import (
	"fmt"
	docopt "github.com/docopt/docopt.go"
	"github.com/fiorix/go-redis/redis"
	hpr_utils "github.com/ncode/hot-potato-router/utils"
	"strconv"
	"strings"
)

var (
	cfg = hpr_utils.NewConfig()
	rc  = redis.New(cfg.Options["redis"]["server_list"])
)

func main() {
	usage := `Hot Potato Router Control

Usage:
  hprctl add <vhost> <backend_ip:port> [--weight=<n>]
  hprctl del <vhost> <backend_ip:port> [--weight=<n>]
  hprctl show <vhost>
  hprctl list

Args:
  add           add a new vhost and backend
  dell          del a vhost and a backend
  show          show all backends from a given vhost
  list          list all vhosts

Options:
  -h --help     Show this screen.
  --version     Show version.`

	/*  --weight=<n>  Weight in wrr [default: 1].
	`*/

	arguments, _ := docopt.Parse(usage, nil, true, "Hot Potato Router 0.3.0", false)
	if arguments["add"] == true {
		// _, err := rc.ZAdd(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), arguments["--weight"], arguments["<backend_ip:port>"])
		_, err := rc.ZAdd(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), 1, arguments["<backend_ip:port>"])
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		return
	}

	if arguments["del"] == true {
		// _, err := rc.ZRem(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), arguments["--weight"], arguments["<backend_ip:port>"])
		_, err := rc.ZRem(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), 1, arguments["<backend_ip:port>"])
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		return
	}

	if arguments["show"] == true {
		bes, err := rc.ZRange(fmt.Sprintf("hpr-backends::%s", arguments["<vhost>"]), 0, -1, true)
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		fmt.Printf(":: vhost [ %s ]\n", arguments["<vhost>"])
		var url string
		for _, be := range bes {
			count, err := strconv.Atoi(be)
			if err != nil {
				url = be
				continue
			}
			fmt.Printf("-- backend %s weight=%d\n", url, count)
		}
		return
	}

	if arguments["list"] == true {
		keys, err := rc.Keys("hpr-backends::*")
		hpr_utils.CheckPanic(err, "Unable to write on hpr database")
		for _, k := range keys {
			fmt.Printf(":: vhost [ %s ]\n", strings.TrimLeft(k, "hpr-backends::"))
		}
		return
	}
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	wikiclient "github.com/cooper/go-wikiclient"
	"log"
	"net/http"
	"regexp"
	"time"
)

var imageRegex = regexp.MustCompile("")

// master handler
func handleRoot(w http.ResponseWriter, r *http.Request) {
	var delayedWiki *wikiInfo

	// try each wiki
	for _, wiki := range wikis {

		// wrong host
		if wiki.host != r.Host {
			log.Printf("wiki '%s' host mismatch: %s != %s\n", wiki.name, wiki.host, r.Host)
			// if the wiki host is empty, it is the fallback wiki.
			// delay it until we've checked all other wikis.
			if wiki.host == "" && delayedWiki == nil {
				log.Printf("wiki '%s' host is empty; delaying\n", wiki.name)
				delayedWiki = &wiki
			}

			continue
		}

		// host matches
		log.Printf("wiki '%s' host matched; trying it... ", wiki.name)
		if handleMainPage(wiki, w, r) {
			log.Printf("yep\n")
			return
		}
		log.Printf("nope\n")
	}

	// try the delayed wiki
	if delayedWiki != nil {
		log.Printf("wiki '%s' was delayed; trying it... ", delayedWiki.name)
		if handleMainPage(*delayedWiki, w, r) {
			log.Printf("yep\n")

			return
		}
		log.Printf("nope\n")
	}

	// anything else is a 404
	log.Println("404")
	http.NotFound(w, r)
}

func handleMainPage(wiki wikiInfo, w http.ResponseWriter, r *http.Request) bool {

	// wrong root
	wikiRoot := wiki.conf.Get("root.wiki")
	if r.URL.Path != wikiRoot && r.URL.Path != wikiRoot+"/" {
		return false
	}

	// no main page configured
	mainPage := wiki.conf.Get("main_page")
	if mainPage == "" {
		return false
	}

	wiki.client = wikiclient.NewClient(tr, wiki.defaultSess, 3*time.Second)
	handlePage(wiki, mainPage, w, r)
	return true
}

// page request
func handlePage(wiki wikiInfo, relPath string, w http.ResponseWriter, r *http.Request) {
	res, err := wiki.client.DisplayPage(relPath)
	if handleError(wiki, err, w, r) || handleError(wiki, res, w, r) {
		return
	}
	renderTemplate(wiki, w, "page", wikiPage{
		Res:        res,
		Title:      res.Get("title"),
		WikiTitle:  wiki.title,
		WikiLogo:   wiki.template.logo,
		WikiRoot:   wiki.conf.Get("root.wiki"),
		StaticRoot: wiki.template.staticRoot,
		navigation: wiki.conf.GetSlice("navigation"),
	})
}

// image request
func handleImage(wiki wikiInfo, relPath string, w http.ResponseWriter, r *http.Request) {
	res, err := wiki.client.DisplayImage(relPath, 0, 0)
	if handleError(wiki, err, w, r) || handleError(wiki, res, w, r) {
		return
	}
	http.ServeFile(w, r, res.Get("path"))
}

// this is set true when calling handlePage for the error page. this way, if an
// error occurs when trying to display the error page, we don't infinitely loop
// between handleError and handlePage
var useLowLevelError bool

func handleError(wiki wikiInfo, errMaybe interface{}, w http.ResponseWriter, r *http.Request) bool {
	msg := "An unknown error has occurred"
	switch err := errMaybe.(type) {

	// if there's no error, stop
	case nil:
		return false

		// message of type "not found" is an error; otherwise, stop
	case wikiclient.Message:
		if err.Get("type") != "not found" {
			return false
		}
		msg = err.Get("error")

		// string
	case string:
		msg = err

		// error
	case error:
		msg = err.Error()

	}

	// if we have an error page, use it
	errorPage := wiki.conf.Get("error_page")
	if !useLowLevelError && errorPage != "" {
		useLowLevelError = true
		handlePage(wiki, errorPage, w, r)
		useLowLevelError = false
		return true
	}

	// generic error response
	http.Error(w, msg, http.StatusNotFound)
	return true
}

func renderTemplate(wiki wikiInfo, w http.ResponseWriter, templateName string, p wikiPage) {
	err := wiki.template.template.ExecuteTemplate(w, templateName+".tpl", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<commit_msg>don't delay wikis with wrong root<commit_after>// Copyright (c) 2017, Mitchell Cooper
package main

import (
	wikiclient "github.com/cooper/go-wikiclient"
	"log"
	"net/http"
	"regexp"
	"time"
)

var imageRegex = regexp.MustCompile("")

// master handler
func handleRoot(w http.ResponseWriter, r *http.Request) {
	var delayedWiki *wikiInfo

	// try each wiki
	for _, wiki := range wikis {

		// wrong root
		wikiRoot := wiki.conf.Get("root.wiki")
		if r.URL.Path != wikiRoot && r.URL.Path != wikiRoot+"/" {
			continue
		}

		// wrong host
		if wiki.host != r.Host {
			log.Printf("wiki '%s' host mismatch: %s != %s\n", wiki.name, wiki.host, r.Host)
			// if the wiki host is empty, it is the fallback wiki.
			// delay it until we've checked all other wikis.
			if wiki.host == "" && delayedWiki == nil {
				log.Printf("wiki '%s' host is empty; delaying\n", wiki.name)
				delayedWiki = &wiki
			}

			continue
		}

		// host matches
		log.Printf("wiki '%s' host matched; trying it... ", wiki.name)
		if handleMainPage(wiki, w, r) {
			log.Printf("yep\n")
			return
		}
		log.Printf("nope\n")
	}

	// try the delayed wiki
	if delayedWiki != nil {
		log.Printf("wiki '%s' was delayed; trying it... ", delayedWiki.name)
		if handleMainPage(*delayedWiki, w, r) {
			log.Printf("yep\n")

			return
		}
		log.Printf("nope\n")
	}

	// anything else is a 404
	log.Println("404")
	http.NotFound(w, r)
}

func handleMainPage(wiki wikiInfo, w http.ResponseWriter, r *http.Request) bool {

	// no main page configured
	mainPage := wiki.conf.Get("main_page")
	if mainPage == "" {
		return false
	}

	wiki.client = wikiclient.NewClient(tr, wiki.defaultSess, 3*time.Second)
	handlePage(wiki, mainPage, w, r)
	return true
}

// page request
func handlePage(wiki wikiInfo, relPath string, w http.ResponseWriter, r *http.Request) {
	res, err := wiki.client.DisplayPage(relPath)
	if handleError(wiki, err, w, r) || handleError(wiki, res, w, r) {
		return
	}
	renderTemplate(wiki, w, "page", wikiPage{
		Res:        res,
		Title:      res.Get("title"),
		WikiTitle:  wiki.title,
		WikiLogo:   wiki.template.logo,
		WikiRoot:   wiki.conf.Get("root.wiki"),
		StaticRoot: wiki.template.staticRoot,
		navigation: wiki.conf.GetSlice("navigation"),
	})
}

// image request
func handleImage(wiki wikiInfo, relPath string, w http.ResponseWriter, r *http.Request) {
	res, err := wiki.client.DisplayImage(relPath, 0, 0)
	if handleError(wiki, err, w, r) || handleError(wiki, res, w, r) {
		return
	}
	http.ServeFile(w, r, res.Get("path"))
}

// this is set true when calling handlePage for the error page. this way, if an
// error occurs when trying to display the error page, we don't infinitely loop
// between handleError and handlePage
var useLowLevelError bool

func handleError(wiki wikiInfo, errMaybe interface{}, w http.ResponseWriter, r *http.Request) bool {
	msg := "An unknown error has occurred"
	switch err := errMaybe.(type) {

	// if there's no error, stop
	case nil:
		return false

		// message of type "not found" is an error; otherwise, stop
	case wikiclient.Message:
		if err.Get("type") != "not found" {
			return false
		}
		msg = err.Get("error")

		// string
	case string:
		msg = err

		// error
	case error:
		msg = err.Error()

	}

	// if we have an error page, use it
	errorPage := wiki.conf.Get("error_page")
	if !useLowLevelError && errorPage != "" {
		useLowLevelError = true
		handlePage(wiki, errorPage, w, r)
		useLowLevelError = false
		return true
	}

	// generic error response
	http.Error(w, msg, http.StatusNotFound)
	return true
}

func renderTemplate(wiki wikiInfo, w http.ResponseWriter, templateName string, p wikiPage) {
	err := wiki.template.template.ExecuteTemplate(w, templateName+".tpl", p)
	if err != nil {
		http.Error(w, err.Error(), http.StatusInternalServerError)
	}
}
<|endoftext|>


<commit_before>package main

import (
    "github.com/miekg/dns"
    "strings"
    "log"
    "flag"
    "net"
    "time"
    "runtime"
    "sync"
)

var wg sync.WaitGroup

var protoFlag = flag.String("p","","Specify protocol to use for DoS")
var destPort = flag.Int("P",53,"Specify destination port for DoS")
var destIPs = flag.String("d","","Specify an single host or a list of hosts seperated by comma (example: 1.2.3.4 or 1.2.3.4,2.3.4.5)")
var rateFlag = flag.Int("r",1,"Specify the amount of protocol requests per second")
var durationFlag = flag.Int("D",60,"Specify the total duration of the test")

func dnsQuery(config *dns.ClientConfig, queryCounter int) {
    defer wg.Done()
    dnsClient := new(dns.Client)
    message := new(dns.Msg)
    message.SetQuestion(dns.Fqdn("cnn.com"), dns.TypeA)
    message.RecursionDesired = true

    response, _, err := dnsClient.Exchange(message, net.JoinHostPort(config.Servers[0], "53"))

    if err != nil {
        //log.Println(err)
    }

    if response == nil {
    } else {
        if response.Rcode != dns.RcodeSuccess {
            log.Println(" query fail")
        }
        //log.Println(rtt)
        queryCounter = queryCounter + 1
    }
}

func main() {
    runtime.GOMAXPROCS(0)
    var runCounter int
    runCounter = 0
    var queryCounter int
    queryCounter = 0

    flag.Parse()
    log.Printf("Starting DoS against %s on Port %d via protocol %s at rate %d/s", *destIPs, *destPort, *protoFlag, *rateFlag)
    config := new(dns.ClientConfig)
    config.Servers = strings.Split(*destIPs,":")
    config.Port = string(*destPort)
    config.Ndots = 1
    config.Timeout = 1
    config.Attempts = 1
    ticker := time.NewTicker(time.Second * 1)

    for t := range ticker.C {
        runCounter = runCounter + 1
        log.Println(t)
        for i := 0; i < *rateFlag; i++ {
            wg.Add(1)
            go dnsQuery(config, queryCounter)
        }
        if runCounter == *durationFlag {
            wg.Wait()
            log.Printf("Completed %d queries over %d runs to %s",queryCounter,runCounter ,*destIPs)
            break
        }
    }

}
<commit_msg>tuned performance<commit_after>package main

import (
    "github.com/miekg/dns"
    "strings"
    "log"
    "flag"
    "net"
    "time"
    "runtime"
    "sync"
)

var wg sync.WaitGroup

var protoFlag = flag.String("p","","Specify protocol to use for DoS")
var destPort = flag.Int("P",53,"Specify destination port for DoS")
var destIPs = flag.String("d","","Specify an single host or a list of hosts seperated by comma (example: 1.2.3.4 or 1.2.3.4,2.3.4.5)")
var rateFlag = flag.Int("r",1,"Specify the amount of protocol requests per second")
var durationFlag = flag.Int("D",60,"Specify the total duration of the test")

func dnsQuery(config *dns.ClientConfig, queryCounter int) {
    defer wg.Done()
    dnsClient := new(dns.Client)
    message := new(dns.Msg)
    message.SetQuestion(dns.Fqdn("cnn.com"), dns.TypeA)
    message.RecursionDesired = true

    response, _, err := dnsClient.Exchange(message, net.JoinHostPort(config.Servers[0], "53"))

    if err != nil {
        //log.Println(err)
    }

    if response == nil {
    } else {
        if response.Rcode != dns.RcodeSuccess {
            log.Println(" query fail")
        }
        //log.Println(rtt)
        queryCounter = queryCounter + 1
    }
}

func main() {
    runtime.GOMAXPROCS(0)
    var runCounter int
    runCounter = 0
    var queryCounter int
    queryCounter = 0

    flag.Parse()
    log.Printf("Starting DoS against %s on Port %d via protocol %s at rate %d/s", *destIPs, *destPort, *protoFlag, *rateFlag)
    config := new(dns.ClientConfig)
    config.Servers = strings.Split(*destIPs,":")
    config.Port = string(*destPort)
    config.Ndots = 1
    config.Timeout = 1
    config.Attempts = 1
    ticker := time.NewTicker(time.Second * 1)

    for t := range ticker.C {
        runCounter = runCounter + 1
        for i := 0; i < *rateFlag; i++ {
            wg.Add(1)
            go dnsQuery(config, queryCounter)
        }
        if runCounter == *durationFlag {
            wg.Wait()
            log.Printf("Completed %d queries over %d runs to %s",queryCounter,runCounter ,*destIPs)
            break
        }
    }

}
<|endoftext|>


<commit_before>// Copyright 2020 Google LLC.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package idtoken

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

type cachingClient struct {
	client *http.Client

	// clock optionally specifies a func to return the current time.
	// If nil, time.Now is used.
	clock func() time.Time

	mu    sync.Mutex
	certs map[string]*cachedResponse
}

func newCachingClient(client *http.Client) *cachingClient {
	return &cachingClient{
		client: client,
		certs:  make(map[string]*cachedResponse, 2),
	}
}

type cachedResponse struct {
	resp *certResponse
	exp  time.Time
}

func (c *cachingClient) getCert(ctx context.Context, url string) (*certResponse, error) {
	if response, ok := c.get(url); ok {
		return response, nil
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	req = req.WithContext(ctx)
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("idtoken: unable to retrieve cert, got status code %d", resp.StatusCode)
	}

	certResp := &certResponse{}
	if err := json.NewDecoder(resp.Body).Decode(certResp); err != nil {
		return nil, err

	}
	c.set(url, certResp, resp.Header)
	return certResp, nil
}

func (c *cachingClient) now() time.Time {
	if c.clock != nil {
		return c.clock()
	}
	return time.Now()
}

func (c *cachingClient) get(url string) (*certResponse, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	cachedResp, ok := c.certs[url]
	if !ok {
		return nil, false
	}
	if c.now().After(cachedResp.exp) {
		return nil, false
	}
	return cachedResp.resp, true
}

func (c *cachingClient) set(url string, resp *certResponse, headers http.Header) {
	exp := c.calculateExpireTime(headers)
	c.mu.Lock()
	c.certs[url] = &cachedResponse{resp: resp, exp: exp}
	c.mu.Unlock()
}

// calculateExpireTime will determine the expire time for the cache based on
// HTTP headers. If there is any difficulty reading the headers the fallback is
// to set the cache to expire now.
func (c *cachingClient) calculateExpireTime(headers http.Header) time.Time {
	var maxAge int
	cc := strings.Split(headers.Get("cache-control"), ",")
	for _, v := range cc {
		if strings.Contains(v, "max-age") {
			ss := strings.Split(v, "=")
			if len(ss) < 2 {
				return c.now()
			}
			ma, err := strconv.Atoi(ss[1])
			if err != nil {
				return c.now()
			}
			maxAge = ma
		}
	}
	age, err := strconv.Atoi(headers.Get("age"))
	if err != nil {
		return c.now()
	}
	return c.now().Add(time.Duration(maxAge-age) * time.Second)
}
<commit_msg>fix(idtoken): Allow missing age in http response header (#1715)<commit_after>// Copyright 2020 Google LLC.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package idtoken

import (
	"context"
	"encoding/json"
	"fmt"
	"net/http"
	"strconv"
	"strings"
	"sync"
	"time"
)

type cachingClient struct {
	client *http.Client

	// clock optionally specifies a func to return the current time.
	// If nil, time.Now is used.
	clock func() time.Time

	mu    sync.Mutex
	certs map[string]*cachedResponse
}

func newCachingClient(client *http.Client) *cachingClient {
	return &cachingClient{
		client: client,
		certs:  make(map[string]*cachedResponse, 2),
	}
}

type cachedResponse struct {
	resp *certResponse
	exp  time.Time
}

func (c *cachingClient) getCert(ctx context.Context, url string) (*certResponse, error) {
	if response, ok := c.get(url); ok {
		return response, nil
	}
	req, err := http.NewRequest(http.MethodGet, url, nil)
	if err != nil {
		return nil, err
	}
	req = req.WithContext(ctx)
	resp, err := c.client.Do(req)
	if err != nil {
		return nil, err
	}
	defer resp.Body.Close()
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("idtoken: unable to retrieve cert, got status code %d", resp.StatusCode)
	}

	certResp := &certResponse{}
	if err := json.NewDecoder(resp.Body).Decode(certResp); err != nil {
		return nil, err

	}
	c.set(url, certResp, resp.Header)
	return certResp, nil
}

func (c *cachingClient) now() time.Time {
	if c.clock != nil {
		return c.clock()
	}
	return time.Now()
}

func (c *cachingClient) get(url string) (*certResponse, bool) {
	c.mu.Lock()
	defer c.mu.Unlock()
	cachedResp, ok := c.certs[url]
	if !ok {
		return nil, false
	}
	if c.now().After(cachedResp.exp) {
		return nil, false
	}
	return cachedResp.resp, true
}

func (c *cachingClient) set(url string, resp *certResponse, headers http.Header) {
	exp := c.calculateExpireTime(headers)
	c.mu.Lock()
	c.certs[url] = &cachedResponse{resp: resp, exp: exp}
	c.mu.Unlock()
}

// calculateExpireTime will determine the expire time for the cache based on
// HTTP headers. If there is any difficulty reading the headers the fallback is
// to set the cache to expire now.
func (c *cachingClient) calculateExpireTime(headers http.Header) time.Time {
	var maxAge int
	cc := strings.Split(headers.Get("cache-control"), ",")
	for _, v := range cc {
		if strings.Contains(v, "max-age") {
			ss := strings.Split(v, "=")
			if len(ss) < 2 {
				return c.now()
			}
			ma, err := strconv.Atoi(ss[1])
			if err != nil {
				return c.now()
			}
			maxAge = ma
		}
	}
	a := headers.Get("age")
	if a == "" {
		return c.now().Add(time.Duration(maxAge) * time.Second)
	}
	age, err := strconv.Atoi(a)
	if err != nil {
		return c.now()
	}
	return c.now().Add(time.Duration(maxAge-age) * time.Second)
}
<|endoftext|>


<commit_before>// Copyright 2021 Google LLC
//
// 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.

package cmd

import (
	"os"
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

// verbose states if the -v or --verbose flag as been set for debug purposes
var verbose bool

// current version of arete
var version = "0.0.2-alpha"

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "arete",
	Short: "Arete makes deploying GCP infrastructure easier",
	Long: `Arete is a wrapper that makes deploying solutions onto Google Cloud Platform easier.

It utilizes Googles Config Connector and Config Controller to deploy declaritive resources into your environment
with as little changes as required.`,
}

// rootCmd represents the base command when called without any subcommands
var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print out the current version of arete",
	Run: func(cmd *cobra.Command, args[]string) {
		fmt.Printf("%s\n", version)
	},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

	err := rootCmd.Execute()
	if err != nil {
		log.Error().Err(err).Msg("")
	}
}

// Init the CLI and add global flags
func init() {
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
	rootCmd.AddCommand(versionCmd)
	viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))
}

<commit_msg>Updating version num<commit_after>// Copyright 2021 Google LLC
//
// 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.

package cmd

import (
	"os"
	"fmt"

	"github.com/spf13/cobra"
	"github.com/spf13/viper"
	"github.com/rs/zerolog"
	"github.com/rs/zerolog/log"
)

// verbose states if the -v or --verbose flag as been set for debug purposes
var verbose bool

// current version of arete
var version = "0.0.3-alpha"

// rootCmd represents the base command when called without any subcommands
var rootCmd = &cobra.Command{
	Use:   "arete",
	Short: "Arete makes deploying GCP infrastructure easier",
	Long: `Arete is a wrapper that makes deploying solutions onto Google Cloud Platform easier.

It utilizes Googles Config Connector and Config Controller to deploy declaritive resources into your environment
with as little changes as required.`,
}

// rootCmd represents the base command when called without any subcommands
var versionCmd = &cobra.Command{
	Use:   "version",
	Short: "Print out the current version of arete",
	Run: func(cmd *cobra.Command, args[]string) {
		fmt.Printf("%s\n", version)
	},
}

// Execute adds all child commands to the root command and sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
	log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})

	err := rootCmd.Execute()
	if err != nil {
		log.Error().Err(err).Msg("")
	}
}

// Init the CLI and add global flags
func init() {
	rootCmd.PersistentFlags().BoolVarP(&verbose, "verbose", "v", false, "verbose output")
	rootCmd.AddCommand(versionCmd)
	viper.BindPFlag("verbose", rootCmd.PersistentFlags().Lookup("verbose"))
}

<|endoftext|>


<commit_before>package cli

import (
	"os"

	auditFile "github.com/hashicorp/vault/builtin/audit/file"
	auditSyslog "github.com/hashicorp/vault/builtin/audit/syslog"

	credAppId "github.com/hashicorp/vault/builtin/credential/app-id"
	credCert "github.com/hashicorp/vault/builtin/credential/cert"
	credGitHub "github.com/hashicorp/vault/builtin/credential/github"
	credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
	credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"

	"github.com/hashicorp/vault/builtin/logical/aws"
	"github.com/hashicorp/vault/builtin/logical/consul"
	"github.com/hashicorp/vault/builtin/logical/mysql"
	"github.com/hashicorp/vault/builtin/logical/postgresql"
	"github.com/hashicorp/vault/builtin/logical/transit"

	"github.com/hashicorp/vault/audit"
	tokenDisk "github.com/hashicorp/vault/builtin/token/disk"
	"github.com/hashicorp/vault/command"
	"github.com/hashicorp/vault/logical"
	"github.com/mitchellh/cli"
)

// Commands returns the mapping of CLI commands for Vault. The meta
// parameter lets you set meta options for all commands.
func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
	if metaPtr == nil {
		metaPtr = new(command.Meta)
	}

	meta := *metaPtr
	if meta.Ui == nil {
		meta.Ui = &cli.BasicUi{
			Writer:      os.Stdout,
			ErrorWriter: os.Stderr,
		}
	}

	return map[string]cli.CommandFactory{
		"init": func() (cli.Command, error) {
			return &command.InitCommand{
				Meta: meta,
			}, nil
		},

		"server": func() (cli.Command, error) {
			return &command.ServerCommand{
				Meta: meta,
				AuditBackends: map[string]audit.Factory{
					"file":   auditFile.Factory,
					"syslog": auditSyslog.Factory,
				},
				CredentialBackends: map[string]logical.Factory{
					"cert":     credCert.Factory,
					"app-id":   credAppId.Factory,
					"github":   credGitHub.Factory,
					"userpass": credUserpass.Factory,
					"ldap":     credLdap.Factory,
				},
				LogicalBackends: map[string]logical.Factory{
					"aws":        aws.Factory,
					"consul":     consul.Factory,
					"postgresql": postgresql.Factory,
					"transit":    transit.Factory,
					"mysql":      mysql.Factory,
				},
			}, nil
		},

		"help": func() (cli.Command, error) {
			return &command.HelpCommand{
				Meta: meta,
			}, nil
		},

		"auth": func() (cli.Command, error) {
			return &command.AuthCommand{
				Meta: meta,
				Handlers: map[string]command.AuthHandler{
					"github":   &credGitHub.CLIHandler{},
					"userpass": &credUserpass.CLIHandler{},
					"ldap":     &credLdap.CLIHandler{},
				},
			}, nil
		},

		"auth-enable": func() (cli.Command, error) {
			return &command.AuthEnableCommand{
				Meta: meta,
			}, nil
		},

		"auth-disable": func() (cli.Command, error) {
			return &command.AuthDisableCommand{
				Meta: meta,
			}, nil
		},

		"audit-list": func() (cli.Command, error) {
			return &command.AuditListCommand{
				Meta: meta,
			}, nil
		},

		"audit-disable": func() (cli.Command, error) {
			return &command.AuditDisableCommand{
				Meta: meta,
			}, nil
		},

		"audit-enable": func() (cli.Command, error) {
			return &command.AuditEnableCommand{
				Meta: meta,
			}, nil
		},

		"policies": func() (cli.Command, error) {
			return &command.PolicyListCommand{
				Meta: meta,
			}, nil
		},

		"policy-delete": func() (cli.Command, error) {
			return &command.PolicyDeleteCommand{
				Meta: meta,
			}, nil
		},

		"policy-write": func() (cli.Command, error) {
			return &command.PolicyWriteCommand{
				Meta: meta,
			}, nil
		},

		"read": func() (cli.Command, error) {
			return &command.ReadCommand{
				Meta: meta,
			}, nil
		},

		"write": func() (cli.Command, error) {
			return &command.WriteCommand{
				Meta: meta,
			}, nil
		},

		"delete": func() (cli.Command, error) {
			return &command.DeleteCommand{
				Meta: meta,
			}, nil
		},

		"renew": func() (cli.Command, error) {
			return &command.RenewCommand{
				Meta: meta,
			}, nil
		},

		"revoke": func() (cli.Command, error) {
			return &command.RevokeCommand{
				Meta: meta,
			}, nil
		},

		"seal": func() (cli.Command, error) {
			return &command.SealCommand{
				Meta: meta,
			}, nil
		},

		"status": func() (cli.Command, error) {
			return &command.StatusCommand{
				Meta: meta,
			}, nil
		},

		"unseal": func() (cli.Command, error) {
			return &command.UnsealCommand{
				Meta: meta,
			}, nil
		},

		"mount": func() (cli.Command, error) {
			return &command.MountCommand{
				Meta: meta,
			}, nil
		},

		"mounts": func() (cli.Command, error) {
			return &command.MountsCommand{
				Meta: meta,
			}, nil
		},

		"remount": func() (cli.Command, error) {
			return &command.RemountCommand{
				Meta: meta,
			}, nil
		},

		"unmount": func() (cli.Command, error) {
			return &command.UnmountCommand{
				Meta: meta,
			}, nil
		},

		"token-create": func() (cli.Command, error) {
			return &command.TokenCreateCommand{
				Meta: meta,
			}, nil
		},

		"token-renew": func() (cli.Command, error) {
			return &command.TokenRenewCommand{
				Meta: meta,
			}, nil
		},

		"token-revoke": func() (cli.Command, error) {
			return &command.TokenRevokeCommand{
				Meta: meta,
			}, nil
		},

		"version": func() (cli.Command, error) {
			ver := Version
			rel := VersionPrerelease
			if GitDescribe != "" {
				ver = GitDescribe
			}
			if GitDescribe == "" && rel == "" {
				rel = "dev"
			}

			return &command.VersionCommand{
				Revision:          GitCommit,
				Version:           ver,
				VersionPrerelease: rel,
				Ui:                meta.Ui,
			}, nil
		},

		// The commands below are hidden from the help output
		"token-disk": func() (cli.Command, error) {
			return &tokenDisk.Command{}, nil
		},
	}
}
<commit_msg>cli: fix dev on version flga<commit_after>package cli

import (
	"os"

	auditFile "github.com/hashicorp/vault/builtin/audit/file"
	auditSyslog "github.com/hashicorp/vault/builtin/audit/syslog"

	credAppId "github.com/hashicorp/vault/builtin/credential/app-id"
	credCert "github.com/hashicorp/vault/builtin/credential/cert"
	credGitHub "github.com/hashicorp/vault/builtin/credential/github"
	credLdap "github.com/hashicorp/vault/builtin/credential/ldap"
	credUserpass "github.com/hashicorp/vault/builtin/credential/userpass"

	"github.com/hashicorp/vault/builtin/logical/aws"
	"github.com/hashicorp/vault/builtin/logical/consul"
	"github.com/hashicorp/vault/builtin/logical/mysql"
	"github.com/hashicorp/vault/builtin/logical/postgresql"
	"github.com/hashicorp/vault/builtin/logical/transit"

	"github.com/hashicorp/vault/audit"
	tokenDisk "github.com/hashicorp/vault/builtin/token/disk"
	"github.com/hashicorp/vault/command"
	"github.com/hashicorp/vault/logical"
	"github.com/mitchellh/cli"
)

// Commands returns the mapping of CLI commands for Vault. The meta
// parameter lets you set meta options for all commands.
func Commands(metaPtr *command.Meta) map[string]cli.CommandFactory {
	if metaPtr == nil {
		metaPtr = new(command.Meta)
	}

	meta := *metaPtr
	if meta.Ui == nil {
		meta.Ui = &cli.BasicUi{
			Writer:      os.Stdout,
			ErrorWriter: os.Stderr,
		}
	}

	return map[string]cli.CommandFactory{
		"init": func() (cli.Command, error) {
			return &command.InitCommand{
				Meta: meta,
			}, nil
		},

		"server": func() (cli.Command, error) {
			return &command.ServerCommand{
				Meta: meta,
				AuditBackends: map[string]audit.Factory{
					"file":   auditFile.Factory,
					"syslog": auditSyslog.Factory,
				},
				CredentialBackends: map[string]logical.Factory{
					"cert":     credCert.Factory,
					"app-id":   credAppId.Factory,
					"github":   credGitHub.Factory,
					"userpass": credUserpass.Factory,
					"ldap":     credLdap.Factory,
				},
				LogicalBackends: map[string]logical.Factory{
					"aws":        aws.Factory,
					"consul":     consul.Factory,
					"postgresql": postgresql.Factory,
					"transit":    transit.Factory,
					"mysql":      mysql.Factory,
				},
			}, nil
		},

		"help": func() (cli.Command, error) {
			return &command.HelpCommand{
				Meta: meta,
			}, nil
		},

		"auth": func() (cli.Command, error) {
			return &command.AuthCommand{
				Meta: meta,
				Handlers: map[string]command.AuthHandler{
					"github":   &credGitHub.CLIHandler{},
					"userpass": &credUserpass.CLIHandler{},
					"ldap":     &credLdap.CLIHandler{},
				},
			}, nil
		},

		"auth-enable": func() (cli.Command, error) {
			return &command.AuthEnableCommand{
				Meta: meta,
			}, nil
		},

		"auth-disable": func() (cli.Command, error) {
			return &command.AuthDisableCommand{
				Meta: meta,
			}, nil
		},

		"audit-list": func() (cli.Command, error) {
			return &command.AuditListCommand{
				Meta: meta,
			}, nil
		},

		"audit-disable": func() (cli.Command, error) {
			return &command.AuditDisableCommand{
				Meta: meta,
			}, nil
		},

		"audit-enable": func() (cli.Command, error) {
			return &command.AuditEnableCommand{
				Meta: meta,
			}, nil
		},

		"policies": func() (cli.Command, error) {
			return &command.PolicyListCommand{
				Meta: meta,
			}, nil
		},

		"policy-delete": func() (cli.Command, error) {
			return &command.PolicyDeleteCommand{
				Meta: meta,
			}, nil
		},

		"policy-write": func() (cli.Command, error) {
			return &command.PolicyWriteCommand{
				Meta: meta,
			}, nil
		},

		"read": func() (cli.Command, error) {
			return &command.ReadCommand{
				Meta: meta,
			}, nil
		},

		"write": func() (cli.Command, error) {
			return &command.WriteCommand{
				Meta: meta,
			}, nil
		},

		"delete": func() (cli.Command, error) {
			return &command.DeleteCommand{
				Meta: meta,
			}, nil
		},

		"renew": func() (cli.Command, error) {
			return &command.RenewCommand{
				Meta: meta,
			}, nil
		},

		"revoke": func() (cli.Command, error) {
			return &command.RevokeCommand{
				Meta: meta,
			}, nil
		},

		"seal": func() (cli.Command, error) {
			return &command.SealCommand{
				Meta: meta,
			}, nil
		},

		"status": func() (cli.Command, error) {
			return &command.StatusCommand{
				Meta: meta,
			}, nil
		},

		"unseal": func() (cli.Command, error) {
			return &command.UnsealCommand{
				Meta: meta,
			}, nil
		},

		"mount": func() (cli.Command, error) {
			return &command.MountCommand{
				Meta: meta,
			}, nil
		},

		"mounts": func() (cli.Command, error) {
			return &command.MountsCommand{
				Meta: meta,
			}, nil
		},

		"remount": func() (cli.Command, error) {
			return &command.RemountCommand{
				Meta: meta,
			}, nil
		},

		"unmount": func() (cli.Command, error) {
			return &command.UnmountCommand{
				Meta: meta,
			}, nil
		},

		"token-create": func() (cli.Command, error) {
			return &command.TokenCreateCommand{
				Meta: meta,
			}, nil
		},

		"token-renew": func() (cli.Command, error) {
			return &command.TokenRenewCommand{
				Meta: meta,
			}, nil
		},

		"token-revoke": func() (cli.Command, error) {
			return &command.TokenRevokeCommand{
				Meta: meta,
			}, nil
		},

		"version": func() (cli.Command, error) {
			ver := Version
			rel := VersionPrerelease
			if GitDescribe != "" {
				ver = GitDescribe
			}
			if GitDescribe == "" && rel == "" && VesrionPrerelease == "" {
				rel = "dev"
			}

			return &command.VersionCommand{
				Revision:          GitCommit,
				Version:           ver,
				VersionPrerelease: rel,
				Ui:                meta.Ui,
			}, nil
		},

		// The commands below are hidden from the help output
		"token-disk": func() (cli.Command, error) {
			return &tokenDisk.Command{}, nil
		},
	}
}
<|endoftext|>