<commit_before>package sconsify

import (
	"errors"
	"math/rand"
	"sort"
)

type Playlists struct {
	playlists         map[string]*Playlist
	currentIndexTrack int
	currentPlaylist   string
	playMode          int
	premadeTracks     []*Track
}

const (
	NormalMode     = iota
	RandomMode     = iota
	AllRandomMode  = iota
	SequentialMode = iota
)

func InitPlaylists() *Playlists {
	playlists := &Playlists{
		playlists: make(map[string]*Playlist),
		playMode:  NormalMode,
	}
	return playlists
}

func (playlists *Playlists) Get(name string) *Playlist {
	return playlists.playlists[name]
}

func (playlists *Playlists) Playlists() int {
	return len(playlists.playlists)
}

func (playlists *Playlists) AddPlaylist(name string, playlist *Playlist) {
	playlists.playlists[name] = playlist
}

func (playlists *Playlists) Merge(newPlaylist *Playlists) {
	for key, value := range newPlaylist.playlists {
		playlists.playlists[key] = value
	}
}

func (playlists *Playlists) GetNames() []string {
	names := make([]string, playlists.Playlists())
	i := 0
	for name, _ := range playlists.playlists {
		names[i] = name
		i++
	}
	return names
}

func (playlists *Playlists) Tracks() int {
	numberOfTracks := 0
	for _, playlist := range playlists.playlists {
		numberOfTracks += playlist.Tracks()
	}
	return numberOfTracks
}

func (playlists *Playlists) PremadeTracks() int {
	return len(playlists.premadeTracks)
}

func (playlists *Playlists) buildPlaylistForNewMode() error {
	if playlists.isNormalMode() {
		playlists.premadeTracks = nil
		return nil
	}

	var numberOfTracks int
	var playlist *Playlist
	if playlists.isRandomMode() {
		playlist = playlists.Get(playlists.currentPlaylist)
		numberOfTracks = playlist.Tracks()
	} else {
		// all random and sequential
		numberOfTracks = playlists.Tracks()
	}

	if numberOfTracks == 0 {
		return errors.New("No tracks selected")
	}

	playlists.premadeTracks = make([]*Track, numberOfTracks)
	if playlists.isRandomMode() {
		playlists.buildRandomModeTracks(playlist, numberOfTracks)
	} else if playlists.isAllRandomMode() {
		playlists.buildAllRandomModeTracks(numberOfTracks)
	} else {
		// sequential
		playlists.buildSequentialModeTracks()
	}

	playlists.currentIndexTrack = -1

	return nil
}

func (playlists *Playlists) buildRandomModeTracks(playlist *Playlist, numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for i := 0; i < playlist.Tracks(); i++ {
		playlists.premadeTracks[perm[index]] = playlist.Track(i)
		index++
	}
}

func (playlists *Playlists) buildAllRandomModeTracks(numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for _, playlist := range playlists.playlists {
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[perm[index]] = playlist.Track(i)
			index++
		}
	}
}

func (playlists *Playlists) buildSequentialModeTracks() {
	names := playlists.GetNames()
	sort.Strings(names)

	index := 0
	for _, name := range names {
		playlist := playlists.playlists[name]
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[index] = playlist.Track(i)
			index++
		}
	}
}

func getRandomPermutation(numberOfTracks int) []int {
	return rand.Perm(numberOfTracks)
}

func (playlists *Playlists) GetRandomNextPlaylistAndTrack() (string, int) {
	index := rand.Intn(playlists.Playlists())
	count := 0
	var playlist *Playlist
	var newPlaylistName string
	for key, value := range playlists.playlists {
		if index == count {
			newPlaylistName = key
			playlist = value
			break
		}
		count++
	}
	return newPlaylistName, playlist.GetRandomNextTrack()
}

func (playlists *Playlists) GetModeAsString() string {
	if playlists.playMode == RandomMode {
		return "[Random] "
	}
	if playlists.playMode == AllRandomMode {
		return "[All Random] "
	}
	return ""
}

func (playlists *Playlists) SetCurrents(currentPlaylist string, currentIndexTrack int) {
	playlists.currentPlaylist = currentPlaylist
	playlists.currentIndexTrack = currentIndexTrack
}

func (playlists *Playlists) GetNext() (*Track, bool) {
	repeating := false
	if playlists.premadeTracks != nil {
		playlists.currentIndexTrack++
		if playlists.currentIndexTrack >= len(playlists.premadeTracks) {
			playlists.currentIndexTrack = 0
			repeating = true
		}
		return playlists.premadeTracks[playlists.currentIndexTrack], repeating
	}

	playlist := playlists.Get(playlists.currentPlaylist)
	playlists.currentIndexTrack = playlist.GetNextTrack(playlists.currentIndexTrack)

	return playlist.Track(playlists.currentIndexTrack), repeating
}

func (playlists *Playlists) SetMode(mode int) {
	playlists.playMode = mode
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) InvertMode(mode int) int {
	if mode == playlists.playMode {
		playlists.SetMode(NormalMode)
	} else {
		playlists.SetMode(mode)
	}
	return playlists.playMode
}

func (playlists *Playlists) HasPlaylistSelected() bool {
	return playlists.currentPlaylist != ""
}

func (playlists *Playlists) isAllRandomMode() bool {
	return playlists.playMode == AllRandomMode
}

func (playlists *Playlists) isRandomMode() bool {
	return playlists.playMode == RandomMode
}

func (playlists *Playlists) isNormalMode() bool {
	return playlists.playMode == NormalMode
}
<commit_msg>Remove unused method<commit_after>package sconsify

import (
	"errors"
	"math/rand"
	"sort"
)

type Playlists struct {
	playlists         map[string]*Playlist
	currentIndexTrack int
	currentPlaylist   string
	playMode          int
	premadeTracks     []*Track
}

const (
	NormalMode     = iota
	RandomMode     = iota
	AllRandomMode  = iota
	SequentialMode = iota
)

func InitPlaylists() *Playlists {
	playlists := &Playlists{
		playlists: make(map[string]*Playlist),
		playMode:  NormalMode,
	}
	return playlists
}

func (playlists *Playlists) Get(name string) *Playlist {
	return playlists.playlists[name]
}

func (playlists *Playlists) Playlists() int {
	return len(playlists.playlists)
}

func (playlists *Playlists) AddPlaylist(name string, playlist *Playlist) {
	playlists.playlists[name] = playlist
}

func (playlists *Playlists) Merge(newPlaylist *Playlists) {
	for key, value := range newPlaylist.playlists {
		playlists.playlists[key] = value
	}
}

func (playlists *Playlists) GetNames() []string {
	names := make([]string, playlists.Playlists())
	i := 0
	for name, _ := range playlists.playlists {
		names[i] = name
		i++
	}
	return names
}

func (playlists *Playlists) Tracks() int {
	numberOfTracks := 0
	for _, playlist := range playlists.playlists {
		numberOfTracks += playlist.Tracks()
	}
	return numberOfTracks
}

func (playlists *Playlists) PremadeTracks() int {
	return len(playlists.premadeTracks)
}

func (playlists *Playlists) buildPlaylistForNewMode() error {
	if playlists.isNormalMode() {
		playlists.premadeTracks = nil
		return nil
	}

	var numberOfTracks int
	var playlist *Playlist
	if playlists.isRandomMode() {
		playlist = playlists.Get(playlists.currentPlaylist)
		numberOfTracks = playlist.Tracks()
	} else {
		// all random and sequential
		numberOfTracks = playlists.Tracks()
	}

	if numberOfTracks == 0 {
		return errors.New("No tracks selected")
	}

	playlists.premadeTracks = make([]*Track, numberOfTracks)
	if playlists.isRandomMode() {
		playlists.buildRandomModeTracks(playlist, numberOfTracks)
	} else if playlists.isAllRandomMode() {
		playlists.buildAllRandomModeTracks(numberOfTracks)
	} else {
		// sequential
		playlists.buildSequentialModeTracks()
	}

	playlists.currentIndexTrack = -1

	return nil
}

func (playlists *Playlists) buildRandomModeTracks(playlist *Playlist, numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for i := 0; i < playlist.Tracks(); i++ {
		playlists.premadeTracks[perm[index]] = playlist.Track(i)
		index++
	}
}

func (playlists *Playlists) buildAllRandomModeTracks(numberOfTracks int) {
	perm := getRandomPermutation(numberOfTracks)

	index := 0
	for _, playlist := range playlists.playlists {
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[perm[index]] = playlist.Track(i)
			index++
		}
	}
}

func (playlists *Playlists) buildSequentialModeTracks() {
	names := playlists.GetNames()
	sort.Strings(names)

	index := 0
	for _, name := range names {
		playlist := playlists.playlists[name]
		for i := 0; i < playlist.Tracks(); i++ {
			playlists.premadeTracks[index] = playlist.Track(i)
			index++
		}
	}
}

func getRandomPermutation(numberOfTracks int) []int {
	return rand.Perm(numberOfTracks)
}

func (playlists *Playlists) GetModeAsString() string {
	if playlists.playMode == RandomMode {
		return "[Random] "
	}
	if playlists.playMode == AllRandomMode {
		return "[All Random] "
	}
	return ""
}

func (playlists *Playlists) SetCurrents(currentPlaylist string, currentIndexTrack int) {
	playlists.currentPlaylist = currentPlaylist
	playlists.currentIndexTrack = currentIndexTrack
}

