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

package wallet

import (
	"context"
	"crypto/rand"
	"crypto/tls"
	"net"

	"decred.org/cspp"
	"decred.org/cspp/coinjoin"
	"decred.org/dcrwallet/errors"
	"decred.org/dcrwallet/wallet/txauthor"
	"decred.org/dcrwallet/wallet/txrules"
	"decred.org/dcrwallet/wallet/txsizes"
	"decred.org/dcrwallet/wallet/udb"
	"decred.org/dcrwallet/wallet/walletdb"
	"github.com/decred/dcrd/dcrutil/v3"
	"github.com/decred/dcrd/wire"
	"github.com/decred/go-socks/socks"
	"golang.org/x/sync/errgroup"
)

// must be sorted large to small
var splitPoints = [...]dcrutil.Amount{
	1 << 36, // 687.19476736
	1 << 34, // 171.79869184
	1 << 32, // 042.94967296
	1 << 30, // 010.73741824
	1 << 28, // 002.68435456
	1 << 26, // 000.67108864
	1 << 24, // 000.16777216
	1 << 22, // 000.04194304
	1 << 20, // 000.01048576
	1 << 18, // 000.00262144
}

var splitSems = [len(splitPoints)]chan struct{}{}

func init() {
	for i := range splitSems {
		splitSems[i] = make(chan struct{}, 10)
	}
}

var errNoSplitDenomination = errors.New("no suitable split denomination")

// DialFunc provides a method to dial a network connection.
// If the dialed network connection is secured by TLS, TLS
// configuration is provided by the method, not the caller.
type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)

func (w *Wallet) MixOutput(ctx context.Context, dialTLS DialFunc, csppserver string, output *wire.OutPoint, changeAccount, mixAccount, mixBranch uint32) error {
	op := errors.Opf("wallet.MixOutput(%v)", output)

	sdiff, err := w.NextStakeDifficulty(ctx)
	if err != nil {
		return errors.E(op, err)
	}

	var updates []func(walletdb.ReadWriteTx) error

	hold, err := w.holdUnlock()
	if err != nil {
		return errors.E(op, err)
	}
	defer hold.release()

	var prevScript []byte
	var amount dcrutil.Amount
	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
		txDetails, err := w.txStore.TxDetails(txmgrNs, &output.Hash)
		if err != nil {
			return err
		}
		prevScript = txDetails.MsgTx.TxOut[output.Index].PkScript
		amount = dcrutil.Amount(txDetails.MsgTx.TxOut[output.Index].Value)
		return nil
	})
	if err != nil {
		return errors.E(op, err)
	}

	w.lockedOutpointMu.Lock()
	w.lockedOutpoints[*output] = struct{}{}
	w.lockedOutpointMu.Unlock()
	defer func() {
		w.lockedOutpointMu.Lock()
		delete(w.lockedOutpoints, *output)
		w.lockedOutpointMu.Unlock()
	}()

	var count int
	var mixValue, remValue dcrutil.Amount
	for i, v := range splitPoints {
		// When the sdiff is more than four times this mixed output
		// amount, there is a smaller common mixed amount with more
		// pairing activity (due to CoinShuffle++ participation from
		// ticket buyers).  Skipping this amount and moving to the next
		// smallest common mixed amount will result in quicker pairings,
		// or pairings occurring at all.  Unlike any downmixing of mixed
		// ticketbuying change, this will result in four or more outputs
		// when mixing larger UTXOs.
		if i != len(splitPoints)-1 && 4*v >= sdiff {
			continue
		}
		count = int(amount / v)
		if count > 0 {
			remValue = amount - dcrutil.Amount(count)*v
			mixValue = v
			select {
			case <-ctx.Done():
				return errors.E(op, ctx.Err())
			case splitSems[i] <- struct{}{}:
				defer func() { <-splitSems[i] }()
			}
			break
		}
	}
	if mixValue == splitPoints[len(splitPoints)-1] {
		remValue = 0
	}
	if mixValue == 0 {
		err := errors.Errorf("output %v (%v): %w", output, amount, errNoSplitDenomination)
		return errors.E(op, err)
	}

	const (
		txVersion = 1
		locktime  = 0
		expiry    = 0
	)
	pairing := coinjoin.EncodeDesc(coinjoin.P2PKHv0, int64(mixValue), txVersion, locktime, expiry)
	ses, err := cspp.NewSession(rand.Reader, debugLog, pairing, count)
	if err != nil {
		return errors.E(op, err)
	}
	var conn net.Conn
	if dialTLS != nil {
		conn, err = dialTLS(ctx, "tcp", csppserver)
	} else {
		conn, err = tls.Dial("tcp", csppserver, nil)
	}
	if err != nil {
		return errors.E(op, err)
	}
	defer conn.Close()
	log.Infof("Dialed CSPPServer %v -> %v", conn.LocalAddr(), conn.RemoteAddr())

	// Create change output from remaining value and contributed fee
	const P2PKHv0Len = 25
	feeRate := w.RelayFee()
	inScriptSizes := []int{txsizes.RedeemP2PKHSigScriptSize}
	outScriptSizes := make([]int, count)
	for i := range outScriptSizes {
		outScriptSizes[i] = P2PKHv0Len
	}
	size := txsizes.EstimateSerializeSizeFromScriptSizes(inScriptSizes, outScriptSizes, P2PKHv0Len)
	changeValue := remValue - txrules.FeeForSerializeSize(feeRate, size)
	var change *wire.TxOut
	if !txrules.IsDustAmount(changeValue, P2PKHv0Len, feeRate) {
		persist := w.deferPersistReturnedChild(ctx, &updates)
		const accountName = "" // not used, so can be faked.
		addr, err := w.nextAddress(ctx, op, persist,
			accountName, changeAccount, udb.InternalBranch, WithGapPolicyIgnore())
		if err != nil {
			return errors.E(op, err)
		}
		changeScript, version, err := addressScript(addr)
		if err != nil {
			return errors.E(op, err)
		}
		change = &wire.TxOut{
			Value:    int64(changeValue),
			PkScript: changeScript,
			Version:  version,
		}
	}

	log.Infof("Mixing output %v (%v)", output, amount)
	cj := w.newCsppJoin(ctx, change, mixValue, mixAccount, mixBranch, int(count))
	cj.addTxIn(prevScript, &wire.TxIn{
		PreviousOutPoint: *output,
		ValueIn:          int64(amount),
	})
	err = ses.DiceMix(ctx, conn, cj)
	if err != nil {
		return errors.E(op, err)
	}
	cjHash := cj.tx.TxHash()
	log.Infof("Completed CoinShuffle++ mix of output %v in transaction %v", output, &cjHash)

	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
		for _, f := range updates {
			if err := f(dbtx); err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return errors.E(op, err)
	}

	return nil
}

// MixAccount individually mixes outputs of an account into standard
// denominations, creating newly mixed outputs for a mixed account.
//
// Due to performance concerns of timing out in a CoinShuffle++ run, this
// function may throttle how many of the outputs are mixed each call.
func (w *Wallet) MixAccount(ctx context.Context, dialTLS DialFunc, csppserver string, changeAccount, mixAccount, mixBranch uint32) error {
	const op errors.Op = "wallet.MixAccount"

	hold, err := w.holdUnlock()
	if err != nil {
		return errors.E(op, err)
	}
	defer hold.release()

	_, tipHeight := w.MainChainTip(ctx)
	var credits []udb.Credit
	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		var err error
		credits, err = w.findEligibleOutputs(dbtx, changeAccount, 1, tipHeight)
		return err
	})
	if err != nil {
		return errors.E(op, err)
	}
	unlockedCredits := credits[:0]
	for i := range credits {
		if credits[i].Amount <= splitPoints[len(splitPoints)-1] {
			continue
		}
		w.lockedOutpointMu.Lock()
		_, locked := w.lockedOutpoints[credits[i].OutPoint]
		if !locked {
			w.lockedOutpoints[credits[i].OutPoint] = struct{}{}
			unlockedCredits = append(unlockedCredits, credits[i])
		}
		w.lockedOutpointMu.Unlock()
	}
	credits = unlockedCredits
	shuffle(len(credits), func(i, j int) {
		credits[i], credits[j] = credits[j], credits[i]
	})
	if len(credits) > 32 { // simple throttle
		credits = credits[:32]
	}
	var g errgroup.Group
	for i := range credits {
		op := &credits[i].OutPoint
		g.Go(func() error {
			err := w.MixOutput(ctx, dialTLS, csppserver, op, changeAccount, mixAccount, mixBranch)
			if errors.Is(err, errNoSplitDenomination) {
				return nil
			}
			if errors.Is(err, socks.ErrPoolMaxConnections) {
				return nil
			}
			return err
		})
	}
	err = g.Wait()
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}

// randomInputSource wraps an InputSource to randomly pick UTXOs.
// This involves reading all UTXOs from the underlying source into memory.
func randomInputSource(source txauthor.InputSource) txauthor.InputSource {
	all, err := source(dcrutil.MaxAmount)
	if err == nil {
		shuffleUTXOs(all)
	}
	var n int
	var tot dcrutil.Amount
	return func(target dcrutil.Amount) (*txauthor.InputDetail, error) {
		if err != nil {
			return nil, err
		}
		if all.Amount <= target {
			return all, nil
		}
		for n < len(all.Inputs) {
			tot += dcrutil.Amount(all.Inputs[n].ValueIn)
			n++
			if tot >= target {
				break
			}
		}
		selected := &txauthor.InputDetail{
			Amount:            tot,
			Inputs:            all.Inputs[:n],
			Scripts:           all.Scripts[:n],
			RedeemScriptSizes: all.RedeemScriptSizes[:n],
		}
		return selected, nil
	}
}
<commit_msg>Cap how many mixed outputs downmixing produces<commit_after>// Copyright (c) 2019 The Decred developers
// Use of this source code is governed by an ISC
// license that can be found in the LICENSE file.

package wallet

import (
	"context"
	"crypto/rand"
	"crypto/tls"
	"net"

	"decred.org/cspp"
	"decred.org/cspp/coinjoin"
	"decred.org/dcrwallet/errors"
	"decred.org/dcrwallet/wallet/txauthor"
	"decred.org/dcrwallet/wallet/txrules"
	"decred.org/dcrwallet/wallet/txsizes"
	"decred.org/dcrwallet/wallet/udb"
	"decred.org/dcrwallet/wallet/walletdb"
	"github.com/decred/dcrd/dcrutil/v3"
	"github.com/decred/dcrd/wire"
	"github.com/decred/go-socks/socks"
	"golang.org/x/sync/errgroup"
)

// must be sorted large to small
var splitPoints = [...]dcrutil.Amount{
	1 << 36, // 687.19476736
	1 << 34, // 171.79869184
	1 << 32, // 042.94967296
	1 << 30, // 010.73741824
	1 << 28, // 002.68435456
	1 << 26, // 000.67108864
	1 << 24, // 000.16777216
	1 << 22, // 000.04194304
	1 << 20, // 000.01048576
	1 << 18, // 000.00262144
}

var splitSems = [len(splitPoints)]chan struct{}{}

func init() {
	for i := range splitSems {
		splitSems[i] = make(chan struct{}, 10)
	}
}

var errNoSplitDenomination = errors.New("no suitable split denomination")

// DialFunc provides a method to dial a network connection.
// If the dialed network connection is secured by TLS, TLS
// configuration is provided by the method, not the caller.
type DialFunc func(ctx context.Context, network, addr string) (net.Conn, error)

