<commit_before>// Copyright 2018 Google LLC
//
// 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
//
//    https://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 mapper

import (
	"context"
	"crypto/sha256"
	"fmt"
	"math/big"
	"time"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/golang/glog"
	"github.com/google/trillian"
)

const (
	oneEther int64 = 1000000000000000000
)

var oneEtherRatio = big.NewFloat(float64(1) / float64(oneEther))

type Mapper struct {
	logID, mapID int64
	tlog         trillian.TrillianLogClient
	tmap         trillian.TrillianMapClient

	unsortedBlocks chan *types.Block
	sortedBlocks   chan *types.Block
}

func New(tl trillian.TrillianLogClient, logID int64, tm trillian.TrillianMapClient, mapID int64) *Mapper {
	return &Mapper{
		logID: logID,
		mapID: mapID,
		tlog:  tl,
		tmap:  tm,

		unsortedBlocks: make(chan *types.Block, 100000),
		sortedBlocks:   make(chan *types.Block, 100000),
	}
}

func (m *Mapper) fetchBlocks(ctx context.Context, from int64) {
	ticker := time.NewTicker(time.Second)

nextAttempt:
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
		}

		num := int64(200)
		leaves := make([]int64, num)
		for i := int64(0); i < num; i++ {
			leaves[i] = from + i
		}

		entries, err := m.tlog.GetLeavesByIndex(ctx, &trillian.GetLeavesByIndexRequest{LogId: m.logID, LeafIndex: leaves})
		if err != nil {
			glog.Errorf("Failed to get %d leaves starting at index %d: %v", num, from, err)
			continue nextAttempt
		}

		for _, l := range entries.Leaves {
			block := &types.Block{}
			if err := rlp.DecodeBytes(l.LeafValue, block); err != nil {
				glog.Errorf("Failed to decode block from log at index %d: %v", l.LeafIndex, err)
				continue nextAttempt
			}
			m.unsortedBlocks <- block
		}

		from += num
	}
}

func (m *Mapper) pipelineBlocks(ctx context.Context, from int64) {
	ticker := time.NewTicker(time.Second)
	blocksByNumber := make(map[int64]*types.Block)

	go m.fetchBlocks(ctx, from)

nextAttempt:
	for {
		select {
		case <-ctx.Done():
			for range m.unsortedBlocks {
			}
			return
		case b := <-m.unsortedBlocks:
			blocksByNumber[b.Number().Int64()] = b
		case <-ticker.C:
		}

		// try to sort some blocks:
		for {
			b, found := blocksByNumber[from]
			if !found {
				continue nextAttempt
			}
			m.sortedBlocks <- b
			from++
		}

	}
}

func isProtectedV(V *big.Int) bool {
	if V.BitLen() <= 8 {
		v := V.Uint64()
		return v != 27 && v != 28
	}
	// anything not 27 or 28 are considered unprotected
	return true
}

// deriveChainId derives the chain id from the given v parameter
func deriveChainId(v *big.Int) *big.Int {
	if v.BitLen() <= 64 {
		v := v.Uint64()
		if v == 27 || v == 28 {
			return new(big.Int)
		}
		return new(big.Int).SetUint64((v - 35) / 2)
	}
	v = new(big.Int).Sub(v, big.NewInt(35))
	return v.Div(v, big.NewInt(2))
}

func ethBalance(b *big.Int) string {
	a := &big.Float{}
	a.SetInt(b)
	a = a.Mul(a, oneEtherRatio)
	return fmt.Sprintf("Ξ%s", a.String())
}

// deriveSigner makes a *best* guess about which signer to use.
func deriveSigner(V *big.Int) types.Signer {
	if V.Sign() != 0 && isProtectedV(V) {
		return types.NewEIP155Signer(deriveChainId(V))
	} else {
		return types.HomesteadSigner{}
	}
}

func fmtAddress(a []byte) string {
	return fmt.Sprintf("%x", a[:])
}

func index(a []byte) string {
	r := sha256.Sum256(a)
	return string(r[:])
}

func (m *Mapper) mapTransactionsFrom(ctx context.Context, b *types.Block) error {
	numTX := len(b.Transactions())
	if numTX == 0 {
		return nil
	}
	glog.Infof("Mapping %d transactions from block @ %v", len(b.Transactions()), b.Number())
	glog.V(1).Infof("Block: %v", b.String())

	deltas := make(map[string]*big.Int)

	// Add miner credit
	minerIndex := index(b.Coinbase().Bytes())
	credit := big.NewInt(int64(5) * int64(1+len(b.Uncles())/32))
	glog.Infof("Miner credit: %s", credit.String())
	credit.Mul(credit, big.NewInt(oneEther))
	deltas[minerIndex] = credit

	for i, tx := range b.Transactions() {
		v, _, _ := tx.RawSignatureValues()
		if v == nil {
			return fmt.Errorf("nil signature on tx@%d@%v", i, b.Number())
		}
		signer := deriveSigner(v)
		from, err := types.Sender(signer, tx)
		if err != nil {
			return fmt.Errorf("unable to derive sender on tx@%d@%v", i, b.Number())
		}

		// Handle sender costs
		sIndex := index(from.Bytes())
		sBal, ok := deltas[sIndex]
		if !ok {
			sBal = big.NewInt(0)
		}
		sBal.Sub(sBal, tx.Cost())
		deltas[sIndex] = sBal

		to := tx.To()
		if to == nil {
			glog.Infof("start-contract TX with nil To: address %d@%v", i, b.Number())
			continue
		}

		rIndex := index(to.Bytes())
		rBal, ok := deltas[rIndex]
		if !ok {
			rBal = big.NewInt(0)
		}
		rBal.Add(rBal, tx.Value())
		deltas[rIndex] = rBal

		{
			// only using floats for printing, map should use the fixed point representation!
			sender := fmtAddress(from.Bytes())
			recipient := fmtAddress(to.Bytes())
			amount := float64(tx.Value().Int64()) / float64(oneEther)
			cost := float64(tx.Cost().Int64()) / float64(oneEther)
			glog.Infof("Ξ%f from %s... (%x...) -> %s... (%x...), costing Ξ%f", amount, sender[:5], sIndex[:5], recipient[:5], rIndex[:5], cost)
		}
	}
	glog.V(1).Infof("Have %d deltas", len(deltas))
	if len(deltas) == 0 {
		return nil
	}

	getRequest := &trillian.GetMapLeavesRequest{
		MapId: m.mapID,
	}
	for k := range deltas {
		getRequest.Index = append(getRequest.Index, []byte(k))
	}

	glog.V(1).Info("Get map leaves...")
	get, err := m.tmap.GetLeaves(ctx, getRequest)
	if err != nil {
		return fmt.Errorf("failed to get current balances: %v", err)
	}
	glog.V(1).Infof("Got %d map leaves.", len(get.MapLeafInclusion))

	if ld, ll := len(deltas), len(get.MapLeafInclusion); ld != ll {
		glog.Exitf("Got %d leaves, expected %d", ll, ld)
	}

	setRequest := &trillian.SetMapLeavesRequest{
		MapId:  m.mapID,
		Leaves: make([]*trillian.MapLeaf, 0),
	}

	for _, l := range get.MapLeafInclusion {
		bal := big.NewInt(0)
		if len(l.Leaf.LeafValue) > 0 {
			var ok bool
			bal, ok = bal.SetString(string(l.Leaf.LeafValue), 10)
			if !ok {
				glog.Warningf("Leaf value for %x... (%s) is corrupt, resetting to zero balance", l.Leaf.Index[:5], string(l.Leaf.LeafValue))
				bal = big.NewInt(0)
			}
		}
		k := string(l.Leaf.Index)
		d, ok := deltas[k]
		if !ok {
			glog.Warning("No delta for leaf index %x", l.Leaf.Index)
			continue
		}
		delete(deltas, k)
		glog.V(1).Infof("index %x... had: %s", l.Leaf.Index[:5], ethBalance(bal))
		bal.Add(bal, d)
		l.Leaf.LeafValue = []byte(bal.String())
		setRequest.Leaves = append(setRequest.Leaves, l.Leaf)
		glog.Infof("index %x... now has: %s", l.Leaf.Index[:5], ethBalance(bal))
	}

	if len(deltas) != 0 {
		glog.Exitf("Arg, didn't use all deltas, still have:\n%+v", deltas)
	}

	glog.V(1).Infof("Setting %d map leaves.", len(setRequest.Leaves))
	_, err = m.tmap.SetLeaves(ctx, setRequest)
	if err != nil {
		return fmt.Errorf("failed to update balances: %v", err)
	}

	return nil
}

func (m *Mapper) Map(ctx context.Context) {
	from := int64(0)
	go m.pipelineBlocks(ctx, from)

	for {
		select {
		case <-ctx.Done():
			return
		case nextBlock := <-m.sortedBlocks:
			if nextBlock.Number().Int64() != from {
				glog.Exitf("Got unexpected block number %s, wanted %d", nextBlock.Number(), from)
			}
			// TODO(al): batching...
			if err := m.mapTransactionsFrom(ctx, nextBlock); err != nil {
				glog.Exitf("Couldn't map transactions from block %v", err)
			}
			from++
		}
	}
}
<commit_msg>Backoff if trying to fetch blocks from the log fails.<commit_after>// Copyright 2018 Google LLC
//
// 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
//
//    https://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 mapper

import (
	"context"
	"crypto/sha256"
	"fmt"
	"math/big"
	"time"

	"github.com/ethereum/go-ethereum/core/types"
	"github.com/ethereum/go-ethereum/rlp"
	"github.com/golang/glog"
	"github.com/google/trillian"
)

const (
	oneEther int64 = 1000000000000000000
)

var oneEtherRatio = big.NewFloat(float64(1) / float64(oneEther))

type Mapper struct {
	logID, mapID int64
	tlog         trillian.TrillianLogClient
	tmap         trillian.TrillianMapClient

	unsortedBlocks chan *types.Block
	sortedBlocks   chan *types.Block
}

func New(tl trillian.TrillianLogClient, logID int64, tm trillian.TrillianMapClient, mapID int64) *Mapper {
	return &Mapper{
		logID: logID,
		mapID: mapID,
		tlog:  tl,
		tmap:  tm,

		unsortedBlocks: make(chan *types.Block, 100000),
		sortedBlocks:   make(chan *types.Block, 100000),
	}
}

const maxNumBlocks int64 = 100

var numBlocks = maxNumBlocks

func (m *Mapper) fetchBlocks(ctx context.Context, from int64) {
	ticker := time.NewTicker(time.Second)

nextAttempt:
	for {
		select {
		case <-ctx.Done():
			return
		case <-ticker.C:
		}

		leaves := make([]int64, numBlocks)
		for i := int64(0); i < numBlocks; i++ {
			leaves[i] = from + i
		}

		entries, err := m.tlog.GetLeavesByIndex(ctx, &trillian.GetLeavesByIndexRequest{LogId: m.logID, LeafIndex: leaves})
		if err != nil {
			glog.Errorf("Failed to get %d leaves starting at index %d: %v", numBlocks, from, err)
			numBlocks /= 2
			continue nextAttempt
		}
		if numBlocks < maxNumBlocks {
			numBlocks += (maxNumBlocks - numBlocks) / 2
		}

		for _, l := range entries.Leaves {
			block := &types.Block{}
			if err := rlp.DecodeBytes(l.LeafValue, block); err != nil {
				glog.Errorf("Failed to decode block from log at index %d: %v", l.LeafIndex, err)
				continue nextAttempt
			}
			m.unsortedBlocks <- block
		}

		from += numBlocks
	}
}

func (m *Mapper) pipelineBlocks(ctx context.Context, from int64) {
	ticker := time.NewTicker(time.Second)
	blocksByNumber := make(map[int64]*types.Block)

	go m.fetchBlocks(ctx, from)

nextAttempt:
	for {
		select {
		case <-ctx.Done():
			for range m.unsortedBlocks {
			}
			return
		case b := <-m.unsortedBlocks:
			blocksByNumber[b.Number().Int64()] = b
		case <-ticker.C:
		}

		// try to sort some blocks:
		for {
			b, found := blocksByNumber[from]
			if !found {
				continue nextAttempt
			}
			m.sortedBlocks <- b
			from++
		}

	}
}

