<commit_before>package main

import (
	"fmt"
	"sort"
)

// Participant someone having a role in the tournament
type Participant interface {
	ID() string
}

// Player is a Participant that is actively participating in battles.
type Player struct {
	Name           string `json:"name"`
	PreferredColor string `json:"preferred_color"`
	Shots          int    `json:"shots"`
	Sweeps         int    `json:"sweeps"`
	Kills          int    `json:"kills"`
	Self           int    `json:"self"`
	Explosions     int    `json:"explosions"`
	Matches        int    `json:"matches"`
	Match          *Match
}

// NewPlayer returns a new instance of a player
func NewPlayer() Player {
	p := Player{}
	return p
}

func (p *Player) String() string {
	return fmt.Sprintf(
		"%s: %dsh %dsw %dk %ds %de",
		p.Name,
		p.Shots,
		p.Sweeps,
		p.Kills,
		p.Self,
		p.Explosions,
	)
}

// Score calculates the score to determine runnerup positions.
func (p *Player) Score() (out int) {
	// This algorithm is probably flawed, but at least it should be able to
	// determine who is the most entertaining.
	// When executed, a sweep is basically 11 points since scoring a sweep
	// also comes with a shot and three kills.

	out += p.Sweeps * 5
	out += p.Shots * 3
	out += p.Kills * 2
	out += p.Self
	out += p.Explosions

	return
}

// URL returns the URL to this player
//
// Used for action URLs
func (p *Player) URL() string {
	out := fmt.Sprintf(
		"%s/%d/",
		p.Match.URL(),
		p.Index(),
	)
	return out
}

// Color returns the color that the player prefers.
func (p *Player) Color() string {
	if p.IsPrefill() {
		return "prefill"
	}
	return p.PreferredColor
}

// Index returns the index in the current match
func (p *Player) Index() int {
	if p.Match != nil {
		for i, o := range p.Match.Players {
			if p.Name == o.Name {
				return i
			}
		}
	}
	return -1
}

// IsPrefill returns whether the player is a prefill placeholder or not
func (p *Player) IsPrefill() bool {
	return p.Name == ""
}

// AddShot increases the shot count
func (p *Player) AddShot() {
	p.Shots++
}

// RemoveShot decreases the shot count
// Fails silently if shots are zero.
func (p *Player) RemoveShot() {
	if p.Shots == 0 {
		return
	}
	p.Shots--
}

// AddSweep increases the sweep count, gives three kills and a shot.
func (p *Player) AddSweep() {
	p.Sweeps++
	p.AddShot()
	p.AddKill()
	p.AddKill()
	p.AddKill()
}

// RemoveSweep decreases the sweep count, three kills and a shot
// Fails silently if sweeps are zero.
func (p *Player) RemoveSweep() {
	if p.Sweeps == 0 {
		return
	}
	p.Sweeps--
	p.RemoveShot()
	p.RemoveKill()
	p.RemoveKill()
	p.RemoveKill()
}

// AddKill increases the kill count
func (p *Player) AddKill(kills ...int) {
	// This is basically only to help out with testing.
	// Adding an optional argument with the amount of kills lets us just use
	// one call to AddKill() rather than 10.
	if len(kills) > 0 {
		p.Kills += kills[0]
	} else {
		p.Kills++
	}
}

// RemoveKill decreases the kill count
// Fails silently if kills are zero.
func (p *Player) RemoveKill() {
	if p.Kills == 0 {
		return
	}
	p.Kills--
}

// AddSelf increases the self count, decreases the kill, and gives a shot
func (p *Player) AddSelf() {
	p.Self++
	p.RemoveKill()
	p.AddShot()
}

// RemoveSelf decreases the self count and a shot
// Fails silently if selfs are zero.
func (p *Player) RemoveSelf() {
	if p.Self == 0 {
		return
	}
	p.Self--
	p.AddKill()
	p.RemoveShot()
}

// AddExplosion increases the explosion count, the kill count and gives a shot
func (p *Player) AddExplosion() {
	p.Explosions++
	p.AddShot()
	p.AddKill()
}

// RemoveExplosion decreases the explosion count, a shot and a kill
// Fails silently if Explosions are zero.
func (p *Player) RemoveExplosion() {
	if p.Explosions == 0 {
		return
	}
	p.Explosions--
	p.RemoveShot()
	p.RemoveKill()
}

// Reset resets the stats on a Player to 0
//
// It is to be run in Match.Start()
func (p *Player) Reset() {
	p.Shots = 0
	p.Sweeps = 0
	p.Kills = 0
	p.Self = 0
	p.Explosions = 0
	p.Matches = 0
}

// Update updates a player with the scores of another
//
// This is primarily used by the tournament score calculator
func (p *Player) Update(other *Player) error {
	p.Shots += other.Shots
	p.Sweeps += other.Sweeps
	p.Kills += other.Kills
	p.Self += other.Self
	p.Explosions += other.Explosions

	// Every call to this method is per match. Count every call
	// as if a match.
	p.Matches++

	return nil
}

// HTML renders the HTML of a player
func (p *Player) HTML() (out string) {
	return
}

// ByScore is a sort.Interface that sorts players by their score
type ByScore []Player

func (s ByScore) Len() int {
	return len(s)

}
func (s ByScore) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]

}
func (s ByScore) Less(i, j int) bool {
	// Technically not Less, but we want biggest first...
	return s[i].Score() > s[j].Score()
}

// SortByScore returns a list in order of the score the players have
func SortByScore(ps []Player) []Player {
	sort.Sort(ByScore(ps))
	return ps
}

// ByKills is a sort.Interface that sorts players by their kills
type ByKills []Player

func (s ByKills) Len() int {
	return len(s)

}
func (s ByKills) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]

}
func (s ByKills) Less(i, j int) bool {
	// Technically not Less, but we want biggest first...
	return s[i].Kills > s[j].Kills
}

// SortByKills returns a list in order of the kills the players have
func SortByKills(ps []Player) []Player {
	sort.Sort(ByKills(ps))
	return ps
}

// ByRunnerup is a sort.Interface that sorts players by their runnerup status
//
// This is almost exactly the same as score, but the number of matches a player
// has played factors in, and players that have played less matches are sorted
// favorably.
type ByRunnerup []Player

func (s ByRunnerup) Len() int {
	return len(s)

}
func (s ByRunnerup) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]

}
func (s ByRunnerup) Less(i, j int) bool {
	if s[i].Matches == s[j].Matches {
		// Same as by kills
		return s[i].Kills > s[j].Kills
	}
	// Lower is better - the ones that have not played should be at the top
	return s[i].Matches < s[j].Matches
}

// SortByRunnerup returns a list in order of the kills the players have
func SortByRunnerup(ps []Player) []Player {
	sort.Sort(ByRunnerup(ps))
	return ps
}

// Judge is a Participant that has access to the judge functions
type Judge struct {
}
<commit_msg>Add Player.ScoreData{}<commit_after>package main

import (
	"fmt"
	"sort"
)

// Participant someone having a role in the tournament
type Participant interface {
	ID() string
}

// ScoreData is a structured Key/Value pair list for scores
type ScoreData struct {
	Key    string
	Value  int
	Player *Player
}

// Player is a Participant that is actively participating in battles.
type Player struct {
	Name           string `json:"name"`
	PreferredColor string `json:"preferred_color"`
	Shots          int    `json:"shots"`
	Sweeps         int    `json:"sweeps"`
	Kills          int    `json:"kills"`
	Self           int    `json:"self"`
	Explosions     int    `json:"explosions"`
	Matches        int    `json:"matches"`
	Match          *Match
}

// NewPlayer returns a new instance of a player
func NewPlayer() Player {
	p := Player{}
	return p
}

func (p *Player) String() string {
	return fmt.Sprintf(
		"%s: %dsh %dsw %dk %ds %de",
		p.Name,
		p.Shots,
		p.Sweeps,
		p.Kills,
		p.Self,
		p.Explosions,
	)
}

// Score calculates the score to determine runnerup positions.
func (p *Player) Score() (out int) {
	// This algorithm is probably flawed, but at least it should be able to
	// determine who is the most entertaining.
	// When executed, a sweep is basically 11 points since scoring a sweep
	// also comes with a shot and three kills.

	out += p.Sweeps * 5
	out += p.Shots * 3
	out += p.Kills * 2
	out += p.Self
	out += p.Explosions

	return
}

// ScoreData returns this players set of ScoreData
func (p *Player) ScoreData() []ScoreData {
	sd := []ScoreData{
		ScoreData{Key: "kills", Value: p.Kills, Player: p},
		ScoreData{Key: "shots", Value: p.Shots, Player: p},
		ScoreData{Key: "sweeps", Value: p.Sweeps, Player: p},
		ScoreData{Key: "self", Value: p.Self, Player: p},
		ScoreData{Key: "explosions", Value: p.Explosions, Player: p},
	}
	return sd
}

// URL returns the URL to this player
//
// Used for action URLs
func (p *Player) URL() string {
	out := fmt.Sprintf(
		"%s/%d/",
		p.Match.URL(),
		p.Index(),
	)
	return out
}

// Color returns the color that the player prefers.
func (p *Player) Color() string {
	if p.IsPrefill() {
		return "prefill"
	}
	return p.PreferredColor
}

// Index returns the index in the current match
func (p *Player) Index() int {
	if p.Match != nil {
		for i, o := range p.Match.Players {
			if p.Name == o.Name {
				return i
			}
		}
	}
	return -1
}

// IsPrefill returns whether the player is a prefill placeholder or not
func (p *Player) IsPrefill() bool {
	return p.Name == ""
}

// AddShot increases the shot count
func (p *Player) AddShot() {
	p.Shots++
}

// RemoveShot decreases the shot count
// Fails silently if shots are zero.
func (p *Player) RemoveShot() {
	if p.Shots == 0 {
		return
	}
	p.Shots--
}

// AddSweep increases the sweep count, gives three kills and a shot.
func (p *Player) AddSweep() {
	p.Sweeps++
	p.AddShot()
	p.AddKill()
	p.AddKill()
	p.AddKill()
}

// RemoveSweep decreases the sweep count, three kills and a shot
// Fails silently if sweeps are zero.
func (p *Player) RemoveSweep() {
	if p.Sweeps == 0 {
		return
	}
	p.Sweeps--
	p.RemoveShot()
	p.RemoveKill()
	p.RemoveKill()
	p.RemoveKill()
}

// AddKill increases the kill count
func (p *Player) AddKill(kills ...int) {
	// This is basically only to help out with testing.
	// Adding an optional argument with the amount of kills lets us just use
	// one call to AddKill() rather than 10.
	if len(kills) > 0 {
		p.Kills += kills[0]
	} else {
		p.Kills++
	}
}

// RemoveKill decreases the kill count
// Fails silently if kills are zero.
func (p *Player) RemoveKill() {
	if p.Kills == 0 {
		return
	}
	p.Kills--
}

// AddSelf increases the self count, decreases the kill, and gives a shot
func (p *Player) AddSelf() {
	p.Self++
	p.RemoveKill()
	p.AddShot()
}

// RemoveSelf decreases the self count and a shot
// Fails silently if selfs are zero.
func (p *Player) RemoveSelf() {
	if p.Self == 0 {
		return
	}
	p.Self--
	p.AddKill()
	p.RemoveShot()
}

// AddExplosion increases the explosion count, the kill count and gives a shot
func (p *Player) AddExplosion() {
	p.Explosions++
	p.AddShot()
	p.AddKill()
}

// RemoveExplosion decreases the explosion count, a shot and a kill
// Fails silently if Explosions are zero.
func (p *Player) RemoveExplosion() {
	if p.Explosions == 0 {
		return
	}
	p.Explosions--
	p.RemoveShot()
	p.RemoveKill()
}

// Reset resets the stats on a Player to 0
//
// It is to be run in Match.Start()
func (p *Player) Reset() {
	p.Shots = 0
	p.Sweeps = 0
	p.Kills = 0
	p.Self = 0
	p.Explosions = 0
	p.Matches = 0
}

// Update updates a player with the scores of another
//
// This is primarily used by the tournament score calculator
func (p *Player) Update(other *Player) error {
	p.Shots += other.Shots
	p.Sweeps += other.Sweeps
	p.Kills += other.Kills
	p.Self += other.Self
	p.Explosions += other.Explosions

	// Every call to this method is per match. Count every call
	// as if a match.
	p.Matches++

	return nil
}

// HTML renders the HTML of a player
func (p *Player) HTML() (out string) {
	return
}

// ByScore is a sort.Interface that sorts players by their score
type ByScore []Player

