<commit_before>package main

import (
	"errors"
	"fmt"
)

type rod int

const (
	RodDigging rod = iota
	RodBlink
	RodTeleportOther
	RodLightningBolt
	RodFireball
	RodFog
	RodObstruction
	RodShatter
	RodSwapping
	// below unimplemented
	RodConfusingClouds
	RodFear
	RodFreezingClouds
)

func (r rod) Letter() rune {
	return '/'
}

func (r rod) Rare() bool {
	switch r {
	case RodDigging, RodTeleportOther, RodShatter, RodSwapping:
		return true
	default:
		return false
	}
}

func (r rod) String() string {
	var text string
	switch r {
	case RodDigging:
		text = "rod of digging"
	case RodBlink:
		text = "rod of blinking"
	case RodTeleportOther:
		text = "rod of teleport other"
	case RodFog:
		text = "rod of fog"
	case RodFear:
		text = "rod of fear"
	case RodFireball:
		text = "rod of fireball"
	case RodLightningBolt:
		text = "rod of lightning bolt"
	case RodObstruction:
		text = "rod of obstruction"
	case RodShatter:
		text = "rod of shatter"
	case RodSwapping:
		text = "rod of swapping"
	case RodFreezingClouds:
		text = "rod of freezing clouds"
	case RodConfusingClouds:
		text = "rod of confusing clouds"
	}
	return text
}

func (r rod) Desc() string {
	var text string
	switch r {
	case RodDigging:
		text = "digs through walls."
	case RodBlink:
		text = "makes you blink away within your line of sight."
	case RodTeleportOther:
		text = "teleports away one of your foes."
	case RodFog:
		text = "creates a dense fog that reduces your (and monster's) line of sight."
	case RodFireball:
		text = "throws a 1-radius fireball at your foes."
	case RodLightningBolt:
		text = "throws a lightning bolt through one or more enemies."
	case RodObstruction:
		text = "creates a temporary wall at targeted location."
	case RodShatter:
		text = "induces an explosion around a wall. The wall can disintegrate."
	case RodSwapping:
		text = "makes you swap positions with a targeted monster."
	case RodFear:
		text = "TODO"
	case RodFreezingClouds:
		text = "TODO"
	case RodConfusingClouds:
		text = "TODO"
	}
	return fmt.Sprintf("The %s %s", r, text)
}

type rodProps struct {
	Charge int
}

func (r rod) MaxCharge() (charges int) {
	switch r {
	case RodBlink:
		charges = 5
	case RodTeleportOther, RodDigging:
		charges = 3
	default:
		charges = 4
	}
	return charges
}

func (r rod) Rate() int {
	rate := r.MaxCharge() - 2
	if rate < 1 {
		rate = 1
	}
	return rate
}

func (r rod) MPCost() (mp int) {
	switch r {
	case RodBlink:
		mp = 3
	case RodTeleportOther, RodDigging, RodShatter:
		mp = 5
	default:
		mp = 4
	}
	return mp
}

func (r rod) Use(g *game, ev event) error {
	rods := g.Player.Rods
	if rods[r].Charge <= 0 {
		return errors.New("No charges remaining on this rod.")
	}
	if r.MPCost() > g.Player.MP {
		return errors.New("Not enough magic points for using this rod.")
	}
	var err error
	switch r {
	case RodBlink:
		err = g.EvokeRodBlink(ev)
	case RodTeleportOther:
		err = g.EvokeRodTeleportOther(ev)
	case RodLightningBolt:
		err = g.EvokeRodLightningBolt(ev)
	case RodFireball:
		err = g.EvokeRodFireball(ev)
	case RodFog:
		err = g.EvokeRodFog(ev)
	case RodDigging:
		err = g.EvokeRodDigging(ev)
	case RodObstruction:
		err = g.EvokeRodObstruction(ev)
	case RodShatter:
		err = g.EvokeRodShatter(ev)
	case RodSwapping:
		err = g.EvokeRodSwapping(ev)
	}

	if err != nil {
		return err
	}
	rods[r].Charge--
	g.Player.MP -= r.MPCost()
	g.StoryPrintf("You evoked your %s.", r)
	g.FairAction()
	ev.Renew(g, 7)
	return nil
}

func (g *game) EvokeRodBlink(ev event) error {
	if g.Player.HasStatus(StatusLignification) {
		return errors.New("You cannot blink while lignified.")
	}
	g.Blink(ev)
	return nil
}

func (g *game) Blink(ev event) {
	if g.Player.HasStatus(StatusLignification) {
		return
	}
	losPos := []position{}
	for pos, b := range g.Player.LOS {
		if !b {
			continue
		}
		if g.Dungeon.Cell(pos).T != FreeCell {
			continue
		}
		mons, _ := g.MonsterAt(pos)
		if mons.Exists() {
			continue
		}
		losPos = append(losPos, pos)
	}
	if len(losPos) == 0 {
		// should not happen
		g.Print("You could not blink.")
		return
	}
	npos := losPos[RandInt(len(losPos))]
	if npos.Distance(g.Player.Pos) <= 3 {
		// Give close cells less chance to make blinking more useful
		npos = losPos[RandInt(len(losPos))]
	}
	g.Player.Pos = npos
	g.Print("You blink away.")
	g.CollectGround()
	g.ComputeLOS()
	g.MakeMonstersAware()
}

func (g *game) EvokeRodTeleportOther(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{}) {
		return errors.New("Ok, then.")
	}
	mons, _ := g.MonsterAt(g.Player.Target)
	// mons not nil (check done in the targeter)
	mons.TeleportAway(g)
	return nil
}

func (g *game) EvokeRodLightningBolt(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{}) {
		return errors.New("Ok, then.")
	}
	ray := g.Ray(g.Player.Target)
	g.Print("A lightning bolt emerges straight from the rod.")
	for _, pos := range ray {
		mons, _ := g.MonsterAt(pos)
		if mons == nil {
			continue
		}
		mons.HP -= RandInt(21)
		if mons.HP <= 0 {
			g.Printf("%s is killed by the bolt.", Indefinite(mons.Kind.String(), true))
			g.HandleKill(mons)
		}
		g.MakeNoise(12, mons.Pos)
		mons.MakeHuntIfHurt(g)
	}
	return nil
}

func (g *game) EvokeRodFireball(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{area: true, minDist: true}) {
		return errors.New("Ok, then.")
	}
	neighbors := g.Dungeon.FreeNeighbors(g.Player.Target)
	g.Print("A fireball emerges straight from the rod.")
	for _, pos := range append(neighbors, g.Player.Target) {
		mons, _ := g.MonsterAt(pos)
		if mons == nil {
			continue
		}
		mons.HP -= RandInt(21)
		if mons.HP <= 0 {
			g.Printf("%s is killed by the fireball.", Indefinite(mons.Kind.String(), true))
			g.HandleKill(mons)
		}
		g.MakeNoise(12, mons.Pos)
		mons.MakeHuntIfHurt(g)
	}
	return nil
}

type cloud int

const (
	CloudFog cloud = iota
)

func (g *game) EvokeRodFog(ev event) error {
	g.Fog(g.Player.Pos, 3, ev)
	g.Print("You are surrounded by a dense fog.")
	return nil
}

func (g *game) Fog(at position, radius int, ev event) {
	dij := &normalPath{game: g}
	nm := Dijkstra(dij, []position{at}, radius)
	for pos := range nm {
		_, ok := g.Clouds[pos]
		if !ok {
			g.Clouds[pos] = CloudFog
			g.PushEvent(&cloudEvent{ERank: ev.Rank() + 100 + RandInt(100), EAction: CloudEnd, Pos: pos})
		}
	}
	g.ComputeLOS()
}

func (g *game) EvokeRodDigging(ev event) error {
	if !g.ui.ChooseTarget(g, &wallChooser{}) {
		return errors.New("Ok, then.")
	}
	pos := g.Player.Target
	for i := 0; i < 3; i++ {
		g.Dungeon.SetCell(pos, FreeCell)
		g.MakeNoise(17, pos)
		g.Fog(pos, 1, ev)
		pos = pos.To(pos.Dir(g.Player.Pos))
		if !g.Player.LOS[pos] {
			g.UnknownDig[pos] = true
		}
		if !pos.valid() || g.Dungeon.Cell(pos).T != WallCell {
			break
		}
	}
	g.Print("You see the wall disintegrate with a crash.")
	g.ComputeLOS()
	g.MakeMonstersAware()
	return nil
}

func (g *game) EvokeRodShatter(ev event) error {
	if !g.ui.ChooseTarget(g, &wallChooser{minDist: true}) {
		return errors.New("Ok, then.")
	}
	neighbors := g.Dungeon.FreeNeighbors(g.Player.Target)
	if RandInt(2) == 0 {
		g.Dungeon.SetCell(g.Player.Target, FreeCell)
		g.ComputeLOS()
		g.MakeMonstersAware()
		g.MakeNoise(19, g.Player.Target)
		g.Fog(g.Player.Target, 2, ev)
		g.Print("You see the wall disappear in a noisy explosion.")
	} else {
		g.MakeNoise(15, g.Player.Target)
		g.Print("You see an explosion around the wall.")
	}
	for _, pos := range neighbors {
		mons, _ := g.MonsterAt(pos)
		if !mons.Exists() {
			continue
		}
		mons.HP -= RandInt(30)
		if mons.HP <= 0 {
			g.Printf("%s is killed by the explosion.", Indefinite(mons.Kind.String(), true))
			g.HandleKill(mons)
		}
		g.MakeNoise(12, mons.Pos)
		mons.MakeHuntIfHurt(g)
	}
	return nil
}

func (g *game) EvokeRodObstruction(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{needsFreeWay: true, free: true}) {
		return errors.New("Ok, then.")
	}
	neighbors := g.Dungeon.FreeNeighbors(g.Player.Target)
	for _, pos := range neighbors {
		g.MakeNoise(18, pos)
		break
	}
	g.Dungeon.SetCell(g.Player.Target, WallCell)
	if g.TemporalWalls != nil {
		g.TemporalWalls[g.Player.Target] = true
	}
	g.PushEvent(&cloudEvent{ERank: ev.Rank() + 200 + RandInt(50), Pos: g.Player.Target, EAction: ObstructionEnd})
	g.Printf("You see a wall appear from nothing.")
	g.ComputeLOS()
	return nil
}

func (g *game) EvokeRodSwapping(ev event) error {
	if g.Player.HasStatus(StatusLignification) {
		return errors.New("You cannot blink while lignified.")
	}
	if !g.ui.ChooseTarget(g, &chooser{}) {
		return errors.New("Ok, then.")
	}
	mons, _ := g.MonsterAt(g.Player.Target)
	// mons not nil (check done in the targeter)
	mons.Pos, g.Player.Pos = g.Player.Pos, mons.Pos
	mons.MakeAware(g)
	g.Printf("You swap positions with the %s.", mons.Kind)
	g.CollectGround()
	g.ComputeLOS()
	g.MakeMonstersAware()
	return nil
}

func (g *game) GeneratedRodsCount() int {
	count := 0
	for _, b := range g.GeneratedRods {
		if b {
			count++
		}
	}
	return count
}

func (g *game) GenerateRod() {
	count := 0
	for {
		count++
		if count > 1000 {
			panic("GenerateRod")
		}
		pos := g.FreeCellForStatic()
		r := rod(RandInt(int(RodShatter) + 1))
		if r.Rare() && RandInt(3) == 0 {
			r = rod(RandInt(int(RodSwapping) + 1))
		}
		if g.Player.Rods[r] == nil && !g.GeneratedRods[r] {
			g.GeneratedRods[r] = true
			g.Rods[pos] = r
			return
		}
	}
}
<commit_msg>fix message when attempting to use rod of swapping while lignified<commit_after>package main

import (
	"errors"
	"fmt"
)

type rod int

const (
	RodDigging rod = iota
	RodBlink
	RodTeleportOther
	RodLightningBolt
	RodFireball
	RodFog
	RodObstruction
	RodShatter
	RodSwapping
	// below unimplemented
	RodConfusingClouds
	RodFear
	RodFreezingClouds
)

func (r rod) Letter() rune {
	return '/'
}

func (r rod) Rare() bool {
	switch r {
	case RodDigging, RodTeleportOther, RodShatter, RodSwapping:
		return true
	default:
		return false
	}
}

func (r rod) String() string {
	var text string
	switch r {
	case RodDigging:
		text = "rod of digging"
	case RodBlink:
		text = "rod of blinking"
	case RodTeleportOther:
		text = "rod of teleport other"
	case RodFog:
		text = "rod of fog"
	case RodFear:
		text = "rod of fear"
	case RodFireball:
		text = "rod of fireball"
	case RodLightningBolt:
		text = "rod of lightning bolt"
	case RodObstruction:
		text = "rod of obstruction"
	case RodShatter:
		text = "rod of shatter"
	case RodSwapping:
		text = "rod of swapping"
	case RodFreezingClouds:
		text = "rod of freezing clouds"
	case RodConfusingClouds:
		text = "rod of confusing clouds"
	}
	return text
}

