<commit_before>// Copyright 2018 The LUCI 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 buildbucket

import (
	"fmt"
	"strconv"
	"strings"

	"golang.org/x/net/context"

	"go.chromium.org/luci/common/data/strpair"
)

// FormatBuildAddress returns a value for TagBuildAddress tag.
// If number is positive, returns "<bucket>/<builder>/<number>",
// otherwise returns "<id>"
//
// See also ParseBuildAddress.
func FormatBuildAddress(id int64, bucket, builder string, number int) string {
	if number > 0 {
		return fmt.Sprintf("%s/%s/%d", bucket, builder, number)
	}

	return strconv.FormatInt(id, 10)
}

// ParseBuildAddress parses a value of a TagBuildAddress tag.
// See also FormatBuildAddress.
//
// If id is non-zero, project, bucket and builder are zero.
// If bucket is non-zero, id is zero.
func ParseBuildAddress(address string) (id int64, project, bucket, builder string, number int, err error) {
	parts := strings.Split(address, "/")
	switch len(parts) {
	case 3:
		var numberStr string
		bucket, builder, numberStr = parts[0], parts[1], parts[2]
		project = ProjectFromBucket(bucket)
		number, err = strconv.Atoi(numberStr)
	case 1:
		id, err = strconv.ParseInt(parts[0], 10, 64)
	default:
		err = fmt.Errorf("unrecognized build address format %q", address)
	}
	return
}

// ValidateBuildAddress returns an error if the build address is invalid.
func ValidateBuildAddress(address string) error {
	_, _, _, _, _, err := ParseBuildAddress(address)
	return err
}

// GetByAddress fetches a build by its address.
// Returns (nil, nil) if build is not found.
func GetByAddress(c context.Context, client *Service, address string) (*ApiCommonBuildMessage, error) {
	id, _, _, _, _, err := ParseBuildAddress(address)
	if err != nil {
		return nil, err
	}

	if id != 0 {
		res, err := client.Get(id).Context(c).Do()
		switch {
		case err != nil:
			return nil, err
		case res.Error != nil && res.Error.Reason == ReasonNotFound:
			return nil, nil
		default:
			return res.Build, nil
		}
	}

	msgs, err := client.Search().
		Context(c).
		Tag(strpair.Format(TagBuildAddress, address)).
		Fetch(1, nil)
	switch {
	case err != nil:
		return nil, err
	case len(msgs) == 0:
		return nil, nil
	default:
		return msgs[0], nil
	}
}

// ProjectFromBucket tries to retrieve project id from bucket name.
// Returns "" on failure.
func ProjectFromBucket(bucket string) string {
	// Buildbucket guarantees that buckets that start with "luci."
	// have "luci.<project id>." prefix.
	parts := strings.Split(bucket, ".")
	if len(parts) >= 3 && parts[0] == "luci" {
		return parts[1]
	}
	return ""
}
<commit_msg>[buildbucket v1] Always include experimental when fetching by address<commit_after>// Copyright 2018 The LUCI 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 buildbucket

import (
	"fmt"
	"strconv"
	"strings"

	"golang.org/x/net/context"

	"go.chromium.org/luci/common/data/strpair"
)

// FormatBuildAddress returns a value for TagBuildAddress tag.
// If number is positive, returns "<bucket>/<builder>/<number>",
// otherwise returns "<id>"
//
// See also ParseBuildAddress.
func FormatBuildAddress(id int64, bucket, builder string, number int) string {
	if number > 0 {
		return fmt.Sprintf("%s/%s/%d", bucket, builder, number)
	}

	return strconv.FormatInt(id, 10)
}

// ParseBuildAddress parses a value of a TagBuildAddress tag.
// See also FormatBuildAddress.
//
// If id is non-zero, project, bucket and builder are zero.
// If bucket is non-zero, id is zero.
func ParseBuildAddress(address string) (id int64, project, bucket, builder string, number int, err error) {
	parts := strings.Split(address, "/")
	switch len(parts) {
	case 3:
		var numberStr string
		bucket, builder, numberStr = parts[0], parts[1], parts[2]
		project = ProjectFromBucket(bucket)
		number, err = strconv.Atoi(numberStr)
	case 1:
		id, err = strconv.ParseInt(parts[0], 10, 64)
	default:
		err = fmt.Errorf("unrecognized build address format %q", address)
	}
	return
}

// ValidateBuildAddress returns an error if the build address is invalid.
func ValidateBuildAddress(address string) error {
	_, _, _, _, _, err := ParseBuildAddress(address)
	return err
}

// GetByAddress fetches a build by its address.
// Returns (nil, nil) if build is not found.
func GetByAddress(c context.Context, client *Service, address string) (*ApiCommonBuildMessage, error) {
	id, _, _, _, _, err := ParseBuildAddress(address)
	if err != nil {
		return nil, err
	}

	if id != 0 {
		res, err := client.Get(id).Context(c).Do()
		switch {
		case err != nil:
			return nil, err
		case res.Error != nil && res.Error.Reason == ReasonNotFound:
			return nil, nil
		default:
			return res.Build, nil
		}
	}

	msgs, err := client.Search().
		Context(c).
		Tag(strpair.Format(TagBuildAddress, address)).
		IncludeExperimental(true).
		Fetch(1, nil)
	switch {
	case err != nil:
		return nil, err
	case len(msgs) == 0:
		return nil, nil
	default:
		return msgs[0], nil
	}
}

// ProjectFromBucket tries to retrieve project id from bucket name.
// Returns "" on failure.
func ProjectFromBucket(bucket string) string {
	// Buildbucket guarantees that buckets that start with "luci."
	// have "luci.<project id>." prefix.
	parts := strings.Split(bucket, ".")
	if len(parts) >= 3 && parts[0] == "luci" {
		return parts[1]
	}
	return ""
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014-2015 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/wire"
	rpc "github.com/btcsuite/btcrpcclient"
	"github.com/btcsuite/btcutil"
)

// minFee is the minimum tx fee that can be paid
const minFee btcutil.Amount = 1e4 // 0.0001 BTC

// utxoQueue is the queue of utxos belonging to a actor
// utxos are queued after a block is received and are dispatched
// to their respective owner from com.poolUtxos
// they are dequeued from simulateTx and splitUtxos
type utxoQueue struct {
	utxos   []*TxOut
	enqueue chan *TxOut
	dequeue chan *TxOut
}

// Actor describes an actor on the simulation network.  Each actor runs
// independantly without external input to decide it's behavior.
type Actor struct {
	*Node
	quit             chan struct{}
	wg               sync.WaitGroup
	ownedAddresses   []btcutil.Address
	utxoQueue        *utxoQueue
	miningAddr       chan btcutil.Address
	walletPassphrase string
}

// TxOut is a valid tx output that can be used to generate transactions
type TxOut struct {
	OutPoint *wire.OutPoint
	Amount   btcutil.Amount
}

// NewActor creates a new actor which runs its own wallet process connecting
// to the btcd node server specified by node, and listening for simulator
// websocket connections on the specified port.
func NewActor(node *Node, port uint16) (*Actor, error) {
	// Please don't run this as root.
	if port < 1024 {
		return nil, errors.New("invalid actor port")
	}

	// Set btcwallet node args
	args, err := newBtcwalletArgs(port, node.Args.(*btcdArgs))
	if err != nil {
		return nil, err
	}

	logFile, err := getLogFile(args.prefix)
	if err != nil {
		log.Printf("Cannot get log file, logging disabled: %v", err)
	}
	btcwallet, err := NewNodeFromArgs(args, nil, logFile)
	if err != nil {
		return nil, err
	}

	a := Actor{
		Node:             btcwallet,
		quit:             make(chan struct{}),
		ownedAddresses:   make([]btcutil.Address, *maxAddresses),
		miningAddr:       make(chan btcutil.Address),
		walletPassphrase: "password",
		utxoQueue: &utxoQueue{
			enqueue: make(chan *TxOut),
			dequeue: make(chan *TxOut),
		},
	}
	return &a, nil
}

