<commit_before>package chain

import (
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcwallet/waddrmgr"
)

// BlockFilterer is used to iteratively scan blocks for a set of addresses of
// interest. This is done by constructing reverse indexes mapping the
// addresses to a ScopedIndex, which permits the reconstruction of the exact
// child deriviation paths that reported matches.
//
// Once initialized, a BlockFilterer can be used to scan any number of blocks
// until a invocation of `FilterBlock` returns true. This allows the reverse
// indexes to be resused in the event that the set of addresses does not need to
// be altered. After a match is reported, a new BlockFilterer should be
// initialized with the updated set of addresses that include any new keys that
// are now within our look-ahead.
//
// We track internal and external addresses separately in order to conserve the
// amount of space occupied in memory. Specifically, the account and branch
// combined contribute only 1-bit of information when using the default scopes
// used by the wallet. Thus we can avoid storing an additional 64-bits per
// address of interest by not storing the full derivation paths, and instead
// opting to allow the caller to contextually infer the account (DefaultAccount)
// and branch (Internal or External).
type BlockFilterer struct {
	// Params specifies the chain params of the current network.
	Params *chaincfg.Params

	// ExReverseFilter holds a reverse index mapping an external address to
	// the scoped index from which it was derived.
	ExReverseFilter map[string]waddrmgr.ScopedIndex

	// InReverseFilter holds a reverse index mapping an internal address to
	// the scoped index from which it was derived.
	InReverseFilter map[string]waddrmgr.ScopedIndex

	// WathcedOutPoints is a global set of outpoints being tracked by the
	// wallet. This allows the block filterer to check for spends from an
	// outpoint we own.
	WatchedOutPoints map[wire.OutPoint]struct{}

	// FoundExternal is a two-layer map recording the scope and index of
	// external addresses found in a single block.
	FoundExternal map[waddrmgr.KeyScope]map[uint32]struct{}

	// FoundInternal is a two-layer map recording the scope and index of
	// internal addresses found in a single block.
	FoundInternal map[waddrmgr.KeyScope]map[uint32]struct{}

	// FoundOutPoints is a set of outpoints found in a single block whose
	// address belongs to the wallet.
	FoundOutPoints map[wire.OutPoint]struct{}

	// RelevantTxns records the transactions found in a particular block
	// that contained matches from an address in either ExReverseFilter or
	// InReverseFilter.
	RelevantTxns []*wire.MsgTx
}

// NewBlockFilterer constructs the reverse indexes for the current set of
// external and internal addresses that we are searching for, and is used to
// scan successive blocks for addresses of interest. A particular block filter
// can be reused until the first call from `FitlerBlock` returns true.
func NewBlockFilterer(params *chaincfg.Params,
	req *FilterBlocksRequest) *BlockFilterer {

	// Construct a reverse index by address string for the requested
	// external addresses.
	nExAddrs := len(req.ExternalAddrs)
	exReverseFilter := make(map[string]waddrmgr.ScopedIndex, nExAddrs)
	for scopedIndex, addr := range req.ExternalAddrs {
		exReverseFilter[addr.EncodeAddress()] = scopedIndex
	}

	// Construct a reverse index by address string for the requested
	// internal addresses.
	nInAddrs := len(req.InternalAddrs)
	inReverseFilter := make(map[string]waddrmgr.ScopedIndex, nInAddrs)
	for scopedIndex, addr := range req.InternalAddrs {
		inReverseFilter[addr.EncodeAddress()] = scopedIndex
	}

	foundExternal := make(map[waddrmgr.KeyScope]map[uint32]struct{})
	foundInternal := make(map[waddrmgr.KeyScope]map[uint32]struct{})
	foundOutPoints := make(map[wire.OutPoint]struct{})

	return &BlockFilterer{
		Params:           params,
		ExReverseFilter:  exReverseFilter,
		InReverseFilter:  inReverseFilter,
		WatchedOutPoints: req.WatchedOutPoints,
		FoundExternal:    foundExternal,
		FoundInternal:    foundInternal,
		FoundOutPoints:   foundOutPoints,
	}
}

// FilterBlock parses all txns in the provided block, searching for any that
// contain addresses of interest in either the external or internal reverse
// filters. This method return true iff the block contains a non-zero number of
// addresses of interest, or a transaction in the block spends from outpoints
// controlled by the wallet.
func (bf *BlockFilterer) FilterBlock(block *wire.MsgBlock) bool {
	var hasRelevantTxns bool
	for _, tx := range block.Transactions {
		if bf.FilterTx(tx) {
			bf.RelevantTxns = append(bf.RelevantTxns, tx)
			hasRelevantTxns = true
		}
	}

	return hasRelevantTxns
}

// FilterTx scans all txouts in the provided txn, testing to see if any found
// addresses match those contained within the external or internal reverse
// indexes. This method returns true iff the txn contains a non-zero number of
// addresses of interest, or the transaction spends from an outpoint that
// belongs to the wallet.
func (bf *BlockFilterer) FilterTx(tx *wire.MsgTx) bool {
	var isRelevant bool

	// First, check the inputs to this transaction to see if they spend any
	// inputs belonging to the wallet. In addition to checking
	// WatchedOutPoints, we also check FoundOutPoints, in case a txn spends
	// from an outpoint created in the same block.
	for _, in := range tx.TxIn {
		if _, ok := bf.WatchedOutPoints[in.PreviousOutPoint]; ok {
			isRelevant = true
		}
		if _, ok := bf.FoundOutPoints[in.PreviousOutPoint]; ok {
			isRelevant = true
		}
	}

	// Now, parse all of the outputs created by this transactions, and see
	// if they contain any addresses known the wallet using our reverse
	// indexes for both external and internal addresses. If a new output is
	// found, we will add the outpoint to our set of FoundOutPoints.
	for i, out := range tx.TxOut {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			out.PkScript, bf.Params,
		)
		if err != nil {
			log.Warnf("Could not parse output script in %s:%d: %v",
				tx.TxHash(), i, err)
			continue
		}

		if !bf.FilterOutputAddrs(addrs) {
			continue
		}

		// If we've reached this point, then the output contains an
		// address of interest.
		isRelevant = true

		// Record the outpoint that containing the address in our set of
		// found outpoints, so that the caller can update its global
		// set of watched outpoints.
		outPoint := wire.OutPoint{
			Hash:  *btcutil.NewTx(tx).Hash(),
			Index: uint32(i),
		}

		bf.FoundOutPoints[outPoint] = struct{}{}
	}

	return isRelevant
}

// FilterOutputAddrs tests the set of addresses against the block filterer's
// external and internal reverse address indexes. If any are found, they are
// added to set of external and internal found addresses, respectively. This
// method returns true iff a non-zero number of the provided addresses are of
// interest.
func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool {
	var isRelevant bool
	for _, addr := range addrs {
		addrStr := addr.EncodeAddress()
		if scopedIndex, ok := bf.ExReverseFilter[addrStr]; ok {
			bf.foundExternal(scopedIndex)
			isRelevant = true
		}
		if scopedIndex, ok := bf.InReverseFilter[addrStr]; ok {
			bf.foundInternal(scopedIndex)
			isRelevant = true
		}
	}

	return isRelevant
}

// foundExternal marks the scoped index as found within the block filterer's
// FoundExternal map. If this the first index found for a particular scope, the
// scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundExternal(scopedIndex waddrmgr.ScopedIndex) {
	if _, ok := bf.FoundExternal[scopedIndex.Scope]; !ok {
		bf.FoundExternal[scopedIndex.Scope] = make(map[uint32]struct{})
	}
	bf.FoundExternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{}
}

// foundInternal marks the scoped index as found within the block filterer's
// FoundInternal map. If this the first index found for a particular scope, the
// scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundInternal(scopedIndex waddrmgr.ScopedIndex) {
	if _, ok := bf.FoundInternal[scopedIndex.Scope]; !ok {
		bf.FoundInternal[scopedIndex.Scope] = make(map[uint32]struct{})
	}
	bf.FoundInternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{}
}
<commit_msg>chain: update block filterer to map outpoints to addrs<commit_after>package chain

import (
	"github.com/btcsuite/btcd/chaincfg"
	"github.com/btcsuite/btcd/txscript"
	"github.com/btcsuite/btcd/wire"
	"github.com/btcsuite/btcutil"
	"github.com/btcsuite/btcwallet/waddrmgr"
)

// BlockFilterer is used to iteratively scan blocks for a set of addresses of
// interest. This is done by constructing reverse indexes mapping the
// addresses to a ScopedIndex, which permits the reconstruction of the exact
// child deriviation paths that reported matches.
//
// Once initialized, a BlockFilterer can be used to scan any number of blocks
// until a invocation of `FilterBlock` returns true. This allows the reverse
// indexes to be resused in the event that the set of addresses does not need to
// be altered. After a match is reported, a new BlockFilterer should be
// initialized with the updated set of addresses that include any new keys that
// are now within our look-ahead.
//
// We track internal and external addresses separately in order to conserve the
// amount of space occupied in memory. Specifically, the account and branch
// combined contribute only 1-bit of information when using the default scopes
// used by the wallet. Thus we can avoid storing an additional 64-bits per
// address of interest by not storing the full derivation paths, and instead
// opting to allow the caller to contextually infer the account (DefaultAccount)
// and branch (Internal or External).
type BlockFilterer struct {
	// Params specifies the chain params of the current network.
	Params *chaincfg.Params

	// ExReverseFilter holds a reverse index mapping an external address to
	// the scoped index from which it was derived.
	ExReverseFilter map[string]waddrmgr.ScopedIndex

	// InReverseFilter holds a reverse index mapping an internal address to
	// the scoped index from which it was derived.
	InReverseFilter map[string]waddrmgr.ScopedIndex

	// WathcedOutPoints is a global set of outpoints being tracked by the
	// wallet. This allows the block filterer to check for spends from an
	// outpoint we own.
	WatchedOutPoints map[wire.OutPoint]btcutil.Address

	// FoundExternal is a two-layer map recording the scope and index of
	// external addresses found in a single block.
	FoundExternal map[waddrmgr.KeyScope]map[uint32]struct{}

	// FoundInternal is a two-layer map recording the scope and index of
	// internal addresses found in a single block.
	FoundInternal map[waddrmgr.KeyScope]map[uint32]struct{}

	// FoundOutPoints is a set of outpoints found in a single block whose
	// address belongs to the wallet.
	FoundOutPoints map[wire.OutPoint]btcutil.Address

	// RelevantTxns records the transactions found in a particular block
	// that contained matches from an address in either ExReverseFilter or
	// InReverseFilter.
	RelevantTxns []*wire.MsgTx
}

// NewBlockFilterer constructs the reverse indexes for the current set of
// external and internal addresses that we are searching for, and is used to
// scan successive blocks for addresses of interest. A particular block filter
// can be reused until the first call from `FitlerBlock` returns true.
func NewBlockFilterer(params *chaincfg.Params,
	req *FilterBlocksRequest) *BlockFilterer {

	// Construct a reverse index by address string for the requested
	// external addresses.
	nExAddrs := len(req.ExternalAddrs)
	exReverseFilter := make(map[string]waddrmgr.ScopedIndex, nExAddrs)
	for scopedIndex, addr := range req.ExternalAddrs {
		exReverseFilter[addr.EncodeAddress()] = scopedIndex
	}

	// Construct a reverse index by address string for the requested
	// internal addresses.
	nInAddrs := len(req.InternalAddrs)
	inReverseFilter := make(map[string]waddrmgr.ScopedIndex, nInAddrs)
	for scopedIndex, addr := range req.InternalAddrs {
		inReverseFilter[addr.EncodeAddress()] = scopedIndex
	}

	foundExternal := make(map[waddrmgr.KeyScope]map[uint32]struct{})
	foundInternal := make(map[waddrmgr.KeyScope]map[uint32]struct{})
	foundOutPoints := make(map[wire.OutPoint]btcutil.Address)

	return &BlockFilterer{
		Params:           params,
		ExReverseFilter:  exReverseFilter,
		InReverseFilter:  inReverseFilter,
		WatchedOutPoints: req.WatchedOutPoints,
		FoundExternal:    foundExternal,
		FoundInternal:    foundInternal,
		FoundOutPoints:   foundOutPoints,
	}
}