func (s ByScore) Len() int {
	return len(s)

}
func (s ByScore) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]

}
func (s ByScore) Less(i, j int) bool {
	// Technically not Less, but we want biggest first...
	return s[i].Score() > s[j].Score()
}

// SortByScore returns a list in order of the score the players have
func SortByScore(ps []Player) []Player {
	sort.Sort(ByScore(ps))
	return ps
}

// ByKills is a sort.Interface that sorts players by their kills
type ByKills []Player

func (s ByKills) Len() int {
	return len(s)

}
func (s ByKills) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]

}
func (s ByKills) Less(i, j int) bool {
	// Technically not Less, but we want biggest first...
	return s[i].Kills > s[j].Kills
}

// SortByKills returns a list in order of the kills the players have
func SortByKills(ps []Player) []Player {
	sort.Sort(ByKills(ps))
	return ps
}

// ByRunnerup is a sort.Interface that sorts players by their runnerup status
//
// This is almost exactly the same as score, but the number of matches a player
// has played factors in, and players that have played less matches are sorted
// favorably.
type ByRunnerup []Player

func (s ByRunnerup) Len() int {
	return len(s)

}
func (s ByRunnerup) Swap(i, j int) {
	s[i], s[j] = s[j], s[i]

}
func (s ByRunnerup) Less(i, j int) bool {
	if s[i].Matches == s[j].Matches {
		// Same as by kills
		return s[i].Kills > s[j].Kills
	}
	// Lower is better - the ones that have not played should be at the top
	return s[i].Matches < s[j].Matches
}

// SortByRunnerup returns a list in order of the kills the players have
func SortByRunnerup(ps []Player) []Player {
	sort.Sort(ByRunnerup(ps))
	return ps
}

// Judge is a Participant that has access to the judge functions
type Judge struct {
}
<|endoftext|>


<commit_before>package main

import (
	"container/heap"
	"errors"
	"fmt"
)

type player struct {
	LOS         map[position]bool
	Rays        rayMap
	Pos         position
	HP          int
	MP          int
	Consumables map[consumable]int
	Gold        int
	Target      position
	Statuses    map[status]int
	Armour      armour
	Weapon      weapon
	Shield      shield
	Aptitudes   map[aptitude]bool
	Rods        map[rod]*rodProps
}

func (p *player) HPMax() int {
	hpmax := 40
	if p.Aptitudes[AptHealthy] {
		hpmax += 10
	}
	return hpmax
}

func (p *player) MPMax() int {
	mpmax := 10
	if p.Aptitudes[AptMagic] {
		mpmax += 5
	}
	return mpmax
}

func (p *player) Accuracy() int {
	acc := 15
	if p.Aptitudes[AptAccurate] {
		acc += 3
	}
	return acc
}

func (p *player) Armor() int {
	ar := 0
	switch p.Armour {
	case LeatherArmour:
		ar += 3
	case ChainMail:
		ar += 4
	case PlateArmour:
		ar += 6
	}
	if p.Aptitudes[AptScales] {
		ar += 2
	}
	if p.HasStatus(StatusLignification) {
		ar = 8 + ar/2
	}
	if p.HasStatus(StatusCorrosion) {
		ar -= 2 * p.Statuses[StatusCorrosion]
		if ar < 0 {
			ar = 0
		}
	}
	return ar
}

func (p *player) Attack() int {
	attack := p.Weapon.Attack()
	if p.Aptitudes[AptStrong] {
		attack += attack / 5
	}
	if p.HasStatus(StatusCorrosion) {
		penalty := p.Statuses[StatusCorrosion]
		if penalty > 5 {
			penalty = 5
		}
		attack -= penalty
	}
	return attack
}

func (p *player) Block() int {
	block := p.Shield.Block()
	if p.HasStatus(StatusDisabledShield) {
		block /= 3
	}
	return block
}

func (p *player) Evasion() int {
	ev := 15
	if p.Aptitudes[AptAgile] {
		ev += 3
	}
	if p.HasStatus(StatusAgile) {
		ev += 5
	}
	return ev
}

func (p *player) HasStatus(st status) bool {
	return p.Statuses[st] > 0
}

func (p *player) AptitudeCount() int {
	count := 0
	for _, b := range p.Aptitudes {
		if b {
			count++
		}
	}
	return count
}

func (g *game) AutoToDir(ev event) bool {
	if g.MonsterInLOS() == nil {
		err := g.MovePlayer(g.Player.Pos.To(*g.AutoDir), ev)
		if err != nil {
			g.Print(err.Error())
			g.AutoDir = nil
			return false
		}
		return true
	}
	g.AutoDir = nil
	return false
}

func (g *game) GoToDir(dir direction, ev event) error {
	if g.MonsterInLOS() != nil {
		g.AutoDir = nil
		return errors.New("You cannot travel while there are monsters in view.")
	}
	err := g.MovePlayer(g.Player.Pos.To(dir), ev)
	if err != nil {
		return err
	}
	g.AutoDir = &dir
	return nil
}

func (g *game) MoveToTarget(ev event) bool {
	if g.MonsterInLOS() == nil {
		path := g.PlayerPath(g.Player.Pos, *g.AutoTarget)
		if len(path) > 1 {
			err := g.MovePlayer(path[len(path)-2], ev)
			if err != nil {
				g.Print(err.Error())
				g.AutoTarget = nil
				return false
			}
			return true
		}
	}
	g.AutoTarget = nil
	return false
}

func (g *game) WaitTurn(ev event) {
	// XXX Really wait for 10 ?
	g.ScummingAction(ev)
	ev.Renew(g, 10)
}

func (g *game) ExistsMonster() bool {
	for _, mons := range g.Monsters {
		if mons.Exists() {
			return true
		}
	}
	return false
}

func (g *game) ScummingAction(ev event) {
	if g.Player.HP == g.Player.HPMax() {
		g.Scumming++
	}
	if g.Scumming == 100 {
		if g.ExistsMonster() {
			g.Print("You feel a little bored.")
		}
	}
	if g.Scumming > 120 {
		if !g.ExistsMonster() {
			g.Scumming = 0
			return
		}
		g.Player.HP = g.Player.HP / 2
		if RandInt(2) == 0 {
			g.MakeNoise(100, g.Player.Pos)
			neighbors := g.Dungeon.Neighbors(g.Player.Pos)
			for _, pos := range neighbors {
				if RandInt(3) != 0 {
					g.Dungeon.SetCell(pos, FreeCell)
				}
			}
			g.Print("You hear a terrible explosion coming from the ground. You are lignified.")
			g.Player.Statuses[StatusLignification]++
			heap.Push(g.Events, &simpleEvent{ERank: ev.Rank() + 240 + RandInt(10), EAction: LignificationEnd})
		} else {
			delay := 20 + RandInt(5)
			g.Player.Statuses[StatusTele]++
			heap.Push(g.Events, &simpleEvent{ERank: ev.Rank() + delay, EAction: Teleportation})
			g.Print("Something hurt you! You feel unstable.")
		}
		g.Scumming = 0
	}
}

func (g *game) FairAction() {
	g.Scumming -= 10
	if g.Scumming < 0 {
		g.Scumming = 0
	}
}

func (g *game) Rest(ev event) error {
	if g.MonsterInLOS() != nil {
		return fmt.Errorf("You cannot sleep while monsters are in view.")
	}
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() && !g.Player.HasStatus(StatusExhausted) &&
		!g.Player.HasStatus(StatusConfusion) && !g.Player.HasStatus(StatusLignification) {
		return errors.New("You do not need to rest.")
	}
	g.WaitTurn(ev)
	g.Resting = true
	return nil
}

func (g *game) Equip(ev event) error {
	if eq, ok := g.Equipables[g.Player.Pos]; ok {
		eq.Equip(g)
		ev.Renew(g, 10)
		return nil
	}
	return errors.New("Found nothing to equip here.")
}

func (g *game) Teleportation(ev event) {
	var pos position
	i := 0
	count := 0
	for {
		count++
		if count > 1000 {
			panic("Teleportation")
		}
		pos = g.FreeCell()
		if pos.Distance(g.Player.Pos) < 15 && i < 1000 {
			i++
			continue
		}
		break

	}
	g.Player.Statuses[StatusTele]--
	if g.Dungeon.Valid(pos) {
		// should always happen
		g.Player.Pos = pos
		g.Print("You feel yourself teleported away.")
		g.ComputeLOS()
		g.MakeMonstersAware()
	} else {
		g.Print("Something went wrong with the teleportation.")
	}
}

func (g *game) MovePlayer(pos position, ev event) error {
	if !g.Dungeon.Valid(pos) || g.Dungeon.Cell(pos).T == WallCell {
		return errors.New("You cannot move there.")
	}
	if g.Player.HasStatus(StatusConfusion) {
		switch pos.Dir(g.Player.Pos) {
		case E, N, W, S:
		default:
			return errors.New("You cannot use diagonal movements while confused.")
		}
	}
	delay := 10
	switch g.Dungeon.Cell(pos).T {
	case FreeCell:
		mons, _ := g.MonsterAt(pos)
		if !mons.Exists() {
			if g.Player.HasStatus(StatusLignification) {
				return errors.New("You cannot move while lignified")
			}
			g.Player.Pos = pos
			if g.Gold[pos] > 0 {
				g.Player.Gold += g.Gold[pos]
				delete(g.Gold, pos)
			}
			if c, ok := g.Collectables[pos]; ok && c != nil {
				g.Player.Consumables[c.Consumable] += c.Quantity
				delete(g.Collectables, pos)
				if c.Quantity > 1 {
					g.Printf("You take %d %s.", c.Quantity, c.Consumable.Plural())
				} else {
					g.Printf("You take %s.", Indefinite(c.Consumable.String(), false))
				}
			}
			if r, ok := g.Rods[pos]; ok {
				g.Player.Rods[r] = &rodProps{Charge: r.MaxCharge() - 1}
				delete(g.Rods, pos)
				g.Printf("You take a %s.", r)
				g.StoryPrintf("You found and took a %s.", r)
			}
			g.ComputeLOS()
			if g.Autoexploring {
				mons := g.MonsterInLOS()
				if mons.Exists() {
					g.Printf("You see %s (%v).", Indefinite(mons.Kind.String(), false), mons.State)
				}
				g.FairAction()
			} else {
				g.ScummingAction(ev)
			}
			g.MakeMonstersAware()
			if g.Player.Aptitudes[AptFast] {
				// only fast for movement
				delay -= 2
			}
			if g.Player.HasStatus(StatusSwift) {
				// only fast for movement
				delay -= 3
			}
		} else {
			g.FairAction()
			g.AttackMonster(mons)
		}
	}
	if g.Player.HasStatus(StatusBerserk) {
		delay -= 3
	}
	if g.Player.HasStatus(StatusSlow) {
		delay += 3
	}
	ev.Renew(g, delay)
	return nil
}

func (g *game) HealPlayer(ev event) {
	if g.Player.HP < g.Player.HPMax() {
		g.Player.HP++
	}
	delay := 50
	if g.Player.Aptitudes[AptRegen] {
		delay = 25
	}
	ev.Renew(g, delay)
}

func (g *game) MPRegen(ev event) {
	if g.Player.MP < g.Player.MPMax() {
		g.Player.MP++
	}
	delay := 100
	ev.Renew(g, delay)
}
<commit_msg>it's fair to wait while MP is not full too<commit_after>package main

import (
	"container/heap"
	"errors"
	"fmt"
)

type player struct {
	LOS         map[position]bool
	Rays        rayMap
	Pos         position
	HP          int
	MP          int
	Consumables map[consumable]int
	Gold        int
	Target      position
	Statuses    map[status]int
	Armour      armour
	Weapon      weapon
	Shield      shield
	Aptitudes   map[aptitude]bool
	Rods        map[rod]*rodProps
}

func (p *player) HPMax() int {
	hpmax := 40
	if p.Aptitudes[AptHealthy] {
		hpmax += 10
	}
	return hpmax
}

func (p *player) MPMax() int {
	mpmax := 10
	if p.Aptitudes[AptMagic] {
		mpmax += 5
	}
	return mpmax
}

func (p *player) Accuracy() int {
	acc := 15
	if p.Aptitudes[AptAccurate] {
		acc += 3
	}
	return acc
}

func (p *player) Armor() int {
	ar := 0
	switch p.Armour {
	case LeatherArmour:
		ar += 3
	case ChainMail:
		ar += 4
	case PlateArmour:
		ar += 6
	}
	if p.Aptitudes[AptScales] {
		ar += 2
	}
	if p.HasStatus(StatusLignification) {
		ar = 8 + ar/2
	}
	if p.HasStatus(StatusCorrosion) {
		ar -= 2 * p.Statuses[StatusCorrosion]
		if ar < 0 {
			ar = 0
		}
	}
	return ar
}