func (w *Wallet) MixOutput(ctx context.Context, dialTLS DialFunc, csppserver string, output *wire.OutPoint, changeAccount, mixAccount, mixBranch uint32) error {
	op := errors.Opf("wallet.MixOutput(%v)", output)

	sdiff, err := w.NextStakeDifficulty(ctx)
	if err != nil {
		return errors.E(op, err)
	}

	var updates []func(walletdb.ReadWriteTx) error

	hold, err := w.holdUnlock()
	if err != nil {
		return errors.E(op, err)
	}
	defer hold.release()

	var prevScript []byte
	var amount dcrutil.Amount
	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		txmgrNs := dbtx.ReadBucket(wtxmgrNamespaceKey)
		txDetails, err := w.txStore.TxDetails(txmgrNs, &output.Hash)
		if err != nil {
			return err
		}
		prevScript = txDetails.MsgTx.TxOut[output.Index].PkScript
		amount = dcrutil.Amount(txDetails.MsgTx.TxOut[output.Index].Value)
		return nil
	})
	if err != nil {
		return errors.E(op, err)
	}

	w.lockedOutpointMu.Lock()
	w.lockedOutpoints[*output] = struct{}{}
	w.lockedOutpointMu.Unlock()
	defer func() {
		w.lockedOutpointMu.Lock()
		delete(w.lockedOutpoints, *output)
		w.lockedOutpointMu.Unlock()
	}()

	var count int
	var mixValue, remValue dcrutil.Amount
	for i, v := range splitPoints {
		// When the sdiff is more than four times this mixed output
		// amount, there is a smaller common mixed amount with more
		// pairing activity (due to CoinShuffle++ participation from
		// ticket buyers).  Skipping this amount and moving to the next
		// smallest common mixed amount will result in quicker pairings,
		// or pairings occurring at all.  The number of mixed outputs is
		// capped to prevent a single mix being overwhelmingly funded by
		// a single output, and to conserve memory resources.
		if i != len(splitPoints)-1 && 4*v >= sdiff {
			continue
		}
		count = int(amount / v)
		if count > 4 {
			count = 4
		}
		if count > 0 {
			remValue = amount - dcrutil.Amount(count)*v
			mixValue = v
			select {
			case <-ctx.Done():
				return errors.E(op, ctx.Err())
			case splitSems[i] <- struct{}{}:
				defer func() { <-splitSems[i] }()
			}
			break
		}
	}
	if mixValue == splitPoints[len(splitPoints)-1] {
		remValue = 0
	}
	if mixValue == 0 {
		err := errors.Errorf("output %v (%v): %w", output, amount, errNoSplitDenomination)
		return errors.E(op, err)
	}

	const (
		txVersion = 1
		locktime  = 0
		expiry    = 0
	)
	pairing := coinjoin.EncodeDesc(coinjoin.P2PKHv0, int64(mixValue), txVersion, locktime, expiry)
	ses, err := cspp.NewSession(rand.Reader, debugLog, pairing, count)
	if err != nil {
		return errors.E(op, err)
	}
	var conn net.Conn
	if dialTLS != nil {
		conn, err = dialTLS(ctx, "tcp", csppserver)
	} else {
		conn, err = tls.Dial("tcp", csppserver, nil)
	}
	if err != nil {
		return errors.E(op, err)
	}
	defer conn.Close()
	log.Infof("Dialed CSPPServer %v -> %v", conn.LocalAddr(), conn.RemoteAddr())

	// Create change output from remaining value and contributed fee
	const P2PKHv0Len = 25
	feeRate := w.RelayFee()
	inScriptSizes := []int{txsizes.RedeemP2PKHSigScriptSize}
	outScriptSizes := make([]int, count)
	for i := range outScriptSizes {
		outScriptSizes[i] = P2PKHv0Len
	}
	size := txsizes.EstimateSerializeSizeFromScriptSizes(inScriptSizes, outScriptSizes, P2PKHv0Len)
	changeValue := remValue - txrules.FeeForSerializeSize(feeRate, size)
	var change *wire.TxOut
	if !txrules.IsDustAmount(changeValue, P2PKHv0Len, feeRate) {
		persist := w.deferPersistReturnedChild(ctx, &updates)
		const accountName = "" // not used, so can be faked.
		addr, err := w.nextAddress(ctx, op, persist,
			accountName, changeAccount, udb.InternalBranch, WithGapPolicyIgnore())
		if err != nil {
			return errors.E(op, err)
		}
		changeScript, version, err := addressScript(addr)
		if err != nil {
			return errors.E(op, err)
		}
		change = &wire.TxOut{
			Value:    int64(changeValue),
			PkScript: changeScript,
			Version:  version,
		}
	}

	log.Infof("Mixing output %v (%v)", output, amount)
	cj := w.newCsppJoin(ctx, change, mixValue, mixAccount, mixBranch, int(count))
	cj.addTxIn(prevScript, &wire.TxIn{
		PreviousOutPoint: *output,
		ValueIn:          int64(amount),
	})
	err = ses.DiceMix(ctx, conn, cj)
	if err != nil {
		return errors.E(op, err)
	}
	cjHash := cj.tx.TxHash()
	log.Infof("Completed CoinShuffle++ mix of output %v in transaction %v", output, &cjHash)

	err = walletdb.Update(ctx, w.db, func(dbtx walletdb.ReadWriteTx) error {
		for _, f := range updates {
			if err := f(dbtx); err != nil {
				return err
			}
		}
		return nil
	})
	if err != nil {
		return errors.E(op, err)
	}

	return nil
}

// MixAccount individually mixes outputs of an account into standard
// denominations, creating newly mixed outputs for a mixed account.
//
// Due to performance concerns of timing out in a CoinShuffle++ run, this
// function may throttle how many of the outputs are mixed each call.
func (w *Wallet) MixAccount(ctx context.Context, dialTLS DialFunc, csppserver string, changeAccount, mixAccount, mixBranch uint32) error {
	const op errors.Op = "wallet.MixAccount"

	hold, err := w.holdUnlock()
	if err != nil {
		return errors.E(op, err)
	}
	defer hold.release()

	_, tipHeight := w.MainChainTip(ctx)
	var credits []udb.Credit
	err = walletdb.View(ctx, w.db, func(dbtx walletdb.ReadTx) error {
		var err error
		credits, err = w.findEligibleOutputs(dbtx, changeAccount, 1, tipHeight)
		return err
	})
	if err != nil {
		return errors.E(op, err)
	}
	unlockedCredits := credits[:0]
	for i := range credits {
		if credits[i].Amount <= splitPoints[len(splitPoints)-1] {
			continue
		}
		w.lockedOutpointMu.Lock()
		_, locked := w.lockedOutpoints[credits[i].OutPoint]
		if !locked {
			w.lockedOutpoints[credits[i].OutPoint] = struct{}{}
			unlockedCredits = append(unlockedCredits, credits[i])
		}
		w.lockedOutpointMu.Unlock()
	}
	credits = unlockedCredits
	shuffle(len(credits), func(i, j int) {
		credits[i], credits[j] = credits[j], credits[i]
	})
	if len(credits) > 32 { // simple throttle
		credits = credits[:32]
	}
	var g errgroup.Group
	for i := range credits {
		op := &credits[i].OutPoint
		g.Go(func() error {
			err := w.MixOutput(ctx, dialTLS, csppserver, op, changeAccount, mixAccount, mixBranch)
			if errors.Is(err, errNoSplitDenomination) {
				return nil
			}
			if errors.Is(err, socks.ErrPoolMaxConnections) {
				return nil
			}
			return err
		})
	}
	err = g.Wait()
	if err != nil {
		return errors.E(op, err)
	}
	return nil
}

// randomInputSource wraps an InputSource to randomly pick UTXOs.
// This involves reading all UTXOs from the underlying source into memory.
func randomInputSource(source txauthor.InputSource) txauthor.InputSource {
	all, err := source(dcrutil.MaxAmount)
	if err == nil {
		shuffleUTXOs(all)
	}
	var n int
	var tot dcrutil.Amount
	return func(target dcrutil.Amount) (*txauthor.InputDetail, error) {
		if err != nil {
			return nil, err
		}
		if all.Amount <= target {
			return all, nil
		}
		for n < len(all.Inputs) {
			tot += dcrutil.Amount(all.Inputs[n].ValueIn)
			n++
			if tot >= target {
				break
			}
		}
		selected := &txauthor.InputDetail{
			Amount:            tot,
			Inputs:            all.Inputs[:n],
			Scripts:           all.Scripts[:n],
			RedeemScriptSizes: all.RedeemScriptSizes[:n],
		}
		return selected, nil
	}
}
<|endoftext|>


<commit_before>package goes

import (
	"errors"
	"fmt"
	"log"
	"net"
	"time"

	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/pgermishuys/goes/protobuf"
	"github.com/satori/go.uuid"
)

type Configuration struct {
	Address             string
	Port                int
	Login               string
	Password            string
	ReconnectionDelay   int
	MaxReconnects       int
	MaxOperationRetries int
}

type EventStoreConnection struct {
	Config        *Configuration
	Socket        *net.TCPConn
	connected     bool
	requests      map[uuid.UUID]chan<- TCPPackage
	subscriptions map[uuid.UUID]*Subscription
	ConnectionID  uuid.UUID
	Mutex         *sync.Mutex
}

// NewConfiguration creates a configuration with defualt settings
func NewConfiguration() *Configuration {
	return &Configuration{
		ReconnectionDelay:   100,
		MaxReconnects:       10,
		MaxOperationRetries: 10,
	}
}

// Connect attempts to connect to Event Store using the given configuration
func (connection *EventStoreConnection) Connect() error {
	connection.requests = make(map[uuid.UUID]chan<- TCPPackage)
	connection.subscriptions = make(map[uuid.UUID]*Subscription)

	return connectWithRetries(connection, connection.Config.MaxReconnects)
}

// Close attempts to close the connection to Event Store
func (connection *EventStoreConnection) Close() error {
	connection.Mutex.Lock()
	connection.connected = false
	connection.Mutex.Unlock()
	log.Printf("[info] closing the connection (id: %+v) to event store...\n'", connection.ConnectionID)
	err := connection.Socket.Close()
	connection.Socket = nil
	if err != nil {
		log.Printf("[error] failed closing the connection to event store...%+v\n'", err)
	}
	connectionClosed(connection)
	return err
}

// NewConnection sets up a new Event Store Connection but does not open the connection
func NewEventStoreConnection(config *Configuration) (*EventStoreConnection, error) {
	if len(config.Address) == 0 {
		return nil, fmt.Errorf("The address (%v) cannot be an empty string", config.Address)
	}
	if config.Port <= 0 {
		return nil, fmt.Errorf("The port (%v) cannot be less or equal to 0", config.Port)
	}
	conn := &EventStoreConnection{
		Config:       config,
		ConnectionID: uuid.NewV4(),
		Mutex:        &sync.Mutex{},
	}
	log.Printf("[info] created new event store connection : %+v", conn)
	return conn, nil
}

func connectWithRetries(connection *EventStoreConnection, retryAttempts int) error {
	if retryAttempts > 0 {
		err := connectInternal(connection)
		if err != nil {
			log.Printf("[error] reconnect attempt %v of %v failed: %v", (connection.Config.MaxReconnects-retryAttempts)+1, connection.Config.MaxReconnects, err.Error())
			time.Sleep(time.Duration(connection.Config.ReconnectionDelay) * time.Millisecond)
			return connectWithRetries(connection, retryAttempts-1)
		}
		return nil
	} else {
		connectionClosed(connection)
		return errors.New(fmt.Sprintf("failed to reconnect. Retry limit of %v reached.", connection.Config.MaxReconnects))
	}
}

func connectInternal(connection *EventStoreConnection) error {
	log.Printf("[info] connecting (id: %+v) to event store...\n", connection.ConnectionID)

	address := fmt.Sprintf("%s:%v", connection.Config.Address, connection.Config.Port)
	resolvedAddress, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return fmt.Errorf("failed to resolve tcp address %s\n", address)
	}
	conn, err := net.DialTCP("tcp", nil, resolvedAddress)
	if err != nil {
		return fmt.Errorf("failed to connect to event store on %+v. details: %s\n", address, err.Error())
	}
	log.Printf("[info] successfully connected to event store on %s (id: %+v)\n", address, connection.ConnectionID)
	connection.Socket = conn
	connection.connected = true

	go readFromSocket(connection)
	return nil
}

func connectionClosed(connection *EventStoreConnection) {
	log.Printf("[error] connection (id: %+v) closed\n", connection.ConnectionID)

	reason := protobuf.SubscriptionDropped_Unsubscribed
	subDropped := &protobuf.SubscriptionDropped{
		Reason: &reason,
	}
	data, err := proto.Marshal(subDropped)
	if err != nil {
		log.Fatal("[fatal] marshalling error: ", err)
	}

	for _, sub := range connection.subscriptions {
		pkg, err := newPackage(subscriptionDropped, data, sub.CorrelationID.Bytes(), connection.Config.Login, connection.Config.Password)
		if err != nil {
			log.Printf("[error] failed to drop subscription %v", sub.CorrelationID)
		}
		sub.Channel <- pkg
	}
	connection.requests = make(map[uuid.UUID]chan<- TCPPackage)
	connection.subscriptions = make(map[uuid.UUID]*Subscription)
}