// FilterBlock parses all txns in the provided block, searching for any that
// contain addresses of interest in either the external or internal reverse
// filters. This method return true iff the block contains a non-zero number of
// addresses of interest, or a transaction in the block spends from outpoints
// controlled by the wallet.
func (bf *BlockFilterer) FilterBlock(block *wire.MsgBlock) bool {
	var hasRelevantTxns bool
	for _, tx := range block.Transactions {
		if bf.FilterTx(tx) {
			bf.RelevantTxns = append(bf.RelevantTxns, tx)
			hasRelevantTxns = true
		}
	}

	return hasRelevantTxns
}

// FilterTx scans all txouts in the provided txn, testing to see if any found
// addresses match those contained within the external or internal reverse
// indexes. This method returns true iff the txn contains a non-zero number of
// addresses of interest, or the transaction spends from an outpoint that
// belongs to the wallet.
func (bf *BlockFilterer) FilterTx(tx *wire.MsgTx) bool {
	var isRelevant bool

	// First, check the inputs to this transaction to see if they spend any
	// inputs belonging to the wallet. In addition to checking
	// WatchedOutPoints, we also check FoundOutPoints, in case a txn spends
	// from an outpoint created in the same block.
	for _, in := range tx.TxIn {
		if _, ok := bf.WatchedOutPoints[in.PreviousOutPoint]; ok {
			isRelevant = true
		}
		if _, ok := bf.FoundOutPoints[in.PreviousOutPoint]; ok {
			isRelevant = true
		}
	}

	// Now, parse all of the outputs created by this transactions, and see
	// if they contain any addresses known the wallet using our reverse
	// indexes for both external and internal addresses. If a new output is
	// found, we will add the outpoint to our set of FoundOutPoints.
	for i, out := range tx.TxOut {
		_, addrs, _, err := txscript.ExtractPkScriptAddrs(
			out.PkScript, bf.Params,
		)
		if err != nil {
			log.Warnf("Could not parse output script in %s:%d: %v",
				tx.TxHash(), i, err)
			continue
		}

		if !bf.FilterOutputAddrs(addrs) {
			continue
		}

		// If we've reached this point, then the output contains an
		// address of interest.
		isRelevant = true

		// Record the outpoint that containing the address in our set of
		// found outpoints, so that the caller can update its global
		// set of watched outpoints.
		outPoint := wire.OutPoint{
			Hash:  *btcutil.NewTx(tx).Hash(),
			Index: uint32(i),
		}

		bf.FoundOutPoints[outPoint] = addrs[0]
	}

	return isRelevant
}

// FilterOutputAddrs tests the set of addresses against the block filterer's
// external and internal reverse address indexes. If any are found, they are
// added to set of external and internal found addresses, respectively. This
// method returns true iff a non-zero number of the provided addresses are of
// interest.
func (bf *BlockFilterer) FilterOutputAddrs(addrs []btcutil.Address) bool {
	var isRelevant bool
	for _, addr := range addrs {
		addrStr := addr.EncodeAddress()
		if scopedIndex, ok := bf.ExReverseFilter[addrStr]; ok {
			bf.foundExternal(scopedIndex)
			isRelevant = true
		}
		if scopedIndex, ok := bf.InReverseFilter[addrStr]; ok {
			bf.foundInternal(scopedIndex)
			isRelevant = true
		}
	}

	return isRelevant
}

// foundExternal marks the scoped index as found within the block filterer's
// FoundExternal map. If this the first index found for a particular scope, the
// scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundExternal(scopedIndex waddrmgr.ScopedIndex) {
	if _, ok := bf.FoundExternal[scopedIndex.Scope]; !ok {
		bf.FoundExternal[scopedIndex.Scope] = make(map[uint32]struct{})
	}
	bf.FoundExternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{}
}

// foundInternal marks the scoped index as found within the block filterer's
// FoundInternal map. If this the first index found for a particular scope, the
// scope's second layer map will be initialized before marking the index.
func (bf *BlockFilterer) foundInternal(scopedIndex waddrmgr.ScopedIndex) {
	if _, ok := bf.FoundInternal[scopedIndex.Scope]; !ok {
		bf.FoundInternal[scopedIndex.Scope] = make(map[uint32]struct{})
	}
	bf.FoundInternal[scopedIndex.Scope][scopedIndex.Index] = struct{}{}
}
<|endoftext|>


<commit_before><commit_msg>iterate more than one backend for backend service status<commit_after><|endoftext|>


<commit_before><commit_msg>Adds placeholder for flag mechanism in downstream data<commit_after><|endoftext|>


<commit_before><commit_msg>replicaset: close cloned sessions<commit_after><|endoftext|>


<commit_before>package filter

import (
	"errors"
	"image"
	"image/color"
	"math"
	"strconv"

	"github.com/jangler/imp/util"
)

var lumHelp = `lum factor [gFactor bFactor [aFactor]]

Multiply the brightness of the image by the given factor. Individual factors
may be given for each channel.`

func lumFunc(img *image.RGBA, args []string) (*image.RGBA, []string) {
	numArgs := 0
	factor := []float64{1.0, 1.0, 1.0, 1.0}
	for numArgs < 4 && len(args) > 0 {
		f, err := strconv.ParseFloat(args[0], 64)
		if err != nil {
			break
		}
		factor[numArgs] = f
		args = args[1:]
		numArgs++
	}
	if numArgs != 1 && numArgs != 3 && numArgs != 4 {
		util.Die(errors.New(lumHelp))
	}
	if numArgs == 1 {
		factor[1] = factor[0]
		factor[2] = factor[0]
	}

	b := img.Bounds()
	for y := b.Min.Y; y < b.Max.Y; y++ {
		for x := b.Min.X; x < b.Max.X; x++ {
			r, g, b, a := img.At(x, y).RGBA()
			newA := uint8(math.Min(255, float64(a>>8)*factor[3]))
			c := color.RGBA{
				uint8(math.Min(float64(newA),
					float64(r>>8)*factor[0]*factor[3])),
				uint8(math.Min(float64(newA),
					float64(g>>8)*factor[1]*factor[3])),
				uint8(math.Min(float64(newA),
					float64(b>>8)*factor[2]*factor[3])),
				newA,
			}
			img.SetRGBA(x, y, c)
		}
	}

	return img, args
}

func init() {
	addFilter(&Filter{"lum", lumHelp, lumFunc})
}
<commit_msg>Excess alpha in lum no longer overloads color channels<commit_after>package filter

import (
	"errors"
	"image"
	"image/color"
	"math"
	"strconv"

	"github.com/jangler/imp/util"
)

var lumHelp = `lum factor [gFactor bFactor [aFactor]]

Multiply the brightness of the image by the given factor. Individual factors
may be given for each channel.`

func lumFunc(img *image.RGBA, args []string) (*image.RGBA, []string) {
	numArgs := 0
	factor := []float64{1.0, 1.0, 1.0, 1.0}
	for numArgs < 4 && len(args) > 0 {
		f, err := strconv.ParseFloat(args[0], 64)
		if err != nil {
			break
		}
		factor[numArgs] = f
		args = args[1:]
		numArgs++
	}
	if numArgs != 1 && numArgs != 3 && numArgs != 4 {
		util.Die(errors.New(lumHelp))
	}
	if numArgs == 1 {
		factor[1] = factor[0]
		factor[2] = factor[0]
	}

	b := img.Bounds()
	for y := b.Min.Y; y < b.Max.Y; y++ {
		for x := b.Min.X; x < b.Max.X; x++ {
			r, g, b, a := img.At(x, y).RGBA()
			newA := uint8(math.Min(255, float64(a>>8)*factor[3]))
			aRatio := float64(int(newA)<<8) / float64(a)
			c := color.RGBA{
				uint8(math.Min(float64(newA),
					float64(r>>8)*factor[0]*aRatio)),
				uint8(math.Min(float64(newA),
					float64(g>>8)*factor[1]*aRatio)),
				uint8(math.Min(float64(newA),
					float64(b>>8)*factor[2]*aRatio)),
				newA,
			}
			img.SetRGBA(x, y, c)
		}
	}

	return img, args
}

func init() {
	addFilter(&Filter{"lum", lumHelp, lumFunc})
}
<|endoftext|>


<commit_before>// Package flagconfig provides a flag to specifiy a config file which
// will in turn be used to read unspecified flag values.
package flagconfig

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var configFile = flag.String(
	"c", defaultConfig(), "Config file to read flags from.")

func Usage() {
	Parse()
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	flag.VisitAll(func(f *flag.Flag) {
		fmt.Fprintf(os.Stderr, "  -%s=%s: %s\n", f.Name, f.Value.String(), f.Usage)
	})
}

func defaultConfig() string {
	home := os.Getenv("HOME")
	basename := filepath.Base(os.Args[0])
	path := filepath.Join(home, ".config", basename, "config")
	_, err := os.Open(path)
	if err == nil {
		return path
	}
	path = filepath.Join("/", "etc", "conf.d", basename)
	_, err = os.Open(path)
	if err == nil {
		return path
	}
	return ""
}

func contains(list []*flag.Flag, f *flag.Flag) bool {
	for _, i := range list {
		if i == f {
			return true
		}
	}
	return false
}

func readConfig() map[string]string {
	bytes, err := ioutil.ReadFile(*configFile)
	if err != nil {
		log.Fatalf("Failed to read config file %s: %s", *configFile, err)
	}
	lines := strings.Split(string(bytes), "\n")
	result := make(map[string]string, len(lines))
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" || trimmed[0] == '#' {
			continue
		}
		parts := strings.Split(line, "=")
		if len(parts) != 2 {
			log.Fatalf("Invalid config line: %s", line)
		}
		result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
	}
	return result
}

func Parse() {
	ParseSet(flag.CommandLine)
}

func ParseSet(set *flag.FlagSet) {
	if *configFile == "" {
		return
	}
	config := readConfig()
	explicit := make([]*flag.Flag, 0)
	all := make([]*flag.Flag, 0)
	set.Visit(func(f *flag.Flag) {
		explicit = append(explicit, f)
	})
	set.VisitAll(func(f *flag.Flag) {
		all = append(all, f)
		if !contains(explicit, f) {
			val := config[f.Name]
			if val != "" {
				err := f.Value.Set(val)
				if err != nil {
					log.Fatalf("Failed to set flag %s with value %s", f.Name, val)
				}
			}
		}
	})
Outer:
	for name, val := range config {
		for _, f := range all {
			if f.Name == name {
				continue Outer
			}
		}
		log.Fatalf("Unknown flag %s=%s in config file.", name, val)
	}
}
<commit_msg>Modify ParseSet() to take a filename argument (#2)<commit_after>// Package flagconfig provides a flag to specifiy a config file which
// will in turn be used to read unspecified flag values.
package flagconfig

import (
	"flag"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	"strings"
)

var configFile = flag.String(
	"c", defaultConfig(), "Config file to read flags from.")

func Usage() {
	Parse()
	fmt.Fprintf(os.Stderr, "Usage of %s:\n", os.Args[0])
	flag.VisitAll(func(f *flag.Flag) {
		fmt.Fprintf(os.Stderr, "  -%s=%s: %s\n", f.Name, f.Value.String(), f.Usage)
	})
}

func defaultConfig() string {
	home := os.Getenv("HOME")
	basename := filepath.Base(os.Args[0])
	path := filepath.Join(home, ".config", basename, "config")
	_, err := os.Open(path)
	if err == nil {
		return path
	}
	path = filepath.Join("/", "etc", "conf.d", basename)
	_, err = os.Open(path)
	if err == nil {
		return path
	}
	return ""
}

func contains(list []*flag.Flag, f *flag.Flag) bool {
	for _, i := range list {
		if i == f {
			return true
		}
	}
	return false
}

func readConfig(filename string) map[string]string {
	bytes, err := ioutil.ReadFile(filename)
	if err != nil {
		log.Fatalf("Failed to read config file %s: %s", *configFile, err)
	}
	lines := strings.Split(string(bytes), "\n")
	result := make(map[string]string, len(lines))
	for _, line := range lines {
		trimmed := strings.TrimSpace(line)
		if trimmed == "" || trimmed[0] == '#' {
			continue
		}
		parts := strings.Split(line, "=")
		if len(parts) != 2 {
			log.Fatalf("Invalid config line: %s", line)
		}
		result[strings.TrimSpace(parts[0])] = strings.TrimSpace(parts[1])
	}
	return result
}

func Parse() {
	ParseSet(flag.CommandLine)
}

func ParseSet(set *flag.FlagSet) {
	ParseFile(set, *configFile)
}

func ParseFile(set *flag.FlagSet, filename string) {
	if filename == "" {
		return
	}
	config := readConfig(filename)
	explicit := make([]*flag.Flag, 0)
	all := make([]*flag.Flag, 0)
	set.Visit(func(f *flag.Flag) {
		explicit = append(explicit, f)
	})
	set.VisitAll(func(f *flag.Flag) {
		all = append(all, f)
		if !contains(explicit, f) {
			val := config[f.Name]
			if val != "" {
				err := f.Value.Set(val)
				if err != nil {
					log.Fatalf("Failed to set flag %s with value %s", f.Name, val)
				}
			}
		}
	})
Outer:
	for name, val := range config {
		for _, f := range all {
			if f.Name == name {
				continue Outer
			}
		}
		log.Fatalf("Unknown flag %s=%s in config file.", name, val)
	}
}
<|endoftext|>


<commit_before>package buffalo

import (
	"testing"

	"github.com/stretchr/testify/require"
)

func Test_FlashSet(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	r.Equal(f.data, map[string][]string{
		"error": []string{"something"},
	})
}

func Test_FlashGet(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	r.Equal(f.Get("error"), []string{"something"})
}

func Test_FlashDelete(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	r.Equal(f.Get("error"), []string{"something"})

	f.Delete("error")
	r.Equal(f.Get("error"), []string(nil))
}

func Test_FlashClear(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	f.Set("warning", "warning")
	r.Equal(f.Get("error"), []string{"something"})
	r.Equal(f.Get("warning"), []string{"warning"})

	f.Clear()
	r.Equal(f.data, map[string][]string{})

	r.Equal(f.Get("error"), []string(nil))
	r.Equal(f.Get("warning"), []string(nil))
}

func Test_FlashAdd(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})

	f.Add("error", "something")
	r.Equal(f.data, map[string][]string{
		"error": []string{"something"},
	})

	f.Add("error", "other")
	r.Equal(f.data, map[string][]string{
		"error": []string{"something", "other"},
	})
}
<commit_msg>adding some tests for the rendering of the Errors and All<commit_after>package buffalo