func (r rod) Desc() string {
	var text string
	switch r {
	case RodDigging:
		text = "digs through walls."
	case RodBlink:
		text = "makes you blink away within your line of sight."
	case RodTeleportOther:
		text = "teleports away one of your foes."
	case RodFog:
		text = "creates a dense fog that reduces your (and monster's) line of sight."
	case RodFireball:
		text = "throws a 1-radius fireball at your foes."
	case RodLightningBolt:
		text = "throws a lightning bolt through one or more enemies."
	case RodObstruction:
		text = "creates a temporary wall at targeted location."
	case RodShatter:
		text = "induces an explosion around a wall. The wall can disintegrate."
	case RodSwapping:
		text = "makes you swap positions with a targeted monster."
	case RodFear:
		text = "TODO"
	case RodFreezingClouds:
		text = "TODO"
	case RodConfusingClouds:
		text = "TODO"
	}
	return fmt.Sprintf("The %s %s", r, text)
}

type rodProps struct {
	Charge int
}

func (r rod) MaxCharge() (charges int) {
	switch r {
	case RodBlink:
		charges = 5
	case RodTeleportOther, RodDigging:
		charges = 3
	default:
		charges = 4
	}
	return charges
}

func (r rod) Rate() int {
	rate := r.MaxCharge() - 2
	if rate < 1 {
		rate = 1
	}
	return rate
}

func (r rod) MPCost() (mp int) {
	switch r {
	case RodBlink:
		mp = 3
	case RodTeleportOther, RodDigging, RodShatter:
		mp = 5
	default:
		mp = 4
	}
	return mp
}

func (r rod) Use(g *game, ev event) error {
	rods := g.Player.Rods
	if rods[r].Charge <= 0 {
		return errors.New("No charges remaining on this rod.")
	}
	if r.MPCost() > g.Player.MP {
		return errors.New("Not enough magic points for using this rod.")
	}
	var err error
	switch r {
	case RodBlink:
		err = g.EvokeRodBlink(ev)
	case RodTeleportOther:
		err = g.EvokeRodTeleportOther(ev)
	case RodLightningBolt:
		err = g.EvokeRodLightningBolt(ev)
	case RodFireball:
		err = g.EvokeRodFireball(ev)
	case RodFog:
		err = g.EvokeRodFog(ev)
	case RodDigging:
		err = g.EvokeRodDigging(ev)
	case RodObstruction:
		err = g.EvokeRodObstruction(ev)
	case RodShatter:
		err = g.EvokeRodShatter(ev)
	case RodSwapping:
		err = g.EvokeRodSwapping(ev)
	}

	if err != nil {
		return err
	}
	rods[r].Charge--
	g.Player.MP -= r.MPCost()
	g.StoryPrintf("You evoked your %s.", r)
	g.FairAction()
	ev.Renew(g, 7)
	return nil
}

func (g *game) EvokeRodBlink(ev event) error {
	if g.Player.HasStatus(StatusLignification) {
		return errors.New("You cannot blink while lignified.")
	}
	g.Blink(ev)
	return nil
}

func (g *game) Blink(ev event) {
	if g.Player.HasStatus(StatusLignification) {
		return
	}
	losPos := []position{}
	for pos, b := range g.Player.LOS {
		if !b {
			continue
		}
		if g.Dungeon.Cell(pos).T != FreeCell {
			continue
		}
		mons, _ := g.MonsterAt(pos)
		if mons.Exists() {
			continue
		}
		losPos = append(losPos, pos)
	}
	if len(losPos) == 0 {
		// should not happen
		g.Print("You could not blink.")
		return
	}
	npos := losPos[RandInt(len(losPos))]
	if npos.Distance(g.Player.Pos) <= 3 {
		// Give close cells less chance to make blinking more useful
		npos = losPos[RandInt(len(losPos))]
	}
	g.Player.Pos = npos
	g.Print("You blink away.")
	g.CollectGround()
	g.ComputeLOS()
	g.MakeMonstersAware()
}

func (g *game) EvokeRodTeleportOther(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{}) {
		return errors.New("Ok, then.")
	}
	mons, _ := g.MonsterAt(g.Player.Target)
	// mons not nil (check done in the targeter)
	mons.TeleportAway(g)
	return nil
}

func (g *game) EvokeRodLightningBolt(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{}) {
		return errors.New("Ok, then.")
	}
	ray := g.Ray(g.Player.Target)
	g.Print("A lightning bolt emerges straight from the rod.")
	for _, pos := range ray {
		mons, _ := g.MonsterAt(pos)
		if mons == nil {
			continue
		}
		mons.HP -= RandInt(21)
		if mons.HP <= 0 {
			g.Printf("%s is killed by the bolt.", Indefinite(mons.Kind.String(), true))
			g.HandleKill(mons)
		}
		g.MakeNoise(12, mons.Pos)
		mons.MakeHuntIfHurt(g)
	}
	return nil
}

func (g *game) EvokeRodFireball(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{area: true, minDist: true}) {
		return errors.New("Ok, then.")
	}
	neighbors := g.Dungeon.FreeNeighbors(g.Player.Target)
	g.Print("A fireball emerges straight from the rod.")
	for _, pos := range append(neighbors, g.Player.Target) {
		mons, _ := g.MonsterAt(pos)
		if mons == nil {
			continue
		}
		mons.HP -= RandInt(21)
		if mons.HP <= 0 {
			g.Printf("%s is killed by the fireball.", Indefinite(mons.Kind.String(), true))
			g.HandleKill(mons)
		}
		g.MakeNoise(12, mons.Pos)
		mons.MakeHuntIfHurt(g)
	}
	return nil
}

type cloud int

const (
	CloudFog cloud = iota
)

func (g *game) EvokeRodFog(ev event) error {
	g.Fog(g.Player.Pos, 3, ev)
	g.Print("You are surrounded by a dense fog.")
	return nil
}

func (g *game) Fog(at position, radius int, ev event) {
	dij := &normalPath{game: g}
	nm := Dijkstra(dij, []position{at}, radius)
	for pos := range nm {
		_, ok := g.Clouds[pos]
		if !ok {
			g.Clouds[pos] = CloudFog
			g.PushEvent(&cloudEvent{ERank: ev.Rank() + 100 + RandInt(100), EAction: CloudEnd, Pos: pos})
		}
	}
	g.ComputeLOS()
}

func (g *game) EvokeRodDigging(ev event) error {
	if !g.ui.ChooseTarget(g, &wallChooser{}) {
		return errors.New("Ok, then.")
	}
	pos := g.Player.Target
	for i := 0; i < 3; i++ {
		g.Dungeon.SetCell(pos, FreeCell)
		g.MakeNoise(17, pos)
		g.Fog(pos, 1, ev)
		pos = pos.To(pos.Dir(g.Player.Pos))
		if !g.Player.LOS[pos] {
			g.UnknownDig[pos] = true
		}
		if !pos.valid() || g.Dungeon.Cell(pos).T != WallCell {
			break
		}
	}
	g.Print("You see the wall disintegrate with a crash.")
	g.ComputeLOS()
	g.MakeMonstersAware()
	return nil
}

func (g *game) EvokeRodShatter(ev event) error {
	if !g.ui.ChooseTarget(g, &wallChooser{minDist: true}) {
		return errors.New("Ok, then.")
	}
	neighbors := g.Dungeon.FreeNeighbors(g.Player.Target)
	if RandInt(2) == 0 {
		g.Dungeon.SetCell(g.Player.Target, FreeCell)
		g.ComputeLOS()
		g.MakeMonstersAware()
		g.MakeNoise(19, g.Player.Target)
		g.Fog(g.Player.Target, 2, ev)
		g.Print("You see the wall disappear in a noisy explosion.")
	} else {
		g.MakeNoise(15, g.Player.Target)
		g.Print("You see an explosion around the wall.")
	}
	for _, pos := range neighbors {
		mons, _ := g.MonsterAt(pos)
		if !mons.Exists() {
			continue
		}
		mons.HP -= RandInt(30)
		if mons.HP <= 0 {
			g.Printf("%s is killed by the explosion.", Indefinite(mons.Kind.String(), true))
			g.HandleKill(mons)
		}
		g.MakeNoise(12, mons.Pos)
		mons.MakeHuntIfHurt(g)
	}
	return nil
}

func (g *game) EvokeRodObstruction(ev event) error {
	if !g.ui.ChooseTarget(g, &chooser{needsFreeWay: true, free: true}) {
		return errors.New("Ok, then.")
	}
	neighbors := g.Dungeon.FreeNeighbors(g.Player.Target)
	for _, pos := range neighbors {
		g.MakeNoise(18, pos)
		break
	}
	g.Dungeon.SetCell(g.Player.Target, WallCell)
	if g.TemporalWalls != nil {
		g.TemporalWalls[g.Player.Target] = true
	}
	g.PushEvent(&cloudEvent{ERank: ev.Rank() + 200 + RandInt(50), Pos: g.Player.Target, EAction: ObstructionEnd})
	g.Printf("You see a wall appear from nothing.")
	g.ComputeLOS()
	return nil
}

func (g *game) EvokeRodSwapping(ev event) error {
	if g.Player.HasStatus(StatusLignification) {
		return errors.New("You cannot use this rod while lignified.")
	}
	if !g.ui.ChooseTarget(g, &chooser{}) {
		return errors.New("Ok, then.")
	}
	mons, _ := g.MonsterAt(g.Player.Target)
	// mons not nil (check done in the targeter)
	mons.Pos, g.Player.Pos = g.Player.Pos, mons.Pos
	mons.MakeAware(g)
	g.Printf("You swap positions with the %s.", mons.Kind)
	g.CollectGround()
	g.ComputeLOS()
	g.MakeMonstersAware()
	return nil
}

func (g *game) GeneratedRodsCount() int {
	count := 0
	for _, b := range g.GeneratedRods {
		if b {
			count++
		}
	}
	return count
}

func (g *game) GenerateRod() {
	count := 0
	for {
		count++
		if count > 1000 {
			panic("GenerateRod")
		}
		pos := g.FreeCellForStatic()
		r := rod(RandInt(int(RodShatter) + 1))
		if r.Rare() && RandInt(3) == 0 {
			r = rod(RandInt(int(RodSwapping) + 1))
		}
		if g.Player.Rods[r] == nil && !g.GeneratedRods[r] {
			g.GeneratedRods[r] = true
			g.Rods[pos] = r
			return
		}
	}
}
<|endoftext|>


<commit_before>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     June 24, 2021
;       related version of root zone:     2021062401
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<commit_msg>Automatic update for Thu Jul 22 08:19:14 UTC 2021<commit_after>package dnsr

var root = `
;       This file holds the information on root name servers needed to
;       initialize cache of Internet domain name servers
;       (e.g. reference this file in the "cache  .  <file>"
;       configuration file of BIND domain name servers).
;
;       This file is made available by InterNIC
;       under anonymous FTP as
;           file                /domain/named.cache
;           on server           FTP.INTERNIC.NET
;       -OR-                    RS.INTERNIC.NET
;
;       last update:     July 21, 2021
;       related version of root zone:     2021072102
;
; FORMERLY NS.INTERNIC.NET
;
.                        3600000      NS    A.ROOT-SERVERS.NET.
A.ROOT-SERVERS.NET.      3600000      A     198.41.0.4
A.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:ba3e::2:30
;
; FORMERLY NS1.ISI.EDU
;
.                        3600000      NS    B.ROOT-SERVERS.NET.
B.ROOT-SERVERS.NET.      3600000      A     199.9.14.201
B.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:200::b
;
; FORMERLY C.PSI.NET
;
.                        3600000      NS    C.ROOT-SERVERS.NET.
C.ROOT-SERVERS.NET.      3600000      A     192.33.4.12
C.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2::c
;
; FORMERLY TERP.UMD.EDU
;
.                        3600000      NS    D.ROOT-SERVERS.NET.
D.ROOT-SERVERS.NET.      3600000      A     199.7.91.13
D.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2d::d
;
; FORMERLY NS.NASA.GOV
;
.                        3600000      NS    E.ROOT-SERVERS.NET.
E.ROOT-SERVERS.NET.      3600000      A     192.203.230.10
E.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:a8::e
;
; FORMERLY NS.ISC.ORG
;
.                        3600000      NS    F.ROOT-SERVERS.NET.
F.ROOT-SERVERS.NET.      3600000      A     192.5.5.241
F.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:2f::f
;
; FORMERLY NS.NIC.DDN.MIL
;
.                        3600000      NS    G.ROOT-SERVERS.NET.
G.ROOT-SERVERS.NET.      3600000      A     192.112.36.4
G.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:12::d0d
;
; FORMERLY AOS.ARL.ARMY.MIL
;
.                        3600000      NS    H.ROOT-SERVERS.NET.
H.ROOT-SERVERS.NET.      3600000      A     198.97.190.53
H.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:1::53
;
; FORMERLY NIC.NORDU.NET
;
.                        3600000      NS    I.ROOT-SERVERS.NET.
I.ROOT-SERVERS.NET.      3600000      A     192.36.148.17
I.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fe::53
;
; OPERATED BY VERISIGN, INC.
;
.                        3600000      NS    J.ROOT-SERVERS.NET.
J.ROOT-SERVERS.NET.      3600000      A     192.58.128.30
J.ROOT-SERVERS.NET.      3600000      AAAA  2001:503:c27::2:30
;
; OPERATED BY RIPE NCC
;
.                        3600000      NS    K.ROOT-SERVERS.NET.
K.ROOT-SERVERS.NET.      3600000      A     193.0.14.129
K.ROOT-SERVERS.NET.      3600000      AAAA  2001:7fd::1
;
; OPERATED BY ICANN
;
.                        3600000      NS    L.ROOT-SERVERS.NET.
L.ROOT-SERVERS.NET.      3600000      A     199.7.83.42
L.ROOT-SERVERS.NET.      3600000      AAAA  2001:500:9f::42
;
; OPERATED BY WIDE
;
.                        3600000      NS    M.ROOT-SERVERS.NET.
M.ROOT-SERVERS.NET.      3600000      A     202.12.27.33
M.ROOT-SERVERS.NET.      3600000      AAAA  2001:dc3::35
; End of file`
<|endoftext|>


