<commit_before>// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package bloom

import (
	"encoding/binary"
	"math"
	"sync"

	"github.com/btcboost/copernicus/model/outpoint"
	"github.com/btcboost/copernicus/model/script"
	"github.com/btcboost/copernicus/model/tx"
	"github.com/btcboost/copernicus/net/wire"
	"github.com/btcboost/copernicus/util"
	"github.com/pkg/errors"
)

type MsgTx tx.Tx

// ln2Squared is simply the square of the natural log of 2.
const ln2Squared = math.Ln2 * math.Ln2

// minUint32 is a convenience function to return the minimum value of the two
// passed uint32 values.
func minUint32(a, b uint32) uint32 {
	if a < b {
		return a
	}
	return b
}

// Filter defines a bitcoin bloom filter that provides easy manipulation of raw
// filter data.
type Filter struct {
	mtx           sync.Mutex
	msgFilterLoad *wire.MsgFilterLoad
}

// NewFilter creates a new bloom filter instance, mainly to be used by SPV
// clients.  The tweak parameter is a random value added to the seed value.
// The false positive rate is the probability of a false positive where 1.0 is
// "match everything" and zero is unachievable.  Thus, providing any false
// positive rates less than 0 or greater than 1 will be adjusted to the valid
// range.
//
// For more information on what values to use for both elements and fprate,
// see https://en.wikipedia.org/wiki/Bloom_filter.
func NewFilter(elements, tweak uint32, fprate float64, flags wire.BloomUpdateType) *Filter {
	// Massage the false positive rate to sane values.
	if fprate > 1.0 {
		fprate = 1.0
	}
	if fprate < 1e-9 {
		fprate = 1e-9
	}

	// Calculate the size of the filter in bytes for the given number of
	// elements and false positive rate.
	//
	// Equivalent to m = -(n*ln(p) / ln(2)^2), where m is in bits.
	// Then clamp it to the maximum filter size and convert to bytes.
	dataLen := uint32(-1 * float64(elements) * math.Log(fprate) / ln2Squared)
	dataLen = minUint32(dataLen, wire.MaxFilterLoadFilterSize*8) / 8

	// Calculate the number of hash functions based on the size of the
	// filter calculated above and the number of elements.
	//
	// Equivalent to k = (m/n) * ln(2)
	// Then clamp it to the maximum allowed hash funcs.
	hashFuncs := uint32(float64(dataLen*8) / float64(elements) * math.Ln2)
	hashFuncs = minUint32(hashFuncs, wire.MaxFilterLoadHashFuncs)

	data := make([]byte, dataLen)
	msg := wire.NewMsgFilterLoad(data, hashFuncs, tweak, flags)

	return &Filter{
		msgFilterLoad: msg,
	}
}

// LoadFilter creates a new Filter instance with the given underlying
// wire.MsgFilterLoad.
func LoadFilter(filter *wire.MsgFilterLoad) *Filter {
	return &Filter{
		msgFilterLoad: filter,
	}
}

// IsLoaded returns true if a filter is loaded, otherwise false.
//
// This function is safe for concurrent access.
func (bf *Filter) IsLoaded() bool {
	bf.mtx.Lock()
	loaded := bf.msgFilterLoad != nil
	bf.mtx.Unlock()
	return loaded
}

// Reload loads a new filter replacing any existing filter.
//
// This function is safe for concurrent access.
func (bf *Filter) Reload(filter *wire.MsgFilterLoad) {
	bf.mtx.Lock()
	bf.msgFilterLoad = filter
	bf.mtx.Unlock()
}

// Unload unloads the bloom filter.
//
// This function is safe for concurrent access.
func (bf *Filter) Unload() {
	bf.mtx.Lock()
	bf.msgFilterLoad = nil
	bf.mtx.Unlock()
}

// hash returns the bit offset in the bloom filter which corresponds to the
// passed data for the given indepedent hash function number.
func (bf *Filter) hash(hashNum uint32, data []byte) uint32 {
	// bitcoind: 0xfba4c795 chosen as it guarantees a reasonable bit
	// difference between hashNum values.
	//
	// Note that << 3 is equivalent to multiplying by 8, but is faster.
	// Thus the returned hash is brought into range of the number of bits
	// the filter has and returned.
	mm := MurmurHash3(hashNum*0xfba4c795+bf.msgFilterLoad.Tweak, data)
	return mm % (uint32(len(bf.msgFilterLoad.Filter)) << 3)
}

// matches returns true if the bloom filter might contain the passed data and
// false if it definitely does not.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matches(data []byte) bool {
	if bf.msgFilterLoad == nil {
		return false
	}

	// The bloom filter does not contain the data if any of the bit offsets
	// which result from hashing the data using each independent hash
	// function are not set.  The shifts and masks below are a faster
	// equivalent of:
	//   arrayIndex := idx / 8     (idx >> 3)
	//   bitOffset := idx % 8      (idx & 7)
	///  if filter[arrayIndex] & 1<<bitOffset == 0 { ... }
	for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
		idx := bf.hash(i, data)
		if bf.msgFilterLoad.Filter[idx>>3]&(1<<(idx&7)) == 0 {
			return false
		}
	}
	return true
}

// Matches returns true if the bloom filter might contain the passed data and
// false if it definitely does not.
//
// This function is safe for concurrent access.
func (bf *Filter) Matches(data []byte) bool {
	bf.mtx.Lock()
	match := bf.matches(data)
	bf.mtx.Unlock()
	return match
}

// matchesOutPoint returns true if the bloom filter might contain the passed
// outpoint and false if it definitely does not.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matchesOutPoint(outpoint *outpoint.OutPoint) bool {
	// Serialize
	var buf [util.Hash256Size + 4]byte
	copy(buf[:], outpoint.Hash[:])
	binary.LittleEndian.PutUint32(buf[util.Hash256Size:], outpoint.Index)

	return bf.matches(buf[:])
}

// MatchesOutPoint returns true if the bloom filter might contain the passed
// outpoint and false if it definitely does not.
//
// This function is safe for concurrent access.
func (bf *Filter) MatchesOutPoint(outpoint *outpoint.OutPoint) bool {
	bf.mtx.Lock()
	match := bf.matchesOutPoint(outpoint)
	bf.mtx.Unlock()
	return match
}

// add adds the passed byte slice to the bloom filter.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) add(data []byte) {
	if bf.msgFilterLoad == nil {
		return
	}

	// Adding data to a bloom filter consists of setting all of the bit
	// offsets which result from hashing the data using each independent
	// hash function.  The shifts and masks below are a faster equivalent
	// of:
	//   arrayIndex := idx / 8    (idx >> 3)
	//   bitOffset := idx % 8     (idx & 7)
	///  filter[arrayIndex] |= 1<<bitOffset
	for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
		idx := bf.hash(i, data)
		bf.msgFilterLoad.Filter[idx>>3] |= (1 << (7 & idx))
	}
}

// Add adds the passed byte slice to the bloom filter.
//
// This function is safe for concurrent access.
func (bf *Filter) Add(data []byte) {
	bf.mtx.Lock()
	bf.add(data)
	bf.mtx.Unlock()
}

// AddHash adds the passed chainhash.Hash to the Filter.
//
// This function is safe for concurrent access.
func (bf *Filter) AddHash(hash *util.Hash) {
	bf.mtx.Lock()
	bf.add(hash[:])
	bf.mtx.Unlock()
}

// addOutPoint adds the passed transaction outpoint to the bloom filter.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) addOutPoint(outpoint *outpoint.OutPoint) {
	// Serialize
	var buf [util.Hash256Size + 4]byte
	copy(buf[:], outpoint.Hash[:])
	binary.LittleEndian.PutUint32(buf[util.Hash256Size:], outpoint.Index)

	bf.add(buf[:])
}

// AddOutPoint adds the passed transaction outpoint to the bloom filter.
//
// This function is safe for concurrent access.
func (bf *Filter) AddOutPoint(outpoint *outpoint.OutPoint) {
	bf.mtx.Lock()
	bf.addOutPoint(outpoint)
	bf.mtx.Unlock()
}

// maybeAddOutpoint potentially adds the passed outpoint to the bloom filter
// depending on the bloom update flags and the type of the passed public key
// script.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) maybeAddOutpoint(outHash *util.Hash, outIdx uint32) {
	switch bf.msgFilterLoad.Flags {
	case wire.BloomUpdateAll:
		outpoint := outpoint.NewOutPoint(outHash, outIdx)
		bf.addOutPoint(outpoint)
	case wire.BloomUpdateP2PubkeyOnly:
		var sc *script.Script
		class, err := sc.CheckScriptPubKeyStandard()
		if err != nil {
			errors.New("The script not standard...")
		} else if class == script.ScriptPubkey || class == script.ScriptMultiSig {
			outpoint := outpoint.NewOutPoint(outHash, outIdx)
			bf.addOutPoint(outpoint)
		}
	}
}

// matchTxAndUpdate returns true if the bloom filter matches data within the
// passed transaction, otherwise false is returned.  If the filter does match
// the passed transaction, it will also update the filter depending on the bloom
// update flags set via the loaded filter if needed.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matchTxAndUpdate(tx *tx.Tx) bool {
	// Check if the filter matches the hash of the transaction.
	// This is useful for finding transactions when they appear in a block.
	matched := bf.matches(tx.GetHash()[:])

	// Check if the filter matches any data elements in the public key
	// scripts of any of the outputs.  When it does, add the outpoint that
	// matched so transactions which spend from the matched transaction are
	// also included in the filter.  This removes the burden of updating the
	// filter for this scenario from the client.  It is also more efficient
	// on the network since it avoids the need for another filteradd message
	// from the client and avoids some potential races that could otherwise
	// occur.

	//for i, txOut := range tx.GetOuts() {
	//	pushedData, err := script.PushedData(txOut.GetScriptPubKey())
	//	if err != nil {
	//		continue
	//	}
	//
	//	for _, data := range pushedData {
	//		if !bf.matches(data) {
	//			continue
	//		}
	//
	//		matched = true
	//		hash := tx.GetHash()
	//		bf.maybeAddOutpoint(&hash, uint32(i))
	//		break
	//	}
	//}

	// Nothing more to do if a match has already been made.
	if matched {
		return true
	}

	// At this point, the transaction and none of the data elements in the
	// public key scripts of its outputs matched.

	// Check if the filter matches any outpoints this transaction spends or
	// any any data elements in the signature scripts of any of the inputs.

	//for _, txin := range tx.GetIns() {
	//	if bf.matchesOutPoint(txin.PreviousOutPoint) {
	//		return true
	//	}
	//
	//	pushedData, err := script.PushedData(txin.GetScriptSig())
	//	if err != nil {
	//		continue
	//	}
	//	for _, data := range pushedData {
	//		if bf.matches(data) {
	//			return true
	//		}
	//	}
	//}

	return false
}