import (
	"io/ioutil"
	"os"
	"testing"

	"github.com/gobuffalo/buffalo/render"
	"github.com/markbates/willie"
	"github.com/stretchr/testify/require"
)

func Test_FlashSet(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	r.Equal(f.data, map[string][]string{
		"error": []string{"something"},
	})
}

func Test_FlashGet(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	r.Equal(f.Get("error"), []string{"something"})
}

func Test_FlashDelete(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	r.Equal(f.Get("error"), []string{"something"})

	f.Delete("error")
	r.Equal(f.Get("error"), []string(nil))
}

func Test_FlashClear(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})
	f.Set("error", "something")
	f.Set("warning", "warning")
	r.Equal(f.Get("error"), []string{"something"})
	r.Equal(f.Get("warning"), []string{"warning"})

	f.Clear()
	r.Equal(f.data, map[string][]string{})

	r.Equal(f.Get("error"), []string(nil))
	r.Equal(f.Get("warning"), []string(nil))
}

func Test_FlashAdd(t *testing.T) {
	r := require.New(t)
	f := newFlash(&Session{})

	r.Equal(f.data, map[string][]string{})

	f.Add("error", "something")
	r.Equal(f.data, map[string][]string{
		"error": []string{"something"},
	})

	f.Add("error", "other")
	r.Equal(f.data, map[string][]string{
		"error": []string{"something", "other"},
	})
}

func Test_FlashRender(t *testing.T) {
	tempFolder := os.TempDir()
	ioutil.WriteFile(tempFolder+"application.html", []byte("{{yield}}"), 0755)
	ioutil.WriteFile(tempFolder+"show.html", []byte(errorsTPL), 0755)

	r := require.New(t)
	a := Automatic(Options{})
	rr := render.New(render.Options{
		HTMLLayout: tempFolder + "application.html",
	})

	a.GET("/", func(c Context) error {
		c.Flash().Add("errors", "Error AJ set")
		c.Flash().Add("errors", "Error DAL set")

		return c.Render(201, rr.HTML(tempFolder+"show.html"))
	})

	w := willie.New(a)
	res := w.Request("/").Get()

	r.Contains(res.Body.String(), "Error AJ set")
	r.Contains(res.Body.String(), "Error DAL set")
}

func Test_FlashRenderElse(t *testing.T) {
	tempFolder := os.TempDir()
	ioutil.WriteFile(tempFolder+"application.html", []byte("{{yield}}"), 0755)
	ioutil.WriteFile(tempFolder+"show.html", []byte(errorsTPL), 0755)

	r := require.New(t)
	a := Automatic(Options{})
	rr := render.New(render.Options{
		HTMLLayout: tempFolder + "application.html",
	})

	a.GET("/", func(c Context) error {
		return c.Render(201, rr.HTML(tempFolder+"show.html"))
	})

	w := willie.New(a)

	res := w.Request("/").Get()
	r.NotContains(res.Body.String(), "Flash:")
}

const errorsTPL = `{{#each flash.Errors as |k flash|}}
	Flash:
    {{k}}:{{flash}}
{{/each}}`

func Test_FlashRenderAll(t *testing.T) {
	tempFolder := os.TempDir()
	ioutil.WriteFile(tempFolder+"application.html", []byte("{{yield}}"), 0755)
	ioutil.WriteFile(tempFolder+"show.html", []byte(keyTPL), 0755)

	r := require.New(t)
	a := Automatic(Options{})
	rr := render.New(render.Options{
		HTMLLayout: tempFolder + "application.html",
	})

	a.GET("/", func(c Context) error {
		c.Flash().Set("something", "something to say!")
		return c.Render(201, rr.HTML(tempFolder+"show.html"))
	})

	w := willie.New(a)
	res := w.Request("/").Get()

	r.Contains(res.Body.String(), "something to say!")
}

const keyTPL = `{{#each flash.All as |k flash|}}
	Flash:
    {{k}}:{{flash}}
{{/each}}`
<|endoftext|>


<commit_before>package fleet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
)

type client struct {
	http *http.Client
}

type Client interface {
	Units() ([]Unit, error)
	StartUnit(name string, options []UnitOption) (*http.Response, error)
	DestroyUnit(name string) (*http.Response, error)
	UnitState(name string) (UnitState, error)
}

var _ Client = &client{}

type Unit struct {
	CurrentState string       `json:"currentState,omitempty"`
	DesiredState string       `json:"desiredState"`
	MachineID    string       `json:"machineID,omitempty"`
	Name         string       `json:"name,omitempty"`
	Options      []UnitOption `json:"options"`
}

type UnitOption struct {
	Section string `json:"section"`
	Name    string `json:"name"`
	Value   string `json:"value"`
}

type UnitState struct {
	Name        string `json:"name"`
	Hash        string `json:"hash"`
	MachineID   string `json:"machineID"`
	LoadState   string `json:"systemdLoadState"`
	ActiveState string `json:"systemdActiveState"`
	SubState    string `json:"systemdSubState"`
}

type UnitsResponse struct {
	Units []Unit `json:"units"`
}

type StatesResponse struct {
	States []UnitState `json:"states"`
}

func NewClient(path string) client {
	dialFunc := func(string, string) (net.Conn, error) {
		return net.Dial("unix", path)
	}

	httpClient := http.Client{
		Transport: &http.Transport{
			Dial: dialFunc,
		},
	}

	return client{&httpClient}
}

func (c *client) Units() ([]Unit, error) {
	response, err := c.http.Get("http://sock/fleet/v1/units")
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(response.Body)
	var parsedResponse UnitsResponse
	err = decoder.Decode(&parsedResponse)
	if err != nil {
		return nil, err
	}

	return parsedResponse.Units, nil
}

func (c *client) StartUnit(name string, options []UnitOption) (*http.Response, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/units/%s", name)
	unit := Unit{
		DesiredState: "launched",
		Options:      options,
	}

	var b bytes.Buffer
	enc := json.NewEncoder(&b)
	enc.Encode(unit)

	r, err := http.NewRequest("PUT", url, &b)
	if err != nil {
		return nil, err
	}

	r.Header.Add("Content-Type", "application/json")

	return c.http.Do(r)
}

func (c *client) DestroyUnit(name string) (*http.Response, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/units/%s", name)

	r, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return nil, err
	}

	return c.http.Do(r)
}

func (c *client) UnitState(name string) (UnitState, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/state?unitName=%s", name)
	response, err := c.http.Get(url)
	if err != nil {
		return UnitState{}, err
	}

	decoder := json.NewDecoder(response.Body)
	var parsedResponse StatesResponse
	err = decoder.Decode(&parsedResponse)
	if err != nil {
		return UnitState{}, err
	}

	if len(parsedResponse.States) > 0 {
		return parsedResponse.States[0], nil
	} else {
		return UnitState{}, nil
	}
}
<commit_msg>Add documentation to the fleet package.<commit_after>// fleet is a package for communicating with Fleet's HTTP API to list units and
// their states, launch new units, and destroy units.  It uses the API that is
// documented here:
//   https://github.com/coreos/fleet/blob/master/Documentation/api-v1.md
package fleet

import (
	"bytes"
	"encoding/json"
	"fmt"
	"net"
	"net/http"
)

// Client is the main interface for the Fleet client that is implemented in this
// package.
type Client interface {
	Units() ([]Unit, error)
	StartUnit(name string, options []UnitOption) (*http.Response, error)
	DestroyUnit(name string) (*http.Response, error)
	UnitState(name string) (UnitState, error)
}

// client is the Fleet client implemenation for the interface described above.
// Use NewClient in this package to get a new client with the HTTP client wired
// up to talk to fleet's socket.
type client struct {
	http *http.Client
}

var _ Client = &client{}

// Unit is the representation of a Fleet unit and is used to deserialze the JSON
// returned from the Fleet API.  This is also the struct that is returned when
// this package returns units via Units().
type Unit struct {
	CurrentState string       `json:"currentState,omitempty"`
	DesiredState string       `json:"desiredState"`
	MachineID    string       `json:"machineID,omitempty"`
	Name         string       `json:"name,omitempty"`
	Options      []UnitOption `json:"options"`
}

// UnitOption is the representation for each of a Fleet unit's individual
// options.  It is used to deserialze the JSON returned from the Fleet API as
// well as provide the information for starting a new unit.
type UnitOption struct {
	Section string `json:"section"`
	Name    string `json:"name"`
	Value   string `json:"value"`
}

// UnitState is the representation for detailed information about the current
// state of a Fleet unit.  It is used to deserialize the JSON returned from the
// Fleet API when requesting the state of a given unit.  This is also the struct
// that is returned when this package returns the state via UnitState().
type UnitState struct {
	Name        string `json:"name"`
	Hash        string `json:"hash"`
	MachineID   string `json:"machineID"`
	LoadState   string `json:"systemdLoadState"`
	ActiveState string `json:"systemdActiveState"`
	SubState    string `json:"systemdSubState"`
}

// UnitsResponse is the wrapper for a Fleet API response containing an array of
// units and is used to deserialize JSON.
type UnitsResponse struct {
	Units []Unit `json:"units"`
}

// StatesResponse is the wrapper for a Fleet API response containing an array of
// unit states and is used to deserialize JSON.
type StatesResponse struct {
	States []UnitState `json:"states"`
}

// NewClient construct an HTTP client using the given path as the socket to dial
// out to for making requests and returns a new Fleet client with the HTTP
// client wired up.  Currently this method *only* supports communicating with
// Fleet over a socket.
func NewClient(path string) client {
	dialFunc := func(string, string) (net.Conn, error) {
		return net.Dial("unix", path)
	}

	httpClient := http.Client{
		Transport: &http.Transport{
			Dial: dialFunc,
		},
	}

	return client{&httpClient}
}

// Units queries the Fleet API for a list of all units and returns them as an
// array of Unit structs.
func (c *client) Units() ([]Unit, error) {
	response, err := c.http.Get("http://sock/fleet/v1/units")
	if err != nil {
		return nil, err
	}

	decoder := json.NewDecoder(response.Body)
	var parsedResponse UnitsResponse
	err = decoder.Decode(&parsedResponse)
	if err != nil {
		return nil, err
	}

	return parsedResponse.Units, nil
}