<commit_before>package main

import (
	"debug/pe"
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"unsafe"
)

type ImageResourceDirectory struct {
	Characteristics      uint32
	TimeDateStamp        uint32
	MajorVersion         uint16
	MinorVersion         uint16
	NumberOfNamedEntries uint16
	NumberOfIdEntries    uint16
}

type ImageResourceDirectoryEntry struct {
	NameOrId     uint32
	OffsetToData uint32
}

type ImageResourceDataEntry struct {
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32
	Reserved     uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31
	TYPE_MANIFEST     = 24
)

type Writer struct {
	W      io.Writer
	Offset uint32 //FIXME: uint64?
	Err    error
}

func (w *Writer) WriteLE(v interface{}) {
	if w.Err != nil {
		return
	}
	w.Err = binary.Write(w.W, binary.LittleEndian, v)
	if w.Err != nil {
		return
	}
	w.Offset += uint32(reflect.TypeOf(v).Size())
}

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

func run() error {
	//TODO: allow options:
	// -o FILENAME - output file name
	// + advanced specification of multiple resources, as a tree (json?)
	if len(os.Args) <= 1 {
		return fmt.Errorf("USAGE: %s FILE.exe.manifest\n"+
			"Generates FILE.res",
			os.Args[0])
	}

	var fix1, fix2, fix3, fix4, fix5, fix6 uint32
	//fix1=1 // ASCIIZ manifest & -1 to manifest length info (?)
	fix2 = 0x02ca // symbols (strings) table at the end
	//fix3 = 0x02c0 // relocations
	//fix4 = 0xc0300040 // section characteristics
	//fix5 = 0x52a26e9e // timestamp in rsrc directories
	fix6 = 1 // force append stuff for fix2 & fix3 at end of file

	fname := os.Args[1]
	suffix := ".exe.manifest"
	if !strings.HasSuffix(fname, suffix) {
		return fmt.Errorf("Filename '%s' does not end in suffix '%s'", fname, suffix)
	}
	fname = fname[:len(fname)-len(suffix)]

	manifest, err := ioutil.ReadFile(fname + suffix)
	if err != nil {
		return err
	}
	if fix1 != 0 {
		manifest = append(manifest, byte(0)) // seems needed?
	}

	out, err := os.Create(fname + ".res")
	if err != nil {
		return err
	}
	defer out.Close()
	w := Writer{W: out}

	coffhdr := pe.FileHeader{
		Machine:              0x014c, //FIXME: find out how to differentiate this value, or maybe not necessary for Go
		NumberOfSections:     1,      // .rsrc
		TimeDateStamp:        0,      // was also 0 in sample data from MinGW's windres.exe
		PointerToSymbolTable: uint32(fix2),
		NumberOfSymbols:      uint32(fix2 / 550),
		SizeOfOptionalHeader: 0,
		Characteristics:      0x0104, //FIXME: copied from windres.exe output, find out what should be here and why
	}
	w.WriteLE(coffhdr)
	if w.Err != nil {
		return fmt.Errorf("Error writing COFF header: %s", w.Err)
	}

	secthdr := pe.SectionHeader32{
		Name: [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0},
		SizeOfRawData: uint32(3*unsafe.Sizeof(ImageResourceDirectory{})+
			3*unsafe.Sizeof(ImageResourceDirectoryEntry{})+
			1*unsafe.Sizeof(ImageResourceDataEntry{})) +
			uint32(len(manifest)), //FIXME: probably must include all the .rsrc directory structures too
		PointerToRawData:     w.Offset + uint32(unsafe.Sizeof(pe.SectionHeader32{})),
		PointerToRelocations: uint32(fix3),
		NumberOfRelocations:  uint16(fix3 / 540),
		Characteristics:      0x40000040 | fix4, // "INITIALIZED_DATA MEM_READ" ?
	}
	w.WriteLE(secthdr)
	if w.Err != nil {
		return fmt.Errorf("Error writing .rsrc section header: %s", w.Err)
	}

	// now, build "directory hierarchy" of .rsrc section: first type, then id/name, then language

	diroff := w.Offset // all "OffsetToData" are relative to this point

	w.WriteLE(ImageResourceDirectory{
		NumberOfIdEntries: 1,
		TimeDateStamp:     fix5,
	})
	w.WriteLE(ImageResourceDirectoryEntry{
		NameOrId:     TYPE_MANIFEST,
		OffsetToData: MASK_SUBDIRECTORY | (w.Offset + uint32(unsafe.Sizeof(ImageResourceDirectoryEntry{})) - diroff),
	})
	w.WriteLE(ImageResourceDirectory{
		NumberOfIdEntries: 1,
		TimeDateStamp:     fix5,
	})
	w.WriteLE(ImageResourceDirectoryEntry{
		NameOrId:     1, // ID
		OffsetToData: MASK_SUBDIRECTORY | (w.Offset + uint32(unsafe.Sizeof(ImageResourceDirectoryEntry{})) - diroff),
	})
	w.WriteLE(ImageResourceDirectory{
		NumberOfIdEntries: 1,
		TimeDateStamp:     fix5,
	})
	w.WriteLE(ImageResourceDirectoryEntry{
		NameOrId:     0x0409, //FIXME: language; what value should be here?
		OffsetToData: w.Offset + uint32(unsafe.Sizeof(ImageResourceDirectoryEntry{})) - diroff,
	})

	w.WriteLE(ImageResourceDataEntry{
		OffsetToData: w.Offset + uint32(unsafe.Sizeof(ImageResourceDataEntry{})) - diroff,
		Size1:        uint32(len(manifest)) - uint32(fix1),
		CodePage:     0, //FIXME: what value here? for now just tried 0
	})

	if w.Err != nil {
		return fmt.Errorf("Error writing .rsrc Directory Hierarchy: %s", w.Err)
	}

	if fix2+fix3+fix6 > 0 {
		manifest = append(manifest, []byte{
			0x48, 0, 0, 0,
			0, 0, 0, 0,
			7, 0, '.', 'r',
			's', 'r', 'c', 0,
			0, 0, 0, 0,
			0, 0, 1, 0,
			0, 0, 3, 0,
			4, 0, 0, 0,
		}...)
	}

	_, err = w.W.Write(manifest)
	if err != nil {
		return fmt.Errorf("Error writing manifest contents: %s", err)
	}

	return nil
}
<commit_msg>leave only pseudofix no.2 in code<commit_after>package main

import (
	"debug/pe"
	"encoding/binary"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"reflect"
	"strings"
	"unsafe"
)

type ImageResourceDirectory struct {
	Characteristics      uint32
	TimeDateStamp        uint32
	MajorVersion         uint16
	MinorVersion         uint16
	NumberOfNamedEntries uint16
	NumberOfIdEntries    uint16
}

type ImageResourceDirectoryEntry struct {
	NameOrId     uint32
	OffsetToData uint32
}

type ImageResourceDataEntry struct {
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32
	Reserved     uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31
	TYPE_MANIFEST     = 24
)

type Writer struct {
	W      io.Writer
	Offset uint32 //FIXME: uint64?
	Err    error
}

func (w *Writer) WriteLE(v interface{}) {
	if w.Err != nil {
		return
	}
	w.Err = binary.Write(w.W, binary.LittleEndian, v)
	if w.Err != nil {
		return
	}
	w.Offset += uint32(reflect.TypeOf(v).Size())
}

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

func run() error {
	//TODO: allow options:
	// -o FILENAME - output file name
	// + advanced specification of multiple resources, as a tree (json?)
	if len(os.Args) <= 1 {
		return fmt.Errorf("USAGE: %s FILE.exe.manifest\n"+
			"Generates FILE.res",
			os.Args[0])
	}

	//TODO: try to simplify by adding fake section at beginning, containing strings table in data, and characteristics saying "drop me when linking"

	var fix2 uint32
	fix2 = 0x02ca // symbols (strings) table at the end

	fname := os.Args[1]
	suffix := ".exe.manifest"
	if !strings.HasSuffix(fname, suffix) {
		return fmt.Errorf("Filename '%s' does not end in suffix '%s'", fname, suffix)
	}
	fname = fname[:len(fname)-len(suffix)]

	manifest, err := ioutil.ReadFile(fname + suffix)
	if err != nil {
		return err
	}

	out, err := os.Create(fname + ".res")
	if err != nil {
		return err
	}
	defer out.Close()
	w := Writer{W: out}

	coffhdr := pe.FileHeader{
		Machine:              0x014c, //FIXME: find out how to differentiate this value, or maybe not necessary for Go
		NumberOfSections:     1,      // .rsrc
		TimeDateStamp:        0,      // was also 0 in sample data from MinGW's windres.exe
		PointerToSymbolTable: uint32(fix2),
		NumberOfSymbols:      uint32(fix2 / 550),
		SizeOfOptionalHeader: 0,
		Characteristics:      0x0104, //FIXME: copied from windres.exe output, find out what should be here and why
	}
	w.WriteLE(coffhdr)
	if w.Err != nil {
		return fmt.Errorf("Error writing COFF header: %s", w.Err)
	}

	secthdr := pe.SectionHeader32{
		Name: [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0},
		SizeOfRawData: uint32(3*unsafe.Sizeof(ImageResourceDirectory{})+
			3*unsafe.Sizeof(ImageResourceDirectoryEntry{})+
			1*unsafe.Sizeof(ImageResourceDataEntry{})) +
			uint32(len(manifest)), //FIXME: probably must include all the .rsrc directory structures too
		PointerToRawData: w.Offset + uint32(unsafe.Sizeof(pe.SectionHeader32{})),
	}
	w.WriteLE(secthdr)
	if w.Err != nil {
		return fmt.Errorf("Error writing .rsrc section header: %s", w.Err)
	}

	// now, build "directory hierarchy" of .rsrc section: first type, then id/name, then language

	diroff := w.Offset // all "OffsetToData" are relative to this point

	w.WriteLE(ImageResourceDirectory{
		NumberOfIdEntries: 1,
	})
	w.WriteLE(ImageResourceDirectoryEntry{
		NameOrId:     TYPE_MANIFEST,
		OffsetToData: MASK_SUBDIRECTORY | (w.Offset + uint32(unsafe.Sizeof(ImageResourceDirectoryEntry{})) - diroff),
	})
	w.WriteLE(ImageResourceDirectory{
		NumberOfIdEntries: 1,
	})
	w.WriteLE(ImageResourceDirectoryEntry{
		NameOrId:     1, // ID
		OffsetToData: MASK_SUBDIRECTORY | (w.Offset + uint32(unsafe.Sizeof(ImageResourceDirectoryEntry{})) - diroff),
	})
	w.WriteLE(ImageResourceDirectory{
		NumberOfIdEntries: 1,
	})
	w.WriteLE(ImageResourceDirectoryEntry{
		NameOrId:     0x0409, //FIXME: language; what value should be here?
		OffsetToData: w.Offset + uint32(unsafe.Sizeof(ImageResourceDirectoryEntry{})) - diroff,
	})

	w.WriteLE(ImageResourceDataEntry{
		OffsetToData: w.Offset + uint32(unsafe.Sizeof(ImageResourceDataEntry{})) - diroff,
		Size1:        uint32(len(manifest)),
		CodePage:     0, //FIXME: what value here? for now just tried 0
	})

	if w.Err != nil {
		return fmt.Errorf("Error writing .rsrc Directory Hierarchy: %s", w.Err)
	}

	if fix2 > 0 {
		manifest = append(manifest, []byte{
			0x48, 0, 0, 0,
			0, 0, 0, 0,
			7, 0, '.', 'r',
			's', 'r', 'c', 0,
			0, 0, 0, 0,
			0, 0, 1, 0,
			0, 0, 3, 0,
			4, 0, 0, 0,
		}...)
	}

	_, err = w.W.Write(manifest)
	if err != nil {
		return fmt.Errorf("Error writing manifest contents: %s", err)
	}

	return nil
}
<|endoftext|>