func (playlists *Playlists) GetNext() (*Track, bool) {
	repeating := false
	if playlists.premadeTracks != nil {
		playlists.currentIndexTrack++
		if playlists.currentIndexTrack >= len(playlists.premadeTracks) {
			playlists.currentIndexTrack = 0
			repeating = true
		}
		return playlists.premadeTracks[playlists.currentIndexTrack], repeating
	}

	playlist := playlists.Get(playlists.currentPlaylist)
	playlists.currentIndexTrack = playlist.GetNextTrack(playlists.currentIndexTrack)

	return playlist.Track(playlists.currentIndexTrack), repeating
}

func (playlists *Playlists) SetMode(mode int) {
	playlists.playMode = mode
	playlists.buildPlaylistForNewMode()
}

func (playlists *Playlists) InvertMode(mode int) int {
	if mode == playlists.playMode {
		playlists.SetMode(NormalMode)
	} else {
		playlists.SetMode(mode)
	}
	return playlists.playMode
}

func (playlists *Playlists) HasPlaylistSelected() bool {
	return playlists.currentPlaylist != ""
}

func (playlists *Playlists) isAllRandomMode() bool {
	return playlists.playMode == AllRandomMode
}

func (playlists *Playlists) isRandomMode() bool {
	return playlists.playMode == RandomMode
}

func (playlists *Playlists) isNormalMode() bool {
	return playlists.playMode == NormalMode
}
<|endoftext|>


<commit_before>package net

import (
	"log"

	"github.com/ghthor/filu/auth"
)

type AuthenticatedUser struct {
	Username string
}

func AuthenticateFrom(conn Conn, stream auth.Stream) (AuthenticatedUser, error) {
readNextType:
	eType, err := conn.NextType()
	if err != nil {
		return AuthenticatedUser{}, err
	}

	switch eType {
	default:
		log.Println("unexpected encoded type: ", eType)
		// TODO: Notify client their protocol usage is incorrect
		// TODO: Log error to universal error log
		goto readNextType

	case ET_USER_LOGIN_REQUEST:
	}

	var r UserLoginRequest
	err = conn.Decode(&r)
	if err != nil {
		return AuthenticatedUser{}, err
	}

	authReq := auth.NewRequest(r.Name, r.Password)
	stream.RequestAuthentication() <- authReq

	select {
	case user := <-authReq.CreatedUser:
		err = conn.Encode(UserCreateSuccess{user.Username})
		if err != nil {
			return AuthenticatedUser{}, err
		}

		return AuthenticatedUser{user.Username}, nil

	case user := <-authReq.AuthenticatedUser:
		err = conn.Encode(UserLoginSuccess{user.Username})
		if err != nil {
			return AuthenticatedUser{}, err
		}

		return AuthenticatedUser{user.Username}, nil

	case invalid := <-authReq.InvalidPassword:
		err := conn.Encode(UserLoginFailure{invalid.Username})
		if err != nil {
			return AuthenticatedUser{}, err
		}
	}

	goto readNextType
}
<commit_msg>[filu/net] Add ProtocolError reply during login loop for invalid requests<commit_after>package net

import (
	"fmt"
	"log"

	"github.com/ghthor/filu/auth"
)

type AuthenticatedUser struct {
	Username string
}

func AuthenticateFrom(conn Conn, stream auth.Stream) (AuthenticatedUser, error) {
readNextType:
	eType, err := conn.NextType()
	if err != nil {
		return AuthenticatedUser{}, err
	}

	switch eType {
	default:
		// TODO: Log error to universal error log
		log.Println("unexpected EncodeType:", eType)

		err := conn.Encode(ProtocolError(
			fmt.Sprintf("expected EncodeType(%v) got EncodeType(%v)", ET_USER_LOGIN_REQUEST, eType),
		))
		if err != nil {
			return AuthenticatedUser{}, err
		}

		goto readNextType

	case ET_USER_LOGIN_REQUEST:
	}

	var r UserLoginRequest
	err = conn.Decode(&r)
	if err != nil {
		return AuthenticatedUser{}, err
	}

	authReq := auth.NewRequest(r.Name, r.Password)
	stream.RequestAuthentication() <- authReq

	select {
	case user := <-authReq.CreatedUser:
		err = conn.Encode(UserCreateSuccess{user.Username})
		if err != nil {
			return AuthenticatedUser{}, err
		}

		return AuthenticatedUser{user.Username}, nil

	case user := <-authReq.AuthenticatedUser:
		err = conn.Encode(UserLoginSuccess{user.Username})
		if err != nil {
			return AuthenticatedUser{}, err
		}

		return AuthenticatedUser{user.Username}, nil

	case invalid := <-authReq.InvalidPassword:
		err := conn.Encode(UserLoginFailure{invalid.Username})
		if err != nil {
			return AuthenticatedUser{}, err
		}
	}

	goto readNextType
}
<|endoftext|>


<commit_before>package coinparam

import (
	"time"

	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
	"github.com/mit-dci/lit/wire"
)

// DummyUsdNetParams for DummyUsd fork defines the network parameters for the DummyUsd network.
var DummyUsdNetParams = Params{
	Name:          "dollar",
	NetMagicBytes: 0x44535564,
	DefaultPort:   "26999",
	DNSSeeds:      []string{},

	// Chain parameters
	GenesisBlock: &DummyUsdGenesisBlock,
	GenesisHash:  &DummyUsdGenesisHash,
	PoWFunction: func(b []byte, height int32) chainhash.Hash {
		return chainhash.DoubleHashH(b)
	},
	DiffCalcFunction: diffBitcoin,
	//	func(r io.ReadSeeker, height, startheight int32, p *Params) (uint32, error) {
	//		return diffBTC(r, height, startheight, p, false)
	//	},
	FeePerByte:               80,
	PowLimit:                 regressionPowLimit,
	PowLimitBits:             0x207fffff,
	CoinbaseMaturity:         100,
	SubsidyReductionInterval: 150,
	TargetTimespan:           time.Hour * 24 * 14, // 14 days
	TargetTimePerBlock:       time.Minute * 10,    // 10 minutes
	RetargetAdjustmentFactor: 4,                   // 25% less, 400% more
	ReduceMinDifficulty:      true,
	MinDiffReductionTime:     time.Minute * 20, // TargetTimePerBlock * 2
	GenerateSupported:        true,

	// Checkpoints ordered from oldest to newest.
	Checkpoints: nil,

	// Enforce current block version once majority of the network has
	// upgraded.
	// 75% (750 / 1000)
	// Reject previous block versions once a majority of the network has
	// upgraded.
	// 95% (950 / 1000)
	BlockEnforceNumRequired: 750,
	BlockRejectNumRequired:  950,
	BlockUpgradeNumToCheck:  1000,

	// Mempool parameters
	RelayNonStdTxs: true,

	// Address encoding magics
	PubKeyHashAddrID: 0x1e, // starts with D
	ScriptHashAddrID: 0x5a, // starts with d
	PrivateKeyID:     0x83, // starts with u
	Bech32Prefix:     "dusd",

	// BIP32 hierarchical deterministic extended key magics
	HDPrivateKeyID: [4]byte{0x04, 0xA5, 0xB3, 0xF4}, // starts with tprv
	HDPublicKeyID:  [4]byte{0x04, 0xA5, 0xB7, 0x8F}, // starts with tpub

	// BIP44 coin type used in the hierarchical deterministic path for
	// address generation.
	HDCoinType: 262,
	TestCoin:   true,
}

var DummyUsdGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
	0xd8, 0x69, 0x52, 0xad, 0x56, 0xac, 0xda, 0xe2,
	0x32, 0x33, 0xa9, 0xb3, 0x24, 0x66, 0x27, 0x89,
	0xf7, 0x39, 0x60, 0xa3, 0x7a, 0x17, 0xae, 0xf7,
	0x69, 0x2e, 0xf0, 0x1b, 0x6d, 0x33, 0x4d, 0x19,
})

var DummyUsdGenesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
	0x7e, 0xb6, 0x28, 0xda, 0xa0, 0xd2, 0xef, 0x48,
	0x1f, 0x45, 0x9f, 0x5b, 0x35, 0x47, 0x18, 0x41,
	0xb6, 0x69, 0x20, 0xdc, 0x12, 0xa3, 0xbf, 0x94,
	0x92, 0xca, 0xfe, 0xfc, 0x4e, 0xab, 0xb0, 0x72,
})

var DummyUsdGenesisCoinbaseTx = wire.MsgTx{
	Version: 1,
	TxIn: []*wire.TxIn{
		{
			PreviousOutPoint: wire.OutPoint{
				Hash:  chainhash.Hash{},
				Index: 0xffffffff,
			},
			SignatureScript: []byte{
				0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x41,
				0x49, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72,
				0x20, 0x69, 0x66, 0x20, 0x70, 0x65, 0x6f, 0x70,
				0x6c, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20,
				0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x20, 0x74,
				0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6a,
				0x75, 0x73, 0x74, 0x20, 0x62, 0x69, 0x74, 0x63,
				0x6f, 0x69, 0x6e, 0x20, 0x77, 0x69, 0x74, 0x68,
				0x20, 0x61, 0x20, 0x66, 0x6c, 0x61, 0x76, 0x6f,
				0x72,
			},
			Sequence: 0xffffffff,
		},
	},
	TxOut: []*wire.TxOut{
		{
			Value: 0x12a05f200,
			PkScript: []byte{
				0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */
				0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */
				0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */
				0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */
				0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */
				0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */
				0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */
				0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */
				0x1d, 0x5f, 0xac, /* |._.| */
			},
		},
	},
	LockTime: 0,
}