// StartUnit uses the given name and unit options to PUT a request to launch
// a unit.  The UnitOption array is similar to a unit file that you'd use to
// start a unit via fleetctl.
func (c *client) StartUnit(name string, options []UnitOption) (*http.Response, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/units/%s", name)
	unit := Unit{
		DesiredState: "launched",
		Options:      options,
	}

	var b bytes.Buffer
	enc := json.NewEncoder(&b)
	enc.Encode(unit)

	r, err := http.NewRequest("PUT", url, &b)
	if err != nil {
		return nil, err
	}

	r.Header.Add("Content-Type", "application/json")

	return c.http.Do(r)
}

// DestroyUnit issues a DELETE request to the Fleet API to remove the unit for
// the name passed.
func (c *client) DestroyUnit(name string) (*http.Response, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/units/%s", name)

	r, err := http.NewRequest("DELETE", url, nil)
	if err != nil {
		return nil, err
	}

	return c.http.Do(r)
}

// UnitState gets detailed state information about the unit that is passed.  We
// assume that a full name is given and that only one match will be returned
// from the Fleet API for that name.
func (c *client) UnitState(name string) (UnitState, error) {
	url := fmt.Sprintf("http://sock/fleet/v1/state?unitName=%s", name)
	response, err := c.http.Get(url)
	if err != nil {
		return UnitState{}, err
	}

	decoder := json.NewDecoder(response.Body)
	var parsedResponse StatesResponse
	err = decoder.Decode(&parsedResponse)
	if err != nil {
		return UnitState{}, err
	}

	if len(parsedResponse.States) > 0 {
		return parsedResponse.States[0], nil
	} else {
		return UnitState{}, nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. 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 fs_test

import (
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math"
	"math/rand"
	"os"
	"os/user"
	"strconv"
	"strings"
	"time"

	"github.com/googlecloudplatform/gcsfuse/fs"
	"github.com/googlecloudplatform/gcsfuse/perms"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/fuse/fusetesting"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsutil"
	"github.com/jacobsa/oglematchers"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

const (
	filePerms os.FileMode = 0740
	dirPerms              = 0754
)

// A struct that can be embedded to inherit common file system test behaviors.
type fsTest struct {
	ctx    context.Context
	clock  timeutil.Clock
	bucket gcs.Bucket
	mfs    *fuse.MountedFileSystem
	Dir    string

	// Files to close when tearing down. Nil entries are skipped.
	f1 *os.File
	f2 *os.File
}

var _ fsTestInterface = &fsTest{}

func (t *fsTest) setUpFSTest(cfg FSTestConfig) {
	var err error

	t.ctx = cfg.ctx
	t.clock = cfg.ServerConfig.Clock
	t.bucket = cfg.ServerConfig.Bucket

	// Set up ownership.
	cfg.ServerConfig.Uid, cfg.ServerConfig.Gid, err = perms.MyUserAndGroup()
	AssertEq(nil, err)

	// Set up permissions.
	cfg.ServerConfig.FilePerms = filePerms
	cfg.ServerConfig.DirPerms = dirPerms

	// Set up a temporary directory for mounting.
	t.Dir, err = ioutil.TempDir("", "fs_test")
	AssertEq(nil, err)

	// Create a file system server.
	server, err := fs.NewServer(&cfg.ServerConfig)
	AssertEq(nil, err)

	// Mount the file system.
	mountCfg := cfg.MountConfig
	mountCfg.OpContext = t.ctx

	t.mfs, err = fuse.Mount(t.Dir, server, &mountCfg)
	AssertEq(nil, err)
}

func (t *fsTest) tearDownFsTest() {
	var err error

	// Close any files we opened.
	if t.f1 != nil {
		ExpectEq(nil, t.f1.Close())
	}

	if t.f2 != nil {
		ExpectEq(nil, t.f2.Close())
	}

	// Unmount the file system. Try again on "resource busy" errors.
	delay := 10 * time.Millisecond
	for {
		err := fuse.Unmount(t.mfs.Dir())
		if err == nil {
			break
		}

		if strings.Contains(err.Error(), "resource busy") {
			log.Println("Resource busy error while unmounting; trying again")
			time.Sleep(delay)
			delay = time.Duration(1.3 * float64(delay))
			continue
		}

		AddFailure("MountedFileSystem.Unmount: %v", err)
		AbortTest()
	}

	if err := t.mfs.Join(t.ctx); err != nil {
		AssertEq(nil, err)
	}

	// Unlink the mount point.
	if err = os.Remove(t.Dir); err != nil {
		err = fmt.Errorf("Unlinking mount point: %v", err)
		return
	}
}

func (t *fsTest) createWithContents(name string, contents string) error {
	return t.createObjects(map[string]string{name: contents})
}

func (t *fsTest) createObjects(in map[string]string) error {
	err := gcsutil.CreateObjects(t.ctx, t.bucket, in)
	return err
}

func (t *fsTest) createEmptyObjects(names []string) error {
	err := gcsutil.CreateEmptyObjects(t.ctx, t.bucket, names)
	return err
}

// Ensure that the clock will report a different time after returning.
func (t *fsTest) advanceTime() {
	// For simulated clocks, we can just advance the time.
	if c, ok := t.clock.(*timeutil.SimulatedClock); ok {
		c.AdvanceTime(time.Second)
		return
	}

	// Otherwise, sleep a moment.
	time.Sleep(time.Millisecond)
}

// Return a matcher that matches event times as reported by the bucket
// corresponding to the supplied start time as measured by the test.
func (t *fsTest) matchesStartTime(start time.Time) oglematchers.Matcher {
	// For simulated clocks we can use exact equality.
	if _, ok := t.clock.(*timeutil.SimulatedClock); ok {
		return timeutil.TimeEq(start)
	}

	// Otherwise, we need to take into account latency between the start of our
	// call and the time the server actually executed the operation.
	const slop = 60 * time.Second
	return timeutil.TimeNear(start, slop)
}

// Repeatedly call fusetesting.ReadDirPicky until an error is encountered or
// until the result has the given length.
//
// This is a hacky workaround for the lack of list-after-write consistency in
// GCS that must be used when interacting with GCS through a side channel
// rather than through the file system. We set up some objects through a back
// door, then list repeatedly until we see the state we hope to see.
func (t *fsTest) readDirUntil(
	desiredLen int,
	dir string) (entries []os.FileInfo, err error) {
	startTime := time.Now()
	endTime := startTime.Add(5 * time.Second)

	for i := 0; ; i++ {
		entries, err = fusetesting.ReadDirPicky(dir)
		if err != nil || len(entries) == desiredLen {
			return
		}

		// Should we stop?
		if time.Now().After(endTime) {
			err = errors.New("Timeout waiting for the given length.")
			break
		}

		// Sleep for awhile.
		const baseDelay = 10 * time.Millisecond
		time.Sleep(time.Duration(math.Pow(1.3, float64(i)) * float64(baseDelay)))

		// If this is taking awhile, log that fact so that the user can tell why
		// the test is hanging.
		if time.Since(startTime) > time.Second {
			var names []string
			for _, fi := range entries {
				names = append(names, fi.Name())
			}

			log.Printf(
				"readDirUntil waiting for length %v. Current: %v, names: %v",
				desiredLen,
				len(entries),
				names)
		}
	}

	return
}

////////////////////////////////////////////////////////////////////////
// Common helpers
////////////////////////////////////////////////////////////////////////

func getFileNames(entries []os.FileInfo) (names []string) {
	for _, e := range entries {
		names = append(names, e.Name())
	}

	return
}

// REQUIRES: n % 4 == 0
func randString(n int) string {
	bytes := make([]byte, n)
	for i := 0; i < n; i += 4 {
		u32 := rand.Uint32()
		bytes[i] = byte(u32 >> 0)
		bytes[i+1] = byte(u32 >> 8)
		bytes[i+2] = byte(u32 >> 16)
		bytes[i+3] = byte(u32 >> 24)
	}

	return string(bytes)
}

func readRange(r io.ReadSeeker, offset int64, n int) (s string, err error) {
	if _, err = r.Seek(offset, 0); err != nil {
		return
	}

	bytes := make([]byte, n)
	if _, err = io.ReadFull(r, bytes); err != nil {
		return
	}

	s = string(bytes)
	return
}

func currentUid() uint32 {
	user, err := user.Current()
	AssertEq(nil, err)

	uid, err := strconv.ParseUint(user.Uid, 10, 32)
	AssertEq(nil, err)

	return uint32(uid)
}

func currentGid() uint32 {
	user, err := user.Current()
	AssertEq(nil, err)

	gid, err := strconv.ParseUint(user.Gid, 10, 32)
	AssertEq(nil, err)

	return uint32(gid)
}
<commit_msg>Groomed fs_test.go.<commit_after>// Copyright 2015 Google Inc. 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 fs_test

import (
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"math/rand"
	"os"
	"os/user"
	"strconv"
	"strings"
	"time"

	"github.com/googlecloudplatform/gcsfuse/fs"
	"github.com/googlecloudplatform/gcsfuse/perms"
	"github.com/googlecloudplatform/gcsfuse/timeutil"
	"github.com/jacobsa/fuse"
	"github.com/jacobsa/gcloud/gcs"
	"github.com/jacobsa/gcloud/gcs/gcsfake"
	"github.com/jacobsa/gcloud/gcs/gcsutil"
	. "github.com/jacobsa/ogletest"
	"golang.org/x/net/context"
)

const (
	filePerms os.FileMode = 0740
	dirPerms              = 0754
)

////////////////////////////////////////////////////////////////////////
// Boilerplate
////////////////////////////////////////////////////////////////////////

// A struct that can be embedded to inherit common file system test behaviors.
type fsTest struct {
	ctx context.Context

	// Configuration
	serverCfg fs.ServerConfig
	mountCfg  fuse.MountConfig

	// Dependencies
	clock  timeutil.SimulatedClock
	bucket gcs.Bucket

	// Mount information
	mfs *fuse.MountedFileSystem
	Dir string

	// Files to close when tearing down. Nil entries are skipped.
	f1 *os.File
	f2 *os.File
}

var _ SetUpInterface = &fsTest{}
var _ TearDownInterface = &fsTest{}

func (t *fsTest) SetUp(ti *TestInfo) {
	var err error
	t.ctx = ti.Ctx

	// Set up the clock.
	t.clock.SetTime(time.Date(2015, 4, 5, 2, 15, 0, 0, time.Local))

	// And the bucket.
	t.bucket = gcsfake.NewFakeBucket(&t.clock, "some_bucket")

	// Set up ownership.
	t.serverCfg.Uid, t.serverCfg.Gid, err = perms.MyUserAndGroup()
	AssertEq(nil, err)

	// Set up permissions.
	t.serverCfg.FilePerms = filePerms
	t.serverCfg.DirPerms = dirPerms

	// Use temporary space to speed tests.
	t.serverCfg.TempDirLimit = 1 << 27 // 128 MiB

	// Tests assume SupportNlink is enabled.
	t.serverCfg.SupportNlink = true

	// Set up a temporary directory for mounting.
	t.Dir, err = ioutil.TempDir("", "fs_test")
	AssertEq(nil, err)

	// Create a file system server.
	server, err := fs.NewServer(&t.serverCfg)
	AssertEq(nil, err)

	// Mount the file system.
	mountCfg := t.MountConfig
	mountCfg.OpContext = t.ctx

	t.mfs, err = fuse.Mount(t.Dir, server, &mountCfg)
	AssertEq(nil, err)
}

func (t *fsTest) TearDown() {
	var err error

	// Close any files we opened.
	if t.f1 != nil {
		ExpectEq(nil, t.f1.Close())
	}

	if t.f2 != nil {
		ExpectEq(nil, t.f2.Close())
	}

	// Unmount the file system. Try again on "resource busy" errors.
	delay := 10 * time.Millisecond
	for {
		err := fuse.Unmount(t.mfs.Dir())
		if err == nil {
			break
		}

		if strings.Contains(err.Error(), "resource busy") {
			log.Println("Resource busy error while unmounting; trying again")
			time.Sleep(delay)
			delay = time.Duration(1.3 * float64(delay))
			continue
		}

		AddFailure("MountedFileSystem.Unmount: %v", err)
		AbortTest()
	}

	if err := t.mfs.Join(t.ctx); err != nil {
		AssertEq(nil, err)
	}

	// Unlink the mount point.
	if err = os.Remove(t.Dir); err != nil {
		err = fmt.Errorf("Unlinking mount point: %v", err)
		return
	}
}

func (t *fsTest) createWithContents(name string, contents string) error {
	return t.createObjects(map[string]string{name: contents})
}

func (t *fsTest) createObjects(in map[string]string) error {
	err := gcsutil.CreateObjects(t.ctx, t.bucket, in)
	return err
}

func (t *fsTest) createEmptyObjects(names []string) error {
	err := gcsutil.CreateEmptyObjects(t.ctx, t.bucket, names)
	return err
}

////////////////////////////////////////////////////////////////////////
// Common helpers
////////////////////////////////////////////////////////////////////////

func getFileNames(entries []os.FileInfo) (names []string) {
	for _, e := range entries {
		names = append(names, e.Name())
	}

	return
}

// REQUIRES: n % 4 == 0
func randString(n int) string {
	bytes := make([]byte, n)
	for i := 0; i < n; i += 4 {
		u32 := rand.Uint32()
		bytes[i] = byte(u32 >> 0)
		bytes[i+1] = byte(u32 >> 8)
		bytes[i+2] = byte(u32 >> 16)
		bytes[i+3] = byte(u32 >> 24)
	}

	return string(bytes)
}

func readRange(r io.ReadSeeker, offset int64, n int) (s string, err error) {
	if _, err = r.Seek(offset, 0); err != nil {
		return
	}

	bytes := make([]byte, n)
	if _, err = io.ReadFull(r, bytes); err != nil {
		return
	}

	s = string(bytes)
	return
}

func currentUid() uint32 {
	user, err := user.Current()
	AssertEq(nil, err)

	uid, err := strconv.ParseUint(user.Uid, 10, 32)
	AssertEq(nil, err)

	return uint32(uid)
}

func currentGid() uint32 {
	user, err := user.Current()
	AssertEq(nil, err)

	gid, err := strconv.ParseUint(user.Gid, 10, 32)
	AssertEq(nil, err)

	return uint32(gid)
}
<|endoftext|>


<commit_before>// Copyright 2016 The NorthShore Authors 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 fsm

import (
	"errors"
	"log"

	"github.com/looplab/fsm"
)

// BlueprintState represents a state of the Blueprint
type BlueprintState string

// StageState represents a state of the Stage
type StageState string

const (
	// BlueprintStateNew is default state of the Blueprint
	BlueprintStateNew BlueprintState = "new"
	// BlueprintStateProvision is the Blueprint status while provisioning
	BlueprintStateProvision BlueprintState = "provision"
	// BlueprintStateActive is the Blueprint status when all Stages are up and ready
	BlueprintStateActive BlueprintState = "active"
	// BlueprintStateInactive is the Blueprint status when some Stage is down
	BlueprintStateInactive BlueprintState = "inactive"
)

const (
	// StageStateNew is default state of the Stage
	StageStateNew StageState = "new"
	// StageStateCreated indicates that container is created
	StageStateCreated StageState = "created"
	// StageStateRunning indicates that container is running
	StageStateRunning StageState = "running"
	// StageStatePaused indicates that container is paused
	StageStatePaused StageState = "paused"
	// StageStateStopped indicates that container is stopped
	StageStateStopped StageState = "stopped"
	// StageStateDeleted indicates that container is deleted
	StageStateDeleted StageState = "deleted"
)

// BlueprintFSM represents a finite state machine of Blueprint
type BlueprintFSM struct {
	// State is current Blueprint status
	State BlueprintState `json:"state"`
	// StagesStates represents statuses of Blueprint Stages
	StagesStates map[string]StageState `json:"stagesStates"`
	// FSM is the finite state machine
	FSM *fsm.FSM `json:"-"`
}

// NewBlueprintFSM constructs a Blueprint states data
func NewBlueprintFSM(stages []string) *BlueprintFSM {
	stagesStates := map[string]StageState{}
	for _, v := range stages {
		stagesStates[v] = StageStateNew
	}

	bpFSM := &BlueprintFSM{
		BlueprintStateNew,
		stagesStates,
		nil,
	}

	// https://godoc.org/github.com/looplab/fsm#NewFSM
	bpFSM.FSM = fsm.NewFSM(
		string(BlueprintStateNew),
		fsm.Events{
			{
				Name: "activate",
				Src:  []string{string(BlueprintStateInactive), string(BlueprintStateProvision)},
				Dst:  string(BlueprintStateActive),
			},
			{
				Name: "inactivate",
				Src:  []string{string(BlueprintStateActive), string(BlueprintStateProvision)},
				Dst:  string(BlueprintStateInactive),
			},
			{
				Name: "start",
				Src:  []string{string(BlueprintStateNew)},
				Dst:  string(BlueprintStateProvision),
			},
		},
		fsm.Callbacks{
			"before_activate": func(e *fsm.Event) { bpFSM.beforeActivate(e) },
			"after_event":     func(e *fsm.Event) { bpFSM.afterEvent(e) },
			"activate":        func(e *fsm.Event) { bpFSM.afterActivate(e) },
			"inactivate":      func(e *fsm.Event) { bpFSM.afterInactivate(e) },
			"start":           func(e *fsm.Event) { bpFSM.afterStart(e) },
		},
	)

	return bpFSM
}

func (bpFSM *BlueprintFSM) afterEvent(e *fsm.Event) {
	log.Printf("#BlueprintFSM,#afterEvent %+v %+v", e, bpFSM)
}

func (bpFSM *BlueprintFSM) beforeActivate(e *fsm.Event) {
	for _, v := range bpFSM.StagesStates {
		if v != StageStateRunning {
			e.Cancel(errors.New("Some stage isn't running"))
		}
	}
}

func (bpFSM *BlueprintFSM) afterActivate(e *fsm.Event) {
	bpFSM.State = BlueprintStateActive
}

func (bpFSM *BlueprintFSM) afterInactivate(e *fsm.Event) {
	bpFSM.State = BlueprintStateInactive
}

func (bpFSM *BlueprintFSM) afterStart(e *fsm.Event) {
	bpFSM.State = BlueprintStateProvision
}

// Start creates and runs Stages in Blueprint Pipeline
func (bpFSM *BlueprintFSM) Start() {
	bpFSM.FSM.Event("start")
}

// Update updates current Blueprint Pipeline status with Stages
func (bpFSM *BlueprintFSM) Update(stagesStates map[string]StageState) {
	event := "activate"
	for k, v := range stagesStates {
		bpFSM.StagesStates[k] = v
		switch v {
		case
			StageStatePaused,
			StageStateStopped,
			StageStateDeleted:
			event = "inactivate"
		}
	}

	err := bpFSM.FSM.Event(event, stagesStates)
	if err != nil {
		log.Println("#BlueprintFSM,#Update,#Error", err)
	}
}
<commit_msg>[fsm] Obtain state from FSM instead of control state by FSM<commit_after>// Copyright 2016 The NorthShore Authors 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 fsm

import (
	"errors"
	"log"

	"github.com/looplab/fsm"
)

// BlueprintState represents a state of the Blueprint
type BlueprintState string

// StageState represents a state of the Stage
type StageState string

const (
	// BlueprintStateNew is default state of the Blueprint
	BlueprintStateNew BlueprintState = "new"
	// BlueprintStateProvision is the Blueprint status while provisioning
	BlueprintStateProvision BlueprintState = "provision"
	// BlueprintStateActive is the Blueprint status when all Stages are up and ready
	BlueprintStateActive BlueprintState = "active"
	// BlueprintStateInactive is the Blueprint status when some Stage is down
	BlueprintStateInactive BlueprintState = "inactive"
)

const (
	// StageStateNew is default state of the Stage
	StageStateNew StageState = "new"
	// StageStateCreated indicates that container is created
	StageStateCreated StageState = "created"
	// StageStateRunning indicates that container is running
	StageStateRunning StageState = "running"
	// StageStatePaused indicates that container is paused
	StageStatePaused StageState = "paused"
	// StageStateStopped indicates that container is stopped
	StageStateStopped StageState = "stopped"
	// StageStateDeleted indicates that container is deleted
	StageStateDeleted StageState = "deleted"
)

// BlueprintFSM represents a finite state machine of Blueprint
type BlueprintFSM struct {
	// State is current Blueprint status
	State BlueprintState `json:"state"`
	// StagesStates represents statuses of Blueprint Stages
	StagesStates map[string]StageState `json:"stagesStates"`
	// FSM is the finite state machine
	FSM *fsm.FSM `json:"-"`
}

// NewBlueprintFSM constructs a Blueprint states data
func NewBlueprintFSM(stages []string) *BlueprintFSM {
	stagesStates := map[string]StageState{}
	for _, v := range stages {
		stagesStates[v] = StageStateNew
	}

	bpFSM := &BlueprintFSM{
		BlueprintStateNew,
		stagesStates,
		nil,
	}

	// https://godoc.org/github.com/looplab/fsm#NewFSM
	bpFSM.FSM = fsm.NewFSM(
		string(BlueprintStateNew),
		fsm.Events{
			{
				Name: "activate",
				Src:  []string{string(BlueprintStateInactive), string(BlueprintStateProvision)},
				Dst:  string(BlueprintStateActive),
			},
			{
				Name: "inactivate",
				Src:  []string{string(BlueprintStateActive), string(BlueprintStateProvision)},
				Dst:  string(BlueprintStateInactive),
			},
			{
				Name: "start",
				Src:  []string{string(BlueprintStateNew)},
				Dst:  string(BlueprintStateProvision),
			},
		},
		fsm.Callbacks{
			"before_activate": func(e *fsm.Event) { bpFSM.beforeActivate(e) },
			"after_event":     func(e *fsm.Event) { bpFSM.afterEvent(e) },
		},
	)

	return bpFSM
}

func (bpFSM *BlueprintFSM) afterEvent(e *fsm.Event) {
	bpFSM.State = BlueprintState(bpFSM.FSM.Current())
}

func (bpFSM *BlueprintFSM) beforeActivate(e *fsm.Event) {
	for _, v := range bpFSM.StagesStates {
		if v != StageStateRunning {
			e.Cancel(errors.New("Some stage isn't running"))
		}
	}
}

// Start creates and runs Stages in Blueprint Pipeline
func (bpFSM *BlueprintFSM) Start() {
	bpFSM.FSM.Event("start")
}

// Update updates current Blueprint Pipeline status with Stages
func (bpFSM *BlueprintFSM) Update(stagesStates map[string]StageState) {
	event := "activate"
	for k, v := range stagesStates {
		bpFSM.StagesStates[k] = v
		switch v {
		case
			StageStatePaused,
			StageStateStopped,
			StageStateDeleted:
			event = "inactivate"
		}
	}

	err := bpFSM.FSM.Event(event, stagesStates)
	if err != nil {
		log.Println("#BlueprintFSM,#Update,#Error", err)
	}
}
<|endoftext|>


<commit_before>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package console

import (
	"fmt"
	"io"
	"regexp"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"

	expect "github.com/google/goexpect"
	"google.golang.org/grpc/codes"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/client-go/log"
)

const (
	PromptExpression = `(\$ |\# )`
	CRLF             = "\r\n"
	UTFPosEscape     = "\u001b\\[[0-9]+;[0-9]+H"
)

var (
	shellSuccess = regexp.MustCompile(RetValue("0"))
	shellFail    = regexp.MustCompile(RetValue("[1-9].*"))
)

// ExpectBatch runs the batch from `expected` connecting to the `vmi` console and
// wait `timeout` for the batch to return.
// NOTE: there is a safer version that validates sended commands `SafeExpectBatch` refer to it about limitations.
func ExpectBatch(vmi *v1.VirtualMachineInstance, expected []expect.Batcher, timeout time.Duration) error {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		return err
	}

	expecter, _, err := NewExpecter(virtClient, vmi, 30*time.Second)
	if err != nil {
		return err
	}
	defer expecter.Close()

	resp, err := expecter.ExpectBatch(expected, timeout)
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("%v", resp)
	}
	return err
}

// SafeExpectBatch runs the batch from `expected` connecting to a VMI's console and
// wait `timeout` for the batch to return, it also check that the sended commands arrives to console checking.
// NOTE: This functions heritage limitations from `ExpectBatchWithValidatedSend` refer to it to check them.
func SafeExpectBatch(vmi *v1.VirtualMachineInstance, expected []expect.Batcher, wait int) error {
	_, err := SafeExpectBatchWithResponse(vmi, expected, wait)
	return err
}

// SafeExpectBatchWithResponse runs the batch from `expected` connecting to a VMI's console and
// wait `timeout` for the batch to return with a response.
// It includes a safety check which validates that the commands arrive to the console.
// NOTE: This functions inherits limitations from `ExpectBatchWithValidatedSend`, refer to it for more information.
func SafeExpectBatchWithResponse(vmi *v1.VirtualMachineInstance, expected []expect.Batcher, wait int) ([]expect.BatchRes, error) {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		panic(err)
	}
	expecter, _, err := NewExpecter(virtClient, vmi, 30*time.Second)
	if err != nil {
		return nil, err
	}
	defer expecter.Close()

	resp, err := ExpectBatchWithValidatedSend(expecter, expected, time.Second*time.Duration(wait))
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("%v", resp)
	}
	return resp, err
}