// MatchTxAndUpdate returns true if the bloom filter matches data within the
// passed transaction, otherwise false is returned.  If the filter does match
// the passed transaction, it will also update the filter depending on the bloom
// update flags set via the loaded filter if needed.
//
// This function is safe for concurrent access.
func (bf *Filter) MatchTxAndUpdate(tx *tx.Tx) bool {
	bf.mtx.Lock()
	match := bf.matchTxAndUpdate(tx)
	bf.mtx.Unlock()
	return match
}

// MsgFilterLoad returns the underlying wire.MsgFilterLoad for the bloom
// filter.
//
// This function is safe for concurrent access.
func (bf *Filter) MsgFilterLoad() *wire.MsgFilterLoad {
	bf.mtx.Lock()
	msg := bf.msgFilterLoad
	bf.mtx.Unlock()
	return msg
}
<commit_msg>fix filter.go<commit_after>// Copyright (c) 2014-2016 The btcsuite developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package bloom

import (
	"encoding/binary"
	"math"
	"sync"

	"github.com/btcboost/copernicus/model/outpoint"
	// "github.com/btcboost/copernicus/model/script"
	"github.com/btcboost/copernicus/model/tx"
	"github.com/btcboost/copernicus/net/wire"
	"github.com/btcboost/copernicus/util"
	// "github.com/pkg/errors"
)

type MsgTx tx.Tx

// ln2Squared is simply the square of the natural log of 2.
const ln2Squared = math.Ln2 * math.Ln2

// minUint32 is a convenience function to return the minimum value of the two
// passed uint32 values.
func minUint32(a, b uint32) uint32 {
	if a < b {
		return a
	}
	return b
}

// Filter defines a bitcoin bloom filter that provides easy manipulation of raw
// filter data.
type Filter struct {
	mtx           sync.Mutex
	msgFilterLoad *wire.MsgFilterLoad
}

// NewFilter creates a new bloom filter instance, mainly to be used by SPV
// clients.  The tweak parameter is a random value added to the seed value.
// The false positive rate is the probability of a false positive where 1.0 is
// "match everything" and zero is unachievable.  Thus, providing any false
// positive rates less than 0 or greater than 1 will be adjusted to the valid
// range.
//
// For more information on what values to use for both elements and fprate,
// see https://en.wikipedia.org/wiki/Bloom_filter.
func NewFilter(elements, tweak uint32, fprate float64, flags wire.BloomUpdateType) *Filter {
	// Massage the false positive rate to sane values.
	if fprate > 1.0 {
		fprate = 1.0
	}
	if fprate < 1e-9 {
		fprate = 1e-9
	}

	// Calculate the size of the filter in bytes for the given number of
	// elements and false positive rate.
	//
	// Equivalent to m = -(n*ln(p) / ln(2)^2), where m is in bits.
	// Then clamp it to the maximum filter size and convert to bytes.
	dataLen := uint32(-1 * float64(elements) * math.Log(fprate) / ln2Squared)
	dataLen = minUint32(dataLen, wire.MaxFilterLoadFilterSize*8) / 8

	// Calculate the number of hash functions based on the size of the
	// filter calculated above and the number of elements.
	//
	// Equivalent to k = (m/n) * ln(2)
	// Then clamp it to the maximum allowed hash funcs.
	hashFuncs := uint32(float64(dataLen*8) / float64(elements) * math.Ln2)
	hashFuncs = minUint32(hashFuncs, wire.MaxFilterLoadHashFuncs)

	data := make([]byte, dataLen)
	msg := wire.NewMsgFilterLoad(data, hashFuncs, tweak, flags)

	return &Filter{
		msgFilterLoad: msg,
	}
}

// LoadFilter creates a new Filter instance with the given underlying
// wire.MsgFilterLoad.
func LoadFilter(filter *wire.MsgFilterLoad) *Filter {
	return &Filter{
		msgFilterLoad: filter,
	}
}

// IsLoaded returns true if a filter is loaded, otherwise false.
//
// This function is safe for concurrent access.
func (bf *Filter) IsLoaded() bool {
	bf.mtx.Lock()
	loaded := bf.msgFilterLoad != nil
	bf.mtx.Unlock()
	return loaded
}

// Reload loads a new filter replacing any existing filter.
//
// This function is safe for concurrent access.
func (bf *Filter) Reload(filter *wire.MsgFilterLoad) {
	bf.mtx.Lock()
	bf.msgFilterLoad = filter
	bf.mtx.Unlock()
}

// Unload unloads the bloom filter.
//
// This function is safe for concurrent access.
func (bf *Filter) Unload() {
	bf.mtx.Lock()
	bf.msgFilterLoad = nil
	bf.mtx.Unlock()
}

// hash returns the bit offset in the bloom filter which corresponds to the
// passed data for the given indepedent hash function number.
func (bf *Filter) hash(hashNum uint32, data []byte) uint32 {
	// bitcoind: 0xfba4c795 chosen as it guarantees a reasonable bit
	// difference between hashNum values.
	//
	// Note that << 3 is equivalent to multiplying by 8, but is faster.
	// Thus the returned hash is brought into range of the number of bits
	// the filter has and returned.
	mm := MurmurHash3(hashNum*0xfba4c795+bf.msgFilterLoad.Tweak, data)
	return mm % (uint32(len(bf.msgFilterLoad.Filter)) << 3)
}

// matches returns true if the bloom filter might contain the passed data and
// false if it definitely does not.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matches(data []byte) bool {
	if bf.msgFilterLoad == nil {
		return false
	}

	// The bloom filter does not contain the data if any of the bit offsets
	// which result from hashing the data using each independent hash
	// function are not set.  The shifts and masks below are a faster
	// equivalent of:
	//   arrayIndex := idx / 8     (idx >> 3)
	//   bitOffset := idx % 8      (idx & 7)
	///  if filter[arrayIndex] & 1<<bitOffset == 0 { ... }
	for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
		idx := bf.hash(i, data)
		if bf.msgFilterLoad.Filter[idx>>3]&(1<<(idx&7)) == 0 {
			return false
		}
	}
	return true
}

// Matches returns true if the bloom filter might contain the passed data and
// false if it definitely does not.
//
// This function is safe for concurrent access.
func (bf *Filter) Matches(data []byte) bool {
	bf.mtx.Lock()
	match := bf.matches(data)
	bf.mtx.Unlock()
	return match
}

// matchesOutPoint returns true if the bloom filter might contain the passed
// outpoint and false if it definitely does not.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matchesOutPoint(outpoint *outpoint.OutPoint) bool {
	// Serialize
	var buf [util.Hash256Size + 4]byte
	copy(buf[:], outpoint.Hash[:])
	binary.LittleEndian.PutUint32(buf[util.Hash256Size:], outpoint.Index)

	return bf.matches(buf[:])
}

// MatchesOutPoint returns true if the bloom filter might contain the passed
// outpoint and false if it definitely does not.
//
// This function is safe for concurrent access.
func (bf *Filter) MatchesOutPoint(outpoint *outpoint.OutPoint) bool {
	bf.mtx.Lock()
	match := bf.matchesOutPoint(outpoint)
	bf.mtx.Unlock()
	return match
}

// add adds the passed byte slice to the bloom filter.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) add(data []byte) {
	if bf.msgFilterLoad == nil {
		return
	}

	// Adding data to a bloom filter consists of setting all of the bit
	// offsets which result from hashing the data using each independent
	// hash function.  The shifts and masks below are a faster equivalent
	// of:
	//   arrayIndex := idx / 8    (idx >> 3)
	//   bitOffset := idx % 8     (idx & 7)
	///  filter[arrayIndex] |= 1<<bitOffset
	for i := uint32(0); i < bf.msgFilterLoad.HashFuncs; i++ {
		idx := bf.hash(i, data)
		bf.msgFilterLoad.Filter[idx>>3] |= (1 << (7 & idx))
	}
}

// Add adds the passed byte slice to the bloom filter.
//
// This function is safe for concurrent access.
func (bf *Filter) Add(data []byte) {
	bf.mtx.Lock()
	bf.add(data)
	bf.mtx.Unlock()
}

// AddHash adds the passed chainhash.Hash to the Filter.
//
// This function is safe for concurrent access.
func (bf *Filter) AddHash(hash *util.Hash) {
	bf.mtx.Lock()
	bf.add(hash[:])
	bf.mtx.Unlock()
}

// addOutPoint adds the passed transaction outpoint to the bloom filter.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) addOutPoint(outpoint *outpoint.OutPoint) {
	// Serialize
	var buf [util.Hash256Size + 4]byte
	copy(buf[:], outpoint.Hash[:])
	binary.LittleEndian.PutUint32(buf[util.Hash256Size:], outpoint.Index)

	bf.add(buf[:])
}

// AddOutPoint adds the passed transaction outpoint to the bloom filter.
//
// This function is safe for concurrent access.
func (bf *Filter) AddOutPoint(outpoint *outpoint.OutPoint) {
	bf.mtx.Lock()
	bf.addOutPoint(outpoint)
	bf.mtx.Unlock()
}

// maybeAddOutpoint potentially adds the passed outpoint to the bloom filter
// depending on the bloom update flags and the type of the passed public key
// script.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) maybeAddOutpoint(outHash *util.Hash, outIdx uint32) {
	// switch bf.msgFilterLoad.Flags {
	// case wire.BloomUpdateAll:
	// 	outpoint := outpoint.NewOutPoint(outHash, outIdx)
	// 	bf.addOutPoint(outpoint)
	// case wire.BloomUpdateP2PubkeyOnly:
	// 	var sc *script.Script
	// 	class, err := sc.CheckScriptPubKeyStandard()
	// 	if err != nil {
	// 		errors.New("The script not standard...")
	// 	} else if class == script.ScriptPubkey || class == script.ScriptMultiSig {
	// 		outpoint := outpoint.NewOutPoint(outHash, outIdx)
	// 		bf.addOutPoint(outpoint)
	// 	}
	// }
}

// matchTxAndUpdate returns true if the bloom filter matches data within the
// passed transaction, otherwise false is returned.  If the filter does match
// the passed transaction, it will also update the filter depending on the bloom
// update flags set via the loaded filter if needed.
//
// This function MUST be called with the filter lock held.
func (bf *Filter) matchTxAndUpdate(tx *tx.Tx) bool {
	// Check if the filter matches the hash of the transaction.
	// This is useful for finding transactions when they appear in a block.
	// matched := bf.matches(tx.GetHash()[:])

	// Check if the filter matches any data elements in the public key
	// scripts of any of the outputs.  When it does, add the outpoint that
	// matched so transactions which spend from the matched transaction are
	// also included in the filter.  This removes the burden of updating the
	// filter for this scenario from the client.  It is also more efficient
	// on the network since it avoids the need for another filteradd message
	// from the client and avoids some potential races that could otherwise
	// occur.

	//for i, txOut := range tx.GetOuts() {
	//	pushedData, err := script.PushedData(txOut.GetScriptPubKey())
	//	if err != nil {
	//		continue
	//	}
	//
	//	for _, data := range pushedData {
	//		if !bf.matches(data) {
	//			continue
	//		}
	//
	//		matched = true
	//		hash := tx.GetHash()
	//		bf.maybeAddOutpoint(&hash, uint32(i))
	//		break
	//	}
	//}

	// Nothing more to do if a match has already been made.
	// if matched {
	// 	return true
	// }

	// At this point, the transaction and none of the data elements in the
	// public key scripts of its outputs matched.

	// Check if the filter matches any outpoints this transaction spends or
	// any any data elements in the signature scripts of any of the inputs.

	//for _, txin := range tx.GetIns() {
	//	if bf.matchesOutPoint(txin.PreviousOutPoint) {
	//		return true
	//	}
	//
	//	pushedData, err := script.PushedData(txin.GetScriptSig())
	//	if err != nil {
	//		continue
	//	}
	//	for _, data := range pushedData {
	//		if bf.matches(data) {
	//			return true
	//		}
	//	}
	//}

	return false
}

