<commit_before>package transactionpool

import (
	"errors"
	"os"
	"path/filepath"

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

	"github.com/NebulousLabs/bolt"
)

var (
	// bucketRecentConsensusChange holds the most recent consensus change seen
	// by the transaction pool.
	bucketRecentConsensusChange = []byte("RecentConsensusChange")

	// bucketConfirmedTransactions holds the ids of every transaction that has
	// been confirmed on the blockchain.
	bucketConfirmedTransactions = []byte("ConfirmedTransactions")

	// errNilConsensusChange is returned if there is no consensus change in the
	// database.
	errNilConsensusChange = errors.New("no consensus change found")

	// fieldRecentConsensusChange is the field in bucketRecentConsensusChange
	// that holds the value of the most recent consensus change.
	fieldRecentConsensusChange = []byte("RecentConsensusChange")
)

// resetDB deletes all consensus related persistence from the transaction pool.
func (tp *TransactionPool) resetDB(tx *bolt.Tx) error {
	err := tx.DeleteBucket(bucketConfirmedTransactions)
	if err != nil {
		return err
	}
	err = tp.putRecentConsensusChange(tx, modules.ConsensusChangeBeginning)
	if err != nil {
		return err
	}
	_, err = tx.CreateBucket(bucketConfirmedTransactions)
	return err
}

// initPersist creates buckets in the database
func (tp *TransactionPool) initPersist() error {
	// Create the persist directory if it does not yet exist.
	err := os.MkdirAll(tp.persistDir, 0700)
	if err != nil {
		return err
	}

	// Open the database file.
	tp.db, err = persist.OpenDatabase(dbMetadata, filepath.Join(tp.persistDir, dbFilename))
	if err != nil {
		return err
	}

	// Create the database and get the most recent consensus change.
	cc := modules.ConsensusChangeBeginning
	err = tp.db.Update(func(tx *bolt.Tx) error {
		// Create the database buckets.
		buckets := [][]byte{
			bucketRecentConsensusChange,
			bucketConfirmedTransactions,
		}
		for _, bucket := range buckets {
			_, err := tx.CreateBucketIfNotExists(bucket)
			if err != nil {
				return err
			}
		}

		// Get the recent consensus change.
		cc, err = tp.getRecentConsensusChange(tx)
		if err == errNilConsensusChange {
			return tp.putRecentConsensusChange(tx, modules.ConsensusChangeBeginning)
		}
		return err
	})
	if err != nil {
		return err
	}

	// Subscribe to the consensus set using the most recent consensus change.
	err = tp.consensusSet.ConsensusSetSubscribe(tp, cc)
	if err == modules.ErrInvalidConsensusChangeID {
		// Reset and rescan because the consensus set does not recognize the
		// provided consensus change id.
		resetErr := tp.db.Update(func(tx *bolt.Tx) error {
			return tp.resetDB(tx)
		})
		if resetErr != nil {
			return resetErr
		}
		return tp.consensusSet.ConsensusSetSubscribe(tp, modules.ConsensusChangeBeginning)
	}
	return err
}

// getRecentConsensusChange returns the most recent consensus change from the
// database.
func (tp *TransactionPool) getRecentConsensusChange(tx *bolt.Tx) (cc modules.ConsensusChangeID, err error) {
	ccBytes := tx.Bucket(bucketRecentConsensusChange).Get(fieldRecentConsensusChange)
	if ccBytes == nil {
		return modules.ConsensusChangeID{}, errNilConsensusChange
	}
	copy(cc[:], ccBytes)
	return cc, nil
}

// putRecentConsensusChange updates the most recent consensus change seen by
// the transaction pool.
func (tp *TransactionPool) putRecentConsensusChange(tx *bolt.Tx, cc modules.ConsensusChangeID) error {
	return tx.Bucket(bucketRecentConsensusChange).Put(fieldRecentConsensusChange, cc[:])
}

// transactionConfirmed returns true if the transaction has been confirmed on
// the blockchain and false if the transaction has not been confirmed on the
// blockchain.
func (tp *TransactionPool) transactionConfirmed(tx *bolt.Tx, id types.TransactionID) bool {
	confirmedBytes := tx.Bucket(bucketConfirmedTransactions).Get(id[:])
	if confirmedBytes == nil {
		return false
	}
	if confirmedBytes[0] == 1 {
		return true
	}
	build.Critical("transaction database has an illegal value for a txid")
	return false
}

// addTransaction adds a transaction to the list of confirmed transactions.
func (tp *TransactionPool) addTransaction(tx *bolt.Tx, id types.TransactionID) error {
	return tx.Bucket(bucketConfirmedTransactions).Put(id[:], []byte{1})
}

// deleteTransaction deletes a transaction from the list of confirmed
// transactions.
func (tp *TransactionPool) deleteTransaction(tx *bolt.Tx, id types.TransactionID) error {
	return tx.Bucket(bucketConfirmedTransactions).Delete(id[:])
}
<commit_msg>pr comments - small tweaks<commit_after>package transactionpool

import (
	"errors"
	"os"
	"path/filepath"

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

	"github.com/NebulousLabs/bolt"
)

var (
	// bucketRecentConsensusChange holds the most recent consensus change seen
	// by the transaction pool.
	bucketRecentConsensusChange = []byte("RecentConsensusChange")

	// bucketConfirmedTransactions holds the ids of every transaction that has
	// been confirmed on the blockchain.
	bucketConfirmedTransactions = []byte("ConfirmedTransactions")

	// errNilConsensusChange is returned if there is no consensus change in the
	// database.
	errNilConsensusChange = errors.New("no consensus change found")

	// fieldRecentConsensusChange is the field in bucketRecentConsensusChange
	// that holds the value of the most recent consensus change.
	fieldRecentConsensusChange = []byte("RecentConsensusChange")
)

// resetDB deletes all consensus related persistence from the transaction pool.
func (tp *TransactionPool) resetDB(tx *bolt.Tx) error {
	err := tx.DeleteBucket(bucketConfirmedTransactions)
	if err != nil {
		return err
	}
	err = tp.putRecentConsensusChange(tx, modules.ConsensusChangeBeginning)
	if err != nil {
		return err
	}
	_, err = tx.CreateBucket(bucketConfirmedTransactions)
	return err
}

// initPersist creates buckets in the database
func (tp *TransactionPool) initPersist() error {
	// Create the persist directory if it does not yet exist.
	err := os.MkdirAll(tp.persistDir, 0700)
	if err != nil {
		return err
	}

	// Open the database file.
	tp.db, err = persist.OpenDatabase(dbMetadata, filepath.Join(tp.persistDir, dbFilename))
	if err != nil {
		return err
	}

	// Create the database and get the most recent consensus change.
	var cc modules.ConsensusChangeID
	err = tp.db.Update(func(tx *bolt.Tx) error {
		// Create the database buckets.
		buckets := [][]byte{
			bucketRecentConsensusChange,
			bucketConfirmedTransactions,
		}
		for _, bucket := range buckets {
			_, err := tx.CreateBucketIfNotExists(bucket)
			if err != nil {
				return err
			}
		}

		// Get the recent consensus change.
		cc, err = tp.getRecentConsensusChange(tx)
		if err == errNilConsensusChange {
			return tp.putRecentConsensusChange(tx, modules.ConsensusChangeBeginning)
		}
		return err
	})
	if err != nil {
		return err
	}

	// Subscribe to the consensus set using the most recent consensus change.
	err = tp.consensusSet.ConsensusSetSubscribe(tp, cc)
	if err == modules.ErrInvalidConsensusChangeID {
		// Reset and rescan because the consensus set does not recognize the
		// provided consensus change id.
		resetErr := tp.db.Update(func(tx *bolt.Tx) error {
			return tp.resetDB(tx)
		})
		if resetErr != nil {
			return resetErr
		}
		return tp.consensusSet.ConsensusSetSubscribe(tp, modules.ConsensusChangeBeginning)
	}
	return err
}

// getRecentConsensusChange returns the most recent consensus change from the
// database.
func (tp *TransactionPool) getRecentConsensusChange(tx *bolt.Tx) (cc modules.ConsensusChangeID, err error) {
	ccBytes := tx.Bucket(bucketRecentConsensusChange).Get(fieldRecentConsensusChange)
	if ccBytes == nil {
		return modules.ConsensusChangeID{}, errNilConsensusChange
	}
	copy(cc[:], ccBytes)
	return cc, nil
}

// putRecentConsensusChange updates the most recent consensus change seen by
// the transaction pool.
func (tp *TransactionPool) putRecentConsensusChange(tx *bolt.Tx, cc modules.ConsensusChangeID) error {
	return tx.Bucket(bucketRecentConsensusChange).Put(fieldRecentConsensusChange, cc[:])
}

// transactionConfirmed returns true if the transaction has been confirmed on
// the blockchain and false if the transaction has not been confirmed on the
// blockchain.
func (tp *TransactionPool) transactionConfirmed(tx *bolt.Tx, id types.TransactionID) bool {
	confirmedBytes := tx.Bucket(bucketConfirmedTransactions).Get(id[:])
	if confirmedBytes == nil {
		return false
	}
	return true
}

// addTransaction adds a transaction to the list of confirmed transactions.
func (tp *TransactionPool) addTransaction(tx *bolt.Tx, id types.TransactionID) error {
	return tx.Bucket(bucketConfirmedTransactions).Put(id[:], []byte{})
}

// deleteTransaction deletes a transaction from the list of confirmed
// transactions.
func (tp *TransactionPool) deleteTransaction(tx *bolt.Tx, id types.TransactionID) error {
	return tx.Bucket(bucketConfirmedTransactions).Delete(id[:])
}
<|endoftext|>


<commit_before>/**
 * Copyright 2014 @ z3q.net.
 * name :
 * author : jarryliu
 * date : 2013-12-09 20:14
 * description :
 * history :
 */

package dps

import (
	"errors"
	"fmt"
	"go2o/src/core/domain/interface/member"
	"go2o/src/core/domain/interface/partner"
	"go2o/src/core/domain/interface/valueobject"
	"go2o/src/core/dto"
	"go2o/src/core/infrastructure/format"
	"go2o/src/core/query"
	"time"
)

type memberService struct {
	_memberRep member.IMemberRep
	_query     *query.MemberQuery
}

func NewMemberService(rep member.IMemberRep, q *query.MemberQuery) *memberService {
	return &memberService{
		_memberRep: rep,
		_query:     q,
	}
}

func (this *memberService) GetMember(id int) *member.ValueMember {
	v := this._memberRep.GetMember(id)
	if v != nil {
		nv := v.GetValue()
		return &nv
	}
	return nil
}

func (this *memberService) getMember(partnerId, memberId int) (member.IMember, error) {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return m, member.ErrNoSuchMember
	}
	if m.GetRelation().RegisterPartnerId != partnerId {
		return m, partner.ErrPartnerNotMatch
	}
	return m, nil
}

func (this *memberService) GetMemberIdByInvitationCode(code string) int {
	return this._memberRep.GetMemberIdByInvitationCode(code)
}

func (this *memberService) SaveMember(v *member.ValueMember) (int, error) {
	if v.Id > 0 {
		return this.updateMember(v)
	}
	return this.createMember(v)
}

func (this *memberService) updateMember(v *member.ValueMember) (int, error) {
	m := this._memberRep.GetMember(v.Id)
	if m == nil {
		return -1, member.ErrNoSuchMember
	}
	if err := m.SetValue(v); err != nil {
		return m.GetAggregateRootId(), err
	}
	return m.Save()
}

func (this *memberService) createMember(v *member.ValueMember) (int, error) {
	m := this._memberRep.CreateMember(v)
	return m.Save()
}

func (this *memberService) SaveRelation(memberId int, cardId string, invitationId, partnerId int) error {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return member.ErrNoSuchMember
	}

	rl := m.GetRelation()
	rl.RefereesId = invitationId
	rl.RegisterPartnerId = partnerId
	rl.CardId = cardId

	return m.SaveRelation(rl)
}