// RunCommand runs the command line from `command connecting to an already logged in console at vmi
// and wait `timeout` for command to return.
// NOTE: The safer version `ExpectBatchWithValidatedSend` is not used here since it does not support cases.
func RunCommand(vmi *v1.VirtualMachineInstance, command string, timeout time.Duration) error {
	err := ExpectBatch(vmi, []expect.Batcher{
		&expect.BSnd{S: "\n"},
		&expect.BExp{R: PromptExpression},
		&expect.BSnd{S: command + "\n"},
		&expect.BExp{R: PromptExpression},
		&expect.BSnd{S: "echo $?\n"},
		&expect.BCas{C: []expect.Caser{
			&expect.Case{
				R: shellSuccess,
				T: expect.OK(),
			},
			&expect.Case{
				R: shellFail,
				T: expect.Fail(expect.NewStatus(codes.Unavailable, command+" failed")),
			},
		}},
	}, timeout)
	if err != nil {
		return fmt.Errorf("Failed to run [%s] at VMI %s, error: %v", command, vmi.Name, err)
	}
	return nil
}

// SecureBootExpecter should be called on a VMI that has EFI enabled
// It will parse the kernel output (dmesg) and succeed if it finds that Secure boot is enabled
func SecureBootExpecter(vmi *v1.VirtualMachineInstance) error {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		return err
	}
	expecter, _, err := NewExpecter(virtClient, vmi, 10*time.Second)
	if err != nil {
		return err
	}
	defer expecter.Close()

	b := append([]expect.Batcher{
		&expect.BExp{R: "secureboot: Secure boot enabled"},
	})
	res, err := expecter.ExpectBatch(b, 180*time.Second)
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("Kernel: %+v", res)
		return err
	}

	return err
}