// MatchTxAndUpdate returns true if the bloom filter matches data within the
// passed transaction, otherwise false is returned.  If the filter does match
// the passed transaction, it will also update the filter depending on the bloom
// update flags set via the loaded filter if needed.
//
// This function is safe for concurrent access.
func (bf *Filter) MatchTxAndUpdate(tx *tx.Tx) bool {
	bf.mtx.Lock()
	match := bf.matchTxAndUpdate(tx)
	bf.mtx.Unlock()
	return match
}

// MsgFilterLoad returns the underlying wire.MsgFilterLoad for the bloom
// filter.
//
// This function is safe for concurrent access.
func (bf *Filter) MsgFilterLoad() *wire.MsgFilterLoad {
	bf.mtx.Lock()
	msg := bf.msgFilterLoad
	bf.mtx.Unlock()
	return msg
}
<|endoftext|>


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

package template

import (
	"bytes";
	"fmt";
	"io";
	"os";
	"reflect";
	"template";
	"testing";
)

type Test struct {
	in, out string
}

type T struct {
	item string;
	value string;
}

type S struct {
	header string;
	integer int;
	raw string;
	innerT T;
	data []T;
	pdata []*T;
	empty []*T;
	emptystring string;
	null []*T;
}

var t1 = T{ "ItemNumber1", "ValueNumber1" }
var t2 = T{ "ItemNumber2", "ValueNumber2" }

func uppercase(v interface{}) string {
	s := v.(string);
	t := "";
	for i := 0; i < len(s); i++ {
		c := s[i];
		if 'a' <= c && c <= 'z' {
			c = c + 'A' - 'a'
		}
		t += string(c);
	}
	return t;
}

func plus1(v interface{}) string {
	i := v.(int);
	return fmt.Sprint(i + 1);
}

func writer(f func(interface{}) string) (func(io.Writer, interface{}, string)) {
	return func(w io.Writer, v interface{}, format string) {
		io.WriteString(w, f(v));
	}
}

var formatters = FormatterMap {
	"uppercase" : writer(uppercase),
	"+1" : writer(plus1),
}

var tests = []*Test {
	// Simple
	&Test{ "", "" },
	&Test{ "abc\ndef\n", "abc\ndef\n" },
	&Test{ " {.meta-left}   \n", "{" },
	&Test{ " {.meta-right}   \n", "}" },
	&Test{ " {.space}   \n", " " },
	&Test{ " {.tab}   \n", "\t" },
	&Test{ "     {#comment}   \n", "" },

	// Variables at top level
	&Test{
		"{header}={integer}\n",

		"Header=77\n"
	},

	// Section
	&Test{
		"{.section data }\n"
		"some text for the section\n"
		"{.end}\n",

		"some text for the section\n"
	},
	&Test{
		"{.section data }\n"
		"{header}={integer}\n"
		"{.end}\n",

		"Header=77\n"
	},
	&Test{
		"{.section pdata }\n"
		"{header}={integer}\n"
		"{.end}\n",

		"Header=77\n"
	},
	&Test{
		"{.section pdata }\n"
		"data present\n"
		"{.or}\n"
		"data not present\n"
		"{.end}\n",

		"data present\n"
	},
	&Test{
		"{.section empty }\n"
		"data present\n"
		"{.or}\n"
		"data not present\n"
		"{.end}\n",

		"data not present\n"
	},
	&Test{
		"{.section null }\n"
		"data present\n"
		"{.or}\n"
		"data not present\n"
		"{.end}\n",

		"data not present\n"
	},
	&Test{
		"{.section pdata }\n"
		"{header}={integer}\n"
		"{.section @ }\n"
		"{header}={integer}\n"
		"{.end}\n"
		"{.end}\n",

		"Header=77\n"
		"Header=77\n"
	},
	&Test{
		"{.section data}{.end} {header}\n",

		" Header\n"
	},

	// Repeated
	&Test{
		"{.section pdata }\n"
		"{.repeated section @ }\n"
		"{item}={value}\n"
		"{.end}\n"
		"{.end}\n",

		"ItemNumber1=ValueNumber1\n"
		"ItemNumber2=ValueNumber2\n"
	},
	&Test{
		"{.section pdata }\n"
		"{.repeated section @ }\n"
		"{item}={value}\n"
		"{.or}\n"
		"this should not appear\n"
		"{.end}\n"
		"{.end}\n",

		"ItemNumber1=ValueNumber1\n"
		"ItemNumber2=ValueNumber2\n"
	},
	&Test{
		"{.section @ }\n"
		"{.repeated section empty }\n"
		"{item}={value}\n"
		"{.or}\n"
		"this should appear: empty field\n"
		"{.end}\n"
		"{.end}\n",

		"this should appear: empty field\n"
	},
	&Test{
		"{.section pdata }\n"
		"{.repeated section @ }\n"
		"{item}={value}\n"
		"{.alternates with}DIVIDER\n"
		"{.or}\n"
		"this should not appear\n"
		"{.end}\n"
		"{.end}\n",

		"ItemNumber1=ValueNumber1\n"
		"DIVIDER\n"
		"ItemNumber2=ValueNumber2\n"
	},

	// Nested names
	&Test{
		"{.section @ }\n"
		"{innerT.item}={innerT.value}\n"
		"{.end}",

		"ItemNumber1=ValueNumber1\n"
	},

	// Formatters
	&Test{
		"{.section pdata }\n"
		"{header|uppercase}={integer|+1}\n"
		"{header|html}={integer|str}\n"
		"{.end}\n",

		"HEADER=78\n"
		"Header=77\n"
	},

	&Test{
		"{raw}\n"
		"{raw|html}\n",

		"&<>!@ #$%^\n"
		"&<>!@ #$%^\n"
	},

	&Test{
		"{.section emptystring}emptystring{.end}\n"
		"{.section header}header{.end}\n",

		"\nheader\n"
	},
}

func TestAll(t *testing.T) {
	s := new(S);
	// initialized by hand for clarity.
	s.header = "Header";
	s.integer = 77;
	s.raw = "&<>!@ #$%^";
	s.innerT = t1;
	s.data = []T{ t1, t2 };
	s.pdata = []*T{ &t1, &t2 };
	s.empty = []*T{ };
	s.null = nil;

	var buf bytes.Buffer;
	for i, test := range tests {
		buf.Reset();
		tmpl, err := Parse(test.in, formatters);
		if err != nil {
			t.Error("unexpected parse error:", err);
			continue;
		}
		err = tmpl.Execute(s, &buf);
		if err != nil {
			t.Error("unexpected execute error:", err)
		}
		if string(buf.Data()) != test.out {
			t.Errorf("for %q: expected %q got %q", test.in, test.out, string(buf.Data()));
		}
	}
}

func TestStringDriverType(t *testing.T) {
	tmpl, err := Parse("template: {@}", nil);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	var b bytes.Buffer;
	err = tmpl.Execute("hello", &b);
	if err != nil {
		t.Error("unexpected execute error:", err)
	}
	s := string(b.Data());
	if s != "template: hello" {
		t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s)
	}
}

func TestTwice(t *testing.T) {
	tmpl, err := Parse("template: {@}", nil);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	var b bytes.Buffer;
	err = tmpl.Execute("hello", &b);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	s := string(b.Data());
	text := "template: hello";
	if s != text {
		t.Errorf("failed passing string as data: expected %q got %q", text, s);
	}
	err = tmpl.Execute("hello", &b);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	s = string(b.Data());
	text += text;
	if s != text {
		t.Errorf("failed passing string as data: expected %q got %q", text, s);
	}
}

func TestCustomDelims(t *testing.T) {
	// try various lengths.  zero should catch error.
	for i := 0; i < 7; i++ {
		for j := 0; j < 7; j++ {
			tmpl := New(nil);
			// first two chars deliberately the same to test equal left and right delims
			ldelim := "$!#$%^&"[0:i];
			rdelim := "$*&^%$!"[0:j];
			tmpl.SetDelims(ldelim, rdelim);
			// if braces, this would be template: {@}{.meta-left}{.meta-right}
			text := "template: " +
				ldelim + "@" + rdelim +
				ldelim + ".meta-left" + rdelim +
				ldelim + ".meta-right" + rdelim;
			err := tmpl.Parse(text);
			if err != nil {
				if i == 0 || j == 0 {	// expected
					continue
				}
				t.Error("unexpected parse error:", err)
			} else if i == 0 || j == 0 {
				t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim);
				continue;
			}
			var b bytes.Buffer;
			err = tmpl.Execute("hello", &b);
			s := string(b.Data());
			if s != "template: hello" + ldelim + rdelim {
				t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
			}
		}
	}
}
<commit_msg>add test of invariant in findVar<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package template

import (
	"bytes";
	"fmt";
	"io";
	"os";
	"reflect";
	"template";
	"testing";
)

type Test struct {
	in, out string
}

type T struct {
	item string;
	value string;
}

type S struct {
	header string;
	integer int;
	raw string;
	innerT T;
	innerPointerT *T;
	data []T;
	pdata []*T;
	empty []*T;
	emptystring string;
	null []*T;
}

var t1 = T{ "ItemNumber1", "ValueNumber1" }
var t2 = T{ "ItemNumber2", "ValueNumber2" }

func uppercase(v interface{}) string {
	s := v.(string);
	t := "";
	for i := 0; i < len(s); i++ {
		c := s[i];
		if 'a' <= c && c <= 'z' {
			c = c + 'A' - 'a'
		}
		t += string(c);
	}
	return t;
}

func plus1(v interface{}) string {
	i := v.(int);
	return fmt.Sprint(i + 1);
}

func writer(f func(interface{}) string) (func(io.Writer, interface{}, string)) {
	return func(w io.Writer, v interface{}, format string) {
		io.WriteString(w, f(v));
	}
}

var formatters = FormatterMap {
	"uppercase" : writer(uppercase),
	"+1" : writer(plus1),
}