func (p *player) Attack() int {
	attack := p.Weapon.Attack()
	if p.Aptitudes[AptStrong] {
		attack += attack / 5
	}
	if p.HasStatus(StatusCorrosion) {
		penalty := p.Statuses[StatusCorrosion]
		if penalty > 5 {
			penalty = 5
		}
		attack -= penalty
	}
	return attack
}

func (p *player) Block() int {
	block := p.Shield.Block()
	if p.HasStatus(StatusDisabledShield) {
		block /= 3
	}
	return block
}

func (p *player) Evasion() int {
	ev := 15
	if p.Aptitudes[AptAgile] {
		ev += 3
	}
	if p.HasStatus(StatusAgile) {
		ev += 5
	}
	return ev
}

func (p *player) HasStatus(st status) bool {
	return p.Statuses[st] > 0
}

func (p *player) AptitudeCount() int {
	count := 0
	for _, b := range p.Aptitudes {
		if b {
			count++
		}
	}
	return count
}

func (g *game) AutoToDir(ev event) bool {
	if g.MonsterInLOS() == nil {
		err := g.MovePlayer(g.Player.Pos.To(*g.AutoDir), ev)
		if err != nil {
			g.Print(err.Error())
			g.AutoDir = nil
			return false
		}
		return true
	}
	g.AutoDir = nil
	return false
}

func (g *game) GoToDir(dir direction, ev event) error {
	if g.MonsterInLOS() != nil {
		g.AutoDir = nil
		return errors.New("You cannot travel while there are monsters in view.")
	}
	err := g.MovePlayer(g.Player.Pos.To(dir), ev)
	if err != nil {
		return err
	}
	g.AutoDir = &dir
	return nil
}

func (g *game) MoveToTarget(ev event) bool {
	if g.MonsterInLOS() == nil {
		path := g.PlayerPath(g.Player.Pos, *g.AutoTarget)
		if len(path) > 1 {
			err := g.MovePlayer(path[len(path)-2], ev)
			if err != nil {
				g.Print(err.Error())
				g.AutoTarget = nil
				return false
			}
			return true
		}
	}
	g.AutoTarget = nil
	return false
}

func (g *game) WaitTurn(ev event) {
	// XXX Really wait for 10 ?
	g.ScummingAction(ev)
	ev.Renew(g, 10)
}

func (g *game) ExistsMonster() bool {
	for _, mons := range g.Monsters {
		if mons.Exists() {
			return true
		}
	}
	return false
}

func (g *game) ScummingAction(ev event) {
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() {
		g.Scumming++
	}
	if g.Scumming == 100 {
		if g.ExistsMonster() {
			g.Print("You feel a little bored.")
		}
	}
	if g.Scumming > 120 {
		if !g.ExistsMonster() {
			g.Scumming = 0
			return
		}
		g.Player.HP = g.Player.HP / 2
		if RandInt(2) == 0 {
			g.MakeNoise(100, g.Player.Pos)
			neighbors := g.Dungeon.Neighbors(g.Player.Pos)
			for _, pos := range neighbors {
				if RandInt(3) != 0 {
					g.Dungeon.SetCell(pos, FreeCell)
				}
			}
			g.Print("You hear a terrible explosion coming from the ground. You are lignified.")
			g.Player.Statuses[StatusLignification]++
			heap.Push(g.Events, &simpleEvent{ERank: ev.Rank() + 240 + RandInt(10), EAction: LignificationEnd})
		} else {
			delay := 20 + RandInt(5)
			g.Player.Statuses[StatusTele]++
			heap.Push(g.Events, &simpleEvent{ERank: ev.Rank() + delay, EAction: Teleportation})
			g.Print("Something hurt you! You feel unstable.")
		}
		g.Scumming = 0
	}
}

func (g *game) FairAction() {
	g.Scumming -= 10
	if g.Scumming < 0 {
		g.Scumming = 0
	}
}

func (g *game) Rest(ev event) error {
	if g.MonsterInLOS() != nil {
		return fmt.Errorf("You cannot sleep while monsters are in view.")
	}
	if g.Player.HP == g.Player.HPMax() && g.Player.MP == g.Player.MPMax() && !g.Player.HasStatus(StatusExhausted) &&
		!g.Player.HasStatus(StatusConfusion) && !g.Player.HasStatus(StatusLignification) {
		return errors.New("You do not need to rest.")
	}
	g.WaitTurn(ev)
	g.Resting = true
	return nil
}

func (g *game) Equip(ev event) error {
	if eq, ok := g.Equipables[g.Player.Pos]; ok {
		eq.Equip(g)
		ev.Renew(g, 10)
		return nil
	}
	return errors.New("Found nothing to equip here.")
}

func (g *game) Teleportation(ev event) {
	var pos position
	i := 0
	count := 0
	for {
		count++
		if count > 1000 {
			panic("Teleportation")
		}
		pos = g.FreeCell()
		if pos.Distance(g.Player.Pos) < 15 && i < 1000 {
			i++
			continue
		}
		break

	}
	g.Player.Statuses[StatusTele]--
	if g.Dungeon.Valid(pos) {
		// should always happen
		g.Player.Pos = pos
		g.Print("You feel yourself teleported away.")
		g.ComputeLOS()
		g.MakeMonstersAware()
	} else {
		g.Print("Something went wrong with the teleportation.")
	}
}

func (g *game) MovePlayer(pos position, ev event) error {
	if !g.Dungeon.Valid(pos) || g.Dungeon.Cell(pos).T == WallCell {
		return errors.New("You cannot move there.")
	}
	if g.Player.HasStatus(StatusConfusion) {
		switch pos.Dir(g.Player.Pos) {
		case E, N, W, S:
		default:
			return errors.New("You cannot use diagonal movements while confused.")
		}
	}
	delay := 10
	switch g.Dungeon.Cell(pos).T {
	case FreeCell:
		mons, _ := g.MonsterAt(pos)
		if !mons.Exists() {
			if g.Player.HasStatus(StatusLignification) {
				return errors.New("You cannot move while lignified")
			}
			g.Player.Pos = pos
			if g.Gold[pos] > 0 {
				g.Player.Gold += g.Gold[pos]
				delete(g.Gold, pos)
			}
			if c, ok := g.Collectables[pos]; ok && c != nil {
				g.Player.Consumables[c.Consumable] += c.Quantity
				delete(g.Collectables, pos)
				if c.Quantity > 1 {
					g.Printf("You take %d %s.", c.Quantity, c.Consumable.Plural())
				} else {
					g.Printf("You take %s.", Indefinite(c.Consumable.String(), false))
				}
			}
			if r, ok := g.Rods[pos]; ok {
				g.Player.Rods[r] = &rodProps{Charge: r.MaxCharge() - 1}
				delete(g.Rods, pos)
				g.Printf("You take a %s.", r)
				g.StoryPrintf("You found and took a %s.", r)
			}
			g.ComputeLOS()
			if g.Autoexploring {
				mons := g.MonsterInLOS()
				if mons.Exists() {
					g.Printf("You see %s (%v).", Indefinite(mons.Kind.String(), false), mons.State)
				}
				g.FairAction()
			} else {
				g.ScummingAction(ev)
			}
			g.MakeMonstersAware()
			if g.Player.Aptitudes[AptFast] {
				// only fast for movement
				delay -= 2
			}
			if g.Player.HasStatus(StatusSwift) {
				// only fast for movement
				delay -= 3
			}
		} else {
			g.FairAction()
			g.AttackMonster(mons)
		}
	}
	if g.Player.HasStatus(StatusBerserk) {
		delay -= 3
	}
	if g.Player.HasStatus(StatusSlow) {
		delay += 3
	}
	ev.Renew(g, delay)
	return nil
}

func (g *game) HealPlayer(ev event) {
	if g.Player.HP < g.Player.HPMax() {
		g.Player.HP++
	}
	delay := 50
	if g.Player.Aptitudes[AptRegen] {
		delay = 25
	}
	ev.Renew(g, delay)
}

func (g *game) MPRegen(ev event) {
	if g.Player.MP < g.Player.MPMax() {
		g.Player.MP++
	}
	delay := 100
	ev.Renew(g, delay)
}
<|endoftext|>


<commit_before>package bench

import (
	"fmt"
	"os/exec"
	"testing"

	"github.com/vitesse-ftian/dggo/vitessedata/ssh"
)

func TestGenData(t *testing.T) {
	conf, err := GetConfig()
	if err != nil {
		t.Errorf("Configuration error: %s", err.Error())
	}

	segs, err := Segs()
	if err != nil {
		t.Errorf("Cannot get deepgreen segs, error: %s.", err.Error())
	}

	t.Run("Step=make", func(t *testing.T) {
		cmd := fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && make", Dir())
		err := exec.Command("bash", "-c", cmd).Run()
		if err != nil {
			t.Errorf("Cannot make dbgen.  error: %s", err.Error())
		}

		cmd = fmt.Sprintf("mkdir -p %s/gen", Dir())
		err = exec.Command("bash", "-c", cmd).Run()
		if err != nil {
			t.Errorf("Cannot create gen dir.  error: %s", err.Error())
		}
	})

	t.Run("Step=dbgen", func(t *testing.T) {
		// We deliberately run dbgen one section by one section, because
		// we only assume there is enough disk space for one section.
		nseg := len(segs)
		for _, seg := range segs {
			t.Logf("DB Seg %d, Addr %s.", seg.Id, seg.Addr)
			cmd := fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && ./dbgen -s %d -C %d -S %d", Dir(), conf.Scale, nseg, seg.Id+1)

			err := exec.Command("bash", "-c", cmd).Run()
			if err != nil {
				t.Errorf("Cannot call dbgen.")
			}

			seghost := []string{seg.Addr}
			datadir := fmt.Sprintf("%s/tpch/scale-%d/seg-%d", conf.Staging, conf.Scale, seg.Id)
			cmd = fmt.Sprintf("rm -fr %s; mkdir -p %s", datadir, datadir)
			if ssh.ExecAnyError(ssh.ExecCmdOn(seghost, cmd)) != nil {
				t.Errorf("Cannot prepare staging data dir %s on host %s.", datadir, seg.Addr)
			}

			if seg.Id == 0 {
				cmd = fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && scp *tbl* %s:%s/", Dir(), seghost, datadir)
			} else {
				cmd = fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && scp *tbl.* %s:%s/", Dir(), seghost, datadir)
			}

			err = exec.Command("bash", "-c", cmd).Run()
			if err != nil {
				t.Errorf("Cannot scp dbgen.")
			}

			cmd = fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && rm -f *tbl*", Dir())
			err = exec.Command("bash", "-c", cmd).Run()
			if err != nil {
				t.Errorf("Cannot clean dbgen.")
			}
		}
	})
}
<commit_msg>Generate data in parallel.<commit_after>package bench

import (
	"fmt"
	"os/exec"
	"testing"

	"github.com/vitesse-ftian/dggo/vitessedata/ssh"
)

func TestGenData(t *testing.T) {
	conf, err := GetConfig()
	if err != nil {
		t.Errorf("Configuration error: %s", err.Error())
	}

	segs, err := Segs()
	if err != nil {
		t.Errorf("Cannot get deepgreen segs, error: %s.", err.Error())
	}

	t.Run("Step=make", func(t *testing.T) {
		cmd := fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && make", Dir())
		err := exec.Command("bash", "-c", cmd).Run()
		if err != nil {
			t.Errorf("Cannot make dbgen.  error: %s", err.Error())
		}

		cmd = fmt.Sprintf("mkdir -p %s/gen", Dir())
		err = exec.Command("bash", "-c", cmd).Run()
		if err != nil {
			t.Errorf("Cannot create gen dir.  error: %s", err.Error())
		}
	})

	t.Run("Step=dbgen", func(t *testing.T) {
		// dbgen is run parallely on each seg,
		nseg := len(segs)
		hosts := make([]string, nseg)
		cmds := make([]string, nseg)

		for i, seg := range segs {
			hosts[i] = seg.Addr
			datadir := fmt.Sprintf("%s/tpch/scale-%d/seg-%d", conf.Staging, conf.Scale, seg.Id)
			cmds[i] = fmt.Sprintf("rm -fr %s; mkdir -p %s", datadir, datadir)
		}

		if ssh.ExecAnyError(ssh.ExecOn(hosts, cmds)) != nil {
			t.Errorf("Cannot prepare staging data dir.")
		}

		for _, seg := range segs {
			datadir := fmt.Sprintf("%s/tpch/scale-%d/seg-%d", conf.Staging, conf.Scale, seg.Id)
			cmd := fmt.Sprintf("cd %s; scp -r tpch_2_15_0 %s:%s/", Dir(), seg.Addr, datadir)
			err := exec.Command("bash", "-c", cmd).Run()
			if err != nil {
				t.Errorf("Cannot scp tpch_2_15_0.")
			}
		}

		for i, seg := range segs {
			datadir := fmt.Sprintf("%s/tpch/scale-%d/seg-%d", conf.Staging, conf.Scale, seg.Id)
			// nation and region table (*tbl files).  should only be copied to dest dir once.
			// other tables are generated in parts (*tbl.*).   Note the .
			if seg.Id == 0 {
				cmds[i] = fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && ./dbgen -s %d -C %d -S %d && mv *tbl* ../..", datadir, conf.Scale, nseg, seg.Id+1)
			} else {
				cmds[i] = fmt.Sprintf("cd %s/tpch_2_15_0/dbgen && ./dbgen -s %d -C %d -S %d && mv *tbl.* ../..", datadir, conf.Scale, nseg, seg.Id+1)
			}
		}

		if ssh.ExecAnyError(ssh.ExecOn(hosts, cmds)) != nil {
			t.Errorf("Cannot generate data.")
		}
	})
}
<|endoftext|>