func isProtectedV(V *big.Int) bool {
	if V.BitLen() <= 8 {
		v := V.Uint64()
		return v != 27 && v != 28
	}
	// anything not 27 or 28 are considered unprotected
	return true
}

// deriveChainId derives the chain id from the given v parameter
func deriveChainId(v *big.Int) *big.Int {
	if v.BitLen() <= 64 {
		v := v.Uint64()
		if v == 27 || v == 28 {
			return new(big.Int)
		}
		return new(big.Int).SetUint64((v - 35) / 2)
	}
	v = new(big.Int).Sub(v, big.NewInt(35))
	return v.Div(v, big.NewInt(2))
}

func ethBalance(b *big.Int) string {
	a := &big.Float{}
	a.SetInt(b)
	a = a.Mul(a, oneEtherRatio)
	return fmt.Sprintf("Ξ%s", a.String())
}

// deriveSigner makes a *best* guess about which signer to use.
func deriveSigner(V *big.Int) types.Signer {
	if V.Sign() != 0 && isProtectedV(V) {
		return types.NewEIP155Signer(deriveChainId(V))
	} else {
		return types.HomesteadSigner{}
	}
}

func fmtAddress(a []byte) string {
	return fmt.Sprintf("%x", a[:])
}

func index(a []byte) string {
	r := sha256.Sum256(a)
	return string(r[:])
}

func (m *Mapper) mapTransactionsFrom(ctx context.Context, b *types.Block) error {
	numTX := len(b.Transactions())
	if numTX == 0 {
		return nil
	}
	glog.Infof("Mapping %d transactions from block @ %v", len(b.Transactions()), b.Number())
	glog.V(1).Infof("Block: %v", b.String())

	deltas := make(map[string]*big.Int)

	// Add miner credit
	minerIndex := index(b.Coinbase().Bytes())
	credit := big.NewInt(int64(5) * int64(1+len(b.Uncles())/32))
	glog.Infof("Miner credit: %s", credit.String())
	credit.Mul(credit, big.NewInt(oneEther))
	deltas[minerIndex] = credit

	for i, tx := range b.Transactions() {
		v, _, _ := tx.RawSignatureValues()
		if v == nil {
			return fmt.Errorf("nil signature on tx@%d@%v", i, b.Number())
		}
		signer := deriveSigner(v)
		from, err := types.Sender(signer, tx)
		if err != nil {
			return fmt.Errorf("unable to derive sender on tx@%d@%v", i, b.Number())
		}

		// Handle sender costs
		sIndex := index(from.Bytes())
		sBal, ok := deltas[sIndex]
		if !ok {
			sBal = big.NewInt(0)
		}
		sBal.Sub(sBal, tx.Cost())
		deltas[sIndex] = sBal

		to := tx.To()
		if to == nil {
			glog.Infof("start-contract TX with nil To: address %d@%v", i, b.Number())
			continue
		}

		rIndex := index(to.Bytes())
		rBal, ok := deltas[rIndex]
		if !ok {
			rBal = big.NewInt(0)
		}
		rBal.Add(rBal, tx.Value())
		deltas[rIndex] = rBal

		{
			// only using floats for printing, map should use the fixed point representation!
			sender := fmtAddress(from.Bytes())
			recipient := fmtAddress(to.Bytes())
			amount := float64(tx.Value().Int64()) / float64(oneEther)
			cost := float64(tx.Cost().Int64()) / float64(oneEther)
			glog.Infof("Ξ%f from %s... (%x...) -> %s... (%x...), costing Ξ%f", amount, sender[:5], sIndex[:5], recipient[:5], rIndex[:5], cost)
		}
	}
	glog.V(1).Infof("Have %d deltas", len(deltas))
	if len(deltas) == 0 {
		return nil
	}

	getRequest := &trillian.GetMapLeavesRequest{
		MapId: m.mapID,
	}
	for k := range deltas {
		getRequest.Index = append(getRequest.Index, []byte(k))
	}

	glog.V(1).Info("Get map leaves...")
	get, err := m.tmap.GetLeaves(ctx, getRequest)
	if err != nil {
		return fmt.Errorf("failed to get current balances: %v", err)
	}
	glog.V(1).Infof("Got %d map leaves.", len(get.MapLeafInclusion))

	if ld, ll := len(deltas), len(get.MapLeafInclusion); ld != ll {
		glog.Exitf("Got %d leaves, expected %d", ll, ld)
	}

	setRequest := &trillian.SetMapLeavesRequest{
		MapId:  m.mapID,
		Leaves: make([]*trillian.MapLeaf, 0),
	}

	for _, l := range get.MapLeafInclusion {
		bal := big.NewInt(0)
		if len(l.Leaf.LeafValue) > 0 {
			var ok bool
			bal, ok = bal.SetString(string(l.Leaf.LeafValue), 10)
			if !ok {
				glog.Warningf("Leaf value for %x... (%s) is corrupt, resetting to zero balance", l.Leaf.Index[:5], string(l.Leaf.LeafValue))
				bal = big.NewInt(0)
			}
		}
		k := string(l.Leaf.Index)
		d, ok := deltas[k]
		if !ok {
			glog.Warning("No delta for leaf index %x", l.Leaf.Index)
			continue
		}
		delete(deltas, k)
		glog.V(1).Infof("index %x... had: %s", l.Leaf.Index[:5], ethBalance(bal))
		bal.Add(bal, d)
		l.Leaf.LeafValue = []byte(bal.String())
		setRequest.Leaves = append(setRequest.Leaves, l.Leaf)
		glog.Infof("index %x... now has: %s", l.Leaf.Index[:5], ethBalance(bal))
	}

	if len(deltas) != 0 {
		glog.Exitf("Arg, didn't use all deltas, still have:\n%+v", deltas)
	}

	glog.V(1).Infof("Setting %d map leaves.", len(setRequest.Leaves))
	_, err = m.tmap.SetLeaves(ctx, setRequest)
	if err != nil {
		return fmt.Errorf("failed to update balances: %v", err)
	}

	return nil
}

func (m *Mapper) Map(ctx context.Context) {
	from := int64(0)
	go m.pipelineBlocks(ctx, from)

	for {
		select {
		case <-ctx.Done():
			return
		case nextBlock := <-m.sortedBlocks:
			if nextBlock.Number().Int64() != from {
				glog.Exitf("Got unexpected block number %s, wanted %d", nextBlock.Number(), from)
			}
			// TODO(al): batching...
			if err := m.mapTransactionsFrom(ctx, nextBlock); err != nil {
				glog.Exitf("Couldn't map transactions from block %v", err)
			}
			from++
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 The Ebiten 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.

// +build example jsgo

package main

import (
	"fmt"
	"image/color"
	"log"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
)

var pointerImage *ebiten.Image

func init() {
	pointerImage, _ = ebiten.NewImage(4, 4, ebiten.FilterDefault)
	pointerImage.Fill(color.RGBA{0xff, 0, 0, 0xff})
}

const (
	screenWidth  = 320
	screenHeight = 240
)

var (
	x = 0.0
	y = 0.0
)

func update(screen *ebiten.Image) error {
	dx, dy := ebiten.Wheel()
	x += dx
	y += dy

	if ebiten.IsDrawingSkipped() {
		return nil
	}

	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(x, y)
	op.GeoM.Translate(screenWidth/2, screenHeight/2)
	screen.DrawImage(pointerImage, op)

	ebitenutil.DebugPrint(screen,
		fmt.Sprintf("Move the red point by mouse wheel\n(%0.2f, %0.2f)", x, y))

	return nil
}

func main() {
	if err := ebiten.Run(update, screenWidth, screenHeight, 2, "Wheel (Ebiten Demo)"); err != nil {
		log.Fatal(err)
	}
}
<commit_msg>examples/wheel: Use RunGame (#1182)<commit_after>// Copyright 2018 The Ebiten 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.

// +build example jsgo

package main

import (
	"fmt"
	"image/color"
	"log"

	"github.com/hajimehoshi/ebiten"
	"github.com/hajimehoshi/ebiten/ebitenutil"
)

var pointerImage *ebiten.Image

func init() {
	pointerImage, _ = ebiten.NewImage(4, 4, ebiten.FilterDefault)
	pointerImage.Fill(color.RGBA{0xff, 0, 0, 0xff})
}

const (
	screenWidth  = 320
	screenHeight = 240
)

type Game struct {
	x float64
	y float64
}

func (g *Game) Update(screen *ebiten.Image) error {
	dx, dy := ebiten.Wheel()
	g.x += dx
	g.y += dy
	return nil
}

func (g *Game) Draw(screen *ebiten.Image) {
	op := &ebiten.DrawImageOptions{}
	op.GeoM.Translate(g.x, g.y)
	op.GeoM.Translate(screenWidth/2, screenHeight/2)
	screen.DrawImage(pointerImage, op)

	ebitenutil.DebugPrint(screen,
		fmt.Sprintf("Move the red point by mouse wheel\n(%0.2f, %0.2f)", g.x, g.y))
}

func (g *Game) Layout(outsideWidth, outsideHeight int) (int, int) {
	return screenWidth, screenHeight
}

func main() {
	g := &Game{x: 0.0, y: 0.0}

	ebiten.SetWindowSize(screenWidth*2, screenHeight*2)
	ebiten.SetWindowTitle("Wheel (Ebiten Demo)")
	if err := ebiten.RunGame(g); err != nil {
		log.Fatal(err)
	}
}
<|endoftext|>


<commit_before>package virtualmachinedisk

import (
	"encoding/xml"

	"github.com/Azure/azure-sdk-for-go/management"
)

// DiskClient is used to perform operations on Azure Disks
type DiskClient struct {
	client management.Client
}

// CreateDiskParameters represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type CreateDiskParameters struct {
	XMLName   xml.Name            `xml:"http://schemas.microsoft.com/windowsazure Disk"`
	OS        OperatingSystemType `xml:",omitempty"`
	Label     string
	MediaLink string `xml:",omitempty"`
	Name      string
}

// UpdateDiskParameters represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type UpdateDiskParameters struct {
	XMLName         xml.Name `xml:"http://schemas.microsoft.com/windowsazure Disk"`
	Name            string
	Label           string `xml:",omitempty"`
	ResizedSizeInGB int    `xml:",omitempty"`
}

// ListDiskResponse represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type ListDiskResponse struct {
	XMLName xml.Name `xml:"http://schemas.microsoft.com/windowsazure Disks"`
	Disk    []DiskResponse
}

// DiskResponse represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type DiskResponse struct {
	XMLName             xml.Name `xml:"http://schemas.microsoft.com/windowsazure Disk"`
	AffinityGroup       string
	AttachedTo          Resource
	IsCorrupted         bool
	OS                  OperatingSystemType
	Location            string
	LogicalDiskSizeInGB int
	MediaLink           string
	Name                string
	SourceImageName     string
	CreatedTime         string
	IOType              IOType
}

// Resource describes the resource details a disk is currently attached to
type Resource struct {
	XMLName           xml.Name `xml:"http://schemas.microsoft.com/windowsazure AttachedTo"`
	DeploymentName    string
	HostedServiceName string
	RoleName          string
}

// IOType represents an IO type
type IOType string

// These constants represent the possible IO types
const (
	IOTypeProvisioned IOType = "Provisioned"
	IOTypeStandard    IOType = "Standard"
)

// OperatingSystemType represents an operating system type
type OperatingSystemType string

// These constants represent the valid operating system types
const (
	OperatingSystemTypeNull    OperatingSystemType = "NULL"
	OperatingSystemTypeLinux   OperatingSystemType = "Linux"
	OperatingSystemTypeWindows OperatingSystemType = "Windows"
)

// CreateDataDiskParameters represents a data disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type CreateDataDiskParameters struct {
	XMLName             xml.Name        `xml:"http://schemas.microsoft.com/windowsazure DataVirtualHardDisk"`
	HostCaching         HostCachingType `xml:",omitempty"`
	DiskLabel           string          `xml:",omitempty"`
	DiskName            string          `xml:",omitempty"`
	Lun                 int             `xml:",omitempty"`
	LogicalDiskSizeInGB int             `xml:",omitempty"`
	MediaLink           string
	SourceMediaLink     string `xml:",omitempty"`
}