var tests = []*Test {
	// Simple
	&Test{ "", "" },
	&Test{ "abc\ndef\n", "abc\ndef\n" },
	&Test{ " {.meta-left}   \n", "{" },
	&Test{ " {.meta-right}   \n", "}" },
	&Test{ " {.space}   \n", " " },
	&Test{ " {.tab}   \n", "\t" },
	&Test{ "     {#comment}   \n", "" },

	// Variables at top level
	&Test{
		"{header}={integer}\n",

		"Header=77\n"
	},

	// Section
	&Test{
		"{.section data }\n"
		"some text for the section\n"
		"{.end}\n",

		"some text for the section\n"
	},
	&Test{
		"{.section data }\n"
		"{header}={integer}\n"
		"{.end}\n",

		"Header=77\n"
	},
	&Test{
		"{.section pdata }\n"
		"{header}={integer}\n"
		"{.end}\n",

		"Header=77\n"
	},
	&Test{
		"{.section pdata }\n"
		"data present\n"
		"{.or}\n"
		"data not present\n"
		"{.end}\n",

		"data present\n"
	},
	&Test{
		"{.section empty }\n"
		"data present\n"
		"{.or}\n"
		"data not present\n"
		"{.end}\n",

		"data not present\n"
	},
	&Test{
		"{.section null }\n"
		"data present\n"
		"{.or}\n"
		"data not present\n"
		"{.end}\n",

		"data not present\n"
	},
	&Test{
		"{.section pdata }\n"
		"{header}={integer}\n"
		"{.section @ }\n"
		"{header}={integer}\n"
		"{.end}\n"
		"{.end}\n",

		"Header=77\n"
		"Header=77\n"
	},
	&Test{
		"{.section data}{.end} {header}\n",

		" Header\n"
	},

	// Repeated
	&Test{
		"{.section pdata }\n"
		"{.repeated section @ }\n"
		"{item}={value}\n"
		"{.end}\n"
		"{.end}\n",

		"ItemNumber1=ValueNumber1\n"
		"ItemNumber2=ValueNumber2\n"
	},
	&Test{
		"{.section pdata }\n"
		"{.repeated section @ }\n"
		"{item}={value}\n"
		"{.or}\n"
		"this should not appear\n"
		"{.end}\n"
		"{.end}\n",

		"ItemNumber1=ValueNumber1\n"
		"ItemNumber2=ValueNumber2\n"
	},
	&Test{
		"{.section @ }\n"
		"{.repeated section empty }\n"
		"{item}={value}\n"
		"{.or}\n"
		"this should appear: empty field\n"
		"{.end}\n"
		"{.end}\n",

		"this should appear: empty field\n"
	},
	&Test{
		"{.section pdata }\n"
		"{.repeated section @ }\n"
		"{item}={value}\n"
		"{.alternates with}DIVIDER\n"
		"{.or}\n"
		"this should not appear\n"
		"{.end}\n"
		"{.end}\n",

		"ItemNumber1=ValueNumber1\n"
		"DIVIDER\n"
		"ItemNumber2=ValueNumber2\n"
	},

	// Nested names
	&Test{
		"{.section @ }\n"
		"{innerT.item}={innerT.value}\n"
		"{.end}",

		"ItemNumber1=ValueNumber1\n"
	},

	// Formatters
	&Test{
		"{.section pdata }\n"
		"{header|uppercase}={integer|+1}\n"
		"{header|html}={integer|str}\n"
		"{.end}\n",

		"HEADER=78\n"
		"Header=77\n"
	},

	&Test{
		"{raw}\n"
		"{raw|html}\n",

		"&<>!@ #$%^\n"
		"&<>!@ #$%^\n"
	},

	&Test{
		"{.section emptystring}emptystring{.end}\n"
		"{.section header}header{.end}\n",

		"\nheader\n"
	},
}

func TestAll(t *testing.T) {
	s := new(S);
	// initialized by hand for clarity.
	s.header = "Header";
	s.integer = 77;
	s.raw = "&<>!@ #$%^";
	s.innerT = t1;
	s.data = []T{ t1, t2 };
	s.pdata = []*T{ &t1, &t2 };
	s.empty = []*T{ };
	s.null = nil;

	var buf bytes.Buffer;
	for i, test := range tests {
		buf.Reset();
		tmpl, err := Parse(test.in, formatters);
		if err != nil {
			t.Error("unexpected parse error:", err);
			continue;
		}
		err = tmpl.Execute(s, &buf);
		if err != nil {
			t.Error("unexpected execute error:", err)
		}
		if string(buf.Data()) != test.out {
			t.Errorf("for %q: expected %q got %q", test.in, test.out, string(buf.Data()));
		}
	}
}

func TestStringDriverType(t *testing.T) {
	tmpl, err := Parse("template: {@}", nil);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	var b bytes.Buffer;
	err = tmpl.Execute("hello", &b);
	if err != nil {
		t.Error("unexpected execute error:", err)
	}
	s := string(b.Data());
	if s != "template: hello" {
		t.Errorf("failed passing string as data: expected %q got %q", "template: hello", s)
	}
}

func TestTwice(t *testing.T) {
	tmpl, err := Parse("template: {@}", nil);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	var b bytes.Buffer;
	err = tmpl.Execute("hello", &b);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	s := string(b.Data());
	text := "template: hello";
	if s != text {
		t.Errorf("failed passing string as data: expected %q got %q", text, s);
	}
	err = tmpl.Execute("hello", &b);
	if err != nil {
		t.Error("unexpected parse error:", err)
	}
	s = string(b.Data());
	text += text;
	if s != text {
		t.Errorf("failed passing string as data: expected %q got %q", text, s);
	}
}

func TestCustomDelims(t *testing.T) {
	// try various lengths.  zero should catch error.
	for i := 0; i < 7; i++ {
		for j := 0; j < 7; j++ {
			tmpl := New(nil);
			// first two chars deliberately the same to test equal left and right delims
			ldelim := "$!#$%^&"[0:i];
			rdelim := "$*&^%$!"[0:j];
			tmpl.SetDelims(ldelim, rdelim);
			// if braces, this would be template: {@}{.meta-left}{.meta-right}
			text := "template: " +
				ldelim + "@" + rdelim +
				ldelim + ".meta-left" + rdelim +
				ldelim + ".meta-right" + rdelim;
			err := tmpl.Parse(text);
			if err != nil {
				if i == 0 || j == 0 {	// expected
					continue
				}
				t.Error("unexpected parse error:", err)
			} else if i == 0 || j == 0 {
				t.Errorf("expected parse error for empty delimiter: %d %d %q %q", i, j, ldelim, rdelim);
				continue;
			}
			var b bytes.Buffer;
			err = tmpl.Execute("hello", &b);
			s := string(b.Data());
			if s != "template: hello" + ldelim + rdelim {
				t.Errorf("failed delim check(%q %q) %q got %q", ldelim, rdelim, text, s)
			}
		}
	}
}

// Test that a variable evaluates to the field itself and does not further indirection
func TestVarIndirection(t *testing.T) {
	s := new(S);
	// initialized by hand for clarity.
	s.innerPointerT = &t1;

	var buf bytes.Buffer;
	input := "{.section @}{innerPointerT}{.end}";
	tmpl, err := Parse(input, nil);
	if err != nil {
		t.Fatal("unexpected parse error:", err);
	}
	err = tmpl.Execute(s, &buf);
	if err != nil {
		t.Fatal("unexpected execute error:", err)
	}
	expect := fmt.Sprintf("%v", &t1);	// output should be hex address of t1
	if string(buf.Data()) != expect {
		t.Errorf("for %q: expected %q got %q", input, expect, string(buf.Data()));
	}
}
<|endoftext|>


<commit_before>package kontrol

import (
	"koding/db/models"
	"testing"

	"gopkg.in/mgo.v2/bson"

	"github.com/koding/kite/protocol"
)

func TestFilterResult(t *testing.T) {
	kite1 := &KodingKiteWithToken{
		Kite: protocol.Kite{
			ID: "id_1",
		},
		// other fields are not used
	}

	kite2 := &KodingKiteWithToken{
		Kite: protocol.Kite{
			ID: "id_2",
		},
	}

	pastDueGroup := &models.Group{
		Id: bson.NewObjectId(),
		Payment: models.Payment{
			Subscription: models.Subscription{
				Status: "past_due",
			},
		},
	}

	activeGroup1 := &models.Group{
		Id: bson.NewObjectId(),
		Payment: models.Payment{
			Subscription: models.Subscription{
				Status: "active",
			},
		},
	}
	activeGroup2 := &models.Group{
		Id: bson.NewObjectId(),
		Payment: models.Payment{
			Subscription: models.Subscription{
				Status: "active",
			},
		},
	}

	tests := []struct {
		name                 string
		getKodingKitesResult *GetKodingKitesResult
		groups               []*models.Group
		kitesByGroupID       map[string][]*KodingKiteWithToken

		response *GetKodingKitesResult
	}{
		{
			name: "should stay same - everything is perfect",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1, activeGroup2},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1, kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
		{
			name: "remove past_due_group's kites when there is another group",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1, pastDueGroup},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1},
			},
		},
		{
			name: "remove past_due_group's kites when there isnt any other group",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{pastDueGroup},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1},
			},
		},
		{
			name: "should not remove if the group is not in  group list",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
		{
			name: "should not remove anything if there is no group",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
		{
			name: "should not remove anything if there is no past due group kite",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1, activeGroup2, pastDueGroup},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				activeGroup2.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			// t.Parallel()
			filterRes := filterResult(test.getKodingKitesResult, test.groups, test.kitesByGroupID)
			if len(test.response.Kites) != len(filterRes.Kites) {
				t.Fatalf("len expected %d, got %d", len(test.response.Kites), len(filterRes.Kites))
			}

			for i := 0; i < len(test.response.Kites); i++ {
				expected := test.response.Kites[i]
				got := filterRes.Kites[i]

				if expected.Kite.ID != filterRes.Kites[i].Kite.ID {
					t.Fatalf("kite id expected %s, got %s", expected.Kite.ID, got.Kite.ID)
				}
			}
		})
	}
}
<commit_msg>go.race: parallelization is fixed for test<commit_after>package kontrol

import (
	"koding/db/models"
	"testing"

	"gopkg.in/mgo.v2/bson"

	"github.com/koding/kite/protocol"
)