// NetBootExpecter should be called on a VMI that has BIOS serial logging enabled
// It will parse the SeaBIOS output and succeed if it finds the string "iPXE"
func NetBootExpecter(vmi *v1.VirtualMachineInstance) error {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		return err
	}
	expecter, _, err := NewExpecter(virtClient, vmi, 10*time.Second)
	if err != nil {
		return err
	}
	defer expecter.Close()

	esc := UTFPosEscape
	b := append([]expect.Batcher{
		// SeaBIOS uses escape (\u001b) combinations for letter placement on screen
		// The regex below effectively grep for "iPXE" while ignoring those
		//&expect.BExp{R: "\u001b\\[7;27Hi\u001b\\[7;28HP\u001b\\[7;29HX\u001b\\[7;30HE"},
		&expect.BExp{R: esc + "i" + esc + "P" + esc + "X" + esc + "E"},
	})
	res, err := expecter.ExpectBatch(b, 30*time.Second)
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("BIOS: %+v", res)
		return err
	}

	return err
}

// NewExpecter will connect to an already logged in VMI console and return the generated expecter it will wait `timeout` for the connection.
func NewExpecter(virtCli kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance, timeout time.Duration, opts ...expect.Option) (expect.Expecter, <-chan error, error) {
	vmiReader, vmiWriter := io.Pipe()
	expecterReader, expecterWriter := io.Pipe()
	resCh := make(chan error)

	startTime := time.Now()
	con, err := virtCli.VirtualMachineInstance(vmi.Namespace).SerialConsole(vmi.Name, &kubecli.SerialConsoleOptions{ConnectionTimeout: timeout})
	if err != nil {
		return nil, nil, err
	}
	timeout = timeout - time.Now().Sub(startTime)

	go func() {
		resCh <- con.Stream(kubecli.StreamOptions{
			In:  vmiReader,
			Out: expecterWriter,
		})
	}()

	opts = append(opts, expect.SendTimeout(timeout))
	opts = append(opts, expect.Verbose(true))
	opts = append(opts, expect.VerboseWriter(GinkgoWriter))
	return expect.SpawnGeneric(&expect.GenOptions{
		In:  vmiWriter,
		Out: expecterReader,
		Wait: func() error {
			return <-resCh
		},
		Close: func() error {
			expecterWriter.Close()
			vmiReader.Close()
			return nil
		},
		Check: func() bool { return true },
	}, timeout, opts...)
}

// ExpectBatchWithValidatedSend adds the expect.BSnd command to the exect.BExp expression.
// It is done to make sure the match was found in the result of the expect.BSnd
// command and not in a leftover that wasn't removed from the buffer.
// NOTE: the method contains the following limitations:
//       - Use of `BatchSwitchCase`
//       - Multiline commands
//       - No more than one sequential send or receive
func ExpectBatchWithValidatedSend(expecter expect.Expecter, batch []expect.Batcher, timeout time.Duration) ([]expect.BatchRes, error) {
	sendFlag := false
	expectFlag := false
	previousSend := ""

	if len(batch) < 2 {
		return nil, fmt.Errorf("ExpectBatchWithValidatedSend requires at least 2 batchers, supplied %v", batch)
	}

	for i, batcher := range batch {
		switch batcher.Cmd() {
		case expect.BatchExpect:
			if expectFlag == true {
				return nil, fmt.Errorf("Two sequential expect.BExp are not allowed")
			}
			expectFlag = true
			sendFlag = false
			if _, ok := batch[i].(*expect.BExp); !ok {
				return nil, fmt.Errorf("ExpectBatchWithValidatedSend support only expect of type BExp")
			}
			bExp, _ := batch[i].(*expect.BExp)
			previousSend := regexp.QuoteMeta(previousSend)

			// Remove the \n since it is translated by the console to \r\n.
			previousSend = strings.TrimSuffix(previousSend, "\n")
			bExp.R = fmt.Sprintf("%s%s%s", previousSend, "((?s).*)", bExp.R)
			previousSend = ""
		case expect.BatchSend:
			if sendFlag == true {
				return nil, fmt.Errorf("Two sequential expect.BSend are not allowed")
			}
			sendFlag = true
			expectFlag = false
			previousSend = batcher.Arg()
		case expect.BatchSwitchCase:
			return nil, fmt.Errorf("ExpectBatchWithValidatedSend doesn't support BatchSwitchCase")
		default:
			return nil, fmt.Errorf("Unkown command: ExpectBatchWithValidatedSend supports only BatchExpect and BatchSend")
		}
	}

	res, err := expecter.ExpectBatch(batch, timeout)
	return res, err
}

func RetValue(retcode string) string {
	return "\n" + retcode + CRLF + ".*" + PromptExpression
}

type VMIExpecterFactory func(*v1.VirtualMachineInstance) (expect.Expecter, error)
<commit_msg>tests, console: Remove VMIExpecterFactory<commit_after>/*
 * This file is part of the KubeVirt project
 *
 * 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.
 *
 * Copyright 2020 Red Hat, Inc.
 *
 */

package console

import (
	"fmt"
	"io"
	"regexp"
	"strings"
	"time"

	. "github.com/onsi/ginkgo"

	expect "github.com/google/goexpect"
	"google.golang.org/grpc/codes"

	v1 "kubevirt.io/client-go/api/v1"
	"kubevirt.io/client-go/kubecli"
	"kubevirt.io/client-go/log"
)

const (
	PromptExpression = `(\$ |\# )`
	CRLF             = "\r\n"
	UTFPosEscape     = "\u001b\\[[0-9]+;[0-9]+H"
)

var (
	shellSuccess = regexp.MustCompile(RetValue("0"))
	shellFail    = regexp.MustCompile(RetValue("[1-9].*"))
)

// ExpectBatch runs the batch from `expected` connecting to the `vmi` console and
// wait `timeout` for the batch to return.
// NOTE: there is a safer version that validates sended commands `SafeExpectBatch` refer to it about limitations.
func ExpectBatch(vmi *v1.VirtualMachineInstance, expected []expect.Batcher, timeout time.Duration) error {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		return err
	}

	expecter, _, err := NewExpecter(virtClient, vmi, 30*time.Second)
	if err != nil {
		return err
	}
	defer expecter.Close()

	resp, err := expecter.ExpectBatch(expected, timeout)
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("%v", resp)
	}
	return err
}

// SafeExpectBatch runs the batch from `expected` connecting to a VMI's console and
// wait `timeout` for the batch to return, it also check that the sended commands arrives to console checking.
// NOTE: This functions heritage limitations from `ExpectBatchWithValidatedSend` refer to it to check them.
func SafeExpectBatch(vmi *v1.VirtualMachineInstance, expected []expect.Batcher, wait int) error {
	_, err := SafeExpectBatchWithResponse(vmi, expected, wait)
	return err
}

// SafeExpectBatchWithResponse runs the batch from `expected` connecting to a VMI's console and
// wait `timeout` for the batch to return with a response.
// It includes a safety check which validates that the commands arrive to the console.
// NOTE: This functions inherits limitations from `ExpectBatchWithValidatedSend`, refer to it for more information.
func SafeExpectBatchWithResponse(vmi *v1.VirtualMachineInstance, expected []expect.Batcher, wait int) ([]expect.BatchRes, error) {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		panic(err)
	}
	expecter, _, err := NewExpecter(virtClient, vmi, 30*time.Second)
	if err != nil {
		return nil, err
	}
	defer expecter.Close()

	resp, err := ExpectBatchWithValidatedSend(expecter, expected, time.Second*time.Duration(wait))
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("%v", resp)
	}
	return resp, err
}

// RunCommand runs the command line from `command connecting to an already logged in console at vmi
// and wait `timeout` for command to return.
// NOTE: The safer version `ExpectBatchWithValidatedSend` is not used here since it does not support cases.
func RunCommand(vmi *v1.VirtualMachineInstance, command string, timeout time.Duration) error {
	err := ExpectBatch(vmi, []expect.Batcher{
		&expect.BSnd{S: "\n"},
		&expect.BExp{R: PromptExpression},
		&expect.BSnd{S: command + "\n"},
		&expect.BExp{R: PromptExpression},
		&expect.BSnd{S: "echo $?\n"},
		&expect.BCas{C: []expect.Caser{
			&expect.Case{
				R: shellSuccess,
				T: expect.OK(),
			},
			&expect.Case{
				R: shellFail,
				T: expect.Fail(expect.NewStatus(codes.Unavailable, command+" failed")),
			},
		}},
	}, timeout)
	if err != nil {
		return fmt.Errorf("Failed to run [%s] at VMI %s, error: %v", command, vmi.Name, err)
	}
	return nil
}

// SecureBootExpecter should be called on a VMI that has EFI enabled
// It will parse the kernel output (dmesg) and succeed if it finds that Secure boot is enabled
func SecureBootExpecter(vmi *v1.VirtualMachineInstance) error {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		return err
	}
	expecter, _, err := NewExpecter(virtClient, vmi, 10*time.Second)
	if err != nil {
		return err
	}
	defer expecter.Close()

	b := append([]expect.Batcher{
		&expect.BExp{R: "secureboot: Secure boot enabled"},
	})
	res, err := expecter.ExpectBatch(b, 180*time.Second)
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("Kernel: %+v", res)
		return err
	}

	return err
}

// NetBootExpecter should be called on a VMI that has BIOS serial logging enabled
// It will parse the SeaBIOS output and succeed if it finds the string "iPXE"
func NetBootExpecter(vmi *v1.VirtualMachineInstance) error {
	virtClient, err := kubecli.GetKubevirtClient()
	if err != nil {
		return err
	}
	expecter, _, err := NewExpecter(virtClient, vmi, 10*time.Second)
	if err != nil {
		return err
	}
	defer expecter.Close()

	esc := UTFPosEscape
	b := append([]expect.Batcher{
		// SeaBIOS uses escape (\u001b) combinations for letter placement on screen
		// The regex below effectively grep for "iPXE" while ignoring those
		//&expect.BExp{R: "\u001b\\[7;27Hi\u001b\\[7;28HP\u001b\\[7;29HX\u001b\\[7;30HE"},
		&expect.BExp{R: esc + "i" + esc + "P" + esc + "X" + esc + "E"},
	})
	res, err := expecter.ExpectBatch(b, 30*time.Second)
	if err != nil {
		log.DefaultLogger().Object(vmi).Infof("BIOS: %+v", res)
		return err
	}

	return err
}