<commit_before>package main

// TODO:
// - to store icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
//   - also need to first read and split input ico file

import (
	"debug/pe"
	"encoding/binary"
	"flag"
	"fmt"
	"os"
	"reflect"

	"github.com/akavel/rsrc/ico"
)

type Dir struct { // struct IMAGE_RESOURCE_DIRECTORY
	Characteristics      uint32
	TimeDateStamp        uint32
	MajorVersion         uint16
	MinorVersion         uint16
	NumberOfNamedEntries uint16
	NumberOfIdEntries    uint16
	DirEntries
	Dirs
}

type DirEntries []DirEntry
type Dirs []Dir

type DirEntry struct { // struct IMAGE_RESOURCE_DIRECTORY_ENTRY
	NameOrId     uint32
	OffsetToData uint32
}

type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32
	Reserved     uint32
}

type RelocationEntry struct {
	RVA         uint32 // "offset within the Section's raw data where the address starts."
	SymbolIndex uint32 // "(zero based) index in the Symbol table to which the reference refers."
	Type        uint16
}

type Symbol struct {
	Name           [8]byte
	Value          uint32
	SectionNumber  uint16
	Type           uint16
	StorageClass   uint8
	AuxiliaryCount uint8
}

type StringsHeader struct {
	Length uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31

	RT_ICON       = 3
	RT_GROUP_ICON = 3 + 11
	RT_MANIFEST   = 24
)

var (
	STRING_RSRC = [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0}
	LANG_ENTRY  = DirEntries{{NameOrId: 0x0409}} //FIXME: language; what value should be here?
)

type GRPICONDIR struct {
	ico.ICONDIR
	Entries []GRPICONDIRENTRY
}

type GRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	Id uint16
}

