<commit_before>package consensus

import (
	"bytes"
	"errors"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"

	"github.com/NebulousLabs/bolt"
)

var (
	errDoSBlock        = errors.New("block is known to be invalid")
	errNoBlockMap      = errors.New("block map is not in database")
	errInconsistentSet = errors.New("consensus set is not in a consistent state")
	errOrphan          = errors.New("block has no known parent")
)

// managedBroadcastBlock will broadcast a block to the consensus set's peers.
func (cs *ConsensusSet) managedBroadcastBlock(b types.Block) {
	// broadcast the block header to all peers
	go cs.gateway.Broadcast("RelayHeader", b.Header(), cs.gateway.Peers())
}

// validateHeaderAndBlock does some early, low computation verification on the
// block. Callers should not assume that validation will happen in a particular
// order.
func (cs *ConsensusSet) validateHeaderAndBlock(tx dbTx, b types.Block) error {
	// Check if the block is a DoS block - a known invalid block that is expensive
	// to validate.
	id := b.ID()
	_, exists := cs.dosBlocks[id]
	if exists {
		return errDoSBlock
	}

	// Check if the block is already known.
	blockMap := tx.Bucket(BlockMap)
	if blockMap == nil {
		return errNoBlockMap
	}
	if blockMap.Get(id[:]) != nil {
		return modules.ErrBlockKnown
	}

	// Check for the parent.
	parentID := b.ParentID
	parentBytes := blockMap.Get(parentID[:])
	if parentBytes == nil {
		return errOrphan
	}
	var parent processedBlock
	err := cs.marshaler.Unmarshal(parentBytes, &parent)
	if err != nil {
		return err
	}
	// Check that the timestamp is not too far in the past to be acceptable.
	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)

	return cs.blockValidator.ValidateBlock(b, minTimestamp, parent.ChildTarget, parent.Height+1, cs.log)
}

// checkHeaderTarget returns true if the header's ID meets the given target.
func checkHeaderTarget(h types.BlockHeader, target types.Target) bool {
	blockHash := h.ID()
	return bytes.Compare(target[:], blockHash[:]) >= 0
}

// validateHeader does some early, low computation verification on the header
// to determine if the block should be downloaded. Callers should not assume
// that validation will happen in a particular order.
func (cs *ConsensusSet) validateHeader(tx dbTx, h types.BlockHeader) error {
	// Check if the block is a DoS block - a known invalid block that is expensive
	// to validate.
	id := h.ID()
	_, exists := cs.dosBlocks[id]
	if exists {
		return errDoSBlock
	}

	// Check if the block is already known.
	blockMap := tx.Bucket(BlockMap)
	if blockMap == nil {
		return errNoBlockMap
	}
	if blockMap.Get(id[:]) != nil {
		return modules.ErrBlockKnown
	}

	// Check for the parent.
	parentID := h.ParentID
	parentBytes := blockMap.Get(parentID[:])
	if parentBytes == nil {
		return errOrphan
	}
	var parent processedBlock
	err := cs.marshaler.Unmarshal(parentBytes, &parent)
	if err != nil {
		return err
	}

	// Check that the target of the new block is sufficient.
	if !checkHeaderTarget(h, parent.ChildTarget) {
		return modules.ErrBlockUnsolved
	}

	// TODO: check if the block is a non extending block once headers-first
	// downloads are implemented.

	// Check that the timestamp is not too far in the past to be acceptable.
	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)
	if minTimestamp > h.Timestamp {
		return errEarlyTimestamp
	}

	// Check if the block is in the extreme future. We make a distinction between
	// future and extreme future because there is an assumption that by the time
	// the extreme future arrives, this block will no longer be a part of the
	// longest fork because it will have been ignored by all of the miners.
	if h.Timestamp > types.CurrentTimestamp()+types.ExtremeFutureThreshold {
		return errExtremeFutureTimestamp
	}

	// We do not check if the header is in the near future here, because we want
	// to get the corresponding block as soon as possible, even if the block is in
	// the near future.

	return nil
}

// addBlockToTree inserts a block into the blockNode tree by adding it to its
// parent's list of children. If the new blockNode is heavier than the current
// node, the blockchain is forked to put the new block and its parents at the
// tip. An error will be returned if block verification fails or if the block
// does not extend the longest fork.
//
// addBlockToTree must use its own database update because it might need to
// modify the database while returning an error on the block. To prevent error
// tracking complexity, the error is handled inside the function so that 'nil'
// can be appropriately returned by the database and the transaction can be
// committed. Switching to a managed tx through bolt will make this complexity
// unneeded.
func (cs *ConsensusSet) addBlockToTree(b types.Block) (ce changeEntry, err error) {
	var nonExtending bool
	err = cs.db.Update(func(tx *bolt.Tx) error {
		pb, err := getBlockMap(tx, b.ParentID)
		if build.DEBUG && err != nil {
			panic(err)
		}
		currentNode := currentProcessedBlock(tx)
		newNode := cs.newChild(tx, pb, b)

		// modules.ErrNonExtendingBlock should be returned if the block does
		// not extend the current blockchain, however the changes from newChild
		// should be committed (which means 'nil' must be returned). A flag is
		// set to indicate that modules.ErrNonExtending should be returned.
		nonExtending = !newNode.heavierThan(currentNode)
		if nonExtending {
			return nil
		}
		var revertedBlocks, appliedBlocks []*processedBlock
		revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode)
		if err != nil {
			return err
		}
		for _, rn := range revertedBlocks {
			ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID())
		}
		for _, an := range appliedBlocks {
			ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID())
		}
		// To have correct error handling, appendChangeLog must be called
		// before appending to the in-memory changelog. If this call fails, the
		// change is going to be reverted, but the in-memory changelog is not
		// going to be reverted.
		//
		// Technically, if bolt fails for some other reason (such as a
		// filesystem error), the in-memory changelog will be incorrect anyway.
		// Restarting Sia will fix it. The in-memory changelog is being phased
		// out.
		err = appendChangeLog(tx, ce)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return changeEntry{}, err
	}
	if nonExtending {
		return changeEntry{}, modules.ErrNonExtendingBlock
	}
	return ce, nil
}

// managedAcceptBlock will try to add a block to the consensus set. If the
// block does not extend the longest currently known chain, an error is
// returned but the block is still kept in memory. If the block extends a fork
// such that the fork becomes the longest currently known chain, the consensus
// set will reorganize itself to recognize the new longest fork. Accepted
// blocks are not relayed.
//
// Typically AcceptBlock should be used so that the accepted block is relayed.
// This method is typically only be used when there would otherwise be multiple
// consecutive calls to AcceptBlock with each successive call accepting the
// child block of the previous call.
func (cs *ConsensusSet) managedAcceptBlock(b types.Block) error {
	// Grab a lock on the consensus set. Lock is demoted later in the function,
	// failure to unlock before returning an error will cause a deadlock.
	cs.mu.Lock()

	// Start verification inside of a bolt View tx.
	err := cs.db.View(func(tx *bolt.Tx) error {
		// Do not accept a block if the database is inconsistent.
		if inconsistencyDetected(tx) {
			return errInconsistentSet
		}

		// Do some relatively inexpensive checks to validate the header and block.
		// Validation generally occurs in the order of least expensive validation
		// first.
		err := cs.validateHeaderAndBlock(boltTxWrapper{tx}, b)
		if err != nil {
			// If the block is in the near future, but too far to be acceptable, then
			// save the block and add it to the consensus set after it is no longer
			// too far in the future.
			//
			// TODO: an attacker could mine many blocks off the genesis block all in the
			// future and we would spawn a goroutine per each block. To fix this, either
			// ban peers that send lots of future blocks and stop spawning goroutines
			// after we are already waiting on a large number of future blocks.
			//
			// TODO: an attacker could broadcast a future block many times and we would
			// spawn a goroutine for each broadcast. To fix this we should create a
			// cache of future blocks, like we already do for DoS blocks, and only spawn
			// a goroutine if we haven't already spawned one for that block. To limit
			// the size of the cache of future blocks, make it a constant size (say 50)
			// over which we would evict the block furthest in the future before adding
			// a new block to the cache.
			if err == errFutureTimestamp {
				go func() {
					time.Sleep(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second)
					err := cs.managedAcceptBlock(b)
					if err != nil {
						cs.log.Debugln("WARN: failed to accept a future block:", err)
					}
					cs.managedBroadcastBlock(b)
				}()
			}
			return err
		}
		return nil
	})
	if err != nil {
		cs.mu.Unlock()
		return err
	}

	// Try adding the block to the block tree. This call will perform
	// verification on the block before adding the block to the block tree. An
	// error is returned if verification fails or if the block does not extend
	// the longest fork.
	changeEntry, err := cs.addBlockToTree(b)
	if err != nil {
		cs.mu.Unlock()
		return err
	}
	// If appliedBlocks is 0, revertedBlocks will also be 0.
	if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 {
		panic("appliedBlocks and revertedBlocks are mismatched!")
	}

	// Updates complete, demote the lock.
	if len(changeEntry.AppliedBlocks) > 0 {
		cs.readlockUpdateSubscribers(changeEntry)
	}
	cs.mu.Unlock()
	return nil
}

// AcceptBlock will try to add a block to the consensus set. If the block does
// not extend the longest currently known chain, an error is returned but the
// block is still kept in memory. If the block extends a fork such that the
// fork becomes the longest currently known chain, the consensus set will
// reorganize itself to recognize the new longest fork. If a block is accepted
// without error, it will be relayed to all connected peers. This function
// should only be called for new blocks.
func (cs *ConsensusSet) AcceptBlock(b types.Block) error {
	err := cs.tg.Add()
	if err != nil {
		return err
	}
	defer cs.tg.Done()

	err = cs.managedAcceptBlock(b)
	if err != nil {
		return err
	}
	cs.managedBroadcastBlock(b)
	return nil
}
<commit_msg>use defer to unlock mutex in managedAcceptBlock<commit_after>package consensus

import (
	"bytes"
	"errors"
	"time"

	"github.com/NebulousLabs/Sia/build"
	"github.com/NebulousLabs/Sia/modules"
	"github.com/NebulousLabs/Sia/types"

	"github.com/NebulousLabs/bolt"
)

var (
	errDoSBlock        = errors.New("block is known to be invalid")
	errNoBlockMap      = errors.New("block map is not in database")
	errInconsistentSet = errors.New("consensus set is not in a consistent state")
	errOrphan          = errors.New("block has no known parent")
)

// managedBroadcastBlock will broadcast a block to the consensus set's peers.
func (cs *ConsensusSet) managedBroadcastBlock(b types.Block) {
	// broadcast the block header to all peers
	go cs.gateway.Broadcast("RelayHeader", b.Header(), cs.gateway.Peers())
}