// regTestGenesisBlock defines the genesis block of the block chain which serves
// as the public transaction ledger for the regression test network.
var DummyUsdGenesisBlock = wire.MsgBlock{
	Header: wire.BlockHeader{
		Version:    1,
		PrevBlock:  chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
		MerkleRoot: DummyUsdGenesisMerkleRoot,
		Timestamp:  time.Unix(1537252543, 0),
		Bits:       0x207fffff,
		Nonce:      0,
	},
	Transactions: []*wire.MsgTx{&DummyUsdGenesisCoinbaseTx},
}
<commit_msg>Fix diff adjustment for dummy USD regtest<commit_after>package coinparam

import (
	"time"

	"github.com/mit-dci/lit/btcutil/chaincfg/chainhash"
	"github.com/mit-dci/lit/wire"
)

// DummyUsdNetParams for DummyUsd fork defines the network parameters for the DummyUsd network.
var DummyUsdNetParams = Params{
	Name:          "dollar",
	NetMagicBytes: 0x44535564,
	DefaultPort:   "26999",
	DNSSeeds:      []string{},

	// Chain parameters
	GenesisBlock: &DummyUsdGenesisBlock,
	GenesisHash:  &DummyUsdGenesisHash,
	PoWFunction: func(b []byte, height int32) chainhash.Hash {
		return chainhash.DoubleHashH(b)
	},
	DiffCalcFunction: func(headers []*wire.BlockHeader, height int32, p *Params) (uint32, error) {
		return p.PowLimitBits, nil
	},
	FeePerByte:               80,
	PowLimit:                 regressionPowLimit,
	PowLimitBits:             0x207fffff,
	CoinbaseMaturity:         100,
	SubsidyReductionInterval: 150,
	TargetTimespan:           time.Hour * 24 * 14, // 14 days
	TargetTimePerBlock:       time.Minute * 10,    // 10 minutes
	RetargetAdjustmentFactor: 4,                   // 25% less, 400% more
	ReduceMinDifficulty:      true,
	MinDiffReductionTime:     time.Minute * 20, // TargetTimePerBlock * 2
	GenerateSupported:        true,

	// Checkpoints ordered from oldest to newest.
	Checkpoints: nil,

	// Enforce current block version once majority of the network has
	// upgraded.
	// 75% (750 / 1000)
	// Reject previous block versions once a majority of the network has
	// upgraded.
	// 95% (950 / 1000)
	BlockEnforceNumRequired: 750,
	BlockRejectNumRequired:  950,
	BlockUpgradeNumToCheck:  1000,

	// Mempool parameters
	RelayNonStdTxs: true,

	// Address encoding magics
	PubKeyHashAddrID: 0x1e, // starts with D
	ScriptHashAddrID: 0x5a, // starts with d
	PrivateKeyID:     0x83, // starts with u
	Bech32Prefix:     "dusd",

	// BIP32 hierarchical deterministic extended key magics
	HDPrivateKeyID: [4]byte{0x04, 0xA5, 0xB3, 0xF4}, // starts with tprv
	HDPublicKeyID:  [4]byte{0x04, 0xA5, 0xB7, 0x8F}, // starts with tpub

	// BIP44 coin type used in the hierarchical deterministic path for
	// address generation.
	HDCoinType: 262,
	TestCoin:   true,
}

var DummyUsdGenesisHash = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
	0xd8, 0x69, 0x52, 0xad, 0x56, 0xac, 0xda, 0xe2,
	0x32, 0x33, 0xa9, 0xb3, 0x24, 0x66, 0x27, 0x89,
	0xf7, 0x39, 0x60, 0xa3, 0x7a, 0x17, 0xae, 0xf7,
	0x69, 0x2e, 0xf0, 0x1b, 0x6d, 0x33, 0x4d, 0x19,
})

var DummyUsdGenesisMerkleRoot = chainhash.Hash([chainhash.HashSize]byte{ // Make go vet happy.
	0x7e, 0xb6, 0x28, 0xda, 0xa0, 0xd2, 0xef, 0x48,
	0x1f, 0x45, 0x9f, 0x5b, 0x35, 0x47, 0x18, 0x41,
	0xb6, 0x69, 0x20, 0xdc, 0x12, 0xa3, 0xbf, 0x94,
	0x92, 0xca, 0xfe, 0xfc, 0x4e, 0xab, 0xb0, 0x72,
})

var DummyUsdGenesisCoinbaseTx = wire.MsgTx{
	Version: 1,
	TxIn: []*wire.TxIn{
		{
			PreviousOutPoint: wire.OutPoint{
				Hash:  chainhash.Hash{},
				Index: 0xffffffff,
			},
			SignatureScript: []byte{
				0x04, 0xff, 0xff, 0x00, 0x1d, 0x01, 0x04, 0x41,
				0x49, 0x20, 0x77, 0x6f, 0x6e, 0x64, 0x65, 0x72,
				0x20, 0x69, 0x66, 0x20, 0x70, 0x65, 0x6f, 0x70,
				0x6c, 0x65, 0x20, 0x77, 0x69, 0x6c, 0x6c, 0x20,
				0x6e, 0x6f, 0x74, 0x69, 0x63, 0x65, 0x20, 0x74,
				0x68, 0x69, 0x73, 0x20, 0x69, 0x73, 0x20, 0x6a,
				0x75, 0x73, 0x74, 0x20, 0x62, 0x69, 0x74, 0x63,
				0x6f, 0x69, 0x6e, 0x20, 0x77, 0x69, 0x74, 0x68,
				0x20, 0x61, 0x20, 0x66, 0x6c, 0x61, 0x76, 0x6f,
				0x72,
			},
			Sequence: 0xffffffff,
		},
	},
	TxOut: []*wire.TxOut{
		{
			Value: 0x12a05f200,
			PkScript: []byte{
				0x41, 0x04, 0x67, 0x8a, 0xfd, 0xb0, 0xfe, 0x55, /* |A.g....U| */
				0x48, 0x27, 0x19, 0x67, 0xf1, 0xa6, 0x71, 0x30, /* |H'.g..q0| */
				0xb7, 0x10, 0x5c, 0xd6, 0xa8, 0x28, 0xe0, 0x39, /* |..\..(.9| */
				0x09, 0xa6, 0x79, 0x62, 0xe0, 0xea, 0x1f, 0x61, /* |..yb...a| */
				0xde, 0xb6, 0x49, 0xf6, 0xbc, 0x3f, 0x4c, 0xef, /* |..I..?L.| */
				0x38, 0xc4, 0xf3, 0x55, 0x04, 0xe5, 0x1e, 0xc1, /* |8..U....| */
				0x12, 0xde, 0x5c, 0x38, 0x4d, 0xf7, 0xba, 0x0b, /* |..\8M...| */
				0x8d, 0x57, 0x8a, 0x4c, 0x70, 0x2b, 0x6b, 0xf1, /* |.W.Lp+k.| */
				0x1d, 0x5f, 0xac, /* |._.| */
			},
		},
	},
	LockTime: 0,
}

// regTestGenesisBlock defines the genesis block of the block chain which serves
// as the public transaction ledger for the regression test network.
var DummyUsdGenesisBlock = wire.MsgBlock{
	Header: wire.BlockHeader{
		Version:    1,
		PrevBlock:  chainhash.Hash{}, // 0000000000000000000000000000000000000000000000000000000000000000
		MerkleRoot: DummyUsdGenesisMerkleRoot,
		Timestamp:  time.Unix(1537252543, 0),
		Bits:       0x207fffff,
		Nonce:      0,
	},
	Transactions: []*wire.MsgTx{&DummyUsdGenesisCoinbaseTx},
}
<|endoftext|>


<commit_before>package command

import (
	"os"
	"strings"
	"testing"

	"github.com/mitchellh/cli"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/terraform"
)

func TestTaint(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, statePath, testTaintStr)
}

func TestTaint_lockedState(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	unlock, err := testLockState("./testdata", statePath)
	if err != nil {
		t.Fatal(err)
	}
	defer unlock()
	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"test_instance.foo",
	}
	if code := c.Run(args); code == 0 {
		t.Fatal("expected error")
	}

	output := ui.ErrorWriter.String()
	if !strings.Contains(output, "lock") {
		t.Fatal("command output does not look like a lock error:", output)
	}
}

func TestTaint_backup(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, path+".backup", testTaintDefaultStr)
	testStateOutput(t, path, testTaintStr)
}

func TestTaint_backupDisable(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-backup", "-",
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if _, err := os.Stat(path + ".backup"); err == nil {
		t.Fatal("backup path should not exist")
	}

	testStateOutput(t, path, testTaintStr)
}

func TestTaint_badState(t *testing.T) {
	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", "i-should-not-exist-ever",
		"foo",
	}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}
}

func TestTaint_defaultState(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, path, testTaintStr)
}

func TestTaint_missing(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"test_instance.bar",
	}
	if code := c.Run(args); code == 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.OutputWriter.String())
	}
}

func TestTaint_missingAllow(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-allow-missing",
		"-state", statePath,
		"test_instance.bar",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}
}

func TestTaint_stateOut(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state-out", "foo",
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, path, testTaintDefaultStr)
	testStateOutput(t, "foo", testTaintStr)
}

func TestTaint_module(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "blah",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"blah"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-module=child",
		"-state", statePath,
		"test_instance.blah",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, statePath, testTaintModuleStr)
}