<commit_before>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcs

import (
	"crypto/md5"
	"fmt"
	"io"
)

// A request to create an object, accepted by Bucket.CreateObject.
type CreateObjectRequest struct {
	// The name with which to create the object. This field must be set.
	//
	// Object names must:
	//
	// *  be non-empty.
	// *  be no longer than 1024 bytes.
	// *  be valid UTF-8.
	// *  not contain the code point U+000A (line feed).
	// *  not contain the code point U+000D (carriage return).
	//
	// See here for authoritative documentation:
	//     https://cloud.google.com/storage/docs/bucket-naming#objectnames
	Name string

	// Optional information with which to create the object. See here for more
	// information:
	//
	//     https://cloud.google.com/storage/docs/json_api/v1/objects#resource
	//
	ContentType     string
	ContentLanguage string
	ContentEncoding string
	CacheControl    string
	Metadata        map[string]string

	// A reader from which to obtain the contents of the object. Must be non-nil.
	Contents io.Reader

	// If non-nil, the object will not be created if the checksum of the received
	// contents does not match the supplied value.
	CRC32C *uint32

	// If non-nil, the object will not be created if the MD5 sum of the received
	// contents does not match the supplied value.
	MD5 *[md5.Size]byte

	// If non-nil, the object will be created/overwritten only if the current
	// generation for the object name is equal to the given value. Zero means the
	// object does not exist.
	GenerationPrecondition *int64
}

// A request to copy an object to a new name, preserving all metadata.
type CopyObjectRequest struct {
	SrcName string
	DstName string
}

// A [start, limit) range of bytes within an object.
//
// Semantics:
//
//  *  If Limit is less than or equal to Start, the range is treated as empty.
//
//  *  If Limit is greater than the length of the object, the range is
//     implicitly truncated.
//
type ByteRange struct {
	Start uint64
	Limit uint64
}

func (br ByteRange) String() string {
	return fmt.Sprintf("[%d, %d)", br.Start, br.Limit)
}

// A request to read the contents of an object at a particular generation.
type ReadObjectRequest struct {
	// The name of the object to read.
	Name string

	// The generation of the object to read. Zero means the latest generation.
	Generation int64

	// If present, limit the contents returned to a range within the object.
	Range *ByteRange
}

type StatObjectRequest struct {
	// The name of the object in question.
	Name string
}

type ListObjectsRequest struct {
	// List only objects whose names begin with this prefix.
	Prefix string

	// Collapse results based on a delimiter.
	//
	// If non-empty, enable the following behavior. For each run of one or more
	// objects whose names are of the form:
	//
	//     <Prefix><S><Delimiter><...>
	//
	// where <S> is a string that doesn't itself contain Delimiter and <...> is
	// anything, return a single Collaped entry in the listing consisting of
	//
	//     <Prefix><S><Delimiter>
	//
	// instead of one Object record per object. If a collapsed entry consists of
	// a large number of objects, this may be more efficient.
	Delimiter string

	// Used to continue a listing where a previous one left off. See
	// Listing.ContinuationToken for more information.
	ContinuationToken string

	// The maximum number of objects and collapsed runs to return. Fewer than
	// this number may actually be returned. If this is zero, a sensible default
	// is used.
	MaxResults int
}

// A set of objects and delimter-based collapsed runs returned by a call to
// ListObjects. See also ListObjectsRequest.
type Listing struct {
	// Records for objects matching the listing criteria.
	//
	// Guaranteed to be strictly increasing under a lexicographical comparison on
	// (name, generation) pairs.
	Objects []*Object

	// Collapsed entries for runs of names sharing a prefix followed by a
	// delimiter. See notes on ListObjectsRequest.Delimiter.
	//
	// Guaranteed to be strictly increasing.
	CollapsedRuns []string

	// A continuation token, for fetching more results.
	//
	// If non-empty, this listing does not represent the full set of matching
	// objects in the bucket. Call ListObjects again with the request's
	// ContinuationToken field set to this value to continue where you left off.
	//
	// Guarantees, for replies R1 and R2, with R2 continuing from R1:
	//
	//  *  All of R1's object names are strictly less than all object names and
	//     collapsed runs in R2.
	//
	//  *  All of R1's collapsed runs are strictly less than all object names and
	//     prefixes in R2.
	//
	// (Cf. Google-internal bug 19286144)
	//
	// Note that there is no guarantee of atomicity of listings. Objects written
	// and deleted concurrently with a single or multiple listing requests may or
	// may not be returned.
	ContinuationToken string
}

// A request to update the metadata of an object, accepted by
// Bucket.UpdateObject.
type UpdateObjectRequest struct {
	// The name of the object to update. Must be specified.
	Name string

	// String fields in the object to update (or not). The semantics are as
	// follows, for a given field F:
	//
	//  *  If F is set to nil, the corresponding GCS object field is untouched.
	//
	//  *  If *F is the empty string, then the corresponding GCS object field is
	//     removed.
	//
	//  *  Otherwise, the corresponding GCS object field is set to *F.
	//
	//  *  There is no facility for setting a GCS object field to the empty
	//     string, since many of the fields do not actually allow that as a legal
	//     value.
	//
	// Note that the GCS object's content type field cannot be removed.
	ContentType     *string
	ContentEncoding *string
	ContentLanguage *string
	CacheControl    *string

	// User-provided metadata updates. Keys that are not mentioned are untouched.
	// Keys whose values are nil are deleted, and others are updated to the
	// supplied string. There is no facility for completely removing user
	// metadata.
	Metadata map[string]*string
}
<commit_msg>Added ComposeObjectsRequest.<commit_after>// Copyright 2015 Google Inc. All Rights Reserved.
//
// 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 gcs

import (
	"crypto/md5"
	"fmt"
	"io"
)

// A request to create an object, accepted by Bucket.CreateObject.
type CreateObjectRequest struct {
	// The name with which to create the object. This field must be set.
	//
	// Object names must:
	//
	// *  be non-empty.
	// *  be no longer than 1024 bytes.
	// *  be valid UTF-8.
	// *  not contain the code point U+000A (line feed).
	// *  not contain the code point U+000D (carriage return).
	//
	// See here for authoritative documentation:
	//     https://cloud.google.com/storage/docs/bucket-naming#objectnames
	Name string

	// Optional information with which to create the object. See here for more
	// information:
	//
	//     https://cloud.google.com/storage/docs/json_api/v1/objects#resource
	//
	ContentType     string
	ContentLanguage string
	ContentEncoding string
	CacheControl    string
	Metadata        map[string]string

	// A reader from which to obtain the contents of the object. Must be non-nil.
	Contents io.Reader

	// If non-nil, the object will not be created if the checksum of the received
	// contents does not match the supplied value.
	CRC32C *uint32

	// If non-nil, the object will not be created if the MD5 sum of the received
	// contents does not match the supplied value.
	MD5 *[md5.Size]byte

	// If non-nil, the object will be created/overwritten only if the current
	// generation for the object name is equal to the given value. Zero means the
	// object does not exist.
	GenerationPrecondition *int64
}

// A request to copy an object to a new name, preserving all metadata.
type CopyObjectRequest struct {
	SrcName string
	DstName string
}

// A request to compose multiple objects into a single composite object.
type ComposeObjectsRequest struct {
	// The name of the destination composite object.
	DstName string

	// If non-nil, the destination object will be created/overwritten only if the
	// current generation for its name is equal to the given value. Zero means
	// the object does not exist.
	DstGenerationPrecondition *int64

	// The source objects from which to compose.
	Sources []ComposeSource
}

type ComposeSource struct {
	// The name of the source object.
	Name string

	// The generation of the source object to compose from. Zero means the latest
	// generation.
	Generation int64
}

// A [start, limit) range of bytes within an object.
//
// Semantics:
//
//  *  If Limit is less than or equal to Start, the range is treated as empty.
//
//  *  If Limit is greater than the length of the object, the range is
//     implicitly truncated.
//
type ByteRange struct {
	Start uint64
	Limit uint64
}

func (br ByteRange) String() string {
	return fmt.Sprintf("[%d, %d)", br.Start, br.Limit)
}

// A request to read the contents of an object at a particular generation.
type ReadObjectRequest struct {
	// The name of the object to read.
	Name string

	// The generation of the object to read. Zero means the latest generation.
	Generation int64

	// If present, limit the contents returned to a range within the object.
	Range *ByteRange
}

type StatObjectRequest struct {
	// The name of the object in question.
	Name string
}

type ListObjectsRequest struct {
	// List only objects whose names begin with this prefix.
	Prefix string

	// Collapse results based on a delimiter.
	//
	// If non-empty, enable the following behavior. For each run of one or more
	// objects whose names are of the form:
	//
	//     <Prefix><S><Delimiter><...>
	//
	// where <S> is a string that doesn't itself contain Delimiter and <...> is
	// anything, return a single Collaped entry in the listing consisting of
	//
	//     <Prefix><S><Delimiter>
	//
	// instead of one Object record per object. If a collapsed entry consists of
	// a large number of objects, this may be more efficient.
	Delimiter string

	// Used to continue a listing where a previous one left off. See
	// Listing.ContinuationToken for more information.
	ContinuationToken string

	// The maximum number of objects and collapsed runs to return. Fewer than
	// this number may actually be returned. If this is zero, a sensible default
	// is used.
	MaxResults int
}

// A set of objects and delimter-based collapsed runs returned by a call to
// ListObjects. See also ListObjectsRequest.
type Listing struct {
	// Records for objects matching the listing criteria.
	//
	// Guaranteed to be strictly increasing under a lexicographical comparison on
	// (name, generation) pairs.
	Objects []*Object

	// Collapsed entries for runs of names sharing a prefix followed by a
	// delimiter. See notes on ListObjectsRequest.Delimiter.
	//
	// Guaranteed to be strictly increasing.
	CollapsedRuns []string

	// A continuation token, for fetching more results.
	//
	// If non-empty, this listing does not represent the full set of matching
	// objects in the bucket. Call ListObjects again with the request's
	// ContinuationToken field set to this value to continue where you left off.
	//
	// Guarantees, for replies R1 and R2, with R2 continuing from R1:
	//
	//  *  All of R1's object names are strictly less than all object names and
	//     collapsed runs in R2.
	//
	//  *  All of R1's collapsed runs are strictly less than all object names and
	//     prefixes in R2.
	//
	// (Cf. Google-internal bug 19286144)
	//
	// Note that there is no guarantee of atomicity of listings. Objects written
	// and deleted concurrently with a single or multiple listing requests may or
	// may not be returned.
	ContinuationToken string
}

// A request to update the metadata of an object, accepted by
// Bucket.UpdateObject.
type UpdateObjectRequest struct {
	// The name of the object to update. Must be specified.
	Name string

	// String fields in the object to update (or not). The semantics are as
	// follows, for a given field F:
	//
	//  *  If F is set to nil, the corresponding GCS object field is untouched.
	//
	//  *  If *F is the empty string, then the corresponding GCS object field is
	//     removed.
	//
	//  *  Otherwise, the corresponding GCS object field is set to *F.
	//
	//  *  There is no facility for setting a GCS object field to the empty
	//     string, since many of the fields do not actually allow that as a legal
	//     value.
	//
	// Note that the GCS object's content type field cannot be removed.
	ContentType     *string
	ContentEncoding *string
	ContentLanguage *string
	CacheControl    *string

	// User-provided metadata updates. Keys that are not mentioned are untouched.
	// Keys whose values are nil are deleted, and others are updated to the
	// supplied string. There is no facility for completely removing user
	// metadata.
	Metadata map[string]*string
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-zigbee"
	"github.com/ninjasphere/go-zigbee/gateway"
	"github.com/ninjasphere/go-zigbee/nwkmgr"
)