// validateHeaderAndBlock does some early, low computation verification on the
// block. Callers should not assume that validation will happen in a particular
// order.
func (cs *ConsensusSet) validateHeaderAndBlock(tx dbTx, b types.Block) error {
	// Check if the block is a DoS block - a known invalid block that is expensive
	// to validate.
	id := b.ID()
	_, exists := cs.dosBlocks[id]
	if exists {
		return errDoSBlock
	}

	// Check if the block is already known.
	blockMap := tx.Bucket(BlockMap)
	if blockMap == nil {
		return errNoBlockMap
	}
	if blockMap.Get(id[:]) != nil {
		return modules.ErrBlockKnown
	}

	// Check for the parent.
	parentID := b.ParentID
	parentBytes := blockMap.Get(parentID[:])
	if parentBytes == nil {
		return errOrphan
	}
	var parent processedBlock
	err := cs.marshaler.Unmarshal(parentBytes, &parent)
	if err != nil {
		return err
	}
	// Check that the timestamp is not too far in the past to be acceptable.
	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)

	return cs.blockValidator.ValidateBlock(b, minTimestamp, parent.ChildTarget, parent.Height+1, cs.log)
}

// checkHeaderTarget returns true if the header's ID meets the given target.
func checkHeaderTarget(h types.BlockHeader, target types.Target) bool {
	blockHash := h.ID()
	return bytes.Compare(target[:], blockHash[:]) >= 0
}

// validateHeader does some early, low computation verification on the header
// to determine if the block should be downloaded. Callers should not assume
// that validation will happen in a particular order.
func (cs *ConsensusSet) validateHeader(tx dbTx, h types.BlockHeader) error {
	// Check if the block is a DoS block - a known invalid block that is expensive
	// to validate.
	id := h.ID()
	_, exists := cs.dosBlocks[id]
	if exists {
		return errDoSBlock
	}

	// Check if the block is already known.
	blockMap := tx.Bucket(BlockMap)
	if blockMap == nil {
		return errNoBlockMap
	}
	if blockMap.Get(id[:]) != nil {
		return modules.ErrBlockKnown
	}

	// Check for the parent.
	parentID := h.ParentID
	parentBytes := blockMap.Get(parentID[:])
	if parentBytes == nil {
		return errOrphan
	}
	var parent processedBlock
	err := cs.marshaler.Unmarshal(parentBytes, &parent)
	if err != nil {
		return err
	}

	// Check that the target of the new block is sufficient.
	if !checkHeaderTarget(h, parent.ChildTarget) {
		return modules.ErrBlockUnsolved
	}

	// TODO: check if the block is a non extending block once headers-first
	// downloads are implemented.

	// Check that the timestamp is not too far in the past to be acceptable.
	minTimestamp := cs.blockRuleHelper.minimumValidChildTimestamp(blockMap, &parent)
	if minTimestamp > h.Timestamp {
		return errEarlyTimestamp
	}

	// Check if the block is in the extreme future. We make a distinction between
	// future and extreme future because there is an assumption that by the time
	// the extreme future arrives, this block will no longer be a part of the
	// longest fork because it will have been ignored by all of the miners.
	if h.Timestamp > types.CurrentTimestamp()+types.ExtremeFutureThreshold {
		return errExtremeFutureTimestamp
	}

	// We do not check if the header is in the near future here, because we want
	// to get the corresponding block as soon as possible, even if the block is in
	// the near future.

	return nil
}

// addBlockToTree inserts a block into the blockNode tree by adding it to its
// parent's list of children. If the new blockNode is heavier than the current
// node, the blockchain is forked to put the new block and its parents at the
// tip. An error will be returned if block verification fails or if the block
// does not extend the longest fork.
//
// addBlockToTree must use its own database update because it might need to
// modify the database while returning an error on the block. To prevent error
// tracking complexity, the error is handled inside the function so that 'nil'
// can be appropriately returned by the database and the transaction can be
// committed. Switching to a managed tx through bolt will make this complexity
// unneeded.
func (cs *ConsensusSet) addBlockToTree(b types.Block) (ce changeEntry, err error) {
	var nonExtending bool
	err = cs.db.Update(func(tx *bolt.Tx) error {
		pb, err := getBlockMap(tx, b.ParentID)
		if build.DEBUG && err != nil {
			panic(err)
		}
		currentNode := currentProcessedBlock(tx)
		newNode := cs.newChild(tx, pb, b)

		// modules.ErrNonExtendingBlock should be returned if the block does
		// not extend the current blockchain, however the changes from newChild
		// should be committed (which means 'nil' must be returned). A flag is
		// set to indicate that modules.ErrNonExtending should be returned.
		nonExtending = !newNode.heavierThan(currentNode)
		if nonExtending {
			return nil
		}
		var revertedBlocks, appliedBlocks []*processedBlock
		revertedBlocks, appliedBlocks, err = cs.forkBlockchain(tx, newNode)
		if err != nil {
			return err
		}
		for _, rn := range revertedBlocks {
			ce.RevertedBlocks = append(ce.RevertedBlocks, rn.Block.ID())
		}
		for _, an := range appliedBlocks {
			ce.AppliedBlocks = append(ce.AppliedBlocks, an.Block.ID())
		}
		// To have correct error handling, appendChangeLog must be called
		// before appending to the in-memory changelog. If this call fails, the
		// change is going to be reverted, but the in-memory changelog is not
		// going to be reverted.
		//
		// Technically, if bolt fails for some other reason (such as a
		// filesystem error), the in-memory changelog will be incorrect anyway.
		// Restarting Sia will fix it. The in-memory changelog is being phased
		// out.
		err = appendChangeLog(tx, ce)
		if err != nil {
			return err
		}
		return nil
	})
	if err != nil {
		return changeEntry{}, err
	}
	if nonExtending {
		return changeEntry{}, modules.ErrNonExtendingBlock
	}
	return ce, nil
}

// managedAcceptBlock will try to add a block to the consensus set. If the
// block does not extend the longest currently known chain, an error is
// returned but the block is still kept in memory. If the block extends a fork
// such that the fork becomes the longest currently known chain, the consensus
// set will reorganize itself to recognize the new longest fork. Accepted
// blocks are not relayed.
//
// Typically AcceptBlock should be used so that the accepted block is relayed.
// This method is typically only be used when there would otherwise be multiple
// consecutive calls to AcceptBlock with each successive call accepting the
// child block of the previous call.
func (cs *ConsensusSet) managedAcceptBlock(b types.Block) error {
	// Grab a lock on the consensus set.
	cs.mu.Lock()
	defer cs.mu.Unlock()

	// Start verification inside of a bolt View tx.
	err := cs.db.View(func(tx *bolt.Tx) error {
		// Do not accept a block if the database is inconsistent.
		if inconsistencyDetected(tx) {
			return errInconsistentSet
		}

		// Do some relatively inexpensive checks to validate the header and block.
		// Validation generally occurs in the order of least expensive validation
		// first.
		err := cs.validateHeaderAndBlock(boltTxWrapper{tx}, b)
		if err != nil {
			// If the block is in the near future, but too far to be acceptable, then
			// save the block and add it to the consensus set after it is no longer
			// too far in the future.
			//
			// TODO: an attacker could mine many blocks off the genesis block all in the
			// future and we would spawn a goroutine per each block. To fix this, either
			// ban peers that send lots of future blocks and stop spawning goroutines
			// after we are already waiting on a large number of future blocks.
			//
			// TODO: an attacker could broadcast a future block many times and we would
			// spawn a goroutine for each broadcast. To fix this we should create a
			// cache of future blocks, like we already do for DoS blocks, and only spawn
			// a goroutine if we haven't already spawned one for that block. To limit
			// the size of the cache of future blocks, make it a constant size (say 50)
			// over which we would evict the block furthest in the future before adding
			// a new block to the cache.
			if err == errFutureTimestamp {
				go func() {
					time.Sleep(time.Duration(b.Timestamp-(types.CurrentTimestamp()+types.FutureThreshold)) * time.Second)
					err := cs.managedAcceptBlock(b)
					if err != nil {
						cs.log.Debugln("WARN: failed to accept a future block:", err)
					}
					cs.managedBroadcastBlock(b)
				}()
			}
			return err
		}
		return nil
	})
	if err != nil {
		return err
	}

	// Try adding the block to the block tree. This call will perform
	// verification on the block before adding the block to the block tree. An
	// error is returned if verification fails or if the block does not extend
	// the longest fork.
	changeEntry, err := cs.addBlockToTree(b)
	if err != nil {
		return err
	}
	// If appliedBlocks is 0, revertedBlocks will also be 0.
	if build.DEBUG && len(changeEntry.AppliedBlocks) == 0 && len(changeEntry.RevertedBlocks) != 0 {
		panic("appliedBlocks and revertedBlocks are mismatched!")
	}

	// Updates complete, demote the lock.
	if len(changeEntry.AppliedBlocks) > 0 {
		cs.readlockUpdateSubscribers(changeEntry)
	}
	return nil
}

// AcceptBlock will try to add a block to the consensus set. If the block does
// not extend the longest currently known chain, an error is returned but the
// block is still kept in memory. If the block extends a fork such that the
// fork becomes the longest currently known chain, the consensus set will
// reorganize itself to recognize the new longest fork. If a block is accepted
// without error, it will be relayed to all connected peers. This function
// should only be called for new blocks.
func (cs *ConsensusSet) AcceptBlock(b types.Block) error {
	err := cs.tg.Add()
	if err != nil {
		return err
	}
	defer cs.tg.Done()

	err = cs.managedAcceptBlock(b)
	if err != nil {
		return err
	}
	cs.managedBroadcastBlock(b)
	return nil
}
<|endoftext|>


<commit_before>package irutil

import (
	"go/types"
	"strings"

	"honnef.co/go/tools/go/ir"
	"honnef.co/go/tools/go/types/typeutil"
)

func Reachable(from, to *ir.BasicBlock) bool {
	if from == to {
		return true
	}
	if from.Dominates(to) {
		return true
	}

	found := false
	Walk(from, func(b *ir.BasicBlock) bool {
		if b == to {
			found = true
			return false
		}
		return true
	})
	return found
}

func Walk(b *ir.BasicBlock, fn func(*ir.BasicBlock) bool) {
	seen := map[*ir.BasicBlock]bool{}
	wl := []*ir.BasicBlock{b}
	for len(wl) > 0 {
		b := wl[len(wl)-1]
		wl = wl[:len(wl)-1]
		if seen[b] {
			continue
		}
		seen[b] = true
		if !fn(b) {
			continue
		}
		wl = append(wl, b.Succs...)
	}
}