const testTaintStr = `
test_instance.foo: (tainted)
  ID = bar
`

const testTaintDefaultStr = `
test_instance.foo:
  ID = bar
`

const testTaintModuleStr = `
test_instance.foo:
  ID = bar

module.child:
  test_instance.blah: (tainted)
    ID = blah
`
<commit_msg>command: Fix all of the "taint" command tests<commit_after>package command

import (
	"os"
	"strings"
	"testing"

	"github.com/mitchellh/cli"

	"github.com/hashicorp/terraform/addrs"
	"github.com/hashicorp/terraform/states"
	"github.com/hashicorp/terraform/terraform"
)

func TestTaint(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, statePath, testTaintStr)
}

func TestTaint_lockedState(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	unlock, err := testLockState("./testdata", statePath)
	if err != nil {
		t.Fatal(err)
	}
	defer unlock()
	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"test_instance.foo",
	}
	if code := c.Run(args); code == 0 {
		t.Fatal("expected error")
	}

	output := ui.ErrorWriter.String()
	if !strings.Contains(output, "lock") {
		t.Fatal("command output does not look like a lock error:", output)
	}
}

func TestTaint_backup(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, path+".backup", testTaintDefaultStr)
	testStateOutput(t, path, testTaintStr)
}

func TestTaint_backupDisable(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-backup", "-",
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	if _, err := os.Stat(path + ".backup"); err == nil {
		t.Fatal("backup path should not exist")
	}

	testStateOutput(t, path, testTaintStr)
}

func TestTaint_badState(t *testing.T) {
	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", "i-should-not-exist-ever",
		"foo",
	}
	if code := c.Run(args); code != 1 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}
}

func TestTaint_defaultState(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, path, testTaintStr)
}

func TestTaint_missing(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"test_instance.bar",
	}
	if code := c.Run(args); code == 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.OutputWriter.String())
	}
}

func TestTaint_missingAllow(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-allow-missing",
		"-state", statePath,
		"test_instance.bar",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}
}

func TestTaint_stateOut(t *testing.T) {
	// Get a temp cwd
	tmp, cwd := testCwd(t)
	defer testFixCwd(t, tmp, cwd)

	// Write the temp state
	state := &terraform.State{
		Modules: []*terraform.ModuleState{
			&terraform.ModuleState{
				Path: []string{"root"},
				Resources: map[string]*terraform.ResourceState{
					"test_instance.foo": &terraform.ResourceState{
						Type: "test_instance",
						Primary: &terraform.InstanceState{
							ID: "bar",
						},
					},
				},
			},
		},
	}
	path := testStateFileDefault(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state-out", "foo",
		"test_instance.foo",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, path, testTaintDefaultStr)
	testStateOutput(t, "foo", testTaintStr)
}

func TestTaint_module(t *testing.T) {
	state := states.BuildState(func(s *states.SyncState) {
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "foo",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"bar"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
		s.SetResourceInstanceCurrent(
			addrs.Resource{
				Mode: addrs.ManagedResourceMode,
				Type: "test_instance",
				Name: "blah",
			}.Instance(addrs.NoKey).Absolute(addrs.RootModuleInstance.Child("child", addrs.NoKey)),
			&states.ResourceInstanceObjectSrc{
				AttrsJSON: []byte(`{"id":"blah"}`),
				Status:    states.ObjectReady,
			},
			addrs.ProviderConfig{Type: "test"}.Absolute(addrs.RootModuleInstance),
		)
	})
	statePath := testStateFile(t, state)

	ui := new(cli.MockUi)
	c := &TaintCommand{
		Meta: Meta{
			Ui: ui,
		},
	}

	args := []string{
		"-state", statePath,
		"module.child.test_instance.blah",
	}
	if code := c.Run(args); code != 0 {
		t.Fatalf("bad: %d\n\n%s", code, ui.ErrorWriter.String())
	}

	testStateOutput(t, statePath, testTaintModuleStr)
}

const testTaintStr = `
test_instance.foo: (tainted)
  ID = bar
  provider = provider.test
`

const testTaintDefaultStr = `
test_instance.foo:
  ID = bar
  provider = provider.test
`

const testTaintModuleStr = `
test_instance.foo:
  ID = bar
  provider = provider.test

module.child:
  test_instance.blah: (tainted)
    ID = blah
    provider = provider.test
`
<|endoftext|>


<commit_before>package execCmd

import (
	"os/exec"
	"fmt"
	_ "log"
	"strings"
	"ircflu/auth"
	"ircflu/commands"
	"ircflu/msgsystem"
)

type ExecCommand struct {
	messagesIn chan msgsystem.Message
	messagesOut chan msgsystem.Message
}

func (h *ExecCommand) Name() string {
	return "exec"
}

func (h *ExecCommand) MessageInChan() chan msgsystem.Message {
	return h.messagesIn
}

func (h *ExecCommand) SetMessageInChan(channel chan msgsystem.Message) {
	h.messagesIn = channel
}

func (h *ExecCommand) MessageOutChan() chan msgsystem.Message {
	return h.messagesOut
}

func (h *ExecCommand) SetMessageOutChan(channel chan msgsystem.Message) {
	h.messagesOut = channel
}

func (h *ExecCommand) Parse(msg msgsystem.Message) bool {
	channel := msg.To
	m := strings.Split(msg.Msg, " ")
	cmd := m[0]
	params := strings.TrimSpace(strings.Join(m[1:], " "))

	switch cmd {
		case "!exec":
			if !auth.IsAuthed(msg.Source) || strings.Index(params, "rm ") >= 0 || strings.Index(params, "mv ") >= 0 {
				r := msgsystem.Message{
					To: channel,
					Msg: "Security breach. Talk to ircflu admin!",
				}
				h.messagesOut <- r
				return true
			}

			if len(params) > 0 {
				fmt.Println("Executing:", params)

				c := strings.Split(params, " ")
				e := exec.Command(c[0], c[1:]...)
				out, err := e.CombinedOutput()
				fmt.Println("Output:", string(out))
				fmt.Println("Error:", err)

				r := msgsystem.Message{
					To: []string{msg.Source},
				}
				if err != nil {
					r.Msg = "Command '" + params + "' failed: " + err.Error()
				} else {
					r.Msg = "Command '" + params + "' succeeded!"
				}

				h.messagesOut <- r
			} else {
				r := msgsystem.Message{
					To: channel,
					Msg: "Usage: !exec [command]",
				}
				h.messagesOut <- r
			}

			return true
	}

	return false
}

func init() {
	execCmd := ExecCommand{}
	commands.RegisterCommand(&execCmd)
}
<commit_msg>* Return a response before executing command.<commit_after>package execCmd

import (
	"os/exec"
	"fmt"
	_ "log"
	"strings"
	"ircflu/auth"
	"ircflu/commands"
	"ircflu/msgsystem"
)

type ExecCommand struct {
	messagesIn chan msgsystem.Message
	messagesOut chan msgsystem.Message
}

func (h *ExecCommand) Name() string {
	return "exec"
}

func (h *ExecCommand) MessageInChan() chan msgsystem.Message {
	return h.messagesIn
}

func (h *ExecCommand) SetMessageInChan(channel chan msgsystem.Message) {
	h.messagesIn = channel
}

func (h *ExecCommand) MessageOutChan() chan msgsystem.Message {
	return h.messagesOut
}

func (h *ExecCommand) SetMessageOutChan(channel chan msgsystem.Message) {
	h.messagesOut = channel
}

func (h *ExecCommand) Parse(msg msgsystem.Message) bool {
	channel := msg.To
	m := strings.Split(msg.Msg, " ")
	cmd := m[0]
	params := strings.TrimSpace(strings.Join(m[1:], " "))

	switch cmd {
		case "!exec":
			if !auth.IsAuthed(msg.Source) || strings.Index(params, "rm ") >= 0 || strings.Index(params, "mv ") >= 0 {
				r := msgsystem.Message{
					To: channel,
					Msg: "Security breach. Talk to ircflu admin!",
				}
				h.messagesOut <- r
				return true
			}

			if len(params) > 0 {
				r := msgsystem.Message{
					To: channel,
					Msg: "Executing command!",
				}
				h.messagesOut <- r

				c := strings.Split(params, " ")
				e := exec.Command(c[0], c[1:]...)
				out, err := e.CombinedOutput()
				fmt.Println("Output:", string(out))
				fmt.Println("Error:", err)

				r = msgsystem.Message{
					To: []string{msg.Source},
				}
				if err != nil {
					r.Msg = "Command '" + params + "' failed: " + err.Error()
				} else {
					r.Msg = "Command '" + params + "' succeeded!"
				}

				h.messagesOut <- r
			} else {
				r := msgsystem.Message{
					To: channel,
					Msg: "Usage: !exec [command]",
				}
				h.messagesOut <- r
			}

			return true
	}

	return false
}

func init() {
	execCmd := ExecCommand{}
	commands.RegisterCommand(&execCmd)
}
<|endoftext|>


<commit_before>package gitManip

import (
	"fmt"

	"github.com/fatih/color"

	"bytes"

	"github.com/k0pernicus/goyave/traces"
	git "gopkg.in/libgit2/git2go.v26"
)

/*Map to match the RepositoryState enum type with a string
 */