const (
	ClusterIDBasic    uint32 = 0x00
	ClusterIDOnOff    uint32 = 0x06
	ClusterIDTemp     uint32 = 0x402
	ClusterIDHumidity uint32 = 0x405
	ClusterIDPower    uint32 = 0x702
)

var (
	info = ninja.LoadModuleInfo("./package.json")
)

type ZStackConfig struct {
	Hostname       string
	OtasrvrPort    int
	GatewayPort    int
	NwkmgrPort     int
	StableFlagFile string
}

type DriverConfig struct {
}

type Driver struct {
	conn      *ninja.Connection
	sendEvent func(event string, payload interface{}) error

	devices map[uint64]*Device

	NetworkReady chan bool

	nwkmgrConn  *zigbee.ZStackNwkMgr
	gatewayConn *zigbee.ZStackGateway
	otaConn     *zigbee.ZStackOta
	config      *DriverConfig
}

func NewDriver() *Driver {
	return &Driver{
		NetworkReady: make(chan bool),
	}
}

type Device struct {
	info       *model.Device
	driver     *Driver
	deviceInfo *nwkmgr.NwkDeviceInfoT
	channels   []Channel
	sendEvent  func(event string, payload interface{}) error
}

type Channel struct {
	device   *Device
	endpoint *nwkmgr.NwkSimpleDescriptorT
}

func (d *Driver) GetModuleInfo() *model.Module {
	return info
}

func (d *Driver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Driver) Reset(hard bool) error {
	return d.nwkmgrConn.Reset(hard)
}

func (d *Driver) Start(config *DriverConfig) error {
	d.config = config
	return d.sendEvent("config", config)
}

func (d *Driver) Stop() error {
	return fmt.Errorf("This driver does not support being stopped. YOU HAVE NO POWER HERE.")
}

func (d *Driver) StartPairing(period uint32) (*uint32, error) {
	err := d.PermitJoin(uint32(period))
	if err != nil {
		err = fmt.Errorf("permit Join failed: %v", err)
		log.Fatalf("%s", err)
	}
	return &period, err
}

func (d *Driver) PermitJoin(period uint32) error {

	log.Printf("Join window will open for %d seconds.", period)

	//joinTime := uint32(30)
	permitJoinRequest := &nwkmgr.NwkSetPermitJoinReq{
		PermitJoinTime: &period,
		PermitJoin:     nwkmgr.NwkPermitJoinTypeT_PERMIT_ALL.Enum(),
	}

	permitJoinResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	go func() {
		// logging the close of the join window is helpful when debugging
		// join behaviour.
		time.Sleep(time.Duration(period) * time.Second)
		log.Printf("Join window has closed after %d seconds.", period)
	}()
	err := d.nwkmgrConn.SendCommand(permitJoinRequest, permitJoinResponse)
	if err != nil {
		return fmt.Errorf("Failed to enable joining: %s", err)
	}
	if permitJoinResponse.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to enable joining: %s", permitJoinResponse.Status)
	}
	//log.Println("Permit join response: ")
	//outJSON(permitJoinResponse)

	return nil
}

func (d *Driver) Connect(cfg *ZStackConfig, networkReady chan bool) error {

	waitUntilZStackReady(config.StableFlagFile)

	d.devices = make(map[uint64]*Device)

	conn, err := ninja.Connect("com.ninjablocks.zigbee")
	if err != nil {
		return fmt.Errorf("Could not connect to MQTT: %s", err)
	}

	d.conn = conn

	err = conn.ExportDriver(d)
	if err != nil {
		return fmt.Errorf("Could not export driver: %s", err)
	}

	d.nwkmgrConn, err = zigbee.ConnectToNwkMgrServer(cfg.Hostname, cfg.NwkmgrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to nwkmgr %s", err)
	}
	d.nwkmgrConn.OnDeviceFound = func(deviceInfo *nwkmgr.NwkDeviceInfoT) {
		d.onDeviceFound(deviceInfo)
	}

	done := false
	d.nwkmgrConn.OnNetworkReady = func() {
		if !done {
			done = true
			networkReady <- true
		}
	}

	d.otaConn, err = zigbee.ConnectToOtaServer(cfg.Hostname, cfg.OtasrvrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to ota server %s", err)
	}

	d.gatewayConn, err = zigbee.ConnectToGatewayServer(cfg.Hostname, cfg.GatewayPort)
	if err != nil {
		return fmt.Errorf("Error connecting to gateway %s", err)
	}

	/*keyResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	keyRequest := &nwkmgr.NwkChangeNwkKeyReq{
		NewKey: []byte{0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39},
	}

	err = d.nwkmgrConn.SendCommand(keyRequest, keyResponse)
	if err != nil {
		return fmt.Errorf("Failed setting network key: %s", err)
	}

	spew.Dump(keyResponse)
	log.Println("Sleeping for 10 seconds after setting network key")

	time.Sleep(10 * time.Second)*/

	networkInfo := &nwkmgr.NwkZigbeeNwkInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkZigbeeNwkInfoReq{}, networkInfo)
	if err != nil {
		return fmt.Errorf("Failed getting network info: %s", err)
	}

	if *networkInfo.Status == nwkmgr.NwkNetworkStatusT_NWK_DOWN {
		return fmt.Errorf("The ZigBee network is down")
	}

	localDevice := &nwkmgr.NwkGetLocalDeviceInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetLocalDeviceInfoReq{}, localDevice)
	if err != nil {
		return fmt.Errorf("Failed getting local device info: %s", err)
	}

	spew.Dump("device info", localDevice.String())

	networkKey := &nwkmgr.NwkGetNwkKeyCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetNwkKeyReq{}, networkKey)
	if err != nil {
		return fmt.Errorf("Failed getting network key: %s", err)
	}

	spew.Dump(networkKey)

	log.Printf("Started coordinator. Channel:%d Pan ID:0x%X Key:% X", *networkInfo.NwkChannel, *networkInfo.PanId, networkKey.NewKey)

	return nil
}

func (d *Driver) FetchDevices() error {

	return d.nwkmgrConn.FetchDeviceList()

}

func (d *Device) GetDriver() ninja.Driver {
	return d.driver
}

func (d *Device) GetDeviceInfo() *model.Device {
	return d.info
}

func (d *Device) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Device) getBasicInfo() error {

	log.Printf("Getting basic information from %X", *d.deviceInfo.IeeeAddress)

	cluster := ClusterIDBasic
	ManufacturerNameAttribute := uint32(0x004)
	ModelIdentifierAttribute := uint32(0x005)

	request := &gateway.GwReadDeviceAttributeReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    d.deviceInfo.IeeeAddress,
		},
		ClusterId:     &cluster,
		AttributeList: []uint32{ManufacturerNameAttribute, ModelIdentifierAttribute},
	}

	response := &gateway.GwReadDeviceAttributeRspInd{}
	err := d.driver.gatewayConn.SendAsyncCommand(request, response, 10*time.Second)
	if err != nil {
		return fmt.Errorf("Error getting basic device information state : %s", err)
	}
	if response.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to get basic device information. status: %s", response.Status.String())
	}

	sigs := make(map[string]string)

	manufacturerName := ""
	modelIdentifier := ""

	for _, attribute := range response.AttributeRecordList {

		switch *attribute.AttributeId {
		case ManufacturerNameAttribute:
			manufacturerName = string(attribute.AttributeValue)
		case ModelIdentifierAttribute:
			modelIdentifier = string(attribute.AttributeValue)
		default:
			log.Printf("Unknown attribute returned when finding basic info %s", *attribute.AttributeId)
		}
	}

	sigs["zigbee:ManufacturerName"] = manufacturerName
	sigs["zigbee:ModelIdentifier"] = modelIdentifier

	name := fmt.Sprintf("%s by %s", modelIdentifier, manufacturerName)
	id := fmt.Sprintf("%X", *d.deviceInfo.IeeeAddress)

	d.info.Name = &name

	d.info.Signatures = &sigs
	d.info.NaturalIDType = "zigbee"
	d.info.NaturalID = id
	d.info.Name = &name

	return nil
}