func Vararg(x *ir.Slice) ([]ir.Value, bool) {
	var out []ir.Value
	slice, ok := x.X.(*ir.Alloc)
	if !ok {
		return nil, false
	}
	for _, ref := range *slice.Referrers() {
		if ref == x {
			continue
		}
		if ref.Block() != x.Block() {
			return nil, false
		}
		idx, ok := ref.(*ir.IndexAddr)
		if !ok {
			return nil, false
		}
		if len(*idx.Referrers()) != 1 {
			return nil, false
		}
		store, ok := (*idx.Referrers())[0].(*ir.Store)
		if !ok {
			return nil, false
		}
		out = append(out, store.Val)
	}
	return out, true
}

func CallName(call *ir.CallCommon) string {
	if call.IsInvoke() {
		return ""
	}
	switch v := call.Value.(type) {
	case *ir.Function:
		fn, ok := v.Object().(*types.Func)
		if !ok {
			return ""
		}
		return typeutil.FuncName(fn)
	case *ir.Builtin:
		return v.Name()
	}
	return ""
}

func IsCallTo(call *ir.CallCommon, name string) bool { return CallName(call) == name }

func IsCallToAny(call *ir.CallCommon, names ...string) bool {
	q := CallName(call)
	for _, name := range names {
		if q == name {
			return true
		}
	}
	return false
}

func FilterDebug(instr []ir.Instruction) []ir.Instruction {
	var out []ir.Instruction
	for _, ins := range instr {
		if _, ok := ins.(*ir.DebugRef); !ok {
			out = append(out, ins)
		}
	}
	return out
}

func IsExample(fn *ir.Function) bool {
	if !strings.HasPrefix(fn.Name(), "Example") {
		return false
	}
	f := fn.Prog.Fset.File(fn.Pos())
	if f == nil {
		return false
	}
	return strings.HasSuffix(f.Name(), "_test.go")
}

// Flatten recursively returns the underlying value of an ir.Sigma or
// ir.Phi node. If all edges in an ir.Phi node are the same (after
// flattening), the flattened edge will get returned. If flattening is
// not possible, nil is returned.
func Flatten(v ir.Value) ir.Value {
	failed := false
	seen := map[ir.Value]struct{}{}
	var out ir.Value
	var dfs func(v ir.Value)
	dfs = func(v ir.Value) {
		if failed {
			return
		}
		if _, ok := seen[v]; ok {
			return
		}
		seen[v] = struct{}{}

		switch v := v.(type) {
		case *ir.Sigma:
			dfs(v.X)
		case *ir.Phi:
			for _, e := range v.Edges {
				dfs(e)
			}
		default:
			if out == nil {
				out = v
			} else if out != v {
				failed = true
			}
		}
	}
	dfs(v)

	if failed {
		return nil
	}
	return out
}
<commit_msg>go/ir/irutil: update Vararg to support Copy instruction<commit_after>package irutil

import (
	"go/types"
	"strings"

	"honnef.co/go/tools/go/ir"
	"honnef.co/go/tools/go/types/typeutil"
)

func Reachable(from, to *ir.BasicBlock) bool {
	if from == to {
		return true
	}
	if from.Dominates(to) {
		return true
	}

	found := false
	Walk(from, func(b *ir.BasicBlock) bool {
		if b == to {
			found = true
			return false
		}
		return true
	})
	return found
}

func Walk(b *ir.BasicBlock, fn func(*ir.BasicBlock) bool) {
	seen := map[*ir.BasicBlock]bool{}
	wl := []*ir.BasicBlock{b}
	for len(wl) > 0 {
		b := wl[len(wl)-1]
		wl = wl[:len(wl)-1]
		if seen[b] {
			continue
		}
		seen[b] = true
		if !fn(b) {
			continue
		}
		wl = append(wl, b.Succs...)
	}
}

func Vararg(x *ir.Slice) ([]ir.Value, bool) {
	var out []ir.Value
	alloc, ok := ir.Unwrap(x.X).(*ir.Alloc)
	if !ok {
		return nil, false
	}
	var checkAlloc func(alloc ir.Value) bool
	checkAlloc = func(alloc ir.Value) bool {
		for _, ref := range *alloc.Referrers() {
			if ref == x {
				continue
			}
			if ref.Block() != x.Block() {
				return false
			}
			switch ref := ref.(type) {
			case *ir.IndexAddr:
				idx := ref
				if len(*idx.Referrers()) != 1 {
					return false
				}
				store, ok := (*idx.Referrers())[0].(*ir.Store)
				if !ok {
					return false
				}
				out = append(out, store.Val)
			case *ir.Copy:
				if !checkAlloc(ref) {
					return false
				}
			default:
				return false
			}
		}
		return true
	}
	if !checkAlloc(alloc) {
		return nil, false
	}
	return out, true
}

func CallName(call *ir.CallCommon) string {
	if call.IsInvoke() {
		return ""
	}
	switch v := call.Value.(type) {
	case *ir.Function:
		fn, ok := v.Object().(*types.Func)
		if !ok {
			return ""
		}
		return typeutil.FuncName(fn)
	case *ir.Builtin:
		return v.Name()
	}
	return ""
}

func IsCallTo(call *ir.CallCommon, name string) bool { return CallName(call) == name }

func IsCallToAny(call *ir.CallCommon, names ...string) bool {
	q := CallName(call)
	for _, name := range names {
		if q == name {
			return true
		}
	}
	return false
}

func FilterDebug(instr []ir.Instruction) []ir.Instruction {
	var out []ir.Instruction
	for _, ins := range instr {
		if _, ok := ins.(*ir.DebugRef); !ok {
			out = append(out, ins)
		}
	}
	return out
}

func IsExample(fn *ir.Function) bool {
	if !strings.HasPrefix(fn.Name(), "Example") {
		return false
	}
	f := fn.Prog.Fset.File(fn.Pos())
	if f == nil {
		return false
	}
	return strings.HasSuffix(f.Name(), "_test.go")
}

// Flatten recursively returns the underlying value of an ir.Sigma or
// ir.Phi node. If all edges in an ir.Phi node are the same (after
// flattening), the flattened edge will get returned. If flattening is
// not possible, nil is returned.
func Flatten(v ir.Value) ir.Value {
	failed := false
	seen := map[ir.Value]struct{}{}
	var out ir.Value
	var dfs func(v ir.Value)
	dfs = func(v ir.Value) {
		if failed {
			return
		}
		if _, ok := seen[v]; ok {
			return
		}
		seen[v] = struct{}{}

		switch v := v.(type) {
		case *ir.Sigma:
			dfs(v.X)
		case *ir.Phi:
			for _, e := range v.Edges {
				dfs(e)
			}
		default:
			if out == nil {
				out = v
			} else if out != v {
				failed = true
			}
		}
	}
	dfs(v)

	if failed {
		return nil
	}
	return out
}
<|endoftext|>