var repositoryStateToString = map[git.RepositoryState]string{
	git.RepositoryStateNone:                 "None",
	git.RepositoryStateMerge:                "Merge",
	git.RepositoryStateRevert:               "Revert",
	git.RepositoryStateCherrypick:           "Cherrypick",
	git.RepositoryStateBisect:               "Bisect",
	git.RepositoryStateRebase:               "Rebase",
	git.RepositoryStateRebaseInteractive:    "Rebase Interactive",
	git.RepositoryStateRebaseMerge:          "Rebase Merge",
	git.RepositoryStateApplyMailbox:         "Apply Mailbox",
	git.RepositoryStateApplyMailboxOrRebase: "Apply Mailbox or Rebase",
}

/*Global variable to set the StatusOption parameter, in order to list each file status
 */
var statusOption = git.StatusOptions{
	Show:     git.StatusShowIndexAndWorkdir,
	Flags:    git.StatusOptIncludeUntracked,
	Pathspec: []string{},
}

/*GitObject contains informations about the current git repository
 *
 *The structure is:
 *  accessible:
 *		Is the repository still exists in the hard drive?
 *	path:
 *		The path file.
 *	repository:
 *		The object repository.
 */
type GitObject struct {
	accessible error
	path       string
	repository git.Repository
}

/*New is a constructor for GitObject
 *
 * It neeeds:
 *	path:
 *		The path of the current repository.
 */
func New(path string) *GitObject {
	r, err := git.OpenRepository(path)
	return &GitObject{accessible: err, path: path, repository: *r}
}

/*Clone is cloning a given repository, from a public URL
 *
 * It needs:
 * path:
 *		The local path to clone the repository.
 *	URL:
 *		The remote URL to fetch the repository.
 */
func Clone(path, URL string) error {
	_, err := git.Clone(URL, path, &git.CloneOptions{})
	return err
}

/*GetRemoteURL returns the associated remote URL of a given local path repository
 *
 * It needs:
 *	path
 *		The local path of a git repository
 */
func GetRemoteURL(path string) string {
	r, err := git.OpenRepository(path)
	if err != nil {
		fmt.Println("The repository can't be opened")
		return ""
	}
	remoteCollection := r.Remotes
	originRemote, err := remoteCollection.Lookup("origin")
	if err != nil {
		traces.WarningTracer.Printf("can't lookup origin remote URL for %s", path)
		return ""
	}
	return originRemote.Url()
}

/*isAccesible returns the information that is the current git repository is existing or not.
 *This method returns a boolean value: true if the git repository is still accesible (still exists), or false if not.
 */
func (g *GitObject) isAccessible() bool {
	return g.accessible == nil
}

/*Status prints the current status of the repository, accessible via the structure path field.
 *This method works only if the repository is accessible.
 */
func (g *GitObject) Status() {
	if g.isAccessible() {
		if err := g.printChanges(); err != nil {
			color.RedString("Impossible to get stats from %s, due to error %s", g.path, err)
		}
	} else {
		color.RedString("Repository %s not found!", g.path)
	}
}

/*getDiffWithWT returns the difference between the working tree and the index, for the current git repository.
 *If there is an error processing the request, it returns an error.
 */
func (g *GitObject) getDiffWithWT() (*git.Diff, error) {
	// Get the index of the repository
	currentIndex, err := g.repository.Index()
	if err != nil {
		return nil, err
	}
	// Get the default diff options, and add it custom flags
	defaultDiffOptions, err := git.DefaultDiffOptions()
	if err != nil {
		return nil, err
	}
	defaultDiffOptions.Flags = defaultDiffOptions.Flags | git.DiffIncludeUntracked | git.DiffIncludeTypeChange
	// Check the difference between the working directory and the index
	diff, err := g.repository.DiffIndexToWorkdir(currentIndex, &defaultDiffOptions)
	if err != nil {
		return nil, err
	}
	return diff, nil
}

/*printChanges prints out all changes for the current git repository.
 *If there is an error processing the request, it returns this one.
 */
func (g *GitObject) printChanges() error {
	diff, err := g.getDiffWithWT()
	var buffer bytes.Buffer
	if err != nil {
		return err
	}
	numDeltas, err := diff.NumDeltas()
	if err != nil {
		return err
	}
	headDetached, err := g.repository.IsHeadDetached()
	if err != nil {
		return err
	}
	if headDetached {
		outputHead := fmt.Sprintf("%s", color.RedString("\t/!\\ The repository's HEAD is detached! /!\\\n"))
		buffer.WriteString(outputHead)
	}
	if numDeltas > 0 {
		buffer.WriteString(fmt.Sprintf("%s %s\t[%d modification(s)]\n", color.RedString("✘"), g.path, numDeltas))
		for i := 0; i < numDeltas; i++ {
			delta, _ := diff.GetDelta(i)
			currentStatus := delta.Status
			newFile := delta.NewFile.Path
			oldFile := delta.OldFile.Path
			switch currentStatus {
			case git.DeltaAdded:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been added!\n", color.MagentaString(newFile)))
			case git.DeltaDeleted:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been deleted!\n", color.MagentaString(newFile)))
			case git.DeltaModified:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been modified!\n", color.MagentaString(newFile)))
			case git.DeltaRenamed:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been renamed to %s!\n", color.MagentaString(oldFile), color.MagentaString(newFile)))
			case git.DeltaUntracked:
				buffer.WriteString(fmt.Sprintf("\t===> %s is untracked - please to add it or update the gitignore file!\n", color.MagentaString(newFile)))
			case git.DeltaTypeChange:
				buffer.WriteString(fmt.Sprintf("\t===> the type of %s has been changed from %d to %d!", color.MagentaString(newFile), delta.OldFile.Mode, delta.NewFile.Mode))
			}
		}
	} else {
		buffer.WriteString(fmt.Sprintf("%s %s\n", color.GreenString("✔"), g.path))
	}
	repository_head, err := g.repository.Head()
	if err == nil {
		repository_id := repository_head.Target()
		commits_ahead, commits_behind, err := g.repository.AheadBehind(repository_id, repository_id)
		fmt.Printf("%d commits ahead!\n", commits_ahead)
		fmt.Printf("%d commits behind!\n", commits_behind)
		if err != nil {
			buffer.WriteString(fmt.Sprintf("%s", color.RedString("\tAn error occured checking the ahead/behind commits...\n")))
		} else if commits_ahead != 0 {
			buffer.WriteString(fmt.Sprintf("\tYou need to push the last modifications!\n"))
		}
	}
	fmt.Print(buffer.String())
	return nil
}
<commit_msg>Add a function to check the number of commits ahead/behind<commit_after>package gitManip

import (
	"fmt"

	"github.com/fatih/color"

	"bytes"

	"github.com/k0pernicus/goyave/traces"
	git "gopkg.in/libgit2/git2go.v26"
)

/*Map to match the RepositoryState enum type with a string
 */
var repositoryStateToString = map[git.RepositoryState]string{
	git.RepositoryStateNone:                 "None",
	git.RepositoryStateMerge:                "Merge",
	git.RepositoryStateRevert:               "Revert",
	git.RepositoryStateCherrypick:           "Cherrypick",
	git.RepositoryStateBisect:               "Bisect",
	git.RepositoryStateRebase:               "Rebase",
	git.RepositoryStateRebaseInteractive:    "Rebase Interactive",
	git.RepositoryStateRebaseMerge:          "Rebase Merge",
	git.RepositoryStateApplyMailbox:         "Apply Mailbox",
	git.RepositoryStateApplyMailboxOrRebase: "Apply Mailbox or Rebase",
}

/*Global variable to set the StatusOption parameter, in order to list each file status
 */
var statusOption = git.StatusOptions{
	Show:     git.StatusShowIndexAndWorkdir,
	Flags:    git.StatusOptIncludeUntracked,
	Pathspec: []string{},
}

/*GitObject contains informations about the current git repository
 *
 *The structure is:
 *  accessible:
 *		Is the repository still exists in the hard drive?
 *	path:
 *		The path file.
 *	repository:
 *		The object repository.
 */
type GitObject struct {
	accessible error
	path       string
	repository git.Repository
}

/*New is a constructor for GitObject
 *
 * It neeeds:
 *	path:
 *		The path of the current repository.
 */
func New(path string) *GitObject {
	r, err := git.OpenRepository(path)
	return &GitObject{accessible: err, path: path, repository: *r}
}

/*Clone is cloning a given repository, from a public URL
 *
 * It needs:
 * path:
 *		The local path to clone the repository.
 *	URL:
 *		The remote URL to fetch the repository.
 */
func Clone(path, URL string) error {
	_, err := git.Clone(URL, path, &git.CloneOptions{})
	return err
}

/*GetRemoteURL returns the associated remote URL of a given local path repository
 *
 * It needs:
 *	path
 *		The local path of a git repository
 */
func GetRemoteURL(path string) string {
	r, err := git.OpenRepository(path)
	if err != nil {
		fmt.Println("The repository can't be opened")
		return ""
	}
	remoteCollection := r.Remotes
	originRemote, err := remoteCollection.Lookup("origin")
	if err != nil {
		traces.WarningTracer.Printf("can't lookup origin remote URL for %s", path)
		return ""
	}
	return originRemote.Url()
}

/*isAccesible returns the information that is the current git repository is existing or not.
 *This method returns a boolean value: true if the git repository is still accesible (still exists), or false if not.
 */
func (g *GitObject) isAccessible() bool {
	return g.accessible == nil
}

/*Status prints the current status of the repository, accessible via the structure path field.
 *This method works only if the repository is accessible.
 */