func (this *memberService) GetLevel(memberId int) *valueobject.MemberLevel {
	if m := this._memberRep.GetMember(memberId); m != nil {
		return m.GetLevel()
	}
	return nil
}

func (this *memberService) GetRelation(memberId int) *member.MemberRelation {
	return this._memberRep.GetRelation(memberId)
}

// 锁定/解锁会员
func (this *memberService) LockMember(partnerId, id int) (bool, error) {
	m := this._memberRep.GetMember(id)
	if m == nil {
		return false, member.ErrNoSuchMember
	}

	state := m.GetValue().State
	if state == 1 {
		return false, m.Lock()
	}
	return true, m.Unlock()
}

// 登陆
func (this *memberService) Login(partnerId int, usr, pwd string) (bool, *member.ValueMember, error) {
	val := this._memberRep.GetMemberValueByUsr(usr)
	if val == nil {
		val = this._memberRep.GetMemberValueByPhone(usr)
	}
	if val == nil {
		return false, nil, errors.New("会员不存在")
	}

	if val.Pwd != pwd {
		return false, nil, errors.New("会员用户或密码不正确")
	}

	if val.State == 0 {
		return false, nil, errors.New("会员已停用")
	}

	m := this._memberRep.GetMember(val.Id)
	rl := m.GetRelation()

	if partnerId != -1 && rl.RegisterPartnerId != partnerId {
		return false, nil, errors.New("无法登陆:NOT MATCH PARTNER!")
	}

	unix := time.Now().Unix()
	val.LastLoginTime = unix
	val.UpdateTime = unix

	m.SetValue(val)
	m.Save()

	return true, val, nil
}

func (this *memberService) CheckUsr(usr string, memberId int) error {
	if len(usr) < 6 {
		return member.ErrUserLength
	}
	var id int = this._memberRep.GetMemberIdByUser(usr)
	if id == 0 {
		return nil
	} else if memberId != 0 && id == memberId {
		return nil
	}

	return errors.New("用户名已被使用")
}

func (this *memberService) GetAccount(memberId int) *member.AccountValue {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	//m, _ := this._memberRep.GetMember(memberId)
	//m.AddExp(300)
	return m.GetAccount().GetValue()
}

func (this *memberService) GetBank(memberId int) *member.BankInfo {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	b := m.GetBank()
	return &b
}

func (this *memberService) SaveBankInfo(v *member.BankInfo) error {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: v.MemberId})
	return m.SaveBank(v)
}

// 获取返现记录
func (this *memberService) QueryIncomeLog(memberId, page, size int,
	where, orderBy string) (num int, rows []map[string]interface{}) {
	return this._query.QueryIncomeLog(memberId, page, size, where, orderBy)
}

// 查询分页订单
func (this *memberService) QueryPagerOrder(memberId, page, size int,
	where, orderBy string) (num int, rows []map[string]interface{}) {
	return this._query.QueryPagerOrder(memberId, page, size, where, orderBy)
}

/*********** 收货地址 ***********/
func (this *memberService) GetDeliverAddress(memberId int) []*member.DeliverAddress {
	return this._memberRep.GetDeliverAddress(memberId)
}

//获取配送地址
func (this *memberService) GetDeliverAddressById(memberId,
	deliverId int) *member.DeliverAddress {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	v := m.GetDeliver(deliverId).GetValue()
	return &v
}

//保存配送地址
func (this *memberService) SaveDeliverAddress(memberId int, e *member.DeliverAddress) (int, error) {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	var v member.IDeliver
	if e.Id > 0 {
		v = m.GetDeliver(e.Id)
		v.SetValue(e)
	} else {
		v = m.CreateDeliver(e)
	}
	return v.Save()
}

//删除配送地址
func (this *memberService) DeleteDeliverAddress(memberId int, deliverId int) error {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	return m.DeleteDeliver(deliverId)
}

func (this *memberService) ModifyPassword(memberId int, oldPwd, newPwd string) error {
	m := this._memberRep.GetMember(memberId)
	if m != nil {
		return m.ModifyPassword(newPwd, oldPwd)
	}
	return member.ErrNoSuchMember
}

func (this *memberService) ModifyTradePassword(memberId int, oldPwd, newPwd string) error {
	m := this._memberRep.GetMember(memberId)
	if m != nil {
		return m.ModifyTradePassword(newPwd, oldPwd)
	}
	return member.ErrNoSuchMember
}

//判断会员是否由指定会员邀请推荐的
func (this *memberService) IsInvitation(memberId int, invitationMemberId int) bool {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	return m.Invitation().InvitationBy(invitationMemberId)
}

// 获取我邀请的会员及会员邀请的人数
func (this *memberService) GetMyInvitationMembers(memberId int) ([]*member.ValueMember, map[int]int) {
	iv := this._memberRep.CreateMember(&member.ValueMember{Id: memberId}).Invitation()
	return iv.GetMyInvitationMembers(), iv.GetSubInvitationNum()
}

// 获取会员最后更新时间
func (this *memberService) GetMemberLatestUpdateTime(memberId int) int64 {
	return this._memberRep.GetMemberLatestUpdateTime(memberId)
}

// 获取会员汇总信息
func (this *memberService) GetMemberSummary(memberId int) *dto.MemberSummary {
	var m member.IMember = this._memberRep.GetMember(memberId)
	if m != nil {
		mv := m.GetValue()
		acv := m.GetAccount().GetValue()
		lv := m.GetLevel()
		return &dto.MemberSummary{
			Id:             m.GetAggregateRootId(),
			Usr:            mv.Usr,
			Name:           mv.Name,
			Exp:            mv.Exp,
			Level:          mv.Level,
			LevelName:      lv.Name,
			Integral:       acv.Integral,
			Balance:        acv.Balance,
			PresentBalance: acv.PresentBalance,
			UpdateTime:     mv.UpdateTime,
		}
	}
	return nil
}

// 获取余额变动信息
func (this *memberService) GetBalanceInfoById(memberId, infoId int) *member.BalanceInfoValue {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return nil
	}
	return m.GetAccount().GetBalanceInfo(infoId)
}

// 充值
func (this *memberService) Charge(partnerId, memberId, chargeType int, title, tradeNo string, amount float32) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}
	return m.GetAccount().Charge(chargeType, title, tradeNo, amount)
}

// 提现
func (this *memberService) SubmitApplyCash(partnerId, memberId int, outTradePwd string,
	applyType int, applyAmount float32) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}

	tradePwd := m.GetValue().TradePwd
	if len(tradePwd) == 0 {
		return member.ErrNotSetTradePwd
	}

	if tradePwd != outTradePwd {
		return member.ErrIncorrectTradePwd
	}

	acc := m.GetAccount()
	var title string
	switch applyType {
	case member.TypeApplyCashToBank:
		title = "提现到银行卡"
	case member.TypeApplyCashToCharge:
		title = "充值账户"
	case member.TypeApplyCashToServiceProvider:
		title = "充值到第三方账户"
	}
	return acc.RequestApplyCash(applyType, title, applyAmount)
}

// 获取最近的提现
func (this *memberService) GetLatestApplyCash(memberId int) *member.BalanceInfoValue {
	return this._query.GetLatestBalanceInfoByKind(memberId, member.KindBalanceApplyCash)
}

// 获取最近的提现描述
func (this *memberService) GetLatestApplyCashText(memberId int) string {
	var latestInfo string
	latestApplyInfo := this.GetLatestApplyCash(memberId)
	if latestApplyInfo != nil {
		var sText string
		switch latestApplyInfo.State {
		case 0:
			sText = "已提交"
		case 1:
			sText = "已审核"
		case 2:
			sText = "被退回"
		case 3:
			sText = "已完成"
		}
		latestInfo = fmt.Sprintf("您于%s申请提现%s,%s。",
			time.Unix(latestApplyInfo.CreateTime, 0).Format("2006-01-02 15:04:05"),
			format.FormatFloat(latestApplyInfo.Amount),
			sText)
	}
	return latestInfo
}

// 确认提现
func (this *memberService) ConfirmApplyCash(partnerId int, memberId int, infoId int, pass bool, remark string) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}
	return m.GetAccount().ConfirmApplyCash(infoId, pass, remark)
}

// 完成提现
func (this *memberService) FinishApplyCash(partnerId, memberId, id int, tradeNo string) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}
	return m.GetAccount().FinishApplyCash(id, tradeNo)
}
<commit_msg>freezes service<commit_after>/**
 * Copyright 2014 @ z3q.net.
 * name :
 * author : jarryliu
 * date : 2013-12-09 20:14
 * description :
 * history :
 */

package dps

import (
	"errors"
	"fmt"
	"go2o/src/core/domain/interface/member"
	"go2o/src/core/domain/interface/partner"
	"go2o/src/core/domain/interface/valueobject"
	"go2o/src/core/dto"
	"go2o/src/core/infrastructure/format"
	"go2o/src/core/query"
	"time"
)

type memberService struct {
	_memberRep member.IMemberRep
	_query     *query.MemberQuery
}

func NewMemberService(rep member.IMemberRep, q *query.MemberQuery) *memberService {
	return &memberService{
		_memberRep: rep,
		_query:     q,
	}
}

func (this *memberService) GetMember(id int) *member.ValueMember {
	v := this._memberRep.GetMember(id)
	if v != nil {
		nv := v.GetValue()
		return &nv
	}
	return nil
}

func (this *memberService) getMember(partnerId, memberId int) (member.IMember, error) {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return m, member.ErrNoSuchMember
	}
	if m.GetRelation().RegisterPartnerId != partnerId {
		return m, partner.ErrPartnerNotMatch
	}
	return m, nil
}

func (this *memberService) GetMemberIdByInvitationCode(code string) int {
	return this._memberRep.GetMemberIdByInvitationCode(code)
}

func (this *memberService) SaveMember(v *member.ValueMember) (int, error) {
	if v.Id > 0 {
		return this.updateMember(v)
	}
	return this.createMember(v)
}

func (this *memberService) updateMember(v *member.ValueMember) (int, error) {
	m := this._memberRep.GetMember(v.Id)
	if m == nil {
		return -1, member.ErrNoSuchMember
	}
	if err := m.SetValue(v); err != nil {
		return m.GetAggregateRootId(), err
	}
	return m.Save()
}

func (this *memberService) createMember(v *member.ValueMember) (int, error) {
	m := this._memberRep.CreateMember(v)
	return m.Save()
}

func (this *memberService) SaveRelation(memberId int, cardId string, invitationId, partnerId int) error {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return member.ErrNoSuchMember
	}

	rl := m.GetRelation()
	rl.RefereesId = invitationId
	rl.RegisterPartnerId = partnerId
	rl.CardId = cardId

	return m.SaveRelation(rl)
}

func (this *memberService) GetLevel(memberId int) *valueobject.MemberLevel {
	if m := this._memberRep.GetMember(memberId); m != nil {
		return m.GetLevel()
	}
	return nil
}

func (this *memberService) GetRelation(memberId int) *member.MemberRelation {
	return this._memberRep.GetRelation(memberId)
}

// 锁定/解锁会员
func (this *memberService) LockMember(partnerId, id int) (bool, error) {
	m := this._memberRep.GetMember(id)
	if m == nil {
		return false, member.ErrNoSuchMember
	}

	state := m.GetValue().State
	if state == 1 {
		return false, m.Lock()
	}
	return true, m.Unlock()
}