<commit_before>/*
Copyright 2013 Tamás Gulácsi

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 godrv

import (
	"database/sql"
	"flag"
	"testing"
	"time"
)

var fDsn = flag.String("dsn", "", "Oracle DSN")

func TestSimple(t *testing.T) {
	conn := getConnection(t)
	defer conn.Close()

	var (
		err  error
		num  int
		nul  interface{}
		rat  float64
		raw  []byte
		str  string
		date time.Time
		//dur time.Duration
	)
	for i, tst := range []struct {
		qry string
		dst interface{}
	}{
		{"SELECT ROWNUM FROM DUAL", &num},
		{"SELECT LOG(10, 2) FROM DUAL", &rat},
		{"SELECT 'árvíztűrő tükörfúrógép' FROM DUAL", &str},
		{"SELECT HEXTORAW('00') FROM DUAL", &raw},
		{"SELECT TO_DATE('2006-05-04 15:07:08', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL", &date},
		{"SELECT NULL FROM DUAL", &nul},
	} {
		row := conn.QueryRow(tst.qry)
		if err = row.Scan(tst.dst); err != nil {
			t.Errorf("%d. error with %q test: %s", i, tst.qry, err)
		}
		t.Logf("%d. %q result: %#v", i, tst.qry, tst.dst)
	}

	qry := "SELECT rn, CHR(rn) FROM (SELECT ROWNUM rn FROM all_objects WHERE ROWNUM < 256)"
	rows, err := conn.Query(qry)
	if err != nil {
		t.Errorf("error with multirow test, query %q: %s", qry, err)
	}
	defer rows.Close()
	cols, err := rows.Columns()
	if err != nil {
		t.Errorf("error getting columns for %q: %s", qry, err)
	}
	t.Logf("columns for %q: %v", qry, cols)
	for rows.Next() {
		if err = rows.Scan(&num, &str); err != nil {
			t.Errorf("error scanning row: %s", err)
		}
		//t.Logf("%d=%q", num, str)
	}
}

// TestClob
func TestClob(t *testing.T) {
	conn := getConnection(t)
	defer conn.Close()

}

var testDB *sql.DB

func getConnection(t *testing.T) *sql.DB {
	var err error
	if testDB != nil && testDB.Ping() == nil {
		return testDB
	}
	flag.Parse()
	if testDB, err = sql.Open("goracle", *fDsn); err != nil {
		t.Fatalf("error connecting to %q: %s", *fDsn, err)
	}
	return testDB
}
<commit_msg>little bit better test for select CLOB<commit_after>/*
Copyright 2013 Tamás Gulácsi

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 godrv

import (
	"database/sql"
	"flag"
	"strings"
	"testing"
	//"time"

	"github.com/tgulacsi/goracle/oracle"
)

var fDsn = flag.String("dsn", "", "Oracle DSN")

func TestSimple(t *testing.T) {
	conn := getConnection(t)
	defer conn.Close()

	var (
		err error
		dst interface{}
	)
	for i, qry := range []string{
		"SELECT ROWNUM FROM DUAL",
		"SELECT LOG(10, 2) FROM DUAL",
		"SELECT 'árvíztűrő tükörfúrógép' FROM DUAL",
		"SELECT HEXTORAW('00') FROM DUAL",
		"SELECT TO_DATE('2006-05-04 15:07:08', 'YYYY-MM-DD HH24:MI:SS') FROM DUAL",
		"SELECT NULL FROM DUAL",
		"SELECT TO_CLOB('árvíztűrő tükörfúrógép') FROM DUAL",
	} {
		row := conn.QueryRow(qry)
		if err = row.Scan(&dst); err != nil {
			t.Errorf("%d. error with %q test: %s", i, qry, err)
		}
		t.Logf("%d. %q result: %#v", i, qry, dst)
		if strings.Index(qry, " TO_CLOB(") >= 0 {
			clob := dst.(*oracle.ExternalLobVar)
            b, e := clob.ReadAll()
            if e != nil {
                t.Errorf("error reading clob (%v): %s", clob, e)
            } else {
			t.Logf("clob=%s", b)
        }
		}
	}

	qry := "SELECT rn, CHR(rn) FROM (SELECT ROWNUM rn FROM all_objects WHERE ROWNUM < 256)"
	rows, err := conn.Query(qry)
	if err != nil {
		t.Errorf("error with multirow test, query %q: %s", qry, err)
	}
	defer rows.Close()
	cols, err := rows.Columns()
	if err != nil {
		t.Errorf("error getting columns for %q: %s", qry, err)
	}
	t.Logf("columns for %q: %v", qry, cols)
	var (
		num int
		str string
	)
	for rows.Next() {
		if err = rows.Scan(&num, &str); err != nil {
			t.Errorf("error scanning row: %s", err)
		}
		//t.Logf("%d=%q", num, str)
	}
}

// TestClob
func TestClob(t *testing.T) {
	conn := getConnection(t)
	defer conn.Close()

}

var testDB *sql.DB

func getConnection(t *testing.T) *sql.DB {
	var err error
	if testDB != nil && testDB.Ping() == nil {
		return testDB
	}
	flag.Parse()
	if testDB, err = sql.Open("goracle", *fDsn); err != nil {
		t.Fatalf("error connecting to %q: %s", *fDsn, err)
	}
	return testDB
}
<|endoftext|>


<commit_before>package main

import (
	"code.google.com/p/goplan9/plan9"
	"net"
	"flag"
	"fmt"
	"io"
	"log"
	"sync"
)

var (
	listen = flag.String("listen", "127.0.0.1:5640", "Address to listen")
	help = flag.Bool("h", false, "Help")
)

type Server struct {
	listener net.Listener
	explorer FileExplorer
}

func NewServer(lnet, laddr string, explorer FileExplorer) (*Server, error) {
	s := &Server{}
	l, err := net.Listen(lnet, laddr)
	if err != nil { return nil, err }
	s.listener = l
	s.explorer = explorer
	return s, nil
}

func (s *Server) Close() error {
	return s.listener.Close()
}

func (s *Server) Start() error {
	for {
		client, err := s.listener.Accept()
		println("got client")
		if err != nil {
			println("error", err)
			return err
		}
		cc := NewClientConn(client, s, s.explorer)
		go cc.handle()
	}
	return nil
}

func (s *Server) handleError(err error, c net.Conn) {
	log.Printf("Client %v caused error %v", c.RemoteAddr(), err)
}

func readFCall(out chan *plan9.Fcall, done chan signal, err chan error, input io.Reader) {
loop:
	for {
		select {
		case <-done:
			close(out)
			break loop
		default:
			fc, e := plan9.ReadFcall(input)
			if e != nil {
				err <- e
				close(out)
				break
			}
			out <- fc
		}
	}
}

func writeFCall(out io.Writer, done chan signal, err chan error, input chan *plan9.Fcall) {
loop:
	for {
		select {
		case <-done:
			break loop
		case fc := <-input:
			if fc == nil {
				continue
			}
			e := plan9.WriteFcall(out, fc)
			if e != nil {
				err <- e
				break
			}
		}
	}
}

// Handle all client interaction
type ClientConn struct {
	sync.RWMutex
	// remote connection
	conn net.Conn
	// server
	server *Server
	// map between path's and fileRef's
	fileRefs map[uint64]*fileRef
	// map between a fid and a qid
	fidmap map[uint32]uint64
	// explorer used to navigate the FS
	explorer FileExplorer
}

// Interface used to navigate, open and close files in the system
type FileExplorer interface {
	// Must return the unique identifier for this explorer root.
	//
	// The returned value can't be zero, since 0 is considered a non-existing path
	Root() uint64
	// Should return the unique identifier and type of the given name under the given directory
	//
	// If the name don't exists under parent, should return 0
	Walk(parent uint64, name string) (uint64, FileType)
	// Open the file for subsequent reading/writing
	//
	// If the error is nil, the file is considered ready for processing
	Open(file uint64, mode FileMode) error
}

// Represent a reference to a file
type fileRef struct {
	plan9.Qid
}

// Type of a file
type FileType uint8

const (
	FTFILE = FileType(plan9.QTFILE)
	FTDIR = FileType(plan9.QTDIR)
	FTMOUNT = FileType(plan9.QTMOUNT)
)

type FileMode uint8
const (
	FMREAD = FileMode(plan9.OREAD)
	FMWRITE = FileMode(plan9.OWRITE)
	FMRDWR = FileMode(plan9.ORDWR)
)

func NewClientConn(conn net.Conn, server *Server, explorer FileExplorer) *ClientConn {
	cc := &ClientConn{conn: conn, server: server, fileRefs: make(map[uint64]*fileRef),
		explorer: explorer, fidmap: make(map[uint32]uint64)};
	return cc
}

// used to send signal's between goroutines
type signal struct{}

func (c *ClientConn) handle() {
	defer c.Close()
	input := make(chan *plan9.Fcall)
	output := make(chan *plan9.Fcall)
	done := make(chan signal)
	err := make(chan error)

	go readFCall(input, done, err, c.conn)
	go writeFCall(c.conn, done, err, output)
loop:
	for {
		select {
		case fc := <-input:
			go c.process(fc, output)
		case e := <-err:
			c.server.handleError(e, c.conn)
			done <- signal{}
			done <- signal{}
			break loop
		}
	}
}

func (c *ClientConn) process(fc *plan9.Fcall, out chan *plan9.Fcall) {
	println(">>>\t", fc.String())
	switch fc.Type {
	case plan9.Tversion:
		fc = c.version(fc)
	case plan9.Tattach:
		fc = c.attach(fc)
	case plan9.Twalk:
		fc = c.walk(fc)
	case plan9.Topen:
		fc = c.open(fc)
	default:
		println("!!!\t", fc.String())
		fc = nil
	}
	if fc != nil {
		println("<<<\t", fc.String())
	}
	out <- fc
}

func (c *ClientConn) version(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rversion
	fc.Version = "9P2000"
	return fc
}

func (c *ClientConn) attach(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rattach
	fref, _ := c.createFileRef(c.explorer.Root(), FTMOUNT, 0)
	fc.Qid = fref.Qid
	c.bindFid(fc.Fid, fc.Qid.Path)
	return fc
}

func (c *ClientConn) walk(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rwalk
	fref, has := c.fidRef(fc.Fid)
	if !has {
		return c.invalidFidErr(fc)
	}
	if _, has := c.fidRef(fc.Newfid); has {
		return c.fidUsedErr(fc)
	}
	current := fref.Path
	for idx, name := range fc.Wname {
		var ft FileType
		current, ft = c.explorer.Walk(current, name)
		if current == 0 {
			return c.fileNotFoundErr(fc)
		}
		ref, err := c.createFileRef(current, ft, 0)
		if err != nil {
			return c.unexpectedErr(fc, err)
		}
		fc.Wqid = append(fc.Wqid, ref.Qid)

		// if the last match isn't a directory, there is no need to find
		// another part of the path
		//
		// so, just break here
		if ft == FTFILE && idx != len(fc.Wname) - 1 {
			println("here")
			return c.fileNotFoundErr(fc)
		}
	}
	// make a bind between the last qid and the new fid
	c.bindFid(fc.Newfid, fc.Wqid[len(fc.Wqid)-1].Path)
	return fc
}

func (c *ClientConn) open(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Ropen
	if fref, has := c.fidRef(fc.Fid); has {
		err := c.explorer.Open(fref.Path, FileMode(fc.Mode))
		if err != nil {
			return c.unexpectedErr(fc, err)
		}
		fc.Qid = fref.Qid
		return fc
	}
	return c.invalidFidErr(fc)
}

func (c *ClientConn) invalidFidErr(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = "fid not found"
	return fc
}

func (c *ClientConn) fileNotFoundErr(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = "file not found"
	return fc
}

func (c *ClientConn) fidUsedErr(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = "fid in use"
	return fc
}

func (c *ClientConn) unexpectedErr(fc *plan9.Fcall, err error) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = err.Error()
	return fc
}

// Return the file referenced by the given fid
func (c *ClientConn) fidRef(fid uint32) (*fileRef, bool) {
	c.RLock()
	defer c.RUnlock()
	if qid, has := c.fidmap[fid]; has {
		fref, has := c.fileRefs[qid]
		return fref, has
	}
	return nil, false
}

// Bind the given fid to the provided path
//
// if path is 0, remove the fid
func (c *ClientConn) bindFid(fid uint32, path uint64) {
	c.Lock()
	defer c.Unlock()
	_, has := c.fidmap[fid]
	if path == 0 && has {
		delete(c.fidmap, fid)
		return
	}
	c.fidmap[fid] = path
}

// Create a file for the given path, if the path is already present, then just check
// if the version/type are the same, if they are, just return the existing file, otherwise
// return an error
//
// If the version is 0, then this check is ignored and any version existing on the server is returned
//
// Every new resources created will have a version of 1 instead of 0.
func (c *ClientConn) createFileRef(path uint64, ft FileType, version uint32) (*fileRef, error) {
	c.Lock()
	defer c.Unlock()
	if fref, has := c.fileRefs[path]; has {
		if fref.Path == path && (fref.Vers == version || version == 0) && uint8(ft) == fref.Type {
			return fref, nil
		}
		return fref, fmt.Errorf("path %v already used", path)
	}
	fref := &fileRef{}
	if version == 0 {
		version = 1
	}
	fref.Type = uint8(ft)
	fref.Vers = version
	fref.Path = path
	c.fileRefs[path] = fref
	return fref, nil
}

func (c *ClientConn) Close() error {
	return c.conn.Close()
}

type dummyExplorer struct{}

func (d dummyExplorer) Root() uint64 {
	return 1
}

func (d dummyExplorer) Walk(parent uint64, name string) (uint64, FileType) {
	println("\t...searching ", parent, " for file: ", name)
	if parent == 1 && name == "dummy" {
		return 2, FTFILE
	}
	return 0, FTFILE
}

func (d dummyExplorer) Open(file uint64, mode FileMode) error {
	if file != 2 {
		return fmt.Errorf("file not found")
	}
	if mode != FMREAD {
		return fmt.Errorf("file is read only")
	}
	return nil
}

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		return
	}
	server,err := NewServer("tcp", *listen, dummyExplorer{})
	if err != nil {
		log.Fatalf("Unable to create server. Cause: %v", err)
	}
	defer server.Close()
	err = server.Start()
	if err != nil {
		log.Fatalf("Unable to start server. Cause: %v", err)
	}
}
<commit_msg>go fmt<commit_after>package main

import (
	"code.google.com/p/goplan9/plan9"
	"flag"
	"fmt"
	"io"
	"log"
	"net"
	"sync"
)

var (
	listen = flag.String("listen", "127.0.0.1:5640", "Address to listen")
	help   = flag.Bool("h", false, "Help")
)

type Server struct {
	listener net.Listener
	explorer FileExplorer
}

func NewServer(lnet, laddr string, explorer FileExplorer) (*Server, error) {
	s := &Server{}
	l, err := net.Listen(lnet, laddr)
	if err != nil {
		return nil, err
	}
	s.listener = l
	s.explorer = explorer
	return s, nil
}

func (s *Server) Close() error {
	return s.listener.Close()
}

func (s *Server) Start() error {
	for {
		client, err := s.listener.Accept()
		println("got client")
		if err != nil {
			println("error", err)
			return err
		}
		cc := NewClientConn(client, s, s.explorer)
		go cc.handle()
	}
	return nil
}

func (s *Server) handleError(err error, c net.Conn) {
	log.Printf("Client %v caused error %v", c.RemoteAddr(), err)
}

func readFCall(out chan *plan9.Fcall, done chan signal, err chan error, input io.Reader) {
loop:
	for {
		select {
		case <-done:
			close(out)
			break loop
		default:
			fc, e := plan9.ReadFcall(input)
			if e != nil {
				err <- e
				close(out)
				break
			}
			out <- fc
		}
	}
}

func writeFCall(out io.Writer, done chan signal, err chan error, input chan *plan9.Fcall) {
loop:
	for {
		select {
		case <-done:
			break loop
		case fc := <-input:
			if fc == nil {
				continue
			}
			e := plan9.WriteFcall(out, fc)
			if e != nil {
				err <- e
				break
			}
		}
	}
}

// Handle all client interaction
type ClientConn struct {
	sync.RWMutex
	// remote connection
	conn net.Conn
	// server
	server *Server
	// map between path's and fileRef's
	fileRefs map[uint64]*fileRef
	// map between a fid and a qid
	fidmap map[uint32]uint64
	// explorer used to navigate the FS
	explorer FileExplorer
}

// Interface used to navigate, open and close files in the system
type FileExplorer interface {
	// Must return the unique identifier for this explorer root.
	//
	// The returned value can't be zero, since 0 is considered a non-existing path
	Root() uint64
	// Should return the unique identifier and type of the given name under the given directory
	//
	// If the name don't exists under parent, should return 0
	Walk(parent uint64, name string) (uint64, FileType)
	// Open the file for subsequent reading/writing
	//
	// If the error is nil, the file is considered ready for processing
	Open(file uint64, mode FileMode) error
}

// Represent a reference to a file
type fileRef struct {
	plan9.Qid
}

// Type of a file
type FileType uint8

const (
	FTFILE  = FileType(plan9.QTFILE)
	FTDIR   = FileType(plan9.QTDIR)
	FTMOUNT = FileType(plan9.QTMOUNT)
)

type FileMode uint8

const (
	FMREAD  = FileMode(plan9.OREAD)
	FMWRITE = FileMode(plan9.OWRITE)
	FMRDWR  = FileMode(plan9.ORDWR)
)

func NewClientConn(conn net.Conn, server *Server, explorer FileExplorer) *ClientConn {
	cc := &ClientConn{conn: conn, server: server, fileRefs: make(map[uint64]*fileRef),
		explorer: explorer, fidmap: make(map[uint32]uint64)}
	return cc
}

// used to send signal's between goroutines
type signal struct{}

func (c *ClientConn) handle() {
	defer c.Close()
	input := make(chan *plan9.Fcall)
	output := make(chan *plan9.Fcall)
	done := make(chan signal)
	err := make(chan error)

	go readFCall(input, done, err, c.conn)
	go writeFCall(c.conn, done, err, output)
loop:
	for {
		select {
		case fc := <-input:
			go c.process(fc, output)
		case e := <-err:
			c.server.handleError(e, c.conn)
			done <- signal{}
			done <- signal{}
			break loop
		}
	}
}

func (c *ClientConn) process(fc *plan9.Fcall, out chan *plan9.Fcall) {
	println(">>>\t", fc.String())
	switch fc.Type {
	case plan9.Tversion:
		fc = c.version(fc)
	case plan9.Tattach:
		fc = c.attach(fc)
	case plan9.Twalk:
		fc = c.walk(fc)
	case plan9.Topen:
		fc = c.open(fc)
	default:
		println("!!!\t", fc.String())
		fc = nil
	}
	if fc != nil {
		println("<<<\t", fc.String())
	}
	out <- fc
}

func (c *ClientConn) version(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rversion
	fc.Version = "9P2000"
	return fc
}

func (c *ClientConn) attach(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rattach
	fref, _ := c.createFileRef(c.explorer.Root(), FTMOUNT, 0)
	fc.Qid = fref.Qid
	c.bindFid(fc.Fid, fc.Qid.Path)
	return fc
}

func (c *ClientConn) walk(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rwalk
	fref, has := c.fidRef(fc.Fid)
	if !has {
		return c.invalidFidErr(fc)
	}
	if _, has := c.fidRef(fc.Newfid); has {
		return c.fidUsedErr(fc)
	}
	current := fref.Path
	for idx, name := range fc.Wname {
		var ft FileType
		current, ft = c.explorer.Walk(current, name)
		if current == 0 {
			return c.fileNotFoundErr(fc)
		}
		ref, err := c.createFileRef(current, ft, 0)
		if err != nil {
			return c.unexpectedErr(fc, err)
		}
		fc.Wqid = append(fc.Wqid, ref.Qid)

		// if the last match isn't a directory, there is no need to find
		// another part of the path
		//
		// so, just break here
		if ft == FTFILE && idx != len(fc.Wname)-1 {
			println("here")
			return c.fileNotFoundErr(fc)
		}
	}
	// make a bind between the last qid and the new fid
	c.bindFid(fc.Newfid, fc.Wqid[len(fc.Wqid)-1].Path)
	return fc
}

func (c *ClientConn) open(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Ropen
	if fref, has := c.fidRef(fc.Fid); has {
		err := c.explorer.Open(fref.Path, FileMode(fc.Mode))
		if err != nil {
			return c.unexpectedErr(fc, err)
		}
		fc.Qid = fref.Qid
		return fc
	}
	return c.invalidFidErr(fc)
}

func (c *ClientConn) invalidFidErr(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = "fid not found"
	return fc
}

func (c *ClientConn) fileNotFoundErr(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = "file not found"
	return fc
}

func (c *ClientConn) fidUsedErr(fc *plan9.Fcall) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = "fid in use"
	return fc
}

func (c *ClientConn) unexpectedErr(fc *plan9.Fcall, err error) *plan9.Fcall {
	fc.Type = plan9.Rerror
	fc.Ename = err.Error()
	return fc
}

// Return the file referenced by the given fid
func (c *ClientConn) fidRef(fid uint32) (*fileRef, bool) {
	c.RLock()
	defer c.RUnlock()
	if qid, has := c.fidmap[fid]; has {
		fref, has := c.fileRefs[qid]
		return fref, has
	}
	return nil, false
}

// Bind the given fid to the provided path
//
// if path is 0, remove the fid
func (c *ClientConn) bindFid(fid uint32, path uint64) {
	c.Lock()
	defer c.Unlock()
	_, has := c.fidmap[fid]
	if path == 0 && has {
		delete(c.fidmap, fid)
		return
	}
	c.fidmap[fid] = path
}

// Create a file for the given path, if the path is already present, then just check
// if the version/type are the same, if they are, just return the existing file, otherwise
// return an error
//
// If the version is 0, then this check is ignored and any version existing on the server is returned
//
// Every new resources created will have a version of 1 instead of 0.
func (c *ClientConn) createFileRef(path uint64, ft FileType, version uint32) (*fileRef, error) {
	c.Lock()
	defer c.Unlock()
	if fref, has := c.fileRefs[path]; has {
		if fref.Path == path && (fref.Vers == version || version == 0) && uint8(ft) == fref.Type {
			return fref, nil
		}
		return fref, fmt.Errorf("path %v already used", path)
	}
	fref := &fileRef{}
	if version == 0 {
		version = 1
	}
	fref.Type = uint8(ft)
	fref.Vers = version
	fref.Path = path
	c.fileRefs[path] = fref
	return fref, nil
}

func (c *ClientConn) Close() error {
	return c.conn.Close()
}

type dummyExplorer struct{}

func (d dummyExplorer) Root() uint64 {
	return 1
}

func (d dummyExplorer) Walk(parent uint64, name string) (uint64, FileType) {
	println("\t...searching ", parent, " for file: ", name)
	if parent == 1 && name == "dummy" {
		return 2, FTFILE
	}
	return 0, FTFILE
}

func (d dummyExplorer) Open(file uint64, mode FileMode) error {
	if file != 2 {
		return fmt.Errorf("file not found")
	}
	if mode != FMREAD {
		return fmt.Errorf("file is read only")
	}
	return nil
}

func main() {
	flag.Parse()
	if *help {
		flag.Usage()
		return
	}
	server, err := NewServer("tcp", *listen, dummyExplorer{})
	if err != nil {
		log.Fatalf("Unable to create server. Cause: %v", err)
	}
	defer server.Close()
	err = server.Start()
	if err != nil {
		log.Fatalf("Unable to start server. Cause: %v", err)
	}
}
<|endoftext|>


<commit_before>package gqt_test

import (
	"io"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Security", func() {
	var (
		client *runner.RunningGarden
	)

	JustBeforeEach(func() {
		client = runner.Start(config)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	Describe("Apparmor", func() {
		Context("when the --apparmor flag is pointing to a loaded policy", func() {
			BeforeEach(func() {
				config.AppArmor = "garden-default"
			})

			itEnforcesAppArmorPolicy := func(image garden.ImageRef) {
				It("should enforce the policy when running processes in unprivileged containers", func() {
					container, err := client.Create(garden.ContainerSpec{})
					Expect(err).NotTo(HaveOccurred())

					buffer := gbytes.NewBuffer()

					_, err = container.Run(garden.ProcessSpec{
						Path:  "cat",
						Args:  []string{"/proc/self/attr/current"},
						Image: image,
					}, garden.ProcessIO{
						Stdout: io.MultiWriter(GinkgoWriter, buffer),
						Stderr: GinkgoWriter,
					})
					Expect(err).NotTo(HaveOccurred())

					Eventually(buffer).Should(gbytes.Say("garden-default"))
				})
			}

			itEnforcesAppArmorPolicy(garden.ImageRef{})

			Context("when running a pea", func() {
				var (
					peaRootfs string
				)

				BeforeEach(func() {
					peaRootfs = createPeaRootfs()
				})

				itEnforcesAppArmorPolicy(garden.ImageRef{URI: peaRootfs})
			})

			It("should not enforce the policy when running processes in privileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{
					Privileged: true,
				})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})
		})

		Context("when the --apparmor flag is not set", func() {
			It("should not enforce the policy when running processes in unprivileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})

			It("should not enforce the policy when running processes in privileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{
					Privileged: true,
				})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})
		})

		Context("when the --apparmor flag is pointing to a non-existing policy", func() {
			BeforeEach(func() {
				config.AppArmor = "non-existing-policy"
			})

			It("should return an error when creating a container", func() {
				_, err := client.Create(garden.ContainerSpec{})
				Expect(err).To(HaveOccurred())
			})
		})
	})
})
<commit_msg>Slightly re-write a test<commit_after>package gqt_test

import (
	"io"

	"code.cloudfoundry.org/garden"
	"code.cloudfoundry.org/guardian/gqt/runner"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
	"github.com/onsi/gomega/gbytes"
)

var _ = Describe("Security", func() {
	var (
		client *runner.RunningGarden
	)

	JustBeforeEach(func() {
		client = runner.Start(config)
	})

	AfterEach(func() {
		Expect(client.DestroyAndStop()).To(Succeed())
	})

	Describe("Apparmor", func() {
		Context("when the --apparmor flag is pointing to a loaded policy", func() {
			BeforeEach(func() {
				config.AppArmor = "garden-default"
			})

			Context("when running processes in unprivileged containers", func() {
				var (
					container garden.Container
					err       error
				)

				JustBeforeEach(func() {
					container, err = client.Create(garden.ContainerSpec{})
					Expect(err).NotTo(HaveOccurred())
				})

				It("should enforce the policy", func() {
					buffer := gbytes.NewBuffer()

					_, err = container.Run(garden.ProcessSpec{
						Path:  "cat",
						Args:  []string{"/proc/self/attr/current"},
						Image: garden.ImageRef{},
					}, garden.ProcessIO{
						Stdout: io.MultiWriter(GinkgoWriter, buffer),
						Stderr: GinkgoWriter,
					})
					Expect(err).NotTo(HaveOccurred())

					Eventually(buffer).Should(gbytes.Say("garden-default"))
				})

				Context("when running a pea", func() {
					var (
						peaRootfs string
					)

					BeforeEach(func() {
						peaRootfs = createPeaRootfsTar()
					})

					It("should enforce the policy", func() {
						buffer := gbytes.NewBuffer()

						_, err = container.Run(garden.ProcessSpec{
							Path:  "cat",
							Args:  []string{"/proc/self/attr/current"},
							Image: garden.ImageRef{URI: peaRootfs},
						}, garden.ProcessIO{
							Stdout: io.MultiWriter(GinkgoWriter, buffer),
							Stderr: GinkgoWriter,
						})
						Expect(err).NotTo(HaveOccurred())

						Eventually(buffer).Should(gbytes.Say("garden-default"))
					})
				})
			})

			Context("when running processes in privileged containers", func() {
				It("should not enforce the policy", func() {
					container, err := client.Create(garden.ContainerSpec{
						Privileged: true,
					})
					Expect(err).NotTo(HaveOccurred())

					buffer := gbytes.NewBuffer()

					_, err = container.Run(garden.ProcessSpec{
						Path: "cat",
						Args: []string{"/proc/self/attr/current"},
					}, garden.ProcessIO{
						Stdout: io.MultiWriter(GinkgoWriter, buffer),
						Stderr: GinkgoWriter,
					})
					Expect(err).NotTo(HaveOccurred())

					Eventually(buffer).Should(gbytes.Say("unconfined"))
				})
			})
		})

		Context("when the --apparmor flag is not set", func() {
			It("should not enforce the policy when running processes in unprivileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})

			It("should not enforce the policy when running processes in privileged containers", func() {
				container, err := client.Create(garden.ContainerSpec{
					Privileged: true,
				})
				Expect(err).NotTo(HaveOccurred())

				buffer := gbytes.NewBuffer()

				_, err = container.Run(garden.ProcessSpec{
					Path: "cat",
					Args: []string{"/proc/self/attr/current"},
				}, garden.ProcessIO{
					Stdout: io.MultiWriter(GinkgoWriter, buffer),
					Stderr: GinkgoWriter,
				})
				Expect(err).NotTo(HaveOccurred())

				Eventually(buffer).Should(gbytes.Say("unconfined"))
			})
		})

		Context("when the --apparmor flag is pointing to a non-existing policy", func() {
			BeforeEach(func() {
				config.AppArmor = "non-existing-policy"
			})

			It("should return an error when creating a container", func() {
				_, err := client.Create(garden.ContainerSpec{})
				Expect(err).To(HaveOccurred())
			})
		})
	})
})
<|endoftext|>


<commit_before>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

// Package grpclogger creates a logger interface to be used in grpc logger
// package (google.golang.org/grpc/grpclog). For more information on grpc see
// http://www.grpc.io, for grpc-go see https://github.com/grpc/grpc-go.
package grpclogger

import (
	"errors"
	"fmt"
	"os"

	"github.com/Thomasdezeeuw/logger"
	"github.com/Thomasdezeeuw/logger/internal/util"
	"google.golang.org/grpc/grpclog"
)

type log struct {
	tags    logger.Tags
	closeFn func()
}

func (log *log) Fatal(args ...interface{}) {
	msg := util.InterfaceToString(args)
	logger.Fatal(log.tags, msg)
	exit(log.closeFn)
}

func (log *log) Fatalf(format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	log.Fatal(msg)
}

func (log *log) Fatalln(args ...interface{}) {
	log.Fatal(args...)
}

func (log *log) Print(args ...interface{}) {
	msg := util.InterfaceToString(args)
	logger.Error(log.tags, errors.New(msg))
}

func (log *log) Printf(format string, args ...interface{}) {
	logger.Errorf(log.tags, format, args...)
}

func (log *log) Println(args ...interface{}) {
	log.Print(args...)
}

// Stubbed for testing.
var osExit = os.Exit

// Hate to do this, but it is what the default log package does.
// Stubbed for testing.
var exit = func(closeFn func()) {
	closeFn()
	osExit(1)
}

// CreateLogger creates a new logger that can be used in grpc/grpclog. It
// follows the default log package style of logging and therefore it doesn't
// have log levels (EventType). Because of this all calls to Print* will call
// logger.Error, hoping that grpclogger will only log errors in production.
//
// Another shortcoming of the Logger interface defined by grpclog is that uses
// an empty interface a lot. We combat this by trying to make this into a single
// string using the fmt package. As result of this the message might not look
// very pretty, but it will contain all the provided information.
//
// A third point is that a call to Fatal* in the builtin log package calls to
// os.Exit, which closes the application immediately without running deffered
// statements. To combat that we accept a close function which runs before the
// call to os.Exit. In this function logger.Close must be called by the user.
func CreateLogger(tags logger.Tags, closeFn func()) grpclog.Logger {
	return &log{tags, closeFn}
}
<commit_msg>Fix grpclogger<commit_after>// Copyright (C) 2015-2016 Thomas de Zeeuw.
//
// Licensed under the MIT license that can be found in the LICENSE file.

// Package grpclogger creates a logger interface to be used in grpc logger
// package (google.golang.org/grpc/grpclog). For more information on grpc see
// http://www.grpc.io, for grpc-go see https://github.com/grpc/grpc-go.
package grpclogger

import (
	"errors"
	"fmt"
	"os"

	"github.com/Thomasdezeeuw/logger"
	"github.com/Thomasdezeeuw/logger/internal/util"
	"google.golang.org/grpc/grpclog"
)

type log struct {
	tags    logger.Tags
	closeFn func()
}

func (log *log) Fatal(args ...interface{}) {
	msg := util.InterfacesToString(args)
	logger.Fatal(log.tags, msg)
	exit(log.closeFn)
}

func (log *log) Fatalf(format string, args ...interface{}) {
	msg := fmt.Sprintf(format, args...)
	log.Fatal(msg)
}

func (log *log) Fatalln(args ...interface{}) {
	log.Fatal(args...)
}

func (log *log) Print(args ...interface{}) {
	msg := util.InterfacesToString(args)
	logger.Error(log.tags, errors.New(msg))
}

func (log *log) Printf(format string, args ...interface{}) {
	logger.Errorf(log.tags, format, args...)
}

func (log *log) Println(args ...interface{}) {
	log.Print(args...)
}

// Stubbed for testing.
var osExit = os.Exit

// Hate to do this, but it is what the default log package does.
// Stubbed for testing.
var exit = func(closeFn func()) {
	closeFn()
	osExit(1)
}

// CreateLogger creates a new logger that can be used in grpc/grpclog. It
// follows the default log package style of logging and therefore it doesn't
// have log levels (EventType). Because of this all calls to Print* will call
// logger.Error, hoping that grpclogger will only log errors in production.
//
// Another shortcoming of the Logger interface defined by grpclog is that uses
// an empty interface a lot. We combat this by trying to make this into a single
// string using the fmt package. As result of this the message might not look
// very pretty, but it will contain all the provided information.
//
// A third point is that a call to Fatal* in the builtin log package calls to
// os.Exit, which closes the application immediately without running deffered
// statements. To combat that we accept a close function which runs before the
// call to os.Exit. In this function logger.Close must be called by the user.
func CreateLogger(tags logger.Tags, closeFn func()) grpclog.Logger {
	return &log{tags, closeFn}
}
<|endoftext|>


<commit_before>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package main_test

import (
	"strings"
	"testing"

	"sigs.k8s.io/kustomize/v3/pkg/kusttest"
	"sigs.k8s.io/kustomize/v3/pkg/plugins"
)

const (
	target = `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  replica: 2
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - name: nginx
        image: nginx
`
	targetWithNamespace = `
apiVersion: apps/v1
metadata:
  name: myDeploy
  namespace: namespace1
kind: Deployment
spec:
  replica: 2
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - name: nginx
        image: nginx
`
	targetNoschema = `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    A: X
    B: Y
`
)

func TestPatchStrategicMergeTransformerMissingFile(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")
	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, target)
	if err == nil {
		t.Fatalf("expected error")
	}
	if !strings.Contains(err.Error(),
		"cannot read file \"/app/patch.yaml\"") {
		t.Fatalf("unexpected err: %v", err)
	}
}

func TestBadPatchStrategicMergeTransformer(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
patches: 'thisIsNotAPatch'
`, target)
	if err == nil {
		t.Fatalf("expected error")
	}
	if !strings.Contains(err.Error(),
		"cannot unmarshal string into Go value of type map[string]interface {}") {
		t.Fatalf("unexpected err: %v", err)
	}
}

func TestBothEmptyPatchStrategicMergeTransformer(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
`, target)
	if err == nil {
		t.Fatalf("expected error")
	}
	if !strings.Contains(err.Error(), "empty file path and empty patch content") {
		t.Fatalf("unexpected err: %v", err)
	}
}

func TestPatchStrategicMergeTransformerFromFiles(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  replica: 3
`)

	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, target)

	th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myDeploy
spec:
  replica: 3
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - image: nginx
        name: nginx
`)
}

func TestPatchStrategicMergeTransformerWithInline(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
patches: '{"apiVersion": "apps/v1", "metadata": {"name": "myDeploy"}, "kind": "Deployment", "spec": {"replica": 3}}'
`, target)

	th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myDeploy
spec:
  replica: 3
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - image: nginx
        name: nginx
`)
}

func TestPatchStrategicMergeTransformerMultiplePatches(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
        - name: SOMEENV
          value: BAR
`)

	th.WriteF("/app/patch2.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        env:
        - name: ANOTHERENV
          value: HELLO
      - name: busybox
        image: busybox
`)

	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, target)

	th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myDeploy
spec:
  replica: 2
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - env:
        - name: ANOTHERENV
          value: HELLO
        - name: SOMEENV
          value: BAR
        image: nginx:latest
        name: nginx
      - image: busybox
        name: busybox
`)
}

func TestStrategicMergeTransformerMultiplePatchesWithConflicts(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
        - name: SOMEENV
          value: BAR
`)

	th.WriteF("/app/patch2.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        env:
        - name: ANOTHERENV
          value: HELLO
      - name: busybox
        image: busybox
`)

	err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, target)

	if err == nil {
		t.Fatalf("did not get expected error")
	}
	if !strings.Contains(err.Error(), "conflict") {
		t.Fatalf("expected error to contain %q but get %v", "conflict", err)
	}
}

func TestStrategicMergeTransformerWrongNamespace(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
  namespace: namespace2
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
        - name: SOMEENV
          value: BAR
`)

	err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, targetWithNamespace)

	if err == nil {
		t.Fatalf("did not get expected error")
	}
	if !strings.Contains(err.Error(), "failed to find unique target for patch") {
		t.Fatalf("expected error to contain %q but get %v", "failed to find target for patch", err)
	}
}

func TestStrategicMergeTransformerNoSchema(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    B:
    C: Z
`)
	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, targetNoschema)

	th.AssertActualEqualsExpected(rm, `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    A: X
    C: Z
`)
}

func TestStrategicMergeTransformerNoSchemaMultiPatches(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    B:
    C: Z
`)
	th.WriteF("/app/patch2.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    C: Z
    D: W
  baz:
    hello: world
`)
	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, targetNoschema)

	th.AssertActualEqualsExpected(rm, `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    A: X
    C: Z
    D: W
  baz:
    hello: world
`)
}

func TestStrategicMergeTransformerNoSchemaMultiPatchesWithConflict(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    C: Z
`)
	th.WriteF("/app/patch2.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    C: NOT_Z

`)
	err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, targetNoschema)
	if !strings.Contains(err.Error(), "conflict") {
		t.Fatalf("expected error to contain %q but get %v", "conflict", err)
	}
}
<commit_msg>add labels in test patch files<commit_after>// Copyright 2019 The Kubernetes Authors.
// SPDX-License-Identifier: Apache-2.0

package main_test

import (
	"strings"
	"testing"

	"sigs.k8s.io/kustomize/v3/pkg/kusttest"
	"sigs.k8s.io/kustomize/v3/pkg/plugins"
)

const (
	target = `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  replica: 2
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - name: nginx
        image: nginx
`
	targetWithNamespace = `
apiVersion: apps/v1
metadata:
  name: myDeploy
  namespace: namespace1
kind: Deployment
spec:
  replica: 2
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - name: nginx
        image: nginx
`
	targetNoschema = `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    A: X
    B: Y
`
)

func TestPatchStrategicMergeTransformerMissingFile(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")
	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, target)
	if err == nil {
		t.Fatalf("expected error")
	}
	if !strings.Contains(err.Error(),
		"cannot read file \"/app/patch.yaml\"") {
		t.Fatalf("unexpected err: %v", err)
	}
}

func TestBadPatchStrategicMergeTransformer(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
patches: 'thisIsNotAPatch'
`, target)
	if err == nil {
		t.Fatalf("expected error")
	}
	if !strings.Contains(err.Error(),
		"cannot unmarshal string into Go value of type map[string]interface {}") {
		t.Fatalf("unexpected err: %v", err)
	}
}

func TestBothEmptyPatchStrategicMergeTransformer(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	_, err := th.RunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
`, target)
	if err == nil {
		t.Fatalf("expected error")
	}
	if !strings.Contains(err.Error(), "empty file path and empty patch content") {
		t.Fatalf("unexpected err: %v", err)
	}
}

func TestPatchStrategicMergeTransformerFromFiles(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    metadata:
      labels:
        new-label: new-value
  replica: 3
`)

	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, target)

	th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myDeploy
spec:
  replica: 3
  template:
    metadata:
      labels:
        new-label: new-value
        old-label: old-value
    spec:
      containers:
      - image: nginx
        name: nginx
`)
}

func TestPatchStrategicMergeTransformerWithInline(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
patches: '{"apiVersion": "apps/v1", "metadata": {"name": "myDeploy"}, "kind": "Deployment", "spec": {"replica": 3}}'
`, target)

	th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myDeploy
spec:
  replica: 3
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - image: nginx
        name: nginx
`)
}

func TestPatchStrategicMergeTransformerMultiplePatches(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
        - name: SOMEENV
          value: BAR
`)

	th.WriteF("/app/patch2.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        env:
        - name: ANOTHERENV
          value: HELLO
      - name: busybox
        image: busybox
`)

	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, target)

	th.AssertActualEqualsExpected(rm, `
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myDeploy
spec:
  replica: 2
  template:
    metadata:
      labels:
        old-label: old-value
    spec:
      containers:
      - env:
        - name: ANOTHERENV
          value: HELLO
        - name: SOMEENV
          value: BAR
        image: nginx:latest
        name: nginx
      - image: busybox
        name: busybox
`)
}

func TestStrategicMergeTransformerMultiplePatchesWithConflicts(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
        - name: SOMEENV
          value: BAR
`)

	th.WriteF("/app/patch2.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:1.7.9
        env:
        - name: ANOTHERENV
          value: HELLO
      - name: busybox
        image: busybox
`)

	err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, target)

	if err == nil {
		t.Fatalf("did not get expected error")
	}
	if !strings.Contains(err.Error(), "conflict") {
		t.Fatalf("expected error to contain %q but get %v", "conflict", err)
	}
}

func TestStrategicMergeTransformerWrongNamespace(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch.yaml", `
apiVersion: apps/v1
metadata:
  name: myDeploy
  namespace: namespace2
kind: Deployment
spec:
  template:
    spec:
      containers:
      - name: nginx
        image: nginx:latest
        env:
        - name: SOMEENV
          value: BAR
`)

	err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, targetWithNamespace)

	if err == nil {
		t.Fatalf("did not get expected error")
	}
	if !strings.Contains(err.Error(), "failed to find unique target for patch") {
		t.Fatalf("expected error to contain %q but get %v", "failed to find target for patch", err)
	}
}

func TestStrategicMergeTransformerNoSchema(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    B:
    C: Z
`)
	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch.yaml
`, targetNoschema)

	th.AssertActualEqualsExpected(rm, `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    A: X
    C: Z
`)
}

func TestStrategicMergeTransformerNoSchemaMultiPatches(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    B:
    C: Z
`)
	th.WriteF("/app/patch2.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    C: Z
    D: W
  baz:
    hello: world
`)
	rm := th.LoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, targetNoschema)

	th.AssertActualEqualsExpected(rm, `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    A: X
    C: Z
    D: W
  baz:
    hello: world
`)
}

func TestStrategicMergeTransformerNoSchemaMultiPatchesWithConflict(t *testing.T) {
	tc := plugins.NewEnvForTest(t).Set()
	defer tc.Reset()

	tc.BuildGoPlugin(
		"builtin", "", "PatchStrategicMergeTransformer")

	th := kusttest_test.NewKustTestPluginHarness(t, "/app")

	th.WriteF("/app/patch1.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    C: Z
`)
	th.WriteF("/app/patch2.yaml", `
apiVersion: example.com/v1
kind: Foo
metadata:
  name: my-foo
spec:
  bar:
    C: NOT_Z

`)
	err := th.ErrorFromLoadAndRunTransformer(`
apiVersion: builtin
kind: PatchStrategicMergeTransformer
metadata:
  name: notImportantHere
paths:
- patch1.yaml
- patch2.yaml
`, targetNoschema)
	if !strings.Contains(err.Error(), "conflict") {
		t.Fatalf("expected error to contain %q but get %v", "conflict", err)
	}
}
<|endoftext|>


<commit_before>package lists

import (
	"strconv"

	"github.com/zimmski/tavor/token"
)

// ListItem implements a list item token which references a List token and holds one index of the list to reference a list item
type ListItem struct {
	index int
	list  token.ListToken
}

// NewListItem returns a new instance of a ListItem token referencing the given list and the given index
func NewListItem(index int, list token.ListToken) *ListItem {
	return &ListItem{
		index: index,
		list:  list,
	}
}

// Token interface methods

// Clone returns a copy of the token and all its children
func (l *ListItem) Clone() token.Token {
	return &ListItem{
		index: l.index,
		list:  l.list,
	}
}

// Parse tries to parse the token beginning from the current position in the parser data.
// If the parsing is successful the error argument is nil and the next current position after the token is returned.
func (l *ListItem) Parse(pars *token.InternalParser, cur int) (int, []error) {
	panic("TODO implement")
}

// Permutation sets a specific permutation for this token
func (l *ListItem) Permutation(i uint) error {
	permutations := l.Permutations()

	if i < 1 || i > permutations {
		return &token.PermutationError{
			Type: token.PermutationErrorIndexOutOfBound,
		}
	}

	// do nothing

	return nil
}

// Permutations returns the number of permutations for this token
func (l *ListItem) Permutations() uint {
	return 1
}

// PermutationsAll returns the number of all possible permutations for this token including its children
func (l *ListItem) PermutationsAll() uint {
	return l.Permutations()
}

func (l *ListItem) String() string {
	tok, err := l.list.Get(l.index)
	if err != nil {
		panic(err) // TODO
	}

	return tok.String()
}

// IndexToken interface methods

// Index returns the index of this token in its parent token
func (l *ListItem) Index() int {
	return l.index
}

// IndexItem implements a list item which references an Index token to represent the index itself of this token
type IndexItem struct {
	token token.IndexToken
}

// NewIndexItem returns a new instance of a IndexItem token referencing the given Index token
func NewIndexItem(tok token.IndexToken) *IndexItem {
	return &IndexItem{
		token: tok.Clone().(token.IndexToken),
	}
}

// Token interface methods

// Clone returns a copy of the token and all its children
func (l *IndexItem) Clone() token.Token {
	return &IndexItem{
		token: l.token.Clone().(token.IndexToken),
	}
}

// Parse tries to parse the token beginning from the current position in the parser data.
// If the parsing is successful the error argument is nil and the next current position after the token is returned.
func (l *IndexItem) Parse(pars *token.InternalParser, cur int) (int, []error) {
	panic("TODO implement")
}

// Permutation sets a specific permutation for this token
func (l *IndexItem) Permutation(i uint) error {
	permutations := l.Permutations()

	if i < 1 || i > permutations {
		return &token.PermutationError{
			Type: token.PermutationErrorIndexOutOfBound,
		}
	}

	// do nothing

	return nil
}

// Permutations returns the number of permutations for this token
func (l *IndexItem) Permutations() uint {
	return 1
}

// PermutationsAll returns the number of all possible permutations for this token including its children
func (l *IndexItem) PermutationsAll() uint {
	return l.Permutations()
}

func (l *IndexItem) String() string {
	return strconv.Itoa(l.token.Index())
}

// ScopeToken interface methods

// SetScope sets the scope of the token
func (l *IndexItem) SetScope(variableScope map[string]token.Token) {
	if tok, ok := l.token.(token.ScopeToken); ok {
		tok.SetScope(variableScope)
	}
}

// UniqueItem implements a list item token which holds an distinct list item of a referenced List token
type UniqueItem struct {
	original *UniqueItem
	list     token.ListToken
	picked   map[int]struct{}

	index int
}

// NewUniqueItem returns a new instance of a UniqueItem token referencing the given List token
func NewUniqueItem(list token.ListToken) *UniqueItem {
	l := &UniqueItem{
		list:   list,
		picked: make(map[int]struct{}),

		index: -1,
	}

	l.original = l

	return l
}

func (l *UniqueItem) pick(i int) {
	nList := l.original.list.Len()
	nPicked := len(l.original.picked)

	if nPicked >= nList {
		panic("already picked everything!") // TODO
	}

	// TODO make this WAYYYYYYYYY more effiecent
	for {
		c := i

		if _, ok := l.original.picked[c]; !ok {
			l.index = c
			l.original.picked[c] = struct{}{}

			break
		}
	}
}

// Token interface methods

// Clone returns a copy of the token and all its children
func (l *UniqueItem) Clone() token.Token {
	n := &UniqueItem{
		original: l.original,
		list:     nil,
		picked:   nil,

		index: -1,
	}

	return n
}

/*
// Fuzz fuzzes this token using the random generator by choosing one of the possible permutations for this token
func (l *UniqueItem) Fuzz(r rand.Rand) {
	if l.index == -1 {
		l.pick(r)
	}
}
*/