// UpdateDataDiskParameters represents a data disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type UpdateDataDiskParameters struct {
	XMLName     xml.Name        `xml:"http://schemas.microsoft.com/windowsazure DataVirtualHardDisk"`
	HostCaching HostCachingType `xml:",omitempty"`
	DiskName    string
	Lun         int
	MediaLink   string
}

// DataDiskResponse represents a data disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type DataDiskResponse struct {
	XMLName             xml.Name `xml:"http://schemas.microsoft.com/windowsazure DataVirtualHardDisk"`
	HostCaching         HostCachingType
	DiskLabel           string
	DiskName            string
	Lun                 int
	LogicalDiskSizeInGB int
	MediaLink           string
}

// HostCachingType represents a host caching type
type HostCachingType string

// These constants represent the valid host caching types
const (
	HostCachingTypeNone      HostCachingType = "None"
	HostCachingTypeReadOnly  HostCachingType = "ReadOnly"
	HostCachingTypeReadWrite HostCachingType = "ReadWrite"
)
<commit_msg>Updating bad struct field name<commit_after>package virtualmachinedisk

import (
	"encoding/xml"

	"github.com/Azure/azure-sdk-for-go/management"
)

// DiskClient is used to perform operations on Azure Disks
type DiskClient struct {
	client management.Client
}

// CreateDiskParameters represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type CreateDiskParameters struct {
	XMLName   xml.Name            `xml:"http://schemas.microsoft.com/windowsazure Disk"`
	OS        OperatingSystemType `xml:",omitempty"`
	Label     string
	MediaLink string `xml:",omitempty"`
	Name      string
}

// UpdateDiskParameters represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type UpdateDiskParameters struct {
	XMLName         xml.Name `xml:"http://schemas.microsoft.com/windowsazure Disk"`
	DiskName        string
	Label           string `xml:",omitempty"`
	ResizedSizeInGB int    `xml:",omitempty"`
}

// ListDiskResponse represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type ListDiskResponse struct {
	XMLName xml.Name `xml:"http://schemas.microsoft.com/windowsazure Disks"`
	Disk    []DiskResponse
}

// DiskResponse represents a disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type DiskResponse struct {
	XMLName             xml.Name `xml:"http://schemas.microsoft.com/windowsazure Disk"`
	AffinityGroup       string
	AttachedTo          Resource
	IsCorrupted         bool
	OS                  OperatingSystemType
	Location            string
	LogicalDiskSizeInGB int
	MediaLink           string
	Name                string
	SourceImageName     string
	CreatedTime         string
	IOType              IOType
}

// Resource describes the resource details a disk is currently attached to
type Resource struct {
	XMLName           xml.Name `xml:"http://schemas.microsoft.com/windowsazure AttachedTo"`
	DeploymentName    string
	HostedServiceName string
	RoleName          string
}

// IOType represents an IO type
type IOType string

// These constants represent the possible IO types
const (
	IOTypeProvisioned IOType = "Provisioned"
	IOTypeStandard    IOType = "Standard"
)

// OperatingSystemType represents an operating system type
type OperatingSystemType string

// These constants represent the valid operating system types
const (
	OperatingSystemTypeNull    OperatingSystemType = "NULL"
	OperatingSystemTypeLinux   OperatingSystemType = "Linux"
	OperatingSystemTypeWindows OperatingSystemType = "Windows"
)

// CreateDataDiskParameters represents a data disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type CreateDataDiskParameters struct {
	XMLName             xml.Name        `xml:"http://schemas.microsoft.com/windowsazure DataVirtualHardDisk"`
	HostCaching         HostCachingType `xml:",omitempty"`
	DiskLabel           string          `xml:",omitempty"`
	DiskName            string          `xml:",omitempty"`
	Lun                 int             `xml:",omitempty"`
	LogicalDiskSizeInGB int             `xml:",omitempty"`
	MediaLink           string
	SourceMediaLink     string `xml:",omitempty"`
}

// UpdateDataDiskParameters represents a data disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type UpdateDataDiskParameters struct {
	XMLName     xml.Name        `xml:"http://schemas.microsoft.com/windowsazure DataVirtualHardDisk"`
	HostCaching HostCachingType `xml:",omitempty"`
	DiskName    string
	Lun         int
	MediaLink   string
}

// DataDiskResponse represents a data disk
//
// https://msdn.microsoft.com/en-us/library/azure/jj157188.aspx
type DataDiskResponse struct {
	XMLName             xml.Name `xml:"http://schemas.microsoft.com/windowsazure DataVirtualHardDisk"`
	HostCaching         HostCachingType
	DiskLabel           string
	DiskName            string
	Lun                 int
	LogicalDiskSizeInGB int
	MediaLink           string
}

// HostCachingType represents a host caching type
type HostCachingType string

// These constants represent the valid host caching types
const (
	HostCachingTypeNone      HostCachingType = "None"
	HostCachingTypeReadOnly  HostCachingType = "ReadOnly"
	HostCachingTypeReadWrite HostCachingType = "ReadWrite"
)
<|endoftext|>


<commit_before>package asciidocgo

import (
	"strconv"
	"strings"
	"testing"
	. "github.com/smartystreets/goconvey/convey"
)

func TestAbstractNode(t *testing.T) {

	Convey("An abstractNode can be initialized", t, func() {

		Convey("By default, an AbstractNode can be created", func() {
			So(&abstractNode{}, ShouldNotBeNil)
		})
		Convey("An AbstractNode takes a parent and a context", func() {
			So(newAbstractNode(nil, document), ShouldNotBeNil)
		})
		Convey("If context is a document, then parent is nil and document is parent", func() {
			parent := &abstractNode{}
			an := newAbstractNode(parent, document)
			So(an.Context(), ShouldEqual, document)
			So(an.Parent(), ShouldBeNil)
			So(an.Document(), ShouldEqual, parent)
		})
		Convey("If context is not document, then parent is parent and document is parent document", func() {
			parent := &abstractNode{nil, document, &abstractNode{}, nil, &substitutors{}}
			an := newAbstractNode(parent, section)
			So(an.Context(), ShouldEqual, section)
			So(an.Parent(), ShouldEqual, parent)
			So(an.Document(), ShouldEqual, parent.Document())
		})
		Convey("If context is not document, and parent is nil, then document is nil", func() {
			an := newAbstractNode(nil, section)
			So(an.Context(), ShouldEqual, section)
			So(an.Parent(), ShouldBeNil)
			So(an.Document(), ShouldBeNil)
		})

		Convey("An abstractNode has an empty attributes map", func() {
			an := newAbstractNode(nil, section)
			So(len(an.Attributes()), ShouldEqual, 0)
		})
	})

	Convey("An abstractNode can be associated to a parent", t, func() {
		an := newAbstractNode(nil, section)
		documentParent := &abstractNode{}
		parent := &abstractNode{nil, document, documentParent, nil, &substitutors{}}
		an.SetParent(parent)
		So(an.Parent(), ShouldEqual, parent)
		So(an.Document(), ShouldEqual, parent.Document())
	})

	Convey("An abstractNode can retrieve an attribute", t, func() {

		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("key", "val1", true)
		parent := newAbstractNode(parentDocument, document)
		an := newAbstractNode(nil, document)
		Convey("If inherited, it is the attribute if there, or the document attribute, or default value", func() {
			So(an.Attr("key", nil, true), ShouldBeNil)
			an.setAttr("key", "val", false)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
			an.SetParent(parent)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
			delete(an.attributes, "key")
			So(an.Attr("key", nil, true), ShouldEqual, "val1")
			// an should have for parent a child, which has an as a document
			// then an.document would be "parent".document, meaning an, when
			// setting an.setParent(child)
			an.document = an
			an.setAttr("key", "val", false)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
		})
		Convey("If not inherited, it is the attribute if there, or default value", func() {
			an.SetParent(parent)
			So(an.Document(), ShouldEqual, parentDocument)
			So(an.Document(), ShouldEqual, parent.Document())
			So(parentDocument.Attr("key", nil, false), ShouldEqual, "val1")
			So(an.Attr("key", nil, false), ShouldEqual, "val")
		})
	})
	Convey("An abstractNode can set an attribute", t, func() {
		an := newAbstractNode(nil, document)
		an.setAttr("key", "val", true)
		So(an.Attr("key", nil, true), ShouldEqual, "val")
		Convey("If not override and already present, the value should not change", func() {
			res := an.setAttr("key", "val1", false)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
			So(res, ShouldBeFalse)
		})
	})

	Convey("An abstractNode can set an option attribute", t, func() {
		an := newAbstractNode(nil, document)
		Convey("First option means options attributes has len 1", func() {
			an.SetOption("opt1")
			So(len(an.attributes["options"].(map[string]bool)), ShouldEqual, 1)
			So(an.Attr("opt1-option", nil, false), ShouldEqual, true)
		})
		Convey("Second option means options attributes has len 2", func() {
			an.SetOption("opt2")
			So(len(an.attributes["options"].(map[string]bool)), ShouldEqual, 2)
			So(an.Attr("opt2-option", nil, false), ShouldEqual, true)
		})
	})

	Convey("An abstractNode can get an option attribute", t, func() {
		an := newAbstractNode(nil, document)
		Convey("Zero option means Option returns false", func() {
			So(an.Option("opt1"), ShouldBeFalse)
			an.SetOption("opt1")
		})
		Convey("One option means Option returns true", func() {
			So(an.Option("opt1"), ShouldBeTrue)
		})
	})

	Convey("An abstractNode update option attributes with other attributes", t, func() {
		an := newAbstractNode(nil, document)
		an.setAttr("key1", "val1", true)
		an.setAttr("key2", "val2", true)
		Convey("New Attributes are added during an update", func() {
			attrs := map[string]interface{}{"key3": "val3", "key4": "val4"}
			an.Update(attrs)
			So(an.Attr("key1", nil, false), ShouldEqual, "val1")
			So(an.Attr("key2", nil, false), ShouldEqual, "val2")
			So(an.Attr("key3", nil, false), ShouldEqual, "val3")
			So(an.Attr("key4", nil, false), ShouldEqual, "val4")
		})
		Convey("Common Attributes are overrriden during an update", func() {
			attrs := map[string]interface{}{"key2": "val2b", "key3": "val3"}
			an.Update(attrs)
			So(an.Attr("key1", nil, false), ShouldEqual, "val1")
			So(an.Attr("key2", nil, false), ShouldEqual, "val2b")
			So(an.Attr("key3", nil, false), ShouldEqual, "val3")
		})
	})
	Convey("An abstractNode attributes can check for a role", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "roleFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A role can be checked, whatever its value is", func() {
			So(an.HasRole(nil), ShouldBeFalse)
			So(parentDocument.HasRole(nil), ShouldBeTrue)
			an.SetParent(parent)
			So(an.HasRole(nil), ShouldBeTrue)
		})
		Convey("A role can be checked against an expected value", func() {
			an := newAbstractNode(nil, document)
			an.SetParent(parent)
			So(an.HasRole("roleFromAN"), ShouldBeFalse)
			So(an.HasRole("roleFromParentDocument"), ShouldBeTrue)
			an.setAttr("role", "roleFromAN", true)
			So(an.HasRole("roleFromAN"), ShouldBeTrue)
			So(an.HasRole("roleFromParentDocument"), ShouldBeFalse)
		})
	})
	Convey("An abstractNode attributes can check for a role name", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "role1FromParentDocument role2FromParentDocument role3FromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)

		Convey("A role name can be checked on the document", func() {
			So(an.HasARole("role3FromAN"), ShouldBeFalse)
			So(an.HasARole("role2FromParentDocument"), ShouldBeFalse)
			an.SetParent(parent)
			So(an.Document(), ShouldEqual, parentDocument)
			//So(an.Document().Attr("role", nil, true), ShouldEqual, "test")
			So(an.HasARole("role2FromParentDocument"), ShouldBeTrue)
			So(an.HasARole("role4FromParentDocument"), ShouldBeFalse)
		})
		Convey("A role name can be checked on the abstractNode itself", func() {
			an.setAttr("role", "role1FromAN role2FromAN role3FromAN", true)
			So(an.HasARole("role3FromAN"), ShouldBeTrue)
		})
		Convey("An empty role name is always false=", func() {
			So(an.HasARole(""), ShouldBeFalse)
		})
	})
	Convey("An abstractNode attributes can access role", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "roleFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A role can be access from an document, when an has no role", func() {
			So(an.Role(), ShouldBeNil)
			So(parentDocument.Role(), ShouldEqual, "roleFromParentDocument")
			an.SetParent(parent)
			So(an.Role(), ShouldEqual, "roleFromParentDocument")
		})
		Convey("A role can be access from an itself", func() {
			an.setAttr("role", "roleFromAN", true)
			So(an.Role(), ShouldEqual, "roleFromAN")
		})
	})
	Convey("An abstractNode attributes can access role names", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "role1FromParentDocument role2FromParentDocument role3FromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)

		Convey("A role name can be accessed on the document", func() {
			So(len(an.RoleNames()), ShouldBeZeroValue)
			an.SetParent(parent)
			So(an.Document(), ShouldEqual, parentDocument)
			So(len(an.RoleNames()), ShouldEqual, 3)
		})
		Convey("A role name can be accessed on the abstractNode itself", func() {
			an.setAttr("role", "role1FromAN role2FromAN role3FromAN role5FromAN role4FromAN", true)
			So(len(an.RoleNames()), ShouldEqual, 5)
		})
	})

	Convey("An abstractNode attributes can check for a reftext", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("reftext", "reftextFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A reftext can be checked on the document", func() {
			So(an.HasReftext(), ShouldBeFalse)
			So(parentDocument.HasReftext(), ShouldBeTrue)
			an.SetParent(parent)
			So(an.HasReftext(), ShouldBeTrue)
		})
		Convey("A reftext can be checked directly on the abstractNode", func() {
			an := newAbstractNode(nil, document)
			parentDocument := newAbstractNode(nil, document)
			parent := newAbstractNode(parentDocument, document)
			So(an.HasReftext(), ShouldBeFalse)
			an.SetParent(parent)
			So(an.HasReftext(), ShouldBeFalse)
			an.setAttr("reftext", "reftextFromAN", true)
			So(an.HasReftext(), ShouldBeTrue)
			So(an.Document().HasReftext(), ShouldBeFalse)
		})
	})

	Convey("An abstractNode attributes can access reftext", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("reftext", "reftextFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A reftext can be access from an document, when an has no reftext", func() {
			So(an.Reftext(), ShouldBeNil)
			So(parentDocument.Reftext(), ShouldEqual, "reftextFromParentDocument")
			an.SetParent(parent)
			So(an.Reftext(), ShouldEqual, "reftextFromParentDocument")
		})
		Convey("A reftext can be access from an itself", func() {
			an.setAttr("reftext", "reftextFromAN", true)
			So(an.Reftext(), ShouldEqual, "reftextFromAN")
		})
	})

	Convey("An abstractNode attributes can check for Slash Usage", t, func() {
		an := newAbstractNode(nil, section)
		parentDocument := newAbstractNode(nil, document)
		Convey("A abstractNode without document won't use slash", func() {
			So(an.ShortTagSlash(), ShouldBeNil)
		})
		Convey("A abstractNode with document htmlsyntax set to not xml won't use slash", func() {
			parentDocument.setAttr("htmlsyntax", "notxml", true)
			parent := newAbstractNode(parentDocument, document)
			an.SetParent(parent)
			So(an.ShortTagSlash(), ShouldBeNil)
		})
		Convey("A abstractNode with document htmlsyntax set to not xml will use slash", func() {
			parentDocument.setAttr("htmlsyntax", "xml", true)
			So(strings.Trim(strconv.QuoteRune(*an.ShortTagSlash()), "'"), ShouldEqual, "/")
		})
	})
	Convey("An abstractNode attributes can build media uri", t, func() {
		an := newAbstractNode(nil, section)
		target := ""
		Convey("If the target media is a URI reference, then leave it untouched.", func() {
			target = "data:info"
			// So(REGEXP[":uri_sniff"].String(), ShouldEqual, "^[a-zA-Z][a-zA-Z0-9.+-]*:/{0,2}.*")
			// So(REGEXP[":uri_sniff"].MatchString(target), ShouldBeTrue)
			So(an.MediaUri(target, "dummy"), ShouldEqual, target)
		})
	})
}
<commit_msg>Rename Option() calls into HasOption() calls.<commit_after>package asciidocgo