func readFromSocket(connection *EventStoreConnection) {
	buffer := make([]byte, 40000)
	for {
		connection.Mutex.Lock()
		if connection.connected == false {
			break
		}
		connection.Mutex.Unlock()
		_, err := connection.Socket.Read(buffer)
		if err != nil {
			if connection.connected && err.Error() != "EOF" {
				log.Fatalf("[fatal] (id: %+v) failed to read with %+v\n", connection.ConnectionID, err.Error())
			}
			if err.Error() == "EOF" {
				connection.Close()
				err = connectWithRetries(connection, connection.Config.MaxReconnects)
				if err != nil {
					log.Printf("[error] (id: %+v) %s\n", connection.ConnectionID, err.Error())
				} else {
					log.Printf("[info] connection (id: %+v) reconnected\n", connection.ConnectionID)
				}
			}
			break
		}

		msg, err := parsePackage(buffer)
		if err != nil {
			log.Fatalf("[fatal] could not decode tcp package: %+v\n", err.Error())
		}
		switch msg.Command {
		case heartbeatRequest:
			pkg, err := newPackage(heartbeatResponse, nil, msg.CorrelationID, "", "")
			if err != nil {
				log.Printf("[error] failed to create new heartbeat response package\n")
			}
			channel := make(chan<- TCPPackage)
			go sendPackage(pkg, connection, channel)
			break
		case pong:
			pkg, err := newPackage(ping, nil, uuid.NewV4().Bytes(), "", "")
			if err != nil {
				log.Printf("[error] failed to create new ping response package")
			}
			channel := make(chan<- TCPPackage)
			go sendPackage(pkg, connection, channel)
			break
		case writeEventsCompleted, readEventCompleted, deleteStreamCompleted, readStreamEventsForwardCompleted, readStreamEventsBackwardCompleted, subscriptionConfirmation, streamEventAppeared, createPersistentSubscriptionCompleted, persistentSubscriptionConfirmation:
			correlationID, _ := uuid.FromBytes(msg.CorrelationID)
			if request, ok := connection.requests[correlationID]; ok {
				request <- msg
			}
			break
		case notAuthenticated:
			correlationID, _ := uuid.FromBytes(msg.CorrelationID)
			if request, ok := connection.requests[correlationID]; ok {
				request <- msg
			}
		case 0x0F:
			log.Fatal("[fatal] bad request sent")
			break
		}
	}
}

func sendPackage(pkg TCPPackage, connection *EventStoreConnection, channel chan<- TCPPackage) error {
	correlationID, _ := uuid.FromBytes(pkg.CorrelationID)
	connection.requests[correlationID] = channel
	err := pkg.write(connection)
	if err != nil {
		return err
	}
	return nil
}
<commit_msg>correct go hints<commit_after>package goes

import (
	"fmt"
	"log"
	"net"
	"time"

	"sync"

	"github.com/golang/protobuf/proto"
	"github.com/pgermishuys/goes/protobuf"
	"github.com/satori/go.uuid"
)

type Configuration struct {
	Address             string
	Port                int
	Login               string
	Password            string
	ReconnectionDelay   int
	MaxReconnects       int
	MaxOperationRetries int
}

type EventStoreConnection struct {
	Config        *Configuration
	Socket        *net.TCPConn
	connected     bool
	requests      map[uuid.UUID]chan<- TCPPackage
	subscriptions map[uuid.UUID]*Subscription
	ConnectionID  uuid.UUID
	Mutex         *sync.Mutex
}

// NewConfiguration creates a configuration with defualt settings
func NewConfiguration() *Configuration {
	return &Configuration{
		ReconnectionDelay:   100,
		MaxReconnects:       10,
		MaxOperationRetries: 10,
	}
}

// Connect attempts to connect to Event Store using the given configuration
func (connection *EventStoreConnection) Connect() error {
	connection.requests = make(map[uuid.UUID]chan<- TCPPackage)
	connection.subscriptions = make(map[uuid.UUID]*Subscription)

	return connectWithRetries(connection, connection.Config.MaxReconnects)
}

// Close attempts to close the connection to Event Store
func (connection *EventStoreConnection) Close() error {
	connection.Mutex.Lock()
	connection.connected = false
	connection.Mutex.Unlock()
	log.Printf("[info] closing the connection (id: %+v) to event store...\n'", connection.ConnectionID)
	err := connection.Socket.Close()
	connection.Socket = nil
	if err != nil {
		log.Printf("[error] failed closing the connection to event store...%+v\n'", err)
	}
	closeConnection(connection)
	return err
}

// NewConnection sets up a new Event Store Connection but does not open the connection
func NewEventStoreConnection(config *Configuration) (*EventStoreConnection, error) {
	if len(config.Address) == 0 {
		return nil, fmt.Errorf("The address (%v) cannot be an empty string", config.Address)
	}
	if config.Port <= 0 {
		return nil, fmt.Errorf("The port (%v) cannot be less or equal to 0", config.Port)
	}
	conn := &EventStoreConnection{
		Config:       config,
		ConnectionID: uuid.NewV4(),
		Mutex:        &sync.Mutex{},
	}
	log.Printf("[info] created new event store connection : %+v", conn)
	return conn, nil
}

func connectWithRetries(connection *EventStoreConnection, retryAttempts int) error {
	if retryAttempts > 0 {
		err := connect(connection)
		if err != nil {
			log.Printf("[error] reconnect attempt %v of %v failed: %v", (connection.Config.MaxReconnects-retryAttempts)+1, connection.Config.MaxReconnects, err.Error())
			time.Sleep(time.Duration(connection.Config.ReconnectionDelay) * time.Millisecond)
			return connectWithRetries(connection, retryAttempts-1)
		}
		return nil
	}
	closeConnection(connection)
	return fmt.Errorf("failed to reconnect. Retry limit of %v reached", connection.Config.MaxReconnects)
}

func connect(connection *EventStoreConnection) error {
	log.Printf("[info] connecting (id: %+v) to event store...\n", connection.ConnectionID)

	address := fmt.Sprintf("%s:%v", connection.Config.Address, connection.Config.Port)
	resolvedAddress, err := net.ResolveTCPAddr("tcp", address)
	if err != nil {
		return fmt.Errorf("failed to resolve tcp address %s\n", address)
	}
	conn, err := net.DialTCP("tcp", nil, resolvedAddress)
	if err != nil {
		return fmt.Errorf("failed to connect to event store on %+v. details: %s\n", address, err.Error())
	}
	log.Printf("[info] successfully connected to event store on %s (id: %+v)\n", address, connection.ConnectionID)
	connection.Socket = conn
	connection.connected = true

	go readFromSocket(connection)
	return nil
}

func closeConnection(connection *EventStoreConnection) {
	log.Printf("[error] connection (id: %+v) closed\n", connection.ConnectionID)

	reason := protobuf.SubscriptionDropped_Unsubscribed
	subDropped := &protobuf.SubscriptionDropped{
		Reason: &reason,
	}
	data, err := proto.Marshal(subDropped)
	if err != nil {
		log.Fatal("[fatal] marshalling error: ", err)
	}

	for _, sub := range connection.subscriptions {
		pkg, err := newPackage(subscriptionDropped, data, sub.CorrelationID.Bytes(), connection.Config.Login, connection.Config.Password)
		if err != nil {
			log.Printf("[error] failed to drop subscription %v", sub.CorrelationID)
		}
		sub.Channel <- pkg
	}
	connection.requests = make(map[uuid.UUID]chan<- TCPPackage)
	connection.subscriptions = make(map[uuid.UUID]*Subscription)
}

func readFromSocket(connection *EventStoreConnection) {
	buffer := make([]byte, 40000)
	for {
		connection.Mutex.Lock()
		if connection.connected == false {
			break
		}
		connection.Mutex.Unlock()
		_, err := connection.Socket.Read(buffer)
		if err != nil {
			if connection.connected && err.Error() != "EOF" {
				log.Fatalf("[fatal] (id: %+v) failed to read with %+v\n", connection.ConnectionID, err.Error())
			}
			if err.Error() == "EOF" {
				connection.Close()
				err = connectWithRetries(connection, connection.Config.MaxReconnects)
				if err != nil {
					log.Printf("[error] (id: %+v) %s\n", connection.ConnectionID, err.Error())
				} else {
					log.Printf("[info] connection (id: %+v) reconnected\n", connection.ConnectionID)
				}
			}
			break
		}

		msg, err := parsePackage(buffer)
		if err != nil {
			log.Fatalf("[fatal] could not decode tcp package: %+v\n", err.Error())
		}
		switch msg.Command {
		case heartbeatRequest:
			pkg, err := newPackage(heartbeatResponse, nil, msg.CorrelationID, "", "")
			if err != nil {
				log.Printf("[error] failed to create new heartbeat response package\n")
			}
			channel := make(chan<- TCPPackage)
			go sendPackage(pkg, connection, channel)
			break
		case pong:
			pkg, err := newPackage(ping, nil, uuid.NewV4().Bytes(), "", "")
			if err != nil {
				log.Printf("[error] failed to create new ping response package")
			}
			channel := make(chan<- TCPPackage)
			go sendPackage(pkg, connection, channel)
			break
		case writeEventsCompleted, readEventCompleted, deleteStreamCompleted, readStreamEventsForwardCompleted, readStreamEventsBackwardCompleted, subscriptionConfirmation, streamEventAppeared, createPersistentSubscriptionCompleted, persistentSubscriptionConfirmation:
			correlationID, _ := uuid.FromBytes(msg.CorrelationID)
			if request, ok := connection.requests[correlationID]; ok {
				request <- msg
			}
			break
		case notAuthenticated:
			correlationID, _ := uuid.FromBytes(msg.CorrelationID)
			if request, ok := connection.requests[correlationID]; ok {
				request <- msg
			}
		case 0x0F:
			log.Fatal("[fatal] bad request sent")
			break
		}
	}
}