func main() {
	//TODO: allow in options advanced specification of multiple resources, as a tree (json?)
	var fnamein, fnameico, fnameout string
	flags := flag.NewFlagSet("", flag.ContinueOnError)
	flags.StringVar(&fnamein, "manifest", "", "path to a Windows manifest file to embed")
	flags.StringVar(&fnameico, "ico", "", "UNSUPPORTED: path to ICO file to embed")
	flags.StringVar(&fnameout, "o", "rsrc.syso", "name of output COFF (.res or .syso) file")
	_ = flags.Parse(os.Args[1:])
	if fnamein == "" {
		fmt.Fprintf(os.Stderr, "USAGE: %s -manifest FILE.exe.manifest [-o FILE.syso]\n"+
			"Generates a .syso file with specified resources embedded in .rsrc section,\n"+
			"aimed for consumption by Go linker when building Win32 excecutables.\n"+
			"OPTIONS:\n",
			os.Args[0])
		flags.PrintDefaults()
		os.Exit(1)
	}

	err := run(fnamein, fnameico, fnameout)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

type Coff struct {
	pe.FileHeader
	pe.SectionHeader32

	Dir
	DataEntries []DataEntry
	Data        []interface{}

	Relocations []RelocationEntry
	Symbols     []Symbol
	StringsHeader
}

func run(fnamein, fnameico, fnameout string) error {
	manifest, err := SizedOpen(fnamein)
	if err != nil {
		return fmt.Errorf("Error opening manifest file '%s': %s", fnamein, err)
	}
	defer manifest.Close()

	var icons []ico.ICONDIRENTRY
	var iconsf *os.File
	if fnameico != "" {
		iconsf, err = os.Open(fnameico)
		if err != nil {
			return err
		}
		defer iconsf.Close()
		icons, err = ico.DecodeHeaders(iconsf)
		if err != nil {
			return err
		}
	}

	newid := make(chan uint16)
	go func() {
		for i := uint16(1); ; i++ {
			newid <- i
		}
	}()

	out, err := os.Create(fnameout)
	if err != nil {
		return err
	}
	defer out.Close()
	w := Writer{W: out}

	coff := Coff{
		pe.FileHeader{
			Machine:              0x014c, //FIXME: find out how to differentiate this value, or maybe not necessary for Go
			NumberOfSections:     1,      // .rsrc
			TimeDateStamp:        0,      // was also 0 in sample data from MinGW's windres.exe
			NumberOfSymbols:      1,
			SizeOfOptionalHeader: 0,
			Characteristics:      0x0104, //FIXME: copied from windres.exe output, find out what should be here and why
		},
		pe.SectionHeader32{
			Name:                STRING_RSRC,
			NumberOfRelocations: 1,
			Characteristics:     0x40000040, // "INITIALIZED_DATA MEM_READ" ?
		},

		// now, build "directory hierarchy" of .rsrc section: first type, then id/name, then language
		Dir{
			NumberOfIdEntries: 1,
			DirEntries:        DirEntries{{NameOrId: RT_MANIFEST}},
			Dirs: Dirs{{
				NumberOfIdEntries: 1,
				DirEntries:        DirEntries{{NameOrId: uint32(<-newid)}}, // resource ID
				Dirs: Dirs{{
					NumberOfIdEntries: 1,
					DirEntries:        LANG_ENTRY,
				}},
			}},
		},
		[]DataEntry{
			DataEntry{
				Size1:    uint32(manifest.Size()),
				CodePage: 0, //FIXME: what value here? for now just tried 0
			},
		},
		[]interface{}{
			manifest,
		},

		[]RelocationEntry{RelocationEntry{
			SymbolIndex: 0, // "(zero based) index in the Symbol table to which the reference refers. Once you have loaded the COFF file into memory and know where each symbol is, you find the new updated address for the given symbol and update the reference accordingly."
			Type:        7, // according to ldpe.c, this decodes to: IMAGE_REL_I386_DIR32NB
		}},

		[]Symbol{Symbol{
			Name:           STRING_RSRC,
			Value:          0,
			SectionNumber:  1,
			Type:           0, // FIXME: wtf?
			StorageClass:   3, // FIXME: is it ok? and uint8? and what does the value mean?
			AuxiliaryCount: 0, // FIXME: wtf?
		}},

		StringsHeader{
			Length: uint32(binary.Size(StringsHeader{})), // empty strings table -- but we must still show size of the table's header...
		},
	}

	if len(icons) > 0 {
		/*
			coff.Dir.NumberOfIdEntries+=2

			coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_ICON})
			coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
				NumberOfIdEntries: len(icons),
			})
			dir := &coff.Dir.Dirs[len(coff.Dir.Dirs)-1]
			group := GRPICONDIR{
				Reserved: 0, // magic num.
				Type: 1, // magic num.
				Count: len(icons),
			}
			for i, icon := range icons {
				id := <-newid

				group.Entries = append(group.Entries, GRPICONDIRENTRY{icon, id})
				dir.DirEntries = append(dir.DirEntries, DirEntry{NameOrId: uint32(id)})
				dir.Dirs = append(dir.Dirs, Dir{
					NumberOfIdEntries: 1,
					DirEntries: LANG_ENTRY,
				})

				coff.Data = append(coff.Data, io.NewSectionReader(iconsf, icon.ImageOffset, icon.BytesInRes))
			}

			coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_GROUP_ICON})
			coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
				NumberOfIdEntries: 1,
				DirEntries: DirEntries{{NameOrId: uint32(<-newid)}},
				Dirs: Dirs{{
					NumberOfIdEntries: 1,
					DirEntries: LANG_ENTRY,
				}},
			})
			coff.Data = append(coff.Data, group)
		*/
	}

	// fill in some important offsets in resulting file
	var offset, diroff uint32
	Walk(coff, func(v reflect.Value, path string) error {
		switch path {
		case "/Dir":
			coff.SectionHeader32.PointerToRawData = offset
			diroff = offset
		case "/Dir/Dirs[0]":
			coff.Dir.DirEntries[0].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case "/Dir/Dirs[0]/Dirs[0]":
			coff.Dir.Dirs[0].DirEntries[0].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case "/DataEntries[0]":
			coff.Dir.Dirs[0].Dirs[0].DirEntries[0].OffsetToData = offset - diroff
		case "/DataEntries[0]/OffsetToData":
			coff.Relocations[0].RVA = offset - diroff
		case "/Data[0]":
			coff.DataEntries[0].OffsetToData = offset - diroff
		case "/Relocations":
			coff.SectionHeader32.PointerToRelocations = offset
			coff.SectionHeader32.SizeOfRawData = offset - diroff
		case "/Symbols":
			coff.FileHeader.PointerToSymbolTable = offset
		}

		if Plain(v.Kind()) {
			offset += uint32(binary.Size(v.Interface())) // TODO: change to v.Type().Size() ?
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			offset += uint32(vv.Size())
			return WALK_SKIP
		}
		return nil
	})

	// write the resulting file to disk
	Walk(coff, func(v reflect.Value, path string) error {
		if Plain(v.Kind()) {
			w.WriteLE(v.Interface())
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			w.WriteFromSized(vv)
			return WALK_SKIP
		}
		return nil
	})

	if w.Err != nil {
		return fmt.Errorf("Error writing output file: %s", w.Err)
	}

	return nil
}

func Plain(kind reflect.Kind) bool {
	switch kind {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
		return true
	}
	return false
}
<commit_msg>introducing leafwalker for DirEntries iterating<commit_after>package main

// TODO:
// - to store icons, see: http://blogs.msdn.com/b/oldnewthing/archive/2012/07/20/10331787.aspx
//   - also need to first read and split input ico file

import (
	"debug/pe"
	"encoding/binary"
	"flag"
	"fmt"
	"os"
	"reflect"

	"github.com/akavel/rsrc/ico"
)

type Dir struct { // struct IMAGE_RESOURCE_DIRECTORY
	Characteristics      uint32
	TimeDateStamp        uint32
	MajorVersion         uint16
	MinorVersion         uint16
	NumberOfNamedEntries uint16
	NumberOfIdEntries    uint16
	DirEntries
	Dirs
}

type DirEntries []DirEntry
type Dirs []Dir

type DirEntry struct { // struct IMAGE_RESOURCE_DIRECTORY_ENTRY
	NameOrId     uint32
	OffsetToData uint32
}

type DataEntry struct { // struct IMAGE_RESOURCE_DATA_ENTRY
	OffsetToData uint32
	Size1        uint32
	CodePage     uint32
	Reserved     uint32
}

type RelocationEntry struct {
	RVA         uint32 // "offset within the Section's raw data where the address starts."
	SymbolIndex uint32 // "(zero based) index in the Symbol table to which the reference refers."
	Type        uint16
}

type Symbol struct {
	Name           [8]byte
	Value          uint32
	SectionNumber  uint16
	Type           uint16
	StorageClass   uint8
	AuxiliaryCount uint8
}

type StringsHeader struct {
	Length uint32
}

const (
	MASK_SUBDIRECTORY = 1 << 31

	RT_ICON       = 3
	RT_GROUP_ICON = 3 + 11
	RT_MANIFEST   = 24
)

var (
	STRING_RSRC = [8]byte{'.', 'r', 's', 'r', 'c', 0, 0, 0}
	LANG_ENTRY  = DirEntries{{NameOrId: 0x0409}} //FIXME: language; what value should be here?
)

type GRPICONDIR struct {
	ico.ICONDIR
	Entries []GRPICONDIRENTRY
}

type GRPICONDIRENTRY struct {
	ico.IconDirEntryCommon
	Id uint16
}

func main() {
	//TODO: allow in options advanced specification of multiple resources, as a tree (json?)
	var fnamein, fnameico, fnameout string
	flags := flag.NewFlagSet("", flag.ContinueOnError)
	flags.StringVar(&fnamein, "manifest", "", "path to a Windows manifest file to embed")
	flags.StringVar(&fnameico, "ico", "", "UNSUPPORTED: path to ICO file to embed")
	flags.StringVar(&fnameout, "o", "rsrc.syso", "name of output COFF (.res or .syso) file")
	_ = flags.Parse(os.Args[1:])
	if fnamein == "" {
		fmt.Fprintf(os.Stderr, "USAGE: %s -manifest FILE.exe.manifest [-o FILE.syso]\n"+
			"Generates a .syso file with specified resources embedded in .rsrc section,\n"+
			"aimed for consumption by Go linker when building Win32 excecutables.\n"+
			"OPTIONS:\n",
			os.Args[0])
		flags.PrintDefaults()
		os.Exit(1)
	}

	err := run(fnamein, fnameico, fnameout)
	if err != nil {
		fmt.Fprintln(os.Stderr, err)
		os.Exit(1)
	}
}

type Coff struct {
	pe.FileHeader
	pe.SectionHeader32

	Dir
	DataEntries []DataEntry
	Data        []interface{}

	Relocations []RelocationEntry
	Symbols     []Symbol
	StringsHeader
}

func run(fnamein, fnameico, fnameout string) error {
	manifest, err := SizedOpen(fnamein)
	if err != nil {
		return fmt.Errorf("Error opening manifest file '%s': %s", fnamein, err)
	}
	defer manifest.Close()

	var icons []ico.ICONDIRENTRY
	var iconsf *os.File
	if fnameico != "" {
		iconsf, err = os.Open(fnameico)
		if err != nil {
			return err
		}
		defer iconsf.Close()
		icons, err = ico.DecodeHeaders(iconsf)
		if err != nil {
			return err
		}
	}

	newid := make(chan uint16)
	go func() {
		for i := uint16(1); ; i++ {
			newid <- i
		}
	}()

	out, err := os.Create(fnameout)
	if err != nil {
		return err
	}
	defer out.Close()
	w := Writer{W: out}

	coff := Coff{
		pe.FileHeader{
			Machine:              0x014c, //FIXME: find out how to differentiate this value, or maybe not necessary for Go
			NumberOfSections:     1,      // .rsrc
			TimeDateStamp:        0,      // was also 0 in sample data from MinGW's windres.exe
			NumberOfSymbols:      1,
			SizeOfOptionalHeader: 0,
			Characteristics:      0x0104, //FIXME: copied from windres.exe output, find out what should be here and why
		},
		pe.SectionHeader32{
			Name:                STRING_RSRC,
			NumberOfRelocations: 1,
			Characteristics:     0x40000040, // "INITIALIZED_DATA MEM_READ" ?
		},

		// now, build "directory hierarchy" of .rsrc section: first type, then id/name, then language
		Dir{
			NumberOfIdEntries: 1,
			DirEntries:        DirEntries{{NameOrId: RT_MANIFEST}},
			Dirs: Dirs{{
				NumberOfIdEntries: 1,
				DirEntries:        DirEntries{{NameOrId: uint32(<-newid)}}, // resource ID
				Dirs: Dirs{{
					NumberOfIdEntries: 1,
					DirEntries:        LANG_ENTRY,
				}},
			}},
		},
		[]DataEntry{
			DataEntry{
				Size1:    uint32(manifest.Size()),
				CodePage: 0, //FIXME: what value here? for now just tried 0
			},
		},
		[]interface{}{
			manifest,
		},

		[]RelocationEntry{RelocationEntry{
			SymbolIndex: 0, // "(zero based) index in the Symbol table to which the reference refers. Once you have loaded the COFF file into memory and know where each symbol is, you find the new updated address for the given symbol and update the reference accordingly."
			Type:        7, // according to ldpe.c, this decodes to: IMAGE_REL_I386_DIR32NB
		}},

		[]Symbol{Symbol{
			Name:           STRING_RSRC,
			Value:          0,
			SectionNumber:  1,
			Type:           0, // FIXME: wtf?
			StorageClass:   3, // FIXME: is it ok? and uint8? and what does the value mean?
			AuxiliaryCount: 0, // FIXME: wtf?
		}},

		StringsHeader{
			Length: uint32(binary.Size(StringsHeader{})), // empty strings table -- but we must still show size of the table's header...
		},
	}

	if len(icons) > 0 {
		//FIXME: add corresponding DataEntries
		/*
			coff.Dir.NumberOfIdEntries+=2

			coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_ICON})
			coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
				NumberOfIdEntries: len(icons),
			})
			dir := &coff.Dir.Dirs[len(coff.Dir.Dirs)-1]
			group := GRPICONDIR{
				Reserved: 0, // magic num.
				Type: 1, // magic num.
				Count: len(icons),
			}
			for i, icon := range icons {
				id := <-newid

				group.Entries = append(group.Entries, GRPICONDIRENTRY{icon, id})
				dir.DirEntries = append(dir.DirEntries, DirEntry{NameOrId: uint32(id)})
				dir.Dirs = append(dir.Dirs, Dir{
					NumberOfIdEntries: 1,
					DirEntries: LANG_ENTRY,
				})

				coff.Data = append(coff.Data, io.NewSectionReader(iconsf, icon.ImageOffset, icon.BytesInRes))
			}

			coff.Dir.DirEntries = append(coff.Dir.DirEntries, DirEntry{NameOrId: RT_GROUP_ICON})
			coff.Dir.Dirs = append(coff.Dir.Dirs, Dir{
				NumberOfIdEntries: 1,
				DirEntries: DirEntries{{NameOrId: uint32(<-newid)}},
				Dirs: Dirs{{
					NumberOfIdEntries: 1,
					DirEntries: LANG_ENTRY,
				}},
			})
			coff.Data = append(coff.Data, group)
		*/
	}

	leafwalker := make(chan *DirEntry)
	go func() {
		for _, dir1 := range coff.Dir.Dirs { // resource type
			for _, dir2 := range dir1.Dirs { // resource ID
				for i := range dir2.DirEntries { // resource lang
					leafwalker <- &dir2.DirEntries[i]
				}
			}
		}
	}()

	// fill in some important offsets in resulting file
	var offset, diroff uint32
	Walk(coff, func(v reflect.Value, path string) error {
		switch path {
		case "/Dir":
			coff.SectionHeader32.PointerToRawData = offset
			diroff = offset
		case "/Dir/Dirs[0]":
			coff.Dir.DirEntries[0].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case "/Dir/Dirs[0]/Dirs[0]":
			coff.Dir.Dirs[0].DirEntries[0].OffsetToData = MASK_SUBDIRECTORY | (offset - diroff)
		case "/DataEntries[0]":
			direntry := <-leafwalker
			direntry.OffsetToData = offset - diroff
		case "/DataEntries[0]/OffsetToData":
			coff.Relocations[0].RVA = offset - diroff
		case "/Data[0]":
			coff.DataEntries[0].OffsetToData = offset - diroff
		case "/Relocations":
			coff.SectionHeader32.PointerToRelocations = offset
			coff.SectionHeader32.SizeOfRawData = offset - diroff
		case "/Symbols":
			coff.FileHeader.PointerToSymbolTable = offset
		}

		if Plain(v.Kind()) {
			offset += uint32(binary.Size(v.Interface())) // TODO: change to v.Type().Size() ?
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			offset += uint32(vv.Size())
			return WALK_SKIP
		}
		return nil
	})

	// write the resulting file to disk
	Walk(coff, func(v reflect.Value, path string) error {
		if Plain(v.Kind()) {
			w.WriteLE(v.Interface())
			return nil
		}
		vv, ok := v.Interface().(SizedReader)
		if ok {
			w.WriteFromSized(vv)
			return WALK_SKIP
		}
		return nil
	})

	if w.Err != nil {
		return fmt.Errorf("Error writing output file: %s", w.Err)
	}

	return nil
}

func Plain(kind reflect.Kind) bool {
	switch kind {
	case reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr, reflect.Float32, reflect.Float64, reflect.Complex64, reflect.Complex128:
		return true
	}
	return false
}
<|endoftext|>


<commit_before>package machine

import (
	"fmt"
	"github.com/dockersecuritytools/batten/batten"
)

type JSONResult struct {
	CheckResults	[]CheckResult
}

type CheckResult struct {
	checkid	int
	result	string
	name	string
}

func FormatResultsForMachine(idx int, results *batten.CheckResults) CheckResult {

	checkdefinition := results.CheckDefinition

	//fmt.Printf("[%d/%d]", idx+1, len(batten.Checks))
	//fmt.Printf("%s", checkdefinition.Name())
    var resultString string

	if results.Error != nil {
		resultString = "ERROR"
	} else {
		if results.Success {
			resultString = "PASSED"
		} else {
			resultString = "FAILED"
		}
	}

	return CheckResult{
		checkid:	idx+1,
		result:		resultString,
		name:		checkdefinition.Name(),
	}
}

func ShowResults(results *JSONResult) {
	fmt.Printf("%s\n","{'checks:'")
	fmt.Printf("%s","{")
	for _, result := range results.CheckResults {
		fmt.Printf("[%d:%s:%s]",
			result.checkid,
			result.result,
			result.name)
	}
	fmt.Printf("}\n%s","}")
}
<commit_msg>use json to marshal the result<commit_after>package machine

import (
	"fmt"
	"encoding/json"

	"github.com/dockersecuritytools/batten/batten"
)

type JSONResult struct {
	CheckResults	[]CheckResult
}

type CheckResult struct {
	Checkid	int		`json:"id"`
	Result	string	`json:"result"`
	Name	string	`json:"desc"`
}

func FormatResultsForMachine(idx int, results *batten.CheckResults) CheckResult {

	checkdefinition := results.CheckDefinition

    var resultString string

	if results.Error != nil {
		resultString = "ERROR"
	} else {
		if results.Success {
			resultString = "PASSED"
		} else {
			resultString = "FAILED"
		}
	}

	return CheckResult{
		Checkid:	idx+1,
		Result:		resultString,
		Name:		checkdefinition.Name(),
	}
}

func ShowResults(results *JSONResult) {
	fmt.Printf("%s\n","{'checks:'")
	fmt.Printf("%s","{")
	for _, result := range results.CheckResults {
		jsonString,_ := json.Marshal(result)
		fmt.Println(string(jsonString))
	}
	fmt.Printf("}\n%s","}")
}
<|endoftext|>


<commit_before>package mackerelplugin

import (
	"crypto/sha1"
	"encoding/json"
	"fmt"
	"io"
	"log"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/mackerelio/golib/pluginutil"
)

const (
	UnitFloat          = "float"
	UnitInteger        = "integer"
	UnitPercentage     = "percentage"
	UnitBytes          = "bytes"
	UnitBytesPerSecond = "bytes/sec"
	UnitIOPS           = "iops"
)

// Metrics represents definition of a metric
type Metrics struct {
	Name    string  `json:"name"`
	Label   string  `json:"label"`
	Diff    bool    `json:"-"`
	Stacked bool    `json:"stacked"`
	Scale   float64 `json:"-"`
}

// Graphs represents definition of a graph
type Graphs struct {
	Label   string    `json:"label"`
	Unit    string    `json:"unit"`
	Metrics []Metrics `json:"metrics"`
}

// Plugin is old interface of mackerel-plugin
type Plugin interface {
	FetchMetrics() (map[string]float64, error)
	GraphDefinition() map[string]Graphs
}

// PluginWithPrefix is recommended interface
type PluginWithPrefix interface {
	Plugin
	MetricKeyPrefix() string
}

// MackerelPlugin is for mackerel-agent-plugins
type MackerelPlugin struct {
	Plugin
	Tempfile string
	diff     *bool
	writer   io.Writer
}

// NewMackerelPlugin returns new MackrelPlugin
func NewMackerelPlugin(plugin Plugin) *MackerelPlugin {
	return &MackerelPlugin{Plugin: plugin}
}

func (mp *MackerelPlugin) getWriter() io.Writer {
	if mp.writer == nil {
		mp.writer = os.Stdout
	}
	return mp.writer
}

func (mp *MackerelPlugin) hasDiff() bool {
	if mp.diff == nil {
		diff := false
		mp.diff = &diff
	DiffCheck:
		for _, graph := range mp.GraphDefinition() {
			for _, metric := range graph.Metrics {
				if metric.Diff {
					*mp.diff = true
					break DiffCheck
				}
			}
		}
	}
	return *mp.diff
}

func (mp *MackerelPlugin) printValue(w io.Writer, key string, value float64, now time.Time) {
	if math.IsNaN(value) || math.IsInf(value, 0) {
		log.Printf("Invalid value: key = %s, value = %f\n", key, value)
		return
	}

	if value == float64(int(value)) {
		fmt.Fprintf(w, "%s\t%d\t%d\n", key, int(value), now.Unix())
	} else {
		fmt.Fprintf(w, "%s\t%f\t%d\n", key, value, now.Unix())
	}
}

func (mp *MackerelPlugin) fetchLastValues() (map[string]float64, time.Time, error) {
	if !mp.hasDiff() {
		return nil, time.Unix(0, 0), nil
	}
	lastTime := time.Now()

	f, err := os.Open(mp.tempfilename())
	if err != nil {
		if os.IsNotExist(err) {
			return nil, lastTime, nil
		}
		return nil, lastTime, err
	}
	defer f.Close()

	stat := make(map[string]float64)
	decoder := json.NewDecoder(f)
	err = decoder.Decode(&stat)
	lastTime = time.Unix(int64(stat["_lastTime"]), 0)
	if err != nil {
		return stat, lastTime, err
	}
	return stat, lastTime, nil
}

func (mp *MackerelPlugin) saveValues(values map[string]float64, now time.Time) error {
	if !mp.hasDiff() {
		return nil
	}
	f, err := os.Create(mp.tempfilename())
	if err != nil {
		return err
	}
	defer f.Close()

	values["_lastTime"] = float64(now.Unix())
	encoder := json.NewEncoder(f)
	err = encoder.Encode(values)
	if err != nil {
		return err
	}

	return nil
}

func (mp *MackerelPlugin) calcDiff(value float64, now time.Time, lastValue float64, lastTime time.Time) (float64, error) {
	diffTime := now.Unix() - lastTime.Unix()
	if diffTime > 600 {
		return 0, fmt.Errorf("Too long duration")
	}

	diff := (value - lastValue) * 60 / float64(diffTime)
	if diff < 0 {
		return 0, fmt.Errorf("Counter seems to be reset.")
	}
	return diff, nil
}

func (mp *MackerelPlugin) tempfilename() string {
	if mp.Tempfile == "" {
		mp.Tempfile = mp.generateTempfilePath(os.Args)
	}
	return mp.Tempfile
}

var tempfileSanitizeReg = regexp.MustCompile(`[^-_.A-Za-z0-9]`)

// SetTempfileByBasename sets Tempfile under proper directory with specified basename.
func (mp *MackerelPlugin) SetTempfileByBasename(base string) {
	mp.Tempfile = filepath.Join(pluginutil.PluginWorkDir(), base)
}

func (mp *MackerelPlugin) generateTempfilePath(args []string) string {
	commandPath := args[0]
	var prefix string
	if p, ok := mp.Plugin.(PluginWithPrefix); ok {
		prefix = p.MetricKeyPrefix()
	} else {
		name := filepath.Base(commandPath)
		prefix = strings.TrimPrefix(tempfileSanitizeReg.ReplaceAllString(name, "_"), "mackerel-plugin-")
	}
	filename := fmt.Sprintf(
		"mackerel-plugin-%s-%x",
		prefix,
		// When command-line options are different, mostly different metrics.
		// e.g. `-host` and `-port` options for mackerel-plugin-mysql
		sha1.Sum([]byte(strings.Join(args[1:], " "))),
	)
	return filepath.Join(pluginutil.PluginWorkDir(), filename)
}

// OutputValues output the metrics
func (mp *MackerelPlugin) OutputValues() {
	now := time.Now()
	stat, err := mp.FetchMetrics()
	if err != nil {
		log.Fatalln("OutputValues: ", err)
	}

	lastStat, lastTime, err := mp.fetchLastValues()
	if err != nil {
		log.Println("fetchLastValues (ignore):", err)
	}

	for key, graph := range mp.GraphDefinition() {
		for _, metric := range graph.Metrics {
			if strings.ContainsAny(key+metric.Name, "*#") {
				mp.formatValuesWithWildcard(key, metric, stat, lastStat, now, lastTime)
			} else {
				mp.formatValues(key, metric, stat, lastStat, now, lastTime)
			}
		}
	}

	err = mp.saveValues(stat, now)
	if err != nil {
		log.Fatalf("saveValues: %s", err)
	}
}

func (mp *MackerelPlugin) formatValuesWithWildcard(prefix string, metric Metrics, stat map[string]float64, lastStat map[string]float64, now time.Time, lastTime time.Time) {
	regexpStr := `\A` + prefix + "." + metric.Name
	regexpStr = strings.Replace(regexpStr, ".", `\.`, -1)
	regexpStr = strings.Replace(regexpStr, "*", `[-a-zA-Z0-9_]+`, -1)
	regexpStr = strings.Replace(regexpStr, "#", `[-a-zA-Z0-9_]+`, -1)
	re, err := regexp.Compile(regexpStr)
	if err != nil {
		log.Fatalln("Failed to compile regexp: ", err)
	}
	for k := range stat {
		if re.MatchString(k) {
			metricEach := metric
			metricEach.Name = k
			mp.formatValues("", metricEach, stat, lastStat, now, lastTime)
		}
	}
}

func (mp *MackerelPlugin) formatValues(prefix string, metric Metrics, stat map[string]float64, lastStat map[string]float64, now time.Time, lastTime time.Time) {
	name := metric.Name
	value, ok := stat[name]
	if !ok {
		return
	}
	if metric.Diff {
		lastValue, ok := lastStat[name]
		if ok {
			var err error
			value, err = mp.calcDiff(value, now, lastValue, lastTime)
			if err != nil {
				log.Println("OutputValues: ", err)
			}
		} else {
			log.Printf("%s does not exist at last fetch\n", metric.Name)
			return
		}
	}

	if metric.Scale != 0 {
		value *= metric.Scale
	}

	metricNames := []string{}
	if p, ok := mp.Plugin.(PluginWithPrefix); ok {
		metricNames = append(metricNames, p.MetricKeyPrefix())
	}
	if prefix != "" {
		metricNames = append(metricNames, prefix)
	}
	metricNames = append(metricNames, metric.Name)
	mp.printValue(mp.getWriter(), strings.Join(metricNames, "."), value, now)
}

// GraphDef is graph definitions
type GraphDef struct {
	Graphs map[string]Graphs `json:"graphs"`
}

func title(s string) string {
	r := strings.NewReplacer(".", " ", "_", " ", "*", "", "#", "")
	return strings.TrimSpace(strings.Title(r.Replace(s)))
}

// OutputDefinitions outputs graph definitions
func (mp *MackerelPlugin) OutputDefinitions() {
	fmt.Fprintln(mp.getWriter(), "# mackerel-agent-plugin")
	graphs := make(map[string]Graphs)
	for key, graph := range mp.GraphDefinition() {
		g := graph
		k := key
		if p, ok := mp.Plugin.(PluginWithPrefix); ok {
			prefix := p.MetricKeyPrefix()
			if k == "" {
				k = prefix
			} else {
				k = prefix + "." + k
			}
		}
		if g.Label == "" {
			g.Label = title(k)
		}
		metrics := []Metrics{}
		for _, v := range g.Metrics {
			if v.Label == "" {
				v.Label = title(v.Name)
			}
			metrics = append(metrics, v)
		}
		g.Metrics = metrics
		graphs[k] = g
	}
	var graphdef GraphDef
	graphdef.Graphs = graphs
	b, err := json.Marshal(graphdef)
	if err != nil {
		log.Fatalln("OutputDefinitions: ", err)
	}
	fmt.Fprintln(mp.getWriter(), string(b))
}

// Run the plugin
func (mp *MackerelPlugin) Run() {
	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		mp.OutputDefinitions()
	} else {
		mp.OutputValues()
	}
}
<commit_msg>fix lint<commit_after>package mackerelplugin