import (
	. "github.com/smartystreets/goconvey/convey"
	"strconv"
	"strings"
	"testing"
)

func TestAbstractNode(t *testing.T) {

	Convey("An abstractNode can be initialized", t, func() {

		Convey("By default, an AbstractNode can be created", func() {
			So(&abstractNode{}, ShouldNotBeNil)
		})
		Convey("An AbstractNode takes a parent and a context", func() {
			So(newAbstractNode(nil, document), ShouldNotBeNil)
		})
		Convey("If context is a document, then parent is nil and document is parent", func() {
			parent := &abstractNode{}
			an := newAbstractNode(parent, document)
			So(an.Context(), ShouldEqual, document)
			So(an.Parent(), ShouldBeNil)
			So(an.Document(), ShouldEqual, parent)
		})
		Convey("If context is not document, then parent is parent and document is parent document", func() {
			parent := &abstractNode{nil, document, &abstractNode{}, nil, &substitutors{}}
			an := newAbstractNode(parent, section)
			So(an.Context(), ShouldEqual, section)
			So(an.Parent(), ShouldEqual, parent)
			So(an.Document(), ShouldEqual, parent.Document())
		})
		Convey("If context is not document, and parent is nil, then document is nil", func() {
			an := newAbstractNode(nil, section)
			So(an.Context(), ShouldEqual, section)
			So(an.Parent(), ShouldBeNil)
			So(an.Document(), ShouldBeNil)
		})

		Convey("An abstractNode has an empty attributes map", func() {
			an := newAbstractNode(nil, section)
			So(len(an.Attributes()), ShouldEqual, 0)
		})
	})

	Convey("An abstractNode can be associated to a parent", t, func() {
		an := newAbstractNode(nil, section)
		documentParent := &abstractNode{}
		parent := &abstractNode{nil, document, documentParent, nil, &substitutors{}}
		an.SetParent(parent)
		So(an.Parent(), ShouldEqual, parent)
		So(an.Document(), ShouldEqual, parent.Document())
	})

	Convey("An abstractNode can retrieve an attribute", t, func() {

		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("key", "val1", true)
		parent := newAbstractNode(parentDocument, document)
		an := newAbstractNode(nil, document)
		Convey("If inherited, it is the attribute if there, or the document attribute, or default value", func() {
			So(an.Attr("key", nil, true), ShouldBeNil)
			an.setAttr("key", "val", false)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
			an.SetParent(parent)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
			delete(an.attributes, "key")
			So(an.Attr("key", nil, true), ShouldEqual, "val1")
			// an should have for parent a child, which has an as a document
			// then an.document would be "parent".document, meaning an, when
			// setting an.setParent(child)
			an.document = an
			an.setAttr("key", "val", false)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
		})
		Convey("If not inherited, it is the attribute if there, or default value", func() {
			an.SetParent(parent)
			So(an.Document(), ShouldEqual, parentDocument)
			So(an.Document(), ShouldEqual, parent.Document())
			So(parentDocument.Attr("key", nil, false), ShouldEqual, "val1")
			So(an.Attr("key", nil, false), ShouldEqual, "val")
		})
	})
	Convey("An abstractNode can set an attribute", t, func() {
		an := newAbstractNode(nil, document)
		an.setAttr("key", "val", true)
		So(an.Attr("key", nil, true), ShouldEqual, "val")
		Convey("If not override and already present, the value should not change", func() {
			res := an.setAttr("key", "val1", false)
			So(an.Attr("key", nil, true), ShouldEqual, "val")
			So(res, ShouldBeFalse)
		})
	})

	Convey("An abstractNode can set an option attribute", t, func() {
		an := newAbstractNode(nil, document)
		Convey("First option means options attributes has len 1", func() {
			an.SetOption("opt1")
			So(len(an.attributes["options"].(map[string]bool)), ShouldEqual, 1)
			So(an.Attr("opt1-option", nil, false), ShouldEqual, true)
		})
		Convey("Second option means options attributes has len 2", func() {
			an.SetOption("opt2")
			So(len(an.attributes["options"].(map[string]bool)), ShouldEqual, 2)
			So(an.Attr("opt2-option", nil, false), ShouldEqual, true)
		})
	})

	Convey("An abstractNode can get an option attribute", t, func() {
		an := newAbstractNode(nil, document)
		Convey("Zero option means Option returns false", func() {
			So(an.HasOption("opt1"), ShouldBeFalse)
			an.SetOption("opt1")
		})
		Convey("One option means Option returns true", func() {
			So(an.HasOption("opt1"), ShouldBeTrue)
		})
	})

	Convey("An abstractNode update option attributes with other attributes", t, func() {
		an := newAbstractNode(nil, document)
		an.setAttr("key1", "val1", true)
		an.setAttr("key2", "val2", true)
		Convey("New Attributes are added during an update", func() {
			attrs := map[string]interface{}{"key3": "val3", "key4": "val4"}
			an.Update(attrs)
			So(an.Attr("key1", nil, false), ShouldEqual, "val1")
			So(an.Attr("key2", nil, false), ShouldEqual, "val2")
			So(an.Attr("key3", nil, false), ShouldEqual, "val3")
			So(an.Attr("key4", nil, false), ShouldEqual, "val4")
		})
		Convey("Common Attributes are overrriden during an update", func() {
			attrs := map[string]interface{}{"key2": "val2b", "key3": "val3"}
			an.Update(attrs)
			So(an.Attr("key1", nil, false), ShouldEqual, "val1")
			So(an.Attr("key2", nil, false), ShouldEqual, "val2b")
			So(an.Attr("key3", nil, false), ShouldEqual, "val3")
		})
	})
	Convey("An abstractNode attributes can check for a role", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "roleFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A role can be checked, whatever its value is", func() {
			So(an.HasRole(nil), ShouldBeFalse)
			So(parentDocument.HasRole(nil), ShouldBeTrue)
			an.SetParent(parent)
			So(an.HasRole(nil), ShouldBeTrue)
		})
		Convey("A role can be checked against an expected value", func() {
			an := newAbstractNode(nil, document)
			an.SetParent(parent)
			So(an.HasRole("roleFromAN"), ShouldBeFalse)
			So(an.HasRole("roleFromParentDocument"), ShouldBeTrue)
			an.setAttr("role", "roleFromAN", true)
			So(an.HasRole("roleFromAN"), ShouldBeTrue)
			So(an.HasRole("roleFromParentDocument"), ShouldBeFalse)
		})
	})
	Convey("An abstractNode attributes can check for a role name", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "role1FromParentDocument role2FromParentDocument role3FromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)

		Convey("A role name can be checked on the document", func() {
			So(an.HasARole("role3FromAN"), ShouldBeFalse)
			So(an.HasARole("role2FromParentDocument"), ShouldBeFalse)
			an.SetParent(parent)
			So(an.Document(), ShouldEqual, parentDocument)
			//So(an.Document().Attr("role", nil, true), ShouldEqual, "test")
			So(an.HasARole("role2FromParentDocument"), ShouldBeTrue)
			So(an.HasARole("role4FromParentDocument"), ShouldBeFalse)
		})
		Convey("A role name can be checked on the abstractNode itself", func() {
			an.setAttr("role", "role1FromAN role2FromAN role3FromAN", true)
			So(an.HasARole("role3FromAN"), ShouldBeTrue)
		})
		Convey("An empty role name is always false=", func() {
			So(an.HasARole(""), ShouldBeFalse)
		})
	})
	Convey("An abstractNode attributes can access role", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "roleFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A role can be access from an document, when an has no role", func() {
			So(an.Role(), ShouldBeNil)
			So(parentDocument.Role(), ShouldEqual, "roleFromParentDocument")
			an.SetParent(parent)
			So(an.Role(), ShouldEqual, "roleFromParentDocument")
		})
		Convey("A role can be access from an itself", func() {
			an.setAttr("role", "roleFromAN", true)
			So(an.Role(), ShouldEqual, "roleFromAN")
		})
	})
	Convey("An abstractNode attributes can access role names", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("role", "role1FromParentDocument role2FromParentDocument role3FromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)

		Convey("A role name can be accessed on the document", func() {
			So(len(an.RoleNames()), ShouldBeZeroValue)
			an.SetParent(parent)
			So(an.Document(), ShouldEqual, parentDocument)
			So(len(an.RoleNames()), ShouldEqual, 3)
		})
		Convey("A role name can be accessed on the abstractNode itself", func() {
			an.setAttr("role", "role1FromAN role2FromAN role3FromAN role5FromAN role4FromAN", true)
			So(len(an.RoleNames()), ShouldEqual, 5)
		})
	})

	Convey("An abstractNode attributes can check for a reftext", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("reftext", "reftextFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A reftext can be checked on the document", func() {
			So(an.HasReftext(), ShouldBeFalse)
			So(parentDocument.HasReftext(), ShouldBeTrue)
			an.SetParent(parent)
			So(an.HasReftext(), ShouldBeTrue)
		})
		Convey("A reftext can be checked directly on the abstractNode", func() {
			an := newAbstractNode(nil, document)
			parentDocument := newAbstractNode(nil, document)
			parent := newAbstractNode(parentDocument, document)
			So(an.HasReftext(), ShouldBeFalse)
			an.SetParent(parent)
			So(an.HasReftext(), ShouldBeFalse)
			an.setAttr("reftext", "reftextFromAN", true)
			So(an.HasReftext(), ShouldBeTrue)
			So(an.Document().HasReftext(), ShouldBeFalse)
		})
	})

	Convey("An abstractNode attributes can access reftext", t, func() {
		an := newAbstractNode(nil, document)
		parentDocument := newAbstractNode(nil, document)
		parentDocument.setAttr("reftext", "reftextFromParentDocument", true)
		parent := newAbstractNode(parentDocument, document)
		Convey("A reftext can be access from an document, when an has no reftext", func() {
			So(an.Reftext(), ShouldBeNil)
			So(parentDocument.Reftext(), ShouldEqual, "reftextFromParentDocument")
			an.SetParent(parent)
			So(an.Reftext(), ShouldEqual, "reftextFromParentDocument")
		})
		Convey("A reftext can be access from an itself", func() {
			an.setAttr("reftext", "reftextFromAN", true)
			So(an.Reftext(), ShouldEqual, "reftextFromAN")
		})
	})

	Convey("An abstractNode attributes can check for Slash Usage", t, func() {
		an := newAbstractNode(nil, section)
		parentDocument := newAbstractNode(nil, document)
		Convey("A abstractNode without document won't use slash", func() {
			So(an.ShortTagSlash(), ShouldBeNil)
		})
		Convey("A abstractNode with document htmlsyntax set to not xml won't use slash", func() {
			parentDocument.setAttr("htmlsyntax", "notxml", true)
			parent := newAbstractNode(parentDocument, document)
			an.SetParent(parent)
			So(an.ShortTagSlash(), ShouldBeNil)
		})
		Convey("A abstractNode with document htmlsyntax set to not xml will use slash", func() {
			parentDocument.setAttr("htmlsyntax", "xml", true)
			So(strings.Trim(strconv.QuoteRune(*an.ShortTagSlash()), "'"), ShouldEqual, "/")
		})
	})
	Convey("An abstractNode attributes can build media uri", t, func() {
		an := newAbstractNode(nil, section)
		target := ""
		Convey("If the target media is a URI reference, then leave it untouched.", func() {
			target = "data:info"
			// So(REGEXP[":uri_sniff"].String(), ShouldEqual, "^[a-zA-Z][a-zA-Z0-9.+-]*:/{0,2}.*")
			// So(REGEXP[":uri_sniff"].MatchString(target), ShouldBeTrue)
			So(an.MediaUri(target, "dummy"), ShouldEqual, target)
		})
	})
}
<|endoftext|>