func sendPackage(pkg TCPPackage, connection *EventStoreConnection, channel chan<- TCPPackage) error {
	correlationID, _ := uuid.FromBytes(pkg.CorrelationID)
	connection.requests[correlationID] = channel
	err := pkg.write(connection)
	if err != nil {
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package main

import "fmt"
import "log"
import _ "errors"

import "encoding/json"
import "io/ioutil"
import "net/http"
import "net/url"
import "database/sql"
import _ "github.com/mattn/go-sqlite3"

//import _ "github.com/jmoiron/sqlx"

// `q` is the YQL query
func yql(jrsp interface{}, q string) (err error) {
	// form the YQL URL:
	u := `http://query.yahooapis.com/v1/public/yql?q=` + url.QueryEscape(q) + `&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`
	resp, err := http.Get(u)
	if err != nil {
		return
	}

	// read body:
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	// Need a 200 response
	if resp.StatusCode != 200 {
		err = fmt.Errorf("%s", resp.Status)
		return
	}

	// print JSON to console:
	//fmt.Printf("%s\n\n", body)

	// decode JSON:
	err = json.Unmarshal(body, jrsp)
	return
}

// Head to http://developer.yahoo.com/yql/console/?q=select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22YHOO%22%2C%22AAPL%22%2C%22GOOG%22%2C%22MSFT%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys
// to understand this JSON structure.

type Quote struct {
	Symbol             string "symbol"
	LastTradePriceOnly string
}

type QuoteResponse struct {
	Query *struct {
		Results *struct {
			Quote *Quote "quote"
		} "results"
	} "query"
}

type Historical struct {
	Date  string
	Close string
}

type HistoricalResponse struct {
	Query *struct {
		Results *struct {
			Quote []*Historical "quote"
		} "results"
	} "query"
}

func db_create_schema() (db *sql.DB, err error) {
	// using sqlite 3.8.0 release
	db, err = sql.Open("sqlite3", "stocks.db")
	if err != nil {
		db.Close()
		return
	}

	_, err = db.Exec(`
create table if not exists stock (
	symbol TEXT UNIQUE NOT NULL,
	purchase_price TEXT NOT NULL,
	purchase_date TEXT NOT NULL,
	purchaser_email TEXT NOT NULL,
	trailing_stop_percent TEXT NOT NULL,
	last_stop_price TEXT
)`)
	if err != nil {
		db.Close()
		return
	}

	_, err = db.Exec(`
create table if not exists stock_history (
	symbol TEXT NOT NULL,
	date TEXT NOT NULL,
	closing_price TEXT NOT NULL
)`)
	if err != nil {
		db.Close()
		return
	}

	// Add some test data:
	_, err = db.Exec(`
insert into stock  (symbol, purchase_price, purchase_date, purchaser_email, trailing_stop_percent, last_stop_price)
 			values ('MSFT', '40.00', '2013-12-01', 'email@example.org', '20.00', NULL)
`)
	// ignore non-unique symbol error
	err = nil

	return
}

type Stock struct {
	Symbol              string         `db:"symbol"`
	PurchasePrice       string         `db:"purchase_price"`
	PurchaseDate        string         `db:"purchase_date"`
	PurchaserEmail      string         `db:"purchaser_email"`
	TrailingStopPercent string         `db:"trailing_stop_percent"`
	LastStopPrice       sql.NullString `db:"last_stop_price"`
}

// main:
func main() {
	// Create our DB schema:
	db, err := db_create_schema()
	if err != nil {
		log.Fatal(err)
		return
	}
	defer db.Close()

	// Query stocks table:
	stocks := make([]*Stock, 0, 4) // make(type, len, capacity)
	rows, err := db.Query(`select symbol, purchase_price, purchase_date, purchaser_email, trailing_stop_percent, last_stop_price from stock`)
	if err != nil {
		log.Fatal(err)
		return
	}
	// Read rows:
	{
		defer rows.Close()
		for rows.Next() {
			st := new(Stock)
			err = rows.Scan(&st.Symbol, &st.PurchasePrice, &st.PurchaseDate, &st.PurchaserEmail, &st.TrailingStopPercent, &st.LastStopPrice)
			if err != nil {
				log.Fatal(err)
				return
			}
			stocks = append(stocks, st)
		}
	}
	fmt.Printf("%#v\n", *stocks[0])

	// get current price of MSFT:
	quot := new(QuoteResponse)
	err = yql(quot, `select symbol, LastTradePriceOnly from yahoo.finance.quote where symbol in ("MSFT")`)
	if err != nil {
		log.Fatal(err)
		return
	}

	fmt.Printf("%s\n", *quot.Query.Results.Quote)

	// get historical data for MSFT:
	hist := new(HistoricalResponse)
	err = yql(hist, `select Date, Close from yahoo.finance.historicaldata where symbol = "MSFT" and startDate = "2013-12-04" and endDate = "2013-12-06"`)
	if err != nil {
		log.Fatal(err)
		return
	}

	fmt.Printf("%s %s %s\n", *hist.Query.Results.Quote[0], *hist.Query.Results.Quote[1], *hist.Query.Results.Quote[2])
	return
}
<commit_msg>Using sqlx package now to simplify sql row to object mapping.<commit_after>package main

import "fmt"
import "log"
import _ "errors"

import "encoding/json"
import "io/ioutil"
import "net/http"
import "net/url"

import "database/sql"
import _ "github.com/mattn/go-sqlite3"
import "github.com/jmoiron/sqlx"

// `q` is the YQL query
func yql(jrsp interface{}, q string) (err error) {
	// form the YQL URL:
	u := `http://query.yahooapis.com/v1/public/yql?q=` + url.QueryEscape(q) + `&format=json&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys`
	resp, err := http.Get(u)
	if err != nil {
		return
	}

	// read body:
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return
	}

	// Need a 200 response
	if resp.StatusCode != 200 {
		err = fmt.Errorf("%s", resp.Status)
		return
	}

	// print JSON to console:
	//fmt.Printf("%s\n\n", body)

	// decode JSON:
	err = json.Unmarshal(body, jrsp)
	return
}

// Head to http://developer.yahoo.com/yql/console/?q=select%20*%20from%20yahoo.finance.quote%20where%20symbol%20in%20(%22YHOO%22%2C%22AAPL%22%2C%22GOOG%22%2C%22MSFT%22)&env=store%3A%2F%2Fdatatables.org%2Falltableswithkeys
// to understand this JSON structure.

type Quote struct {
	Symbol             string "symbol"
	LastTradePriceOnly string
}

type QuoteResponse struct {
	Query *struct {
		Results *struct {
			Quote *Quote "quote"
		} "results"
	} "query"
}

type Historical struct {
	Date  string
	Close string
}

type HistoricalResponse struct {
	Query *struct {
		Results *struct {
			Quote []*Historical "quote"
		} "results"
	} "query"
}

func db_create_schema() (db *sqlx.DB, err error) {
	// using sqlite 3.8.0 release
	db, err = sqlx.Connect("sqlite3", "stocks.db")
	if err != nil {
		db.Close()
		return
	}

	_, err = db.Exec(`
create table if not exists stock (
	symbol TEXT UNIQUE NOT NULL,
	purchase_price TEXT NOT NULL,
	purchase_date TEXT NOT NULL,
	purchaser_email TEXT NOT NULL,
	trailing_stop_percent TEXT NOT NULL,
	last_stop_price TEXT
)`)
	if err != nil {
		db.Close()
		return
	}

	_, err = db.Exec(`
create table if not exists stock_history (
	symbol TEXT NOT NULL,
	date TEXT NOT NULL,
	closing_price TEXT NOT NULL
)`)
	if err != nil {
		db.Close()
		return
	}

	// Add some test data:
	_, err = db.Exec(`
insert into stock  (symbol, purchase_price, purchase_date, purchaser_email, trailing_stop_percent, last_stop_price)
 			values ('MSFT', '40.00', '2013-12-01', 'email@example.org', '20.00', NULL)
`)
	// ignore non-unique symbol error
	err = nil

	return
}

type Stock struct {
	Symbol              string         `db:"symbol"`
	PurchasePrice       string         `db:"purchase_price"`
	PurchaseDate        string         `db:"purchase_date"`
	PurchaserEmail      string         `db:"purchaser_email"`
	TrailingStopPercent string         `db:"trailing_stop_percent"`
	LastStopPrice       sql.NullString `db:"last_stop_price"`
}

// main:
func main() {
	// Create our DB schema:
	db, err := db_create_schema()
	if err != nil {
		log.Fatal(err)
		return
	}
	defer db.Close()

	// Query stocks table:
	stocks := make([]*Stock, 0, 4) // make(type, len, capacity)
	err = db.Select(&stocks, `select symbol, purchase_price, purchase_date, purchaser_email, trailing_stop_percent, last_stop_price from stock`)
	fmt.Printf("%#v\n", *stocks[0])

	// get current price of MSFT:
	quot := new(QuoteResponse)
	err = yql(quot, `select symbol, LastTradePriceOnly from yahoo.finance.quote where symbol in ("MSFT")`)
	if err != nil {
		log.Fatal(err)
		return
	}

	fmt.Printf("%s\n", *quot.Query.Results.Quote)

	// get historical data for MSFT:
	hist := new(HistoricalResponse)
	err = yql(hist, `select Date, Close from yahoo.finance.historicaldata where symbol = "MSFT" and startDate = "2013-12-04" and endDate = "2013-12-06"`)
	if err != nil {
		log.Fatal(err)
		return
	}

	fmt.Printf("%s %s %s\n", *hist.Query.Results.Quote[0], *hist.Query.Results.Quote[1], *hist.Query.Results.Quote[2])
	return
}
<|endoftext|>


<commit_before>// Copyright 2015 Hajime Hoshi
//
// 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 main

import (
	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	"log"
	"sort"
	"strings"
)

const (
	screenWidth  = 320
	screenHeight = 240
)

// TODO: Add Key.String() by stringer

var keyNames = map[ebiten.Key]string{
	ebiten.KeyBackspace: "Backspace",
	ebiten.KeyComma:     "','",
	ebiten.KeyDelete:    "Delete",
	ebiten.KeyEnter:     "Enter",
	ebiten.KeyEscape:    "Escape",
	ebiten.KeyPeriod:    "'.'",
	ebiten.KeySpace:     "Space",
	ebiten.KeyTab:       "Tab",

	// Arrows
	ebiten.KeyDown:  "Down",
	ebiten.KeyLeft:  "Left",
	ebiten.KeyRight: "Right",
	ebiten.KeyUp:    "Up",

	// Mods
	ebiten.KeyLeftShift:   "Shift",
	ebiten.KeyLeftControl: "Ctrl",
	ebiten.KeyLeftAlt:     "Alt",
}

func update(screen *ebiten.Image) error {
	pressed := []string{}
	for i := 0; i <= 9; i++ {
		if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.Key0) {
			pressed = append(pressed, string(i+'0'))
		}
	}
	for c := 'A'; c <= 'Z'; c++ {
		if ebiten.IsKeyPressed(ebiten.Key(c) - 'A' + ebiten.KeyA) {
			pressed = append(pressed, string(c))
		}
	}
	for key, name := range keyNames {
		if ebiten.IsKeyPressed(key) {
			pressed = append(pressed, name)
		}
	}
	sort.Strings(pressed)
	str := "Pressed Keys: " + strings.Join(pressed, ", ")
	ebitenutil.DebugPrint(screen, str)
	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Keyboard (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>Add function keys to example/keyboard<commit_after>// Copyright 2015 Hajime Hoshi
//
// 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 main

import (
	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
	"log"
	"sort"
	"strconv"
	"strings"
)

const (
	screenWidth  = 320
	screenHeight = 240
)

// TODO: Add Key.String() by stringer

var keyNames = map[ebiten.Key]string{
	ebiten.KeyBackspace: "Backspace",
	ebiten.KeyComma:     "','",
	ebiten.KeyDelete:    "Delete",
	ebiten.KeyEnter:     "Enter",
	ebiten.KeyEscape:    "Escape",
	ebiten.KeyPeriod:    "'.'",
	ebiten.KeySpace:     "Space",
	ebiten.KeyTab:       "Tab",

	// Arrows
	ebiten.KeyDown:  "Down",
	ebiten.KeyLeft:  "Left",
	ebiten.KeyRight: "Right",
	ebiten.KeyUp:    "Up",

	// Mods
	ebiten.KeyLeftShift:   "Shift",
	ebiten.KeyLeftControl: "Ctrl",
	ebiten.KeyLeftAlt:     "Alt",
}

func update(screen *ebiten.Image) error {
	pressed := []string{}
	for i := 0; i <= 9; i++ {
		if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.Key0) {
			pressed = append(pressed, string(i+'0'))
		}
	}
	for c := 'A'; c <= 'Z'; c++ {
		if ebiten.IsKeyPressed(ebiten.Key(c) - 'A' + ebiten.KeyA) {
			pressed = append(pressed, string(c))
		}
	}
	for i := 1; i <= 12; i++ {
		if ebiten.IsKeyPressed(ebiten.Key(i) + ebiten.KeyF1 - 1) {
			pressed = append(pressed, "F"+strconv.Itoa(i))
		}
	}
	for key, name := range keyNames {
		if ebiten.IsKeyPressed(key) {
			pressed = append(pressed, name)
		}
	}
	sort.Strings(pressed)
	str := "Pressed Keys: " + strings.Join(pressed, ", ")
	ebitenutil.DebugPrint(screen, str)
	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Keyboard (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package sdees

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"
)

type Config struct {
	Remote, Editor, CurrentDocument string
}

func SetupConfig() {
	var configParameters Config

	var yesno string
	err := errors.New("Incorrect remote")
	for {
		fmt.Print("Enter remote (e.g.: git@github.com:USER/REPO.git): ")
		fmt.Scanln(&yesno)
		cwd, _ := os.Getwd()
		os.Chdir(CachePath)
		os.RemoveAll(HashString(yesno))
		cmd := exec.Command("git", "clone", yesno, HashString(yesno))
		_, err := cmd.Output()
		os.Chdir(cwd)
		if err != nil {
			fmt.Println("Could not clone, please re-enter")
		} else {
			break
		}
	}
	configParameters.Remote = yesno

	fmt.Printf("Which editor do you want to use: vim (default), nano, or emacs? ")
	fmt.Scanln(&yesno)
	if strings.TrimSpace(strings.ToLower(yesno)) == "nano" {
		configParameters.Editor = "nano"
	} else if strings.TrimSpace(strings.ToLower(yesno)) == "emacs" {
		configParameters.Editor = "emacs"
	} else {
		configParameters.Editor = "vim"
	}
	configParameters.CurrentDocument = ""

	b, err := json.Marshal(configParameters)
	if err != nil {
		log.Println(err)
	}
	ioutil.WriteFile(path.Join(ConfigPath, "config.json"), b, 0644)
}

func LoadConfiguration() {
	defer timeTrack(time.Now(), "Loaded and saved configuration")
	var c Config
	data, err := ioutil.ReadFile(path.Join(ConfigPath, "config.json"))
	if err != nil {
		logger.Error("Could not load config.json")
		return
	}
	json.Unmarshal(data, &c)
	if len(CurrentDocument) == 0 {
		CurrentDocument = c.CurrentDocument
	}
	Editor = c.Editor
	Remote = c.Remote
	RemoteFolder = path.Join(CachePath, HashString(Remote))
	if len(Remote) == 0 {
		SetupConfig()
	}
}

func SaveConfiguration(editor string, remote string, currentdoc string) {
	defer timeTrack(time.Now(), "Saved configuration")
	c := Config{Editor: editor, Remote: remote, CurrentDocument: currentdoc}
	b, err := json.Marshal(c)
	if err != nil {
		log.Println(err)
	}
	ioutil.WriteFile(path.Join(ConfigPath, "config.json"), b, 0644)
}
<commit_msg>Added more debugging<commit_after>package sdees

import (
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path"
	"strings"
	"time"
)

type Config struct {
	Remote, Editor, CurrentDocument string
}

func SetupConfig() {
	var configParameters Config

	var yesno string
	err := errors.New("Incorrect remote")
	for {
		fmt.Print("Enter remote (e.g.: git@github.com:USER/REPO.git): ")
		fmt.Scanln(&yesno)
		cwd, _ := os.Getwd()
		os.Chdir(CachePath)
		os.RemoveAll(HashString(yesno))
		cmd := exec.Command("git", "clone", yesno, HashString(yesno))
		_, err := cmd.Output()
		os.Chdir(cwd)
		if err != nil {
			logger.Debug("Tried command '%s' in path %s", strings.Join([]string{"git", "clone", yesno, HashString(yesno)}, " "), CachePath)
			logger.Debug("which resulted in error: %s", err.Error())
			fmt.Println("Could not clone, please re-enter")
		} else {
			break
		}
	}
	configParameters.Remote = yesno

	fmt.Printf("Which editor do you want to use: vim (default), nano, or emacs? ")
	fmt.Scanln(&yesno)
	if strings.TrimSpace(strings.ToLower(yesno)) == "nano" {
		configParameters.Editor = "nano"
	} else if strings.TrimSpace(strings.ToLower(yesno)) == "emacs" {
		configParameters.Editor = "emacs"
	} else {
		configParameters.Editor = "vim"
	}
	configParameters.CurrentDocument = ""

	b, err := json.Marshal(configParameters)
	if err != nil {
		log.Println(err)
	}
	ioutil.WriteFile(path.Join(ConfigPath, "config.json"), b, 0644)
}

func LoadConfiguration() {
	defer timeTrack(time.Now(), "Loaded and saved configuration")
	var c Config
	data, err := ioutil.ReadFile(path.Join(ConfigPath, "config.json"))
	if err != nil {
		logger.Error("Could not load config.json")
		return
	}
	json.Unmarshal(data, &c)
	if len(CurrentDocument) == 0 {
		CurrentDocument = c.CurrentDocument
	}
	Editor = c.Editor
	Remote = c.Remote
	RemoteFolder = path.Join(CachePath, HashString(Remote))
	if len(Remote) == 0 {
		SetupConfig()
	}
}

func SaveConfiguration(editor string, remote string, currentdoc string) {
	defer timeTrack(time.Now(), "Saved configuration")
	c := Config{Editor: editor, Remote: remote, CurrentDocument: currentdoc}
	b, err := json.Marshal(c)
	if err != nil {
		log.Println(err)
	}
	ioutil.WriteFile(path.Join(ConfigPath, "config.json"), b, 0644)
}
<|endoftext|>


<commit_before>package httpcontrol_test

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/daaku/go.httpcontrol"
)

var theAnswer = []byte("42")

func sleepHandler(timeout time.Duration) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(timeout)
			w.Write(theAnswer)
		})
}