// Parse tries to parse the token beginning from the current position in the parser data.
// If the parsing is successful the error argument is nil and the next current position after the token is returned.
func (l *UniqueItem) Parse(pars *token.InternalParser, cur int) (int, []error) {
	panic("TODO implement")
}

// Permutation sets a specific permutation for this token
func (l *UniqueItem) Permutation(i uint) error {
	permutations := l.Permutations()

	if i < 1 || i > permutations {
		return &token.PermutationError{
			Type: token.PermutationErrorIndexOutOfBound,
		}
	}

	// do nothing

	return nil
}

// Permutations returns the number of permutations for this token
func (l *UniqueItem) Permutations() uint {
	return 1
}

// PermutationsAll returns the number of all possible permutations for this token including its children
func (l *UniqueItem) PermutationsAll() uint {
	return l.Permutations()
}

func (l *UniqueItem) String() string {
	i := l.Index()

	tok, err := l.original.list.Get(i)
	if err != nil {
		panic(err) // TODO
	}

	return tok.String()
}

// IndexToken interface methods

// Index returns the index of this token in its parent token
func (l *UniqueItem) Index() int {
	if l.index == -1 {
		l.pick(0)
	}

	return l.index
}

// ResetToken interface methods

// Reset resets the (internal) state of this token and its dependences
func (l *UniqueItem) Reset() {
	if l.index != -1 {
		delete(l.original.picked, l.index)

		l.index = -1
	}
}
<commit_msg>forgot one TODO mark<commit_after>package lists