<commit_before>package run

import (
	"fmt"
	"time"

	"github.com/influxdb/influxdb/cluster"
	"github.com/influxdb/influxdb/meta"
	"github.com/influxdb/influxdb/services/admin"
	"github.com/influxdb/influxdb/services/collectd"
	"github.com/influxdb/influxdb/services/continuous_querier"
	"github.com/influxdb/influxdb/services/graphite"
	"github.com/influxdb/influxdb/services/httpd"
	"github.com/influxdb/influxdb/services/opentsdb"
	"github.com/influxdb/influxdb/services/retention"
	"github.com/influxdb/influxdb/services/udp"
	"github.com/influxdb/influxdb/tsdb"
)

// Server represents a container for the metadata and storage data and services.
// It is built using a Config and it manages the startup and shutdown of all
// services in the proper order.
type Server struct {
	err     chan error
	closing chan struct{}

	MetaStore     *meta.Store
	TSDBStore     *tsdb.Store
	QueryExecutor *tsdb.QueryExecutor
	PointsWriter  *cluster.PointsWriter
	ShardWriter   *cluster.ShardWriter

	Services []Service
}

// NewServer returns a new instance of Server built from a config.
func NewServer(c *Config) *Server {
	// Construct base meta store and data store.
	s := &Server{
		err:       make(chan error),
		closing:   make(chan struct{}),
		MetaStore: meta.NewStore(c.Meta),
		TSDBStore: tsdb.NewStore(c.Data.Dir),
	}

	// Initialize query executor.
	s.QueryExecutor = tsdb.NewQueryExecutor(s.TSDBStore)
	s.QueryExecutor.MetaStore = s.MetaStore
	s.QueryExecutor.MetaStatementExecutor = &meta.StatementExecutor{Store: s.MetaStore}

	// Set the shard writer
	s.ShardWriter = cluster.NewShardWriter(time.Duration(c.Cluster.ShardWriterTimeout))
	s.ShardWriter.MetaStore = s.MetaStore

	// Initialize points writer.
	s.PointsWriter = cluster.NewPointsWriter()
	s.PointsWriter.MetaStore = s.MetaStore
	s.PointsWriter.TSDBStore = s.TSDBStore
	s.PointsWriter.ShardWriter = s.ShardWriter

	// Append services.
	s.appendClusterService(c.Cluster)
	s.appendAdminService(c.Admin)
	s.appendHTTPDService(c.HTTPD)
	s.appendCollectdService(c.Collectd)
	s.appendOpenTSDBService(c.OpenTSDB)
	s.appendUDPService(c.UDP)
	s.appendRetentionPolicyService(c.Retention)
	s.appendContinuousQueryService(c.ContinuousQuery)
	for _, g := range c.Graphites {
		s.appendGraphiteService(g)
	}

	return s
}

func (s *Server) appendClusterService(c cluster.Config) {
	srv := cluster.NewService(c)
	srv.TSDBStore = s.TSDBStore
	s.Services = append(s.Services, srv)
}

func (s *Server) appendRetentionPolicyService(c retention.Config) {
	if !c.Enabled {
		return
	}
	srv := retention.NewService(c)
	srv.MetaStore = s.MetaStore
	srv.TSDBStore = s.TSDBStore
	s.Services = append(s.Services, srv)
}