func errorHandler(timeout time.Duration) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(timeout)
			w.WriteHeader(500)
			w.Write(theAnswer)
		})
}

func assertResponse(req *http.Response, t *testing.T) {
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(b, theAnswer) {
		t.Fatalf(`did not find expected bytes "%s" instead found "%s"`, theAnswer, b)
	}
}

func call(f func() error, t *testing.T) {
	if err := f(); err != nil {
		t.Fatal(err)
	}
}

func TestOkWithDefaults(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(sleepHandler(time.Millisecond))
	defer server.Close()
	transport := &httpcontrol.Transport{}
	call(transport.Start, t)
	defer call(transport.Close, t)
	client := &http.Client{Transport: transport}
	res, err := client.Get(server.URL)
	if err != nil {
		t.Fatal(err)
	}
	assertResponse(res, t)
}

func TestHttpError(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(errorHandler(time.Millisecond))
	defer server.Close()
	transport := &httpcontrol.Transport{}
	call(transport.Start, t)
	defer call(transport.Close, t)
	client := &http.Client{Transport: transport}
	res, err := client.Get(server.URL)
	if err != nil {
		t.Fatal(err)
	}
	assertResponse(res, t)
	if res.StatusCode != 500 {
		t.Fatal("was expecting 500 got %d", res.StatusCode)
	}
}

func TestDialTimeout(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(sleepHandler(time.Millisecond))
	server.Close()
	transport := &httpcontrol.Transport{}
	call(transport.Start, t)
	defer call(transport.Close, t)
	client := &http.Client{Transport: transport}
	res, err := client.Get(server.URL)
	if err == nil {
		t.Fatal("was expecting an error")
	}
	if res != nil {
		t.Fatal("was expecting nil response")
	}
	if !strings.Contains(err.Error(), "dial") {
		t.Fatal("was expecting dial related error")
	}
}

func TestResponseHeaderTimeout(t *testing.T) {
	t.Parallel()
}

func TestResponseTimeout(t *testing.T) {
	t.Parallel()
}

func TestSafeRetry(t *testing.T) {
	t.Parallel()
}

func TestUnsafeRetry(t *testing.T) {
	t.Parallel()
}

func TestRedirect(t *testing.T) {
	t.Parallel()
}
<commit_msg>test stats information<commit_after>package httpcontrol_test

import (
	"bytes"
	"io/ioutil"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"
	"time"

	"github.com/daaku/go.httpcontrol"
)

var theAnswer = []byte("42")

func sleepHandler(timeout time.Duration) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(timeout)
			w.Write(theAnswer)
		})
}

func errorHandler(timeout time.Duration) http.Handler {
	return http.HandlerFunc(
		func(w http.ResponseWriter, r *http.Request) {
			time.Sleep(timeout)
			w.WriteHeader(500)
			w.Write(theAnswer)
		})
}

func assertResponse(req *http.Response, t *testing.T) {
	b, err := ioutil.ReadAll(req.Body)
	if err != nil {
		t.Fatal(err)
	}
	if !bytes.Equal(b, theAnswer) {
		t.Fatalf(`did not find expected bytes "%s" instead found "%s"`, theAnswer, b)
	}
}

func call(f func() error, t *testing.T) {
	if err := f(); err != nil {
		t.Fatal(err)
	}
}

func TestOkWithDefaults(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(sleepHandler(time.Millisecond))
	defer server.Close()
	transport := &httpcontrol.Transport{}
	transport.Stats = func(stats *httpcontrol.Stats) {
		if stats.Error != nil {
			t.Fatal(stats.Error)
		}
		if stats.Request == nil {
			t.Fatal("got nil request in stats")
		}
		if stats.Response == nil {
			t.Fatal("got nil response in stats")
		}
		if stats.Retry.Count != 0 {
			t.Fatal("was expecting retry count of 0")
		}
		if stats.Retry.Pending {
			t.Fatal("was expecting no retry pending")
		}
	}
	call(transport.Start, t)
	defer call(transport.Close, t)
	client := &http.Client{Transport: transport}
	res, err := client.Get(server.URL)
	if err != nil {
		t.Fatal(err)
	}
	assertResponse(res, t)
}

func TestHttpError(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(errorHandler(time.Millisecond))
	defer server.Close()
	transport := &httpcontrol.Transport{}
	transport.Stats = func(stats *httpcontrol.Stats) {
		if stats.Error != nil {
			t.Fatal(stats.Error)
		}
	}
	call(transport.Start, t)
	defer call(transport.Close, t)
	client := &http.Client{Transport: transport}
	res, err := client.Get(server.URL)
	if err != nil {
		t.Fatal(err)
	}
	assertResponse(res, t)
	if res.StatusCode != 500 {
		t.Fatal("was expecting 500 got %d", res.StatusCode)
	}
}

func TestDialTimeout(t *testing.T) {
	t.Parallel()
	server := httptest.NewServer(sleepHandler(time.Millisecond))
	server.Close()
	transport := &httpcontrol.Transport{}
	transport.Stats = func(stats *httpcontrol.Stats) {
		if stats.Error == nil {
			t.Fatal("was expecting error")
		}
	}
	call(transport.Start, t)
	defer call(transport.Close, t)
	client := &http.Client{Transport: transport}
	res, err := client.Get(server.URL)
	if err == nil {
		t.Fatal("was expecting an error")
	}
	if res != nil {
		t.Fatal("was expecting nil response")
	}
	if !strings.Contains(err.Error(), "dial") {
		t.Fatal("was expecting dial related error")
	}
}

func TestResponseHeaderTimeout(t *testing.T) {
	t.Parallel()
}

func TestResponseTimeout(t *testing.T) {
	t.Parallel()
}

func TestSafeRetry(t *testing.T) {
	t.Parallel()
}

func TestUnsafeRetry(t *testing.T) {
	t.Parallel()
}

func TestRedirect(t *testing.T) {
	t.Parallel()
}
<|endoftext|>


<commit_before>package boilerplate

import (
	"archive/zip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/aisk/logp"
	"github.com/cheggaaa/pb"
	"github.com/fatih/color"
	"github.com/leancloud/lean-cli/api/regions"
	"github.com/leancloud/lean-cli/utils"
	"github.com/leancloud/lean-cli/version"
	"github.com/levigross/grequests"
	"github.com/mattn/go-colorable"
)

func CreateProject(boil *Boilerplate, dest string, appID string, region regions.Region) error {
	if boil.DownloadURL != "" {
		if err := os.Mkdir(dest, 0775); err != nil {
			return err
		}

		dir, err := ioutil.TempDir("", "leanengine")
		if err != nil {
			return err
		}
		defer os.RemoveAll(dir)
		zipFilePath := filepath.Join(dir, "getting-started.zip")

		var downloadURLs []string
		if region.InChina() {
			downloadURLs = []string{"https://releases.leanapp.cn", "https://api.github.com/repos"}
		} else {
			downloadURLs = []string{"https://api.github.com/repos", "https://releases.leanapp.cn"}
		}
		err = downloadToFile(downloadURLs[0]+boil.DownloadURL, zipFilePath)
		if err != nil {
			err = downloadToFile(downloadURLs[1]+boil.DownloadURL, zipFilePath)
			if err != nil {
				return err
			}
		}

		logp.Info("Creating project...")

		zipFile, err := zip.OpenReader(zipFilePath)
		if err != nil {
			return err
		}
		defer zipFile.Close()
		for _, f := range zipFile.File {
			// Remove outer directory name.
			f.Name = f.Name[strings.Index(f.Name, "/"):]
			err := extractAndWriteFile(f, dest)
			if err != nil {
				return err
			}
		}
	}

	if boil.CMD != nil {
		args := boil.CMD(dest)

		_, err := exec.LookPath(args[0])

		if err != nil {
			return fmt.Errorf("You should install `%s` before create %s project", args[0], boil.Name)
		}

		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			return err
		}
	}

	if boil.Files != nil {
		for name, body := range boil.Files {
			if err := ioutil.WriteFile(filepath.Join(dest, name), []byte(body), 0644); err != nil {
				return err
			}
		}
	}

	logp.Info(fmt.Printf("Created %s project in `%s`", boil.Name, dest))

	if boil.Message != "" {
		logp.Info(boil.Message)
	}

	return nil
}