func (g *GitObject) Status() {
	if g.isAccessible() {
		if err := g.printChanges(); err != nil {
			color.RedString("Impossible to get stats from %s, due to error %s", g.path, err)
		}
	} else {
		color.RedString("Repository %s not found!", g.path)
	}
}

/*getDiffWithWT returns the difference between the working tree and the index, for the current git repository.
 *If there is an error processing the request, it returns an error.
 */
func (g *GitObject) getDiffWithWT() (*git.Diff, error) {
	// Get the index of the repository
	currentIndex, err := g.repository.Index()
	if err != nil {
		return nil, err
	}
	// Get the default diff options, and add it custom flags
	defaultDiffOptions, err := git.DefaultDiffOptions()
	if err != nil {
		return nil, err
	}
	defaultDiffOptions.Flags = defaultDiffOptions.Flags | git.DiffIncludeUntracked | git.DiffIncludeTypeChange
	// Check the difference between the working directory and the index
	diff, err := g.repository.DiffIndexToWorkdir(currentIndex, &defaultDiffOptions)
	if err != nil {
		return nil, err
	}
	return diff, nil
}

func (g *GitObject) getCommitsAheadBehind() (int, int, error) {
	repositoryHead, err := g.repository.Head()
	// Check upstream branch head
	cBranch := repositoryHead.Branch()
	cReference, err := cBranch.Upstream()
	if err != nil {
		return -1, -1, err
	}
	cReferenceTarget := cReference.Target()
	cRepositoryTarget := repositoryHead.Target()
	commitsAhead, commitsBehind, err := g.repository.AheadBehind(cRepositoryTarget, cReferenceTarget)
	return commitsAhead, commitsBehind, err
}

/*printChanges prints out all changes for the current git repository.
 *If there is an error processing the request, it returns this one.
 */
func (g *GitObject) printChanges() error {
	diff, err := g.getDiffWithWT()
	var buffer bytes.Buffer
	if err != nil {
		return err
	}
	numDeltas, err := diff.NumDeltas()
	if err != nil {
		return err
	}
	headDetached, err := g.repository.IsHeadDetached()
	if err != nil {
		return err
	}
	if headDetached {
		outputHead := fmt.Sprintf("%s", color.RedString("\t/!\\ The repository's HEAD is detached! /!\\\n"))
		buffer.WriteString(outputHead)
	}
	if numDeltas > 0 {
		buffer.WriteString(fmt.Sprintf("%s %s\t[%d modification(s)]\n", color.RedString("✘"), g.path, numDeltas))
		for i := 0; i < numDeltas; i++ {
			delta, _ := diff.GetDelta(i)
			currentStatus := delta.Status
			newFile := delta.NewFile.Path
			oldFile := delta.OldFile.Path
			switch currentStatus {
			case git.DeltaAdded:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been added!\n", color.MagentaString(newFile)))
			case git.DeltaDeleted:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been deleted!\n", color.MagentaString(newFile)))
			case git.DeltaModified:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been modified!\n", color.MagentaString(newFile)))
			case git.DeltaRenamed:
				buffer.WriteString(fmt.Sprintf("\t===> %s has been renamed to %s!\n", color.MagentaString(oldFile), color.MagentaString(newFile)))
			case git.DeltaUntracked:
				buffer.WriteString(fmt.Sprintf("\t===> %s is untracked - please to add it or update the gitignore file!\n", color.MagentaString(newFile)))
			case git.DeltaTypeChange:
				buffer.WriteString(fmt.Sprintf("\t===> the type of %s has been changed from %d to %d!", color.MagentaString(newFile), delta.OldFile.Mode, delta.NewFile.Mode))
			}
		}
	} else {
		buffer.WriteString(fmt.Sprintf("%s %s\n", color.GreenString("✔"), g.path))
	}
	repository_head, err := g.repository.Head()
	if err == nil {
		repository_id := repository_head.Target()
		commits_ahead, commits_behind, err := g.repository.AheadBehind(repository_id, repository_id)
		fmt.Printf("%d commits ahead!\n", commits_ahead)
		fmt.Printf("%d commits behind!\n", commits_behind)
		if err != nil {
			buffer.WriteString(fmt.Sprintf("%s", color.RedString("\tAn error occured checking the ahead/behind commits...\n")))
		} else if commits_ahead != 0 {
			buffer.WriteString(fmt.Sprintf("\tYou need to push the last modifications!\n"))
		}
	}
	fmt.Print(buffer.String())
	return nil
}
<|endoftext|>


<commit_before>package isolate

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"cloud.google.com/go/storage"
	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gcs"
	"go.skia.org/infra/go/util"
	"google.golang.org/api/option"
)

const (
	DEFAULT_NAMESPACE          = "default-gzip"
	ISOLATE_EXE_SHA1           = "3c3d1bff685b4875937af0771f2f5b1c6a705a11"
	ISOLATESERVER_EXE_SHA1     = "23dfc04c65f2ddf2f8dc8dce0fcdcf57ed6b98a3"
	ISOLATE_SERVER_URL         = "https://isolateserver.appspot.com"
	ISOLATE_SERVER_URL_FAKE    = "fake"
	ISOLATE_SERVER_URL_PRIVATE = "https://chrome-isolated.appspot.com"
	ISOLATE_VERSION            = 1
	GCS_BUCKET                 = "chromium-luci"
	GCS_SUBDIR                 = ""
	TASK_ID_TMPL               = "task_%s"
)

var (
	DEFAULT_BLACKLIST = []string{"*.pyc", ".git", "out", ".recipe_deps"}

	isolatedHashRegexpPattern = fmt.Sprintf("^([a-f0-9]{40})\\s+.*(%s)\\.isolated$", fmt.Sprintf(TASK_ID_TMPL, "\\d+"))
	isolatedHashRegexp        = regexp.MustCompile(isolatedHashRegexpPattern)
)

// Client is a Skia-specific wrapper around the Isolate executable.
type Client struct {
	gcs           *gcs.DownloadHelper
	isolate       string
	isolateserver string
	serverUrl     string
	workdir       string
}

// NewClient returns a Client instance.
func NewClient(workdir, server string) (*Client, error) {
	// By default, the storage client tries really hard to be authenticated
	// with the scopes ReadWrite. Since the isolate executables are public
	// links, this is unnecessay and, in fact, causes errors if the user
	// doesn't have access to the bucket.
	s, err := storage.NewClient(context.Background(), option.WithScopes(storage.ScopeReadOnly), option.WithHTTPClient(&http.Client{}))
	if err != nil {
		return nil, err
	}
	absPath, err := filepath.Abs(workdir)
	if err != nil {
		return nil, err
	}
	c := &Client{
		gcs:           gcs.NewDownloadHelper(s, GCS_BUCKET, GCS_SUBDIR, workdir),
		isolate:       path.Join(workdir, "isolate"),
		isolateserver: path.Join(workdir, "isolateserver"),
		serverUrl:     server,
		workdir:       absPath,
	}
	if err := c.gcs.MaybeDownload("isolate", ISOLATE_EXE_SHA1); err != nil {
		return nil, fmt.Errorf("Unable to create isolate client; failed to download isolate binary: %s", err)
	}
	if err := c.gcs.MaybeDownload("isolateserver", ISOLATESERVER_EXE_SHA1); err != nil {
		return nil, fmt.Errorf("Unable to create isolate client; failed to download isolateserver binary: %s", err)
	}

	return c, nil
}

// Close should be called when finished using the Client.
func (c *Client) Close() error {
	return c.gcs.Close()
}

// ServerURL return the Isolate server URL.
func (c *Client) ServerURL() string {
	return c.serverUrl
}

// Task is a description of the necessary inputs to isolate a task.
type Task struct {
	// BaseDir is the directory in which the files to be isolated reside.
	BaseDir string

	// Blacklist is a list of patterns of files not to upload.
	Blacklist []string

	// Deps is a list of isolated hashes upon which this task depends.
	Deps []string

	// ExtraVars is a map containing variable keys and values for the task.
	ExtraVars map[string]string

	// IsolateFile is the isolate file for this task.
	IsolateFile string

	// OsType is the OS on which the task will run.
	OsType string
}

// Validate returns an error if the Task is not valid.
func (t *Task) Validate() error {
	if t.BaseDir == "" {
		return fmt.Errorf("BaseDir is required.")
	}
	if t.IsolateFile == "" {
		return fmt.Errorf("IsolateFile is required.")
	}
	return nil
}

// WriteIsolatedGenJson writes a temporary .isolated.gen.json file for the task.
func WriteIsolatedGenJson(t *Task, genJsonFile, isolatedFile string) error {
	if err := t.Validate(); err != nil {
		return err
	}
	isolateFile, err := filepath.Abs(t.IsolateFile)
	if err != nil {
		return err
	}
	args := []string{
		"--isolate", isolateFile,
		"--isolated", isolatedFile,
	}
	if t.OsType != "" {
		args = append(args, "--config-variable", "OS", t.OsType)
	}
	for _, b := range t.Blacklist {
		args = append(args, "--blacklist", b)
	}
	for k, v := range t.ExtraVars {
		args = append(args, "--extra-variable", k, v)
	}
	baseDir, err := filepath.Abs(t.BaseDir)
	if err != nil {
		return err
	}
	gen := struct {
		Version int      `json:"version"`
		Dir     string   `json:"dir"`
		Args    []string `json:"args"`
	}{
		Version: ISOLATE_VERSION,
		Dir:     baseDir,
		Args:    args,
	}
	f, err := os.Create(genJsonFile)
	if err != nil {
		return fmt.Errorf("Failed to create %s: %s", genJsonFile, err)
	}
	defer util.Close(f)
	if err := json.NewEncoder(f).Encode(&gen); err != nil {
		return fmt.Errorf("Failed to write %s: %s", genJsonFile, err)
	}
	return nil
}