func (s *Server) appendAdminService(c admin.Config) {
	if !c.Enabled {
		return
	}
	srv := admin.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendHTTPDService(c httpd.Config) {
	if !c.Enabled {
		return
	}
	srv := httpd.NewService(c)
	srv.Handler.MetaStore = s.MetaStore
	srv.Handler.QueryExecutor = s.QueryExecutor
	srv.Handler.PointsWriter = s.PointsWriter
	s.Services = append(s.Services, srv)
}

func (s *Server) appendCollectdService(c collectd.Config) {
	if !c.Enabled {
		return
	}
	srv := collectd.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendOpenTSDBService(c opentsdb.Config) {
	if !c.Enabled {
		return
	}
	srv := opentsdb.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendGraphiteService(c graphite.Config) {
	if !c.Enabled {
		return
	}
	srv := graphite.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendUDPService(c udp.Config) {
	if !c.Enabled {
		return
	}
	srv := udp.NewService(c)
	srv.Server.PointsWriter = s.PointsWriter
}

func (s *Server) appendContinuousQueryService(c continuous_querier.Config) {
	srv := continuous_querier.NewService(c)
	srv.MetaStore = s.MetaStore
	srv.QueryExecutor = s.QueryExecutor
	srv.PointsWriter = s.PointsWriter
	s.Services = append(s.Services, srv)
}

// Err returns an error channel that multiplexes all out of band errors received from all services.
func (s *Server) Err() <-chan error { return s.err }

// Open opens the meta and data store and all services.
func (s *Server) Open() error {
	if err := func() error {
		// Open meta store.
		if err := s.MetaStore.Open(); err != nil {
			return fmt.Errorf("open meta store: %s", err)
		}
		go s.monitorErrorChan(s.MetaStore.Err())

		// Wait for the store to initialize.
		<-s.MetaStore.Ready()

		// Open TSDB store.
		if err := s.TSDBStore.Open(); err != nil {
			return fmt.Errorf("open tsdb store: %s", err)
		}

		for _, service := range s.Services {
			if err := service.Open(); err != nil {
				return fmt.Errorf("open service: %s", err)
			}
		}

		return nil

	}(); err != nil {
		s.Close()
		return err
	}

	return nil
}

// Close shuts down the meta and data stores and all services.
func (s *Server) Close() error {
	if s.MetaStore != nil {
		s.MetaStore.Close()
	}
	if s.TSDBStore != nil {
		s.TSDBStore.Close()
	}
	for _, service := range s.Services {
		service.Close()
	}
	close(s.closing)
	return nil
}

// monitorErrorChan reads an error channel and resends it through the server.
func (s *Server) monitorErrorChan(ch <-chan error) {
	for {
		select {
		case err, ok := <-ch:
			if !ok {
				return
			}
			s.err <- err
		case <-s.closing:
			return
		}
	}
}

// Service represents a service attached to the server.
type Service interface {
	Open() error
	Close() error
}
<commit_msg>fix #2733: check config before enabling CQ service<commit_after>package run

import (
	"fmt"
	"time"

	"github.com/influxdb/influxdb/cluster"
	"github.com/influxdb/influxdb/meta"
	"github.com/influxdb/influxdb/services/admin"
	"github.com/influxdb/influxdb/services/collectd"
	"github.com/influxdb/influxdb/services/continuous_querier"
	"github.com/influxdb/influxdb/services/graphite"
	"github.com/influxdb/influxdb/services/httpd"
	"github.com/influxdb/influxdb/services/opentsdb"
	"github.com/influxdb/influxdb/services/retention"
	"github.com/influxdb/influxdb/services/udp"
	"github.com/influxdb/influxdb/tsdb"
)

// Server represents a container for the metadata and storage data and services.
// It is built using a Config and it manages the startup and shutdown of all
// services in the proper order.
type Server struct {
	err     chan error
	closing chan struct{}

	MetaStore     *meta.Store
	TSDBStore     *tsdb.Store
	QueryExecutor *tsdb.QueryExecutor
	PointsWriter  *cluster.PointsWriter
	ShardWriter   *cluster.ShardWriter

	Services []Service
}

// NewServer returns a new instance of Server built from a config.
func NewServer(c *Config) *Server {
	// Construct base meta store and data store.
	s := &Server{
		err:       make(chan error),
		closing:   make(chan struct{}),
		MetaStore: meta.NewStore(c.Meta),
		TSDBStore: tsdb.NewStore(c.Data.Dir),
	}

	// Initialize query executor.
	s.QueryExecutor = tsdb.NewQueryExecutor(s.TSDBStore)
	s.QueryExecutor.MetaStore = s.MetaStore
	s.QueryExecutor.MetaStatementExecutor = &meta.StatementExecutor{Store: s.MetaStore}

	// Set the shard writer
	s.ShardWriter = cluster.NewShardWriter(time.Duration(c.Cluster.ShardWriterTimeout))
	s.ShardWriter.MetaStore = s.MetaStore

	// Initialize points writer.
	s.PointsWriter = cluster.NewPointsWriter()
	s.PointsWriter.MetaStore = s.MetaStore
	s.PointsWriter.TSDBStore = s.TSDBStore
	s.PointsWriter.ShardWriter = s.ShardWriter

	// Append services.
	s.appendClusterService(c.Cluster)
	s.appendAdminService(c.Admin)
	s.appendHTTPDService(c.HTTPD)
	s.appendCollectdService(c.Collectd)
	s.appendOpenTSDBService(c.OpenTSDB)
	s.appendUDPService(c.UDP)
	s.appendRetentionPolicyService(c.Retention)
	s.appendContinuousQueryService(c.ContinuousQuery)
	for _, g := range c.Graphites {
		s.appendGraphiteService(g)
	}

	return s
}

func (s *Server) appendClusterService(c cluster.Config) {
	srv := cluster.NewService(c)
	srv.TSDBStore = s.TSDBStore
	s.Services = append(s.Services, srv)
}

func (s *Server) appendRetentionPolicyService(c retention.Config) {
	if !c.Enabled {
		return
	}
	srv := retention.NewService(c)
	srv.MetaStore = s.MetaStore
	srv.TSDBStore = s.TSDBStore
	s.Services = append(s.Services, srv)
}

func (s *Server) appendAdminService(c admin.Config) {
	if !c.Enabled {
		return
	}
	srv := admin.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendHTTPDService(c httpd.Config) {
	if !c.Enabled {
		return
	}
	srv := httpd.NewService(c)
	srv.Handler.MetaStore = s.MetaStore
	srv.Handler.QueryExecutor = s.QueryExecutor
	srv.Handler.PointsWriter = s.PointsWriter
	s.Services = append(s.Services, srv)
}

func (s *Server) appendCollectdService(c collectd.Config) {
	if !c.Enabled {
		return
	}
	srv := collectd.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendOpenTSDBService(c opentsdb.Config) {
	if !c.Enabled {
		return
	}
	srv := opentsdb.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendGraphiteService(c graphite.Config) {
	if !c.Enabled {
		return
	}
	srv := graphite.NewService(c)
	s.Services = append(s.Services, srv)
}

func (s *Server) appendUDPService(c udp.Config) {
	if !c.Enabled {
		return
	}
	srv := udp.NewService(c)
	srv.Server.PointsWriter = s.PointsWriter
}

func (s *Server) appendContinuousQueryService(c continuous_querier.Config) {
	if !c.Enabled {
		return
	}
	srv := continuous_querier.NewService(c)
	srv.MetaStore = s.MetaStore
	srv.QueryExecutor = s.QueryExecutor
	srv.PointsWriter = s.PointsWriter
	s.Services = append(s.Services, srv)
}

// Err returns an error channel that multiplexes all out of band errors received from all services.
func (s *Server) Err() <-chan error { return s.err }

// Open opens the meta and data store and all services.
func (s *Server) Open() error {
	if err := func() error {
		// Open meta store.
		if err := s.MetaStore.Open(); err != nil {
			return fmt.Errorf("open meta store: %s", err)
		}
		go s.monitorErrorChan(s.MetaStore.Err())

		// Wait for the store to initialize.
		<-s.MetaStore.Ready()

		// Open TSDB store.
		if err := s.TSDBStore.Open(); err != nil {
			return fmt.Errorf("open tsdb store: %s", err)
		}

		for _, service := range s.Services {
			if err := service.Open(); err != nil {
				return fmt.Errorf("open service: %s", err)
			}
		}

		return nil

	}(); err != nil {
		s.Close()
		return err
	}

	return nil
}

// Close shuts down the meta and data stores and all services.
func (s *Server) Close() error {
	if s.MetaStore != nil {
		s.MetaStore.Close()
	}
	if s.TSDBStore != nil {
		s.TSDBStore.Close()
	}
	for _, service := range s.Services {
		service.Close()
	}
	close(s.closing)
	return nil
}

// monitorErrorChan reads an error channel and resends it through the server.
func (s *Server) monitorErrorChan(ch <-chan error) {
	for {
		select {
		case err, ok := <-ch:
			if !ok {
				return
			}
			s.err <- err
		case <-s.closing:
			return
		}
	}
}

// Service represents a service attached to the server.
type Service interface {
	Open() error
	Close() error
}
<|endoftext|>


<commit_before>//go:generate mapstructure-to-hcl2 -type Config

package dockerimport

import (
	"context"
	"fmt"

	"github.com/hashicorp/hcl/v2/hcldec"
	"github.com/hashicorp/packer/builder/docker"
	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/helper/config"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/post-processor/artifice"
	"github.com/hashicorp/packer/template/interpolate"
)

const BuilderId = "packer.post-processor.docker-import"

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	Repository string   `mapstructure:"repository"`
	Tag        string   `mapstructure:"tag"`
	Changes    []string `mapstructure:"changes"`

	ctx interpolate.Context
}

type PostProcessor struct {
	config Config
}

func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }

func (p *PostProcessor) Configure(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{},
		},
	}, raws...)
	if err != nil {
		return err
	}

	return nil

}

func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) {
	switch artifact.BuilderId() {
	case docker.BuilderId, artifice.BuilderId:
		break
	default:
		err := fmt.Errorf(
			"Unknown artifact type: %s\nCan only import from Docker builder and Artifice post-processor artifacts.",
			artifact.BuilderId())
		return nil, false, false, err
	}

	importRepo := p.config.Repository
	if p.config.Tag != "" {
		importRepo += ":" + p.config.Tag
	}

	driver := &docker.DockerDriver{Ctx: &p.config.ctx, Ui: ui}

	ui.Message("Importing image: " + artifact.Id())
	ui.Message("Repository: " + importRepo)
	id, err := driver.Import(artifact.Files()[0], p.config.Changes, importRepo)
	if err != nil {
		return nil, false, false, err
	}

	ui.Message("Imported ID: " + id)

	// Build the artifact
	artifact = &docker.ImportArtifact{
		BuilderIdValue: BuilderId,
		Driver:         driver,
		IdValue:        importRepo,
	}

	return artifact, false, false, nil
}
<commit_msg>make friendly error message (#9605)<commit_after>//go:generate mapstructure-to-hcl2 -type Config

package dockerimport

import (
	"context"
	"fmt"

	"github.com/hashicorp/hcl/v2/hcldec"
	"github.com/hashicorp/packer/builder/docker"
	"github.com/hashicorp/packer/common"
	"github.com/hashicorp/packer/helper/config"
	"github.com/hashicorp/packer/packer"
	"github.com/hashicorp/packer/post-processor/artifice"
	"github.com/hashicorp/packer/template/interpolate"
)

const BuilderId = "packer.post-processor.docker-import"

type Config struct {
	common.PackerConfig `mapstructure:",squash"`

	Repository string   `mapstructure:"repository"`
	Tag        string   `mapstructure:"tag"`
	Changes    []string `mapstructure:"changes"`

	ctx interpolate.Context
}

type PostProcessor struct {
	config Config
}

func (p *PostProcessor) ConfigSpec() hcldec.ObjectSpec { return p.config.FlatMapstructure().HCL2Spec() }

func (p *PostProcessor) Configure(raws ...interface{}) error {
	err := config.Decode(&p.config, &config.DecodeOpts{
		Interpolate:        true,
		InterpolateContext: &p.config.ctx,
		InterpolateFilter: &interpolate.RenderFilter{
			Exclude: []string{},
		},
	}, raws...)
	if err != nil {
		return err
	}

	return nil

}

func (p *PostProcessor) PostProcess(ctx context.Context, ui packer.Ui, artifact packer.Artifact) (packer.Artifact, bool, bool, error) {
	switch artifact.BuilderId() {
	case docker.BuilderId, artifice.BuilderId:
		break
	default:
		err := fmt.Errorf(
			"Unknown artifact type: %s\nCan only import from Docker builder "+
				"and Artifice post-processor artifacts. If you are getting this "+
				"error after having run the docker builder, it may be because you "+
				"set commit: true in your Docker builder, so the image is "+
				"already imported. ",
			artifact.BuilderId())
		return nil, false, false, err
	}

	importRepo := p.config.Repository
	if p.config.Tag != "" {
		importRepo += ":" + p.config.Tag
	}

	driver := &docker.DockerDriver{Ctx: &p.config.ctx, Ui: ui}

	ui.Message("Importing image: " + artifact.Id())
	ui.Message("Repository: " + importRepo)
	id, err := driver.Import(artifact.Files()[0], p.config.Changes, importRepo)
	if err != nil {
		return nil, false, false, err
	}

	ui.Message("Imported ID: " + id)

	// Build the artifact
	artifact = &docker.ImportArtifact{
		BuilderIdValue: BuilderId,
		Driver:         driver,
		IdValue:        importRepo,
	}

	return artifact, false, false, nil
}
<|endoftext|>


<commit_before>package webfw_test

import (
	"net/http"

	"github.com/urandom/webfw"
	"github.com/urandom/webfw/context"
	"github.com/urandom/webfw/renderer"
)

type Hello struct {
	webfw.BaseController
}

func NewHello(pattern string) Hello {
	return Hello{webfw.NewBaseController(pattern, MethodAll, "")}
}

func (con Hello) Handler(c *context.Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		params := webfw.GetParams(c, r)
		d := renderer.RenderData{"name": params["name"]}

		err := webfw.GetRenderCtx(c, r)(w, d, "hello.tmpl")
		if err != nil {
			webfw.GetLogger(c, r).Print(err)
		}
	}
}

func Example() {
	s := webfw.NewServer()

	dispatcher := s.Dispatcher("/")

	dispatcher.Handle(NewHello("/hello/:name"))
	if err := s.ListenAndServe(); err != nil {
		panic(err)
	}
}

func main() {
	Example()
}
<commit_msg>fix the example api usage<commit_after>package webfw_test

import (
	"net/http"

	"github.com/urandom/webfw"
	"github.com/urandom/webfw/context"
	"github.com/urandom/webfw/renderer"
)

type Hello struct {
	webfw.BaseController
}

func NewHello(pattern string) Hello {
	return Hello{webfw.NewBaseController(pattern, webfw.MethodAll, "")}
}

func (con Hello) Handler(c context.Context) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		params := webfw.GetParams(c, r)
		d := renderer.RenderData{"name": params["name"]}

		err := webfw.GetRenderCtx(c, r)(w, d, "hello.tmpl")
		if err != nil {
			webfw.GetLogger(c, r).Print(err)
		}
	}
}

func Example() {
	s := webfw.NewServer()

	dispatcher := s.Dispatcher("/")

	dispatcher.Handle(NewHello("/hello/:name"))
	if err := s.ListenAndServe(); err != nil {
		panic(err)
	}
}

func main() {
	Example()
}
<|endoftext|>


<commit_before>// Copyright 2021 ETH Zurich
//
// 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 reservationstore

import (
	"context"
	"fmt"
	"sync"
	"time"

	base "github.com/scionproto/scion/go/co/reservation"
	"github.com/scionproto/scion/go/co/reservation/conf"
	"github.com/scionproto/scion/go/co/reservation/segment"
	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/colibri/reservation"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/pathpol"
	"github.com/scionproto/scion/go/lib/serrors"
	colpath "github.com/scionproto/scion/go/lib/slayers/path/colibri"
	"github.com/scionproto/scion/go/lib/snet"
)

// sleepAtLeast is the time duration that the keeper will sleep at a minimum, even
// if it's called very frequently.
const sleepAtLeast = 4 * time.Second

const sleepAtMost = 5 * time.Minute

// min validity in the future for the reservations when checking their compliance,
// the bigger the value, the more probable it is not to break continuity.
// Typically this value would be twice the max. sleep period, to ensure no index would
// expire while the keeper is sleeping.
const minDuration = 2 * sleepAtMost

// min validity of new indices/reservations. The bigger the value, the longer a single index
// can be used. Too big a value could produce errors in the admission for some ASes.
// This value would typically be equal to twice minDuration.
const newIndexMinDuration = 2 * minDuration

// ServiceFacilitator defines a minimal interface that has to be implemented to be
// usable by the keeper.
type ServiceFacilitator interface {
	PathsTo(ctx context.Context, dst addr.IA) ([]snet.Path, error)
	SetupRequest(ctx context.Context, req *segment.SetupReq) error
	ActivateRequest(
		context.Context,
		*base.Request,
		base.PathSteps,
		*colpath.ColibriPathMinimal,
		bool,
	) error
	GetReservationsAtSource(ctx context.Context) ([]*segment.Reservation, error)
	DeleteExpiredIndices(ctx context.Context) error
}

// keeper looks after the reservations configured in reservations.json
// It starts by cleaning up those reservations that have expired.
// The keeper tries to match existing reservations with configured entries.
// If no match is found, a new reservation will be created.
type keeper struct {
	now        func() time.Time
	localIA    addr.IA
	sleepUntil time.Time // nothing to do in the keeper until this time
	provider   ServiceFacilitator
	entries    []*entry
}