func (d *Driver) onDeviceFound(deviceInfo *nwkmgr.NwkDeviceInfoT) {

	fmt.Printf("\n\n")
	fmt.Printf("---- Found Device IEEE:%X ----\f", *deviceInfo.IeeeAddress)

	if d.devices[*deviceInfo.IeeeAddress] != nil {
		fmt.Printf("We've already seen this device")
	}

	/*sigs, _ := simplejson.NewJson([]byte(`{
	    "ninja:manufacturer": "Phillips",
	    "ninja:productName": "Hue",
	    "manufacturer:productModelId": "",
	    "ninja:productType": "Light",
	    "ninja:thingType": "light"
	}`))*/

	device := &Device{
		info:       &model.Device{},
		driver:     d,
		deviceInfo: deviceInfo,
	}

	err := device.getBasicInfo()
	if err != nil {
		log.Printf("Failed to get basic info: %s", err)
		return
	}

	spew.Dump(deviceInfo)

	err = d.conn.ExportDevice(device)

	log.Printf("Got device : %d", *deviceInfo.IeeeAddress)

	for _, endpoint := range deviceInfo.SimpleDescList {
		log.Printf("Got endpoint : %d", *endpoint.EndpointId)

		if containsUInt32(endpoint.InputClusters, ClusterIDOnOff) {
			log.Printf("This endpoint has on/off cluster")

			onOff := &OnOffChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := onOff.init()
			if err != nil {
				log.Printf("Failed initialising on/off channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDPower) {
			log.Printf("This endpoint has power cluster")

			power := &PowerChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := power.init()
			if err != nil {
				log.Printf("Failed initialising power channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDTemp) {
			log.Printf("This endpoint has temperature cluster")

			temp := &TempChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := temp.init()
			if err != nil {
				log.Printf("Failed initialising temp channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDHumidity) {
			log.Printf("This endpoint has humidity cluster")

			humidity := &HumidityChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := humidity.init()
			if err != nil {
				log.Printf("Failed initialising humidity channel: %s", err)
			}

		}

	}

	d.devices[*deviceInfo.IeeeAddress] = device

	fmt.Printf("---- Finished Device IEEE:%X ----\n", *deviceInfo.IeeeAddress)

}

func getCurDir() string {
	pwd, _ := os.Getwd()
	return pwd + "/"
}

func containsUInt32(hackstack []uint32, needle uint32) bool {
	for _, cluster := range hackstack {
		if cluster == needle {
			return true
		}
	}
	return false
}

func waitUntilZStackReady(checkFile string) {
	if checkFile == "" {
		return
	}

	// cooperate with zigbeeHAgw so that we don't start the zigbee driver
	// until we look somewhat stable.
	log.Printf("waiting until zigbeeHAgw writes %s", checkFile)
	for {
		if _, err := os.Stat(checkFile); err == nil {
			break
		} else {
			time.Sleep(time.Second)
		}
	}
	log.Printf("%s detected. start up continues...", checkFile)
}
<commit_msg>Subscribe to pairing-requested events generated by user-agent channels.<commit_after>package main

import (
	"fmt"
	"log"
	"os"
	"time"

	"github.com/davecgh/go-spew/spew"
	"github.com/ninjasphere/go-ninja/api"
	"github.com/ninjasphere/go-ninja/model"
	"github.com/ninjasphere/go-ninja/events"
	"github.com/ninjasphere/go-zigbee"
	"github.com/ninjasphere/go-zigbee/gateway"
	"github.com/ninjasphere/go-zigbee/nwkmgr"
)

const (
	ClusterIDBasic    uint32 = 0x00
	ClusterIDOnOff    uint32 = 0x06
	ClusterIDTemp     uint32 = 0x402
	ClusterIDHumidity uint32 = 0x405
	ClusterIDPower    uint32 = 0x702
)

var (
	info = ninja.LoadModuleInfo("./package.json")
)

type ZStackConfig struct {
	Hostname       string
	OtasrvrPort    int
	GatewayPort    int
	NwkmgrPort     int
	StableFlagFile string
}

type DriverConfig struct {
}

type Driver struct {
	conn      *ninja.Connection
	userAgent *ninja.ServiceClient
	sendEvent func(event string, payload interface{}) error

	devices map[uint64]*Device

	NetworkReady chan bool

	nwkmgrConn  *zigbee.ZStackNwkMgr
	gatewayConn *zigbee.ZStackGateway
	otaConn     *zigbee.ZStackOta
	config      *DriverConfig
}

func NewDriver() *Driver {
	return &Driver{
		NetworkReady: make(chan bool),
	}
}

type Device struct {
	info       *model.Device
	driver     *Driver
	deviceInfo *nwkmgr.NwkDeviceInfoT
	channels   []Channel
	sendEvent  func(event string, payload interface{}) error
}

type Channel struct {
	device   *Device
	endpoint *nwkmgr.NwkSimpleDescriptorT
}

func (d *Driver) GetModuleInfo() *model.Module {
	return info
}

func (d *Driver) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Driver) Reset(hard bool) error {
	return d.nwkmgrConn.Reset(hard)
}

func (d *Driver) Start(config *DriverConfig) error {
	d.config = config
	return d.sendEvent("config", config)
}

func (d *Driver) Stop() error {
	return fmt.Errorf("This driver does not support being stopped. YOU HAVE NO POWER HERE.")
}

func (d *Driver) StartPairing(period uint32) (*uint32, error) {
	err := d.PermitJoin(uint32(period))
	if err != nil {
		err = fmt.Errorf("permit Join failed: %v", err)
		log.Fatalf("%s", err)
	}
	return &period, err
}

func (d *Driver) OnPairingRequest(req *events.PairingRequest, topicKeys map[string]string) bool {
     log.Printf("Pairing request of %d seconds received from %s", req.Duration, topicKeys["deviceId"])
     d.PermitJoin(uint32(req.Duration))
     return true
}

func (d *Driver) PermitJoin(period uint32) error {

	log.Printf("Join window will open for %d seconds.", period)

	//joinTime := uint32(30)
	permitJoinRequest := &nwkmgr.NwkSetPermitJoinReq{
		PermitJoinTime: &period,
		PermitJoin:     nwkmgr.NwkPermitJoinTypeT_PERMIT_ALL.Enum(),
	}

	permitJoinResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	go func() {
		// logging the close of the join window is helpful when debugging
		// join behaviour.
		time.Sleep(time.Duration(period) * time.Second)
		log.Printf("Join window has closed after %d seconds.", period)
	}()
	err := d.nwkmgrConn.SendCommand(permitJoinRequest, permitJoinResponse)
	if err != nil {
		return fmt.Errorf("Failed to enable joining: %s", err)
	}
	if permitJoinResponse.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to enable joining: %s", permitJoinResponse.Status)
	}
	//log.Println("Permit join response: ")
	//outJSON(permitJoinResponse)

	return nil
}

func (d *Driver) Connect(cfg *ZStackConfig, networkReady chan bool) error {

	waitUntilZStackReady(config.StableFlagFile)

	d.devices = make(map[uint64]*Device)

	conn, err := ninja.Connect("com.ninjablocks.zigbee")
	if err != nil {
		return fmt.Errorf("Could not connect to MQTT: %s", err)
	}

	d.userAgent = conn.GetServiceClient("$device/:deviceId/channel/user-agent")
	err = d.userAgent.OnUnmarshalledEvent("pairing-requested", d.OnPairingRequest)
	if err != nil {
	       return fmt.Errorf("Failed register user-agent service client: %s", err)
	}

	err = conn.ExportDriver(d)
	if err != nil {
		return fmt.Errorf("Could not export driver: %s", err)
	}

	d.nwkmgrConn, err = zigbee.ConnectToNwkMgrServer(cfg.Hostname, cfg.NwkmgrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to nwkmgr %s", err)
	}
	d.nwkmgrConn.OnDeviceFound = func(deviceInfo *nwkmgr.NwkDeviceInfoT) {
		d.onDeviceFound(deviceInfo)
	}

	done := false
	d.nwkmgrConn.OnNetworkReady = func() {
		if !done {
			done = true
			networkReady <- true
		}
	}

	d.otaConn, err = zigbee.ConnectToOtaServer(cfg.Hostname, cfg.OtasrvrPort)
	if err != nil {
		return fmt.Errorf("Error connecting to ota server %s", err)
	}

	d.gatewayConn, err = zigbee.ConnectToGatewayServer(cfg.Hostname, cfg.GatewayPort)
	if err != nil {
		return fmt.Errorf("Error connecting to gateway %s", err)
	}

	/*keyResponse := &nwkmgr.NwkZigbeeGenericCnf{}

	keyRequest := &nwkmgr.NwkChangeNwkKeyReq{
		NewKey: []byte{0x5a, 0x69, 0x67, 0x42, 0x65, 0x65, 0x41, 0x6c, 0x6c, 0x69, 0x61, 0x6e, 0x63, 0x65, 0x30, 0x39},
	}

	err = d.nwkmgrConn.SendCommand(keyRequest, keyResponse)
	if err != nil {
		return fmt.Errorf("Failed setting network key: %s", err)
	}

	spew.Dump(keyResponse)
	log.Println("Sleeping for 10 seconds after setting network key")

	time.Sleep(10 * time.Second)*/

	networkInfo := &nwkmgr.NwkZigbeeNwkInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkZigbeeNwkInfoReq{}, networkInfo)
	if err != nil {
		return fmt.Errorf("Failed getting network info: %s", err)
	}

	if *networkInfo.Status == nwkmgr.NwkNetworkStatusT_NWK_DOWN {
		return fmt.Errorf("The ZigBee network is down")
	}

	localDevice := &nwkmgr.NwkGetLocalDeviceInfoCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetLocalDeviceInfoReq{}, localDevice)
	if err != nil {
		return fmt.Errorf("Failed getting local device info: %s", err)
	}

	spew.Dump("device info", localDevice.String())

	networkKey := &nwkmgr.NwkGetNwkKeyCnf{}

	err = d.nwkmgrConn.SendCommand(&nwkmgr.NwkGetNwkKeyReq{}, networkKey)
	if err != nil {
		return fmt.Errorf("Failed getting network key: %s", err)
	}

	spew.Dump(networkKey)

	log.Printf("Started coordinator. Channel:%d Pan ID:0x%X Key:% X", *networkInfo.NwkChannel, *networkInfo.PanId, networkKey.NewKey)

	return nil
}

func (d *Driver) FetchDevices() error {

	return d.nwkmgrConn.FetchDeviceList()

}

func (d *Device) GetDriver() ninja.Driver {
	return d.driver
}

func (d *Device) GetDeviceInfo() *model.Device {
	return d.info
}

func (d *Device) SetEventHandler(sendEvent func(event string, payload interface{}) error) {
	d.sendEvent = sendEvent
}

func (d *Device) getBasicInfo() error {

	log.Printf("Getting basic information from %X", *d.deviceInfo.IeeeAddress)

	cluster := ClusterIDBasic
	ManufacturerNameAttribute := uint32(0x004)
	ModelIdentifierAttribute := uint32(0x005)

	request := &gateway.GwReadDeviceAttributeReq{
		DstAddress: &gateway.GwAddressStructT{
			AddressType: gateway.GwAddressTypeT_UNICAST.Enum(),
			IeeeAddr:    d.deviceInfo.IeeeAddress,
		},
		ClusterId:     &cluster,
		AttributeList: []uint32{ManufacturerNameAttribute, ModelIdentifierAttribute},
	}

	response := &gateway.GwReadDeviceAttributeRspInd{}
	err := d.driver.gatewayConn.SendAsyncCommand(request, response, 10*time.Second)
	if err != nil {
		return fmt.Errorf("Error getting basic device information state : %s", err)
	}
	if response.Status.String() != "STATUS_SUCCESS" {
		return fmt.Errorf("Failed to get basic device information. status: %s", response.Status.String())
	}

	sigs := make(map[string]string)

	manufacturerName := ""
	modelIdentifier := ""

	for _, attribute := range response.AttributeRecordList {

		switch *attribute.AttributeId {
		case ManufacturerNameAttribute:
			manufacturerName = string(attribute.AttributeValue)
		case ModelIdentifierAttribute:
			modelIdentifier = string(attribute.AttributeValue)
		default:
			log.Printf("Unknown attribute returned when finding basic info %s", *attribute.AttributeId)
		}
	}

	sigs["zigbee:ManufacturerName"] = manufacturerName
	sigs["zigbee:ModelIdentifier"] = modelIdentifier

	name := fmt.Sprintf("%s by %s", modelIdentifier, manufacturerName)
	id := fmt.Sprintf("%X", *d.deviceInfo.IeeeAddress)

	d.info.Name = &name

	d.info.Signatures = &sigs
	d.info.NaturalIDType = "zigbee"
	d.info.NaturalID = id
	d.info.Name = &name

	return nil
}

func (d *Driver) onDeviceFound(deviceInfo *nwkmgr.NwkDeviceInfoT) {

	fmt.Printf("\n\n")
	fmt.Printf("---- Found Device IEEE:%X ----\f", *deviceInfo.IeeeAddress)

	if d.devices[*deviceInfo.IeeeAddress] != nil {
		fmt.Printf("We've already seen this device")
	}

	/*sigs, _ := simplejson.NewJson([]byte(`{
	    "ninja:manufacturer": "Phillips",
	    "ninja:productName": "Hue",
	    "manufacturer:productModelId": "",
	    "ninja:productType": "Light",
	    "ninja:thingType": "light"
	}`))*/

	device := &Device{
		info:       &model.Device{},
		driver:     d,
		deviceInfo: deviceInfo,
	}

	err := device.getBasicInfo()
	if err != nil {
		log.Printf("Failed to get basic info: %s", err)
		return
	}

	spew.Dump(deviceInfo)

	err = d.conn.ExportDevice(device)

	log.Printf("Got device : %d", *deviceInfo.IeeeAddress)

	for _, endpoint := range deviceInfo.SimpleDescList {
		log.Printf("Got endpoint : %d", *endpoint.EndpointId)

		if containsUInt32(endpoint.InputClusters, ClusterIDOnOff) {
			log.Printf("This endpoint has on/off cluster")

			onOff := &OnOffChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := onOff.init()
			if err != nil {
				log.Printf("Failed initialising on/off channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDPower) {
			log.Printf("This endpoint has power cluster")

			power := &PowerChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := power.init()
			if err != nil {
				log.Printf("Failed initialising power channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDTemp) {
			log.Printf("This endpoint has temperature cluster")

			temp := &TempChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := temp.init()
			if err != nil {
				log.Printf("Failed initialising temp channel: %s", err)
			}

		}

		if containsUInt32(endpoint.InputClusters, ClusterIDHumidity) {
			log.Printf("This endpoint has humidity cluster")

			humidity := &HumidityChannel{
				Channel{
					device:   device,
					endpoint: endpoint,
				},
				nil,
			}

			err := humidity.init()
			if err != nil {
				log.Printf("Failed initialising humidity channel: %s", err)
			}

		}

	}

	d.devices[*deviceInfo.IeeeAddress] = device

	fmt.Printf("---- Finished Device IEEE:%X ----\n", *deviceInfo.IeeeAddress)

}

func getCurDir() string {
	pwd, _ := os.Getwd()
	return pwd + "/"
}

func containsUInt32(hackstack []uint32, needle uint32) bool {
	for _, cluster := range hackstack {
		if cluster == needle {
			return true
		}
	}
	return false
}

func waitUntilZStackReady(checkFile string) {
	if checkFile == "" {
		return
	}

	// cooperate with zigbeeHAgw so that we don't start the zigbee driver
	// until we look somewhat stable.
	log.Printf("waiting until zigbeeHAgw writes %s", checkFile)
	for {
		if _, err := os.Stat(checkFile); err == nil {
			break
		} else {
			time.Sleep(time.Second)
		}
	}
	log.Printf("%s detected. start up continues...", checkFile)
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"

	"github.com/cloudfoundry/cli/plugin"
	"github.com/tcnksm/go-input"
)

// Exit codes are int values that represent an exit code
// for a particular error.
const (
	ExitCodeOK    int = 0
	ExitCodeError int = 1 + iota
)

// EnvDebug is environmental variable for enabling debug
// output
const EnvDebug = "DEBUG_PLUGIN"

const (
	// Dockerfile is file name of Dockerfile
	Dockerfile = "Dockerfile"
)

// dockerfileText is used for build docker image for target application.
var dockerfileText = `FROM tcnksm/cf-buildstep:latest
ENV USER vcap
ADD . /app
RUN /build/builder
CMD /start web`

// Debugf prints debug output when EnvDebug is given
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		fmt.Fprintf(os.Stdout, "[DEBUG] "+format+"\n", args...)
	}
}

// LocalPush
type LocalPush struct {
	OutStream io.Writer
	InStream  io.Reader
}

// Run starts plugin process.
func (p *LocalPush) Run(cliConn plugin.CliConnection, arg []string) {
	Debugf("Run local-push plugin")
	Debugf("Arg: %#v", arg)

	// Ensure local-push is called.
	// Plugin is also called when install/uninstall via cf command.
	// Ignore such other calls.
	if len(arg) < 1 || arg[0] != Name {
		os.Exit(ExitCodeOK)
	}

	// Read CLI context.
	ctx, err := NewCLIContext(cliConn)
	if err != nil {
		fmt.Fprintf(p.OutStream, "Failed to read cf command context: %s\n", err)
		os.Exit(ExitCodeError)
	}

	// Call run instead of doing the work here so we can use
	// `defer` statements within the function and have them work properly.
	// (defers aren't called with os.Exit)
	os.Exit(p.run(ctx, arg))
}

// run runs local-push.
func (p *LocalPush) run(ctx *CLIContext, args []string) int {

	ui := &input.UI{
		Writer: p.OutStream,
		Reader: p.InStream,
	}

	// Check docker is installed or not.
	if _, err := exec.LookPath("docker"); err != nil {
		fmt.Fprintf(p.OutStream, "docker command is not found in your $PATH. Install it before.\n")
		return ExitCodeError
	}

	// Check Dockerfile is exist or not
	if _, err := os.Stat(Dockerfile); !os.IsNotExist(err) {
		fmt.Fprintf(p.OutStream, "Dockerfile is already exist\n")
		query := "Overwrite Dockerfile? [yN]"
		ans, err := ui.Ask(query, &input.Options{
			Default:     "N",
			HideDefault: true,
			HideOrder:   true,
			Required:    true,
			Loop:        true,
			ValidateFunc: func(s string) error {
				if s != "y" && s != "N" {
					return fmt.Errorf("input must be 'y' or 'N'")
				}
				return nil
			},
		})

		if err != nil {
			fmt.Fprintf(p.OutStream, "Failed to read input: %s\n", err)
			return ExitCodeError
		}

		// Stop execution
		if ans != "y" {
			fmt.Fprintf(p.OutStream, "Aborting\n")
			return ExitCodeOK
		}
	}

	fmt.Fprintf(p.OutStream, "(cf-local-push) Generate Dockerfile\n")
	f, err := os.Create("Dockerfile")
	if err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	if _, err := f.Write([]byte(dockerfileText)); err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	fmt.Fprintf(p.OutStream, "(cf-local-push) Start building docker image\n")
	if err := docker("build", "-t", "image-localpush", "."); err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	fmt.Fprintf(p.OutStream, "(cf-local-push) Start running docker container\n")
	if err := docker("run", "-p", "8080:8080", "-e", "PORT=8080", "image-localpush"); err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	return 0
}

func (p *LocalPush) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name:    Name,
		Version: Version,
		Commands: []plugin.Command{
			{
				Name:     "local-push",
				HelpText: "Push cf app on local Docker container",
				UsageDetails: plugin.Usage{
					Usage: p.Usage(),
				},
			},
		},
	}
}

func (p *LocalPush) Usage() string {
	text := `cf local-push

local-push command tells cf to deploy your app on local docker
container.
`
	return text
}
<commit_msg>Clean up after finish<commit_after>package main

import (
	"fmt"
	"io"
	"os"
	"os/exec"

	"flag"

	"os/signal"

	"github.com/cloudfoundry/cli/plugin"
	"github.com/tcnksm/go-input"
)

// Exit codes are int values that represent an exit code
// for a particular error.
const (
	ExitCodeOK    int = 0
	ExitCodeError int = 1 + iota
)

// EnvDebug is environmental variable for enabling debug
// output
const EnvDebug = "DEBUG_PLUGIN"

const (
	// DefaultPort is default port number
	DefaultPort = "8080"

	// DefaultImageName
	DefaultImageName = "cf-local-push"

	// Dockerfile is file name of Dockerfile
	Dockerfile = "Dockerfile"
)

// dockerfileText is used for build docker image for target application.
var dockerfileText = `FROM tcnksm/cf-buildstep:latest
ENV USER vcap
ADD . /app
RUN /build/builder
CMD /start web`

// Debugf prints debug output when EnvDebug is given
func Debugf(format string, args ...interface{}) {
	if env := os.Getenv(EnvDebug); len(env) != 0 {
		fmt.Fprintf(os.Stdout, "[DEBUG] "+format+"\n", args...)
	}
}

// LocalPush
type LocalPush struct {
	OutStream io.Writer
	InStream  io.Reader
}

// Run starts plugin process.
func (p *LocalPush) Run(cliConn plugin.CliConnection, arg []string) {
	Debugf("Run local-push plugin")
	Debugf("Arg: %#v", arg)

	// Ensure local-push is called.
	// Plugin is also called when install/uninstall via cf command.
	// Ignore such other calls.
	if len(arg) < 1 || arg[0] != Name {
		os.Exit(ExitCodeOK)
	}

	// Read CLI context (Currently, ctx val is not used but in future it should).
	ctx, err := NewCLIContext(cliConn)
	if err != nil {
		fmt.Fprintf(p.OutStream, "Failed to read cf command context: %s\n", err)
		os.Exit(ExitCodeError)
	}

	// Call run instead of doing the work here so we can use
	// `defer` statements within the function and have them work properly.
	// (defers aren't called with os.Exit)
	os.Exit(p.run(ctx, arg[1:]))
}

// run runs local-push it returns exit code.
func (p *LocalPush) run(ctx *CLIContext, args []string) int {

	var (
		port  string
		image string
	)

	flags := flag.NewFlagSet("plugin", flag.ContinueOnError)
	flags.SetOutput(p.OutStream)
	flags.Usage = func() {
		fmt.Fprintln(p.OutStream, p.Usage())
	}

	flags.StringVar(&port, "port", DefaultPort, "")
	flags.StringVar(&port, "p", DefaultPort, "(Short)")

	flags.StringVar(&image, "image", DefaultImageName, "")
	flags.StringVar(&image, "i", DefaultImageName, "(Short)")

	if err := flags.Parse(args); err != nil {
		return ExitCodeError
	}

	ui := &input.UI{
		Writer: p.OutStream,
		Reader: p.InStream,
	}

	// Check docker is installed or not.
	if _, err := exec.LookPath("docker"); err != nil {
		fmt.Fprintf(p.OutStream, "docker command is not found in your $PATH. Install it before.\n")
		return ExitCodeError
	}

	// Check Dockerfile is exist or not.
	// If it's exist, ask user to overwriting.
	if _, err := os.Stat(Dockerfile); !os.IsNotExist(err) {
		fmt.Fprintf(p.OutStream, "Dockerfile is already exist\n")
		query := "Overwrite Dockerfile? [yN]"
		ans, err := ui.Ask(query, &input.Options{
			Default:     "N",
			HideDefault: true,
			HideOrder:   true,
			Required:    true,
			Loop:        true,
			ValidateFunc: func(s string) error {
				if s != "y" && s != "N" {
					return fmt.Errorf("input must be 'y' or 'N'")
				}
				return nil
			},
		})

		if err != nil {
			fmt.Fprintf(p.OutStream, "Failed to read input: %s\n", err)
			return ExitCodeError
		}

		// Stop execution
		if ans != "y" {
			fmt.Fprintf(p.OutStream, "Aborting\n")
			return ExitCodeOK
		}
	}

	fmt.Fprintf(p.OutStream, "(cf-local-push) Generate Dockerfile\n")
	f, err := os.Create("Dockerfile")
	if err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	if _, err := f.Write([]byte(dockerfileText)); err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	fmt.Fprintf(p.OutStream, "(cf-local-push) Start building docker image\n")

	docker := &Docker{
		OutStream: p.OutStream,
		Discard:   false,
	}

	if err := docker.execute("build", "-t", image, "."); err != nil {
		fmt.Fprintf(p.OutStream, "%s\n", err)
		return ExitCodeError
	}

	fmt.Fprintf(p.OutStream, "(cf-local-push) Start running docker container\n")

	// errCh
	errCh := make(chan error, 1)

	// port mapping settings
	portMap := fmt.Sprintf("%s:%s", port, port)
	portEnv := fmt.Sprintf("PORT=%s", port)
	portEnvVcap := fmt.Sprintf("VCAP_APP_PORT=%s", port)

	// Use same name as image
	container := image

	go func() {
		Debugf("Run command: docker run -p %s -e %s -e %s--name %s %s",
			portMap, portEnv, portEnvVcap, container, image)
		errCh <- docker.execute("run",
			"-p", portMap,
			"-e", portEnv,
			"-e", portEnvVcap,
			"--name", container,
			image)
	}()

	sigCh := make(chan os.Signal)
	signal.Notify(sigCh, os.Interrupt)

	select {
	case <-sigCh:
		fmt.Fprintf(p.OutStream, "Interrupt: Stop and remove container (It takes a few seconds...")

		// Don't output
		docker.Discard = true

		// Stop & Remove docker container
		docker.execute("stop", container)
		docker.execute("rm", container)

		return ExitCodeOK
	case err := <-errCh:
		if err != nil {
			fmt.Fprintf(p.OutStream, "Failed to run container %s: %s\n", err)
			return ExitCodeError
		}
		return ExitCodeOK
	}
}

func (p *LocalPush) GetMetadata() plugin.PluginMetadata {
	return plugin.PluginMetadata{
		Name:    Name,
		Version: Version,
		Commands: []plugin.Command{
			{
				Name:     "local-push",
				HelpText: "Push cf app on local Docker container",
				UsageDetails: plugin.Usage{
					Usage: p.Usage(),
				},
			},
		},
	}
}

func (p *LocalPush) Usage() string {
	text := `cf local-push [options]

local-push command tells cf to deploy current working directory app on
local docker container. You need to prepare docker environment before.

local-push remove the container after stop the container.

Options:

  -port PORT      Port number to map to docker container. You can access
                  to application via this port. By default, '8080' is used.
                  If you use, docker machine for running docker, you can
                  access application by 'curl $(docker-machine ip):PORT)'.

  -image NAME     Docker image name. By default, 'local-push' is used.

`
	return text
}
<|endoftext|>


<commit_before>package main

import (
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strconv"
)

func init() {
	commands = append(commands, cli.Command{
		Name:      "schedule",
		Usage:     "schedule backups to occur at a regular frequency",
		UsageText: "bytemark schedule backups [--start <date>] <server> <disc> <interval>",
		Description: `schedule backups to occur at a regular interval (defined in seconds)

EXAMPLES

To have daily backups at midnight of a server called 'fileservers' 'very-important-data' disc:
bytemark schedule backups --start 00:00 fileserver very-important-data 86400

To have hourly backups starting at 14:37 (Central European Summer Time) on the 5th of April, 2017:
bytemark schedule backups --start "2017-04-05T14:37:00+02:00" fileserver very-important-data 3600`,
		Action: cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{
			{
				Name:      "backups",
				Usage:     "schedule backups to occur at a regular frequency",
				UsageText: "bytemark schedule backups [--start <date>] <server> <disc> <interval>",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "start",
						Usage: "date & time the schedule starts. Assumes BST/GMT (depending on time of year) if not specified - defaults to 00:00",
					},
				},
				Description: `schedule backups to occur at a regular interval (defined in seconds)

EXAMPLES

To have daily backups at midnight of a server called 'fileservers' 'very-important-data' disc:
bytemark schedule backups --start 00:00 fileserver very-important-data 86400

To have hourly backups starting at 14:37 (Central European Summer Time) on the 5th of April, 2017:
bytemark schedule backups --start "2017-04-05T14:37:00+02:00" fileserver very-important-data 3600`,
				Action: With(VirtualMachineNameProvider, DiscLabelProvider, func(c *Context) (err error) {
					start := c.String("start")
					if start == "" {
						start = "00:00"
					}

					intervalStr, err := c.NextArg()
					if err != nil {
						return
					}

					interval, err := strconv.Atoi(intervalStr)
					if err != nil {
						return
					}

					err = global.Client.CreateBackupSchedule(*c.VirtualMachineName, *c.DiscLabel, start, interval)
					if err == nil {
						log.Log("Schedule set.")
					}
					return
				}),
			},
		},
	})
}
<commit_msg>Fix CreateBackupSchedule call in schedule backups command<commit_after>package main