// Start creates the command to execute a wallet process and starts the
// command in the background, attaching the command's stderr and stdout
// to the passed writers. Nil writers may be used to discard output.
//
// In addition to starting the wallet process, this runs goroutines to
// handle wallet notifications and requests the wallet process to create
// an intial encrypted wallet, so that it can actually send and receive BTC.
//
// If the RPC client connection cannot be established or wallet cannot
// be created, the wallet process is killed and the actor directory
// removed.
func (a *Actor) Start(stderr, stdout io.Writer, com *Communication) error {
	connected := make(chan struct{})
	const timeoutSecs int64 = 3600 * 24

	if err := a.Node.Start(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	ntfnHandlers := &rpc.NotificationHandlers{
		OnClientConnected: func() {
			connected <- struct{}{}
		},
	}
	a.handlers = ntfnHandlers

	if err := a.Connect(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	// Wait for btcd to connect
	<-connected

	// Wait for wallet sync
	for i := 0; i < *maxConnRetries; i++ {
		if _, err := a.client.GetBalance(""); err != nil {
			time.Sleep(time.Duration(i) * 50 * time.Millisecond)
			continue
		}
		break
	}

	// Create wallet addresses and unlock wallet.
	log.Printf("%s: Creating wallet addresses...", a)
	for i := range a.ownedAddresses {
		fmt.Printf("\r%d/%d", i+1, len(a.ownedAddresses))
		addr, err := a.client.GetNewAddress("default")
		if err != nil {
			log.Printf("%s: Cannot create address #%d", a, i+1)
			com.errChan <- struct{}{}
			return err
		}
		a.ownedAddresses[i] = addr
	}
	fmt.Printf("\n")

	if err := a.client.WalletPassphrase(a.walletPassphrase, timeoutSecs); err != nil {
		log.Printf("%s: Cannot unlock wallet: %v", a, err)
		com.errChan <- struct{}{}
		return err
	}

	// Send a random address that will be used by the cpu miner.
	a.miningAddr <- a.ownedAddresses[rand.Int()%len(a.ownedAddresses)]

	// Start a goroutine that queues up a set of utxos belonging to this
	// actor. The utxos are sent from com.poolUtxos which in turn receives
	// block notifications from sim.go
	a.wg.Add(1)
	go a.queueUtxos()

	// Start a goroutine to simulate transactions.
	a.wg.Add(1)
	go a.simulateTx(com.downstream, com.txpool)

	// Start a goroutine to split utxos
	a.wg.Add(1)
	go a.splitUtxos(com.split, com.txpool)

	return nil
}

// simulateTx runs as a goroutine and simulates transactions between actors
//
// It receives a random address downstream, dequeues a utxo, sends a raw
// transaction to the address using the utxo as input
func (a *Actor) simulateTx(downstream <-chan btcutil.Address, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case addr := <-downstream:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{
					addr: amt,
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// splitUtxos runs as a goroutine and builds up a large set of utxos that
// can be used to simulate large tx/block ratios
//
// It receives a 'split' which is int that indicates the number of resultant utxos
// the tx is sent to addresses from the same actor since we're only interested in
// building up the utxo set
func (a *Actor) splitUtxos(split <-chan int, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case split := <-split:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{}

				// Create a output of random amount and sent it
				// to a random address from the address space
				// total number of outputs is split+1, taking into
				// account this utxo which is consumed in the process

				// set a rand start index for getting different random addrs
				randomIndex := rand.Int() % len(a.ownedAddresses)
				for i := 0; i <= split; i++ {
					var to btcutil.Address
					var change btcutil.Amount
					// pick a random address
					if randomIndex+i < len(a.ownedAddresses) {
						to = a.ownedAddresses[randomIndex+i]
					} else {
						// wrap around incase of an overflow
						to = a.ownedAddresses[randomIndex+i-len(a.ownedAddresses)]
					}
					if i == split {
						// last split, so just set the change
						change = amt
					} else {
						// pick a random change amount which is less than amt
						// but have a lower bound at minFee
						change = btcutil.Amount(rand.Int63n(int64(amt) / 2))
						if change < minFee {
							change = minFee
						}
						amt -= change
					}
					amounts[to] = change
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// sendRawTransaction creates a raw transaction, signs it and sends it
func (a *Actor) sendRawTransaction(inputs []btcjson.TransactionInput, amounts map[btcutil.Address]btcutil.Amount) error {
	msgTx, err := a.client.CreateRawTransaction(inputs, amounts)
	if err != nil {
		return err
	}
	// sign it
	msgTx, ok, err := a.client.SignRawTransaction(msgTx)
	if err != nil {
		return err
	}
	if !ok {
		return err
	}
	// and finally send it.
	if _, err := a.client.SendRawTransaction(msgTx, false); err != nil {
		return err
	}
	return nil
}

// queueUtxos receives utxos belonging to this actor and queues them up
func (a *Actor) queueUtxos() {
	defer a.wg.Done()

	enqueue := a.utxoQueue.enqueue
	var dequeue chan *TxOut
	var next *TxOut
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no utxos are queued for handling,
				// the queue is finished.
				if len(a.utxoQueue.utxos) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(a.utxoQueue.utxos) == 0 {
				next = n
				dequeue = a.utxoQueue.dequeue
			}
			a.utxoQueue.utxos = append(a.utxoQueue.utxos, n)
		case dequeue <- next:
			a.utxoQueue.utxos[0] = nil
			a.utxoQueue.utxos = a.utxoQueue.utxos[1:]
			if len(a.utxoQueue.utxos) != 0 {
				next = a.utxoQueue.utxos[0]
			} else {
				// If no more utxos can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}
		case <-a.quit:
			break out
		}
	}
	close(a.utxoQueue.dequeue)
}

// Shutdown performs a shutdown down the actor by first signalling
// all goroutines to stop, waiting for them to stop and them cleaning up
func (a *Actor) Shutdown() {
	select {
	case <-a.quit:
	default:
		close(a.quit)
		a.WaitForShutdown()
		a.Node.Shutdown()
	}
}

// WaitForShutdown waits until every actor goroutine has returned
func (a *Actor) WaitForShutdown() {
	a.wg.Wait()
}
<commit_msg>Catch btcsim up with btcrpcclient api change<commit_after>/*
 * Copyright (c) 2014-2015 Conformal Systems LLC <info@conformal.com>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package main

import (
	"errors"
	"fmt"
	"io"
	"log"
	"math/rand"
	"sync"
	"time"

	"github.com/btcsuite/btcd/btcjson"
	"github.com/btcsuite/btcd/wire"
	rpc "github.com/btcsuite/btcrpcclient"
	"github.com/btcsuite/btcutil"
)

// minFee is the minimum tx fee that can be paid
const minFee btcutil.Amount = 1e4 // 0.0001 BTC

// utxoQueue is the queue of utxos belonging to a actor
// utxos are queued after a block is received and are dispatched
// to their respective owner from com.poolUtxos
// they are dequeued from simulateTx and splitUtxos
type utxoQueue struct {
	utxos   []*TxOut
	enqueue chan *TxOut
	dequeue chan *TxOut
}

// Actor describes an actor on the simulation network.  Each actor runs
// independantly without external input to decide it's behavior.
type Actor struct {
	*Node
	quit             chan struct{}
	wg               sync.WaitGroup
	ownedAddresses   []btcutil.Address
	utxoQueue        *utxoQueue
	miningAddr       chan btcutil.Address
	walletPassphrase string
}

// TxOut is a valid tx output that can be used to generate transactions
type TxOut struct {
	OutPoint *wire.OutPoint
	Amount   btcutil.Amount
}

// NewActor creates a new actor which runs its own wallet process connecting
// to the btcd node server specified by node, and listening for simulator
// websocket connections on the specified port.
func NewActor(node *Node, port uint16) (*Actor, error) {
	// Please don't run this as root.
	if port < 1024 {
		return nil, errors.New("invalid actor port")
	}

	// Set btcwallet node args
	args, err := newBtcwalletArgs(port, node.Args.(*btcdArgs))
	if err != nil {
		return nil, err
	}

	logFile, err := getLogFile(args.prefix)
	if err != nil {
		log.Printf("Cannot get log file, logging disabled: %v", err)
	}
	btcwallet, err := NewNodeFromArgs(args, nil, logFile)
	if err != nil {
		return nil, err
	}

	a := Actor{
		Node:             btcwallet,
		quit:             make(chan struct{}),
		ownedAddresses:   make([]btcutil.Address, *maxAddresses),
		miningAddr:       make(chan btcutil.Address),
		walletPassphrase: "password",
		utxoQueue: &utxoQueue{
			enqueue: make(chan *TxOut),
			dequeue: make(chan *TxOut),
		},
	}
	return &a, nil
}

// Start creates the command to execute a wallet process and starts the
// command in the background, attaching the command's stderr and stdout
// to the passed writers. Nil writers may be used to discard output.
//
// In addition to starting the wallet process, this runs goroutines to
// handle wallet notifications and requests the wallet process to create
// an intial encrypted wallet, so that it can actually send and receive BTC.
//
// If the RPC client connection cannot be established or wallet cannot
// be created, the wallet process is killed and the actor directory
// removed.
func (a *Actor) Start(stderr, stdout io.Writer, com *Communication) error {
	connected := make(chan struct{})
	const timeoutSecs int64 = 3600 * 24

	if err := a.Node.Start(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	ntfnHandlers := &rpc.NotificationHandlers{
		OnClientConnected: func() {
			connected <- struct{}{}
		},
	}
	a.handlers = ntfnHandlers

	if err := a.Connect(); err != nil {
		a.Shutdown()
		com.errChan <- struct{}{}
		return err
	}

	// Wait for btcd to connect
	<-connected

	// Wait for wallet sync
	for i := 0; i < *maxConnRetries; i++ {
		if _, err := a.client.GetBalance(""); err != nil {
			time.Sleep(time.Duration(i) * 50 * time.Millisecond)
			continue
		}
		break
	}

	// Create wallet addresses and unlock wallet.
	log.Printf("%s: Creating wallet addresses...", a)
	for i := range a.ownedAddresses {
		fmt.Printf("\r%d/%d", i+1, len(a.ownedAddresses))
		addr, err := a.client.GetNewAddress("default")
		if err != nil {
			log.Printf("%s: Cannot create address #%d", a, i+1)
			com.errChan <- struct{}{}
			return err
		}
		a.ownedAddresses[i] = addr
	}
	fmt.Printf("\n")

	if err := a.client.WalletPassphrase(a.walletPassphrase, timeoutSecs); err != nil {
		log.Printf("%s: Cannot unlock wallet: %v", a, err)
		com.errChan <- struct{}{}
		return err
	}

	// Send a random address that will be used by the cpu miner.
	a.miningAddr <- a.ownedAddresses[rand.Int()%len(a.ownedAddresses)]

	// Start a goroutine that queues up a set of utxos belonging to this
	// actor. The utxos are sent from com.poolUtxos which in turn receives
	// block notifications from sim.go
	a.wg.Add(1)
	go a.queueUtxos()

	// Start a goroutine to simulate transactions.
	a.wg.Add(1)
	go a.simulateTx(com.downstream, com.txpool)

	// Start a goroutine to split utxos
	a.wg.Add(1)
	go a.splitUtxos(com.split, com.txpool)

	return nil
}

// simulateTx runs as a goroutine and simulates transactions between actors
//
// It receives a random address downstream, dequeues a utxo, sends a raw
// transaction to the address using the utxo as input
func (a *Actor) simulateTx(downstream <-chan btcutil.Address, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case addr := <-downstream:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{
					addr: amt,
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// splitUtxos runs as a goroutine and builds up a large set of utxos that
// can be used to simulate large tx/block ratios
//
// It receives a 'split' which is int that indicates the number of resultant utxos
// the tx is sent to addresses from the same actor since we're only interested in
// building up the utxo set
func (a *Actor) splitUtxos(split <-chan int, txpool chan<- struct{}) {
	defer a.wg.Done()

	for {
		select {
		case utxo := <-a.utxoQueue.dequeue:
			select {
			case split := <-split:
				// Create a raw transaction
				inputs := []btcjson.TransactionInput{{
					Txid: utxo.OutPoint.Hash.String(),
					Vout: utxo.OutPoint.Index,
				}}

				// Provide a fees of minFee to ensure the tx gets mined
				// the utxo amount is guaranteed to be > maxSplit*minFee
				amt := utxo.Amount - minFee
				amounts := map[btcutil.Address]btcutil.Amount{}

				// Create a output of random amount and sent it
				// to a random address from the address space
				// total number of outputs is split+1, taking into
				// account this utxo which is consumed in the process

				// set a rand start index for getting different random addrs
				randomIndex := rand.Int() % len(a.ownedAddresses)
				for i := 0; i <= split; i++ {
					var to btcutil.Address
					var change btcutil.Amount
					// pick a random address
					if randomIndex+i < len(a.ownedAddresses) {
						to = a.ownedAddresses[randomIndex+i]
					} else {
						// wrap around incase of an overflow
						to = a.ownedAddresses[randomIndex+i-len(a.ownedAddresses)]
					}
					if i == split {
						// last split, so just set the change
						change = amt
					} else {
						// pick a random change amount which is less than amt
						// but have a lower bound at minFee
						change = btcutil.Amount(rand.Int63n(int64(amt) / 2))
						if change < minFee {
							change = minFee
						}
						amt -= change
					}
					amounts[to] = change
				}

				err := a.sendRawTransaction(inputs, amounts)
				if err != nil {
					log.Printf("%s: Error sending raw transaction: %v", a, err)
					select {
					case txpool <- struct{}{}:
					case <-a.quit:
						return
					}
					continue
				}

			case <-a.quit:
				return
			}
		case <-a.quit:
			return
		}
	}
}

// sendRawTransaction creates a raw transaction, signs it and sends it
func (a *Actor) sendRawTransaction(inputs []btcjson.TransactionInput, amounts map[btcutil.Address]btcutil.Amount) error {
	msgTx, err := a.client.CreateRawTransaction(inputs, amounts, nil)
	if err != nil {
		return err
	}
	// sign it
	msgTx, ok, err := a.client.SignRawTransaction(msgTx)
	if err != nil {
		return err
	}
	if !ok {
		return err
	}
	// and finally send it.
	if _, err := a.client.SendRawTransaction(msgTx, false); err != nil {
		return err
	}
	return nil
}

// queueUtxos receives utxos belonging to this actor and queues them up
func (a *Actor) queueUtxos() {
	defer a.wg.Done()

	enqueue := a.utxoQueue.enqueue
	var dequeue chan *TxOut
	var next *TxOut
out:
	for {
		select {
		case n, ok := <-enqueue:
			if !ok {
				// If no utxos are queued for handling,
				// the queue is finished.
				if len(a.utxoQueue.utxos) == 0 {
					break out
				}
				// nil channel so no more reads can occur.
				enqueue = nil
				continue
			}
			if len(a.utxoQueue.utxos) == 0 {
				next = n
				dequeue = a.utxoQueue.dequeue
			}
			a.utxoQueue.utxos = append(a.utxoQueue.utxos, n)
		case dequeue <- next:
			a.utxoQueue.utxos[0] = nil
			a.utxoQueue.utxos = a.utxoQueue.utxos[1:]
			if len(a.utxoQueue.utxos) != 0 {
				next = a.utxoQueue.utxos[0]
			} else {
				// If no more utxos can be enqueued, the
				// queue is finished.
				if enqueue == nil {
					break out
				}
				dequeue = nil
			}
		case <-a.quit:
			break out
		}
	}
	close(a.utxoQueue.dequeue)
}

// Shutdown performs a shutdown down the actor by first signalling
// all goroutines to stop, waiting for them to stop and them cleaning up
func (a *Actor) Shutdown() {
	select {
	case <-a.quit:
	default:
		close(a.quit)
		a.WaitForShutdown()
		a.Node.Shutdown()
	}
}

// WaitForShutdown waits until every actor goroutine has returned
func (a *Actor) WaitForShutdown() {
	a.wg.Wait()
}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative Authors

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

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package metricskey

import "k8s.io/apimachinery/pkg/util/sets"

// TODO should be moved to serving. See https://github.com/knative/pkg/issues/608

const (
	// ResourceTypeKnativeRevision is the Stackdriver resource type for Knative revision
	ResourceTypeKnativeRevision = "knative_revision"

	// LabelServiceName is the label for the deployed service name
	LabelServiceName = "service_name"

	// LabelRouteName is the label for immutable name of the route that receives the request
	LabelRouteName = "route_name"

	// LabelConfigurationName is the label for the configuration which created the monitored revision
	LabelConfigurationName = "configuration_name"

	// LabelRevisionName is the label for the monitored revision
	LabelRevisionName = "revision_name"
)

var (
	// KnativeRevisionLabels stores the set of resource labels for resource type knative_revision.
	// LabelRouteName is added as extra label since it is optional, not in this map.
	KnativeRevisionLabels = sets.NewString(
		LabelProject,
		LabelLocation,
		LabelClusterName,
		LabelNamespaceName,
		LabelServiceName,
		LabelConfigurationName,
		LabelRevisionName,
	)

	// KnativeRevisionMetrics stores a set of metric types which are supported
	// by resource type knative_revision.
	KnativeRevisionMetrics = sets.NewString(
		"knative.dev/internal/serving/activator/request_count",
		"knative.dev/internal/serving/activator/request_latencies",
		"knative.dev/serving/autoscaler/desired_pods",
		"knative.dev/serving/autoscaler/requested_pods",
		"knative.dev/serving/autoscaler/actual_pods",
		"knative.dev/serving/autoscaler/stable_request_concurrency",
		"knative.dev/serving/autoscaler/panic_request_concurrency",
		"knative.dev/serving/autoscaler/target_concurrency_per_pod",
		"knative.dev/serving/autoscaler/panic_mode",
		"knative.dev/internal/serving/revision/request_count",
		"knative.dev/internal/serving/revision/request_latencies",
		"knative.dev/internal/serving/controller/cert_expiration_durations",
		"knative.dev/internal/serving/controller/cert_total_num",
	)
)
<commit_msg>add metrics for recording knative certificate issuance latencies (#940)<commit_after>/*
Copyright 2019 The Knative Authors

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

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package metricskey

import "k8s.io/apimachinery/pkg/util/sets"

// TODO should be moved to serving. See https://github.com/knative/pkg/issues/608

const (
	// ResourceTypeKnativeRevision is the Stackdriver resource type for Knative revision
	ResourceTypeKnativeRevision = "knative_revision"

	// LabelServiceName is the label for the deployed service name
	LabelServiceName = "service_name"

	// LabelRouteName is the label for immutable name of the route that receives the request
	LabelRouteName = "route_name"

	// LabelConfigurationName is the label for the configuration which created the monitored revision
	LabelConfigurationName = "configuration_name"

	// LabelRevisionName is the label for the monitored revision
	LabelRevisionName = "revision_name"
)

var (
	// KnativeRevisionLabels stores the set of resource labels for resource type knative_revision.
	// LabelRouteName is added as extra label since it is optional, not in this map.
	KnativeRevisionLabels = sets.NewString(
		LabelProject,
		LabelLocation,
		LabelClusterName,
		LabelNamespaceName,
		LabelServiceName,
		LabelConfigurationName,
		LabelRevisionName,
	)

	// KnativeRevisionMetrics stores a set of metric types which are supported
	// by resource type knative_revision.
	KnativeRevisionMetrics = sets.NewString(
		"knative.dev/internal/serving/activator/request_count",
		"knative.dev/internal/serving/activator/request_latencies",
		"knative.dev/serving/autoscaler/desired_pods",
		"knative.dev/serving/autoscaler/requested_pods",
		"knative.dev/serving/autoscaler/actual_pods",
		"knative.dev/serving/autoscaler/stable_request_concurrency",
		"knative.dev/serving/autoscaler/panic_request_concurrency",
		"knative.dev/serving/autoscaler/target_concurrency_per_pod",
		"knative.dev/serving/autoscaler/panic_mode",
		"knative.dev/internal/serving/revision/request_count",
		"knative.dev/internal/serving/revision/request_latencies",
		"knative.dev/internal/serving/controller/cert_expiration_durations",
		"knative.dev/internal/serving/controller/cert_total_num",
		"knative.dev/internal/serving/controller/cert_issuance_latencies",
	)
)
<|endoftext|>


<commit_before>// Copyright (c) 2019 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package build

import (
	"fmt"
	"io"
	"io/ioutil"

	"github.com/tsavola/gate/build/resolve"
	"github.com/tsavola/gate/image"
	"github.com/tsavola/gate/internal/error/badprogram"
	"github.com/tsavola/gate/internal/error/notfound"
	"github.com/tsavola/gate/internal/error/resourcelimit"
	"github.com/tsavola/gate/internal/executable"
	"github.com/tsavola/gate/snapshot"
	"github.com/tsavola/gate/snapshot/wasm"
	"github.com/tsavola/wag/binding"
	"github.com/tsavola/wag/compile"
	"github.com/tsavola/wag/object"
	"github.com/tsavola/wag/section"
	"github.com/tsavola/wag/wa"
)

const minSnapshotVersion = 0

type Build struct {
	Image                     *image.Build
	SectionMap                image.SectionMap
	Loaders                   map[string]section.CustomContentLoader
	Config                    compile.Config
	Module                    compile.Module
	StackSize                 int
	maxMemorySize             int // For instance.
	entryIndex                int
	snapshot                  bool
	monotonicTime             uint64
	Buffers                   snapshot.Buffers
	bufferSectionHeaderLength int
}

func New(storage image.Storage, moduleSize, maxTextSize int, objectMap *object.CallMap, instance bool,
) (b *Build, err error) {
	b = new(Build)

	b.Image, err = image.NewBuild(storage, moduleSize, maxTextSize, objectMap, instance)
	if err != nil {
		return
	}

	b.Loaders = make(map[string]section.CustomContentLoader)

	b.Config = compile.Config{
		SectionMapper: b.SectionMap.Mapper(),
	}

	b.entryIndex = -1
	return
}

func (b *Build) InstallEarlySnapshotLoaders() {
	b.Loaders[wasm.SectionSnapshot] = func(_ string, r section.Reader, length uint32) (err error) {
		b.installDuplicateSnapshotLoader()

		if length == 0 {
			err = badprogram.Err("gate.snapshot section is empty")
			return
		}

		version, n, err := readVaruint64(r)
		if err != nil {
			return
		}
		length -= uint32(n)

		if version < minSnapshotVersion {
			err = badprogram.Err(fmt.Sprintf("unsupported snapshot version: %d", version))
			return
		}

		_, err = r.ReadByte() // Flags
		if err != nil {
			return
		}
		length--

		b.monotonicTime, n, err = readVaruint64(r)
		if err != nil {
			return
		}
		length -= uint32(n)

		_, err = io.CopyN(ioutil.Discard, r, int64(length))
		if err != nil {
			return
		}

		b.SectionMap.Snapshot = b.SectionMap.Sections[section.Custom]
		b.snapshot = true
		return
	}

	b.Loaders[wasm.SectionExport] = func(_ string, r section.Reader, length uint32) (err error) {
		if !b.snapshot {
			err = badprogram.Err("gate.export section without gate.snapshot section")
			return
		}

		if length < 2 { // Minimum standard section frame size.
			err = badprogram.Err("gate.export section is too short")
			return
		}

		id, err := r.ReadByte()
		if err != nil {
			return
		}
		if id != byte(section.Export) {
			err = badprogram.Err("gate.export section does not contain a standard export section")
			return
		}
		err = r.UnreadByte()
		if err != nil {
			return
		}

		// Don't read payload; wag will load it as a standard section.
		// Duplicate sections are checked automatically.

		b.SectionMap.ExportWrap = b.SectionMap.Sections[section.Custom]
		return
	}

	b.Loaders[wasm.SectionBuffer] = func(_ string, r section.Reader, length uint32) (err error) {
		return badprogram.Err("gate.buffer section appears too early in wasm module")
	}

	b.Loaders[wasm.SectionStack] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.stack section appears too early in wasm module")
	}

	// Loaders keys should not change after this.
	b.Config.CustomSectionLoader = section.CustomLoader(b.Loaders)
}

func (b *Build) ModuleConfig() *compile.ModuleConfig {
	return &compile.ModuleConfig{
		Config: b.Config,
	}
}

// SetMaxMemorySize after initial module sections have been loaded.
func (b *Build) SetMaxMemorySize(maxMemorySize int) (err error) {
	if limit := b.Module.MemorySizeLimit(); limit >= 0 && maxMemorySize > limit {
		maxMemorySize = limit
	}
	b.maxMemorySize = alignMemorySize(maxMemorySize)

	if b.Module.InitialMemorySize() > b.maxMemorySize {
		err = resourcelimit.New("initial program memory size exceeds instance memory size limit")
		return
	}

	return
}

// BindFunctions (imports and optional start and entry functions) after initial
// module sections have been loaded.
func (b *Build) BindFunctions(entryName string) (err error) {
	if m := &b.SectionMap; m.ExportWrap.Length != 0 {
		exportLen := m.Sections[section.Export].Length

		// We didn't read the custom export section content, so offsets are
		// off.  Fix them.
		for i := int(section.Export); i < len(m.Sections); i++ {
			m.Sections[i].Offset -= exportLen
		}
		if m.Buffer.Length != 0 {
			m.Buffer.Offset -= exportLen
		}
		if m.Stack.Length != 0 {
			m.Stack.Offset -= exportLen
		}

		// Validate export wrapper payload length before accessing exports.
		exportEnd := m.Sections[section.Export].Offset + exportLen
		wrapperEnd := m.ExportWrap.Offset + m.ExportWrap.Length
		if exportEnd != wrapperEnd {
			err = badprogram.Err("gate.export section length does not match wrapped export section length")
			return
		}
	}

	err = binding.BindImports(&b.Module, b.Image.ImportResolver())
	if err != nil {
		return
	}

	if !b.snapshot {
		b.entryIndex, err = resolve.EntryFunc(b.Module, entryName)
		if err != nil {
			return
		}
	}

	return
}

func (b *Build) CodeConfig(mapper compile.ObjectMapper) *compile.CodeConfig {
	return &compile.CodeConfig{
		Text:   b.Image.TextBuffer(),
		Mapper: mapper,
		Config: b.Config,
	}
}

func (b *Build) InstallSnapshotDataLoaders() {
	b.Loaders[wasm.SectionBuffer] = func(_ string, r section.Reader, length uint32) (err error) {
		b.installLateSnapshotLoader()
		b.installDuplicateBufferLoader()

		if !b.snapshot {
			err = badprogram.Err("gate.buffer section without gate.snapshot section")
			return
		}

		var dataBuf []byte

		b.Buffers, b.bufferSectionHeaderLength, dataBuf, err = wasm.ReadBufferSectionHeader(r, length)
		if err != nil {
			return
		}

		_, err = io.ReadFull(r, dataBuf)
		if err != nil {
			return
		}

		_, err = io.CopyN(ioutil.Discard, r, int64(length)-int64(b.bufferSectionHeaderLength)-int64(len(dataBuf)))
		if err != nil {
			return
		}

		b.SectionMap.Buffer = b.SectionMap.Sections[section.Custom]
		return
	}

	b.Loaders[wasm.SectionStack] = func(_ string, r section.Reader, length uint32) (err error) {
		b.installLateSnapshotLoader()
		b.installLateBufferLoader()
		b.installDuplicateStackLoader()

		if !b.snapshot {
			err = badprogram.Err("gate.stack section without gate.snapshot section")
			return
		}

		if b.entryIndex >= 0 {
			err = notfound.ErrSuspended
			return
		}

		if length > uint32(b.StackSize)-executable.StackUsageOffset {
			err = badprogram.Err("gate.stack section is too large")
			return
		}

		err = b.finishImageText(int(length))
		if err != nil {
			return
		}

		err = b.Image.ReadStack(r, b.Module.Types(), b.Module.FuncTypeIndexes())
		if err != nil {
			return
		}

		b.SectionMap.Stack = b.SectionMap.Sections[section.Custom]
		return
	}
}

func (b *Build) installDuplicateSnapshotLoader() {
	b.Loaders[wasm.SectionSnapshot] = func(string, section.Reader, uint32) error {
		return badprogram.Err("multiple gate.snapshot sections in wasm module")
	}
}

func (b *Build) installDuplicateBufferLoader() {
	b.Loaders[wasm.SectionBuffer] = func(string, section.Reader, uint32) error {
		return badprogram.Err("multiple gate.buffer sections in wasm module")
	}
}

func (b *Build) installDuplicateStackLoader() {
	b.Loaders[wasm.SectionStack] = func(string, section.Reader, uint32) error {
		return badprogram.Err("multiple gate.stack sections in wasm module")
	}
}

func (b *Build) finishImageText(stackUsage int) error {
	return b.Image.FinishText(b.StackSize, stackUsage, b.Module.GlobalsSize(), b.Module.InitialMemorySize())
}

// FinishImageText after code and snapshot sections have been loaded.
func (b *Build) FinishImageText() (err error) {
	if b.SectionMap.Stack.Offset != 0 {
		return // Already done by stack section loader.
	}

	return b.finishImageText(0)
}

func (b *Build) InstallLateSnapshotLoaders() {
	b.installLateSnapshotLoader()
	b.installLateBufferLoader()
	b.installLateStackLoader()
}

func (b *Build) installLateSnapshotLoader() {
	b.Loaders[wasm.SectionSnapshot] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.snapshot section appears too late in wasm module")
	}
}

func (b *Build) installLateBufferLoader() {
	b.Loaders[wasm.SectionBuffer] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.buffer section appears too late in wasm module")
	}
}

func (b *Build) installLateStackLoader() {
	b.Loaders[wasm.SectionStack] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.stack section appears too late in wasm module")
	}
}

// DataConfig is valid after FinishText.
func (b *Build) DataConfig() *compile.DataConfig {
	return &compile.DataConfig{
		GlobalsMemory:   b.Image.GlobalsMemoryBuffer(),
		MemoryAlignment: b.Image.MemoryAlignment(),
		Config:          b.Config,
	}
}

// FinishProgramImage after module, stack, globals and memory have been
// populated.
func (b *Build) FinishProgramImage() (*image.Program, error) {
	startIndex := -1
	if i, ok := b.Module.StartFunc(); ok {
		startIndex = int(i)
	}

	return b.Image.FinishProgram(b.SectionMap, b.Module, startIndex, true, b.monotonicTime, b.bufferSectionHeaderLength)
}

// FinishInstanceImage after program image has been finished.
func (b *Build) FinishInstanceImage(prog *image.Program) (*image.Instance, error) {
	return b.Image.FinishInstance(prog, b.maxMemorySize, b.entryIndex)
}

func (b *Build) Close() error {
	return b.Image.Close()
}

func alignMemorySize(size int) int {
	mask := wa.PageSize - 1
	return (size + mask) &^ mask
}

func readVaruint64(r section.Reader) (x uint64, n int, err error) {
	var shift uint
	for n = 1; ; n++ {
		var b byte
		b, err = r.ReadByte()
		if err != nil {
			return
		}
		if b < 0x80 {
			if n > 9 || n == 9 && b > 1 {
				err = badprogram.Err("varuint64 is too large")
				return
			}
			x |= uint64(b) << shift
			return
		}
		x |= (uint64(b) & 0x7f) << shift
		shift += 7
	}
}
<commit_msg>build: no entry function when suspended<commit_after>// Copyright (c) 2019 Timo Savola. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package build

import (
	"fmt"
	"io"
	"io/ioutil"

	"github.com/tsavola/gate/build/resolve"
	"github.com/tsavola/gate/image"
	"github.com/tsavola/gate/internal/error/badprogram"
	"github.com/tsavola/gate/internal/error/notfound"
	"github.com/tsavola/gate/internal/error/resourcelimit"
	"github.com/tsavola/gate/internal/executable"
	"github.com/tsavola/gate/snapshot"
	"github.com/tsavola/gate/snapshot/wasm"
	"github.com/tsavola/wag/binding"
	"github.com/tsavola/wag/compile"
	"github.com/tsavola/wag/object"
	"github.com/tsavola/wag/section"
	"github.com/tsavola/wag/wa"
)

const minSnapshotVersion = 0

type Build struct {
	Image                     *image.Build
	SectionMap                image.SectionMap
	Loaders                   map[string]section.CustomContentLoader
	Config                    compile.Config
	Module                    compile.Module
	StackSize                 int
	maxMemorySize             int // For instance.
	entryIndex                int
	snapshot                  bool
	monotonicTime             uint64
	Buffers                   snapshot.Buffers
	bufferSectionHeaderLength int
}

func New(storage image.Storage, moduleSize, maxTextSize int, objectMap *object.CallMap, instance bool,
) (b *Build, err error) {
	b = new(Build)

	b.Image, err = image.NewBuild(storage, moduleSize, maxTextSize, objectMap, instance)
	if err != nil {
		return
	}

	b.Loaders = make(map[string]section.CustomContentLoader)

	b.Config = compile.Config{
		SectionMapper: b.SectionMap.Mapper(),
	}

	b.entryIndex = -1
	return
}

func (b *Build) InstallEarlySnapshotLoaders() {
	b.Loaders[wasm.SectionSnapshot] = func(_ string, r section.Reader, length uint32) (err error) {
		b.installDuplicateSnapshotLoader()

		if length == 0 {
			err = badprogram.Err("gate.snapshot section is empty")
			return
		}

		version, n, err := readVaruint64(r)
		if err != nil {
			return
		}
		length -= uint32(n)

		if version < minSnapshotVersion {
			err = badprogram.Err(fmt.Sprintf("unsupported snapshot version: %d", version))
			return
		}

		_, err = r.ReadByte() // Flags
		if err != nil {
			return
		}
		length--

		b.monotonicTime, n, err = readVaruint64(r)
		if err != nil {
			return
		}
		length -= uint32(n)

		_, err = io.CopyN(ioutil.Discard, r, int64(length))
		if err != nil {
			return
		}

		b.SectionMap.Snapshot = b.SectionMap.Sections[section.Custom]
		b.snapshot = true
		return
	}

	b.Loaders[wasm.SectionExport] = func(_ string, r section.Reader, length uint32) (err error) {
		if !b.snapshot {
			err = badprogram.Err("gate.export section without gate.snapshot section")
			return
		}

		if length < 2 { // Minimum standard section frame size.
			err = badprogram.Err("gate.export section is too short")
			return
		}

		id, err := r.ReadByte()
		if err != nil {
			return
		}
		if id != byte(section.Export) {
			err = badprogram.Err("gate.export section does not contain a standard export section")
			return
		}
		err = r.UnreadByte()
		if err != nil {
			return
		}

		// Don't read payload; wag will load it as a standard section.
		// Duplicate sections are checked automatically.

		b.SectionMap.ExportWrap = b.SectionMap.Sections[section.Custom]
		return
	}

	b.Loaders[wasm.SectionBuffer] = func(_ string, r section.Reader, length uint32) (err error) {
		return badprogram.Err("gate.buffer section appears too early in wasm module")
	}

	b.Loaders[wasm.SectionStack] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.stack section appears too early in wasm module")
	}

	// Loaders keys should not change after this.
	b.Config.CustomSectionLoader = section.CustomLoader(b.Loaders)
}

func (b *Build) ModuleConfig() *compile.ModuleConfig {
	return &compile.ModuleConfig{
		Config: b.Config,
	}
}

// SetMaxMemorySize after initial module sections have been loaded.
func (b *Build) SetMaxMemorySize(maxMemorySize int) (err error) {
	if limit := b.Module.MemorySizeLimit(); limit >= 0 && maxMemorySize > limit {
		maxMemorySize = limit
	}
	b.maxMemorySize = alignMemorySize(maxMemorySize)

	if b.Module.InitialMemorySize() > b.maxMemorySize {
		err = resourcelimit.New("initial program memory size exceeds instance memory size limit")
		return
	}

	return
}

// BindFunctions (imports and entry function) after initial module sections
// have been loaded.
func (b *Build) BindFunctions(entryName string) (err error) {
	m := &b.SectionMap
	if m.ExportWrap.Length != 0 {
		exportLen := m.Sections[section.Export].Length

		// We didn't read the custom export section content, so offsets are
		// off.  Fix them.
		for i := int(section.Export); i < len(m.Sections); i++ {
			m.Sections[i].Offset -= exportLen
		}
		if m.Buffer.Length != 0 {
			m.Buffer.Offset -= exportLen
		}
		if m.Stack.Length != 0 {
			m.Stack.Offset -= exportLen
		}

		// Validate export wrapper payload length before accessing exports.
		exportEnd := m.Sections[section.Export].Offset + exportLen
		wrapperEnd := m.ExportWrap.Offset + m.ExportWrap.Length
		if exportEnd != wrapperEnd {
			err = badprogram.Err("gate.export section length does not match wrapped export section length")
			return
		}
	}

	err = binding.BindImports(&b.Module, b.Image.ImportResolver())
	if err != nil {
		return
	}

	if m.ExportWrap.Length != 0 {
		// Exports are hidden.
		if entryName != "" {
			err = notfound.ErrSuspended
			return
		}
	} else {
		b.entryIndex, err = resolve.EntryFunc(b.Module, entryName)
		if err != nil {
			return
		}
	}

	return
}

func (b *Build) CodeConfig(mapper compile.ObjectMapper) *compile.CodeConfig {
	return &compile.CodeConfig{
		Text:   b.Image.TextBuffer(),
		Mapper: mapper,
		Config: b.Config,
	}
}

func (b *Build) InstallSnapshotDataLoaders() {
	b.Loaders[wasm.SectionBuffer] = func(_ string, r section.Reader, length uint32) (err error) {
		b.installLateSnapshotLoader()
		b.installDuplicateBufferLoader()

		if !b.snapshot {
			err = badprogram.Err("gate.buffer section without gate.snapshot section")
			return
		}

		var dataBuf []byte

		b.Buffers, b.bufferSectionHeaderLength, dataBuf, err = wasm.ReadBufferSectionHeader(r, length)
		if err != nil {
			return
		}

		_, err = io.ReadFull(r, dataBuf)
		if err != nil {
			return
		}

		_, err = io.CopyN(ioutil.Discard, r, int64(length)-int64(b.bufferSectionHeaderLength)-int64(len(dataBuf)))
		if err != nil {
			return
		}

		b.SectionMap.Buffer = b.SectionMap.Sections[section.Custom]
		return
	}

	b.Loaders[wasm.SectionStack] = func(_ string, r section.Reader, length uint32) (err error) {
		b.installLateSnapshotLoader()
		b.installLateBufferLoader()
		b.installDuplicateStackLoader()

		if !b.snapshot {
			err = badprogram.Err("gate.stack section without gate.snapshot section")
			return
		}

		if b.entryIndex >= 0 {
			err = notfound.ErrSuspended
			return
		}

		if length > uint32(b.StackSize)-executable.StackUsageOffset {
			err = badprogram.Err("gate.stack section is too large")
			return
		}

		err = b.finishImageText(int(length))
		if err != nil {
			return
		}

		err = b.Image.ReadStack(r, b.Module.Types(), b.Module.FuncTypeIndexes())
		if err != nil {
			return
		}

		b.SectionMap.Stack = b.SectionMap.Sections[section.Custom]
		return
	}
}

func (b *Build) installDuplicateSnapshotLoader() {
	b.Loaders[wasm.SectionSnapshot] = func(string, section.Reader, uint32) error {
		return badprogram.Err("multiple gate.snapshot sections in wasm module")
	}
}

func (b *Build) installDuplicateBufferLoader() {
	b.Loaders[wasm.SectionBuffer] = func(string, section.Reader, uint32) error {
		return badprogram.Err("multiple gate.buffer sections in wasm module")
	}
}

func (b *Build) installDuplicateStackLoader() {
	b.Loaders[wasm.SectionStack] = func(string, section.Reader, uint32) error {
		return badprogram.Err("multiple gate.stack sections in wasm module")
	}
}

func (b *Build) finishImageText(stackUsage int) error {
	return b.Image.FinishText(b.StackSize, stackUsage, b.Module.GlobalsSize(), b.Module.InitialMemorySize())
}

// FinishImageText after code and snapshot sections have been loaded.
func (b *Build) FinishImageText() (err error) {
	if b.SectionMap.Stack.Offset != 0 {
		return // Already done by stack section loader.
	}

	return b.finishImageText(0)
}

func (b *Build) InstallLateSnapshotLoaders() {
	b.installLateSnapshotLoader()
	b.installLateBufferLoader()
	b.installLateStackLoader()
}

func (b *Build) installLateSnapshotLoader() {
	b.Loaders[wasm.SectionSnapshot] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.snapshot section appears too late in wasm module")
	}
}

func (b *Build) installLateBufferLoader() {
	b.Loaders[wasm.SectionBuffer] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.buffer section appears too late in wasm module")
	}
}

func (b *Build) installLateStackLoader() {
	b.Loaders[wasm.SectionStack] = func(string, section.Reader, uint32) error {
		return badprogram.Err("gate.stack section appears too late in wasm module")
	}
}

// DataConfig is valid after FinishText.
func (b *Build) DataConfig() *compile.DataConfig {
	return &compile.DataConfig{
		GlobalsMemory:   b.Image.GlobalsMemoryBuffer(),
		MemoryAlignment: b.Image.MemoryAlignment(),
		Config:          b.Config,
	}
}

// FinishProgramImage after module, stack, globals and memory have been
// populated.
func (b *Build) FinishProgramImage() (*image.Program, error) {
	startIndex := -1
	if i, ok := b.Module.StartFunc(); ok {
		startIndex = int(i)
	}

	return b.Image.FinishProgram(b.SectionMap, b.Module, startIndex, true, b.monotonicTime, b.bufferSectionHeaderLength)
}

// FinishInstanceImage after program image has been finished.
func (b *Build) FinishInstanceImage(prog *image.Program) (*image.Instance, error) {
	return b.Image.FinishInstance(prog, b.maxMemorySize, b.entryIndex)
}

func (b *Build) Close() error {
	return b.Image.Close()
}

func alignMemorySize(size int) int {
	mask := wa.PageSize - 1
	return (size + mask) &^ mask
}

func readVaruint64(r section.Reader) (x uint64, n int, err error) {
	var shift uint
	for n = 1; ; n++ {
		var b byte
		b, err = r.ReadByte()
		if err != nil {
			return
		}
		if b < 0x80 {
			if n > 9 || n == 9 && b > 1 {
				err = badprogram.Err("varuint64 is too large")
				return
			}
			x |= uint64(b) << shift
			return
		}
		x |= (uint64(b) & 0x7f) << shift
		shift += 7
	}
}
<|endoftext|>


<commit_before>package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
)

var binaryPath = flag.String("bin", "", "binary to be included into the package")

type Build struct {
	appName    string
	version    string
	output     string
	binaryPath string
}

func main() {
	flag.Parse()

	if *binaryPath == "" {
		fmt.Println("please specify application binary with --bin flag")
		os.Exit(1)
	}

	if !fileExist(*binaryPath) {
		fmt.Printf("specified binary doesn't exist: %s\n", *binaryPath)
		os.Exit(1)
	}

	// use binary name as appName
	appName := filepath.Base(*binaryPath)

	build := &Build{
		appName:    appName,
		version:    "0.0.1",
		binaryPath: *binaryPath,
	}

	build.do()
}

func (b *Build) do() {
	switch runtime.GOOS {
	case "darwin":
		b.darwin()
	default:
		fmt.Printf("not supported os: %s.\n", runtime.GOOS)
	}
}

// darwin is building a new .pkg installer for darwin based OS'es.
func (b *Build) darwin() {
	version := b.version
	if b.output == "" {
		b.output = fmt.Sprintf("koding-%s", b.appName)
	}

	scriptDir := "./darwin/scripts"
	installRoot := "./root" // TODO REMOVE

	os.RemoveAll(installRoot) // clean up old build before we continue
	installRootUsr := filepath.Join(installRoot, "/usr/local/bin")

	os.MkdirAll(installRootUsr, 0755)
	err := copyFile(b.binaryPath, installRootUsr+"/"+b.appName)
	if err != nil {
		fmt.Println(err)
		os.Exit(1)
	}

	tempDest, err := ioutil.TempDir("", "tempDest")
	if err != nil {
		return
	}
	defer os.RemoveAll(tempDest)

	b.createScripts(scriptDir)
	b.createLaunchAgent(installRoot)

	cmdPkg := exec.Command("pkgbuild",
		"--identifier", fmt.Sprintf("com.koding.kite.%s.pkg", b.appName),
		"--version", version,
		"--scripts", scriptDir,
		"--root", installRoot,
		"--install-location", "/",
		fmt.Sprintf("%s/com.koding.kite.%s.pkg", tempDest, b.appName),
		// used for next step, also set up for distribution.xml
	)

	res, err := cmdPkg.CombinedOutput()
	if err != nil {
		fmt.Println("res, err", string(res), err)
		return
	}

	distributionFile := "./darwin/Distribution.xml"
	resources := "./darwin/Resources"
	targetFile := b.output + ".pkg"

	b.createDistribution(distributionFile)

	cmdBuild := exec.Command("productbuild",
		"--distribution", distributionFile,
		"--resources", resources,
		"--package-path", tempDest,
		targetFile,
	)

	res, err = cmdBuild.CombinedOutput()
	if err != nil {
		fmt.Println("res, err", string(res), err)
		return
	}

	fmt.Println("everything is ok")

}

func (b *Build) createLaunchAgent(rootDir string) {
	launchDir := fmt.Sprintf("%s/Library/LaunchAgents/", rootDir)
	os.MkdirAll(launchDir, 0700)

	launchFile := fmt.Sprintf("%s/com.koding.kite.%s.plist", launchDir, b.appName)

	lFile, err := os.Create(launchFile)
	if err != nil {
		log.Fatalln(err)
	}

	t := template.Must(template.New("launchAgent").Parse(launchAgent))
	t.Execute(lFile, b.appName)

}

func (b *Build) createDistribution(file string) {
	distFile, err := os.Create(file)
	if err != nil {
		log.Fatalln(err)
	}

	t := template.Must(template.New("distribution").Parse(distribution))
	t.Execute(distFile, b.appName)

}

func (b *Build) createScripts(scriptDir string) {
	os.MkdirAll(scriptDir, 0700) // does return nil if exists

	postInstallFile, err := os.Create(scriptDir + "/postInstall")
	if err != nil {
		log.Fatalln(err)
	}
	postInstallFile.Chmod(0755)

	preInstallFile, err := os.Create(scriptDir + "/preInstall")
	if err != nil {
		log.Fatalln(err)
	}
	preInstallFile.Chmod(0755)

	t := template.Must(template.New("postInstall").Parse(postInstall))
	t.Execute(postInstallFile, b.appName)

	t = template.Must(template.New("preInstall").Parse(preInstall))
	t.Execute(preInstallFile, b.appName)
}

func fileExist(dir string) bool {
	var err error
	_, err = os.Stat(dir)
	if err == nil {
		return true // file exist
	}

	if os.IsNotExist(err) {
		return false // file does not exist
	}

	panic(err) // permission errors or something else bad
}

func copyFile(src, dst string) error {
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	fi, err := sf.Stat()
	if err != nil {
		return err
	}

	if fi.IsDir() {
		return errors.New("src is a directory, please provide a file")
	}

	df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
	if err != nil {
		return err
	}
	defer df.Close()

	if _, err := io.Copy(df, sf); err != nil {
		return err
	}

	return nil
}
<commit_msg>build: build statements should return an error<commit_after>package main

import (
	"errors"
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"path/filepath"
	"runtime"
	"text/template"
)

var binaryPath = flag.String("bin", "", "binary to be included into the package")

type Build struct {
	appName    string
	version    string
	output     string
	binaryPath string
}

func main() {
	flag.Parse()

	if *binaryPath == "" {
		fmt.Println("please specify application binary with --bin flag")
		os.Exit(1)
	}

	if !fileExist(*binaryPath) {
		fmt.Printf("specified binary doesn't exist: %s\n", *binaryPath)
		os.Exit(1)
	}

	// use binary name as appName
	appName := filepath.Base(*binaryPath)

	build := &Build{
		appName:    appName,
		version:    "0.0.1",
		binaryPath: *binaryPath,
	}

	err := build.do()
	if err != nil {
		log.Println(err)
	} else {
		fmt.Println("build successfull")
	}

}

func (b *Build) do() error {
	switch runtime.GOOS {
	case "darwin":
		return b.darwin()
	default:
		return fmt.Errorf("not supported os: %s.\n", runtime.GOOS)
	}
}

func (b *Build) linux() error {
	return nil
}

// darwin is building a new .pkg installer for darwin based OS'es.
func (b *Build) darwin() error {
	version := b.version
	if b.output == "" {
		b.output = fmt.Sprintf("koding-%s", b.appName)
	}

	scriptDir := "./darwin/scripts"
	installRoot := "./root" // TODO REMOVE

	os.RemoveAll(installRoot) // clean up old build before we continue
	installRootUsr := filepath.Join(installRoot, "/usr/local/bin")

	os.MkdirAll(installRootUsr, 0755)
	err := copyFile(b.binaryPath, installRootUsr+"/"+b.appName)
	if err != nil {
		return err
	}

	tempDest, err := ioutil.TempDir("", "tempDest")
	if err != nil {
		return err
	}
	defer os.RemoveAll(tempDest)

	b.createScripts(scriptDir)
	b.createLaunchAgent(installRoot)

	cmdPkg := exec.Command("pkgbuild",
		"--identifier", fmt.Sprintf("com.koding.kite.%s.pkg", b.appName),
		"--version", version,
		"--scripts", scriptDir,
		"--root", installRoot,
		"--install-location", "/",
		fmt.Sprintf("%s/com.koding.kite.%s.pkg", tempDest, b.appName),
		// used for next step, also set up for distribution.xml
	)

	_, err = cmdPkg.CombinedOutput()
	if err != nil {
		return err
	}

	distributionFile := "./darwin/Distribution.xml"
	resources := "./darwin/Resources"
	targetFile := b.output + ".pkg"

	b.createDistribution(distributionFile)

	cmdBuild := exec.Command("productbuild",
		"--distribution", distributionFile,
		"--resources", resources,
		"--package-path", tempDest,
		targetFile,
	)

	_, err = cmdBuild.CombinedOutput()
	if err != nil {
		return err
	}

	return nil
}

func (b *Build) createLaunchAgent(rootDir string) {
	launchDir := fmt.Sprintf("%s/Library/LaunchAgents/", rootDir)
	os.MkdirAll(launchDir, 0700)

	launchFile := fmt.Sprintf("%s/com.koding.kite.%s.plist", launchDir, b.appName)

	lFile, err := os.Create(launchFile)
	if err != nil {
		log.Fatalln(err)
	}

	t := template.Must(template.New("launchAgent").Parse(launchAgent))
	t.Execute(lFile, b.appName)

}

func (b *Build) createDistribution(file string) {
	distFile, err := os.Create(file)
	if err != nil {
		log.Fatalln(err)
	}

	t := template.Must(template.New("distribution").Parse(distribution))
	t.Execute(distFile, b.appName)

}

func (b *Build) createScripts(scriptDir string) {
	os.MkdirAll(scriptDir, 0700) // does return nil if exists

	postInstallFile, err := os.Create(scriptDir + "/postInstall")
	if err != nil {
		log.Fatalln(err)
	}
	postInstallFile.Chmod(0755)

	preInstallFile, err := os.Create(scriptDir + "/preInstall")
	if err != nil {
		log.Fatalln(err)
	}
	preInstallFile.Chmod(0755)

	t := template.Must(template.New("postInstall").Parse(postInstall))
	t.Execute(postInstallFile, b.appName)

	t = template.Must(template.New("preInstall").Parse(preInstall))
	t.Execute(preInstallFile, b.appName)
}

func fileExist(dir string) bool {
	var err error
	_, err = os.Stat(dir)
	if err == nil {
		return true // file exist
	}

	if os.IsNotExist(err) {
		return false // file does not exist
	}

	panic(err) // permission errors or something else bad
}

func copyFile(src, dst string) error {
	sf, err := os.Open(src)
	if err != nil {
		return err
	}
	defer sf.Close()

	fi, err := sf.Stat()
	if err != nil {
		return err
	}

	if fi.IsDir() {
		return errors.New("src is a directory, please provide a file")
	}

	df, err := os.OpenFile(dst, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, fi.Mode())
	if err != nil {
		return err
	}
	defer df.Close()

	if _, err := io.Copy(df, sf); err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/urfave/cli"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
)

var packages = []string{"cli", "altsrc"}

func main() {
	app := cli.NewApp()

	app.Name = "builder"
	app.Usage = "Generates a new urfave/cli build!"

	app.Commands = cli.Commands{
		cli.Command{
			Name:   "vet",
			Action: VetActionFunc,
		},
		cli.Command{
			Name:   "test",
			Action: TestActionFunc,
		},
		cli.Command{
			Name:   "gfmrun",
			Action: GfmrunActionFunc,
		},
		cli.Command{
			Name:   "toc",
			Action: TocActionFunc,
		},
		cli.Command{
			Name:   "generate",
			Action: GenActionFunc,
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func VetActionFunc(_ *cli.Context) error {
	return exec.Command("go", "vet").Run()
}

func TestActionFunc(c *cli.Context) error {
	for _, pkg := range packages {
		var packageName string

		if pkg == "cli" {
			packageName = fmt.Sprintf("github.com/urfave/cli")
		} else {
			packageName = fmt.Sprintf("github.com/urfave/cli/%s", pkg)
		}

		coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg)

		err := exec.Command(
			"go", "test", "-v", coverProfile, packageName,
		).Run()

		if err != nil {
			return err
		}
	}

	return testCleanup()
}

func testCleanup() error {
	var out bytes.Buffer

	for _, pkg := range packages {
		file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(file)
		if err != nil {
			return err
		}

		out.Write(b)
		err = file.Close()
		if err != nil {
			return err
		}

		err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}
	}

	outFile, err := os.Create("coverage.txt")
	if err != nil {
		return err
	}

	_, err = out.WriteTo(outFile)
	if err != nil {
		return err
	}

	err = outFile.Close()
	if err != nil {
		return err
	}

	return nil
}

func GfmrunActionFunc(_ *cli.Context) error {
	file, err := os.Open("README.md")
	if err != nil {
		return err
	}

	var counter int
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "package main") {
			counter++
		}
	}

	err = scanner.Err()
	if err != nil {
		return err
	}

	return exec.Command("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md").Run()
}

func TocActionFunc(_ *cli.Context) error {
	err := exec.Command("node_modules/.bin/markdown-toc", "-i", "README.md").Run()
	if err != nil {
		return err
	}

	err = exec.Command("git", "diff", "--exit-code").Run()
	if err != nil {
		return err
	}

	return nil
}

func GenActionFunc(_ *cli.Context) error {
	err := exec.Command("go", "generate", "flag-gen/main.go").Run()
	if err != nil {
		return err
	}

	err = exec.Command("go", "generate", "cli.go").Run()
	if err != nil {
		return err
	}

	err = exec.Command("git", "diff", "--exit-code").Run()
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>remove unnecessary sprintf<commit_after>package main

import (
	"bufio"
	"bytes"
	"fmt"
	"github.com/urfave/cli"
	"io/ioutil"
	"log"
	"os"
	"os/exec"
	"strings"
)

var packages = []string{"cli", "altsrc"}

func main() {
	app := cli.NewApp()

	app.Name = "builder"
	app.Usage = "Generates a new urfave/cli build!"

	app.Commands = cli.Commands{
		cli.Command{
			Name:   "vet",
			Action: VetActionFunc,
		},
		cli.Command{
			Name:   "test",
			Action: TestActionFunc,
		},
		cli.Command{
			Name:   "gfmrun",
			Action: GfmrunActionFunc,
		},
		cli.Command{
			Name:   "toc",
			Action: TocActionFunc,
		},
		cli.Command{
			Name:   "generate",
			Action: GenActionFunc,
		},
	}

	err := app.Run(os.Args)
	if err != nil {
		log.Fatal(err)
	}
}

func VetActionFunc(_ *cli.Context) error {
	return exec.Command("go", "vet").Run()
}

func TestActionFunc(c *cli.Context) error {
	for _, pkg := range packages {
		var packageName string

		if pkg == "cli" {
			packageName = "github.com/urfave/cli"
		} else {
			packageName = fmt.Sprintf("github.com/urfave/cli/%s", pkg)
		}

		coverProfile := fmt.Sprintf("--coverprofile=%s.coverprofile", pkg)

		err := exec.Command(
			"go", "test", "-v", coverProfile, packageName,
		).Run()

		if err != nil {
			return err
		}
	}

	return testCleanup()
}

func testCleanup() error {
	var out bytes.Buffer

	for _, pkg := range packages {
		file, err := os.Open(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}

		b, err := ioutil.ReadAll(file)
		if err != nil {
			return err
		}

		out.Write(b)
		err = file.Close()
		if err != nil {
			return err
		}

		err = os.Remove(fmt.Sprintf("%s.coverprofile", pkg))
		if err != nil {
			return err
		}
	}

	outFile, err := os.Create("coverage.txt")
	if err != nil {
		return err
	}

	_, err = out.WriteTo(outFile)
	if err != nil {
		return err
	}

	err = outFile.Close()
	if err != nil {
		return err
	}

	return nil
}

func GfmrunActionFunc(_ *cli.Context) error {
	file, err := os.Open("README.md")
	if err != nil {
		return err
	}

	var counter int
	scanner := bufio.NewScanner(file)
	for scanner.Scan() {
		if strings.Contains(scanner.Text(), "package main") {
			counter++
		}
	}

	err = scanner.Err()
	if err != nil {
		return err
	}

	return exec.Command("gfmrun", "-c", fmt.Sprint(counter), "-s", "README.md").Run()
}

func TocActionFunc(_ *cli.Context) error {
	err := exec.Command("node_modules/.bin/markdown-toc", "-i", "README.md").Run()
	if err != nil {
		return err
	}

	err = exec.Command("git", "diff", "--exit-code").Run()
	if err != nil {
		return err
	}

	return nil
}

func GenActionFunc(_ *cli.Context) error {
	err := exec.Command("go", "generate", "flag-gen/main.go").Run()
	if err != nil {
		return err
	}

	err = exec.Command("go", "generate", "cli.go").Run()
	if err != nil {
		return err
	}

	err = exec.Command("git", "diff", "--exit-code").Run()
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


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

package repo

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/Unknwon/com"
	log "gopkg.in/clog.v1"

	"github.com/gogits/git-module"

	"github.com/G-Node/gogs/models"
	"github.com/G-Node/gogs/models/errors"
	"github.com/G-Node/gogs/pkg/context"
	"github.com/G-Node/gogs/pkg/form"
	"github.com/G-Node/gogs/pkg/setting"
	"github.com/G-Node/gogs/pkg/tool"
)

const (
	CREATE  = "repo/create"
	MIGRATE = "repo/migrate"
)

func MustBeNotBare(c *context.Context) {
	if c.Repo.Repository.IsBare {
		c.Handle(404, "MustBeNotBare", nil)
	}
}

func checkContextUser(c *context.Context, uid int64) *models.User {
	orgs, err := models.GetOwnedOrgsByUserIDDesc(c.User.ID, "updated_unix")
	if err != nil {
		c.Handle(500, "GetOwnedOrgsByUserIDDesc", err)
		return nil
	}
	c.Data["Orgs"] = orgs

	// Not equal means current user is an organization.
	if uid == c.User.ID || uid == 0 {
		return c.User
	}

	org, err := models.GetUserByID(uid)
	if errors.IsUserNotExist(err) {
		return c.User
	}

	if err != nil {
		c.Handle(500, "GetUserByID", fmt.Errorf("[%d]: %v", uid, err))
		return nil
	}

	// Check ownership of organization.
	if !org.IsOrganization() || !(c.User.IsAdmin || org.IsOwnedBy(c.User.ID)) {
		c.Error(403)
		return nil
	}
	return org
}

func Create(c *context.Context) {
	c.Data["Title"] = c.Tr("new_repo")

	// Give default value for template to render.
	c.Data["Gitignores"] = models.Gitignores
	c.Data["Licenses"] = models.Licenses
	c.Data["Readmes"] = models.Readmes
	c.Data["readme"] = "Default"
	c.Data["private"] = c.User.LastRepoVisibility
	c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate

	ctxUser := checkContextUser(c, c.QueryInt64("org"))
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	c.HTML(200, CREATE)
}

func handleCreateError(c *context.Context, owner *models.User, err error, name, tpl string, form interface{}) {
	switch {
	case errors.IsReachLimitOfRepo(err):
		c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", owner.RepoCreationNum()), tpl, form)
	case models.IsErrRepoAlreadyExist(err):
		c.Data["Err_RepoName"] = true
		c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
	case models.IsErrNameReserved(err):
		c.Data["Err_RepoName"] = true
		c.RenderWithErr(c.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
	case models.IsErrNamePatternNotAllowed(err):
		c.Data["Err_RepoName"] = true
		c.RenderWithErr(c.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
	default:
		c.Handle(500, name, err)
	}
}

func CreatePost(c *context.Context, f form.CreateRepo) {
	c.Data["Title"] = c.Tr("new_repo")

	c.Data["Gitignores"] = models.Gitignores
	c.Data["Licenses"] = models.Licenses
	c.Data["Readmes"] = models.Readmes

	ctxUser := checkContextUser(c, f.UserID)
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	if c.HasError() {
		c.HTML(200, CREATE)
		return
	}

	repo, err := models.CreateRepository(c.User, ctxUser, models.CreateRepoOptions{
		Name:        f.RepoName,
		Description: f.Description,
		Gitignores:  f.Gitignores,
		License:     f.License,
		Readme:      f.Readme,
		IsPrivate:   f.Private || setting.Repository.ForcePrivate,
		AutoInit:    f.AutoInit,
	})
	if err == nil {
		log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
		c.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	handleCreateError(c, ctxUser, err, "CreatePost", CREATE, &f)
}

func Migrate(c *context.Context) {
	c.Data["Title"] = c.Tr("new_migrate")
	c.Data["private"] = c.User.LastRepoVisibility
	c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
	c.Data["mirror"] = c.Query("mirror") == "1"

	ctxUser := checkContextUser(c, c.QueryInt64("org"))
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	c.HTML(200, MIGRATE)
}

func MigratePost(c *context.Context, f form.MigrateRepo) {
	c.Data["Title"] = c.Tr("new_migrate")

	ctxUser := checkContextUser(c, f.Uid)
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	if c.HasError() {
		c.HTML(200, MIGRATE)
		return
	}

	remoteAddr, err := f.ParseRemoteAddr(c.User)
	if err != nil {
		if models.IsErrInvalidCloneAddr(err) {
			c.Data["Err_CloneAddr"] = true
			addrErr := err.(models.ErrInvalidCloneAddr)
			switch {
			case addrErr.IsURLError:
				c.RenderWithErr(c.Tr("form.url_error"), MIGRATE, &f)
			case addrErr.IsPermissionDenied:
				c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), MIGRATE, &f)
			case addrErr.IsInvalidPath:
				c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), MIGRATE, &f)
			default:
				c.Handle(500, "Unknown error", err)
			}
		} else {
			c.Handle(500, "ParseRemoteAddr", err)
		}
		return
	}

	repo, err := models.MigrateRepository(c.User, ctxUser, models.MigrateRepoOptions{
		Name:        f.RepoName,
		Description: f.Description,
		IsPrivate:   f.Private || setting.Repository.ForcePrivate,
		IsMirror:    f.Mirror,
		RemoteAddr:  remoteAddr,
	})
	if err == nil {
		log.Trace("Repository migrated [%d]: %s/%s", repo.ID, ctxUser.Name, f.RepoName)
		c.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + f.RepoName)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	if strings.Contains(err.Error(), "Authentication failed") ||
		strings.Contains(err.Error(), "could not read Username") {
		c.Data["Err_Auth"] = true
		c.RenderWithErr(c.Tr("form.auth_failed", models.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
		return
	} else if strings.Contains(err.Error(), "fatal:") {
		c.Data["Err_CloneAddr"] = true
		c.RenderWithErr(c.Tr("repo.migrate.failed", models.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
		return
	}

	handleCreateError(c, ctxUser, err, "MigratePost", MIGRATE, &f)
}

func Action(c *context.Context) {
	var err error
	switch c.Params(":action") {
	case "watch":
		err = models.WatchRepo(c.User.ID, c.Repo.Repository.ID, true)
	case "unwatch":
		err = models.WatchRepo(c.User.ID, c.Repo.Repository.ID, false)
	case "star":
		err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, true)
	case "unstar":
		err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, false)
	case "desc": // FIXME: this is not used
		if !c.Repo.IsOwner() {
			c.Error(404)
			return
		}

		c.Repo.Repository.Description = c.Query("desc")
		c.Repo.Repository.Website = c.Query("site")
		err = models.UpdateRepository(c.Repo.Repository, false)
	}

	if err != nil {
		c.Handle(500, fmt.Sprintf("Action (%s)", c.Params(":action")), err)
		return
	}

	redirectTo := c.Query("redirect_to")
	if len(redirectTo) == 0 {
		redirectTo = c.Repo.RepoLink
	}
	c.Redirect(redirectTo)
}

func Download(c *context.Context) {
	var (
		uri         = c.Params("*")
		refName     string
		ext         string
		archivePath string
		archiveType git.ArchiveType
	)

	switch {
	case strings.HasSuffix(uri, ".zip"):
		ext = ".zip"
		archivePath = path.Join(c.Repo.GitRepo.Path, "archives/zip")
		archiveType = git.ZIP
	case strings.HasSuffix(uri, ".tar.gz"):
		ext = ".tar.gz"
		archivePath = path.Join(c.Repo.GitRepo.Path, "archives/targz")
		archiveType = git.TARGZ
	default:
		log.Trace("Unknown format: %s", uri)
		c.Error(404)
		return
	}
	refName = strings.TrimSuffix(uri, ext)

	if !com.IsDir(archivePath) {
		if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
			c.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
			return
		}
	}

	// Get corresponding commit.
	var (
		commit *git.Commit
		err    error
	)
	gitRepo := c.Repo.GitRepo
	if gitRepo.IsBranchExist(refName) {
		commit, err = gitRepo.GetBranchCommit(refName)
		if err != nil {
			c.Handle(500, "GetBranchCommit", err)
			return
		}
	} else if gitRepo.IsTagExist(refName) {
		commit, err = gitRepo.GetTagCommit(refName)
		if err != nil {
			c.Handle(500, "GetTagCommit", err)
			return
		}
	} else if len(refName) >= 7 && len(refName) <= 40 {
		commit, err = gitRepo.GetCommit(refName)
		if err != nil {
			c.NotFound()
			return
		}
	} else {
		c.NotFound()
		return
	}

	archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext)
	if !com.IsFile(archivePath) {
		if err := commit.CreateArchive(archivePath, archiveType); err != nil {
			c.Handle(500, "Download -> CreateArchive "+archivePath, err)
			return
		}
	}

	c.ServeFile(archivePath, c.Repo.Repository.Name+"-"+refName+ext)
}
<commit_msg>[GinR] Add a license by default<commit_after>// Copyright 2014 The Gogs Authors. All rights reserved.
// Use of this source code is governed by a MIT-style
// license that can be found in the LICENSE file.

package repo

import (
	"fmt"
	"os"
	"path"
	"strings"

	"github.com/Unknwon/com"
	log "gopkg.in/clog.v1"

	"github.com/gogits/git-module"

	"github.com/G-Node/gogs/models"
	"github.com/G-Node/gogs/models/errors"
	"github.com/G-Node/gogs/pkg/context"
	"github.com/G-Node/gogs/pkg/form"
	"github.com/G-Node/gogs/pkg/setting"
	"github.com/G-Node/gogs/pkg/tool"
)

const (
	CREATE  = "repo/create"
	MIGRATE = "repo/migrate"
)

func MustBeNotBare(c *context.Context) {
	if c.Repo.Repository.IsBare {
		c.Handle(404, "MustBeNotBare", nil)
	}
}

func checkContextUser(c *context.Context, uid int64) *models.User {
	orgs, err := models.GetOwnedOrgsByUserIDDesc(c.User.ID, "updated_unix")
	if err != nil {
		c.Handle(500, "GetOwnedOrgsByUserIDDesc", err)
		return nil
	}
	c.Data["Orgs"] = orgs

	// Not equal means current user is an organization.
	if uid == c.User.ID || uid == 0 {
		return c.User
	}

	org, err := models.GetUserByID(uid)
	if errors.IsUserNotExist(err) {
		return c.User
	}

	if err != nil {
		c.Handle(500, "GetUserByID", fmt.Errorf("[%d]: %v", uid, err))
		return nil
	}

	// Check ownership of organization.
	if !org.IsOrganization() || !(c.User.IsAdmin || org.IsOwnedBy(c.User.ID)) {
		c.Error(403)
		return nil
	}
	return org
}

func Create(c *context.Context) {
	c.Data["Title"] = c.Tr("new_repo")

	// Give default value for template to render.
	c.Data["Gitignores"] = models.Gitignores
	c.Data["Licenses"] = models.Licenses
	c.Data["license"] = models.Licenses[0]
	c.Data["Readmes"] = models.Readmes
	c.Data["readme"] = "Default"
	c.Data["private"] = c.User.LastRepoVisibility
	c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate

	ctxUser := checkContextUser(c, c.QueryInt64("org"))
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	c.HTML(200, CREATE)
}

func handleCreateError(c *context.Context, owner *models.User, err error, name, tpl string, form interface{}) {
	switch {
	case errors.IsReachLimitOfRepo(err):
		c.RenderWithErr(c.Tr("repo.form.reach_limit_of_creation", owner.RepoCreationNum()), tpl, form)
	case models.IsErrRepoAlreadyExist(err):
		c.Data["Err_RepoName"] = true
		c.RenderWithErr(c.Tr("form.repo_name_been_taken"), tpl, form)
	case models.IsErrNameReserved(err):
		c.Data["Err_RepoName"] = true
		c.RenderWithErr(c.Tr("repo.form.name_reserved", err.(models.ErrNameReserved).Name), tpl, form)
	case models.IsErrNamePatternNotAllowed(err):
		c.Data["Err_RepoName"] = true
		c.RenderWithErr(c.Tr("repo.form.name_pattern_not_allowed", err.(models.ErrNamePatternNotAllowed).Pattern), tpl, form)
	default:
		c.Handle(500, name, err)
	}
}

func CreatePost(c *context.Context, f form.CreateRepo) {
	c.Data["Title"] = c.Tr("new_repo")

	c.Data["Gitignores"] = models.Gitignores
	c.Data["Licenses"] = models.Licenses
	c.Data["Readmes"] = models.Readmes

	ctxUser := checkContextUser(c, f.UserID)
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	if c.HasError() {
		c.HTML(200, CREATE)
		return
	}

	repo, err := models.CreateRepository(c.User, ctxUser, models.CreateRepoOptions{
		Name:        f.RepoName,
		Description: f.Description,
		Gitignores:  f.Gitignores,
		License:     f.License,
		Readme:      f.Readme,
		IsPrivate:   f.Private || setting.Repository.ForcePrivate,
		AutoInit:    f.AutoInit,
	})
	if err == nil {
		log.Trace("Repository created [%d]: %s/%s", repo.ID, ctxUser.Name, repo.Name)
		c.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + repo.Name)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	handleCreateError(c, ctxUser, err, "CreatePost", CREATE, &f)
}

func Migrate(c *context.Context) {
	c.Data["Title"] = c.Tr("new_migrate")
	c.Data["private"] = c.User.LastRepoVisibility
	c.Data["IsForcedPrivate"] = setting.Repository.ForcePrivate
	c.Data["mirror"] = c.Query("mirror") == "1"

	ctxUser := checkContextUser(c, c.QueryInt64("org"))
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	c.HTML(200, MIGRATE)
}

func MigratePost(c *context.Context, f form.MigrateRepo) {
	c.Data["Title"] = c.Tr("new_migrate")

	ctxUser := checkContextUser(c, f.Uid)
	if c.Written() {
		return
	}
	c.Data["ContextUser"] = ctxUser

	if c.HasError() {
		c.HTML(200, MIGRATE)
		return
	}

	remoteAddr, err := f.ParseRemoteAddr(c.User)
	if err != nil {
		if models.IsErrInvalidCloneAddr(err) {
			c.Data["Err_CloneAddr"] = true
			addrErr := err.(models.ErrInvalidCloneAddr)
			switch {
			case addrErr.IsURLError:
				c.RenderWithErr(c.Tr("form.url_error"), MIGRATE, &f)
			case addrErr.IsPermissionDenied:
				c.RenderWithErr(c.Tr("repo.migrate.permission_denied"), MIGRATE, &f)
			case addrErr.IsInvalidPath:
				c.RenderWithErr(c.Tr("repo.migrate.invalid_local_path"), MIGRATE, &f)
			default:
				c.Handle(500, "Unknown error", err)
			}
		} else {
			c.Handle(500, "ParseRemoteAddr", err)
		}
		return
	}

	repo, err := models.MigrateRepository(c.User, ctxUser, models.MigrateRepoOptions{
		Name:        f.RepoName,
		Description: f.Description,
		IsPrivate:   f.Private || setting.Repository.ForcePrivate,
		IsMirror:    f.Mirror,
		RemoteAddr:  remoteAddr,
	})
	if err == nil {
		log.Trace("Repository migrated [%d]: %s/%s", repo.ID, ctxUser.Name, f.RepoName)
		c.Redirect(setting.AppSubURL + "/" + ctxUser.Name + "/" + f.RepoName)
		return
	}

	if repo != nil {
		if errDelete := models.DeleteRepository(ctxUser.ID, repo.ID); errDelete != nil {
			log.Error(4, "DeleteRepository: %v", errDelete)
		}
	}

	if strings.Contains(err.Error(), "Authentication failed") ||
		strings.Contains(err.Error(), "could not read Username") {
		c.Data["Err_Auth"] = true
		c.RenderWithErr(c.Tr("form.auth_failed", models.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
		return
	} else if strings.Contains(err.Error(), "fatal:") {
		c.Data["Err_CloneAddr"] = true
		c.RenderWithErr(c.Tr("repo.migrate.failed", models.HandleMirrorCredentials(err.Error(), true)), MIGRATE, &f)
		return
	}

	handleCreateError(c, ctxUser, err, "MigratePost", MIGRATE, &f)
}

func Action(c *context.Context) {
	var err error
	switch c.Params(":action") {
	case "watch":
		err = models.WatchRepo(c.User.ID, c.Repo.Repository.ID, true)
	case "unwatch":
		err = models.WatchRepo(c.User.ID, c.Repo.Repository.ID, false)
	case "star":
		err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, true)
	case "unstar":
		err = models.StarRepo(c.User.ID, c.Repo.Repository.ID, false)
	case "desc": // FIXME: this is not used
		if !c.Repo.IsOwner() {
			c.Error(404)
			return
		}

		c.Repo.Repository.Description = c.Query("desc")
		c.Repo.Repository.Website = c.Query("site")
		err = models.UpdateRepository(c.Repo.Repository, false)
	}

	if err != nil {
		c.Handle(500, fmt.Sprintf("Action (%s)", c.Params(":action")), err)
		return
	}

	redirectTo := c.Query("redirect_to")
	if len(redirectTo) == 0 {
		redirectTo = c.Repo.RepoLink
	}
	c.Redirect(redirectTo)
}

func Download(c *context.Context) {
	var (
		uri         = c.Params("*")
		refName     string
		ext         string
		archivePath string
		archiveType git.ArchiveType
	)

	switch {
	case strings.HasSuffix(uri, ".zip"):
		ext = ".zip"
		archivePath = path.Join(c.Repo.GitRepo.Path, "archives/zip")
		archiveType = git.ZIP
	case strings.HasSuffix(uri, ".tar.gz"):
		ext = ".tar.gz"
		archivePath = path.Join(c.Repo.GitRepo.Path, "archives/targz")
		archiveType = git.TARGZ
	default:
		log.Trace("Unknown format: %s", uri)
		c.Error(404)
		return
	}
	refName = strings.TrimSuffix(uri, ext)

	if !com.IsDir(archivePath) {
		if err := os.MkdirAll(archivePath, os.ModePerm); err != nil {
			c.Handle(500, "Download -> os.MkdirAll(archivePath)", err)
			return
		}
	}

	// Get corresponding commit.
	var (
		commit *git.Commit
		err    error
	)
	gitRepo := c.Repo.GitRepo
	if gitRepo.IsBranchExist(refName) {
		commit, err = gitRepo.GetBranchCommit(refName)
		if err != nil {
			c.Handle(500, "GetBranchCommit", err)
			return
		}
	} else if gitRepo.IsTagExist(refName) {
		commit, err = gitRepo.GetTagCommit(refName)
		if err != nil {
			c.Handle(500, "GetTagCommit", err)
			return
		}
	} else if len(refName) >= 7 && len(refName) <= 40 {
		commit, err = gitRepo.GetCommit(refName)
		if err != nil {
			c.NotFound()
			return
		}
	} else {
		c.NotFound()
		return
	}

	archivePath = path.Join(archivePath, tool.ShortSHA1(commit.ID.String())+ext)
	if !com.IsFile(archivePath) {
		if err := commit.CreateArchive(archivePath, archiveType); err != nil {
			c.Handle(500, "Download -> CreateArchive "+archivePath, err)
			return
		}
	}

	c.ServeFile(archivePath, c.Repo.Repository.Name+"-"+refName+ext)
}
<|endoftext|>


<commit_before>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| rpc/core/service.go                                      |
|                                                          |
| LastModified: Jan 24, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package core

// Service for RPC.
type Service interface {
	MethodManager
}
<commit_msg>Update service.go<commit_after>/*--------------------------------------------------------*\
|                                                          |
|                          hprose                          |
|                                                          |
| Official WebSite: https://hprose.com                     |
|                                                          |
| rpc/core/service.go                                      |
|                                                          |
| LastModified: Feb 17, 2021                               |
| Author: Ma Bingyao <andot@hprose.com>                    |
|                                                          |
\*________________________________________________________*/

package core

import (
	"context"
	"reflect"
	"sync"
)

// Server is a generic interface used to represent any server.
type Server interface{}

// Handler is an interface used to bind service to any server.
type Handler interface {
	Bind(server Server) error
}

// HandlerFactory is a constructor for Handler.
type HandlerFactory func(service *Service) Handler

var handlerFactories sync.Map
var serverTypes sync.Map

// RegisterHandler for Service.
func RegisterHandler(name string, handlerFactory HandlerFactory, serverType reflect.Type) {
	handlerFactories.Store(name, handlerFactory)
	serverTypes.Store(serverType, name)
}

// Service for RPC.
type Service struct {
	Codec            ServiceCodec
	MaxRequestLength int
	Options          Dict
	invokeManager    PluginManager
	ioManager        PluginManager
	handlers         map[string]Handler
	methodManager
}

// NewService returns an instance of Service.
func NewService() *Service {
	service := &Service{
		Codec:            serviceCodec{},
		MaxRequestLength: 0x7FFFFFFF,
		Options:          NewSafeDict(),
	}
	handlerFactories.Range(func(key, value interface{}) bool {
		handler := value.(HandlerFactory)(service)
		service.handlers[key.(string)] = handler
		return true
	})
	service.ioManager = NewIOManager(service.Process)
	service.invokeManager = NewInvokeManager(service.Execute)
	service.AddFunction(service.methodManager.Names, "~")
	return service
}

// Bind to server.
func (s *Service) Bind(server Server) error {
	serverType := reflect.TypeOf(server)
	if name, ok := serverTypes.Load(serverType); ok {
		handler := s.handlers[name.(string)]
		return handler.Bind(server)
	}
	return UnsupportedServerTypeError{serverType}
}

// Handler returns the handler by the specified name.
func (s *Service) Handler(name string) Handler {
	return s.handlers[name]
}

// Handle the reqeust and returns the response.
func (s *Service) Handle(context context.Context, request []byte) ([]byte, error) {
	return s.ioManager.Handler().(NextIOHandler)(context, request)
}

// Process the reqeust and returns the response.
func (s *Service) Process(context context.Context, request []byte) ([]byte, error) {
	serviceContext := FromContext(context).(ServiceContext)
	name, args, err := s.Codec.Decode(request, serviceContext)
	if err != nil {
		return s.Codec.Encode(err, serviceContext)
	}
	var result interface{}
	func() {
		defer func() {
			if p := recover(); p != nil {
				result = NewPanicError(p)
			}
		}()
		results, err := s.invokeManager.Handler().(NextInvokeHandler)(context, name, args)
		if err != nil {
			result = err
			return
		}
		switch len(results) {
		case 0:
			result = nil
		case 1:
			result = results[0]
		default:
			result = results
		}
	}()
	return s.Codec.Encode(result, serviceContext)
}

// Execute the method and returns the results.
func (s *Service) Execute(context context.Context, name string, args []interface{}) (result []interface{}, err error) {
	serviceContext := FromContext(context).(ServiceContext)
	method := serviceContext.Method()
	if method.Missing() {
		if method.PassContext() {
			return method.(contextMissingMethod)(context, name, args)
		}
		return method.(missingMethod)(name, args)
	}
	n := len(args)
	var in []reflect.Value
	if method.PassContext() {
		in = make([]reflect.Value, n+1)
		in[0] = reflect.ValueOf(context)
		for i := 0; i < n; i++ {
			in[i+1] = reflect.ValueOf(args[i])
		}
	} else {
		in = make([]reflect.Value, n)
		for i := 0; i < n; i++ {
			in[i] = reflect.ValueOf(args[i])
		}
	}
	f := method.Func()
	out := f.Call(in)
	n = len(out)
	if f.Type().Out(n - 1).Implements(errorType) {
		if !out[n-1].IsNil() {
			err = out[n-1].Interface().(error)
		}
		out = out[:n-1]
		n--
	}
	for i := 0; i < n; i++ {
		result = append(result, out[i].Interface())
	}
	return
}

func splitPluginHandlers(handlers []PluginHandler) (invokeHandlers []PluginHandler, ioHandler []PluginHandler) {
	for _, handler := range handlers {
		switch handler.(type) {
		case InvokeHandler:
			invokeHandlers = append(invokeHandlers, handler)
		case IOHandler:
			ioHandler = append(ioHandler, handler)
		}
	}
	return
}

// Use plugin handlers.
func (s *Service) Use(handler ...PluginHandler) *Service {
	invokeHandlers, ioHandler := splitPluginHandlers(handler)
	if len(invokeHandlers) > 0 {
		s.invokeManager.Use(invokeHandlers...)
	}
	if len(ioHandler) > 0 {
		s.ioManager.Use(ioHandler...)
	}
	return s
}

// Unuse plugin handlers.
func (s *Service) Unuse(handler ...PluginHandler) *Service {
	invokeHandlers, ioHandler := splitPluginHandlers(handler)
	if len(invokeHandlers) > 0 {
		s.invokeManager.Unuse(invokeHandlers...)
	}
	if len(ioHandler) > 0 {
		s.ioManager.Unuse(ioHandler...)
	}
	return s
}

// Remove is used for unpublishing method by the specified name.
func (s *Service) Remove(name string) *Service {
	s.methodManager.Remove(name)
	return s
}

// Add is used for publishing the method.
func (s *Service) Add(method Method) *Service {
	s.methodManager.Add(method)
	return s
}

// AddFunction is used for publishing function f with name.
func (s *Service) AddFunction(f interface{}, name string) *Service {
	s.methodManager.AddFunction(f, name)
	return s
}

// AddMethod is used for publishing method named name on target with alias.
func (s *Service) AddMethod(name string, target interface{}, alias ...string) *Service {
	s.methodManager.AddMethod(name, target, alias...)
	return s
}

// AddMethods is used for publishing methods named names on target with namespace.
func (s *Service) AddMethods(names []string, target interface{}, namespace ...string) *Service {
	s.methodManager.AddMethods(names, target, namespace...)
	return s
}

// AddInstanceMethods is used for publishing all the public methods and func fields with namespace.
func (s *Service) AddInstanceMethods(target interface{}, namespace ...string) *Service {
	s.methodManager.AddInstanceMethods(target, namespace...)
	return s
}

// AddAllMethods will publish all methods and non-nil function fields on the
// obj self and on its anonymous or non-anonymous struct fields (or pointer to
// pointer ... to pointer struct fields). This is a recursive operation.
// So it's a pit, if you do not know what you are doing, do not step on.
func (s *Service) AddAllMethods(target interface{}, namespace ...string) *Service {
	s.methodManager.AddAllMethods(target, namespace...)
	return s
}

// AddMissingMethod is used for publishing a method,
// all methods not explicitly published will be redirected to this method.
func (s *Service) AddMissingMethod(f interface{}) *Service {
	s.methodManager.AddMissingMethod(f)
	return s
}

// AddNetRPCMethods is used for publishing methods defined for net/rpc.
func (s *Service) AddNetRPCMethods(rcvr interface{}, namespace ...string) *Service {
	s.methodManager.AddNetRPCMethods(rcvr, namespace...)
	return s
}
<|endoftext|>