type entry struct {
	conf *configuration
	rsv  *segment.Reservation
}

// PrepareSetupRequest creates a valid setup request with the steps always in the direction of
// the traffic of the SegR, and the transport path always in the direction of the next
// colibri service (thus for down-path SegRs the transport will be in the reverse wrt the steps).
func (e *entry) PrepareSetupRequest(now, expTime time.Time, localAS addr.AS,
	p snet.Path) *segment.SetupReq {

	steps, err := base.StepsFromSnet(p)
	if err != nil {
		log.Info("error in SCION path, cannot convert to steps", "err", err, "path", p)
		panic(err)
	}
	currentStep := 0

	// if the SegR is of down-path type, reverse the steps
	if e.conf.pathType == reservation.DownPath {
		steps = steps.Reverse()
		currentStep = len(steps) - 1
	}

	id, _ := reservation.NewID(localAS, make([]byte, reservation.IDSuffixSegLen))
	return &segment.SetupReq{
		Request:        *base.NewRequest(now, id, 0, len(steps)),
		ExpirationTime: expTime,
		PathType:       e.conf.pathType,
		MinBW:          e.conf.minBW,
		MaxBW:          e.conf.maxBW,
		SplitCls:       e.conf.splitCls,
		PathProps:      e.conf.endProps,
		AllocTrail:     reservation.AllocationBeads{},
		Steps:          steps,
		CurrentStep:    currentStep,
		TransportPath:  nil, // new setups are not transported in colibri paths
	}
}

func (e *entry) PrepareRenewalRequest(now, expTime time.Time) *segment.SetupReq {
	return &segment.SetupReq{
		Request: *base.NewRequest(
			now, &e.rsv.ID, e.rsv.NextIndexToRenew(), len(e.rsv.Steps)),
		ExpirationTime: expTime,
		PathType:       e.conf.pathType,
		MinBW:          e.conf.minBW,
		MaxBW:          e.conf.maxBW,
		SplitCls:       e.rsv.TrafficSplit,
		PathProps:      e.rsv.PathEndProps,
		AllocTrail:     reservation.AllocationBeads{},
		Steps:          e.rsv.Steps.Copy(),
		CurrentStep:    0,
		TransportPath:  e.rsv.TransportPath,
		Reservation:    e.rsv,
	}
}

func NewKeeper(
	ctx context.Context,
	provider ServiceFacilitator,
	conf *conf.Reservations,
	localIA addr.IA,
) (*keeper, error) {

	// load configuration
	reqs, err := parseInitial(conf)
	if err != nil {
		return nil, err
	}
	// cleanup expired indices before reading reservations
	if err := provider.DeleteExpiredIndices(ctx); err != nil {
		return nil, err
	}
	// get existing reservations
	rsvs, err := provider.GetReservationsAtSource(ctx)
	if err != nil {
		return nil, err
	}
	entries := matchRsvsWithConfiguration(rsvs, reqs)

	log.Debug("colibri keeper", "reservations", len(entries))
	return &keeper{
		now:        time.Now,
		localIA:    localIA,
		sleepUntil: time.Now().Add(-time.Nanosecond),
		provider:   provider,
		entries:    entries,
	}, nil
}

// OneShot keeps all reservations healthy. Those that need renewal are renewed, those
// that still have no reservation ID for its config will request a new one.
// The function returns the time when it should be called next.
func (k *keeper) OneShot(ctx context.Context) (time.Time, error) {
	wg := sync.WaitGroup{}
	times := make([]time.Time, len(k.entries))
	errs := make(serrors.List, len(k.entries))
	wg.Add(len(k.entries))
	for i, e := range k.entries {
		i, e := i, e
		go func() {
			defer log.HandlePanic()
			defer wg.Done()
			times[i], errs[i] = k.keepReservation(ctx, e)
		}()
	}
	wg.Wait()
	if err := errs.Coalesce(); err != nil {
		return k.now().Add(sleepAtLeast), err
	}
	// wakeupAtLatest is the maximum to wake up the keeper
	wakeupAtLatest := k.now().Add(sleepAtMost)
	for _, t := range times {
		if t.Before(wakeupAtLatest) {
			wakeupAtLatest = t
		}
	}
	// but the keeper must sleep at least a minimum amount of time
	if wakeupAtLatest.Sub(k.now()) < sleepAtLeast {
		wakeupAtLatest = k.now().Add(sleepAtLeast)
	}
	return wakeupAtLatest, nil
}

// keepReservation will ensure that the reservation exists or a request is created.
func (k *keeper) keepReservation(ctx context.Context, e *entry) (time.Time, error) {
	now := k.now()
	var err error
	if e.rsv == nil {
		e.rsv, err = k.askNewReservation(ctx, e)
		if err != nil {
			return time.Time{}, err
		}
	}

	switch compliance(e, k.now().Add(minDuration)) {
	case Compliant:
	case NeedsIndices:
		err = k.askNewIndices(ctx, e)
	case NeedsActivation:
		err = k.activateIndex(ctx, e)
	}

	if err != nil {
		return time.Time{}, err
	}
	return now.Add(newIndexMinDuration), nil
}

// matchRsvsWithConfiguration matches existing reservations with configuration.
// It returns the appropriate entries to manage from the keeper.
// Those entries without a reservation ID must obtain a new reservation;
// those with a reservation ID will need index activation, etc.
func matchRsvsWithConfiguration(rsvs []*segment.Reservation, conf []*configuration) []*entry {
	conf = append(conf[:0:0], conf...)
	// greedy strategy: for each reservation try to match it with the first compatible configuration
	entries := make([]*entry, 0)
	for _, r := range rsvs {
		i := findCompatibleConfiguration(r, conf)
		if i < 0 {
			continue
		}
		entries = append(entries, &entry{
			conf: conf[i],
			rsv:  r,
		})
		// one conf. is matched against this r; remove that entry from the pool
		conf = append(conf[:i], conf[i+1:]...)
	}
	for _, c := range conf {
		entries = append(entries, &entry{
			conf: c,
		})
	}
	return entries
}

// findCompatibleConfiguration finds the first compatible configuration with the reservation.
// It returns the index of the configuration in the slice, or -1 if no valid one is found.
func findCompatibleConfiguration(r *segment.Reservation, conf []*configuration) int {
	for i, c := range conf {
		switch {
		case r.Steps.DstIA() != c.dst:
			continue
		case r.PathType != c.pathType:
			continue
		case r.TrafficSplit != c.splitCls:
			continue
		case r.PathEndProps != c.endProps:
			continue
		case !c.predicate.EvalInterfaces(r.Steps.Interfaces()):
			continue
		}
		return i
	}
	return -1
}

func (k *keeper) activateIndex(ctx context.Context, e *entry) error {
	req := base.NewRequest(k.now(), &e.rsv.ID, e.rsv.NextIndexToActivate().Idx,
		len(e.rsv.Steps))
	inReverse := e.rsv.PathType == reservation.DownPath
	err := k.provider.ActivateRequest(ctx, req, e.rsv.Steps.Copy(), e.rsv.TransportPath, inReverse)
	if err == nil {
		err = e.rsv.SetIndexActive(req.Index)
	}
	return err
}

// askNewIndices requests a renewal
func (k *keeper) askNewIndices(ctx context.Context, e *entry) error {
	now := k.now()
	req := e.PrepareRenewalRequest(now, now.Add(newIndexMinDuration))
	return k.provider.SetupRequest(ctx, req)
}

func (k *keeper) askNewReservation(ctx context.Context, e *entry) (*segment.Reservation, error) {
	now := k.now()
	paths, err := k.provider.PathsTo(ctx, e.conf.dst)
	if err != nil {
		return nil, err
	}
	// try with each possible path
	paths = e.conf.predicate.Eval(paths)
	for _, p := range paths {
		req := e.PrepareSetupRequest(now, now.Add(newIndexMinDuration), k.localIA.AS(), p)
		err := k.provider.SetupRequest(ctx, req)
		if err == nil {
			if req.Reservation == nil {
				panic("logic error, reservation after new request is empty")
			}
		}
		if req.Reservation != nil {
			return req.Reservation, err
		}
		log.Info("error creating new reservation from best effort path", "path", p, "err", err)
	}
	return nil, serrors.New("no more best effort paths to create reservation", "dst", e.conf.dst)
}

// configuration is a 1 to 1 association to a conf.ReservationEntry
type configuration struct {
	dst       addr.IA
	pathType  reservation.PathType
	predicate *pathpol.Sequence
	minBW     reservation.BWCls
	maxBW     reservation.BWCls
	splitCls  reservation.SplitCls
	endProps  reservation.PathEndProps
}

type Compliance int

const (
	NeedsIndices    = Compliance(iota) // ask for a new index
	NeedsActivation                    // ask to activate index
	Compliant                          // already has an active compliant index
)

func (c Compliance) String() string {
	switch c {
	case NeedsIndices:
		return "NeedsIndices"
	case NeedsActivation:
		return "NeedsActivation"
	case Compliant:
		return "Compliant"
	default:
		panic(fmt.Errorf("unknown value for compliance %d", c))
	}
}

// compliance finds the status of the reservation in regard with the configuration.
// It returns Compliant if it contains active indices compatible with the configuration,
// NeedsActivation if the compatible index(es) exist but need to be activated, or
// NeedsIndices if no compatible index exists.
// The function expects a non-nil reservation.
func compliance(e *entry, until time.Time) Compliance {
	idxs := e.rsv.Indices.Filter(
		segment.ByMinBW(e.conf.minBW),
		segment.ByMaxBW(e.conf.maxBW),
		segment.NotConfirmed(),
		segment.ByExpiration(until),
	)
	switch {
	case len(idxs) == 0:
		return NeedsIndices
	case len(idxs.Filter(segment.NotSwitchableFrom(e.rsv.ActiveIndex()))) == 0:
		return NeedsActivation
	default:
		return Compliant
	}
}