// isolateFile is a struct representing the contents of a .isolate file.
type isolateFile struct {
	Command  []string
	Files    []string
	Includes []string
}

// Encode writes the encoded isolateFile into the given io.Writer.
func (f *isolateFile) Encode(w io.Writer) error {
	s := "{\n"
	if f.Includes != nil && len(f.Includes) > 0 {
		s += "  'includes': [\n"
		for _, inc := range f.Includes {
			s += fmt.Sprintf("    '%s',\n", inc)
		}
		s += "],\n"
	}
	s += "  'variables': {\n"
	if f.Command != nil && len(f.Command) > 0 {
		s += "    'command': [\n"
		for _, c := range f.Command {
			s += fmt.Sprintf("      '%s',\n", c)
		}
		s += "    ],\n"
	}
	if f.Files != nil && len(f.Files) > 0 {
		s += "    'files': [\n"
		for _, p := range f.Files {
			s += fmt.Sprintf("      '%s',\n", p)
		}
		s += "    ],\n"
	}
	s += "  },\n}"
	b := []byte(s)
	n, err := w.Write(b)
	if err != nil {
		return err
	}
	if n != len(b) {
		return fmt.Errorf("Failed to write all bytes.")
	}
	return nil
}

// isolatedFile is a struct representing the contents of a .isolated file.
type isolatedFile struct {
	Algo        string                 `json:"algo"`
	Command     []string               `json:"command"`
	Files       map[string]interface{} `json:"files"`
	Includes    []string               `json:"includes"`
	RelativeCwd string                 `json:"relative_cwd"`
	Version     string                 `json:"version"`
}

// readIsolatedFile reads the given isolated file.
func readIsolatedFile(filepath string) (*isolatedFile, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer util.Close(f)
	var isolated isolatedFile
	if err := json.NewDecoder(f).Decode(&isolated); err != nil {
		return nil, err
	}
	return &isolated, nil
}

// writeIsolatedFile writes the given isolated file.
func writeIsolatedFile(filepath string, i *isolatedFile) error {
	f, err := os.Create(filepath)
	if err != nil {
		return err
	}
	if err := json.NewEncoder(f).Encode(i); err != nil {
		defer util.Close(f)
		return err
	}
	return f.Close()
}

// addIsolatedIncludes inserts the given isolated hashes as includes into the
// given isolated file.
func addIsolatedIncludes(filepath string, includes []string) error {
	isolated, err := readIsolatedFile(filepath)
	if err != nil {
		return err
	}
	if isolated.Includes == nil {
		isolated.Includes = make([]string, 0, len(includes))
	}
	isolated.Includes = append(isolated.Includes, includes...)
	return writeIsolatedFile(filepath, isolated)
}

// BatchArchiveTasks runs `isolate batcharchive` for the tasks.
func (c *Client) BatchArchiveTasks(ctx context.Context, genJsonFiles []string, jsonOutput string) error {
	cmd := []string{
		c.isolate, "batcharchive", "--verbose",
		"--isolate-server", c.serverUrl,
	}
	if jsonOutput != "" {
		cmd = append(cmd, "--dump-json", jsonOutput)
	}
	cmd = append(cmd, genJsonFiles...)
	output, err := exec.RunCwd(ctx, c.workdir, cmd...)
	if err != nil {
		return fmt.Errorf("Failed to run isolate: %s\nOutput:\n%s", err, output)
	}
	return nil
}

// IsolateTasks uploads the necessary inputs for the task to the isolate server
// and returns the isolated hashes.
func (c *Client) IsolateTasks(ctx context.Context, tasks []*Task) ([]string, error) {
	// Validation.
	if len(tasks) == 0 {
		return []string{}, nil
	}
	for _, t := range tasks {
		if err := t.Validate(); err != nil {
			return nil, err
		}
	}

	// Setup.
	tmpDir, err := ioutil.TempDir("", "isolate")
	if err != nil {
		return nil, fmt.Errorf("Failed to create temporary dir: %s", err)
	}
	defer util.RemoveAll(tmpDir)

	// Write the .isolated.gen.json files.
	genJsonFiles := make([]string, 0, len(tasks))
	isolatedFiles := make([]string, 0, len(tasks))
	for i, t := range tasks {
		taskId := fmt.Sprintf(TASK_ID_TMPL, strconv.Itoa(i))
		genJsonFile := path.Join(tmpDir, fmt.Sprintf("%s.isolated.gen.json", taskId))
		isolatedFile := path.Join(tmpDir, fmt.Sprintf("%s.isolated", taskId))
		if err := WriteIsolatedGenJson(t, genJsonFile, isolatedFile); err != nil {
			return nil, err
		}
		genJsonFiles = append(genJsonFiles, genJsonFile)
		isolatedFiles = append(isolatedFiles, isolatedFile)
	}

	// Isolate the tasks.
	if err := c.BatchArchiveTasks(ctx, genJsonFiles, ""); err != nil {
		return nil, err
	}

	// Rewrite the isolated files with any extra dependencies.
	for i, f := range isolatedFiles {
		t := tasks[i]
		if t.Deps != nil && len(t.Deps) > 0 {
			if err := addIsolatedIncludes(f, t.Deps); err != nil {
				return nil, err
			}
		}
	}

	// Re-upload the isolated files.
	cmd := []string{
		c.isolateserver, "archive", "--verbose",
		"--isolate-server", c.serverUrl,
	}
	for _, f := range isolatedFiles {
		cmd = append(cmd, "--files", f)
	}
	output, err := exec.RunCwd(ctx, c.workdir, cmd...)
	if err != nil {
		return nil, fmt.Errorf("Failed to run isolate: %s\nOutput:\n%s", err, output)
	}

	// Parse isolated hash for each task from the output.
	hashes := map[string]string{}
	for _, line := range strings.Split(string(output), "\n") {
		m := isolatedHashRegexp.FindStringSubmatch(line)
		if m != nil {
			if len(m) != 3 {
				return nil, fmt.Errorf("Isolated output regexp returned invalid match: %v", m)
			}
			hashes[m[2]] = m[1]
		}
	}
	if len(hashes) != len(tasks) {
		return nil, fmt.Errorf("Ended up with an incorrect number of isolated hashes!")
	}
	rv := make([]string, 0, len(tasks))
	for i := range tasks {
		rv = append(rv, hashes[fmt.Sprintf(TASK_ID_TMPL, strconv.Itoa(i))])
	}
	return rv, nil
}
<commit_msg>[task scheduler] Tweak isolated file format<commit_after>package isolate

import (
	"context"
	"encoding/json"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"os"
	"path"
	"path/filepath"
	"regexp"
	"strconv"
	"strings"

	"cloud.google.com/go/storage"
	"go.skia.org/infra/go/exec"
	"go.skia.org/infra/go/gcs"
	"go.skia.org/infra/go/util"
	"google.golang.org/api/option"
)

const (
	DEFAULT_NAMESPACE          = "default-gzip"
	ISOLATE_EXE_SHA1           = "3c3d1bff685b4875937af0771f2f5b1c6a705a11"
	ISOLATESERVER_EXE_SHA1     = "23dfc04c65f2ddf2f8dc8dce0fcdcf57ed6b98a3"
	ISOLATE_SERVER_URL         = "https://isolateserver.appspot.com"
	ISOLATE_SERVER_URL_FAKE    = "fake"
	ISOLATE_SERVER_URL_PRIVATE = "https://chrome-isolated.appspot.com"
	ISOLATE_VERSION            = 1
	GCS_BUCKET                 = "chromium-luci"
	GCS_SUBDIR                 = ""
	TASK_ID_TMPL               = "task_%s"
)

var (
	DEFAULT_BLACKLIST = []string{"*.pyc", ".git", "out", ".recipe_deps"}

	isolatedHashRegexpPattern = fmt.Sprintf("^([a-f0-9]{40})\\s+.*(%s)\\.isolated$", fmt.Sprintf(TASK_ID_TMPL, "\\d+"))
	isolatedHashRegexp        = regexp.MustCompile(isolatedHashRegexpPattern)
)

// Client is a Skia-specific wrapper around the Isolate executable.
type Client struct {
	gcs           *gcs.DownloadHelper
	isolate       string
	isolateserver string
	serverUrl     string
	workdir       string
}

// NewClient returns a Client instance.
func NewClient(workdir, server string) (*Client, error) {
	// By default, the storage client tries really hard to be authenticated
	// with the scopes ReadWrite. Since the isolate executables are public
	// links, this is unnecessay and, in fact, causes errors if the user
	// doesn't have access to the bucket.
	s, err := storage.NewClient(context.Background(), option.WithScopes(storage.ScopeReadOnly), option.WithHTTPClient(&http.Client{}))
	if err != nil {
		return nil, err
	}
	absPath, err := filepath.Abs(workdir)
	if err != nil {
		return nil, err
	}
	c := &Client{
		gcs:           gcs.NewDownloadHelper(s, GCS_BUCKET, GCS_SUBDIR, workdir),
		isolate:       path.Join(workdir, "isolate"),
		isolateserver: path.Join(workdir, "isolateserver"),
		serverUrl:     server,
		workdir:       absPath,
	}
	if err := c.gcs.MaybeDownload("isolate", ISOLATE_EXE_SHA1); err != nil {
		return nil, fmt.Errorf("Unable to create isolate client; failed to download isolate binary: %s", err)
	}
	if err := c.gcs.MaybeDownload("isolateserver", ISOLATESERVER_EXE_SHA1); err != nil {
		return nil, fmt.Errorf("Unable to create isolate client; failed to download isolateserver binary: %s", err)
	}

	return c, nil
}