import (
	"strconv"

	"github.com/zimmski/tavor/token"
)

// ListItem implements a list item token which references a List token and holds one index of the list to reference a list item
type ListItem struct {
	index int
	list  token.ListToken
}

// NewListItem returns a new instance of a ListItem token referencing the given list and the given index
func NewListItem(index int, list token.ListToken) *ListItem {
	return &ListItem{
		index: index,
		list:  list,
	}
}

// Token interface methods

// Clone returns a copy of the token and all its children
func (l *ListItem) Clone() token.Token {
	return &ListItem{
		index: l.index,
		list:  l.list,
	}
}

// Parse tries to parse the token beginning from the current position in the parser data.
// If the parsing is successful the error argument is nil and the next current position after the token is returned.
func (l *ListItem) Parse(pars *token.InternalParser, cur int) (int, []error) {
	panic("TODO implement")
}

// Permutation sets a specific permutation for this token
func (l *ListItem) Permutation(i uint) error {
	permutations := l.Permutations()

	if i < 1 || i > permutations {
		return &token.PermutationError{
			Type: token.PermutationErrorIndexOutOfBound,
		}
	}

	// do nothing

	return nil
}

// Permutations returns the number of permutations for this token
func (l *ListItem) Permutations() uint {
	return 1
}

// PermutationsAll returns the number of all possible permutations for this token including its children
func (l *ListItem) PermutationsAll() uint {
	return l.Permutations()
}