func TestFilterResult(t *testing.T) {
	kite1 := &KodingKiteWithToken{
		Kite: protocol.Kite{
			ID: "id_1",
		},
		// other fields are not used
	}

	kite2 := &KodingKiteWithToken{
		Kite: protocol.Kite{
			ID: "id_2",
		},
	}

	pastDueGroup := &models.Group{
		Id: bson.NewObjectId(),
		Payment: models.Payment{
			Subscription: models.Subscription{
				Status: "past_due",
			},
		},
	}

	activeGroup1 := &models.Group{
		Id: bson.NewObjectId(),
		Payment: models.Payment{
			Subscription: models.Subscription{
				Status: "active",
			},
		},
	}
	activeGroup2 := &models.Group{
		Id: bson.NewObjectId(),
		Payment: models.Payment{
			Subscription: models.Subscription{
				Status: "active",
			},
		},
	}

	tests := []struct {
		name                 string
		getKodingKitesResult *GetKodingKitesResult
		groups               []*models.Group
		kitesByGroupID       map[string][]*KodingKiteWithToken

		response *GetKodingKitesResult
	}{
		{
			name: "should stay same - everything is perfect",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1, activeGroup2},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1, kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
		{
			name: "remove past_due_group's kites when there is another group",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1, pastDueGroup},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1},
			},
		},
		{
			name: "remove past_due_group's kites when there isnt any other group",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{pastDueGroup},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1},
			},
		},
		{
			name: "should not remove if the group is not in  group list",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
		{
			name: "should not remove anything if there is no group",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				pastDueGroup.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
		{
			name: "should not remove anything if there is no past due group kite",
			getKodingKitesResult: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
			groups: []*models.Group{activeGroup1, activeGroup2, pastDueGroup},
			kitesByGroupID: map[string][]*KodingKiteWithToken{
				activeGroup1.Id.Hex(): {kite1},
				activeGroup2.Id.Hex(): {kite2},
			},
			response: &GetKodingKitesResult{
				Kites: []*KodingKiteWithToken{kite1, kite2},
			},
		},
	}

	for _, test := range tests {
		// capture range variable here
		// otherwise values will not be assigned correctly
		test := test
		t.Run(test.name, func(t *testing.T) {
			t.Parallel()
			filterRes := filterResult(test.getKodingKitesResult, test.groups, test.kitesByGroupID)
			if len(test.response.Kites) != len(filterRes.Kites) {
				t.Fatalf("len expected %d, got %d", len(test.response.Kites), len(filterRes.Kites))
			}

			for i := 0; i < len(test.response.Kites); i++ {
				expected := test.response.Kites[i]
				got := filterRes.Kites[i]

				if expected.Kite.ID != filterRes.Kites[i].Kite.ID {
					t.Fatalf("kite id expected %s, got %s", expected.Kite.ID, got.Kite.ID)
				}
			}
		})
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2020 The Tekton Authors

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

    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 tektonconfig

import (
	"context"
	"fmt"

	mf "github.com/manifestival/manifestival"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
	clientset "github.com/tektoncd/operator/pkg/client/clientset/versioned"
	tektonConfigreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonconfig"
	"github.com/tektoncd/operator/pkg/reconciler/common"
	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektonconfig/pipeline"
	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektonconfig/trigger"
	"knative.dev/pkg/logging"
	pkgreconciler "knative.dev/pkg/reconciler"
)

// Reconciler implements controller.Reconciler for TektonConfig resources.
type Reconciler struct {
	// kubeClientSet allows us to talk to the k8s for core APIs
	kubeClientSet kubernetes.Interface
	// operatorClientSet allows us to configure operator objects
	operatorClientSet clientset.Interface
	// manifest is empty, but with a valid client and logger. all
	// manifests are immutable, and any created during reconcile are
	// expected to be appended to this one, obviating the passing of
	// client & logger
	manifest mf.Manifest
	// Platform-specific behavior to affect the transform
	extension common.Extension
}

// Check that our Reconciler implements controller.Reconciler
var _ tektonConfigreconciler.Interface = (*Reconciler)(nil)
var _ tektonConfigreconciler.Finalizer = (*Reconciler)(nil)

// FinalizeKind removes all resources after deletion of a TektonConfig.
func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.TektonConfig) pkgreconciler.Event {
	logger := logging.FromContext(ctx)

	// List all TektonConfigs to determine if cluster-scoped resources should be deleted.
	tps, err := r.operatorClientSet.OperatorV1alpha1().TektonConfigs().List(ctx, metav1.ListOptions{})
	if err != nil {
		return fmt.Errorf("failed to list all TektonConfigs: %w", err)
	}

	for _, tp := range tps.Items {
		if tp.GetDeletionTimestamp().IsZero() {
			// Not deleting all TektonPipelines. Nothing to do here.
			return nil
		}
	}

	if original.Spec.Profile == common.ProfileLite {
		return pipeline.TektonPipelineCRDelete(r.operatorClientSet.OperatorV1alpha1().TektonPipelines(), common.PipelineResourceName)
	} else {
		// TektonPipeline and TektonTrigger is common for profile type basic and all
		if err := pipeline.TektonPipelineCRDelete(r.operatorClientSet.OperatorV1alpha1().TektonPipelines(), common.PipelineResourceName); err != nil {
			return err
		}
		if err := trigger.TektonTriggerCRDelete(r.operatorClientSet.OperatorV1alpha1().TektonTriggers(), common.TriggerResourceName); err != nil {
			return err
		}
	}

	if err := r.extension.Finalize(ctx, original); err != nil {
		logger.Error("Failed to finalize platform resources", err)
	}

	return nil
}

// ReconcileKind compares the actual state with the desired, and attempts to
// converge the two.
func (r *Reconciler) ReconcileKind(ctx context.Context, tc *v1alpha1.TektonConfig) pkgreconciler.Event {
	logger := logging.FromContext(ctx)
	tc.Status.InitializeConditions()
	tc.Status.ObservedGeneration = tc.Generation

	logger.Infow("Reconciling TektonConfig", "status", tc.Status)
	if tc.GetName() != common.ConfigResourceName {
		msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
			common.ConfigResourceName,
			tc.GetName(),
		)
		logger.Error(msg)
		tc.GetStatus().MarkInstallFailed(msg)
		return nil
	}

	if err := r.extension.PreReconcile(ctx, tc); err != nil {
		tc.GetStatus().MarkInstallFailed(err.Error())
		return err
	}

	var stages common.Stages
	if tc.Spec.Profile == common.ProfileLite {
		stages = common.Stages{
			r.createPipelineCR,
		}
	} else {
		// TektonPipeline and TektonTrigger is common for profile type basic and all
		stages = common.Stages{
			r.createPipelineCR,
			r.createTriggerCR,
		}
	}

	if err := common.Prune(r.kubeClientSet, ctx, tc); err != nil {
		logger.Error(err)
	}

	manifest := r.manifest.Append()
	if err := stages.Execute(ctx, &manifest, tc); err != nil {
		tc.GetStatus().MarkInstallFailed(err.Error())
		return err
	}
	if err := r.extension.PostReconcile(ctx, tc); err != nil {
		tc.GetStatus().MarkInstallFailed(err.Error())
		return err
	}
	tc.Status.MarkInstallSucceeded()
	tc.Status.MarkDeploymentsAvailable()
	return nil
}