// NewExpecter will connect to an already logged in VMI console and return the generated expecter it will wait `timeout` for the connection.
func NewExpecter(virtCli kubecli.KubevirtClient, vmi *v1.VirtualMachineInstance, timeout time.Duration, opts ...expect.Option) (expect.Expecter, <-chan error, error) {
	vmiReader, vmiWriter := io.Pipe()
	expecterReader, expecterWriter := io.Pipe()
	resCh := make(chan error)

	startTime := time.Now()
	con, err := virtCli.VirtualMachineInstance(vmi.Namespace).SerialConsole(vmi.Name, &kubecli.SerialConsoleOptions{ConnectionTimeout: timeout})
	if err != nil {
		return nil, nil, err
	}
	timeout = timeout - time.Now().Sub(startTime)

	go func() {
		resCh <- con.Stream(kubecli.StreamOptions{
			In:  vmiReader,
			Out: expecterWriter,
		})
	}()

	opts = append(opts, expect.SendTimeout(timeout))
	opts = append(opts, expect.Verbose(true))
	opts = append(opts, expect.VerboseWriter(GinkgoWriter))
	return expect.SpawnGeneric(&expect.GenOptions{
		In:  vmiWriter,
		Out: expecterReader,
		Wait: func() error {
			return <-resCh
		},
		Close: func() error {
			expecterWriter.Close()
			vmiReader.Close()
			return nil
		},
		Check: func() bool { return true },
	}, timeout, opts...)
}

// ExpectBatchWithValidatedSend adds the expect.BSnd command to the exect.BExp expression.
// It is done to make sure the match was found in the result of the expect.BSnd
// command and not in a leftover that wasn't removed from the buffer.
// NOTE: the method contains the following limitations:
//       - Use of `BatchSwitchCase`
//       - Multiline commands
//       - No more than one sequential send or receive
func ExpectBatchWithValidatedSend(expecter expect.Expecter, batch []expect.Batcher, timeout time.Duration) ([]expect.BatchRes, error) {
	sendFlag := false
	expectFlag := false
	previousSend := ""

	if len(batch) < 2 {
		return nil, fmt.Errorf("ExpectBatchWithValidatedSend requires at least 2 batchers, supplied %v", batch)
	}

	for i, batcher := range batch {
		switch batcher.Cmd() {
		case expect.BatchExpect:
			if expectFlag == true {
				return nil, fmt.Errorf("Two sequential expect.BExp are not allowed")
			}
			expectFlag = true
			sendFlag = false
			if _, ok := batch[i].(*expect.BExp); !ok {
				return nil, fmt.Errorf("ExpectBatchWithValidatedSend support only expect of type BExp")
			}
			bExp, _ := batch[i].(*expect.BExp)
			previousSend := regexp.QuoteMeta(previousSend)

			// Remove the \n since it is translated by the console to \r\n.
			previousSend = strings.TrimSuffix(previousSend, "\n")
			bExp.R = fmt.Sprintf("%s%s%s", previousSend, "((?s).*)", bExp.R)
			previousSend = ""
		case expect.BatchSend:
			if sendFlag == true {
				return nil, fmt.Errorf("Two sequential expect.BSend are not allowed")
			}
			sendFlag = true
			expectFlag = false
			previousSend = batcher.Arg()
		case expect.BatchSwitchCase:
			return nil, fmt.Errorf("ExpectBatchWithValidatedSend doesn't support BatchSwitchCase")
		default:
			return nil, fmt.Errorf("Unkown command: ExpectBatchWithValidatedSend supports only BatchExpect and BatchSend")
		}
	}

	res, err := expecter.ExpectBatch(batch, timeout)
	return res, err
}

func RetValue(retcode string) string {
	return "\n" + retcode + CRLF + ".*" + PromptExpression
}
<|endoftext|>


<commit_before>package gen

import (
	"reflect"

	"github.com/leanovate/gopter"
)

// PtrOf generates a pointer to a generated element
func PtrOf(elementGen gopter.Gen) gopter.Gen {
	return func(genParams *gopter.GenParameters) *gopter.GenResult {
		element := elementGen(genParams)
		elementShrinker := element.Shrinker
		elementSieve := element.Sieve
		value, ok := element.Retrieve()
		if !ok || genParams.NextBool() {
			result := gopter.NewEmptyResult(reflect.PtrTo(element.ResultType))
			result.Sieve = func(v interface{}) bool {
				if elementSieve == nil {
					return true
				}
				r := reflect.ValueOf(v)
				return !r.IsValid() || r.IsNil() || elementSieve(r.Elem().Interface())
			}
			return result
		}
		// To get the right pointer type we have to create a slice with one element
		slice := reflect.MakeSlice(reflect.SliceOf(element.ResultType), 0, 1)
		slice = reflect.Append(slice, reflect.ValueOf(value))

		result := gopter.NewGenResult(slice.Index(0).Addr().Interface(), PtrShrinker(elementShrinker))
		result.Sieve = func(v interface{}) bool {
			if elementSieve == nil {
				return true
			}
			r := reflect.ValueOf(v)
			return !r.IsValid() || r.IsNil() || elementSieve(r.Elem().Interface())
		}
		return result
	}
}
<commit_msg>Improve docstring for PtrOf generator<commit_after>package gen

import (
	"reflect"

	"github.com/leanovate/gopter"
)

// PtrOf generates either a pointer to a generated element or a nil pointer
func PtrOf(elementGen gopter.Gen) gopter.Gen {
	return func(genParams *gopter.GenParameters) *gopter.GenResult {
		element := elementGen(genParams)
		elementShrinker := element.Shrinker
		elementSieve := element.Sieve
		value, ok := element.Retrieve()
		if !ok || genParams.NextBool() {
			result := gopter.NewEmptyResult(reflect.PtrTo(element.ResultType))
			result.Sieve = func(v interface{}) bool {
				if elementSieve == nil {
					return true
				}
				r := reflect.ValueOf(v)
				return !r.IsValid() || r.IsNil() || elementSieve(r.Elem().Interface())
			}
			return result
		}
		// To get the right pointer type we have to create a slice with one element
		slice := reflect.MakeSlice(reflect.SliceOf(element.ResultType), 0, 1)
		slice = reflect.Append(slice, reflect.ValueOf(value))

		result := gopter.NewGenResult(slice.Index(0).Addr().Interface(), PtrShrinker(elementShrinker))
		result.Sieve = func(v interface{}) bool {
			if elementSieve == nil {
				return true
			}
			r := reflect.ValueOf(v)
			return !r.IsValid() || r.IsNil() || elementSieve(r.Elem().Interface())
		}
		return result
	}
}
<|endoftext|>


<commit_before>package gifexplode

import (
	"appengine"

	"bytes"
	"encoding/base64"
	"fmt"
	"html/template"
	"image/gif"
	"image/png"
	"net/http"
)

const (
	bufSize = 2 << 17
)

var tmpl = template.Must(template.New("tmpl").Parse(`

{{range .Frames}}

{{end}}

`))

func init() {
	http.HandleFunc("/upload", upload)
}

func upload(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	f, _, err := r.FormFile("file")
	if err != nil {
		c.Errorf("formfile: %v", err)
		http.Error(w, "No file specified", http.StatusBadRequest)
		return
	}
	defer f.Close()
	g, err := gif.DecodeAll(f)
	if err != nil {
		c.Errorf("gif decode: %v", err)
		http.Error(w, "Error decoding GIF", http.StatusBadRequest)
		return
	}
	fs := []string{}
	for _, i := range g.Image {
		buf := bytes.NewBuffer(make([]byte, 0, bufSize))
		// TODO: Upgrade to go1.2 and gif.Encode
		err = png.Encode(buf, i)
		if err != nil {
			c.Errorf("png encode: %v", err)
			http.Error(w, "Error encoding", http.StatusInternalServerError)
			return
		}
		dataURI := fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(buf.Bytes()))
		fmt.Println(dataURI)
		fs = append(fs, dataURI)
	}
	tmpl.Execute(w, struct {
		Frames []string
	}{fs})
}
<commit_msg>It works sort of<commit_after>package gifexplode

import (
	"appengine"

	"bytes"
	"encoding/base64"
	"fmt"
	"image/gif"
	"image/png"
	"net/http"
	"text/template"
)

const (
	bufSize = 2 << 17
)

var tmpl = template.Must(template.New("tmpl").Parse(`

  {{range .Frames}}

  {{end}}

`))

func init() {
	http.HandleFunc("/upload", upload)
}

func upload(w http.ResponseWriter, r *http.Request) {
	c := appengine.NewContext(r)
	mpf, _, err := r.FormFile("file")
	//	if err != nil {
	//		c.Errorf("formfile: %v", err)
	//		http.Error(w, "No file specified", http.StatusBadRequest)
	//		return
	//	}
	defer mpf.Close()
	g, err := gif.DecodeAll(mpf)
	if err != nil {
		c.Errorf("gif decode: %v", err)
		http.Error(w, "Error decoding GIF", http.StatusBadRequest)
		return
	}
	fs := make([]string, 0, len(g.Image))
	for _, i := range g.Image {
		buf := bytes.NewBuffer(make([]byte, 0, bufSize))
		// TODO: Upgrade to go1.2 and gif.Encode
		err = png.Encode(buf, i)
		if err != nil {
			c.Errorf("png encode: %v", err)
			http.Error(w, "Error encoding", http.StatusInternalServerError)
			return
		}
		fs = append(fs, fmt.Sprintf("data:image/png;base64,%s", base64.StdEncoding.EncodeToString(buf.Bytes())))
	}
	tmpl.Execute(w, struct {
		Frames []string
	}{fs})
}
<|endoftext|>


<commit_before>package github

import (
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
)

func (g GitHub) DcoVerified(prHook *octokat.PullRequestHook) (bool, error) {
	// we only want the prs that are opened/synchronized
	if !prHook.IsOpened() && !prHook.IsSynchronize() {
		return false, nil
	}

	// get the PR
	pr := prHook.PullRequest
	repo := getRepo(prHook.Repo)

	// check if this is a bump branch, then we don't want to check sig
	if pr.Base.Ref == "release" {
		return true, nil
	}

	content, err := g.getContent(repo, prHook.Number, true)
	if err != nil {
		return false, err
	}

	// we only want apply labels
	// to opened pull requests
	var labels []string

	//check if it's a proposal
	isProposal := strings.Contains(strings.ToLower(pr.Title), "proposal")
	switch {
	case isProposal:
		labels = []string{"status/1-needs-design-review"}
	case content.IsDocsOnly():
		labels = []string{"status/3-needs-docs-review"}
	default:
		labels = []string{"status/0-needs-triage"}
	}

	if labelOs(pr, "windows", content.OnlyWindows) {
		labels = append(labels, "os/windows")
	}
	if labelOs(pr, "freebsd", content.OnlyFreebsd) {
		labels = append(labels, "os/freebsd")
	}

	// add labels if there are any
	// only add labels to new PRs not sync
	if len(labels) > 0 && prHook.IsOpened() {
		log.Debugf("Adding labels %#v to pr %d", labels, prHook.Number)

		if err := g.addLabel(repo, prHook.Number, labels...); err != nil {
			return false, err
		}

		log.Infof("Added labels %#v to pr %d", labels, prHook.Number)
	}

	var verified bool

	if content.CommitsSigned() {
		if err := g.removeLabel(repo, prHook.Number, "dco/no"); err != nil {
			return false, err
		}

		if err := g.removeComment(repo, pr, "sign your commits", content); err != nil {
			return false, err
		}

		if err := g.successStatus(repo, pr.Head.Sha, "docker/dco-signed", "All commits signed"); err != nil {
			return false, err
		}

		verified = true
	} else {
		if err := g.addLabel(repo, prHook.Number, "dco/no"); err != nil {
			return false, err
		}

		if err := g.addDCOUnsignedComment(repo, pr, content); err != nil {
			return false, err
		}

		if err := g.failureStatus(repo, pr.Head.Sha, "docker/dco-signed", "Some commits without signature", "https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work"); err != nil {
			return false, err
		}
	}

	return verified, nil
}

func getRepo(repo *octokat.Repository) octokat.Repo {
	return octokat.Repo{
		Name:     repo.Name,
		UserName: repo.Owner.Login,
	}
}