import (
	"github.com/BytemarkHosting/bytemark-client/util/log"
	"github.com/urfave/cli"
	"strconv"
)

func init() {
	commands = append(commands, cli.Command{
		Name:      "schedule",
		Usage:     "schedule backups to occur at a regular frequency",
		UsageText: "bytemark schedule backups [--start <date>] <server> <disc> <interval>",
		Description: `schedule backups to occur at a regular interval (defined in seconds)

EXAMPLES

To have daily backups at midnight of a server called 'fileservers' 'very-important-data' disc:
bytemark schedule backups --start 00:00 fileserver very-important-data 86400

To have hourly backups starting at 14:37 (Central European Summer Time) on the 5th of April, 2017:
bytemark schedule backups --start "2017-04-05T14:37:00+02:00" fileserver very-important-data 3600`,
		Action: cli.ShowSubcommandHelp,
		Subcommands: []cli.Command{
			{
				Name:      "backups",
				Usage:     "schedule backups to occur at a regular frequency",
				UsageText: "bytemark schedule backups [--start <date>] <server> <disc> <interval>",
				Flags: []cli.Flag{
					cli.StringFlag{
						Name:  "start",
						Usage: "date & time the schedule starts. Assumes BST/GMT (depending on time of year) if not specified - defaults to 00:00",
					},
				},
				Description: `schedule backups to occur at a regular interval (defined in seconds)

EXAMPLES

To have daily backups at midnight of a server called 'fileservers' 'very-important-data' disc:
bytemark schedule backups --start 00:00 fileserver very-important-data 86400

To have hourly backups starting at 14:37 (Central European Summer Time) on the 5th of April, 2017:
bytemark schedule backups --start "2017-04-05T14:37:00+02:00" fileserver very-important-data 3600`,
				Action: With(VirtualMachineNameProvider, DiscLabelProvider, func(c *Context) (err error) {
					start := c.String("start")
					if start == "" {
						start = "00:00"
					}

					intervalStr, err := c.NextArg()
					if err != nil {
						return
					}

					interval, err := strconv.Atoi(intervalStr)
					if err != nil {
						return
					}

					_, err = global.Client.CreateBackupSchedule(*c.VirtualMachineName, *c.DiscLabel, start, interval)
					if err == nil {
						log.Log("Schedule set.")
					}
					return
				}),
			},
		},
	})
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	chttpd "github.com/nochso/colourl/http"
	"net/http"
)