// 登陆
func (this *memberService) Login(partnerId int, usr, pwd string) (bool, *member.ValueMember, error) {
	val := this._memberRep.GetMemberValueByUsr(usr)
	if val == nil {
		val = this._memberRep.GetMemberValueByPhone(usr)
	}
	if val == nil {
		return false, nil, errors.New("会员不存在")
	}

	if val.Pwd != pwd {
		return false, nil, errors.New("会员用户或密码不正确")
	}

	if val.State == 0 {
		return false, nil, errors.New("会员已停用")
	}

	m := this._memberRep.GetMember(val.Id)
	rl := m.GetRelation()

	if partnerId != -1 && rl.RegisterPartnerId != partnerId {
		return false, nil, errors.New("无法登陆:NOT MATCH PARTNER!")
	}

	unix := time.Now().Unix()
	val.LastLoginTime = unix
	val.UpdateTime = unix

	m.SetValue(val)
	m.Save()

	return true, val, nil
}

func (this *memberService) CheckUsr(usr string, memberId int) error {
	if len(usr) < 6 {
		return member.ErrUserLength
	}
	var id int = this._memberRep.GetMemberIdByUser(usr)
	if id == 0 {
		return nil
	} else if memberId != 0 && id == memberId {
		return nil
	}

	return errors.New("用户名已被使用")
}

func (this *memberService) GetAccount(memberId int) *member.AccountValue {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	//m, _ := this._memberRep.GetMember(memberId)
	//m.AddExp(300)
	return m.GetAccount().GetValue()
}

func (this *memberService) GetBank(memberId int) *member.BankInfo {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	b := m.GetBank()
	return &b
}

func (this *memberService) SaveBankInfo(v *member.BankInfo) error {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: v.MemberId})
	return m.SaveBank(v)
}

// 获取返现记录
func (this *memberService) QueryIncomeLog(memberId, page, size int,
	where, orderBy string) (num int, rows []map[string]interface{}) {
	return this._query.QueryIncomeLog(memberId, page, size, where, orderBy)
}

// 查询分页订单
func (this *memberService) QueryPagerOrder(memberId, page, size int,
	where, orderBy string) (num int, rows []map[string]interface{}) {
	return this._query.QueryPagerOrder(memberId, page, size, where, orderBy)
}

/*********** 收货地址 ***********/
func (this *memberService) GetDeliverAddress(memberId int) []*member.DeliverAddress {
	return this._memberRep.GetDeliverAddress(memberId)
}

//获取配送地址
func (this *memberService) GetDeliverAddressById(memberId,
	deliverId int) *member.DeliverAddress {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	v := m.GetDeliver(deliverId).GetValue()
	return &v
}

//保存配送地址
func (this *memberService) SaveDeliverAddress(memberId int, e *member.DeliverAddress) (int, error) {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	var v member.IDeliver
	if e.Id > 0 {
		v = m.GetDeliver(e.Id)
		v.SetValue(e)
	} else {
		v = m.CreateDeliver(e)
	}
	return v.Save()
}

//删除配送地址
func (this *memberService) DeleteDeliverAddress(memberId int, deliverId int) error {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	return m.DeleteDeliver(deliverId)
}

func (this *memberService) ModifyPassword(memberId int, oldPwd, newPwd string) error {
	m := this._memberRep.GetMember(memberId)
	if m != nil {
		return m.ModifyPassword(newPwd, oldPwd)
	}
	return member.ErrNoSuchMember
}

func (this *memberService) ModifyTradePassword(memberId int, oldPwd, newPwd string) error {
	m := this._memberRep.GetMember(memberId)
	if m != nil {
		return m.ModifyTradePassword(newPwd, oldPwd)
	}
	return member.ErrNoSuchMember
}

//判断会员是否由指定会员邀请推荐的
func (this *memberService) IsInvitation(memberId int, invitationMemberId int) bool {
	m := this._memberRep.CreateMember(&member.ValueMember{Id: memberId})
	return m.Invitation().InvitationBy(invitationMemberId)
}

// 获取我邀请的会员及会员邀请的人数
func (this *memberService) GetMyInvitationMembers(memberId int) ([]*member.ValueMember, map[int]int) {
	iv := this._memberRep.CreateMember(&member.ValueMember{Id: memberId}).Invitation()
	return iv.GetMyInvitationMembers(), iv.GetSubInvitationNum()
}

// 获取会员最后更新时间
func (this *memberService) GetMemberLatestUpdateTime(memberId int) int64 {
	return this._memberRep.GetMemberLatestUpdateTime(memberId)
}

// 获取会员汇总信息
func (this *memberService) GetMemberSummary(memberId int) *dto.MemberSummary {
	var m member.IMember = this._memberRep.GetMember(memberId)
	if m != nil {
		mv := m.GetValue()
		acv := m.GetAccount().GetValue()
		lv := m.GetLevel()
		return &dto.MemberSummary{
			Id:             m.GetAggregateRootId(),
			Usr:            mv.Usr,
			Name:           mv.Name,
			Exp:            mv.Exp,
			Level:          mv.Level,
			LevelName:      lv.Name,
			Integral:       acv.Integral,
			Balance:        acv.Balance,
			PresentBalance: acv.PresentBalance,
			UpdateTime:     mv.UpdateTime,
		}
	}
	return nil
}

// 获取余额变动信息
func (this *memberService) GetBalanceInfoById(memberId, infoId int) *member.BalanceInfoValue {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return nil
	}
	return m.GetAccount().GetBalanceInfo(infoId)
}

// 充值
func (this *memberService) Charge(partnerId, memberId, chargeType int, title, tradeNo string, amount float32) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}
	return m.GetAccount().Charge(chargeType, title, tradeNo, amount)
}

// 提现
func (this *memberService) SubmitApplyCash(partnerId, memberId int, outTradePwd string,
	applyType int, applyAmount float32) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}

	tradePwd := m.GetValue().TradePwd
	if len(tradePwd) == 0 {
		return member.ErrNotSetTradePwd
	}

	if tradePwd != outTradePwd {
		return member.ErrIncorrectTradePwd
	}

	acc := m.GetAccount()
	var title string
	switch applyType {
	case member.TypeApplyCashToBank:
		title = "提现到银行卡"
	case member.TypeApplyCashToCharge:
		title = "充值账户"
	case member.TypeApplyCashToServiceProvider:
		title = "充值到第三方账户"
	}
	return acc.RequestApplyCash(applyType, title, applyAmount)
}

// 获取最近的提现
func (this *memberService) GetLatestApplyCash(memberId int) *member.BalanceInfoValue {
	return this._query.GetLatestBalanceInfoByKind(memberId, member.KindBalanceApplyCash)
}

// 获取最近的提现描述
func (this *memberService) GetLatestApplyCashText(memberId int) string {
	var latestInfo string
	latestApplyInfo := this.GetLatestApplyCash(memberId)
	if latestApplyInfo != nil {
		var sText string
		switch latestApplyInfo.State {
		case 0:
			sText = "已提交"
		case 1:
			sText = "已审核"
		case 2:
			sText = "被退回"
		case 3:
			sText = "已完成"
		}
		latestInfo = fmt.Sprintf("您于%s申请提现%s,%s。",
			time.Unix(latestApplyInfo.CreateTime, 0).Format("2006-01-02 15:04:05"),
			format.FormatFloat(latestApplyInfo.Amount),
			sText)
	}
	return latestInfo
}

// 确认提现
func (this *memberService) ConfirmApplyCash(partnerId int, memberId int, infoId int, pass bool, remark string) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}
	return m.GetAccount().ConfirmApplyCash(infoId, pass, remark)
}

// 完成提现
func (this *memberService) FinishApplyCash(partnerId, memberId, id int, tradeNo string) error {
	m, err := this.getMember(partnerId, memberId)
	if err != nil {
		return err
	}
	return m.GetAccount().FinishApplyCash(id, tradeNo)
}

// 冻结余额
func (this *memberService) Freezes(memberId int, amount float32) error {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return member.ErrNoSuchMember
	}
	return m.GetAccount().Freezes(amount)
}

// 解冻金额
func (this *memberService) Unfreezes(memberId int, amount float32) error {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return member.ErrNoSuchMember
	}
	return m.GetAccount().Unfreezes(amount)
}

// 冻结赠送金额
func (this *memberService) FreezesPresent(memberId int, amount float32) error {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return member.ErrNoSuchMember
	}
	return m.GetAccount().FreezesPresent(amount)
}

// 解冻赠送金额
func (this *memberService) UnfreezesPresent(memberId int, amount float32) error {
	m := this._memberRep.GetMember(memberId)
	if m == nil {
		return member.ErrNoSuchMember
	}
	return m.GetAccount().UnfreezesPresent(amount)
}
<|endoftext|>


<commit_before>package recurrent

import (
	"testing"
	"time"

	"github.com/efritz/glock"
	. "github.com/onsi/gomega"
)

type SchedulerSuite struct{}