import (
	"crypto/sha1"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"math"
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"time"

	"github.com/mackerelio/golib/pluginutil"
)

// Metric units
const (
	UnitFloat          = "float"
	UnitInteger        = "integer"
	UnitPercentage     = "percentage"
	UnitBytes          = "bytes"
	UnitBytesPerSecond = "bytes/sec"
	UnitIOPS           = "iops"
)

// Metrics represents definition of a metric
type Metrics struct {
	Name    string  `json:"name"`
	Label   string  `json:"label"`
	Diff    bool    `json:"-"`
	Stacked bool    `json:"stacked"`
	Scale   float64 `json:"-"`
}

// Graphs represents definition of a graph
type Graphs struct {
	Label   string    `json:"label"`
	Unit    string    `json:"unit"`
	Metrics []Metrics `json:"metrics"`
}

// Plugin is old interface of mackerel-plugin
type Plugin interface {
	FetchMetrics() (map[string]float64, error)
	GraphDefinition() map[string]Graphs
}

// PluginWithPrefix is recommended interface
type PluginWithPrefix interface {
	Plugin
	MetricKeyPrefix() string
}

// MackerelPlugin is for mackerel-agent-plugins
type MackerelPlugin struct {
	Plugin
	Tempfile string
	diff     *bool
	writer   io.Writer
}

// NewMackerelPlugin returns new MackrelPlugin
func NewMackerelPlugin(plugin Plugin) *MackerelPlugin {
	return &MackerelPlugin{Plugin: plugin}
}

func (mp *MackerelPlugin) getWriter() io.Writer {
	if mp.writer == nil {
		mp.writer = os.Stdout
	}
	return mp.writer
}

func (mp *MackerelPlugin) hasDiff() bool {
	if mp.diff == nil {
		diff := false
		mp.diff = &diff
	DiffCheck:
		for _, graph := range mp.GraphDefinition() {
			for _, metric := range graph.Metrics {
				if metric.Diff {
					*mp.diff = true
					break DiffCheck
				}
			}
		}
	}
	return *mp.diff
}

func (mp *MackerelPlugin) printValue(w io.Writer, key string, value float64, now time.Time) {
	if math.IsNaN(value) || math.IsInf(value, 0) {
		log.Printf("Invalid value: key = %s, value = %f\n", key, value)
		return
	}

	if value == float64(int(value)) {
		fmt.Fprintf(w, "%s\t%d\t%d\n", key, int(value), now.Unix())
	} else {
		fmt.Fprintf(w, "%s\t%f\t%d\n", key, value, now.Unix())
	}
}

func (mp *MackerelPlugin) fetchLastValues() (map[string]float64, time.Time, error) {
	if !mp.hasDiff() {
		return nil, time.Unix(0, 0), nil
	}
	lastTime := time.Now()

	f, err := os.Open(mp.tempfilename())
	if err != nil {
		if os.IsNotExist(err) {
			return nil, lastTime, nil
		}
		return nil, lastTime, err
	}
	defer f.Close()

	stat := make(map[string]float64)
	decoder := json.NewDecoder(f)
	err = decoder.Decode(&stat)
	lastTime = time.Unix(int64(stat["_lastTime"]), 0)
	if err != nil {
		return stat, lastTime, err
	}
	return stat, lastTime, nil
}

func (mp *MackerelPlugin) saveValues(values map[string]float64, now time.Time) error {
	if !mp.hasDiff() {
		return nil
	}
	f, err := os.Create(mp.tempfilename())
	if err != nil {
		return err
	}
	defer f.Close()

	values["_lastTime"] = float64(now.Unix())
	encoder := json.NewEncoder(f)
	err = encoder.Encode(values)
	if err != nil {
		return err
	}

	return nil
}

func (mp *MackerelPlugin) calcDiff(value float64, now time.Time, lastValue float64, lastTime time.Time) (float64, error) {
	diffTime := now.Unix() - lastTime.Unix()
	if diffTime > 600 {
		return 0, errors.New("too long duration")
	}

	diff := (value - lastValue) * 60 / float64(diffTime)
	if diff < 0 {
		return 0, errors.New("counter seems to be reset")
	}
	return diff, nil
}

func (mp *MackerelPlugin) tempfilename() string {
	if mp.Tempfile == "" {
		mp.Tempfile = mp.generateTempfilePath(os.Args)
	}
	return mp.Tempfile
}

var tempfileSanitizeReg = regexp.MustCompile(`[^-_.A-Za-z0-9]`)

// SetTempfileByBasename sets Tempfile under proper directory with specified basename.
func (mp *MackerelPlugin) SetTempfileByBasename(base string) {
	mp.Tempfile = filepath.Join(pluginutil.PluginWorkDir(), base)
}

func (mp *MackerelPlugin) generateTempfilePath(args []string) string {
	commandPath := args[0]
	var prefix string
	if p, ok := mp.Plugin.(PluginWithPrefix); ok {
		prefix = p.MetricKeyPrefix()
	} else {
		name := filepath.Base(commandPath)
		prefix = strings.TrimPrefix(tempfileSanitizeReg.ReplaceAllString(name, "_"), "mackerel-plugin-")
	}
	filename := fmt.Sprintf(
		"mackerel-plugin-%s-%x",
		prefix,
		// When command-line options are different, mostly different metrics.
		// e.g. `-host` and `-port` options for mackerel-plugin-mysql
		sha1.Sum([]byte(strings.Join(args[1:], " "))),
	)
	return filepath.Join(pluginutil.PluginWorkDir(), filename)
}

// OutputValues output the metrics
func (mp *MackerelPlugin) OutputValues() {
	now := time.Now()
	stat, err := mp.FetchMetrics()
	if err != nil {
		log.Fatalln("OutputValues: ", err)
	}

	lastStat, lastTime, err := mp.fetchLastValues()
	if err != nil {
		log.Println("fetchLastValues (ignore):", err)
	}

	for key, graph := range mp.GraphDefinition() {
		for _, metric := range graph.Metrics {
			if strings.ContainsAny(key+metric.Name, "*#") {
				mp.formatValuesWithWildcard(key, metric, stat, lastStat, now, lastTime)
			} else {
				mp.formatValues(key, metric, stat, lastStat, now, lastTime)
			}
		}
	}

	err = mp.saveValues(stat, now)
	if err != nil {
		log.Fatalf("saveValues: %s", err)
	}
}