func (r *Reconciler) createPipelineCR(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error {
	return pipeline.CreatePipelineCR(comp, r.operatorClientSet.OperatorV1alpha1())
}

func (r *Reconciler) createTriggerCR(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error {
	return trigger.CreateTriggerCR(comp, r.operatorClientSet.OperatorV1alpha1())
}
<commit_msg>call prune at the end of the Reconcile<commit_after>/*
Copyright 2020 The Tekton Authors

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

    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 tektonconfig

import (
	"context"
	"fmt"

	mf "github.com/manifestival/manifestival"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/client-go/kubernetes"

	"github.com/tektoncd/operator/pkg/apis/operator/v1alpha1"
	clientset "github.com/tektoncd/operator/pkg/client/clientset/versioned"
	tektonConfigreconciler "github.com/tektoncd/operator/pkg/client/injection/reconciler/operator/v1alpha1/tektonconfig"
	"github.com/tektoncd/operator/pkg/reconciler/common"
	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektonconfig/pipeline"
	"github.com/tektoncd/operator/pkg/reconciler/kubernetes/tektonconfig/trigger"
	"knative.dev/pkg/logging"
	pkgreconciler "knative.dev/pkg/reconciler"
)

// Reconciler implements controller.Reconciler for TektonConfig resources.
type Reconciler struct {
	// kubeClientSet allows us to talk to the k8s for core APIs
	kubeClientSet kubernetes.Interface
	// operatorClientSet allows us to configure operator objects
	operatorClientSet clientset.Interface
	// manifest is empty, but with a valid client and logger. all
	// manifests are immutable, and any created during reconcile are
	// expected to be appended to this one, obviating the passing of
	// client & logger
	manifest mf.Manifest
	// Platform-specific behavior to affect the transform
	extension common.Extension
}

// Check that our Reconciler implements controller.Reconciler
var _ tektonConfigreconciler.Interface = (*Reconciler)(nil)
var _ tektonConfigreconciler.Finalizer = (*Reconciler)(nil)

// FinalizeKind removes all resources after deletion of a TektonConfig.
func (r *Reconciler) FinalizeKind(ctx context.Context, original *v1alpha1.TektonConfig) pkgreconciler.Event {
	logger := logging.FromContext(ctx)

	// List all TektonConfigs to determine if cluster-scoped resources should be deleted.
	tps, err := r.operatorClientSet.OperatorV1alpha1().TektonConfigs().List(ctx, metav1.ListOptions{})
	if err != nil {
		return fmt.Errorf("failed to list all TektonConfigs: %w", err)
	}

	for _, tp := range tps.Items {
		if tp.GetDeletionTimestamp().IsZero() {
			// Not deleting all TektonPipelines. Nothing to do here.
			return nil
		}
	}

	if original.Spec.Profile == common.ProfileLite {
		return pipeline.TektonPipelineCRDelete(r.operatorClientSet.OperatorV1alpha1().TektonPipelines(), common.PipelineResourceName)
	} else {
		// TektonPipeline and TektonTrigger is common for profile type basic and all
		if err := pipeline.TektonPipelineCRDelete(r.operatorClientSet.OperatorV1alpha1().TektonPipelines(), common.PipelineResourceName); err != nil {
			return err
		}
		if err := trigger.TektonTriggerCRDelete(r.operatorClientSet.OperatorV1alpha1().TektonTriggers(), common.TriggerResourceName); err != nil {
			return err
		}
	}

	if err := r.extension.Finalize(ctx, original); err != nil {
		logger.Error("Failed to finalize platform resources", err)
	}

	return nil
}

// ReconcileKind compares the actual state with the desired, and attempts to
// converge the two.
func (r *Reconciler) ReconcileKind(ctx context.Context, tc *v1alpha1.TektonConfig) pkgreconciler.Event {
	logger := logging.FromContext(ctx)
	tc.Status.InitializeConditions()
	tc.Status.ObservedGeneration = tc.Generation

	logger.Infow("Reconciling TektonConfig", "status", tc.Status)
	if tc.GetName() != common.ConfigResourceName {
		msg := fmt.Sprintf("Resource ignored, Expected Name: %s, Got Name: %s",
			common.ConfigResourceName,
			tc.GetName(),
		)
		logger.Error(msg)
		tc.GetStatus().MarkInstallFailed(msg)
		return nil
	}

	if err := r.extension.PreReconcile(ctx, tc); err != nil {
		tc.GetStatus().MarkInstallFailed(err.Error())
		return err
	}

	var stages common.Stages
	if tc.Spec.Profile == common.ProfileLite {
		stages = common.Stages{
			r.createPipelineCR,
		}
	} else {
		// TektonPipeline and TektonTrigger is common for profile type basic and all
		stages = common.Stages{
			r.createPipelineCR,
			r.createTriggerCR,
		}
	}

	manifest := r.manifest.Append()
	if err := stages.Execute(ctx, &manifest, tc); err != nil {
		tc.GetStatus().MarkInstallFailed(err.Error())
		return err
	}
	if err := r.extension.PostReconcile(ctx, tc); err != nil {
		tc.GetStatus().MarkInstallFailed(err.Error())
		return err
	}

	if err := common.Prune(r.kubeClientSet, ctx, tc); err != nil {
		logger.Error(err)
	}

	tc.Status.MarkInstallSucceeded()
	tc.Status.MarkDeploymentsAvailable()
	return nil
}

func (r *Reconciler) createPipelineCR(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error {
	return pipeline.CreatePipelineCR(comp, r.operatorClientSet.OperatorV1alpha1())
}

func (r *Reconciler) createTriggerCR(ctx context.Context, manifest *mf.Manifest, comp v1alpha1.TektonComponent) error {
	return trigger.CreateTriggerCR(comp, r.operatorClientSet.OperatorV1alpha1())
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"fmt"
	"os"
	"regexp"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/authelia/authelia/internal/utils"
)

var arch string

var supportedArch = []string{"amd64", "arm32v7", "arm64v8", "coverage"}
var defaultArch = "amd64"
var buildkiteQEMU = os.Getenv("BUILDKITE_AGENT_META_DATA_QEMU")
var ciBranch = os.Getenv("BUILDKITE_BRANCH")
var ciPullRequest = os.Getenv("BUILDKITE_PULL_REQUEST")
var ciTag = os.Getenv("BUILDKITE_TAG")
var dockerTags = regexp.MustCompile(`v(?P<Patch>(?P<Minor>(?P<Major>\d+)\.\d+)\.\d+.*)`)
var ignoredSuffixes = regexp.MustCompile("alpha|beta")
var publicRepo = regexp.MustCompile(`.*\:.*`)
var tags = dockerTags.FindStringSubmatch(ciTag)

func init() {
	DockerBuildCmd.PersistentFlags().StringVar(&arch, "arch", defaultArch, "target architecture among: "+strings.Join(supportedArch, ", "))
	DockerPushCmd.PersistentFlags().StringVar(&arch, "arch", defaultArch, "target architecture among: "+strings.Join(supportedArch, ", "))
}

func checkArchIsSupported(arch string) {
	for _, a := range supportedArch {
		if arch == a {
			return
		}
	}

	log.Fatal("Architecture is not supported. Please select one of " + strings.Join(supportedArch, ", ") + ".")
}

func dockerBuildOfficialImage(arch string) error {
	docker := &Docker{}
	// Set default Architecture Dockerfile to amd64.
	dockerfile := "Dockerfile"
	// Set version of QEMU.
	qemuversion := "v5.1.0-2"

	// If not the default value.
	if arch != defaultArch {
		dockerfile = fmt.Sprintf("%s.%s", dockerfile, arch)
	}

	if arch == "arm32v7" {
		if buildkiteQEMU != stringTrue {
			err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run()
			if err != nil {
				panic(err)
			}
		}

		err := utils.CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/"+qemuversion+"/qemu-arm-static -O ./qemu-arm-static && chmod +x ./qemu-arm-static").Run()

		if err != nil {
			panic(err)
		}
	} else if arch == "arm64v8" {
		if buildkiteQEMU != stringTrue {
			err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run()
			if err != nil {
				panic(err)
			}
		}

		err := utils.CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/"+qemuversion+"/qemu-aarch64-static -O ./qemu-aarch64-static && chmod +x ./qemu-aarch64-static").Run()

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

	gitTag := ciTag
	if gitTag == "" {
		// If commit is not tagged, mark the build has having master tag.
		gitTag = masterTag
	}

	cmd := utils.Shell("git rev-parse HEAD")
	cmd.Stdout = nil
	cmd.Stderr = nil
	commitBytes, err := cmd.Output()

	if err != nil {
		log.Fatal(err)
	}

	commitHash := strings.Trim(string(commitBytes), "\n")

	return docker.Build(IntermediateDockerImageName, dockerfile, ".", gitTag, commitHash)
}

// DockerBuildCmd Command for building docker image of Authelia.
var DockerBuildCmd = &cobra.Command{
	Use:   "build",
	Short: "Build the docker image of Authelia",
	Run: func(cmd *cobra.Command, args []string) {
		log.Infof("Building Docker image %s...", DockerImageName)
		checkArchIsSupported(arch)
		err := dockerBuildOfficialImage(arch)

		if err != nil {
			log.Fatal(err)
		}

		docker := &Docker{}
		err = docker.Tag(IntermediateDockerImageName, DockerImageName)

		if err != nil {
			panic(err)
		}
	},
}

// DockerPushCmd Command for pushing Authelia docker image to DockerHub.
var DockerPushCmd = &cobra.Command{
	Use:   "push-image",
	Short: "Publish Authelia docker image to Docker Hub",
	Run: func(cmd *cobra.Command, args []string) {
		log.Infof("Pushing Docker image %s to Docker Hub...", DockerImageName)
		checkArchIsSupported(arch)
		publishDockerImage(arch)
	},
}

// DockerManifestCmd Command for pushing Authelia docker manifest to DockerHub.
var DockerManifestCmd = &cobra.Command{
	Use:   "push-manifest",
	Short: "Publish Authelia docker manifest to Docker Hub",
	Run: func(cmd *cobra.Command, args []string) {
		log.Infof("Pushing Docker manifest of %s to Docker Hub...", DockerImageName)
		publishDockerManifest()
	},
}

func login(docker *Docker) {
	username := os.Getenv("DOCKER_USERNAME")
	password := os.Getenv("DOCKER_PASSWORD")

	if username == "" {
		log.Fatal(errors.New("DOCKER_USERNAME is empty"))
	}

	if password == "" {
		log.Fatal(errors.New("DOCKER_PASSWORD is empty"))
	}

	log.Infof("Login to Docker Hub as %s", username)
	err := docker.Login(username, password)

	if err != nil {
		log.Fatal("Login to Docker Hub failed", err)
	}
}

func deploy(docker *Docker, tag string) {
	imageWithTag := DockerImageName + ":" + tag

	log.Infof("Docker image %s will be deployed on Docker Hub", imageWithTag)

	if err := docker.Tag(DockerImageName, imageWithTag); err != nil {
		log.Fatal(err)
	}

	if err := docker.Push(imageWithTag); err != nil {
		log.Fatal(err)
	}
}

func deployManifest(docker *Docker, tag string, amd64tag string, arm32v7tag string, arm64v8tag string) {
	dockerImagePrefix := DockerImageName + ":"

	log.Infof("Docker manifest %s%s will be deployed on Docker Hub", dockerImagePrefix, tag)

	err := docker.Manifest(dockerImagePrefix+tag, dockerImagePrefix+amd64tag, dockerImagePrefix+arm32v7tag, dockerImagePrefix+arm64v8tag)

	if err != nil {
		log.Fatal(err)
	}

	tags := []string{amd64tag, arm32v7tag, arm64v8tag}
	for _, t := range tags {
		log.Infof("Docker removing tag for %s%s on Docker Hub", dockerImagePrefix, t)

		if err := docker.CleanTag(t); err != nil {
			panic(err)
		}
	}
}

func publishDockerImage(arch string) {
	docker := &Docker{}

	switch {
	case ciTag != "":
		if len(tags) == 4 {
			log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
			login(docker)
			deploy(docker, tags[1]+"-"+arch)

			if !ignoredSuffixes.MatchString(ciTag) {
				deploy(docker, tags[2]+"-"+arch)
				deploy(docker, tags[3]+"-"+arch)
				deploy(docker, "latest-"+arch)
			}
		} else {
			log.Fatal("Docker image will not be published, the specified tag does not conform to the standard")
		}
	case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
		login(docker)
		deploy(docker, ciBranch+"-"+arch)
	case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
		login(docker)
		deploy(docker, "PR"+ciPullRequest+"-"+arch)
	case ciBranch == masterTag && ciPullRequest == stringFalse:
		login(docker)
		deploy(docker, "master-"+arch)
	default:
		log.Info("Docker image will not be published")
	}
}

func publishDockerManifest() {
	docker := &Docker{}

	switch {
	case ciTag != "":
		if len(tags) == 4 {
			log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
			login(docker)
			deployManifest(docker, tags[1], tags[1]+"-amd64", tags[1]+"-arm32v7", tags[1]+"-arm64v8")
			publishDockerReadme(docker)

			if !ignoredSuffixes.MatchString(ciTag) {
				deployManifest(docker, tags[2], tags[2]+"-amd64", tags[2]+"-arm32v7", tags[2]+"-arm64v8")
				deployManifest(docker, tags[3], tags[3]+"-amd64", tags[3]+"-arm32v7", tags[3]+"-arm64v8")
				deployManifest(docker, "latest", "latest-amd64", "latest-arm32v7", "latest-arm64v8")
				publishDockerReadme(docker)
				updateMicroBadger(docker)
			}
		} else {
			log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
		}
	case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
		login(docker)
		deployManifest(docker, ciBranch, ciBranch+"-amd64", ciBranch+"-arm32v7", ciBranch+"-arm64v8")
	case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
		login(docker)
		deployManifest(docker, "PR"+ciPullRequest, "PR"+ciPullRequest+"-amd64", "PR"+ciPullRequest+"-arm32v7", "PR"+ciPullRequest+"-arm64v8")
	case ciBranch == masterTag && ciPullRequest == stringFalse:
		login(docker)
		deployManifest(docker, "master", "master-amd64", "master-arm32v7", "master-arm64v8")
		publishDockerReadme(docker)
	default:
		log.Info("Docker manifest will not be published")
	}
}

func publishDockerReadme(docker *Docker) {
	log.Info("Docker pushing README.md to Docker Hub")

	if err := docker.PublishReadme(); err != nil {
		log.Fatal(err)
	}
}
func updateMicroBadger(docker *Docker) {
	log.Info("Updating MicroBadger metadata from Docker Hub")

	if err := docker.UpdateMicroBadger(); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>[CI] Update QEMU to v5.1.0-5 (#1393)<commit_after>package main

import (
	"errors"
	"fmt"
	"os"
	"regexp"
	"strings"

	log "github.com/sirupsen/logrus"
	"github.com/spf13/cobra"

	"github.com/authelia/authelia/internal/utils"
)

var arch string

var supportedArch = []string{"amd64", "arm32v7", "arm64v8", "coverage"}
var defaultArch = "amd64"
var buildkiteQEMU = os.Getenv("BUILDKITE_AGENT_META_DATA_QEMU")
var ciBranch = os.Getenv("BUILDKITE_BRANCH")
var ciPullRequest = os.Getenv("BUILDKITE_PULL_REQUEST")
var ciTag = os.Getenv("BUILDKITE_TAG")
var dockerTags = regexp.MustCompile(`v(?P<Patch>(?P<Minor>(?P<Major>\d+)\.\d+)\.\d+.*)`)
var ignoredSuffixes = regexp.MustCompile("alpha|beta")
var publicRepo = regexp.MustCompile(`.*\:.*`)
var tags = dockerTags.FindStringSubmatch(ciTag)

func init() {
	DockerBuildCmd.PersistentFlags().StringVar(&arch, "arch", defaultArch, "target architecture among: "+strings.Join(supportedArch, ", "))
	DockerPushCmd.PersistentFlags().StringVar(&arch, "arch", defaultArch, "target architecture among: "+strings.Join(supportedArch, ", "))
}

func checkArchIsSupported(arch string) {
	for _, a := range supportedArch {
		if arch == a {
			return
		}
	}

	log.Fatal("Architecture is not supported. Please select one of " + strings.Join(supportedArch, ", ") + ".")
}

func dockerBuildOfficialImage(arch string) error {
	docker := &Docker{}
	// Set default Architecture Dockerfile to amd64.
	dockerfile := "Dockerfile"
	// Set version of QEMU.
	qemuversion := "v5.1.0-5"

	// If not the default value.
	if arch != defaultArch {
		dockerfile = fmt.Sprintf("%s.%s", dockerfile, arch)
	}

	if arch == "arm32v7" {
		if buildkiteQEMU != stringTrue {
			err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run()
			if err != nil {
				panic(err)
			}
		}

		err := utils.CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/"+qemuversion+"/qemu-arm-static -O ./qemu-arm-static && chmod +x ./qemu-arm-static").Run()

		if err != nil {
			panic(err)
		}
	} else if arch == "arm64v8" {
		if buildkiteQEMU != stringTrue {
			err := utils.CommandWithStdout("docker", "run", "--rm", "--privileged", "multiarch/qemu-user-static", "--reset", "-p", "yes").Run()
			if err != nil {
				panic(err)
			}
		}

		err := utils.CommandWithStdout("bash", "-c", "wget https://github.com/multiarch/qemu-user-static/releases/download/"+qemuversion+"/qemu-aarch64-static -O ./qemu-aarch64-static && chmod +x ./qemu-aarch64-static").Run()

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

	gitTag := ciTag
	if gitTag == "" {
		// If commit is not tagged, mark the build has having master tag.
		gitTag = masterTag
	}

	cmd := utils.Shell("git rev-parse HEAD")
	cmd.Stdout = nil
	cmd.Stderr = nil
	commitBytes, err := cmd.Output()

	if err != nil {
		log.Fatal(err)
	}

	commitHash := strings.Trim(string(commitBytes), "\n")

	return docker.Build(IntermediateDockerImageName, dockerfile, ".", gitTag, commitHash)
}

// DockerBuildCmd Command for building docker image of Authelia.
var DockerBuildCmd = &cobra.Command{
	Use:   "build",
	Short: "Build the docker image of Authelia",
	Run: func(cmd *cobra.Command, args []string) {
		log.Infof("Building Docker image %s...", DockerImageName)
		checkArchIsSupported(arch)
		err := dockerBuildOfficialImage(arch)

		if err != nil {
			log.Fatal(err)
		}

		docker := &Docker{}
		err = docker.Tag(IntermediateDockerImageName, DockerImageName)

		if err != nil {
			panic(err)
		}
	},
}

// DockerPushCmd Command for pushing Authelia docker image to DockerHub.
var DockerPushCmd = &cobra.Command{
	Use:   "push-image",
	Short: "Publish Authelia docker image to Docker Hub",
	Run: func(cmd *cobra.Command, args []string) {
		log.Infof("Pushing Docker image %s to Docker Hub...", DockerImageName)
		checkArchIsSupported(arch)
		publishDockerImage(arch)
	},
}

// DockerManifestCmd Command for pushing Authelia docker manifest to DockerHub.
var DockerManifestCmd = &cobra.Command{
	Use:   "push-manifest",
	Short: "Publish Authelia docker manifest to Docker Hub",
	Run: func(cmd *cobra.Command, args []string) {
		log.Infof("Pushing Docker manifest of %s to Docker Hub...", DockerImageName)
		publishDockerManifest()
	},
}

func login(docker *Docker) {
	username := os.Getenv("DOCKER_USERNAME")
	password := os.Getenv("DOCKER_PASSWORD")

	if username == "" {
		log.Fatal(errors.New("DOCKER_USERNAME is empty"))
	}

	if password == "" {
		log.Fatal(errors.New("DOCKER_PASSWORD is empty"))
	}

	log.Infof("Login to Docker Hub as %s", username)
	err := docker.Login(username, password)

	if err != nil {
		log.Fatal("Login to Docker Hub failed", err)
	}
}

func deploy(docker *Docker, tag string) {
	imageWithTag := DockerImageName + ":" + tag

	log.Infof("Docker image %s will be deployed on Docker Hub", imageWithTag)

	if err := docker.Tag(DockerImageName, imageWithTag); err != nil {
		log.Fatal(err)
	}

	if err := docker.Push(imageWithTag); err != nil {
		log.Fatal(err)
	}
}

func deployManifest(docker *Docker, tag string, amd64tag string, arm32v7tag string, arm64v8tag string) {
	dockerImagePrefix := DockerImageName + ":"

	log.Infof("Docker manifest %s%s will be deployed on Docker Hub", dockerImagePrefix, tag)

	err := docker.Manifest(dockerImagePrefix+tag, dockerImagePrefix+amd64tag, dockerImagePrefix+arm32v7tag, dockerImagePrefix+arm64v8tag)

	if err != nil {
		log.Fatal(err)
	}

	tags := []string{amd64tag, arm32v7tag, arm64v8tag}
	for _, t := range tags {
		log.Infof("Docker removing tag for %s%s on Docker Hub", dockerImagePrefix, t)

		if err := docker.CleanTag(t); err != nil {
			panic(err)
		}
	}
}

func publishDockerImage(arch string) {
	docker := &Docker{}

	switch {
	case ciTag != "":
		if len(tags) == 4 {
			log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
			login(docker)
			deploy(docker, tags[1]+"-"+arch)

			if !ignoredSuffixes.MatchString(ciTag) {
				deploy(docker, tags[2]+"-"+arch)
				deploy(docker, tags[3]+"-"+arch)
				deploy(docker, "latest-"+arch)
			}
		} else {
			log.Fatal("Docker image will not be published, the specified tag does not conform to the standard")
		}
	case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
		login(docker)
		deploy(docker, ciBranch+"-"+arch)
	case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
		login(docker)
		deploy(docker, "PR"+ciPullRequest+"-"+arch)
	case ciBranch == masterTag && ciPullRequest == stringFalse:
		login(docker)
		deploy(docker, "master-"+arch)
	default:
		log.Info("Docker image will not be published")
	}
}

func publishDockerManifest() {
	docker := &Docker{}

	switch {
	case ciTag != "":
		if len(tags) == 4 {
			log.Infof("Detected tags: '%s' | '%s' | '%s'", tags[1], tags[2], tags[3])
			login(docker)
			deployManifest(docker, tags[1], tags[1]+"-amd64", tags[1]+"-arm32v7", tags[1]+"-arm64v8")
			publishDockerReadme(docker)

			if !ignoredSuffixes.MatchString(ciTag) {
				deployManifest(docker, tags[2], tags[2]+"-amd64", tags[2]+"-arm32v7", tags[2]+"-arm64v8")
				deployManifest(docker, tags[3], tags[3]+"-amd64", tags[3]+"-arm32v7", tags[3]+"-arm64v8")
				deployManifest(docker, "latest", "latest-amd64", "latest-arm32v7", "latest-arm64v8")
				publishDockerReadme(docker)
				updateMicroBadger(docker)
			}
		} else {
			log.Fatal("Docker manifest will not be published, the specified tag does not conform to the standard")
		}
	case ciBranch != masterTag && !publicRepo.MatchString(ciBranch):
		login(docker)
		deployManifest(docker, ciBranch, ciBranch+"-amd64", ciBranch+"-arm32v7", ciBranch+"-arm64v8")
	case ciBranch != masterTag && publicRepo.MatchString(ciBranch):
		login(docker)
		deployManifest(docker, "PR"+ciPullRequest, "PR"+ciPullRequest+"-amd64", "PR"+ciPullRequest+"-arm32v7", "PR"+ciPullRequest+"-arm64v8")
	case ciBranch == masterTag && ciPullRequest == stringFalse:
		login(docker)
		deployManifest(docker, "master", "master-amd64", "master-arm32v7", "master-arm64v8")
		publishDockerReadme(docker)
	default:
		log.Info("Docker manifest will not be published")
	}
}

func publishDockerReadme(docker *Docker) {
	log.Info("Docker pushing README.md to Docker Hub")

	if err := docker.PublishReadme(); err != nil {
		log.Fatal(err)
	}
}
func updateMicroBadger(docker *Docker) {
	log.Info("Updating MicroBadger metadata from Docker Hub")

	if err := docker.UpdateMicroBadger(); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"go/types"

	"golang.org/x/tools/go/loader"
)

var (
	buildContext = build.Default

	loaderConfig = loader.Config{
		TypeChecker: types.Config{FakeImportC: true},
		Build:       &buildContext,
		AllowErrors: true,
	}

	config *srcfileConfig

	goBinaryName string

	validVersions = []string{"", "1.3", "1.2", "1.1", "1"}

	// effectiveConfigGOPATHs is a list of GOPATH dirs that were
	// created as a result of the GOPATH config property. These are
	// the dirs that are appended to the actual build context GOPATH.
	effectiveConfigGOPATHs []string
)

// isInEffectiveConfigGOPATH is true if dir is underneath any of the
// dirs in effectiveConfigGOPATHs.
func isInEffectiveConfigGOPATH(dir string) bool {
	for _, gopath := range effectiveConfigGOPATHs {
		if pathHasPrefix(dir, gopath) {
			return true
		}
	}
	return false
}

type srcfileConfig struct {
	// GOROOT, if specified, is made absolute (prefixed with the
	// directory that the repository being built is checked out to)
	// and is set as the GOROOT environment variable.
	GOROOT string

	// GOROOTForCmd, if set, is used as the GOROOT env var when
	// invoking the "go" tool.
	GOROOTForCmd string

	// GOPATH's colon-separated dirs, if specified, are made absolute
	// (prefixed with the directory that the repository being built is
	// checked out to) and the resulting value is appended to the
	// GOPATH environment variable during the build.
	GOPATH string

	// GOVERSION is the version of the go tool that srclib-go
	// should shell out to. If GOVERSION is empty, the system's
	// default go binary is used. The only valid values for
	// GOVERSION are the empty string, "1.3", "1.2", "1.1", and
	// "1", which are transformed into "go", "go1.3", ..., "go1",
	// respectively, when the binary is called.
	GOVERSION string

	PkgPatterns []string // pattern passed to `go list` (defaults to {"./..."})

	// SkipGodeps makes srclib-go skip the Godeps/_workspace directory when
	// scanning for packages. This causes references to those packages to point
	// to the files in their respective repositories instead of the local copies
	// inside of Godeps/_workspace.
	SkipGodeps bool

	// ImportPathRoot is a prefix which is used when converting from repository
	// URI to Go import path.
	ImportPathRoot string

	// VendorDirs are a list of all src directories used by a package,
	// relative to the repository root. These are used for
	// GO15VENDOREXPERIMENT related vendor support, and this is usually
	// not set by the user. At build time a GOPATH is created which will
	// include each VendorDir on it linked to src. VendorDirs should be
	// sorted by longest path to shortest (ie most specific to least
	// specific)
	VendorDirs []string
}

// unmarshalTypedConfig parses config from the Config field of the source unit.
// It stores it in the config global variable.
//
// Callers should typically call config.apply() after calling
// unmarshalTypedConfig to actually apply the config.
func unmarshalTypedConfig(cfg map[string]interface{}) error {
	data, err := json.Marshal(cfg)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(data, &config); err != nil {
		return err
	}

	if config == nil {
		config = &srcfileConfig{}
	}

	return config.apply()
}

// apply applies the configuration.
func (c *srcfileConfig) apply() error {
	// KLUDGE: determine whether we're in the stdlib and if so, set GOROOT to "." before applying config.
	// This is necessary for the stdlib unit names to be correct.
	cloneURL, err := exec.Command("git", "config", "--get", "remote.origin.url").Output()
	if err == nil && strings.HasSuffix(strings.TrimSpace(string(cloneURL)), "github.com/golang/go") && c.GOROOT == "" {
		c.GOROOT = "."
	}

	var versionValid bool
	for _, v := range validVersions {
		if config.GOVERSION == v {
			versionValid = true
			goBinaryName = fmt.Sprintf("go%s", config.GOVERSION)
			if config.GOVERSION != "" && config.GOROOT == "" {
				// If GOROOT is empty, assign $GOROOT<version_num> to it.
				newGOROOT := os.Getenv(fmt.Sprintf("GOROOT%s", strings.Replace(config.GOVERSION, ".", "", -1)))
				if newGOROOT != "" {
					config.GOROOTForCmd = newGOROOT
				}
			}
			break
		}
	}
	if !versionValid {
		return fmt.Errorf("The version %s is not valid. Use one of the following: %v", config.GOVERSION, validVersions)
	}

	if config.GOROOT != "" {
		// clean/absolutize all paths
		config.GOROOT = filepath.Clean(config.GOROOT)
		if !filepath.IsAbs(config.GOROOT) {
			config.GOROOT = filepath.Join(cwd, config.GOROOT)
		}

		buildContext.GOROOT = c.GOROOT
		loaderConfig.Build = &buildContext
	}

	if config.GOPATH != "" {
		// clean/absolutize all paths
		dirs := cleanDirs(filepath.SplitList(config.GOPATH))
		config.GOPATH = strings.Join(dirs, string(filepath.ListSeparator))

		dirs = append(dirs, filepath.SplitList(buildContext.GOPATH)...)
		buildContext.GOPATH = strings.Join(uniq(dirs), string(filepath.ListSeparator))
		loaderConfig.Build = &buildContext
	}

	config.VendorDirs = cleanDirs(config.VendorDirs)

	if config.GOROOTForCmd == "" {
		config.GOROOTForCmd = buildContext.GOROOT
	}

	return nil
}

func (c *srcfileConfig) env() []string {
	return []string{
		"PATH=" + os.Getenv("PATH"),
		"GOARCH=" + buildContext.GOARCH,
		"GOOS=" + buildContext.GOOS,
		"GOROOT=" + config.GOROOTForCmd,
		"GOPATH=" + buildContext.GOPATH,
		"GO15VENDOREXPERIMENT=1",
	}
}

func pathHasPrefix(path, prefix string) bool {
	return prefix == "." || path == prefix || strings.HasPrefix(path, prefix+string(filepath.Separator))
}

// cleanDirs takes a list of paths cleans/abs them + removes duplicates
func cleanDirs(dirs []string) []string {
	dirs = uniq(dirs)
	for i, dir := range dirs {
		dir = filepath.Clean(dir)
		if !filepath.IsAbs(dir) {
			dir = filepath.Join(cwd, dir)
		}
		dirs[i] = dir
	}
	return dirs
}

// uniq maintains the order of s.
func uniq(s []string) []string {
	seen := make(map[string]struct{}, len(s))
	var uniq []string
	for _, s := range s {
		if _, seen := seen[s]; seen {
			continue
		}
		seen[s] = struct{}{}
		uniq = append(uniq, s)
	}
	return uniq
}
<commit_msg>add default importer for reading .a object files<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"go/build"
	"go/importer"
	"os"
	"os/exec"
	"path/filepath"
	"strings"

	"go/types"

	"golang.org/x/tools/go/loader"
)

var (
	buildContext = build.Default

	loaderConfig = loader.Config{
		TypeChecker: types.Config{
			Importer:    importer.Default(),
			FakeImportC: true,
		},
		Build:       &buildContext,
		AllowErrors: true,
	}

	config *srcfileConfig

	goBinaryName string

	validVersions = []string{"", "1.3", "1.2", "1.1", "1"}

	// effectiveConfigGOPATHs is a list of GOPATH dirs that were
	// created as a result of the GOPATH config property. These are
	// the dirs that are appended to the actual build context GOPATH.
	effectiveConfigGOPATHs []string
)

// isInEffectiveConfigGOPATH is true if dir is underneath any of the
// dirs in effectiveConfigGOPATHs.
func isInEffectiveConfigGOPATH(dir string) bool {
	for _, gopath := range effectiveConfigGOPATHs {
		if pathHasPrefix(dir, gopath) {
			return true
		}
	}
	return false
}

type srcfileConfig struct {
	// GOROOT, if specified, is made absolute (prefixed with the
	// directory that the repository being built is checked out to)
	// and is set as the GOROOT environment variable.
	GOROOT string

	// GOROOTForCmd, if set, is used as the GOROOT env var when
	// invoking the "go" tool.
	GOROOTForCmd string

	// GOPATH's colon-separated dirs, if specified, are made absolute
	// (prefixed with the directory that the repository being built is
	// checked out to) and the resulting value is appended to the
	// GOPATH environment variable during the build.
	GOPATH string

	// GOVERSION is the version of the go tool that srclib-go
	// should shell out to. If GOVERSION is empty, the system's
	// default go binary is used. The only valid values for
	// GOVERSION are the empty string, "1.3", "1.2", "1.1", and
	// "1", which are transformed into "go", "go1.3", ..., "go1",
	// respectively, when the binary is called.
	GOVERSION string

	PkgPatterns []string // pattern passed to `go list` (defaults to {"./..."})

	// SkipGodeps makes srclib-go skip the Godeps/_workspace directory when
	// scanning for packages. This causes references to those packages to point
	// to the files in their respective repositories instead of the local copies
	// inside of Godeps/_workspace.
	SkipGodeps bool

	// ImportPathRoot is a prefix which is used when converting from repository
	// URI to Go import path.
	ImportPathRoot string

	// VendorDirs are a list of all src directories used by a package,
	// relative to the repository root. These are used for
	// GO15VENDOREXPERIMENT related vendor support, and this is usually
	// not set by the user. At build time a GOPATH is created which will
	// include each VendorDir on it linked to src. VendorDirs should be
	// sorted by longest path to shortest (ie most specific to least
	// specific)
	VendorDirs []string
}

// unmarshalTypedConfig parses config from the Config field of the source unit.
// It stores it in the config global variable.
//
// Callers should typically call config.apply() after calling
// unmarshalTypedConfig to actually apply the config.
func unmarshalTypedConfig(cfg map[string]interface{}) error {
	data, err := json.Marshal(cfg)
	if err != nil {
		return err
	}

	if err := json.Unmarshal(data, &config); err != nil {
		return err
	}

	if config == nil {
		config = &srcfileConfig{}
	}

	return config.apply()
}

// apply applies the configuration.
func (c *srcfileConfig) apply() error {
	// KLUDGE: determine whether we're in the stdlib and if so, set GOROOT to "." before applying config.
	// This is necessary for the stdlib unit names to be correct.
	cloneURL, err := exec.Command("git", "config", "--get", "remote.origin.url").Output()
	if err == nil && strings.HasSuffix(strings.TrimSpace(string(cloneURL)), "github.com/golang/go") && c.GOROOT == "" {
		c.GOROOT = "."
	}

	var versionValid bool
	for _, v := range validVersions {
		if config.GOVERSION == v {
			versionValid = true
			goBinaryName = fmt.Sprintf("go%s", config.GOVERSION)
			if config.GOVERSION != "" && config.GOROOT == "" {
				// If GOROOT is empty, assign $GOROOT<version_num> to it.
				newGOROOT := os.Getenv(fmt.Sprintf("GOROOT%s", strings.Replace(config.GOVERSION, ".", "", -1)))
				if newGOROOT != "" {
					config.GOROOTForCmd = newGOROOT
				}
			}
			break
		}
	}
	if !versionValid {
		return fmt.Errorf("The version %s is not valid. Use one of the following: %v", config.GOVERSION, validVersions)
	}

	if config.GOROOT != "" {
		// clean/absolutize all paths
		config.GOROOT = filepath.Clean(config.GOROOT)
		if !filepath.IsAbs(config.GOROOT) {
			config.GOROOT = filepath.Join(cwd, config.GOROOT)
		}

		buildContext.GOROOT = c.GOROOT
		loaderConfig.Build = &buildContext
	}

	if config.GOPATH != "" {
		// clean/absolutize all paths
		dirs := cleanDirs(filepath.SplitList(config.GOPATH))
		config.GOPATH = strings.Join(dirs, string(filepath.ListSeparator))

		dirs = append(dirs, filepath.SplitList(buildContext.GOPATH)...)
		buildContext.GOPATH = strings.Join(uniq(dirs), string(filepath.ListSeparator))
		loaderConfig.Build = &buildContext
	}

	config.VendorDirs = cleanDirs(config.VendorDirs)

	if config.GOROOTForCmd == "" {
		config.GOROOTForCmd = buildContext.GOROOT
	}

	return nil
}

func (c *srcfileConfig) env() []string {
	return []string{
		"PATH=" + os.Getenv("PATH"),
		"GOARCH=" + buildContext.GOARCH,
		"GOOS=" + buildContext.GOOS,
		"GOROOT=" + config.GOROOTForCmd,
		"GOPATH=" + buildContext.GOPATH,
		"GO15VENDOREXPERIMENT=1",
	}
}

func pathHasPrefix(path, prefix string) bool {
	return prefix == "." || path == prefix || strings.HasPrefix(path, prefix+string(filepath.Separator))
}

// cleanDirs takes a list of paths cleans/abs them + removes duplicates
func cleanDirs(dirs []string) []string {
	dirs = uniq(dirs)
	for i, dir := range dirs {
		dir = filepath.Clean(dir)
		if !filepath.IsAbs(dir) {
			dir = filepath.Join(cwd, dir)
		}
		dirs[i] = dir
	}
	return dirs
}

// uniq maintains the order of s.
func uniq(s []string) []string {
	seen := make(map[string]struct{}, len(s))
	var uniq []string
	for _, s := range s {
		if _, seen := seen[s]; seen {
			continue
		}
		seen[s] = struct{}{}
		uniq = append(uniq, s)
	}
	return uniq
}
<|endoftext|>