func (s *SchedulerSuite) TestAutomaticPeriod(t *testing.T) {
	var (
		afterChan = make(chan time.Time)
		clock     = glock.NewMockClockWithAfterChan(afterChan)
		sync      = make(chan struct{})
		done      = make(chan struct{})
		attempts  = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newSchedulerWithClock(
		time.Second,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 25; i++ {
			afterChan <- time.Now()
			<-sync
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))
	Expect(clock.GetAfterArgs()[0]).To(Equal(time.Second))
}

func (s *SchedulerSuite) TestThrottledSchedule(t *testing.T) {
	var (
		afterChan  = make(chan time.Time)
		tickerChan = make(chan time.Time)
		clock      = glock.NewMockClockWithAfterChanAndTicker(afterChan, glock.NewMockTicker(tickerChan))
		sync       = make(chan struct{})
		done       = make(chan struct{})
		attempts   = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newThrottledSchedulerWithClock(
		time.Second,
		time.Millisecond,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 25; i++ {
			afterChan <- time.Now()
			<-sync
		}
	}()

	go func() {
		defer close(tickerChan)

		for {
			select {
			case <-done:
				return
			case tickerChan <- time.Now():
			}
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))

	tickerArgs := clock.GetTickerArgs()
	Expect(tickerArgs).To(HaveLen(1))
	Expect(tickerArgs[0]).To(Equal(time.Millisecond))
}

func (s *SchedulerSuite) TestExplicitFire(t *testing.T) {
	var (
		afterChan = make(chan time.Time)
		clock     = glock.NewMockClockWithAfterChan(afterChan)
		sync      = make(chan struct{})
		done      = make(chan struct{})
		attempts  = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newSchedulerWithClock(
		time.Second,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 25; i++ {
			scheduler.Signal()
			<-sync
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))
}

func (s *SchedulerSuite) TestThrottledExplicitFire(t *testing.T) {
	var (
		afterChan  = make(chan time.Time)
		tickerChan = make(chan time.Time)
		clock      = glock.NewMockClockWithAfterChanAndTicker(afterChan, glock.NewMockTicker(tickerChan))
		sync       = make(chan struct{})
		done       = make(chan struct{})
		attempts   = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newThrottledSchedulerWithClock(
		time.Second,
		time.Millisecond,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)
		defer close(tickerChan)

		for i := 0; i < 100; i++ {
			scheduler.Signal()

			if i%4 == 0 {
				tickerChan <- time.Now()
				<-sync
			}
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))
}
<commit_msg>Fix (new) test send on close.<commit_after>package recurrent

import (
	"testing"
	"time"

	"github.com/efritz/glock"
	. "github.com/onsi/gomega"
)

type SchedulerSuite struct{}

func (s *SchedulerSuite) TestAutomaticPeriod(t *testing.T) {
	var (
		afterChan = make(chan time.Time)
		clock     = glock.NewMockClockWithAfterChan(afterChan)
		sync      = make(chan struct{})
		done      = make(chan struct{})
		attempts  = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newSchedulerWithClock(
		time.Second,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 25; i++ {
			afterChan <- time.Now()
			<-sync
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))
	Expect(clock.GetAfterArgs()[0]).To(Equal(time.Second))
}

func (s *SchedulerSuite) TestThrottledSchedule(t *testing.T) {
	var (
		afterChan  = make(chan time.Time)
		tickerChan = make(chan time.Time)
		clock      = glock.NewMockClockWithAfterChanAndTicker(afterChan, glock.NewMockTicker(tickerChan))
		sync       = make(chan struct{})
		done       = make(chan struct{})
		attempts   = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newThrottledSchedulerWithClock(
		time.Second,
		time.Millisecond,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 25; i++ {
			afterChan <- time.Now()
			<-sync
		}
	}()

	go func() {
		defer close(tickerChan)

		for {
			select {
			case <-done:
				return
			case tickerChan <- time.Now():
			}
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))

	tickerArgs := clock.GetTickerArgs()
	Expect(tickerArgs).To(HaveLen(1))
	Expect(tickerArgs[0]).To(Equal(time.Millisecond))
}

func (s *SchedulerSuite) TestExplicitFire(t *testing.T) {
	var (
		afterChan = make(chan time.Time)
		clock     = glock.NewMockClockWithAfterChan(afterChan)
		sync      = make(chan struct{})
		done      = make(chan struct{})
		attempts  = 0
	)

	defer close(sync)
	defer close(afterChan)

	scheduler := newSchedulerWithClock(
		time.Second,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 25; i++ {
			scheduler.Signal()
			<-sync
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))
}

func (s *SchedulerSuite) TestThrottledExplicitFire(t *testing.T) {
	var (
		afterChan  = make(chan time.Time)
		tickerChan = make(chan time.Time)
		clock      = glock.NewMockClockWithAfterChanAndTicker(afterChan, glock.NewMockTicker(tickerChan))
		sync       = make(chan struct{})
		done       = make(chan struct{})
		attempts   = 0
	)

	defer close(sync)
	defer close(afterChan)
	defer close(tickerChan)

	scheduler := newThrottledSchedulerWithClock(
		time.Second,
		time.Millisecond,
		func() {
			attempts++
			sync <- struct{}{}
		},
		clock,
	)

	go func() {
		defer close(done)

		for i := 0; i < 100; i++ {
			scheduler.Signal()

			if i%4 == 0 {
				tickerChan <- time.Now()
				<-sync
			}
		}
	}()

	scheduler.Start()
	<-done
	scheduler.Stop()
	Expect(attempts).To(Equal(25))
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes 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 mungers

import (
	"fmt"
	"regexp"
	"time"

	"k8s.io/test-infra/mungegithub/features"
	"k8s.io/test-infra/mungegithub/github"
	"k8s.io/test-infra/mungegithub/mungers/mungerutil"

	githubapi "github.com/google/go-github/github"
	"github.com/spf13/cobra"
)

const (
	day            = time.Hour * 24
	keepOpenLabel  = "keep-open"
	kindFlakeLabel = "kind/flake"
	stalePeriod    = 90 * day // Close the PR/Issue if no human interaction for `stalePeriod`
	startWarning   = 60 * day
	remindWarning  = 30 * day
	closingComment = `This %s hasn't been active in %s. Closing this %s. Please reopen if you would like to work towards merging this change, if/when the %s is ready for the next round of review.

%s
You can add 'keep-open' label to prevent this from happening again, or add a comment to keep it open another 90 days`
	warningComment = `This %s hasn't been active in %s. It will be closed in %s (%s).

%s
You can add 'keep-open' label to prevent this from happening, or add a comment to keep it open another 90 days`
)

var (
	closingCommentRE = regexp.MustCompile(`This \w hasn't been active in \d+ days?\..*label to prevent this from happening again`)
	warningCommentRE = regexp.MustCompile(`This \w hasn't been active in \d+ days?\..*be closed in \d+ days?`)
)

// CloseStale will ask the Bot to close any PR/Issue that didn't
// have any human interactions in `stalePeriod` duration.
//
// This is done by checking both review and issue comments, and by
// ignoring comments done with a bot name. We also consider re-open on the PR/Issue.
type CloseStale struct{}

func init() {
	s := CloseStale{}
	RegisterMungerOrDie(s)
	RegisterStaleComments(s)
}

// Name is the name usable in --pr-mungers
func (CloseStale) Name() string { return "close-stale" }

// RequiredFeatures is a slice of 'features' that must be provided
func (CloseStale) RequiredFeatures() []string { return []string{} }

// Initialize will initialize the munger
func (CloseStale) Initialize(config *github.Config, features *features.Features) error {
	return nil
}

// EachLoop is called at the start of every munge loop
func (CloseStale) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (CloseStale) AddFlags(cmd *cobra.Command, config *github.Config) {}

func findLastHumanPullRequestUpdate(obj *github.MungeObject) (*time.Time, bool) {
	pr, ok := obj.GetPR()
	if !ok {
		return nil, ok
	}

	comments, ok := obj.ListReviewComments()
	if !ok {
		return nil, ok
	}

	lastHuman := pr.CreatedAt
	for i := range comments {
		comment := comments[i]
		if comment.User == nil || comment.User.Login == nil || comment.CreatedAt == nil || comment.Body == nil {
			continue
		}
		if *comment.User.Login == botName || *comment.User.Login == jenkinsBotName {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, true
}

func findLastHumanIssueUpdate(obj *github.MungeObject) (*time.Time, bool) {
	lastHuman := obj.Issue.CreatedAt

	comments, ok := obj.ListComments()
	if !ok {
		return nil, ok
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if mergeBotComment(comment) || jenkinsBotComment(comment) {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, true
}

func findLastInterestingEventUpdate(obj *github.MungeObject) (*time.Time, bool) {
	lastInteresting := obj.Issue.CreatedAt

	events, ok := obj.GetEvents()
	if !ok {
		return nil, ok
	}

	for i := range events {
		event := events[i]
		if event.Event == nil || *event.Event != "reopened" {
			continue
		}

		if lastInteresting.Before(*event.CreatedAt) {
			lastInteresting = event.CreatedAt
		}
	}

	return lastInteresting, true
}

func findLastModificationTime(obj *github.MungeObject) (*time.Time, bool) {
	lastHumanIssue, ok := findLastHumanIssueUpdate(obj)
	if !ok {
		return nil, ok
	}

	lastInterestingEvent, ok := findLastInterestingEventUpdate(obj)
	if !ok {
		return nil, ok
	}

	var lastModif *time.Time
	lastModif = lastHumanIssue

	if lastInterestingEvent.After(*lastModif) {
		lastModif = lastInterestingEvent
	}

	if obj.IsPR() {
		lastHumanPR, ok := findLastHumanPullRequestUpdate(obj)
		if !ok {
			return lastModif, true
		}

		if lastHumanPR.After(*lastModif) {
			lastModif = lastHumanPR
		}
	}

	return lastModif, true
}

// Find the last warning comment that the bot has posted.
// It can return an empty comment if it fails to find one, even if there are no errors.
func findLatestWarningComment(obj *github.MungeObject) (*githubapi.IssueComment, bool) {
	var lastFoundComment *githubapi.IssueComment

	comments, ok := obj.ListComments()
	if !ok {
		return nil, ok
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if !mergeBotComment(comment) {
			continue
		}

		if !warningCommentRE.MatchString(*comment.Body) {
			continue
		}

		if lastFoundComment == nil || lastFoundComment.CreatedAt.Before(*comment.UpdatedAt) {
			if lastFoundComment != nil {
				obj.DeleteComment(lastFoundComment)
			}
			lastFoundComment = comment
		}
	}

	return lastFoundComment, true
}

func durationToDays(duration time.Duration) string {
	days := duration / day
	dayString := "days"
	if days == 1 || days == -1 {
		dayString = "day"
	}
	return fmt.Sprintf("%d %s", days, dayString)
}

func closeObj(obj *github.MungeObject, inactiveFor time.Duration) {
	mention := mungerutil.GetIssueUsers(obj.Issue).AllUsers().Mention().Join()
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	comment, ok := findLatestWarningComment(obj)
	if !ok {
		return
	}
	if comment != nil {
		obj.DeleteComment(comment)
	}

	var objType string

	if obj.IsPR() {
		objType = "PR"
	} else {
		objType = "Issue"
	}

	obj.WriteComment(fmt.Sprintf(closingComment, objType, durationToDays(inactiveFor), objType, objType, mention))

	if obj.IsPR() {
		obj.ClosePR()
	} else {
		obj.CloseIssuef("")
	}
}

func postWarningComment(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	mention := mungerutil.GetIssueUsers(obj.Issue).AllUsers().Mention().Join()
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	closeDate := time.Now().Add(closeIn).Format("Jan 2, 2006")

	var objType string

	if obj.IsPR() {
		objType = "PR"
	} else {
		objType = "Issue"
	}

	obj.WriteComment(fmt.Sprintf(
		warningComment,
		objType,
		durationToDays(inactiveFor),
		durationToDays(closeIn),
		closeDate,
		mention,
	))
}

func checkAndWarn(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	if closeIn < day {
		// We are going to close the PR/Issue in less than a day. Too late to warn
		return
	}
	comment, ok := findLatestWarningComment(obj)
	if !ok {
		return
	}
	if comment == nil {
		// We don't already have the comment. Post it
		postWarningComment(obj, inactiveFor, closeIn)
	} else if time.Since(*comment.UpdatedAt) > remindWarning {
		// It's time to warn again
		obj.DeleteComment(comment)
		postWarningComment(obj, inactiveFor, closeIn)
	} else {
		// We already have a warning, and it's not expired. Do nothing
	}
}

// Munge is the workhorse that will actually close the PRs/Issues
func (CloseStale) Munge(obj *github.MungeObject) {
	if !obj.IsPR() && !obj.HasLabel(kindFlakeLabel) {
		return
	}

	if obj.HasLabel(keepOpenLabel) {
		return
	}

	lastModif, ok := findLastModificationTime(obj)
	if !ok {
		return
	}

	closeIn := -time.Since(lastModif.Add(stalePeriod))
	inactiveFor := time.Since(*lastModif)
	if closeIn <= 0 {
		closeObj(obj, inactiveFor)
	} else if closeIn <= startWarning {
		checkAndWarn(obj, inactiveFor, closeIn)
	} else {
		// PR/Issue is active. Remove previous potential warning
		comment, ok := findLatestWarningComment(obj)
		if comment != nil && ok {
			obj.DeleteComment(comment)
		}
	}
}

func (CloseStale) isStaleComment(obj *github.MungeObject, comment *githubapi.IssueComment) bool {
	if !mergeBotComment(comment) {
		return false
	}

	if !closingCommentRE.MatchString(*comment.Body) {
		return false
	}

	return true
}

// StaleComments returns a slice of stale comments
func (s CloseStale) StaleComments(obj *github.MungeObject, comments []*githubapi.IssueComment) []*githubapi.IssueComment {
	return forEachCommentTest(obj, comments, s.isStaleComment)
}
<commit_msg>Fix stale nag message.<commit_after>/*
Copyright 2016 The Kubernetes 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 mungers

import (
	"fmt"
	"regexp"
	"time"

	"k8s.io/test-infra/mungegithub/features"
	"k8s.io/test-infra/mungegithub/github"
	"k8s.io/test-infra/mungegithub/mungers/mungerutil"

	githubapi "github.com/google/go-github/github"
	"github.com/spf13/cobra"
)

const (
	day            = time.Hour * 24
	keepOpenLabel  = "keep-open"
	kindFlakeLabel = "kind/flake"
	stalePeriod    = 90 * day // Close the PR/Issue if no human interaction for `stalePeriod`
	startWarning   = 60 * day
	remindWarning  = 30 * day
	closingComment = `This %s hasn't been active in %s. Closing this %s. Please reopen if you would like to work towards merging this change, if/when the %s is ready for the next round of review.

%s
You can add 'keep-open' label to prevent this from happening again, or add a comment to keep it open another 90 days`
	warningComment = `This %s hasn't been active in %s. It will be closed in %s (%s).

%s
You can add 'keep-open' label to prevent this from happening, or add a comment to keep it open another 90 days`
)

var (
	closingCommentRE = regexp.MustCompile(`This \w+ hasn't been active in \d+ days?\..*label to prevent this from happening again`)
	warningCommentRE = regexp.MustCompile(`This \w+ hasn't been active in \d+ days?\..*be closed in \d+ days?`)
)

// CloseStale will ask the Bot to close any PR/Issue that didn't
// have any human interactions in `stalePeriod` duration.
//
// This is done by checking both review and issue comments, and by
// ignoring comments done with a bot name. We also consider re-open on the PR/Issue.
type CloseStale struct{}

func init() {
	s := CloseStale{}
	RegisterMungerOrDie(s)
	RegisterStaleComments(s)
}

// Name is the name usable in --pr-mungers
func (CloseStale) Name() string { return "close-stale" }

// RequiredFeatures is a slice of 'features' that must be provided
func (CloseStale) RequiredFeatures() []string { return []string{} }

// Initialize will initialize the munger
func (CloseStale) Initialize(config *github.Config, features *features.Features) error {
	return nil
}

// EachLoop is called at the start of every munge loop
func (CloseStale) EachLoop() error { return nil }

// AddFlags will add any request flags to the cobra `cmd`
func (CloseStale) AddFlags(cmd *cobra.Command, config *github.Config) {}

func findLastHumanPullRequestUpdate(obj *github.MungeObject) (*time.Time, bool) {
	pr, ok := obj.GetPR()
	if !ok {
		return nil, ok
	}

	comments, ok := obj.ListReviewComments()
	if !ok {
		return nil, ok
	}

	lastHuman := pr.CreatedAt
	for i := range comments {
		comment := comments[i]
		if comment.User == nil || comment.User.Login == nil || comment.CreatedAt == nil || comment.Body == nil {
			continue
		}
		if *comment.User.Login == botName || *comment.User.Login == jenkinsBotName {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, true
}

func findLastHumanIssueUpdate(obj *github.MungeObject) (*time.Time, bool) {
	lastHuman := obj.Issue.CreatedAt

	comments, ok := obj.ListComments()
	if !ok {
		return nil, ok
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if mergeBotComment(comment) || jenkinsBotComment(comment) {
			continue
		}
		if lastHuman.Before(*comment.UpdatedAt) {
			lastHuman = comment.UpdatedAt
		}
	}

	return lastHuman, true
}

func findLastInterestingEventUpdate(obj *github.MungeObject) (*time.Time, bool) {
	lastInteresting := obj.Issue.CreatedAt

	events, ok := obj.GetEvents()
	if !ok {
		return nil, ok
	}

	for i := range events {
		event := events[i]
		if event.Event == nil || *event.Event != "reopened" {
			continue
		}

		if lastInteresting.Before(*event.CreatedAt) {
			lastInteresting = event.CreatedAt
		}
	}

	return lastInteresting, true
}

func findLastModificationTime(obj *github.MungeObject) (*time.Time, bool) {
	lastHumanIssue, ok := findLastHumanIssueUpdate(obj)
	if !ok {
		return nil, ok
	}

	lastInterestingEvent, ok := findLastInterestingEventUpdate(obj)
	if !ok {
		return nil, ok
	}

	var lastModif *time.Time
	lastModif = lastHumanIssue

	if lastInterestingEvent.After(*lastModif) {
		lastModif = lastInterestingEvent
	}

	if obj.IsPR() {
		lastHumanPR, ok := findLastHumanPullRequestUpdate(obj)
		if !ok {
			return lastModif, true
		}

		if lastHumanPR.After(*lastModif) {
			lastModif = lastHumanPR
		}
	}

	return lastModif, true
}

// Find the last warning comment that the bot has posted.
// It can return an empty comment if it fails to find one, even if there are no errors.
func findLatestWarningComment(obj *github.MungeObject) (*githubapi.IssueComment, bool) {
	var lastFoundComment *githubapi.IssueComment

	comments, ok := obj.ListComments()
	if !ok {
		return nil, ok
	}

	for i := range comments {
		comment := comments[i]
		if !validComment(comment) {
			continue
		}
		if !mergeBotComment(comment) {
			continue
		}

		if !warningCommentRE.MatchString(*comment.Body) {
			continue
		}

		if lastFoundComment == nil || lastFoundComment.CreatedAt.Before(*comment.UpdatedAt) {
			if lastFoundComment != nil {
				obj.DeleteComment(lastFoundComment)
			}
			lastFoundComment = comment
		}
	}

	return lastFoundComment, true
}

func durationToDays(duration time.Duration) string {
	days := duration / day
	dayString := "days"
	if days == 1 || days == -1 {
		dayString = "day"
	}
	return fmt.Sprintf("%d %s", days, dayString)
}

func closeObj(obj *github.MungeObject, inactiveFor time.Duration) {
	mention := mungerutil.GetIssueUsers(obj.Issue).AllUsers().Mention().Join()
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	comment, ok := findLatestWarningComment(obj)
	if !ok {
		return
	}
	if comment != nil {
		obj.DeleteComment(comment)
	}

	var objType string

	if obj.IsPR() {
		objType = "PR"
	} else {
		objType = "Issue"
	}

	obj.WriteComment(fmt.Sprintf(closingComment, objType, durationToDays(inactiveFor), objType, objType, mention))

	if obj.IsPR() {
		obj.ClosePR()
	} else {
		obj.CloseIssuef("")
	}
}

func postWarningComment(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	mention := mungerutil.GetIssueUsers(obj.Issue).AllUsers().Mention().Join()
	if mention != "" {
		mention = "cc " + mention + "\n"
	}

	closeDate := time.Now().Add(closeIn).Format("Jan 2, 2006")

	var objType string

	if obj.IsPR() {
		objType = "PR"
	} else {
		objType = "Issue"
	}

	obj.WriteComment(fmt.Sprintf(
		warningComment,
		objType,
		durationToDays(inactiveFor),
		durationToDays(closeIn),
		closeDate,
		mention,
	))
}

func checkAndWarn(obj *github.MungeObject, inactiveFor time.Duration, closeIn time.Duration) {
	if closeIn < day {
		// We are going to close the PR/Issue in less than a day. Too late to warn
		return
	}
	comment, ok := findLatestWarningComment(obj)
	if !ok {
		return
	}
	if comment == nil {
		// We don't already have the comment. Post it
		postWarningComment(obj, inactiveFor, closeIn)
	} else if time.Since(*comment.UpdatedAt) > remindWarning {
		// It's time to warn again
		obj.DeleteComment(comment)
		postWarningComment(obj, inactiveFor, closeIn)
	} else {
		// We already have a warning, and it's not expired. Do nothing
	}
}

// Munge is the workhorse that will actually close the PRs/Issues
func (CloseStale) Munge(obj *github.MungeObject) {
	if !obj.IsPR() && !obj.HasLabel(kindFlakeLabel) {
		return
	}

	if obj.HasLabel(keepOpenLabel) {
		return
	}

	lastModif, ok := findLastModificationTime(obj)
	if !ok {
		return
	}

	closeIn := -time.Since(lastModif.Add(stalePeriod))
	inactiveFor := time.Since(*lastModif)
	if closeIn <= 0 {
		closeObj(obj, inactiveFor)
	} else if closeIn <= startWarning {
		checkAndWarn(obj, inactiveFor, closeIn)
	} else {
		// PR/Issue is active. Remove previous potential warning
		comment, ok := findLatestWarningComment(obj)
		if comment != nil && ok {
			obj.DeleteComment(comment)
		}
	}
}

func (CloseStale) isStaleComment(obj *github.MungeObject, comment *githubapi.IssueComment) bool {
	if !mergeBotComment(comment) {
		return false
	}

	if !closingCommentRE.MatchString(*comment.Body) {
		return false
	}

	return true
}

// StaleComments returns a slice of stale comments
func (s CloseStale) StaleComments(obj *github.MungeObject, comments []*githubapi.IssueComment) []*githubapi.IssueComment {
	return forEachCommentTest(obj, comments, s.isStaleComment)
}
<|endoftext|>


<commit_before>// +build e2e

/*
Copyright 2019 The Knative 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 e2e

import (
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"testing"

	mf "github.com/jcrossley3/manifestival"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"knative.dev/pkg/test/logstream"
	"knative.dev/serving-operator/pkg/apis/serving/v1alpha1"
	"knative.dev/serving-operator/test"
	"knative.dev/serving-operator/test/resources"
)

// TestKnativeServingDeployment verifies the KnativeServing creation, deployment recreation, and KnativeServing deletion.
func TestKnativeServingDeployment(t *testing.T) {
	cancel := logstream.Start(t)
	defer cancel()
	clients := Setup(t)

	names := test.ResourceNames{
		KnativeServing: test.ServingOperatorName,
		Namespace:      test.ServingOperatorNamespace,
	}

	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	defer test.TearDown(clients, names)

	// Create a KnativeServing
	if _, err := resources.CreateKnativeServing(clients.KnativeServing(), names); err != nil {
		t.Fatalf("KnativeService %q failed to create: %v", names.KnativeServing, err)
	}

	// Test if KnativeServing can reach the READY status
	t.Run("create", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
	})

	t.Run("configure", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingConfigure(t, clients, names)
	})

	// Delete the deployments one by one to see if they will be recreated.
	t.Run("restore", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		deploymentRecreation(t, clients, names)
	})

	// Delete the KnativeServing to see if all the deployments will be removed as well
	t.Run("delete", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingDeletion(t, clients, names)
		verifyClusterResourceDeletion(t, clients)
	})
}

// knativeServingVerify verifies if the KnativeServing can reach the READY status.
func knativeServingVerify(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), names.KnativeServing,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to get to the READY status: %v", names.KnativeServing, err)
	}

}

// knativeServingConfigure verifies that KnativeServing config is set properly
func knativeServingConfigure(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	// We'll arbitrarily choose the logging config
	configKey := "logging"
	configMapName := fmt.Sprintf("%s/config-%s", names.Namespace, configKey)
	// Get the existing KS without any spec
	ks, err := clients.KnativeServing().Get(names.KnativeServing, metav1.GetOptions{})
	// Add config to its spec
	ks.Spec = v1alpha1.KnativeServingSpec{
		Config: map[string]map[string]string{
			configKey: map[string]string{
				"loglevel.controller": "debug",
			},
		},
	}
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verifty the relevant configmap has been updated
	err = resources.WaitForConfigMap(configMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		return m["loglevel.controller"] == "debug"
	})
	if err != nil {
		t.Fatal("The operator failed to update the configmap")
	}
	// Now remove the config from the spec and update
	ks.Spec = v1alpha1.KnativeServingSpec{}
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// And verify the configmap entry is gone
	err = resources.WaitForConfigMap(configMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, exists := m["loglevel.controller"]
		return !exists
	})
	if err != nil {
		t.Fatal("The operator failed to revert the configmap")
	}
}

// deploymentRecreation verify whether all the deployments for knative serving are able to recreate, when they are deleted.
func deploymentRecreation(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{})
	if err != nil {
		t.Fatalf("Failed to get any deployment under the namespace %q: %v",
			test.ServingOperatorNamespace, err)
	}
	if len(dpList.Items) == 0 {
		t.Fatalf("No deployment under the namespace %q was found",
			test.ServingOperatorNamespace)
	}
	// Delete the first deployment and verify the operator recreates it
	deployment := dpList.Items[0]
	if err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Delete(deployment.Name,
		&metav1.DeleteOptions{}); err != nil {
		t.Fatalf("Failed to delete deployment %s/%s: %v", deployment.Namespace, deployment.Name, err)
	}

	waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
		dep, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{})
		if err != nil {
			// If the deployment is not found, we continue to wait for the availability.
			if apierrs.IsNotFound(err) {
				return false, nil
			}
			return false, err
		}
		return resources.IsDeploymentAvailable(dep)
	})

	if waitErr != nil {
		t.Fatalf("The deployment %s/%s failed to reach the desired state: %v", deployment.Namespace, deployment.Name, err)
	}

	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), test.ServingOperatorName,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.ServingOperatorName, err)
	}
	t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name)
}

// knativeServingDeletion deletes tha KnativeServing to see if all the deployments will be removed.
func knativeServingDeletion(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if err := clients.KnativeServing().Delete(names.KnativeServing, &metav1.DeleteOptions{}); err != nil {
		t.Fatalf("KnativeService %q failed to delete: %v", names.KnativeServing, err)
	}

	dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{})
	if err != nil {
		t.Fatalf("Error getting any deployment under the namespace %q: %v", names.Namespace, err)
	}

	for _, deployment := range dpList.Items {
		waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
			if _, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{}); err != nil {
				if apierrs.IsNotFound(err) {
					return true, nil
				}
				return false, err
			}
			return false, nil
		})

		if waitErr != nil {
			t.Fatalf("The deployment %s/%s failed to be deleted: %v", deployment.Namespace, deployment.Name, waitErr)
		}
		t.Logf("The deployment %s/%s has been deleted.", deployment.Namespace, deployment.Name)
	}
}

func verifyClusterResourceDeletion(t *testing.T, clients *test.Clients) {
	_, b, _, _ := runtime.Caller(0)
	m, err := mf.NewManifest(filepath.Join((filepath.Dir(b)+"/.."), "config/"), false, clients.Config)
	if err != nil {
		t.Fatal("Failed to load manifest", err)
	}
	if err := verifyNoKnativeServings(clients); err != nil {
		t.Fatal(err)
	}
	for _, u := range m.Resources {
		if u.GetNamespace() == "" && u.GetKind() != "Namespace" {
			waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
				gvrs, _ := meta.UnsafeGuessKindToResource(u.GroupVersionKind())
				if _, err := clients.Dynamic.Resource(gvrs).Get(u.GetName(), metav1.GetOptions{}); apierrs.IsNotFound(err) {
					return true, nil
				} else {
					return false, err
				}
			})

			if waitErr != nil {
				t.Fatalf("The %s %s failed to be deleted: %v", u.GetKind(), u.GetName(), waitErr)
			}
			t.Logf("The %s %s has been deleted.", u.GetKind(), u.GetName())
		}
	}
}

func verifyNoKnativeServings(clients *test.Clients) error {
	servings, err := clients.KnativeServingAll().List(metav1.ListOptions{})
	if err != nil {
		return err
	}
	if len(servings.Items) > 0 {
		return errors.New("Unable to verify cluster-scoped resources are deleted if any KnativeServing exists")
	}
	return nil
}
<commit_msg>golang format tools (#167)<commit_after>// +build e2e

/*
Copyright 2019 The Knative 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 e2e

import (
	"errors"
	"fmt"
	"path/filepath"
	"runtime"
	"testing"

	mf "github.com/jcrossley3/manifestival"
	apierrs "k8s.io/apimachinery/pkg/api/errors"
	"k8s.io/apimachinery/pkg/api/meta"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
	"k8s.io/apimachinery/pkg/util/wait"
	"knative.dev/pkg/test/logstream"
	"knative.dev/serving-operator/pkg/apis/serving/v1alpha1"
	"knative.dev/serving-operator/test"
	"knative.dev/serving-operator/test/resources"
)

// TestKnativeServingDeployment verifies the KnativeServing creation, deployment recreation, and KnativeServing deletion.
func TestKnativeServingDeployment(t *testing.T) {
	cancel := logstream.Start(t)
	defer cancel()
	clients := Setup(t)

	names := test.ResourceNames{
		KnativeServing: test.ServingOperatorName,
		Namespace:      test.ServingOperatorNamespace,
	}

	test.CleanupOnInterrupt(func() { test.TearDown(clients, names) })
	defer test.TearDown(clients, names)

	// Create a KnativeServing
	if _, err := resources.CreateKnativeServing(clients.KnativeServing(), names); err != nil {
		t.Fatalf("KnativeService %q failed to create: %v", names.KnativeServing, err)
	}

	// Test if KnativeServing can reach the READY status
	t.Run("create", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
	})

	t.Run("configure", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingConfigure(t, clients, names)
	})

	// Delete the deployments one by one to see if they will be recreated.
	t.Run("restore", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		deploymentRecreation(t, clients, names)
	})

	// Delete the KnativeServing to see if all the deployments will be removed as well
	t.Run("delete", func(t *testing.T) {
		knativeServingVerify(t, clients, names)
		knativeServingDeletion(t, clients, names)
		verifyClusterResourceDeletion(t, clients)
	})
}

// knativeServingVerify verifies if the KnativeServing can reach the READY status.
func knativeServingVerify(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), names.KnativeServing,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to get to the READY status: %v", names.KnativeServing, err)
	}

}

// knativeServingConfigure verifies that KnativeServing config is set properly
func knativeServingConfigure(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	// We'll arbitrarily choose the logging config
	configKey := "logging"
	configMapName := fmt.Sprintf("%s/config-%s", names.Namespace, configKey)
	// Get the existing KS without any spec
	ks, err := clients.KnativeServing().Get(names.KnativeServing, metav1.GetOptions{})
	// Add config to its spec
	ks.Spec = v1alpha1.KnativeServingSpec{
		Config: map[string]map[string]string{
			configKey: {
				"loglevel.controller": "debug",
			},
		},
	}
	// Update it
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// Verifty the relevant configmap has been updated
	err = resources.WaitForConfigMap(configMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		return m["loglevel.controller"] == "debug"
	})
	if err != nil {
		t.Fatal("The operator failed to update the configmap")
	}
	// Now remove the config from the spec and update
	ks.Spec = v1alpha1.KnativeServingSpec{}
	if ks, err = clients.KnativeServing().Update(ks); err != nil {
		t.Fatalf("KnativeServing %q failed to update: %v", names.KnativeServing, err)
	}
	// And verify the configmap entry is gone
	err = resources.WaitForConfigMap(configMapName, clients.KubeClient.Kube, func(m map[string]string) bool {
		_, exists := m["loglevel.controller"]
		return !exists
	})
	if err != nil {
		t.Fatal("The operator failed to revert the configmap")
	}
}

// deploymentRecreation verify whether all the deployments for knative serving are able to recreate, when they are deleted.
func deploymentRecreation(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{})
	if err != nil {
		t.Fatalf("Failed to get any deployment under the namespace %q: %v",
			test.ServingOperatorNamespace, err)
	}
	if len(dpList.Items) == 0 {
		t.Fatalf("No deployment under the namespace %q was found",
			test.ServingOperatorNamespace)
	}
	// Delete the first deployment and verify the operator recreates it
	deployment := dpList.Items[0]
	if err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Delete(deployment.Name,
		&metav1.DeleteOptions{}); err != nil {
		t.Fatalf("Failed to delete deployment %s/%s: %v", deployment.Namespace, deployment.Name, err)
	}

	waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
		dep, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{})
		if err != nil {
			// If the deployment is not found, we continue to wait for the availability.
			if apierrs.IsNotFound(err) {
				return false, nil
			}
			return false, err
		}
		return resources.IsDeploymentAvailable(dep)
	})

	if waitErr != nil {
		t.Fatalf("The deployment %s/%s failed to reach the desired state: %v", deployment.Namespace, deployment.Name, err)
	}

	if _, err := resources.WaitForKnativeServingState(clients.KnativeServing(), test.ServingOperatorName,
		resources.IsKnativeServingReady); err != nil {
		t.Fatalf("KnativeService %q failed to reach the desired state: %v", test.ServingOperatorName, err)
	}
	t.Logf("The deployment %s/%s reached the desired state.", deployment.Namespace, deployment.Name)
}

// knativeServingDeletion deletes tha KnativeServing to see if all the deployments will be removed.
func knativeServingDeletion(t *testing.T, clients *test.Clients, names test.ResourceNames) {
	if err := clients.KnativeServing().Delete(names.KnativeServing, &metav1.DeleteOptions{}); err != nil {
		t.Fatalf("KnativeService %q failed to delete: %v", names.KnativeServing, err)
	}

	dpList, err := clients.KubeClient.Kube.AppsV1().Deployments(names.Namespace).List(metav1.ListOptions{})
	if err != nil {
		t.Fatalf("Error getting any deployment under the namespace %q: %v", names.Namespace, err)
	}

	for _, deployment := range dpList.Items {
		waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
			if _, err := clients.KubeClient.Kube.AppsV1().Deployments(deployment.Namespace).Get(deployment.Name, metav1.GetOptions{}); err != nil {
				if apierrs.IsNotFound(err) {
					return true, nil
				}
				return false, err
			}
			return false, nil
		})

		if waitErr != nil {
			t.Fatalf("The deployment %s/%s failed to be deleted: %v", deployment.Namespace, deployment.Name, waitErr)
		}
		t.Logf("The deployment %s/%s has been deleted.", deployment.Namespace, deployment.Name)
	}
}

func verifyClusterResourceDeletion(t *testing.T, clients *test.Clients) {
	_, b, _, _ := runtime.Caller(0)
	m, err := mf.NewManifest(filepath.Join((filepath.Dir(b)+"/.."), "config/"), false, clients.Config)
	if err != nil {
		t.Fatal("Failed to load manifest", err)
	}
	if err := verifyNoKnativeServings(clients); err != nil {
		t.Fatal(err)
	}
	for _, u := range m.Resources {
		if u.GetNamespace() == "" && u.GetKind() != "Namespace" {
			waitErr := wait.PollImmediate(resources.Interval, resources.Timeout, func() (bool, error) {
				gvrs, _ := meta.UnsafeGuessKindToResource(u.GroupVersionKind())
				if _, err := clients.Dynamic.Resource(gvrs).Get(u.GetName(), metav1.GetOptions{}); apierrs.IsNotFound(err) {
					return true, nil
				} else {
					return false, err
				}
			})

			if waitErr != nil {
				t.Fatalf("The %s %s failed to be deleted: %v", u.GetKind(), u.GetName(), waitErr)
			}
			t.Logf("The %s %s has been deleted.", u.GetKind(), u.GetName())
		}
	}
}

func verifyNoKnativeServings(clients *test.Clients) error {
	servings, err := clients.KnativeServingAll().List(metav1.ListOptions{})
	if err != nil {
		return err
	}
	if len(servings.Items) > 0 {
		return errors.New("Unable to verify cluster-scoped resources are deleted if any KnativeServing exists")
	}
	return nil
}
<|endoftext|>


<commit_before>// +build lambdabinary

package sparta

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"sync"

	awsLambdaGo "github.com/aws/aws-lambda-go/lambda"
	awsLambdaContext "github.com/aws/aws-lambda-go/lambdacontext"
	cloudformationResources "github.com/mweagle/Sparta/aws/cloudformation/resources"
	gocf "github.com/mweagle/go-cloudformation"
	"github.com/sirupsen/logrus"
)

// StampedServiceName is the name stamp
// https://blog.cloudflare.com/setting-go-variables-at-compile-time/
// StampedServiceName is the serviceName stamped into this binary
var StampedServiceName string

// StampedBuildID is the buildID stamped into the binary
var StampedBuildID string

var discoveryInfo *DiscoveryInfo
var once sync.Once

func initDiscoveryInfo() {
	info, _ := Discover()
	discoveryInfo = info
}

func awsLambdaFunctionName(internalFunctionName string) gocf.Stringable {
	// TODO - move this to use SSM so that it's not human editable?
	// But discover information is per-function, not per stack.
	// Could we put the stack discovery info in there?
	once.Do(initDiscoveryInfo)
	sanitizedName := awsLambdaInternalName(internalFunctionName)

	return gocf.String(fmt.Sprintf("%s%s%s",
		discoveryInfo.StackName,
		functionNameDelimiter,
		sanitizedName))
}

func takesContext(handler reflect.Type) bool {
	handlerTakesContext := false
	if handler.NumIn() > 0 {
		contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
		argumentType := handler.In(0)
		handlerTakesContext = argumentType.Implements(contextType)
	}
	return handlerTakesContext
}

// tappedHandler is the handler that represents this binary's mode
func tappedHandler(handlerSymbol interface{},
	interceptors *LambdaEventInterceptors,
	logger *logrus.Logger) interface{} {

	// If there aren't any, make it a bit easier
	// to call the applyInterceptors function
	if interceptors == nil {
		interceptors = &LambdaEventInterceptors{}
	}

	// Tap the call chain to inject the context params...
	handler := reflect.ValueOf(handlerSymbol)
	handlerType := reflect.TypeOf(handlerSymbol)
	takesContext := takesContext(handlerType)

	// Apply interceptors is a utility function to apply the
	// specified interceptors as part of the lifecycle handler.
	// We can push the specific behaviors into the interceptors
	// and keep this function simple. 🎉
	applyInterceptors := func(ctx context.Context,
		msg json.RawMessage,
		interceptors InterceptorList) context.Context {
		for _, eachInterceptor := range interceptors {
			ctx = eachInterceptor.Interceptor(ctx, msg)
		}
		return ctx
	}

	// How to determine if this handler has tracing enabled? That would be a property
	// of the function template associated with this function.

	// TODO - add Context.Timeout handler to ensure orderly exit
	return func(ctx context.Context, msg json.RawMessage) (interface{}, error) {
		ctx = applyInterceptors(ctx, msg, interceptors.Begin)
		ctx = context.WithValue(ctx, ContextKeyLogger, logger)
		ctx = applyInterceptors(ctx, msg, interceptors.BeforeSetup)

		// Create the entry logger that has some context information
		var logrusEntry *logrus.Entry
		lambdaContext, lambdaContextOk := awsLambdaContext.FromContext(ctx)
		if lambdaContextOk {
			logrusEntry = logrus.
				NewEntry(logger).
				WithFields(logrus.Fields{
					LogFieldRequestID:  lambdaContext.AwsRequestID,
					LogFieldARN:        lambdaContext.InvokedFunctionArn,
					LogFieldBuildID:    StampedBuildID,
					LogFieldInstanceID: InstanceID(),
				})
		} else {
			logrusEntry = logrus.NewEntry(logger)
		}
		ctx = context.WithValue(ctx, ContextKeyRequestLogger, logrusEntry)
		ctx = applyInterceptors(ctx, msg, interceptors.AfterSetup)

		// construct arguments
		var args []reflect.Value
		if takesContext {
			args = append(args, reflect.ValueOf(ctx))
		}
		if (handlerType.NumIn() == 1 && !takesContext) ||
			handlerType.NumIn() == 2 {
			eventType := handlerType.In(handlerType.NumIn() - 1)
			event := reflect.New(eventType)
			unmarshalErr := json.Unmarshal(msg, event.Interface())
			if unmarshalErr != nil {
				return nil, unmarshalErr
			}
			args = append(args, event.Elem())
		}
		ctx = applyInterceptors(ctx, msg, interceptors.BeforeDispatch)
		response := handler.Call(args)
		ctx = applyInterceptors(ctx, msg, interceptors.AfterDispatch)

		// If the user function
		// convert return values into (interface{}, error)
		var err error
		if len(response) > 0 {
			if errVal, ok := response[len(response)-1].Interface().(error); ok {
				err = errVal
			}
		}
		ctx = context.WithValue(ctx, ContextKeyLambdaError, err)
		var val interface{}
		if len(response) > 1 {
			val = response[0].Interface()
		}
		ctx = context.WithValue(ctx, ContextKeyLambdaResponse, val)
		ctx = applyInterceptors(ctx, msg, interceptors.Complete)
		return val, err
	}
}

// Execute creates an HTTP listener to dispatch execution. Typically
// called via Main() via command line arguments.
func Execute(serviceName string,
	lambdaAWSInfos []*LambdaAWSInfo,
	logger *logrus.Logger) error {

	// Initialize the discovery service
	initializeDiscovery(logger)

	// Find the function name based on the dispatch
	// https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
	requestedLambdaFunctionName := os.Getenv("AWS_LAMBDA_FUNCTION_NAME")

	// Log any info when we start up...
	platformLogSysInfo(requestedLambdaFunctionName, logger)

	// So what if we have workflow hooks in here?
	var interceptors *LambdaEventInterceptors

	/*
		There are three types of targets:
			- User functions
			- User custom resources
			- Sparta custom resources
	*/
	// Based on the environment variable, setup the proper listener...
	var lambdaFunctionName gocf.Stringable
	testAWSName := ""
	var handlerSymbol interface{}
	knownNames := []string{}

	//////////////////////////////////////////////////////////////////////////////
	// User registered commands?
	//////////////////////////////////////////////////////////////////////////////
	for _, eachLambdaInfo := range lambdaAWSInfos {
		lambdaFunctionName = awsLambdaFunctionName(eachLambdaInfo.lambdaFunctionName())
		testAWSName = lambdaFunctionName.String().Literal

		knownNames = append(knownNames, testAWSName)
		if requestedLambdaFunctionName == testAWSName {
			handlerSymbol = eachLambdaInfo.handlerSymbol
			interceptors = eachLambdaInfo.Interceptors

		}
		// User defined custom resource handler?
		for _, eachCustomResource := range eachLambdaInfo.customResources {
			lambdaFunctionName = awsLambdaFunctionName(eachCustomResource.userFunctionName)
			testAWSName = lambdaFunctionName.String().Literal
			knownNames = append(knownNames, testAWSName)
			if requestedLambdaFunctionName == testAWSName {
				handlerSymbol = eachCustomResource.handlerSymbol
			}
		}
		if handlerSymbol != nil {
			break
		}
	}

	//////////////////////////////////////////////////////////////////////////////
	// Request to instantiate a CustomResourceHandler that implements
	// the CustomResourceCommand interface?
	//////////////////////////////////////////////////////////////////////////////
	if handlerSymbol == nil {
		requestCustomResourceType := os.Getenv(EnvVarCustomResourceTypeName)
		if requestCustomResourceType != "" {
			knownNames = append(knownNames, fmt.Sprintf("CloudFormation Custom Resource: %s", requestCustomResourceType))
			logger.WithFields(logrus.Fields{
				"customResourceTypeName": requestCustomResourceType,
			}).Debug("Checking to see if there is a custom resource")

			resource := gocf.NewResourceByType(requestCustomResourceType)
			if resource != nil {
				// Handler?
				command, commandOk := resource.(cloudformationResources.CustomResourceCommand)
				if !commandOk {
					logger.Error("CloudFormation type %s doesn't implement cloudformationResources.CustomResourceCommand", requestCustomResourceType)
				} else {
					customHandler := cloudformationResources.CloudFormationLambdaCustomResourceHandler(command, logger)
					if customHandler != nil {
						handlerSymbol = customHandler
					}
				}
			} else {
				logger.Error("Failed to create CloudFormation custom resource of type: %s", requestCustomResourceType)
			}
		}
	}

	if handlerSymbol == nil {
		errorMessage := fmt.Errorf("No handler found for AWS Lambda function: %s. Registered function name: %#v",
			requestedLambdaFunctionName,
			knownNames)
		logger.Error(errorMessage)
		return errorMessage
	}

	// Startup our version...
	tappedHandler := tappedHandler(handlerSymbol, interceptors, logger)
	awsLambdaGo.Start(tappedHandler)
	return nil
}
<commit_msg>Add a TODO regarding panic handler<commit_after>// +build lambdabinary

package sparta

import (
	"context"
	"encoding/json"
	"fmt"
	"os"
	"reflect"
	"sync"

	awsLambdaGo "github.com/aws/aws-lambda-go/lambda"
	awsLambdaContext "github.com/aws/aws-lambda-go/lambdacontext"
	cloudformationResources "github.com/mweagle/Sparta/aws/cloudformation/resources"
	gocf "github.com/mweagle/go-cloudformation"
	"github.com/sirupsen/logrus"
)

// StampedServiceName is the name stamp
// https://blog.cloudflare.com/setting-go-variables-at-compile-time/
// StampedServiceName is the serviceName stamped into this binary
var StampedServiceName string

// StampedBuildID is the buildID stamped into the binary
var StampedBuildID string

var discoveryInfo *DiscoveryInfo
var once sync.Once

func initDiscoveryInfo() {
	info, _ := Discover()
	discoveryInfo = info
}

func awsLambdaFunctionName(internalFunctionName string) gocf.Stringable {
	// TODO - move this to use SSM so that it's not human editable?
	// But discover information is per-function, not per stack.
	// Could we put the stack discovery info in there?
	once.Do(initDiscoveryInfo)
	sanitizedName := awsLambdaInternalName(internalFunctionName)

	return gocf.String(fmt.Sprintf("%s%s%s",
		discoveryInfo.StackName,
		functionNameDelimiter,
		sanitizedName))
}

func takesContext(handler reflect.Type) bool {
	handlerTakesContext := false
	if handler.NumIn() > 0 {
		contextType := reflect.TypeOf((*context.Context)(nil)).Elem()
		argumentType := handler.In(0)
		handlerTakesContext = argumentType.Implements(contextType)
	}
	return handlerTakesContext
}

// tappedHandler is the handler that represents this binary's mode
func tappedHandler(handlerSymbol interface{},
	interceptors *LambdaEventInterceptors,
	logger *logrus.Logger) interface{} {

	// If there aren't any, make it a bit easier
	// to call the applyInterceptors function
	if interceptors == nil {
		interceptors = &LambdaEventInterceptors{}
	}

	// Tap the call chain to inject the context params...
	handler := reflect.ValueOf(handlerSymbol)
	handlerType := reflect.TypeOf(handlerSymbol)
	takesContext := takesContext(handlerType)

	// Apply interceptors is a utility function to apply the
	// specified interceptors as part of the lifecycle handler.
	// We can push the specific behaviors into the interceptors
	// and keep this function simple. 🎉
	applyInterceptors := func(ctx context.Context,
		msg json.RawMessage,
		interceptors InterceptorList) context.Context {
		for _, eachInterceptor := range interceptors {
			ctx = eachInterceptor.Interceptor(ctx, msg)
		}
		return ctx
	}

	// How to determine if this handler has tracing enabled? That would be a property
	// of the function template associated with this function.

	// TODO - add Context.Timeout handler to ensure orderly exit
	return func(ctx context.Context, msg json.RawMessage) (interface{}, error) {

		// TODO - add panic handler.

		ctx = applyInterceptors(ctx, msg, interceptors.Begin)
		ctx = context.WithValue(ctx, ContextKeyLogger, logger)
		ctx = applyInterceptors(ctx, msg, interceptors.BeforeSetup)

		// Create the entry logger that has some context information
		var logrusEntry *logrus.Entry
		lambdaContext, lambdaContextOk := awsLambdaContext.FromContext(ctx)
		if lambdaContextOk {
			logrusEntry = logrus.
				NewEntry(logger).
				WithFields(logrus.Fields{
					LogFieldRequestID:  lambdaContext.AwsRequestID,
					LogFieldARN:        lambdaContext.InvokedFunctionArn,
					LogFieldBuildID:    StampedBuildID,
					LogFieldInstanceID: InstanceID(),
				})
		} else {
			logrusEntry = logrus.NewEntry(logger)
		}
		ctx = context.WithValue(ctx, ContextKeyRequestLogger, logrusEntry)
		ctx = applyInterceptors(ctx, msg, interceptors.AfterSetup)

		// construct arguments
		var args []reflect.Value
		if takesContext {
			args = append(args, reflect.ValueOf(ctx))
		}
		if (handlerType.NumIn() == 1 && !takesContext) ||
			handlerType.NumIn() == 2 {
			eventType := handlerType.In(handlerType.NumIn() - 1)
			event := reflect.New(eventType)
			unmarshalErr := json.Unmarshal(msg, event.Interface())
			if unmarshalErr != nil {
				return nil, unmarshalErr
			}
			args = append(args, event.Elem())
		}
		ctx = applyInterceptors(ctx, msg, interceptors.BeforeDispatch)
		response := handler.Call(args)
		ctx = applyInterceptors(ctx, msg, interceptors.AfterDispatch)

		// If the user function
		// convert return values into (interface{}, error)
		var err error
		if len(response) > 0 {
			if errVal, ok := response[len(response)-1].Interface().(error); ok {
				err = errVal
			}
		}
		ctx = context.WithValue(ctx, ContextKeyLambdaError, err)
		var val interface{}
		if len(response) > 1 {
			val = response[0].Interface()
		}
		ctx = context.WithValue(ctx, ContextKeyLambdaResponse, val)
		ctx = applyInterceptors(ctx, msg, interceptors.Complete)
		return val, err
	}
}

// Execute creates an HTTP listener to dispatch execution. Typically
// called via Main() via command line arguments.
func Execute(serviceName string,
	lambdaAWSInfos []*LambdaAWSInfo,
	logger *logrus.Logger) error {

	// Initialize the discovery service
	initializeDiscovery(logger)

	// Find the function name based on the dispatch
	// https://docs.aws.amazon.com/lambda/latest/dg/current-supported-versions.html
	requestedLambdaFunctionName := os.Getenv("AWS_LAMBDA_FUNCTION_NAME")

	// Log any info when we start up...
	platformLogSysInfo(requestedLambdaFunctionName, logger)

	// So what if we have workflow hooks in here?
	var interceptors *LambdaEventInterceptors

	/*
		There are three types of targets:
			- User functions
			- User custom resources
			- Sparta custom resources
	*/
	// Based on the environment variable, setup the proper listener...
	var lambdaFunctionName gocf.Stringable
	testAWSName := ""
	var handlerSymbol interface{}
	knownNames := []string{}

	//////////////////////////////////////////////////////////////////////////////
	// User registered commands?
	//////////////////////////////////////////////////////////////////////////////
	for _, eachLambdaInfo := range lambdaAWSInfos {
		lambdaFunctionName = awsLambdaFunctionName(eachLambdaInfo.lambdaFunctionName())
		testAWSName = lambdaFunctionName.String().Literal

		knownNames = append(knownNames, testAWSName)
		if requestedLambdaFunctionName == testAWSName {
			handlerSymbol = eachLambdaInfo.handlerSymbol
			interceptors = eachLambdaInfo.Interceptors

		}
		// User defined custom resource handler?
		for _, eachCustomResource := range eachLambdaInfo.customResources {
			lambdaFunctionName = awsLambdaFunctionName(eachCustomResource.userFunctionName)
			testAWSName = lambdaFunctionName.String().Literal
			knownNames = append(knownNames, testAWSName)
			if requestedLambdaFunctionName == testAWSName {
				handlerSymbol = eachCustomResource.handlerSymbol
			}
		}
		if handlerSymbol != nil {
			break
		}
	}

	//////////////////////////////////////////////////////////////////////////////
	// Request to instantiate a CustomResourceHandler that implements
	// the CustomResourceCommand interface?
	//////////////////////////////////////////////////////////////////////////////
	if handlerSymbol == nil {
		requestCustomResourceType := os.Getenv(EnvVarCustomResourceTypeName)
		if requestCustomResourceType != "" {
			knownNames = append(knownNames, fmt.Sprintf("CloudFormation Custom Resource: %s", requestCustomResourceType))
			logger.WithFields(logrus.Fields{
				"customResourceTypeName": requestCustomResourceType,
			}).Debug("Checking to see if there is a custom resource")

			resource := gocf.NewResourceByType(requestCustomResourceType)
			if resource != nil {
				// Handler?
				command, commandOk := resource.(cloudformationResources.CustomResourceCommand)
				if !commandOk {
					logger.Error("CloudFormation type %s doesn't implement cloudformationResources.CustomResourceCommand", requestCustomResourceType)
				} else {
					customHandler := cloudformationResources.CloudFormationLambdaCustomResourceHandler(command, logger)
					if customHandler != nil {
						handlerSymbol = customHandler
					}
				}
			} else {
				logger.Error("Failed to create CloudFormation custom resource of type: %s", requestCustomResourceType)
			}
		}
	}

	if handlerSymbol == nil {
		errorMessage := fmt.Errorf("No handler found for AWS Lambda function: %s. Registered function name: %#v",
			requestedLambdaFunctionName,
			knownNames)
		logger.Error(errorMessage)
		return errorMessage
	}

	// Startup our version...
	tappedHandler := tappedHandler(handlerSymbol, interceptors, logger)
	awsLambdaGo.Start(tappedHandler)
	return nil
}
<|endoftext|>


<commit_before>package secretservice

/*
#cgo pkg-config: libsecret-1

#include "secretservice_linux.h"
#include <stdlib.h>
*/
import "C"
import (
	"errors"
	"unsafe"

	"github.com/docker/docker-credential-helpers/credentials"
)

// Secretservice handles secrets using Linux secret-service as a store.
type Secretservice struct{}

// Add adds new credentials to the keychain.
func (h Secretservice) Add(creds *credentials.Credentials) error {
	if creds == nil {
		return errors.New("missing credentials")
	}
	server := C.CString(creds.ServerURL)
	defer C.free(unsafe.Pointer(server))
	username := C.CString(creds.Username)
	defer C.free(unsafe.Pointer(username))
	secret := C.CString(creds.Secret)
	defer C.free(unsafe.Pointer(secret))

	if err := C.add(server, username, secret); err != nil {
		defer C.g_error_free(err)
		errMsg := (*C.char)(unsafe.Pointer(err.message))
		return errors.New(C.GoString(errMsg))
	}
	return nil
}

// Delete removes credentials from the store.
func (h Secretservice) Delete(serverURL string) error {
	if serverURL == "" {
		return errors.New("missing server url")
	}
	server := C.CString(serverURL)
	defer C.free(unsafe.Pointer(server))

	if err := C.delete(server); err != nil {
		defer C.g_error_free(err)
		errMsg := (*C.char)(unsafe.Pointer(err.message))
		return errors.New(C.GoString(errMsg))
	}
	return nil
}

// Get returns the username and secret to use for a given registry server URL.
func (h Secretservice) Get(serverURL string) (string, string, error) {
	if serverURL == "" {
		return "", "", errors.New("missing server url")
	}
	var username *C.char
	defer C.free(unsafe.Pointer(username))
	var secret *C.char
	defer C.free(unsafe.Pointer(secret))
	server := C.CString(serverURL)
	defer C.free(unsafe.Pointer(server))

	err := C.get(server, &username, &secret)
	if err != nil {
		defer C.g_error_free(err)
		errMsg := (*C.char)(unsafe.Pointer(err.message))
		return "", "", errors.New(C.GoString(errMsg))
	}
	user := C.GoString(username)
	pass := C.GoString(secret)
	if pass == "" {
		return "", "", credentials.NewErrCredentialsNotFound()
	}
	return user, pass, nil
}

// List returns the stored URLs and corresponding usernames.
func (h Secretservice) List() (map[string]string, error) {
	var pathsC **C.char
	defer C.free(unsafe.Pointer(pathsC))
	var acctsC **C.char
	defer C.free(unsafe.Pointer(acctsC))
	var listLenC C.uint
	err := C.list(&pathsC, &acctsC, &listLenC)
	if err != nil {
		defer C.free(unsafe.Pointer(err))
		return nil, errors.New("Error from list function in secretservice_linux.c likely due to error in secretservice library")
	}
	defer C.freeListData(&pathsC, listLenC)
	defer C.freeListData(&acctsC, listLenC)

	listLen := int(listLenC)
	pathTmp := (*[1 << 30]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen]
	acctTmp := (*[1 << 30]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen]
	resp := make(map[string]string)
	for i := 0; i < listLen; i++ {
		resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i])
	}

	return resp, nil
}
<commit_msg>C.free(unsafe.Pointer(err)) -> defer C.g_error_free(err)<commit_after>package secretservice

/*
#cgo pkg-config: libsecret-1

#include "secretservice_linux.h"
#include <stdlib.h>
*/
import "C"
import (
	"errors"
	"unsafe"

	"github.com/docker/docker-credential-helpers/credentials"
)

// Secretservice handles secrets using Linux secret-service as a store.
type Secretservice struct{}

// Add adds new credentials to the keychain.
func (h Secretservice) Add(creds *credentials.Credentials) error {
	if creds == nil {
		return errors.New("missing credentials")
	}
	server := C.CString(creds.ServerURL)
	defer C.free(unsafe.Pointer(server))
	username := C.CString(creds.Username)
	defer C.free(unsafe.Pointer(username))
	secret := C.CString(creds.Secret)
	defer C.free(unsafe.Pointer(secret))

	if err := C.add(server, username, secret); err != nil {
		defer C.g_error_free(err)
		errMsg := (*C.char)(unsafe.Pointer(err.message))
		return errors.New(C.GoString(errMsg))
	}
	return nil
}

// Delete removes credentials from the store.
func (h Secretservice) Delete(serverURL string) error {
	if serverURL == "" {
		return errors.New("missing server url")
	}
	server := C.CString(serverURL)
	defer C.free(unsafe.Pointer(server))

	if err := C.delete(server); err != nil {
		defer C.g_error_free(err)
		errMsg := (*C.char)(unsafe.Pointer(err.message))
		return errors.New(C.GoString(errMsg))
	}
	return nil
}

// Get returns the username and secret to use for a given registry server URL.
func (h Secretservice) Get(serverURL string) (string, string, error) {
	if serverURL == "" {
		return "", "", errors.New("missing server url")
	}
	var username *C.char
	defer C.free(unsafe.Pointer(username))
	var secret *C.char
	defer C.free(unsafe.Pointer(secret))
	server := C.CString(serverURL)
	defer C.free(unsafe.Pointer(server))

	err := C.get(server, &username, &secret)
	if err != nil {
		defer C.g_error_free(err)
		errMsg := (*C.char)(unsafe.Pointer(err.message))
		return "", "", errors.New(C.GoString(errMsg))
	}
	user := C.GoString(username)
	pass := C.GoString(secret)
	if pass == "" {
		return "", "", credentials.NewErrCredentialsNotFound()
	}
	return user, pass, nil
}

// List returns the stored URLs and corresponding usernames.
func (h Secretservice) List() (map[string]string, error) {
	var pathsC **C.char
	defer C.free(unsafe.Pointer(pathsC))
	var acctsC **C.char
	defer C.free(unsafe.Pointer(acctsC))
	var listLenC C.uint
	err := C.list(&pathsC, &acctsC, &listLenC)
	if err != nil {
		defer C.g_error_free(err)
		return nil, errors.New("Error from list function in secretservice_linux.c likely due to error in secretservice library")
	}
	defer C.freeListData(&pathsC, listLenC)
	defer C.freeListData(&acctsC, listLenC)

	listLen := int(listLenC)
	pathTmp := (*[1 << 30]*C.char)(unsafe.Pointer(pathsC))[:listLen:listLen]
	acctTmp := (*[1 << 30]*C.char)(unsafe.Pointer(acctsC))[:listLen:listLen]
	resp := make(map[string]string)
	for i := 0; i < listLen; i++ {
		resp[C.GoString(pathTmp[i])] = C.GoString(acctTmp[i])
	}

	return resp, nil
}
<|endoftext|>