type Boilerplate struct {
	Name        string
	Message     string
	DownloadURL string
	CMD         func(dest string) []string
	Files       map[string]string
}

var Boilerplates = []Boilerplate{
	{
		Name:        "Node.js - Express",
		Message:     "Lean how to use Express at https://expressjs.com",
		DownloadURL: "/leancloud/node-js-getting-started/zipball/master",
	},
	{
		Name:        "Node.js - Koa",
		DownloadURL: "/leancloud/koa-getting-started/zipball/master",
		Message:     "Lean how to use Koa at https://koajs.com",
	},
	{
		Name:        "Python - Flask",
		DownloadURL: "/leancloud/python-getting-started/zipball/master",
		Message:     "Lean how to use Flask at https://flask.palletsprojects.com",
	},
	{
		Name:        "Python - Django",
		DownloadURL: "/leancloud/django-getting-started/zipball/master",
		Message:     "Lean how to use Django at https://docs.djangoproject.com",
	},
	{
		Name:        "Java - Serlvet",
		DownloadURL: "/leancloud/servlet-getting-started/zipball/master",
	},
	{
		Name:        "Java - Spring Boot",
		DownloadURL: "/leancloud/spring-boot-getting-started/zipball/master",
		Message:     "Lean how to use Spring Boot at https://spring.io/projects/spring-boot",
	},
	{
		Name:        "PHP - Slim",
		DownloadURL: "/leancloud/slim-getting-started/zipball/master",
		Message:     "Lean how to use Slim at https://www.slimframework.com",
	},
	{
		Name:        ".NET Core",
		DownloadURL: "/leancloud/dotnet-core-getting-started/zipball/master",
		Message:     "Lean how to use .NET Core at https://docs.microsoft.com/aspnet/core/",
	},
	{
		Name:        "Go - Echo",
		DownloadURL: "/leancloud/golang-getting-started/zipball/master",
		Message:     "Lean how to use Echo at https://echo.labstack.com/",
	},
	{
		Name:  "React Web App (via create-react-app)",
		Files: prepareWebAppFiles("build"),
		CMD: func(dest string) []string {
			return []string{"npx", "create-react-app", dest, "--use-npm"}
		},
	},
	{
		Name:  "Vue Web App (via @vue/cli)",
		Files: prepareWebAppFiles("dist"),
		CMD: func(dest string) []string {
			return []string{"npx", "@vue/cli", "create", "--default", "--packageManager", "npm", dest}
		},
	},
}

// don't know why archive/zip.Reader.File[0].FileInfo().IsDir() always return true,
// this is a trick hack to void this.
func isDir(path string) bool {
	return os.IsPathSeparator(path[len(path)-1])
}

func extractAndWriteFile(f *zip.File, dest string) error {
	rc, err := f.Open()
	if err != nil {
		return err
	}
	defer rc.Close()

	path := filepath.Join(dest, f.Name)

	if isDir(f.Name) {
		if err := os.MkdirAll(path, f.Mode()); err != nil {
			return err
		}
	} else {
		// Use os.Create() since Zip don't store file permissions.
		f, err := os.Create(path)
		if err != nil {
			return err
		}
		defer f.Close()

		_, err = io.Copy(f, rc)
		if err != nil {
			return err
		}
	}
	return nil
}

// downloadToFile allows you to download the contents of the URL to a file
func downloadToFile(url string, fileName string) error {
	resp, err := grequests.Get(url, &grequests.RequestOptions{
		UserAgent: "LeanCloud-CLI/" + version.Version,
	})
	if err != nil {
		return err
	}
	defer resp.Close()

	if resp.StatusCode != 200 {
		return errors.New(utils.FormatServerErrorResult(resp.String()))
	}
	if resp.Error != nil {
		return resp.Error
	}

	fd, err := os.Create(fileName)

	if err != nil {
		return err
	}

	defer resp.Close() // This is a noop if we use the internal ByteBuffer
	defer fd.Close()

	if length, err := strconv.Atoi(resp.Header.Get("Content-Length")); err == nil {
		bar := pb.New(length).SetUnits(pb.U_BYTES).SetMaxWidth(80)
		bar.Output = colorable.NewColorableStderr()
		bar.Prefix(color.GreenString("[INFO]") + " Downloading templates")
		bar.Start()
		defer bar.Finish()
		reader := bar.NewProxyReader(resp)
		if _, err := io.Copy(fd, reader); err != nil && err != io.EOF {
			return err
		}
	} else {
		if _, err := io.Copy(fd, resp); err != nil && err != io.EOF {
			return err
		}
	}

	return nil
}

func prepareWebAppFiles(webRoot string) map[string]string {
	return map[string]string{
		"leanengine.yaml": "build: npm run build",
		"static.json": fmt.Sprintf(`{
  "public": "%s",
  "rewrites": [
    { "source": "**", "destination": "/index.html" }
  ]
}`, webRoot),
	}
}
<commit_msg>:loud_sound: Print the command executed<commit_after>package boilerplate

import (
	"archive/zip"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"strconv"
	"strings"

	"github.com/aisk/logp"
	"github.com/cheggaaa/pb"
	"github.com/fatih/color"
	"github.com/leancloud/lean-cli/api/regions"
	"github.com/leancloud/lean-cli/utils"
	"github.com/leancloud/lean-cli/version"
	"github.com/levigross/grequests"
	"github.com/mattn/go-colorable"
)

func CreateProject(boil *Boilerplate, dest string, appID string, region regions.Region) error {
	if boil.DownloadURL != "" {
		if err := os.Mkdir(dest, 0775); err != nil {
			return err
		}

		dir, err := ioutil.TempDir("", "leanengine")
		if err != nil {
			return err
		}
		defer os.RemoveAll(dir)
		zipFilePath := filepath.Join(dir, "getting-started.zip")

		var downloadURLs []string
		if region.InChina() {
			downloadURLs = []string{"https://releases.leanapp.cn", "https://api.github.com/repos"}
		} else {
			downloadURLs = []string{"https://api.github.com/repos", "https://releases.leanapp.cn"}
		}
		err = downloadToFile(downloadURLs[0]+boil.DownloadURL, zipFilePath)
		if err != nil {
			err = downloadToFile(downloadURLs[1]+boil.DownloadURL, zipFilePath)
			if err != nil {
				return err
			}
		}

		logp.Info("Creating project...")

		zipFile, err := zip.OpenReader(zipFilePath)
		if err != nil {
			return err
		}
		defer zipFile.Close()
		for _, f := range zipFile.File {
			// Remove outer directory name.
			f.Name = f.Name[strings.Index(f.Name, "/"):]
			err := extractAndWriteFile(f, dest)
			if err != nil {
				return err
			}
		}
	}

	if boil.CMD != nil {
		args := boil.CMD(dest)

		logp.Info(fmt.Sprintf("Executing `%s`", strings.Join(args, " ")))

		_, err := exec.LookPath(args[0])

		if err != nil {
			return fmt.Errorf("You should install `%s` before create %s project", args[0], boil.Name)
		}

		cmd := exec.Command(args[0], args[1:]...)
		cmd.Stdin = os.Stdin
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr

		if err := cmd.Run(); err != nil {
			return err
		}
	}

	if boil.Files != nil {
		for name, body := range boil.Files {
			if err := ioutil.WriteFile(filepath.Join(dest, name), []byte(body), 0644); err != nil {
				return err
			}
		}
	}

	logp.Info(fmt.Sprintf("Created %s project in `%s`", boil.Name, dest))

	if boil.Message != "" {
		logp.Info(boil.Message)
	}

	return nil
}

type Boilerplate struct {
	Name        string
	Message     string
	DownloadURL string
	CMD         func(dest string) []string
	Files       map[string]string
}

var Boilerplates = []Boilerplate{
	{
		Name:        "Node.js - Express",
		Message:     "Lean how to use Express at https://expressjs.com",
		DownloadURL: "/leancloud/node-js-getting-started/zipball/master",
	},
	{
		Name:        "Node.js - Koa",
		DownloadURL: "/leancloud/koa-getting-started/zipball/master",
		Message:     "Lean how to use Koa at https://koajs.com",
	},
	{
		Name:        "Python - Flask",
		DownloadURL: "/leancloud/python-getting-started/zipball/master",
		Message:     "Lean how to use Flask at https://flask.palletsprojects.com",
	},
	{
		Name:        "Python - Django",
		DownloadURL: "/leancloud/django-getting-started/zipball/master",
		Message:     "Lean how to use Django at https://docs.djangoproject.com",
	},
	{
		Name:        "Java - Serlvet",
		DownloadURL: "/leancloud/servlet-getting-started/zipball/master",
	},
	{
		Name:        "Java - Spring Boot",
		DownloadURL: "/leancloud/spring-boot-getting-started/zipball/master",
		Message:     "Lean how to use Spring Boot at https://spring.io/projects/spring-boot",
	},
	{
		Name:        "PHP - Slim",
		DownloadURL: "/leancloud/slim-getting-started/zipball/master",
		Message:     "Lean how to use Slim at https://www.slimframework.com",
	},
	{
		Name:        ".NET Core",
		DownloadURL: "/leancloud/dotnet-core-getting-started/zipball/master",
		Message:     "Lean how to use .NET Core at https://docs.microsoft.com/aspnet/core/",
	},
	{
		Name:        "Go - Echo",
		DownloadURL: "/leancloud/golang-getting-started/zipball/master",
		Message:     "Lean how to use Echo at https://echo.labstack.com/",
	},
	{
		Name:  "React Web App (via create-react-app)",
		Files: prepareWebAppFiles("build"),
		CMD: func(dest string) []string {
			return []string{"npx", "create-react-app", dest, "--use-npm"}
		},
	},
	{
		Name:  "Vue Web App (via @vue/cli)",
		Files: prepareWebAppFiles("dist"),
		CMD: func(dest string) []string {
			return []string{"npx", "@vue/cli", "create", "--default", "--packageManager", "npm", dest}
		},
	},
}

// don't know why archive/zip.Reader.File[0].FileInfo().IsDir() always return true,
// this is a trick hack to void this.
func isDir(path string) bool {
	return os.IsPathSeparator(path[len(path)-1])
}

func extractAndWriteFile(f *zip.File, dest string) error {
	rc, err := f.Open()
	if err != nil {
		return err
	}
	defer rc.Close()

	path := filepath.Join(dest, f.Name)

	if isDir(f.Name) {
		if err := os.MkdirAll(path, f.Mode()); err != nil {
			return err
		}
	} else {
		// Use os.Create() since Zip don't store file permissions.
		f, err := os.Create(path)
		if err != nil {
			return err
		}
		defer f.Close()

		_, err = io.Copy(f, rc)
		if err != nil {
			return err
		}
	}
	return nil
}

// downloadToFile allows you to download the contents of the URL to a file
func downloadToFile(url string, fileName string) error {
	resp, err := grequests.Get(url, &grequests.RequestOptions{
		UserAgent: "LeanCloud-CLI/" + version.Version,
	})
	if err != nil {
		return err
	}
	defer resp.Close()

	if resp.StatusCode != 200 {
		return errors.New(utils.FormatServerErrorResult(resp.String()))
	}
	if resp.Error != nil {
		return resp.Error
	}

	fd, err := os.Create(fileName)

	if err != nil {
		return err
	}

	defer resp.Close() // This is a noop if we use the internal ByteBuffer
	defer fd.Close()

	if length, err := strconv.Atoi(resp.Header.Get("Content-Length")); err == nil {
		bar := pb.New(length).SetUnits(pb.U_BYTES).SetMaxWidth(80)
		bar.Output = colorable.NewColorableStderr()
		bar.Prefix(color.GreenString("[INFO]") + " Downloading templates")
		bar.Start()
		defer bar.Finish()
		reader := bar.NewProxyReader(resp)
		if _, err := io.Copy(fd, reader); err != nil && err != io.EOF {
			return err
		}
	} else {
		if _, err := io.Copy(fd, resp); err != nil && err != io.EOF {
			return err
		}
	}

	return nil
}