func (mp *MackerelPlugin) formatValuesWithWildcard(prefix string, metric Metrics, stat map[string]float64, lastStat map[string]float64, now time.Time, lastTime time.Time) {
	regexpStr := `\A` + prefix + "." + metric.Name
	regexpStr = strings.Replace(regexpStr, ".", `\.`, -1)
	regexpStr = strings.Replace(regexpStr, "*", `[-a-zA-Z0-9_]+`, -1)
	regexpStr = strings.Replace(regexpStr, "#", `[-a-zA-Z0-9_]+`, -1)
	re, err := regexp.Compile(regexpStr)
	if err != nil {
		log.Fatalln("Failed to compile regexp: ", err)
	}
	for k := range stat {
		if re.MatchString(k) {
			metricEach := metric
			metricEach.Name = k
			mp.formatValues("", metricEach, stat, lastStat, now, lastTime)
		}
	}
}

func (mp *MackerelPlugin) formatValues(prefix string, metric Metrics, stat map[string]float64, lastStat map[string]float64, now time.Time, lastTime time.Time) {
	name := metric.Name
	value, ok := stat[name]
	if !ok {
		return
	}
	if metric.Diff {
		lastValue, ok := lastStat[name]
		if ok {
			var err error
			value, err = mp.calcDiff(value, now, lastValue, lastTime)
			if err != nil {
				log.Println("OutputValues: ", err)
			}
		} else {
			log.Printf("%s does not exist at last fetch\n", metric.Name)
			return
		}
	}

	if metric.Scale != 0 {
		value *= metric.Scale
	}

	metricNames := []string{}
	if p, ok := mp.Plugin.(PluginWithPrefix); ok {
		metricNames = append(metricNames, p.MetricKeyPrefix())
	}
	if prefix != "" {
		metricNames = append(metricNames, prefix)
	}
	metricNames = append(metricNames, metric.Name)
	mp.printValue(mp.getWriter(), strings.Join(metricNames, "."), value, now)
}

// GraphDef is graph definitions
type GraphDef struct {
	Graphs map[string]Graphs `json:"graphs"`
}

func title(s string) string {
	r := strings.NewReplacer(".", " ", "_", " ", "*", "", "#", "")
	return strings.TrimSpace(strings.Title(r.Replace(s)))
}

// OutputDefinitions outputs graph definitions
func (mp *MackerelPlugin) OutputDefinitions() {
	fmt.Fprintln(mp.getWriter(), "# mackerel-agent-plugin")
	graphs := make(map[string]Graphs)
	for key, graph := range mp.GraphDefinition() {
		g := graph
		k := key
		if p, ok := mp.Plugin.(PluginWithPrefix); ok {
			prefix := p.MetricKeyPrefix()
			if k == "" {
				k = prefix
			} else {
				k = prefix + "." + k
			}
		}
		if g.Label == "" {
			g.Label = title(k)
		}
		metrics := []Metrics{}
		for _, v := range g.Metrics {
			if v.Label == "" {
				v.Label = title(v.Name)
			}
			metrics = append(metrics, v)
		}
		g.Metrics = metrics
		graphs[k] = g
	}
	var graphdef GraphDef
	graphdef.Graphs = graphs
	b, err := json.Marshal(graphdef)
	if err != nil {
		log.Fatalln("OutputDefinitions: ", err)
	}
	fmt.Fprintln(mp.getWriter(), string(b))
}

// Run the plugin
func (mp *MackerelPlugin) Run() {
	if os.Getenv("MACKEREL_AGENT_PLUGIN_META") != "" {
		mp.OutputDefinitions()
	} else {
		mp.OutputValues()
	}
}
<|endoftext|>


<commit_before>package hamming

import (
	"testing"
)

var testCases = []struct {
	expected         int
	strandA, strandB string
	description      string
}{
	{0, "GGACTGA", "GGACTGA", "no difference between identical strands"},
	{3, "ACT", "GGA", "complete hamming distance in small strand"},
	{9, "GGACGGATTCTG", "AGGACGGATTCT", "distance in off by one strand"},
	{1, "GGACG", "GGTCG", "small hamming distance in middle somewhere"},
	{2, "ACCAGGG", "ACTATGG", "larger distance"},
	{3, "AAACTAGGGG", "AGGCTAGCGGTAGGAC", "ignores extra length on other strand when longer"},
	{5, "GACTACGGACAGGGTAGGGAAT", "GACATCGCACACC", "ignores extra length on original strand when longer"},
}

func TestHamming(t *testing.T) {
	for _, tc := range testCases {

		observed := Distance(tc.strandA, tc.strandB)

		if tc.expected != observed {
			t.Fatalf(`%s:
expected: %v
observed: %v`,
				tc.description,
				tc.expected,
				observed,
			)
		}
	}
}
<commit_msg>Update hamming tests in Go<commit_after>package hamming

import (
	"testing"
)

var testCases = []struct {
	expected         int
	strandA, strandB string
	description      string
}{
	{0, "", "", "no difference between empty strands"},
	{2, "AG", "CT", "complete hamming distance for small strand"},
	{0, "A", "A", "no difference between identical strands"},
	{1, "A", "G", "complete distance for single nucleotide strand"},
	{1, "AT", "CT", "small hamming distance"},
	{1, "GGACG", "GGTCG", "small hamming distance in longer strand"},
	{0, "AAAG", "AAA", "ignores extra length on first strand when longer"},
	{0, "AAA", "AAAG", "ignores extra length on second strand when longer"},
	{4, "GATACA", "GCATAA", "large hamming distance"},
	{9, "GGACGGATTCTG", "AGGACGGATTCT", "hamming distance in very long strand"},
}

func TestHamming(t *testing.T) {
	for _, tc := range testCases {

		observed := Distance(tc.strandA, tc.strandB)

		if tc.expected != observed {
			t.Fatalf(`%s:
expected: %v
observed: %v`,
				tc.description,
				tc.expected,
				observed,
			)
		}
	}
}
<|endoftext|>


<commit_before>package auctionrunner_test

import (
	"errors"
	"time"

	"github.com/cloudfoundry/gunk/timeprovider/faketimeprovider"
	"github.com/cloudfoundry/gunk/workpool"

	. "github.com/cloudfoundry-incubator/auction/auctionrunner"
	"github.com/cloudfoundry-incubator/auction/auctiontypes"
	"github.com/cloudfoundry-incubator/auction/auctiontypes/fakes"
	"github.com/cloudfoundry-incubator/runtime-schema/models"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("WorkDistributor", func() {
	var clients map[string]*fakes.FakeSimulationAuctionRep
	var cells map[string]*Cell
	var timeProvider *faketimeprovider.FakeTimeProvider
	var workPool *workpool.WorkPool
	var results DistributeWorkResults

	BeforeEach(func() {
		timeProvider = faketimeprovider.New(time.Now())
		workPool = workpool.NewWorkPool(5)

		clients = map[string]*fakes.FakeSimulationAuctionRep{}
		cells = map[string]*Cell{}
	})

	Context("when the cells are empty", func() {
		It("immediately returns everything as having failed, incrementing the attempt number", func() {
			startAuction := BuildStartAuction(
				BuildLRPStartAuction("pg-7", "ig-7", 0, "lucid64", 10, 10),
				timeProvider.Time(),
			)

			startAuctions := []auctiontypes.StartAuction{startAuction}

			stopAuction := BuildStopAuction(
				BuildLRPStopAuction("pg-1", 1),
				timeProvider.Time(),
			)

			stopAuctions := []auctiontypes.StopAuction{stopAuction}

			results := DistributeWork(workPool, map[string]*Cell{}, timeProvider, startAuctions, stopAuctions)
			Ω(results.SuccessfulStarts).Should(BeEmpty())
			Ω(results.SuccessfulStops).Should(BeEmpty())
			startAuction.Attempts = 1
			stopAuction.Attempts = 1
			Ω(results.FailedStarts).Should(ConsistOf(startAuction))
			Ω(results.FailedStops).Should(ConsistOf(stopAuction))
		})
	})

	Describe("handling start auctions", func() {
		var startAuction auctiontypes.StartAuction

		BeforeEach(func() {
			clients["A"] = &fakes.FakeSimulationAuctionRep{}
			cells["A"] = NewCell(clients["A"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg-1", "ig-1", 0, 10, 10},
				{"pg-2", "ig-2", 0, 10, 10},
			}))

			clients["B"] = &fakes.FakeSimulationAuctionRep{}
			cells["B"] = NewCell(clients["B"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg-3", "ig-3", 0, 10, 10},
			}))

			startAuction = BuildStartAuction(BuildLRPStartAuction("pg-4", "ig-4", 0, "lucid64", 10, 10), timeProvider.Time())
			timeProvider.Increment(time.Minute)
		})

		Context("when it picks a winner", func() {
			BeforeEach(func() {
				results = DistributeWork(workPool, cells, timeProvider, []auctiontypes.StartAuction{startAuction}, nil)
			})

			It("picks the best cell for the job", func() {
				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(1))

				startsToB := clients["B"].PerformArgsForCall(0).Starts

				Ω(startsToB).Should(ConsistOf(
					startAuction.LRPStartAuction,
				))
			})

			It("marks the start auction as succeeded", func() {
				startAuction.Winner = "B"
				startAuction.Attempts = 1
				startAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStarts).Should(ConsistOf(startAuction))
				Ω(results.FailedStarts).Should(BeEmpty())
			})
		})

		Context("when the cell rejects the start auction", func() {
			BeforeEach(func() {
				clients["B"].PerformReturns(auctiontypes.Work{}, errors.New("boom"))
				results = DistributeWork(workPool, cells, timeProvider, []auctiontypes.StartAuction{startAuction}, nil)
			})

			It("marks the start auction as failed", func() {
				startAuction.Attempts = 1
				Ω(results.SuccessfulStarts).Should(BeEmpty())
				Ω(results.FailedStarts).Should(ConsistOf(startAuction))
			})
		})

		Context("when there is no room", func() {
			BeforeEach(func() {
				startAuction = BuildStartAuction(BuildLRPStartAuction("pg-4", "ig-4", 0, "lucid64", 1000, 1000), timeProvider.Time())
				timeProvider.Increment(time.Minute)
				results = DistributeWork(workPool, cells, timeProvider, []auctiontypes.StartAuction{startAuction}, nil)
			})

			It("should not attempt to start the LRP", func() {
				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
			})

			It("should mark the start auction as failed", func() {
				startAuction.Attempts = 1
				Ω(results.SuccessfulStarts).Should(BeEmpty())
				Ω(results.FailedStarts).Should(ConsistOf(startAuction))
			})
		})
	})

	Describe("handling stop auctions", func() {
		var stopAuction auctiontypes.StopAuction

		BeforeEach(func() {
			clients["A"] = &fakes.FakeSimulationAuctionRep{}
			cells["A"] = NewCell(clients["A"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg", "ig-1", 0, 10, 10},
				{"pg", "ig-2", 1, 10, 10},
				{"pg", "ig-3", 1, 10, 10},
				{"pg-one", "ig-4", 0, 10, 10},
				{"pg-other", "ig-5", 0, 10, 10},
			}))

			clients["B"] = &fakes.FakeSimulationAuctionRep{}
			cells["B"] = NewCell(clients["B"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg", "ig-6", 1, 10, 10},
				{"pg-other", "ig-7", 0, 10, 10},
			}))

			clients["C"] = &fakes.FakeSimulationAuctionRep{}
			cells["C"] = NewCell(clients["C"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg", "ig-8", 1, 10, 10},
				{"pg-other", "ig-9", 0, 10, 10},
				{"pg-other", "ig-10", 0, 10, 10},
				{"pg-three", "ig-11", 2, 10, 10},
				{"pg-three", "ig-12", 2, 10, 10},
				{"pg-three", "ig-12", 2, 10, 10},
			}))
		})

		Context("when the stop auction maps onto multiple instances", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg", 1),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("tells the appropriate cells to stop", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(1))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(1))

				stopsToA := clients["A"].PerformArgsForCall(0).Stops
				stopsToC := clients["C"].PerformArgsForCall(0).Stops

				Ω(stopsToA).Should(ConsistOf(
					models.StopLRPInstance{
						ProcessGuid:  "pg",
						InstanceGuid: "ig-2",
						Index:        1,
					},
					models.StopLRPInstance{
						ProcessGuid:  "pg",
						InstanceGuid: "ig-3",
						Index:        1,
					},
				))

				Ω(stopsToC).Should(ConsistOf(models.StopLRPInstance{
					ProcessGuid:  "pg",
					InstanceGuid: "ig-8",
					Index:        1,
				}))
			})

			It("marks the stop auction a success", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				stopAuction.Winner = "B"
				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})

			Context("if a cell fails to stop", func() {
				It("nonetheless markes the stop auction as a success -- if this is really an issue it will come up again later", func() {
					clients["C"].PerformReturns(auctiontypes.Work{}, errors.New("boom"))
					results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

					stopAuction.Winner = "B"
					stopAuction.Attempts = 1
					stopAuction.WaitDuration = time.Minute
					Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
					Ω(results.FailedStops).Should(BeEmpty())
				})
			})
		})

		Context("when the stop auction maps onto a single cell with multiple instances", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg-three", 2),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("stops all but one of the instances (doesn't matter which)", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(1))

				stopsToC := clients["C"].PerformArgsForCall(0).Stops

				Ω(stopsToC).Should(HaveLen(2))

				stopAuction.Winner = "C"
				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})
		})

		Context("when the stop auction maps onto a single instance", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg-one", 0),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("succeeds without taking any actions on any cells", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(0))

				stopAuction.Winner = "A"
				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})
		})

		Context("when no instances are found for the stop auction", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg", 17),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("fails silently -- if this is really an issue it will come up again later", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(0))

				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute

				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})
		})
	})
})
<commit_msg>the auction runner can distribute work amongst cells<commit_after>package auctionrunner_test