func (l *ListItem) String() string {
	tok, err := l.list.Get(l.index)
	if err != nil {
		panic(err) // TODO
	}

	return tok.String()
}

// IndexToken interface methods

// Index returns the index of this token in its parent token
func (l *ListItem) Index() int {
	return l.index
}

// IndexItem implements a list item which references an Index token to represent the index itself of this token
type IndexItem struct {
	token token.IndexToken
}

// NewIndexItem returns a new instance of a IndexItem token referencing the given Index token
func NewIndexItem(tok token.IndexToken) *IndexItem {
	return &IndexItem{
		token: tok.Clone().(token.IndexToken),
	}
}

// Token interface methods

// Clone returns a copy of the token and all its children
func (l *IndexItem) Clone() token.Token {
	return &IndexItem{
		token: l.token.Clone().(token.IndexToken),
	}
}

// Parse tries to parse the token beginning from the current position in the parser data.
// If the parsing is successful the error argument is nil and the next current position after the token is returned.
func (l *IndexItem) Parse(pars *token.InternalParser, cur int) (int, []error) {
	panic("TODO implement")
}

// Permutation sets a specific permutation for this token
func (l *IndexItem) Permutation(i uint) error {
	permutations := l.Permutations()

	if i < 1 || i > permutations {
		return &token.PermutationError{
			Type: token.PermutationErrorIndexOutOfBound,
		}
	}

	// do nothing

	return nil
}