// Close should be called when finished using the Client.
func (c *Client) Close() error {
	return c.gcs.Close()
}

// ServerURL return the Isolate server URL.
func (c *Client) ServerURL() string {
	return c.serverUrl
}

// Task is a description of the necessary inputs to isolate a task.
type Task struct {
	// BaseDir is the directory in which the files to be isolated reside.
	BaseDir string

	// Blacklist is a list of patterns of files not to upload.
	Blacklist []string

	// Deps is a list of isolated hashes upon which this task depends.
	Deps []string

	// ExtraVars is a map containing variable keys and values for the task.
	ExtraVars map[string]string

	// IsolateFile is the isolate file for this task.
	IsolateFile string

	// OsType is the OS on which the task will run.
	OsType string
}

// Validate returns an error if the Task is not valid.
func (t *Task) Validate() error {
	if t.BaseDir == "" {
		return fmt.Errorf("BaseDir is required.")
	}
	if t.IsolateFile == "" {
		return fmt.Errorf("IsolateFile is required.")
	}
	return nil
}

// WriteIsolatedGenJson writes a temporary .isolated.gen.json file for the task.
func WriteIsolatedGenJson(t *Task, genJsonFile, isolatedFile string) error {
	if err := t.Validate(); err != nil {
		return err
	}
	isolateFile, err := filepath.Abs(t.IsolateFile)
	if err != nil {
		return err
	}
	args := []string{
		"--isolate", isolateFile,
		"--isolated", isolatedFile,
	}
	if t.OsType != "" {
		args = append(args, "--config-variable", "OS", t.OsType)
	}
	for _, b := range t.Blacklist {
		args = append(args, "--blacklist", b)
	}
	for k, v := range t.ExtraVars {
		args = append(args, "--extra-variable", k, v)
	}
	baseDir, err := filepath.Abs(t.BaseDir)
	if err != nil {
		return err
	}
	gen := struct {
		Version int      `json:"version"`
		Dir     string   `json:"dir"`
		Args    []string `json:"args"`
	}{
		Version: ISOLATE_VERSION,
		Dir:     baseDir,
		Args:    args,
	}
	f, err := os.Create(genJsonFile)
	if err != nil {
		return fmt.Errorf("Failed to create %s: %s", genJsonFile, err)
	}
	defer util.Close(f)
	if err := json.NewEncoder(f).Encode(&gen); err != nil {
		return fmt.Errorf("Failed to write %s: %s", genJsonFile, err)
	}
	return nil
}

// isolateFile is a struct representing the contents of a .isolate file.
type isolateFile struct {
	Command  []string
	Files    []string
	Includes []string
}

// Encode writes the encoded isolateFile into the given io.Writer.
func (f *isolateFile) Encode(w io.Writer) error {
	s := "{\n"
	if f.Includes != nil && len(f.Includes) > 0 {
		s += "  'includes': [\n"
		for _, inc := range f.Includes {
			s += fmt.Sprintf("    '%s',\n", inc)
		}
		s += "],\n"
	}
	s += "  'variables': {\n"
	if f.Command != nil && len(f.Command) > 0 {
		s += "    'command': [\n"
		for _, c := range f.Command {
			s += fmt.Sprintf("      '%s',\n", c)
		}
		s += "    ],\n"
	}
	if f.Files != nil && len(f.Files) > 0 {
		s += "    'files': [\n"
		for _, p := range f.Files {
			s += fmt.Sprintf("      '%s',\n", p)
		}
		s += "    ],\n"
	}
	s += "  },\n}"
	b := []byte(s)
	n, err := w.Write(b)
	if err != nil {
		return err
	}
	if n != len(b) {
		return fmt.Errorf("Failed to write all bytes.")
	}
	return nil
}

// isolatedFile is a struct representing the contents of a .isolated file.
type isolatedFile struct {
	Algo        string                 `json:"algo,omitempty"`
	Command     []string               `json:"command,omitempty"`
	Files       map[string]interface{} `json:"files,omitempty"`
	Includes    []string               `json:"includes,omitempty"`
	RelativeCwd string                 `json:"relative_cwd,omitempty"`
	Version     string                 `json:"version,omitempty"`
}

// readIsolatedFile reads the given isolated file.
func readIsolatedFile(filepath string) (*isolatedFile, error) {
	f, err := os.Open(filepath)
	if err != nil {
		return nil, err
	}
	defer util.Close(f)
	var isolated isolatedFile
	if err := json.NewDecoder(f).Decode(&isolated); err != nil {
		return nil, err
	}
	return &isolated, nil
}

// writeIsolatedFile writes the given isolated file.
func writeIsolatedFile(filepath string, i *isolatedFile) error {
	f, err := os.Create(filepath)
	if err != nil {
		return err
	}
	if err := json.NewEncoder(f).Encode(i); err != nil {
		defer util.Close(f)
		return err
	}
	return f.Close()
}

// addIsolatedIncludes inserts the given isolated hashes as includes into the
// given isolated file.
func addIsolatedIncludes(filepath string, includes []string) error {
	isolated, err := readIsolatedFile(filepath)
	if err != nil {
		return err
	}
	if isolated.Includes == nil {
		isolated.Includes = make([]string, 0, len(includes))
	}
	isolated.Includes = append(isolated.Includes, includes...)
	return writeIsolatedFile(filepath, isolated)
}

// BatchArchiveTasks runs `isolate batcharchive` for the tasks.
func (c *Client) BatchArchiveTasks(ctx context.Context, genJsonFiles []string, jsonOutput string) error {
	cmd := []string{
		c.isolate, "batcharchive", "--verbose",
		"--isolate-server", c.serverUrl,
	}
	if jsonOutput != "" {
		cmd = append(cmd, "--dump-json", jsonOutput)
	}
	cmd = append(cmd, genJsonFiles...)
	output, err := exec.RunCwd(ctx, c.workdir, cmd...)
	if err != nil {
		return fmt.Errorf("Failed to run isolate: %s\nOutput:\n%s", err, output)
	}
	return nil
}

// IsolateTasks uploads the necessary inputs for the task to the isolate server
// and returns the isolated hashes.
func (c *Client) IsolateTasks(ctx context.Context, tasks []*Task) ([]string, error) {
	// Validation.
	if len(tasks) == 0 {
		return []string{}, nil
	}
	for _, t := range tasks {
		if err := t.Validate(); err != nil {
			return nil, err
		}
	}

	// Setup.
	tmpDir, err := ioutil.TempDir("", "isolate")
	if err != nil {
		return nil, fmt.Errorf("Failed to create temporary dir: %s", err)
	}
	defer util.RemoveAll(tmpDir)

	// Write the .isolated.gen.json files.
	genJsonFiles := make([]string, 0, len(tasks))
	isolatedFiles := make([]string, 0, len(tasks))
	for i, t := range tasks {
		taskId := fmt.Sprintf(TASK_ID_TMPL, strconv.Itoa(i))
		genJsonFile := path.Join(tmpDir, fmt.Sprintf("%s.isolated.gen.json", taskId))
		isolatedFile := path.Join(tmpDir, fmt.Sprintf("%s.isolated", taskId))
		if err := WriteIsolatedGenJson(t, genJsonFile, isolatedFile); err != nil {
			return nil, err
		}
		genJsonFiles = append(genJsonFiles, genJsonFile)
		isolatedFiles = append(isolatedFiles, isolatedFile)
	}

	// Isolate the tasks.
	if err := c.BatchArchiveTasks(ctx, genJsonFiles, ""); err != nil {
		return nil, err
	}

	// Rewrite the isolated files with any extra dependencies.
	for i, f := range isolatedFiles {
		t := tasks[i]
		if t.Deps != nil && len(t.Deps) > 0 {
			if err := addIsolatedIncludes(f, t.Deps); err != nil {
				return nil, err
			}
		}
	}

	// Re-upload the isolated files.
	cmd := []string{
		c.isolateserver, "archive", "--verbose",
		"--isolate-server", c.serverUrl,
	}
	for _, f := range isolatedFiles {
		cmd = append(cmd, "--files", f)
	}
	output, err := exec.RunCwd(ctx, c.workdir, cmd...)
	if err != nil {
		return nil, fmt.Errorf("Failed to run isolate: %s\nOutput:\n%s", err, output)
	}

	// Parse isolated hash for each task from the output.
	hashes := map[string]string{}
	for _, line := range strings.Split(string(output), "\n") {
		m := isolatedHashRegexp.FindStringSubmatch(line)
		if m != nil {
			if len(m) != 3 {
				return nil, fmt.Errorf("Isolated output regexp returned invalid match: %v", m)
			}
			hashes[m[2]] = m[1]
		}
	}
	if len(hashes) != len(tasks) {
		return nil, fmt.Errorf("Ended up with an incorrect number of isolated hashes!")
	}
	rv := make([]string, 0, len(tasks))
	for i := range tasks {
		rv = append(rv, hashes[fmt.Sprintf(TASK_ID_TMPL, strconv.Itoa(i))])
	}
	return rv, nil
}
<|endoftext|>