func labelOs(pr *octokat.PullRequest, os string, fileChecker func() bool) bool {
	return strings.Contains(strings.ToLower(pr.Title), os) ||
		strings.Contains(strings.ToLower(pr.Body), os) ||
		fileChecker()
}
<commit_msg>Comply to docker/docker#14445<commit_after>package github

import (
	"strings"

	log "github.com/Sirupsen/logrus"
	"github.com/crosbymichael/octokat"
)

func (g GitHub) DcoVerified(prHook *octokat.PullRequestHook) (bool, error) {
	// we only want the prs that are opened/synchronized
	if !prHook.IsOpened() && !prHook.IsSynchronize() {
		return false, nil
	}

	// get the PR
	pr := prHook.PullRequest
	repo := getRepo(prHook.Repo)

	// check if this is a bump branch, then we don't want to check sig
	if pr.Base.Ref == "release" {
		return true, nil
	}

	content, err := g.getContent(repo, prHook.Number, true)
	if err != nil {
		return false, err
	}

	// we only want apply labels
	// to opened pull requests
	var labels []string

	//check if it's a proposal
	isProposal := strings.Contains(strings.ToLower(pr.Title), "proposal")
	switch {
	case isProposal:
		labels = []string{"status/1-design-review"}
	case content.IsDocsOnly():
		labels = []string{"status/3-docs-review"}
	default:
		labels = []string{"status/0-triage"}
	}

	if labelOs(pr, "windows", content.OnlyWindows) {
		labels = append(labels, "group/windows")
	}

	// add labels if there are any
	// only add labels to new PRs not sync
	if len(labels) > 0 && prHook.IsOpened() {
		log.Debugf("Adding labels %#v to pr %d", labels, prHook.Number)

		if err := g.addLabel(repo, prHook.Number, labels...); err != nil {
			return false, err
		}

		log.Infof("Added labels %#v to pr %d", labels, prHook.Number)
	}

	var verified bool

	if content.CommitsSigned() {
		if err := g.removeLabel(repo, prHook.Number, "dco/no"); err != nil {
			return false, err
		}

		if err := g.removeComment(repo, pr, "sign your commits", content); err != nil {
			return false, err
		}

		if err := g.successStatus(repo, pr.Head.Sha, "docker/dco-signed", "All commits signed"); err != nil {
			return false, err
		}

		verified = true
	} else {
		if err := g.addLabel(repo, prHook.Number, "dco/no"); err != nil {
			return false, err
		}

		if err := g.addDCOUnsignedComment(repo, pr, content); err != nil {
			return false, err
		}

		if err := g.failureStatus(repo, pr.Head.Sha, "docker/dco-signed", "Some commits without signature", "https://github.com/docker/docker/blob/master/CONTRIBUTING.md#sign-your-work"); err != nil {
			return false, err
		}
	}

	return verified, nil
}

func getRepo(repo *octokat.Repository) octokat.Repo {
	return octokat.Repo{
		Name:     repo.Name,
		UserName: repo.Owner.Login,
	}
}

func labelOs(pr *octokat.PullRequest, os string, fileChecker func() bool) bool {
	return strings.Contains(strings.ToLower(pr.Title), os) ||
		strings.Contains(strings.ToLower(pr.Body), os) ||
		fileChecker()
}
<|endoftext|>


<commit_before>package godingtalk

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

const (
	//VERSION is SDK version
	VERSION = "0.2"
)

//DingTalkClient is the Client to access DingTalk Open API
type DingTalkClient struct {
	CorpID      string
	CorpSecret  string
	AgentID     string
	PartnerID   string
	AccessToken string
	HTTPClient  *http.Client
	Cache       Cache

	//社交相关的属性
	SnsAppID       string
	SnsAppSecret   string
	SnsAccessToken string
}

//Unmarshallable is
type Unmarshallable interface {
	checkError() error
	getWriter() io.Writer
}

//OAPIResponse is
type OAPIResponse struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

func (data *OAPIResponse) checkError() (err error) {
	if data.ErrCode != 0 {
		err = fmt.Errorf("%d: %s", data.ErrCode, data.ErrMsg)
	}
	return err
}

func (data *OAPIResponse) getWriter() io.Writer {
	return nil
}

//MessageResponse is
type MessageResponse struct {
	OAPIResponse
	MessageID string `json:"messageId"`
}

//MessageResponse is
type MessageReadListResponse struct {
	OAPIResponse
	NextCursor     int64    `json:"next_cursor"`
	ReadUserIdList []string `json:"readUserIdList"`
}

//AccessTokenResponse is
type AccessTokenResponse struct {
	OAPIResponse
	AccessToken string `json:"access_token"`
	Expires     int    `json:"expires_in"`
	Created     int64
}

//CreatedAt is when the access token is generated
func (e *AccessTokenResponse) CreatedAt() int64 {
	return e.Created
}

//ExpiresIn is how soon the access token is expired
func (e *AccessTokenResponse) ExpiresIn() int {
	return e.Expires
}

//JsAPITicketResponse is
type JsAPITicketResponse struct {
	OAPIResponse
	Ticket  string
	Expires int `json:"expires_in"`
	Created int64
}

//CreatedAt is when the ticket is generated
func (e *JsAPITicketResponse) CreatedAt() int64 {
	return e.Created
}

//ExpiresIn is how soon the ticket is expired
func (e *JsAPITicketResponse) ExpiresIn() int {
	return e.Expires
}

//NewDingTalkClient creates a DingTalkClient instance
func NewDingTalkClient(corpID string, corpSecret string) *DingTalkClient {
	c := new(DingTalkClient)
	c.CorpID = corpID
	c.CorpSecret = corpSecret
	c.HTTPClient = &http.Client{
		Timeout: 10 * time.Second,
	}
	c.Cache = NewFileCache(".auth_file")
	return c
}

//RefreshAccessToken is to get a valid access token
func (c *DingTalkClient) RefreshAccessToken() error {
	var data AccessTokenResponse
	err := c.Cache.Get(&data)
	if err == nil {
		c.AccessToken = data.AccessToken
		return nil
	}

	params := url.Values{}
	params.Add("corpid", c.CorpID)
	params.Add("corpsecret", c.CorpSecret)
	err = c.httpRPC("gettoken", params, nil, &data)
	if err == nil {
		c.AccessToken = data.AccessToken
		data.Expires = data.Expires | 7200
		data.Created = time.Now().Unix()
		err = c.Cache.Set(&data)
	}
	return err
}

//GetJsAPITicket is to get a valid ticket for JS API
func (c *DingTalkClient) GetJsAPITicket() (ticket string, err error) {
	var data JsAPITicketResponse
	cache := NewFileCache(".jsapi_ticket")
	err = cache.Get(&data)
	if err == nil {
		return data.Ticket, err
	}
	err = c.httpRPC("get_jsapi_ticket", nil, nil, &data)
	if err == nil {
		ticket = data.Ticket
		cache.Set(&data)
	}
	return ticket, err
}

//GetConfig is to return config in json
func (c *DingTalkClient) GetConfig(nonceStr string, timestamp string, url string) string {
	ticket, _ := c.GetJsAPITicket()
	config := map[string]string{
		"url":       url,
		"nonceStr":  nonceStr,
		"agentId":   c.AgentID,
		"timeStamp": timestamp,
		"corpId":    c.CorpID,
		"ticket":    ticket,
		"signature": Sign(ticket, nonceStr, timestamp, url),
	}
	bytes, _ := json.Marshal(&config)
	return string(bytes)
}

//Sign is 签名
func Sign(ticket string, nonceStr string, timeStamp string, url string) string {
	s := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s", ticket, nonceStr, timeStamp, url)
	return sha1Sign(s)
}
<commit_msg>Added UseAppKey to use AppKey and AppSecret<commit_after>package godingtalk

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"time"
)

const (
	//VERSION is SDK version
	VERSION = "0.3"
)

//DingTalkClient is the Client to access DingTalk Open API
type DingTalkClient struct {
	CorpID      string
	CorpSecret  string

    UseAppKey   bool

	AgentID     string
	PartnerID   string
	AccessToken string
	HTTPClient  *http.Client
	Cache       Cache

	//社交相关的属性
	SnsAppID       string
	SnsAppSecret   string
	SnsAccessToken string
}

//Unmarshallable is
type Unmarshallable interface {
	checkError() error
	getWriter() io.Writer
}

//OAPIResponse is
type OAPIResponse struct {
	ErrCode int    `json:"errcode"`
	ErrMsg  string `json:"errmsg"`
}

func (data *OAPIResponse) checkError() (err error) {
	if data.ErrCode != 0 {
		err = fmt.Errorf("%d: %s", data.ErrCode, data.ErrMsg)
	}
	return err
}

func (data *OAPIResponse) getWriter() io.Writer {
	return nil
}

//MessageResponse is
type MessageResponse struct {
	OAPIResponse
	MessageID string `json:"messageId"`
}

//MessageResponse is
type MessageReadListResponse struct {
	OAPIResponse
	NextCursor     int64    `json:"next_cursor"`
	ReadUserIdList []string `json:"readUserIdList"`
}

//AccessTokenResponse is
type AccessTokenResponse struct {
	OAPIResponse
	AccessToken string `json:"access_token"`
	Expires     int    `json:"expires_in"`
	Created     int64
}

//CreatedAt is when the access token is generated
func (e *AccessTokenResponse) CreatedAt() int64 {
	return e.Created
}

//ExpiresIn is how soon the access token is expired
func (e *AccessTokenResponse) ExpiresIn() int {
	return e.Expires
}

//JsAPITicketResponse is
type JsAPITicketResponse struct {
	OAPIResponse
	Ticket  string
	Expires int `json:"expires_in"`
	Created int64
}

//CreatedAt is when the ticket is generated
func (e *JsAPITicketResponse) CreatedAt() int64 {
	return e.Created
}

//ExpiresIn is how soon the ticket is expired
func (e *JsAPITicketResponse) ExpiresIn() int {
	return e.Expires
}

//NewDingTalkClient creates a DingTalkClient instance
func NewDingTalkClient(corpID string, corpSecret string) *DingTalkClient {
	c := new(DingTalkClient)
	c.CorpID = corpID
	c.CorpSecret = corpSecret
	c.HTTPClient = &http.Client{
		Timeout: 10 * time.Second,
	}
	c.Cache = NewFileCache(".auth_file")
	return c
}

//RefreshAccessToken is to get a valid access token
func (c *DingTalkClient) RefreshAccessToken() error {
	var data AccessTokenResponse
	err := c.Cache.Get(&data)
	if err == nil {
		c.AccessToken = data.AccessToken
		return nil
	}

	params := url.Values{}

    if !c.UseAppKey {
        params.Add("corpid", c.CorpID)
	    params.Add("corpsecret", c.CorpSecret)
    } else {
        params.Add("appkey", c.CorpID)
        params.Add("appsecret", c.CorpSecret)
    }

	err = c.httpRPC("gettoken", params, nil, &data)
	if err == nil {
		c.AccessToken = data.AccessToken
		data.Expires = data.Expires | 7200
		data.Created = time.Now().Unix()
		err = c.Cache.Set(&data)
	}
	return err
}

//GetJsAPITicket is to get a valid ticket for JS API
func (c *DingTalkClient) GetJsAPITicket() (ticket string, err error) {
	var data JsAPITicketResponse
	cache := NewFileCache(".jsapi_ticket")
	err = cache.Get(&data)
	if err == nil {
		return data.Ticket, err
	}
	err = c.httpRPC("get_jsapi_ticket", nil, nil, &data)
	if err == nil {
		ticket = data.Ticket
		cache.Set(&data)
	}
	return ticket, err
}

//GetConfig is to return config in json
func (c *DingTalkClient) GetConfig(nonceStr string, timestamp string, url string) string {
	ticket, _ := c.GetJsAPITicket()
	config := map[string]string{
		"url":       url,
		"nonceStr":  nonceStr,
		"agentId":   c.AgentID,
		"timeStamp": timestamp,
		"corpId":    c.CorpID,
		"ticket":    ticket,
		"signature": Sign(ticket, nonceStr, timestamp, url),
	}
	bytes, _ := json.Marshal(&config)
	return string(bytes)
}

//Sign is 签名
func Sign(ticket string, nonceStr string, timeStamp string, url string) string {
	s := fmt.Sprintf("jsapi_ticket=%s&noncestr=%s&timestamp=%s&url=%s", ticket, nonceStr, timeStamp, url)
	return sha1Sign(s)
}
<|endoftext|>