func parseInitial(conf *conf.Reservations) ([]*configuration, error) {
	if conf == nil {
		log.Info("COLIBRI not keeping any reservations")
		return nil, nil
	}
	log.Info("COLIBRI will keep reservations", "count", len(conf.Rsvs))
	initial := make([]*configuration, len(conf.Rsvs))
	for i, r := range conf.Rsvs {
		seq, err := pathpol.NewSequence(r.PathPredicate)
		if err != nil {
			return nil, err
		}

		if r.MinSize > r.MaxSize {
			return nil, serrors.New("min bw must be less or equal than max bw",
				"min_bw", r.MinSize, "max_bw", r.MaxSize)
		}

		initial[i] = &configuration{
			dst:       r.DstAS,
			pathType:  r.PathType,
			predicate: seq,
			minBW:     r.MinSize,
			maxBW:     r.MaxSize,
			splitCls:  r.SplitCls,
			endProps:  reservation.PathEndProps(r.EndProps),
		}
	}
	return initial, nil
}
<commit_msg>fix SetupReq.currentStep in renewal and indices after confirmation for keeper (#141)<commit_after>// Copyright 2021 ETH Zurich
//
// 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 reservationstore

import (
	"context"
	"fmt"
	"sync"
	"time"

	base "github.com/scionproto/scion/go/co/reservation"
	"github.com/scionproto/scion/go/co/reservation/conf"
	"github.com/scionproto/scion/go/co/reservation/segment"
	"github.com/scionproto/scion/go/lib/addr"
	"github.com/scionproto/scion/go/lib/colibri/reservation"
	"github.com/scionproto/scion/go/lib/log"
	"github.com/scionproto/scion/go/lib/pathpol"
	"github.com/scionproto/scion/go/lib/serrors"
	colpath "github.com/scionproto/scion/go/lib/slayers/path/colibri"
	"github.com/scionproto/scion/go/lib/snet"
)

// sleepAtLeast is the time duration that the keeper will sleep at a minimum, even
// if it's called very frequently.
const sleepAtLeast = 4 * time.Second

const sleepAtMost = 5 * time.Minute

// min validity in the future for the reservations when checking their compliance,
// the bigger the value, the more probable it is not to break continuity.
// Typically this value would be twice the max. sleep period, to ensure no index would
// expire while the keeper is sleeping.
const minDuration = 2 * sleepAtMost

// min validity of new indices/reservations. The bigger the value, the longer a single index
// can be used. Too big a value could produce errors in the admission for some ASes.
// This value would typically be equal to twice minDuration.
const newIndexMinDuration = 2 * minDuration

// ServiceFacilitator defines a minimal interface that has to be implemented to be
// usable by the keeper.
type ServiceFacilitator interface {
	PathsTo(ctx context.Context, dst addr.IA) ([]snet.Path, error)
	SetupRequest(ctx context.Context, req *segment.SetupReq) error
	ActivateRequest(
		context.Context,
		*base.Request,
		base.PathSteps,
		*colpath.ColibriPathMinimal,
		bool,
	) error
	GetReservationsAtSource(ctx context.Context) ([]*segment.Reservation, error)
	DeleteExpiredIndices(ctx context.Context) error
}

// keeper looks after the reservations configured in reservations.json
// It starts by cleaning up those reservations that have expired.
// The keeper tries to match existing reservations with configured entries.
// If no match is found, a new reservation will be created.
type keeper struct {
	now        func() time.Time
	localIA    addr.IA
	sleepUntil time.Time // nothing to do in the keeper until this time
	provider   ServiceFacilitator
	entries    []*entry
}

type entry struct {
	conf *configuration
	rsv  *segment.Reservation
}

// PrepareSetupRequest creates a valid setup request with the steps always in the direction of
// the traffic of the SegR, and the transport path always in the direction of the next
// colibri service (thus for down-path SegRs the transport will be in the reverse wrt the steps).
func (e *entry) PrepareSetupRequest(now, expTime time.Time, localAS addr.AS,
	p snet.Path) *segment.SetupReq {

	steps, err := base.StepsFromSnet(p)
	if err != nil {
		log.Info("error in SCION path, cannot convert to steps", "err", err, "path", p)
		panic(err)
	}
	currentStep := 0

	// if the SegR is of down-path type, reverse the steps
	if e.conf.pathType == reservation.DownPath {
		steps = steps.Reverse()
		currentStep = len(steps) - 1
	}

	id, _ := reservation.NewID(localAS, make([]byte, reservation.IDSuffixSegLen))
	return &segment.SetupReq{
		Request:        *base.NewRequest(now, id, 0, len(steps)),
		ExpirationTime: expTime,
		PathType:       e.conf.pathType,
		MinBW:          e.conf.minBW,
		MaxBW:          e.conf.maxBW,
		SplitCls:       e.conf.splitCls,
		PathProps:      e.conf.endProps,
		AllocTrail:     reservation.AllocationBeads{},
		Steps:          steps,
		CurrentStep:    currentStep,
		TransportPath:  nil, // new setups are not transported in colibri paths
	}
}

func (e *entry) PrepareRenewalRequest(now, expTime time.Time) *segment.SetupReq {
	return &segment.SetupReq{
		Request: *base.NewRequest(
			now, &e.rsv.ID, e.rsv.NextIndexToRenew(), len(e.rsv.Steps)),
		ExpirationTime: expTime,
		PathType:       e.conf.pathType,
		MinBW:          e.conf.minBW,
		MaxBW:          e.conf.maxBW,
		SplitCls:       e.rsv.TrafficSplit,
		PathProps:      e.rsv.PathEndProps,
		AllocTrail:     reservation.AllocationBeads{},
		Steps:          e.rsv.Steps.Copy(),
		CurrentStep:    e.rsv.CurrentStep,
		TransportPath:  e.rsv.TransportPath,
		Reservation:    e.rsv,
	}
}

func NewKeeper(
	ctx context.Context,
	provider ServiceFacilitator,
	conf *conf.Reservations,
	localIA addr.IA,
) (*keeper, error) {

	// load configuration
	reqs, err := parseInitial(conf)
	if err != nil {
		return nil, err
	}
	// cleanup expired indices before reading reservations
	if err := provider.DeleteExpiredIndices(ctx); err != nil {
		return nil, err
	}
	// get existing reservations
	rsvs, err := provider.GetReservationsAtSource(ctx)
	if err != nil {
		return nil, err
	}
	entries := matchRsvsWithConfiguration(rsvs, reqs)

	log.Debug("colibri keeper", "reservations", len(entries))
	return &keeper{
		now:        time.Now,
		localIA:    localIA,
		sleepUntil: time.Now().Add(-time.Nanosecond),
		provider:   provider,
		entries:    entries,
	}, nil
}

// OneShot keeps all reservations healthy. Those that need renewal are renewed, those
// that still have no reservation ID for its config will request a new one.
// The function returns the time when it should be called next.
func (k *keeper) OneShot(ctx context.Context) (time.Time, error) {
	wg := sync.WaitGroup{}
	times := make([]time.Time, len(k.entries))
	errs := make(serrors.List, len(k.entries))
	wg.Add(len(k.entries))
	for i, e := range k.entries {
		i, e := i, e
		go func() {
			defer log.HandlePanic()
			defer wg.Done()
			times[i], errs[i] = k.keepReservation(ctx, e)
		}()
	}
	wg.Wait()
	if err := errs.Coalesce(); err != nil {
		return k.now().Add(sleepAtLeast), err
	}
	// wakeupAtLatest is the maximum to wake up the keeper
	wakeupAtLatest := k.now().Add(sleepAtMost)
	for _, t := range times {
		if t.Before(wakeupAtLatest) {
			wakeupAtLatest = t
		}
	}
	// but the keeper must sleep at least a minimum amount of time
	if wakeupAtLatest.Sub(k.now()) < sleepAtLeast {
		wakeupAtLatest = k.now().Add(sleepAtLeast)
	}
	return wakeupAtLatest, nil
}

// keepReservation will ensure that the reservation exists or a request is created.
func (k *keeper) keepReservation(ctx context.Context, e *entry) (time.Time, error) {
	now := k.now()
	var err error
	if e.rsv == nil {
		e.rsv, err = k.askNewReservation(ctx, e)
		if err != nil {
			return time.Time{}, err
		}
	}

	switch compliance(e, k.now().Add(minDuration)) {
	case Compliant:
	case NeedsIndices:
		err = k.askNewIndices(ctx, e)
	case NeedsActivation:
		err = k.activateIndex(ctx, e)
	}

	if err != nil {
		return time.Time{}, err
	}
	return now.Add(newIndexMinDuration), nil
}

// matchRsvsWithConfiguration matches existing reservations with configuration.
// It returns the appropriate entries to manage from the keeper.
// Those entries without a reservation ID must obtain a new reservation;
// those with a reservation ID will need index activation, etc.
func matchRsvsWithConfiguration(rsvs []*segment.Reservation, conf []*configuration) []*entry {
	conf = append(conf[:0:0], conf...)
	// greedy strategy: for each reservation try to match it with the first compatible configuration
	entries := make([]*entry, 0)
	for _, r := range rsvs {
		i := findCompatibleConfiguration(r, conf)
		if i < 0 {
			continue
		}
		entries = append(entries, &entry{
			conf: conf[i],
			rsv:  r,
		})
		// one conf. is matched against this r; remove that entry from the pool
		conf = append(conf[:i], conf[i+1:]...)
	}
	for _, c := range conf {
		entries = append(entries, &entry{
			conf: c,
		})
	}
	return entries
}

// findCompatibleConfiguration finds the first compatible configuration with the reservation.
// It returns the index of the configuration in the slice, or -1 if no valid one is found.
func findCompatibleConfiguration(r *segment.Reservation, conf []*configuration) int {
	for i, c := range conf {
		switch {
		case r.Steps.DstIA() != c.dst:
			continue
		case r.PathType != c.pathType:
			continue
		case r.TrafficSplit != c.splitCls:
			continue
		case r.PathEndProps != c.endProps:
			continue
		case !c.predicate.EvalInterfaces(r.Steps.Interfaces()):
			continue
		}
		return i
	}
	return -1
}

func (k *keeper) activateIndex(ctx context.Context, e *entry) error {
	req := base.NewRequest(k.now(), &e.rsv.ID, e.rsv.NextIndexToActivate().Idx,
		len(e.rsv.Steps))
	inReverse := e.rsv.PathType == reservation.DownPath
	err := k.provider.ActivateRequest(ctx, req, e.rsv.Steps.Copy(), e.rsv.TransportPath, inReverse)
	if err == nil {
		err = e.rsv.SetIndexActive(req.Index)
	}
	return err
}

// askNewIndices requests a renewal
func (k *keeper) askNewIndices(ctx context.Context, e *entry) error {
	now := k.now()
	req := e.PrepareRenewalRequest(now, now.Add(newIndexMinDuration))
	err := k.provider.SetupRequest(ctx, req)
	if err != nil {
		return err
	}
	// otherwise the entry reservation is not updated with the new indices
	// TODO(JordiSubira): Check whether we are missing else from the updated reservation
	// after confirming indices.
	e.rsv.Indices = req.Reservation.Indices
	return nil
}

func (k *keeper) askNewReservation(ctx context.Context, e *entry) (*segment.Reservation, error) {
	now := k.now()
	paths, err := k.provider.PathsTo(ctx, e.conf.dst)
	if err != nil {
		return nil, err
	}
	// try with each possible path
	paths = e.conf.predicate.Eval(paths)
	for _, p := range paths {
		req := e.PrepareSetupRequest(now, now.Add(newIndexMinDuration), k.localIA.AS(), p)
		err := k.provider.SetupRequest(ctx, req)
		if err == nil {
			if req.Reservation == nil {
				panic("logic error, reservation after new request is empty")
			}
		}
		if req.Reservation != nil {
			return req.Reservation, err
		}
		log.Info("error creating new reservation from best effort path", "path", p, "err", err)
	}
	return nil, serrors.New("no more best effort paths to create reservation", "dst", e.conf.dst)
}

// configuration is a 1 to 1 association to a conf.ReservationEntry
type configuration struct {
	dst       addr.IA
	pathType  reservation.PathType
	predicate *pathpol.Sequence
	minBW     reservation.BWCls
	maxBW     reservation.BWCls
	splitCls  reservation.SplitCls
	endProps  reservation.PathEndProps
}

type Compliance int

const (
	NeedsIndices    = Compliance(iota) // ask for a new index
	NeedsActivation                    // ask to activate index
	Compliant                          // already has an active compliant index
)

func (c Compliance) String() string {
	switch c {
	case NeedsIndices:
		return "NeedsIndices"
	case NeedsActivation:
		return "NeedsActivation"
	case Compliant:
		return "Compliant"
	default:
		panic(fmt.Errorf("unknown value for compliance %d", c))
	}
}

// compliance finds the status of the reservation in regard with the configuration.
// It returns Compliant if it contains active indices compatible with the configuration,
// NeedsActivation if the compatible index(es) exist but need to be activated, or
// NeedsIndices if no compatible index exists.
// The function expects a non-nil reservation.
func compliance(e *entry, until time.Time) Compliance {
	idxs := e.rsv.Indices.Filter(
		segment.ByMinBW(e.conf.minBW),
		segment.ByMaxBW(e.conf.maxBW),
		segment.NotConfirmed(),
		segment.ByExpiration(until),
	)
	switch {
	case len(idxs) == 0:
		return NeedsIndices
	case len(idxs.Filter(segment.NotSwitchableFrom(e.rsv.ActiveIndex()))) == 0:
		return NeedsActivation
	default:
		return Compliant
	}
}

func parseInitial(conf *conf.Reservations) ([]*configuration, error) {
	if conf == nil {
		log.Info("COLIBRI not keeping any reservations")
		return nil, nil
	}
	log.Info("COLIBRI will keep reservations", "count", len(conf.Rsvs))
	initial := make([]*configuration, len(conf.Rsvs))
	for i, r := range conf.Rsvs {
		seq, err := pathpol.NewSequence(r.PathPredicate)
		if err != nil {
			return nil, err
		}

		if r.MinSize > r.MaxSize {
			return nil, serrors.New("min bw must be less or equal than max bw",
				"min_bw", r.MinSize, "max_bw", r.MaxSize)
		}

		initial[i] = &configuration{
			dst:       r.DstAS,
			pathType:  r.PathType,
			predicate: seq,
			minBW:     r.MinSize,
			maxBW:     r.MaxSize,
			splitCls:  r.SplitCls,
			endProps:  reservation.PathEndProps(r.EndProps),
		}
	}
	return initial, nil
}
<|endoftext|>