// Permutations returns the number of permutations for this token
func (l *IndexItem) Permutations() uint {
	return 1
}

// PermutationsAll returns the number of all possible permutations for this token including its children
func (l *IndexItem) PermutationsAll() uint {
	return l.Permutations()
}

func (l *IndexItem) String() string {
	return strconv.Itoa(l.token.Index())
}

// ScopeToken interface methods

// SetScope sets the scope of the token
func (l *IndexItem) SetScope(variableScope map[string]token.Token) {
	if tok, ok := l.token.(token.ScopeToken); ok {
		tok.SetScope(variableScope)
	}
}

// UniqueItem implements a list item token which holds an distinct list item of a referenced List token
type UniqueItem struct {
	original *UniqueItem
	list     token.ListToken
	picked   map[int]struct{}

	index int
}

// NewUniqueItem returns a new instance of a UniqueItem token referencing the given List token
func NewUniqueItem(list token.ListToken) *UniqueItem {
	l := &UniqueItem{
		list:   list,
		picked: make(map[int]struct{}),

		index: -1,
	}

	l.original = l

	return l
}

func (l *UniqueItem) pick(i int) {
	nList := l.original.list.Len()
	nPicked := len(l.original.picked)

	if nPicked >= nList {
		panic("already picked everything!") // TODO
	}

	// TODO make this WAYYYYYYYYY more effiecent
	for {
		c := i

		if _, ok := l.original.picked[c]; !ok {
			l.index = c
			l.original.picked[c] = struct{}{}

			break
		}
	}
}

// Token interface methods

// Clone returns a copy of the token and all its children
func (l *UniqueItem) Clone() token.Token {
	n := &UniqueItem{
		original: l.original,
		list:     nil,
		picked:   nil,

		index: -1,
	}

	return n
}

/* TODO FIXME this
// Fuzz fuzzes this token using the random generator by choosing one of the possible permutations for this token
func (l *UniqueItem) Fuzz(r rand.Rand) {
	if l.index == -1 {
		l.pick(r)
	}
}
*/

// Parse tries to parse the token beginning from the current position in the parser data.
// If the parsing is successful the error argument is nil and the next current position after the token is returned.
func (l *UniqueItem) Parse(pars *token.InternalParser, cur int) (int, []error) {
	panic("TODO implement")
}

// Permutation sets a specific permutation for this token
func (l *UniqueItem) Permutation(i uint) error {
	permutations := l.Permutations()

	if i < 1 || i > permutations {
		return &token.PermutationError{
			Type: token.PermutationErrorIndexOutOfBound,
		}
	}

	// do nothing

	return nil
}

// Permutations returns the number of permutations for this token
func (l *UniqueItem) Permutations() uint {
	return 1
}

// PermutationsAll returns the number of all possible permutations for this token including its children
func (l *UniqueItem) PermutationsAll() uint {
	return l.Permutations()
}

func (l *UniqueItem) String() string {
	i := l.Index()

	tok, err := l.original.list.Get(i)
	if err != nil {
		panic(err) // TODO
	}

	return tok.String()
}

// IndexToken interface methods

// Index returns the index of this token in its parent token
func (l *UniqueItem) Index() int {
	if l.index == -1 {
		l.pick(0)
	}

	return l.index
}

// ResetToken interface methods

// Reset resets the (internal) state of this token and its dependences
func (l *UniqueItem) Reset() {
	if l.index != -1 {
		delete(l.original.picked, l.index)

		l.index = -1
	}
}
<|endoftext|>


<commit_before><commit_msg>Use the DNS and IP SANs from the auto config stanza when set<commit_after><|endoftext|>