func prepareWebAppFiles(webRoot string) map[string]string {
	return map[string]string{
		"leanengine.yaml": "build: npm run build",
		"static.json": fmt.Sprintf(`{
  "public": "%s",
  "rewrites": [
    { "source": "**", "destination": "/index.html" }
  ]
}`, webRoot),
	}
}
<|endoftext|>


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

// +build appengine

package build

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"text/template"

	"appengine"
	"appengine/datastore"
	"appengine/delay"
	"appengine/mail"
	"appengine/urlfetch"
)

const (
	mailFrom   = "builder@golang.org" // use this for sending any mail
	failMailTo = "golang-dev@googlegroups.com"
	domain     = "build.golang.org"
	gobotBase  = "http://research.swtch.com/gobot_codereview"
)

// ignoreFailure is a set of builders that we don't email about because
// they are not yet production-ready.
var ignoreFailure = map[string]bool{
	"dragonfly-386":           true,
	"dragonfly-amd64":         true,
	"netbsd-arm-rpi":          true,
	"solaris-amd64-smartos":   true,
	"solaris-amd64-solaris11": true,
}

// notifyOnFailure checks whether the supplied Commit or the subsequent
// Commit (if present) breaks the build for this builder.
// If either of those commits break the build an email notification is sent
// from a delayed task. (We use a task because this way the mail won't be
// sent if the enclosing datastore transaction fails.)
//
// This must be run in a datastore transaction, and the provided *Commit must
// have been retrieved from the datastore within that transaction.
func notifyOnFailure(c appengine.Context, com *Commit, builder string) error {
	if ignoreFailure[builder] {
		return nil
	}

	// TODO(adg): implement notifications for packages
	if com.PackagePath != "" {
		return nil
	}

	p := &Package{Path: com.PackagePath}
	var broken *Commit
	cr := com.Result(builder, "")
	if cr == nil {
		return fmt.Errorf("no result for %s/%s", com.Hash, builder)
	}
	q := datastore.NewQuery("Commit").Ancestor(p.Key(c))
	if cr.OK {
		// This commit is OK. Notify if next Commit is broken.
		next := new(Commit)
		q = q.Filter("ParentHash=", com.Hash)
		if err := firstMatch(c, q, next); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// OK at tip, no notification necessary.
				return nil
			}
			return err
		}
		if nr := next.Result(builder, ""); nr != nil && !nr.OK {
			c.Debugf("commit ok: %#v\nresult: %#v", com, cr)
			c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr)
			broken = next
		}
	} else {
		// This commit is broken. Notify if the previous Commit is OK.
		prev := new(Commit)
		q = q.Filter("Hash=", com.ParentHash)
		if err := firstMatch(c, q, prev); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// No previous result, let the backfill of
				// this result trigger the notification.
				return nil
			}
			return err
		}
		if pr := prev.Result(builder, ""); pr != nil && pr.OK {
			c.Debugf("commit broken: %#v\nresult: %#v", com, cr)
			c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr)
			broken = com
		}
	}
	var err error
	if broken != nil && !broken.FailNotificationSent {
		c.Infof("%s is broken commit; notifying", broken.Hash)
		notifyLater.Call(c, broken, builder) // add task to queue
		broken.FailNotificationSent = true
		_, err = datastore.Put(c, broken.Key(c), broken)
	}
	return err
}

// firstMatch executes the query q and loads the first entity into v.
func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error {
	t := q.Limit(1).Run(c)
	_, err := t.Next(v)
	if err == datastore.Done {
		err = datastore.ErrNoSuchEntity
	}
	return err
}

var notifyLater = delay.Func("notify", notify)

// notify tries to update the CL for the given Commit with a failure message.
// If it doesn't succeed, it sends a failure email to golang-dev.
func notify(c appengine.Context, com *Commit, builder string) {
	if !updateCL(c, com, builder) {
		// Send a mail notification if the CL can't be found.
		sendFailMail(c, com, builder)
	}
}

// updateCL updates the CL for the given Commit with a failure message
// for the given builder.
func updateCL(c appengine.Context, com *Commit, builder string) bool {
	cl, err := lookupCL(c, com)
	if err != nil {
		c.Errorf("could not find CL for %v: %v", com.Hash, err)
		return false
	}
	url := fmt.Sprintf("%v?cl=%v&brokebuild=%v", gobotBase, cl, builder)
	r, err := urlfetch.Client(c).Post(url, "text/plain", nil)
	if err != nil {
		c.Errorf("could not update CL %v: %v", cl, err)
		return false
	}
	r.Body.Close()
	if r.StatusCode != http.StatusOK {
		c.Errorf("could not update CL %v: %v", cl, r.Status)
		return false
	}
	return true
}

var clURL = regexp.MustCompile(`https://codereview.appspot.com/([0-9]+)`)

// lookupCL consults code.google.com for the full change description for the
// provided Commit, and returns the relevant CL number.
func lookupCL(c appengine.Context, com *Commit) (string, error) {
	url := "https://code.google.com/p/go/source/detail?r=" + com.Hash
	r, err := urlfetch.Client(c).Get(url)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return "", fmt.Errorf("retrieving %v: %v", url, r.Status)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	m := clURL.FindAllSubmatch(b, -1)
	if m == nil {
		return "", errors.New("no CL URL found on changeset page")
	}
	// Return the last visible codereview URL on the page,
	// in case the change description refers to another CL.
	return string(m[len(m)-1][1]), nil
}

var sendFailMailTmpl = template.Must(template.New("notify.txt").
	Funcs(template.FuncMap(tmplFuncs)).
	ParseFiles("build/notify.txt"))

func init() {
	gob.Register(&Commit{}) // for delay
}

// sendFailMail sends a mail notification that the build failed on the
// provided commit and builder.
func sendFailMail(c appengine.Context, com *Commit, builder string) {
	// TODO(adg): handle packages

	// get Result
	r := com.Result(builder, "")
	if r == nil {
		c.Errorf("finding result for %q: %+v", builder, com)
		return
	}

	// get Log
	k := datastore.NewKey(c, "Log", r.LogHash, 0, nil)
	l := new(Log)
	if err := datastore.Get(c, k, l); err != nil {
		c.Errorf("finding Log record %v: %v", r.LogHash, err)
		return
	}

	// prepare mail message
	var body bytes.Buffer
	err := sendFailMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "Result": r, "Log": l,
		"Hostname": domain,
	})
	if err != nil {
		c.Errorf("rendering mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}
<commit_msg>go.tools/dashboard/app: send log hash to gobot on build failure<commit_after>// Copyright 2011 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// +build appengine

package build

import (
	"bytes"
	"encoding/gob"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"regexp"
	"text/template"

	"appengine"
	"appengine/datastore"
	"appengine/delay"
	"appengine/mail"
	"appengine/urlfetch"
)

const (
	mailFrom   = "builder@golang.org" // use this for sending any mail
	failMailTo = "golang-dev@googlegroups.com"
	domain     = "build.golang.org"
	gobotBase  = "http://research.swtch.com/gobot_codereview"
)

// ignoreFailure is a set of builders that we don't email about because
// they are not yet production-ready.
var ignoreFailure = map[string]bool{
	"dragonfly-386":           true,
	"dragonfly-amd64":         true,
	"netbsd-arm-rpi":          true,
	"solaris-amd64-smartos":   true,
	"solaris-amd64-solaris11": true,
}

// notifyOnFailure checks whether the supplied Commit or the subsequent
// Commit (if present) breaks the build for this builder.
// If either of those commits break the build an email notification is sent
// from a delayed task. (We use a task because this way the mail won't be
// sent if the enclosing datastore transaction fails.)
//
// This must be run in a datastore transaction, and the provided *Commit must
// have been retrieved from the datastore within that transaction.
func notifyOnFailure(c appengine.Context, com *Commit, builder string) error {
	if ignoreFailure[builder] {
		return nil
	}

	// TODO(adg): implement notifications for packages
	if com.PackagePath != "" {
		return nil
	}

	p := &Package{Path: com.PackagePath}
	var broken *Commit
	cr := com.Result(builder, "")
	if cr == nil {
		return fmt.Errorf("no result for %s/%s", com.Hash, builder)
	}
	q := datastore.NewQuery("Commit").Ancestor(p.Key(c))
	if cr.OK {
		// This commit is OK. Notify if next Commit is broken.
		next := new(Commit)
		q = q.Filter("ParentHash=", com.Hash)
		if err := firstMatch(c, q, next); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// OK at tip, no notification necessary.
				return nil
			}
			return err
		}
		if nr := next.Result(builder, ""); nr != nil && !nr.OK {
			c.Debugf("commit ok: %#v\nresult: %#v", com, cr)
			c.Debugf("next commit broken: %#v\nnext result:%#v", next, nr)
			broken = next
		}
	} else {
		// This commit is broken. Notify if the previous Commit is OK.
		prev := new(Commit)
		q = q.Filter("Hash=", com.ParentHash)
		if err := firstMatch(c, q, prev); err != nil {
			if err == datastore.ErrNoSuchEntity {
				// No previous result, let the backfill of
				// this result trigger the notification.
				return nil
			}
			return err
		}
		if pr := prev.Result(builder, ""); pr != nil && pr.OK {
			c.Debugf("commit broken: %#v\nresult: %#v", com, cr)
			c.Debugf("previous commit ok: %#v\nprevious result:%#v", prev, pr)
			broken = com
		}
	}
	var err error
	if broken != nil && !broken.FailNotificationSent {
		c.Infof("%s is broken commit; notifying", broken.Hash)
		notifyLater.Call(c, broken, builder) // add task to queue
		broken.FailNotificationSent = true
		_, err = datastore.Put(c, broken.Key(c), broken)
	}
	return err
}

// firstMatch executes the query q and loads the first entity into v.
func firstMatch(c appengine.Context, q *datastore.Query, v interface{}) error {
	t := q.Limit(1).Run(c)
	_, err := t.Next(v)
	if err == datastore.Done {
		err = datastore.ErrNoSuchEntity
	}
	return err
}

var notifyLater = delay.Func("notify", notify)

// notify tries to update the CL for the given Commit with a failure message.
// If it doesn't succeed, it sends a failure email to golang-dev.
func notify(c appengine.Context, com *Commit, builder string) {
	if !updateCL(c, com, builder) {
		// Send a mail notification if the CL can't be found.
		sendFailMail(c, com, builder)
	}
}

// updateCL updates the CL for the given Commit with a failure message
// for the given builder.
func updateCL(c appengine.Context, com *Commit, builder string) bool {
	cl, err := lookupCL(c, com)
	if err != nil {
		c.Errorf("could not find CL for %v: %v", com.Hash, err)
		return false
	}
	res := com.Result(builder, "")
	if res == nil {
		c.Errorf("finding result for %q: %+v", builder, com)
		return false
	}
	url := fmt.Sprintf("%v?cl=%v&brokebuild=%v&log=%v", gobotBase, cl, builder, res.LogHash)
	r, err := urlfetch.Client(c).Post(url, "text/plain", nil)
	if err != nil {
		c.Errorf("could not update CL %v: %v", cl, err)
		return false
	}
	r.Body.Close()
	if r.StatusCode != http.StatusOK {
		c.Errorf("could not update CL %v: %v", cl, r.Status)
		return false
	}
	return true
}

var clURL = regexp.MustCompile(`https://codereview.appspot.com/([0-9]+)`)

// lookupCL consults code.google.com for the full change description for the
// provided Commit, and returns the relevant CL number.
func lookupCL(c appengine.Context, com *Commit) (string, error) {
	url := "https://code.google.com/p/go/source/detail?r=" + com.Hash
	r, err := urlfetch.Client(c).Get(url)
	if err != nil {
		return "", err
	}
	defer r.Body.Close()
	if r.StatusCode != http.StatusOK {
		return "", fmt.Errorf("retrieving %v: %v", url, r.Status)
	}
	b, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return "", err
	}
	m := clURL.FindAllSubmatch(b, -1)
	if m == nil {
		return "", errors.New("no CL URL found on changeset page")
	}
	// Return the last visible codereview URL on the page,
	// in case the change description refers to another CL.
	return string(m[len(m)-1][1]), nil
}

var sendFailMailTmpl = template.Must(template.New("notify.txt").
	Funcs(template.FuncMap(tmplFuncs)).
	ParseFiles("build/notify.txt"))

func init() {
	gob.Register(&Commit{}) // for delay
}