var (
	port    int
	verbose bool
)

var (
	Version   string
	BuildDate string
)

func init() {
	flag.IntVar(&port, "p", 9191, "HTTP listening port")
	flag.BoolVar(&verbose, "v", false, "Enable verbose / debug output")
}

func main() {
	flag.Parse()
	if verbose {
		log.SetLevel(log.DebugLevel)
	}
	log.WithFields(log.Fields{
		"version":    Version,
		"build_date": BuildDate,
	}).Info("colourl-http")
	log.WithFields(log.Fields{
		"port":    port,
		"verbose": verbose,
	}).Info("Starting HTTP server")
	http.HandleFunc("/", chttpd.IndexHandler)
	http.HandleFunc("/svg", chttpd.SVGHandler)
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}
<commit_msg>Debug log HTTP requests<commit_after>package main

import (
	"flag"
	"fmt"
	log "github.com/Sirupsen/logrus"
	chttpd "github.com/nochso/colourl/http"
	prefixed "github.com/x-cray/logrus-prefixed-formatter"
	"net/http"
	"time"
)

var (
	port    int
	verbose bool
)

var (
	Version   string
	BuildDate string
)

func init() {
	flag.IntVar(&port, "p", 9191, "HTTP listening port")
	flag.BoolVar(&verbose, "v", false, "Enable verbose / debug output")
}

func main() {
	flag.Parse()
	formatter := new(prefixed.TextFormatter)
	formatter.TimestampFormat = "2006-01-02 15:04:05 Z07:00"
	log.SetFormatter(formatter)
	if verbose {
		log.SetLevel(log.DebugLevel)
	}
	log.WithFields(log.Fields{
		"version":    Version,
		"build_date": BuildDate,
	}).Info("colourl-http")
	log.WithFields(log.Fields{
		"port":    port,
		"verbose": verbose,
	}).Info("Starting HTTP server")
	http.Handle("/", logHandler(chttpd.IndexMux().ServeHTTP))
	http.HandleFunc("/svg", logHandler(chttpd.SVGHandler))
	http.ListenAndServe(fmt.Sprintf(":%d", port), nil)
}

func logHandler(fn http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		start := time.Now()
		fn(w, r)
		log.WithFields(log.Fields{
			"duration": time.Now().Sub(start),
			"url":      r.URL,
			"method":   r.Method,
			"remote":   r.RemoteAddr,
		}).Debug("HTTP request")
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"flag"
	"os"
	"os/signal"
	"time"

	"github.com/kubernetes-csi/external-attacher-csi/pkg/controller"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"
)

const (
	// Number of worker threads
	threads = 10
)

var (
	// TODO: add a better description of attacher name - is it CSI plugin name? Kubernetes plugin name?
	kubeconfig        = flag.String("kubeconfig", "", "Absolute path to the kubeconfig file. Required only when running out of cluster.")
	resync            = flag.Duration("resync", 10*time.Second, "Resync interval of the controller.")
	connectionTimeout = flag.Duration("connection-timeout", 60, "Timeout for waiting for CSI driver socket (in seconds).")
)

func main() {
	flag.Set("logtostderr", "true")
	flag.Parse()

	// Create the client config. Use kubeconfig if given, otherwise assume in-cluster.
	config, err := buildConfig(*kubeconfig)
	if err != nil {
		panic(err)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		panic(err)
	}
	factory := informers.NewSharedInformerFactory(clientset, *resync)

	// TODO: wait for CSI's socket and discover 'attacher' and whether the
	// driver supports ControllerPulishVolume using ControllerGetCapabilities
	attacher := "csi/example"
	handler := controller.NewTrivialHandler(clientset)

	// Start the provision controller which will dynamically provision NFS PVs
	ctrl := controller.NewCSIAttachController(
		clientset,
		attacher,
		handler,
		factory.Storage().V1().VolumeAttachments(),
	)

	// run...
	stopCh := make(chan struct{})
	factory.Start(stopCh)
	go ctrl.Run(threads, stopCh)

	// ...until SIGINT
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
	close(stopCh)
}

func buildConfig(kubeconfig string) (*rest.Config, error) {
	if kubeconfig != "" {
		return clientcmd.BuildConfigFromFlags("", kubeconfig)
	}
	return rest.InClusterConfig()
}
<commit_msg>Connect to real CSI<commit_after>/*
Copyright 2017 The Kubernetes Authors.

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

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

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

package main

import (
	"context"
	"flag"
	"os"
	"os/signal"
	"time"

	"github.com/golang/glog"
	"k8s.io/client-go/informers"
	"k8s.io/client-go/kubernetes"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/clientcmd"

	"github.com/kubernetes-csi/external-attacher-csi/pkg/connection"
	"github.com/kubernetes-csi/external-attacher-csi/pkg/controller"
)

const (
	// Number of worker threads
	threads = 10

	// Default timeout of short CSI calls like GetPluginInfo
	csiTimeout = time.Second
)

// Command line flags
var (
	kubeconfig        = flag.String("kubeconfig", "", "Absolute path to the kubeconfig file. Required only when running out of cluster.")
	resync            = flag.Duration("resync", 10*time.Second, "Resync interval of the controller.")
	connectionTimeout = flag.Int("connection-timeout", 60, "Timeout for waiting for CSI driver socket (in seconds).")
	csiAddress        = flag.String("csi-address", "/run/csi/socket", "Address of the CSI driver socket.")
	dummy             = flag.Bool("dummy", false, "Run in dummy mode, i.e. not connecting to CSI driver and marking everything as attached. Expected CSI driver name is \"csi/dummy\".")
)

func main() {
	flag.Set("logtostderr", "true")
	flag.Parse()

	// Create the client config. Use kubeconfig if given, otherwise assume in-cluster.
	config, err := buildConfig(*kubeconfig)
	if err != nil {
		glog.Error(err.Error())
		os.Exit(1)
	}

	clientset, err := kubernetes.NewForConfig(config)
	if err != nil {
		glog.Error(err.Error())
		os.Exit(1)
	}
	factory := informers.NewSharedInformerFactory(clientset, *resync)

	var handler controller.Handler

	var attacher string
	if *dummy {
		// Do not connect to any CSI, mark everything as attached.
		handler = controller.NewTrivialHandler(clientset)
		attacher = "csi/dummy"
	} else {
		// Connect to CSI.
		csiConn, err := connection.New(*csiAddress, *connectionTimeout)
		if err != nil {
			glog.Error(err.Error())
			os.Exit(1)
		}

		// Find driver name.
		ctx, cancel := context.WithTimeout(context.Background(), csiTimeout)
		defer cancel()
		attacher, err = csiConn.GetDriverName(ctx)
		if err != nil {
			glog.Error(err.Error())
			os.Exit(1)
		}

		// Find out if the driver supports attach/detach.
		supportsAttach, err := csiConn.SupportsControllerPublish(ctx)
		if err != nil {
			glog.Error(err.Error())
			os.Exit(1)
		}
		if !supportsAttach {
			handler = controller.NewTrivialHandler(clientset)
		} else {
			glog.Error("Real attach/detach handler is not implemented yet")
			os.Exit(1)
		}
	}

	ctrl := controller.NewCSIAttachController(
		clientset,
		attacher,
		handler,
		factory.Storage().V1().VolumeAttachments(),
	)

	// run...
	stopCh := make(chan struct{})
	factory.Start(stopCh)
	go ctrl.Run(threads, stopCh)

	// ...until SIGINT
	c := make(chan os.Signal, 1)
	signal.Notify(c, os.Interrupt)
	<-c
	close(stopCh)
}

func buildConfig(kubeconfig string) (*rest.Config, error) {
	if kubeconfig != "" {
		return clientcmd.BuildConfigFromFlags("", kubeconfig)
	}
	return rest.InClusterConfig()
}
<|endoftext|>


<commit_before>/*
   Copyright The containerd Authors.

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

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

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

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/containerd/containerd/cmd/containerd/command"
	"github.com/containerd/containerd/cmd/ctr/app"
	"github.com/urfave/cli"
)

func main() {
	if err := run(); err != nil {
		fmt.Fprint(os.Stderr, err)
		os.Exit(1)
	}
}

func run() error {
	flag.Parse()
	apps := map[string]*cli.App{
		"containerd": command.App(),
		"ctr":        app.New(),
	}
	dir := flag.Arg(1)

	parts := strings.SplitN(flag.Arg(0), ".", 2)
	if len(parts) != 2 {
		return fmt.Errorf("invalid name '%s': name does not contain man page section", flag.Arg(0))
	}
	name, section := parts[0], parts[1]

	appName, ok := apps[name]
	if !ok {
		return fmt.Errorf("invalid application '%s'", name)
	}

	// clear out the usage as we use banners that do not display in man pages
	appName.Usage = ""
	appName.ToMan()
	data, err := appName.ToMan()
	if err != nil {
		return err
	}
	_ = os.MkdirAll(dir, os.ModePerm)
	if err := ioutil.WriteFile(filepath.Join(dir, fmt.Sprintf("%s.%s", name, section)), []byte(data), 0644); err != nil {
		return err
	}
	return nil
}
<commit_msg>Remove useless lines<commit_after>/*
   Copyright The containerd Authors.

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

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

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

package main

import (
	"flag"
	"fmt"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/containerd/containerd/cmd/containerd/command"
	"github.com/containerd/containerd/cmd/ctr/app"
	"github.com/urfave/cli"
)

func main() {
	if err := run(); err != nil {
		fmt.Fprint(os.Stderr, err)
		os.Exit(1)
	}
}

func run() error {
	flag.Parse()
	apps := map[string]*cli.App{
		"containerd": command.App(),
		"ctr":        app.New(),
	}
	dir := flag.Arg(1)

	parts := strings.SplitN(flag.Arg(0), ".", 2)
	if len(parts) != 2 {
		return fmt.Errorf("invalid name '%s': name does not contain man page section", flag.Arg(0))
	}
	name, section := parts[0], parts[1]

	appName, ok := apps[name]
	if !ok {
		return fmt.Errorf("invalid application '%s'", name)
	}

	// clear out the usage as we use banners that do not display in man pages
	appName.Usage = ""
	data, err := appName.ToMan()
	if err != nil {
		return err
	}
	_ = os.MkdirAll(dir, os.ModePerm)
	if err := ioutil.WriteFile(filepath.Join(dir, fmt.Sprintf("%s.%s", name, section)), []byte(data), 0644); err != nil {
		return err
	}
	return nil
}
<|endoftext|>