import (
	"errors"
	"time"

	"github.com/cloudfoundry/gunk/timeprovider/faketimeprovider"
	"github.com/cloudfoundry/gunk/workpool"

	. "github.com/cloudfoundry-incubator/auction/auctionrunner"
	"github.com/cloudfoundry-incubator/auction/auctiontypes"
	"github.com/cloudfoundry-incubator/auction/auctiontypes/fakes"
	"github.com/cloudfoundry-incubator/runtime-schema/models"

	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

var _ = Describe("WorkDistributor", func() {
	var clients map[string]*fakes.FakeSimulationAuctionRep
	var cells map[string]*Cell
	var timeProvider *faketimeprovider.FakeTimeProvider
	var workPool *workpool.WorkPool
	var results DistributeWorkResults

	BeforeEach(func() {
		timeProvider = faketimeprovider.New(time.Now())
		workPool = workpool.NewWorkPool(5)

		clients = map[string]*fakes.FakeSimulationAuctionRep{}
		cells = map[string]*Cell{}
	})

	Context("when the cells are empty", func() {
		It("immediately returns everything as having failed, incrementing the attempt number", func() {
			startAuction := BuildStartAuction(
				BuildLRPStartAuction("pg-7", "ig-7", 0, "lucid64", 10, 10),
				timeProvider.Time(),
			)

			startAuctions := []auctiontypes.StartAuction{startAuction}

			stopAuction := BuildStopAuction(
				BuildLRPStopAuction("pg-1", 1),
				timeProvider.Time(),
			)

			stopAuctions := []auctiontypes.StopAuction{stopAuction}

			results := DistributeWork(workPool, map[string]*Cell{}, timeProvider, startAuctions, stopAuctions)
			Ω(results.SuccessfulStarts).Should(BeEmpty())
			Ω(results.SuccessfulStops).Should(BeEmpty())
			startAuction.Attempts = 1
			stopAuction.Attempts = 1
			Ω(results.FailedStarts).Should(ConsistOf(startAuction))
			Ω(results.FailedStops).Should(ConsistOf(stopAuction))
		})
	})

	Describe("handling start auctions", func() {
		var startAuction auctiontypes.StartAuction

		BeforeEach(func() {
			clients["A"] = &fakes.FakeSimulationAuctionRep{}
			cells["A"] = NewCell(clients["A"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg-1", "ig-1", 0, 10, 10},
				{"pg-2", "ig-2", 0, 10, 10},
			}))

			clients["B"] = &fakes.FakeSimulationAuctionRep{}
			cells["B"] = NewCell(clients["B"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg-3", "ig-3", 0, 10, 10},
			}))

			startAuction = BuildStartAuction(BuildLRPStartAuction("pg-4", "ig-4", 0, "lucid64", 10, 10), timeProvider.Time())
			timeProvider.Increment(time.Minute)
		})

		Context("when it picks a winner", func() {
			BeforeEach(func() {
				results = DistributeWork(workPool, cells, timeProvider, []auctiontypes.StartAuction{startAuction}, nil)
			})

			It("picks the best cell for the job", func() {
				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(1))

				startsToB := clients["B"].PerformArgsForCall(0).Starts

				Ω(startsToB).Should(ConsistOf(
					startAuction.LRPStartAuction,
				))
			})

			It("marks the start auction as succeeded", func() {
				startAuction.Winner = "B"
				startAuction.Attempts = 1
				startAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStarts).Should(ConsistOf(startAuction))
				Ω(results.FailedStarts).Should(BeEmpty())
			})
		})

		Context("when the cell rejects the start auction", func() {
			BeforeEach(func() {
				clients["B"].PerformReturns(auctiontypes.Work{}, errors.New("boom"))
				results = DistributeWork(workPool, cells, timeProvider, []auctiontypes.StartAuction{startAuction}, nil)
			})

			It("marks the start auction as failed", func() {
				startAuction.Attempts = 1
				Ω(results.SuccessfulStarts).Should(BeEmpty())
				Ω(results.FailedStarts).Should(ConsistOf(startAuction))
			})
		})

		Context("when there is no room", func() {
			BeforeEach(func() {
				startAuction = BuildStartAuction(BuildLRPStartAuction("pg-4", "ig-4", 0, "lucid64", 1000, 1000), timeProvider.Time())
				timeProvider.Increment(time.Minute)
				results = DistributeWork(workPool, cells, timeProvider, []auctiontypes.StartAuction{startAuction}, nil)
			})

			It("should not attempt to start the LRP", func() {
				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
			})

			It("should mark the start auction as failed", func() {
				startAuction.Attempts = 1
				Ω(results.SuccessfulStarts).Should(BeEmpty())
				Ω(results.FailedStarts).Should(ConsistOf(startAuction))
			})
		})
	})

	Describe("handling stop auctions", func() {
		var stopAuction auctiontypes.StopAuction

		BeforeEach(func() {
			clients["A"] = &fakes.FakeSimulationAuctionRep{}
			cells["A"] = NewCell(clients["A"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg", "ig-1", 0, 10, 10},
				{"pg", "ig-2", 1, 10, 10},
				{"pg", "ig-3", 1, 10, 10},
				{"pg-one", "ig-4", 0, 10, 10},
				{"pg-other", "ig-5", 0, 10, 10},
			}))

			clients["B"] = &fakes.FakeSimulationAuctionRep{}
			cells["B"] = NewCell(clients["B"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg", "ig-6", 1, 10, 10},
				{"pg-other", "ig-7", 0, 10, 10},
			}))

			clients["C"] = &fakes.FakeSimulationAuctionRep{}
			cells["C"] = NewCell(clients["C"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg", "ig-8", 1, 10, 10},
				{"pg-other", "ig-9", 0, 10, 10},
				{"pg-other", "ig-10", 0, 10, 10},
				{"pg-three", "ig-11", 2, 10, 10},
				{"pg-three", "ig-12", 2, 10, 10},
				{"pg-three", "ig-12", 2, 10, 10},
			}))
		})

		Context("when the stop auction maps onto multiple instances", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg", 1),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("tells the appropriate cells to stop", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(1))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(1))

				stopsToA := clients["A"].PerformArgsForCall(0).Stops
				stopsToC := clients["C"].PerformArgsForCall(0).Stops

				Ω(stopsToA).Should(ConsistOf(
					models.StopLRPInstance{
						ProcessGuid:  "pg",
						InstanceGuid: "ig-2",
						Index:        1,
					},
					models.StopLRPInstance{
						ProcessGuid:  "pg",
						InstanceGuid: "ig-3",
						Index:        1,
					},
				))

				Ω(stopsToC).Should(ConsistOf(models.StopLRPInstance{
					ProcessGuid:  "pg",
					InstanceGuid: "ig-8",
					Index:        1,
				}))
			})

			It("marks the stop auction a success", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				stopAuction.Winner = "B"
				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})

			Context("if a cell fails to stop", func() {
				It("nonetheless markes the stop auction as a success -- if this is really an issue it will come up again later", func() {
					clients["C"].PerformReturns(auctiontypes.Work{}, errors.New("boom"))
					results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

					stopAuction.Winner = "B"
					stopAuction.Attempts = 1
					stopAuction.WaitDuration = time.Minute
					Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
					Ω(results.FailedStops).Should(BeEmpty())
				})
			})
		})

		Context("when the stop auction maps onto a single cell with multiple instances", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg-three", 2),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("stops all but one of the instances (doesn't matter which)", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(1))

				stopsToC := clients["C"].PerformArgsForCall(0).Stops

				Ω(stopsToC).Should(HaveLen(2))

				stopAuction.Winner = "C"
				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})
		})

		Context("when the stop auction maps onto a single instance", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg-one", 0),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("succeeds without taking any actions on any cells", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(0))

				stopAuction.Winner = "A"
				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute
				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})
		})

		Context("when no instances are found for the stop auction", func() {
			BeforeEach(func() {
				stopAuction = BuildStopAuction(
					BuildLRPStopAuction("pg", 17),
					timeProvider.Time(),
				)
				timeProvider.Increment(time.Minute)
			})

			It("fails silently -- if this is really an issue it will come up again later", func() {
				results = DistributeWork(workPool, cells, timeProvider, nil, []auctiontypes.StopAuction{stopAuction})

				Ω(clients["A"].PerformCallCount()).Should(Equal(0))
				Ω(clients["B"].PerformCallCount()).Should(Equal(0))
				Ω(clients["C"].PerformCallCount()).Should(Equal(0))

				stopAuction.Attempts = 1
				stopAuction.WaitDuration = time.Minute

				Ω(results.SuccessfulStops).Should(ConsistOf(stopAuction))
				Ω(results.FailedStops).Should(BeEmpty())
			})
		})
	})

	Describe("a comprehensive scenario", func() {
		BeforeEach(func() {
			clients["A"] = &fakes.FakeSimulationAuctionRep{}
			cells["A"] = NewCell(clients["A"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg-1", "ig-1", 0, 10, 10},
				{"pg-2", "ig-2", 0, 10, 10},
				{"pg-dupe", "ig-3", 0, 80, 80},
			}))

			clients["B"] = &fakes.FakeSimulationAuctionRep{}
			cells["B"] = NewCell(clients["B"], BuildRepState(100, 100, 100, []auctiontypes.LRP{
				{"pg-3", "ig-4", 0, 10, 10},
				{"pg-dupe", "ig-5", 0, 80, 80},
			}))
		})

		It("should optimize the distribution", func() {
			stopAuctions := []auctiontypes.StopAuction{
				BuildStopAuction(
					BuildLRPStopAuction("pg-dupe", 0),
					timeProvider.Time(),
				),
			}

			startAuctions := []auctiontypes.StartAuction{
				BuildStartAuction(
					BuildLRPStartAuction("pg-3", "ig-new-1", 1, "lucid64", 40, 40),
					timeProvider.Time(),
				),
				BuildStartAuction(
					BuildLRPStartAuction("pg-2", "ig-new-2", 1, "lucid64", 10, 10),
					timeProvider.Time(),
				),
				BuildStartAuction(
					BuildLRPStartAuction("pg-nope", "ig-nope", 1, ".net", 10, 10),
					timeProvider.Time(),
				),
			}

			results = DistributeWork(workPool, cells, timeProvider, startAuctions, stopAuctions)

			Ω(clients["A"].PerformCallCount()).Should(Equal(1))
			Ω(clients["B"].PerformCallCount()).Should(Equal(1))

			Ω(clients["A"].PerformArgsForCall(0).Stops).Should(ConsistOf(models.StopLRPInstance{
				ProcessGuid:  "pg-dupe",
				InstanceGuid: "ig-3",
				Index:        0,
			}))
			Ω(clients["B"].PerformArgsForCall(0).Stops).Should(BeEmpty())

			Ω(clients["A"].PerformArgsForCall(0).Starts).Should(ConsistOf(startAuctions[0].LRPStartAuction))
			Ω(clients["B"].PerformArgsForCall(0).Starts).Should(ConsistOf(startAuctions[1].LRPStartAuction))

			successfulStop := stopAuctions[0]
			successfulStop.Winner = "B"
			successfulStop.Attempts = 1
			Ω(results.SuccessfulStops).Should(ConsistOf(successfulStop))

			successfulStart1 := startAuctions[0]
			successfulStart1.Winner = "A"
			successfulStart1.Attempts = 1
			successfulStart2 := startAuctions[1]
			successfulStart2.Winner = "B"
			successfulStart2.Attempts = 1
			Ω(results.SuccessfulStarts).Should(ConsistOf(successfulStart1, successfulStart2))

			Ω(results.FailedStops).Should(BeEmpty())
			failedStartAuction := startAuctions[2]
			failedStartAuction.Attempts = 1
			Ω(results.FailedStarts).Should(ConsistOf(failedStartAuction))
		})
	})
})
<|endoftext|>