// sendFailMail sends a mail notification that the build failed on the
// provided commit and builder.
func sendFailMail(c appengine.Context, com *Commit, builder string) {
	// TODO(adg): handle packages

	// get Result
	r := com.Result(builder, "")
	if r == nil {
		c.Errorf("finding result for %q: %+v", builder, com)
		return
	}

	// get Log
	k := datastore.NewKey(c, "Log", r.LogHash, 0, nil)
	l := new(Log)
	if err := datastore.Get(c, k, l); err != nil {
		c.Errorf("finding Log record %v: %v", r.LogHash, err)
		return
	}

	// prepare mail message
	var body bytes.Buffer
	err := sendFailMailTmpl.Execute(&body, map[string]interface{}{
		"Builder": builder, "Commit": com, "Result": r, "Log": l,
		"Hostname": domain,
	})
	if err != nil {
		c.Errorf("rendering mail template: %v", err)
		return
	}
	subject := fmt.Sprintf("%s broken by %s", builder, shortDesc(com.Desc))
	msg := &mail.Message{
		Sender:  mailFrom,
		To:      []string{failMailTo},
		ReplyTo: failMailTo,
		Subject: subject,
		Body:    body.String(),
	}

	// send mail
	if err := mail.Send(c, msg); err != nil {
		c.Errorf("sending mail: %v", err)
	}
}
<|endoftext|>


<commit_before>package clusteregistrationtokens

import (
	"strings"

	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
)

func Formatter(request *types.APIContext, resource *types.RawResource) {
	if convert.ToBool(resource.Values["internal"]) {
		delete(resource.Links, "remove")
	}
	shellLink := request.URLBuilder.Link("shell", resource)
	shellLink = strings.Replace(shellLink, "http", "ws", 1)
	shellLink = strings.Replace(shellLink, "/shell", "?shell=true", 1)
	resource.Links["shell"] = shellLink
	resource.AddAction(request, "generateKubeconfig")
	resource.AddAction(request, "importYaml")
	resource.AddAction(request, "exportYaml")
}
<commit_msg>Update cluster formatter to support GKE option rename<commit_after>package clusteregistrationtokens

import (
	"strings"

	"github.com/rancher/norman/types"
	"github.com/rancher/norman/types/convert"
	"github.com/rancher/types/client/management/v3"
	"github.com/sirupsen/logrus"
)

func Formatter(request *types.APIContext, resource *types.RawResource) {
	if convert.ToBool(resource.Values["internal"]) {
		delete(resource.Links, "remove")
	}
	shellLink := request.URLBuilder.Link("shell", resource)
	shellLink = strings.Replace(shellLink, "http", "ws", 1)
	shellLink = strings.Replace(shellLink, "/shell", "?shell=true", 1)
	resource.Links["shell"] = shellLink
	resource.AddAction(request, "generateKubeconfig")
	resource.AddAction(request, "importYaml")
	resource.AddAction(request, "exportYaml")

	if gkeConfig, ok := resource.Values[client.ClusterSpecFieldGoogleKubernetesEngineConfig]; ok {
		configMap, ok := gkeConfig.(map[string]interface{})
		if !ok {
			logrus.Errorf("could not convert gke config to map")
			return
		}

		setTrueIfNil(configMap, client.GoogleKubernetesEngineConfigFieldEnableStackdriverLogging)
		setTrueIfNil(configMap, client.GoogleKubernetesEngineConfigFieldEnableStackdriverMonitoring)
		setTrueIfNil(configMap, client.GoogleKubernetesEngineConfigFieldEnableHorizontalPodAutoscaling)
		setTrueIfNil(configMap, client.GoogleKubernetesEngineConfigFieldEnableHTTPLoadBalancing)
		setTrueIfNil(configMap, client.GoogleKubernetesEngineConfigFieldEnableNetworkPolicyConfig)
	}
}

func setTrueIfNil(configMap map[string]interface{}, fieldName string) {
	if configMap[fieldName] == nil {
		configMap[fieldName] = true
	}
}
<|endoftext|>


<commit_before>// +build windows

/*
   Copyright The containerd 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 server

import (
	"github.com/containerd/containerd/oci"
	imagespec "github.com/opencontainers/image-spec/specs-go/v1"
	runtimespec "github.com/opencontainers/runtime-spec/specs-go"
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"

	"github.com/containerd/containerd/pkg/cri/annotations"
	"github.com/containerd/containerd/pkg/cri/config"
	customopts "github.com/containerd/containerd/pkg/cri/opts"
)

// No container mounts for windows.
func (c *criService) containerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
	return nil
}

func (c *criService) containerSpec(
	id string,
	sandboxID string,
	sandboxPid uint32,
	netNSPath string,
	containerName string,
	imageName string,
	config *runtime.ContainerConfig,
	sandboxConfig *runtime.PodSandboxConfig,
	imageConfig *imagespec.ImageConfig,
	extraMounts []*runtime.Mount,
	ociRuntime config.Runtime,
) (*runtimespec.Spec, error) {
	specOpts := []oci.SpecOpts{
		customopts.WithProcessArgs(config, imageConfig),
	}
	if config.GetWorkingDir() != "" {
		specOpts = append(specOpts, oci.WithProcessCwd(config.GetWorkingDir()))
	} else if imageConfig.WorkingDir != "" {
		specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
	}

	if config.GetTty() {
		specOpts = append(specOpts, oci.WithTTY)
	}

	// Apply envs from image config first, so that envs from container config
	// can override them.
	env := imageConfig.Env
	for _, e := range config.GetEnvs() {
		env = append(env, e.GetKey()+"="+e.GetValue())
	}
	specOpts = append(specOpts, oci.WithEnv(env))

	specOpts = append(specOpts,
		// Clear the root location since hcsshim expects it.
		// NOTE: readonly rootfs doesn't work on windows.
		customopts.WithoutRoot,
		customopts.WithWindowsNetworkNamespace(netNSPath),
		oci.WithHostname(sandboxConfig.GetHostname()),
	)

	specOpts = append(specOpts, customopts.WithWindowsMounts(c.os, config, extraMounts))

	// Start with the image config user and override below if RunAsUsername is not "".
	username := imageConfig.User

	windowsConfig := config.GetWindows()
	if windowsConfig != nil {
		specOpts = append(specOpts, customopts.WithWindowsResources(windowsConfig.GetResources()))
		securityCtx := windowsConfig.GetSecurityContext()
		if securityCtx != nil {
			runAsUser := securityCtx.GetRunAsUsername()
			if runAsUser != "" {
				username = runAsUser
			}
			cs := securityCtx.GetCredentialSpec()
			if cs != "" {
				specOpts = append(specOpts, customopts.WithWindowsCredentialSpec(cs))
			}
		}
	}

	// There really isn't a good Windows way to verify that the username is available in the
	// image as early as here like there is for Linux. Later on in the stack hcsshim
	// will handle the behavior of erroring out if the user isn't available in the image
	// when trying to run the init process.
	specOpts = append(specOpts, oci.WithUser(username))

	for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
		ociRuntime.PodAnnotations) {
		specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
	}

	for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
		ociRuntime.ContainerAnnotations) {
		specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
	}

	specOpts = append(specOpts,
		customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer),
		customopts.WithAnnotation(annotations.SandboxID, sandboxID),
		customopts.WithAnnotation(annotations.SandboxNamespace, sandboxConfig.GetMetadata().GetNamespace()),
		customopts.WithAnnotation(annotations.SandboxName, sandboxConfig.GetMetadata().GetName()),
		customopts.WithAnnotation(annotations.ContainerName, containerName),
		customopts.WithAnnotation(annotations.ImageName, imageName),
	)
	return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
}

// No extra spec options needed for windows.
func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
	return nil, nil
}
<commit_msg>cri: append envs from image config to empty slice to avoid env lost<commit_after>// +build windows

/*
   Copyright The containerd 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 server

import (
	"github.com/containerd/containerd/oci"
	imagespec "github.com/opencontainers/image-spec/specs-go/v1"
	runtimespec "github.com/opencontainers/runtime-spec/specs-go"
	runtime "k8s.io/cri-api/pkg/apis/runtime/v1alpha2"

	"github.com/containerd/containerd/pkg/cri/annotations"
	"github.com/containerd/containerd/pkg/cri/config"
	customopts "github.com/containerd/containerd/pkg/cri/opts"
)

// No container mounts for windows.
func (c *criService) containerMounts(sandboxID string, config *runtime.ContainerConfig) []*runtime.Mount {
	return nil
}

func (c *criService) containerSpec(
	id string,
	sandboxID string,
	sandboxPid uint32,
	netNSPath string,
	containerName string,
	imageName string,
	config *runtime.ContainerConfig,
	sandboxConfig *runtime.PodSandboxConfig,
	imageConfig *imagespec.ImageConfig,
	extraMounts []*runtime.Mount,
	ociRuntime config.Runtime,
) (*runtimespec.Spec, error) {
	specOpts := []oci.SpecOpts{
		customopts.WithProcessArgs(config, imageConfig),
	}
	if config.GetWorkingDir() != "" {
		specOpts = append(specOpts, oci.WithProcessCwd(config.GetWorkingDir()))
	} else if imageConfig.WorkingDir != "" {
		specOpts = append(specOpts, oci.WithProcessCwd(imageConfig.WorkingDir))
	}

	if config.GetTty() {
		specOpts = append(specOpts, oci.WithTTY)
	}

	// Apply envs from image config first, so that envs from container config
	// can override them.
	env := append([]string{}, imageConfig.Env...)
	for _, e := range config.GetEnvs() {
		env = append(env, e.GetKey()+"="+e.GetValue())
	}
	specOpts = append(specOpts, oci.WithEnv(env))

	specOpts = append(specOpts,
		// Clear the root location since hcsshim expects it.
		// NOTE: readonly rootfs doesn't work on windows.
		customopts.WithoutRoot,
		customopts.WithWindowsNetworkNamespace(netNSPath),
		oci.WithHostname(sandboxConfig.GetHostname()),
	)

	specOpts = append(specOpts, customopts.WithWindowsMounts(c.os, config, extraMounts))

	// Start with the image config user and override below if RunAsUsername is not "".
	username := imageConfig.User

	windowsConfig := config.GetWindows()
	if windowsConfig != nil {
		specOpts = append(specOpts, customopts.WithWindowsResources(windowsConfig.GetResources()))
		securityCtx := windowsConfig.GetSecurityContext()
		if securityCtx != nil {
			runAsUser := securityCtx.GetRunAsUsername()
			if runAsUser != "" {
				username = runAsUser
			}
			cs := securityCtx.GetCredentialSpec()
			if cs != "" {
				specOpts = append(specOpts, customopts.WithWindowsCredentialSpec(cs))
			}
		}
	}

	// There really isn't a good Windows way to verify that the username is available in the
	// image as early as here like there is for Linux. Later on in the stack hcsshim
	// will handle the behavior of erroring out if the user isn't available in the image
	// when trying to run the init process.
	specOpts = append(specOpts, oci.WithUser(username))

	for pKey, pValue := range getPassthroughAnnotations(sandboxConfig.Annotations,
		ociRuntime.PodAnnotations) {
		specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
	}

	for pKey, pValue := range getPassthroughAnnotations(config.Annotations,
		ociRuntime.ContainerAnnotations) {
		specOpts = append(specOpts, customopts.WithAnnotation(pKey, pValue))
	}

	specOpts = append(specOpts,
		customopts.WithAnnotation(annotations.ContainerType, annotations.ContainerTypeContainer),
		customopts.WithAnnotation(annotations.SandboxID, sandboxID),
		customopts.WithAnnotation(annotations.SandboxNamespace, sandboxConfig.GetMetadata().GetNamespace()),
		customopts.WithAnnotation(annotations.SandboxName, sandboxConfig.GetMetadata().GetName()),
		customopts.WithAnnotation(annotations.ContainerName, containerName),
		customopts.WithAnnotation(annotations.ImageName, imageName),
	)
	return c.runtimeSpec(id, ociRuntime.BaseRuntimeSpec, specOpts...)
}

// No extra spec options needed for windows.
func (c *criService) containerSpecOpts(config *runtime.ContainerConfig, imageConfig *imagespec.ImageConfig) ([]oci.SpecOpts, error) {
	return nil, nil
}
<|endoftext|>