<commit_before>package vime
import "fmt"
import "math/rand"
import "time"
import "strings"

type Vime struct {
    points int
    player_x int
    player_y int
    field [][]string
    instruction string
    result string
    last string
    lost bool
    auto bool
    launch_count int
    death string

    Field_limit int
    Win_condition int

    Obstruction string
    Objective string
    Danger string
    Platform string
    Penalty string
    Empty string
    Launcher_r string
    Launcher_l string
    Launcher_u string
    Launcher_d string
    Player string
    Player_alt string

    // Set these to -1 to disable them.
    // A 0 value gives them default values during initialization.
    Obstruction_prob int
    Objective_prob int
    Danger_prob int
    Platform_prob int
    Penalty_prob int
    Launcher_r_prob int
    Launcher_l_prob int
    Launcher_u_prob int
    Launcher_d_prob int

    Key_r string
    Key_l string
    Key_u string
    Key_d string
    Key_R string
    Key_L string
    Key_U string
    Key_D string
    Key_ping string
    Key_quit string
}
func (this *Vime) Initialize() {
    this.lost = false
    this.auto = false
    rand.Seed(time.Now().UTC().UnixNano())

    if this.Field_limit == 0 { this.Field_limit = 31 }
    if this.player_x == 0 { this.player_x = this.Field_limit / 2 }
    if this.player_y == 0 { this.player_y = this.Field_limit / 2 }
    if this.Win_condition == 0 { this.Win_condition = 20 }

    if this.Empty == "" { this.Empty = " " }
    if this.Player == "" { this.Player = "⍟" }
    if this.Danger == "" { this.Danger = "⚠" }
    if this.Penalty == "" { this.Penalty = "-" }
    if this.Platform == "" { this.Platform = "⛀" }
    if this.Objective == "" { this.Objective = "+" }
    if this.Launcher_r == "" { this.Launcher_r = "→" }
    if this.Launcher_l == "" { this.Launcher_l = "←" }
    if this.Launcher_u == "" { this.Launcher_u = "↑" }
    if this.Launcher_d == "" { this.Launcher_d = "↓" }
    if this.Player_alt == "" { this.Player_alt = "✪" }
    if this.Obstruction == "" { this.Obstruction = "𝌆" }

    if this.Danger_prob == 0 { this.Danger_prob = 10 }
    if this.Penalty_prob == 0 { this.Penalty_prob = 20 }
    if this.Platform_prob == 0 { this.Platform_prob = 5 }
    if this.Objective_prob == 0 { this.Objective_prob = 4 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Obstruction_prob == 0 { this.Obstruction_prob = 15 }

    if this.Key_r == "" { this.Key_r = "ldfu" }
    if this.Key_l == "" { this.Key_l = "habo" }
    if this.Key_u == "" { this.Key_u = "kwp." }
    if this.Key_d == "" { this.Key_d = "jsne" }
    if this.Key_R == "" { this.Key_R = "LDFU" }
    if this.Key_L == "" { this.Key_L = "HABO" }
    if this.Key_U == "" { this.Key_U = "KWP>" }
    if this.Key_D == "" { this.Key_D = "JSNE" }
    if this.Key_ping == "" { this.Key_ping = "zZ" }
    if this.Key_quit == "" { this.Key_quit = "qQ" }

    this.last = this.Empty
    this.field = make([][]string,this.Field_limit)
    for i := 0; i < this.Field_limit; i++ {
        this.field[i] = make([]string,this.Field_limit)
    }
    var objectives_generated int
    objectives_generated = 0
    for i := 0; i < this.Field_limit; i++ {
        for j := 0; j < this.Field_limit; j++ {
            this.field[i][j] = this.Empty
            if rand.Intn(100-0) < this.Danger_prob { this.field[i][j] = this.Danger }
            if rand.Intn(100-0) < this.Penalty_prob { this.field[i][j] = this.Penalty }
            if rand.Intn(100-0) < this.Obstruction_prob { this.field[i][j] = this.Obstruction }
            if rand.Intn(100-0) < this.Platform_prob { this.field[i][j] = this.Platform }
            if rand.Intn(100-0) < this.Launcher_r_prob { this.field[i][j] = this.Launcher_r }
            if rand.Intn(100-0) < this.Launcher_l_prob { this.field[i][j] = this.Launcher_l }
            if rand.Intn(100-0) < this.Launcher_u_prob { this.field[i][j] = this.Launcher_u }
            if rand.Intn(100-0) < this.Launcher_d_prob { this.field[i][j] = this.Launcher_d }
            if rand.Intn(100-0) < this.Objective_prob { this.field[i][j] = this.Objective; objectives_generated += 1 }
            if i < 2 || i >= this.Field_limit - 2 || j < 2 || j >= this.Field_limit - 2 {
                if this.field[i][j] == this.Objective { objectives_generated -= 1 }
                this.field[i][j] = this.Obstruction
            }
        }
    }
    this.field[this.player_x][this.player_y] = this.Player
    if objectives_generated < this.Win_condition + 2 { this.Initialize() }
}
func (this *Vime) flush() {
    for i := 0; i < 100; i++ { fmt.Println("") }
}
func (this *Vime) ping(input int) {
    for i := 0; i < input; i++ {
        this.flush()
            this.field[this.player_y][this.player_x] = this.Player_alt
            this.status()
            time.Sleep(200000000)
            this.field[this.player_y][this.player_x] = this.Player
            this.status()
            time.Sleep(200000000)
    }
}
func (this *Vime) step_on() {
    switch this.result {
        case this.Objective: this.points += 1
        case this.Penalty: this.points -= 1
        case this.Danger: this.lost = true; this.death = "danger"
        case this.Launcher_r, this.Launcher_l, this.Launcher_u, this.Launcher_d:
            this.auto = true
            this.launch_count += 1
    }
    this.field[this.player_y][this.player_x] = this.last
}
func (this *Vime) step_off() {
    this.last = this.field[this.player_y][this.player_x]
    switch this.last {
        case this.Penalty, this.Objective: this.last = this.Empty
        case this.Platform: this.last = this.Obstruction
    }
    this.field[this.player_y][this.player_x] = this.Player
}
func (this *Vime) right(distance int) {
    this.result = this.field[this.player_y][this.player_x+distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) down(distance int) {
    this.result = this.field[this.player_y+distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) left(distance int) {
    this.result = this.field[this.player_y][this.player_x-distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) up(distance int) {
    this.result = this.field[this.player_y-distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) automove() {
    if this.launch_count > 1000 {
        this.auto = false
        this.lost = true
        this.death = "launch"
    }
    if this.result == this.Obstruction && this.auto == true {
        this.auto = false
        this.lost = true
        this.death = "obstruction"
    }
    if this.auto == true {
        switch this.result {
            case this.Launcher_r:
                if this.last != this.Launcher_l {
                    this.right(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_l:
                if this.last != this.Launcher_r {
                    this.left(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_u:
                if this.last != this.Launcher_d {
                    this.up(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_d:
                if this.last != this.Launcher_u {
                    this.down(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
        }
    this.auto = false
    }
}
func (this *Vime) execute() {
    var final_letter string = string(this.instruction[len(this.instruction)-1])
    if strings.Contains(this.Key_r, final_letter) { this.right(1) }
    if strings.Contains(this.Key_l, final_letter) { this.left(1) }
    if strings.Contains(this.Key_u, final_letter) { this.up(1) }
    if strings.Contains(this.Key_d, final_letter) { this.down(1) }
    if strings.Contains(this.Key_R, final_letter) { this.right(2); this.instruction = this.Key_r }
    if strings.Contains(this.Key_L, final_letter) { this.left(2); this.instruction = this.Key_l }
    if strings.Contains(this.Key_U, final_letter) { this.up(2); this.instruction = this.Key_u }
    if strings.Contains(this.Key_D, final_letter) { this.down(2); this.instruction = this.Key_d }
    if strings.Contains(this.Key_ping, final_letter) { this.ping(3) }
    if strings.Contains(this.Key_quit, final_letter) { this.lost = true }
    this.launch_count = 0
}
func (this *Vime) status() {
        this.flush()
        for i := 0; i < this.Field_limit; i++ {
            switch i {
                case 0: fmt.Println(this.field[i], " ", "Objective: collect", this.Win_condition)
                case 1: fmt.Println(this.field[i], " ", this.Objective, "is your objective")
                case 2: fmt.Println(this.field[i], " ", this.Obstruction, "obstructs you")
                case 3: fmt.Println(this.field[i], " ", this.Penalty, "is counterproductive")
                case 4: fmt.Println(this.field[i], " ", this.Danger, "will end you")
                case 5: fmt.Println(this.field[i], " ", this.Platform, "will allow you once")
                case 6: fmt.Println(this.field[i], " ", this.Launcher_r, this.Launcher_l, this.Launcher_d, this.Launcher_u, "will move you")
                case 7: fmt.Println(this.field[i], " ", "Quit with q (unless changed)")
                case 8: fmt.Println(this.field[i], " ", "Ping yourself with with z (unless changed)")
                case 9: fmt.Println(this.field[i], " ", "Execute action with \"Enter\"")
                case 10: fmt.Println(this.field[i], " ", "Points:", this.points)
                default: fmt.Println(this.field[i])
            }
        }
}
func (this *Vime) Run() {
    this.ping(5)
    for {
        if this.points >= this.Win_condition { break }
        if this.lost { break }
        this.status()
        fmt.Scanf("%s",&this.instruction)
        this.execute()
    }
        this.flush()
    if this.points >= this.Win_condition {
        fmt.Println("You Win")
    } else {
        switch this.death {
            case "danger":
                fmt.Println("You were ended.")
                fmt.Scanf("%s",&this.instruction)
            case "obstruction":
                fmt.Println("You were launched up against a wall until you lost conciousness.")
                fmt.Scanf("%s",&this.instruction)
            case "launch":
                fmt.Println("As you endlessly bounce between the launchers, you slowly resign yourself to your strange fate.")
                fmt.Println("You are absolutely sure that there are ways to die that are more stupid and trivial than this, but you cannot seem to think of any.")
                fmt.Println("Oh well, plenty of time for that now.")
                fmt.Scanf("%s",&this.instruction)
            default:
                fmt.Println("Game Over")
                fmt.Scanf("%s",&this.instruction)
        }
    }
}
<commit_msg>Win condition no longer automatically exits.<commit_after>package vime
import "fmt"
import "math/rand"
import "time"
import "strings"

type Vime struct {
    points int
    player_x int
    player_y int
    field [][]string
    instruction string
    result string
    last string
    lost bool
    auto bool
    launch_count int
    death string

    Field_limit int
    Win_condition int

    Obstruction string
    Objective string
    Danger string
    Platform string
    Penalty string
    Empty string
    Launcher_r string
    Launcher_l string
    Launcher_u string
    Launcher_d string
    Player string
    Player_alt string

    // Set these to -1 to disable them.
    // A 0 value gives them default values during initialization.
    Obstruction_prob int
    Objective_prob int
    Danger_prob int
    Platform_prob int
    Penalty_prob int
    Launcher_r_prob int
    Launcher_l_prob int
    Launcher_u_prob int
    Launcher_d_prob int

    Key_r string
    Key_l string
    Key_u string
    Key_d string
    Key_R string
    Key_L string
    Key_U string
    Key_D string
    Key_ping string
    Key_quit string
}
func (this *Vime) Initialize() {
    this.lost = false
    this.auto = false
    rand.Seed(time.Now().UTC().UnixNano())

    if this.Field_limit == 0 { this.Field_limit = 31 }
    if this.player_x == 0 { this.player_x = this.Field_limit / 2 }
    if this.player_y == 0 { this.player_y = this.Field_limit / 2 }
    if this.Win_condition == 0 { this.Win_condition = 20 }

    if this.Empty == "" { this.Empty = " " }
    if this.Player == "" { this.Player = "⍟" }
    if this.Danger == "" { this.Danger = "⚠" }
    if this.Penalty == "" { this.Penalty = "-" }
    if this.Platform == "" { this.Platform = "⛀" }
    if this.Objective == "" { this.Objective = "+" }
    if this.Launcher_r == "" { this.Launcher_r = "→" }
    if this.Launcher_l == "" { this.Launcher_l = "←" }
    if this.Launcher_u == "" { this.Launcher_u = "↑" }
    if this.Launcher_d == "" { this.Launcher_d = "↓" }
    if this.Player_alt == "" { this.Player_alt = "✪" }
    if this.Obstruction == "" { this.Obstruction = "𝌆" }

    if this.Danger_prob == 0 { this.Danger_prob = 10 }
    if this.Penalty_prob == 0 { this.Penalty_prob = 20 }
    if this.Platform_prob == 0 { this.Platform_prob = 5 }
    if this.Objective_prob == 0 { this.Objective_prob = 4 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Launcher_l_prob == 0 { this.Launcher_l_prob = 2 }
    if this.Obstruction_prob == 0 { this.Obstruction_prob = 15 }

    if this.Key_r == "" { this.Key_r = "ldfu" }
    if this.Key_l == "" { this.Key_l = "habo" }
    if this.Key_u == "" { this.Key_u = "kwp." }
    if this.Key_d == "" { this.Key_d = "jsne" }
    if this.Key_R == "" { this.Key_R = "LDFU" }
    if this.Key_L == "" { this.Key_L = "HABO" }
    if this.Key_U == "" { this.Key_U = "KWP>" }
    if this.Key_D == "" { this.Key_D = "JSNE" }
    if this.Key_ping == "" { this.Key_ping = "zZ" }
    if this.Key_quit == "" { this.Key_quit = "qQ" }

    this.last = this.Empty
    this.field = make([][]string,this.Field_limit)
    for i := 0; i < this.Field_limit; i++ {
        this.field[i] = make([]string,this.Field_limit)
    }
    var objectives_generated int
    objectives_generated = 0
    for i := 0; i < this.Field_limit; i++ {
        for j := 0; j < this.Field_limit; j++ {
            this.field[i][j] = this.Empty
            if rand.Intn(100-0) < this.Danger_prob { this.field[i][j] = this.Danger }
            if rand.Intn(100-0) < this.Penalty_prob { this.field[i][j] = this.Penalty }
            if rand.Intn(100-0) < this.Obstruction_prob { this.field[i][j] = this.Obstruction }
            if rand.Intn(100-0) < this.Platform_prob { this.field[i][j] = this.Platform }
            if rand.Intn(100-0) < this.Launcher_r_prob { this.field[i][j] = this.Launcher_r }
            if rand.Intn(100-0) < this.Launcher_l_prob { this.field[i][j] = this.Launcher_l }
            if rand.Intn(100-0) < this.Launcher_u_prob { this.field[i][j] = this.Launcher_u }
            if rand.Intn(100-0) < this.Launcher_d_prob { this.field[i][j] = this.Launcher_d }
            if rand.Intn(100-0) < this.Objective_prob { this.field[i][j] = this.Objective; objectives_generated += 1 }
            if i < 2 || i >= this.Field_limit - 2 || j < 2 || j >= this.Field_limit - 2 {
                if this.field[i][j] == this.Objective { objectives_generated -= 1 }
                this.field[i][j] = this.Obstruction
            }
        }
    }
    this.field[this.player_x][this.player_y] = this.Player
    if objectives_generated < this.Win_condition + 2 { this.Initialize() }
}
func (this *Vime) flush() {
    for i := 0; i < 100; i++ { fmt.Println("") }
}
func (this *Vime) ping(input int) {
    for i := 0; i < input; i++ {
        this.flush()
            this.field[this.player_y][this.player_x] = this.Player_alt
            this.status()
            time.Sleep(200000000)
            this.field[this.player_y][this.player_x] = this.Player
            this.status()
            time.Sleep(200000000)
    }
}
func (this *Vime) step_on() {
    switch this.result {
        case this.Objective: this.points += 1
        case this.Penalty: this.points -= 1
        case this.Danger: this.lost = true; this.death = "danger"
        case this.Launcher_r, this.Launcher_l, this.Launcher_u, this.Launcher_d:
            this.auto = true
            this.launch_count += 1
    }
    this.field[this.player_y][this.player_x] = this.last
}
func (this *Vime) step_off() {
    this.last = this.field[this.player_y][this.player_x]
    switch this.last {
        case this.Penalty, this.Objective: this.last = this.Empty
        case this.Platform: this.last = this.Obstruction
    }
    this.field[this.player_y][this.player_x] = this.Player
}
func (this *Vime) right(distance int) {
    this.result = this.field[this.player_y][this.player_x+distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) down(distance int) {
    this.result = this.field[this.player_y+distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y += distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) left(distance int) {
    this.result = this.field[this.player_y][this.player_x-distance]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_x -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) up(distance int) {
    this.result = this.field[this.player_y-distance][this.player_x]
    if this.result != this.Obstruction {
        this.step_on()
        this.player_y -= distance
        this.step_off()
    }
    this.automove()
}
func (this *Vime) automove() {
    if this.launch_count > 1000 {
        this.auto = false
        this.lost = true
        this.death = "launch"
    }
    if this.result == this.Obstruction && this.auto == true {
        this.auto = false
        this.lost = true
        this.death = "obstruction"
    }
    if this.auto == true {
        switch this.result {
            case this.Launcher_r:
                if this.last != this.Launcher_l {
                    this.right(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_l:
                if this.last != this.Launcher_r {
                    this.left(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_u:
                if this.last != this.Launcher_d {
                    this.up(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
            case this.Launcher_d:
                if this.last != this.Launcher_u {
                    this.down(1)
                } else {
                    this.lost = true
                    this.death = "launch"
                }
        }
    this.auto = false
    }
}
func (this *Vime) execute() {
    var final_letter string = string(this.instruction[len(this.instruction)-1])
    if strings.Contains(this.Key_r, final_letter) { this.right(1) }
    if strings.Contains(this.Key_l, final_letter) { this.left(1) }
    if strings.Contains(this.Key_u, final_letter) { this.up(1) }
    if strings.Contains(this.Key_d, final_letter) { this.down(1) }
    if strings.Contains(this.Key_R, final_letter) { this.right(2); this.instruction = this.Key_r }
    if strings.Contains(this.Key_L, final_letter) { this.left(2); this.instruction = this.Key_l }
    if strings.Contains(this.Key_U, final_letter) { this.up(2); this.instruction = this.Key_u }
    if strings.Contains(this.Key_D, final_letter) { this.down(2); this.instruction = this.Key_d }
    if strings.Contains(this.Key_ping, final_letter) { this.ping(3) }
    if strings.Contains(this.Key_quit, final_letter) { this.lost = true }
    this.launch_count = 0
}
func (this *Vime) status() {
        this.flush()
        for i := 0; i < this.Field_limit; i++ {
            switch i {
                case 0: fmt.Println(this.field[i], " ", "Objective: collect", this.Win_condition)
                case 1: fmt.Println(this.field[i], " ", this.Objective, "is your objective")
                case 2: fmt.Println(this.field[i], " ", this.Obstruction, "obstructs you")
                case 3: fmt.Println(this.field[i], " ", this.Penalty, "is counterproductive")
                case 4: fmt.Println(this.field[i], " ", this.Danger, "will end you")
                case 5: fmt.Println(this.field[i], " ", this.Platform, "will allow you once")
                case 6: fmt.Println(this.field[i], " ", this.Launcher_r, this.Launcher_l, this.Launcher_d, this.Launcher_u, "will move you")
                case 7: fmt.Println(this.field[i], " ", "Quit with q (unless changed)")
                case 8: fmt.Println(this.field[i], " ", "Ping yourself with with z (unless changed)")
                case 9: fmt.Println(this.field[i], " ", "Execute action with \"Enter\"")
                case 10: fmt.Println(this.field[i], " ", "Points:", this.points)
                default: fmt.Println(this.field[i])
            }
        }
}
func (this *Vime) Run() {
    this.ping(5)
    for {
        if this.points >= this.Win_condition { break }
        if this.lost { break }
        this.status()
        fmt.Scanf("%s",&this.instruction)
        this.execute()
    }
        this.flush()
    if this.points >= this.Win_condition {
        fmt.Println("You Win")
        fmt.Scanf("%s",&this.instruction)
    } else {
        switch this.death {
            case "danger":
                fmt.Println("You were ended.")
                fmt.Scanf("%s",&this.instruction)
            case "obstruction":
                fmt.Println("You were launched up against a wall until you lost conciousness.")
                fmt.Scanf("%s",&this.instruction)
            case "launch":
                fmt.Println("As you endlessly bounce between the launchers, you slowly resign yourself to your strange fate.")
                fmt.Println("You are absolutely sure that there are ways to die that are more stupid and trivial than this, but you cannot seem to think of any.")
                fmt.Println("Oh well, plenty of time for that now.")
                fmt.Scanf("%s",&this.instruction)
            default:
                fmt.Println("Game Over")
                fmt.Scanf("%s",&this.instruction)
        }
    }
}
<|endoftext|>


<commit_before>package kafka

import (
	"net"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/optiopay/kafka/proto"
)

type serializableMessage interface {
	Bytes() ([]byte, error)
}

func testServer(messages ...serializableMessage) (net.Listener, error) {
	ln, err := net.Listen("tcp", "")
	if err != nil {
		return nil, err
	}

	responses := make([][]byte, len(messages))
	for i, m := range messages {
		b, err := m.Bytes()
		if err != nil {
			_ = ln.Close()
			return nil, err
		}
		responses[i] = b
	}

	go func() {
		for {
			cli, err := ln.Accept()
			if err != nil {
				return
			}

			go func(conn net.Conn) {
				time.Sleep(time.Millisecond * 50)
				for _, resp := range responses {
					_, _ = cli.Write(resp)
				}
				cli.Close()
			}(cli)
		}
	}()
	return ln, nil
}

func testServer2() (net.Listener, chan serializableMessage, error) {
	ln, err := net.Listen("tcp", "")
	if err != nil {
		return nil, nil, err
	}

	msgs := make(chan serializableMessage)

	go func() {
		for {
			cli, err := ln.Accept()
			if err != nil {
				return
			}

			go func(conn net.Conn) {
				defer func() { _ = cli.Close() }()

				for msg := range msgs {
					b, err := msg.Bytes()
					if err != nil {
						panic(err)
					}
					if _, err = cli.Write(b); err != nil {
						return
					}
				}
			}(cli)
		}
	}()
	return ln, msgs, nil
}

func TestConnectionMetadata(t *testing.T) {
	resp1 := &proto.MetadataResp{
		CorrelationID: 1,
		Brokers: []proto.MetadataRespBroker{
			proto.MetadataRespBroker{
				NodeID: 666,
				Host:   "example.com",
				Port:   999,
			},
		},
		Topics: []proto.MetadataRespTopic{
			proto.MetadataRespTopic{
				Name: "foo",
				Partitions: []proto.MetadataRespPartition{
					proto.MetadataRespPartition{
						ID:       7,
						Leader:   7,
						Replicas: []int32{7},
						Isrs:     []int32{7},
					},
				},
			},
		},
	}
	ln, err := testServer(resp1)
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Metadata(&proto.MetadataReq{
		ClientID: "tester",
		Topics:   []string{"first", "second"},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
	if err := conn.Close(); err != nil {
		t.Fatalf("could not close kafka connection: %s", err)
	}
	if err := ln.Close(); err != nil {
		t.Fatalf("could not close test server: %s", err)
	}
}

func TestConnectionProduce(t *testing.T) {
	resp1 := &proto.ProduceResp{
		CorrelationID: 1,
		Topics: []proto.ProduceRespTopic{
			proto.ProduceRespTopic{
				Name: "first",
				Partitions: []proto.ProduceRespPartition{
					proto.ProduceRespPartition{
						ID:     0,
						Err:    nil,
						Offset: 4,
					},
				},
			},
		},
	}
	resp2 := &proto.ProduceResp{
		CorrelationID: 2,
		Topics: []proto.ProduceRespTopic{
			proto.ProduceRespTopic{
				Name: "first",
				Partitions: []proto.ProduceRespPartition{
					proto.ProduceRespPartition{
						ID:     0,
						Err:    proto.ErrLeaderNotAvailable,
						Offset: -1,
					},
				},
			},
		},
	}

	ln, msgs, err := testServer2()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}

	go func() {
		time.Sleep(time.Millisecond * 10)
		msgs <- resp1

		time.Sleep(time.Millisecond * 10)
		msgs <- resp2
	}()

	resp, err := conn.Produce(&proto.ProduceReq{
		CorrelationID: 1,
		ClientID:      "tester",
		RequiredAcks:  proto.RequiredAcksAll,
		Timeout:       time.Second,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "first",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Key: []byte("key 1"), Value: []byte("value 1")},
							&proto.Message{Key: []byte("key 2"), Value: []byte("value 2")},
						},
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
	resp, err = conn.Produce(&proto.ProduceReq{
		CorrelationID: 2,
		ClientID:      "tester",
		RequiredAcks:  proto.RequiredAcksAll,
		Timeout:       time.Second,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "first",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Key: []byte("key"), Value: []byte("value")},
						},
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp2) {
		t.Fatalf("expected different response %#v", resp)
	}

	if err := conn.Close(); err != nil {
		t.Fatalf("could not close kafka connection: %s", err)
	}
	if err := ln.Close(); err != nil {
		t.Fatalf("could not close test server: %s", err)
	}
}

func TestConnectionFetch(t *testing.T) {
	messages := []*proto.Message{
		&proto.Message{Offset: 4, Key: []byte("f"), Value: []byte("first")},
		&proto.Message{Offset: 5, Key: []byte("s"), Value: []byte("second message")},
	}
	for _, m := range messages {
		m.Crc = proto.ComputeCrc(m)
	}

	resp1 := &proto.FetchResp{
		CorrelationID: 1,
		Topics: []proto.FetchRespTopic{
			proto.FetchRespTopic{
				Name: "foo",
				Partitions: []proto.FetchRespPartition{
					proto.FetchRespPartition{
						ID:        1,
						Err:       nil,
						TipOffset: 20,
						Messages:  messages,
					},
				},
			},
			proto.FetchRespTopic{
				Name: "bar",
				Partitions: []proto.FetchRespPartition{
					proto.FetchRespPartition{
						ID:        6,
						Err:       proto.ErrUnknownTopicOrPartition,
						TipOffset: -1,
						Messages:  []*proto.Message{},
					},
				},
			},
		},
	}
	ln, err := testServer(resp1)
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Fetch(&proto.FetchReq{
		ClientID: "tester",
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}

	// before comparison, set attributes as we expect deserializer to do
	for _, m := range messages {
		m.Topic = "foo"
		m.Partition = 1
	}

	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
}

func TestConnectionOffset(t *testing.T) {
	resp1 := &proto.OffsetResp{
		CorrelationID: 1,
		Topics: []proto.OffsetRespTopic{
			proto.OffsetRespTopic{
				Name: "test",
				Partitions: []proto.OffsetRespPartition{
					proto.OffsetRespPartition{
						ID:      0,
						Offsets: []int64{92, 0},
					},
				},
			},
		},
	}
	ln, err := testServer(resp1)
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Offset(&proto.OffsetReq{
		ClientID: "tester",
		Topics: []proto.OffsetReqTopic{
			proto.OffsetReqTopic{
				Name: "test",
				Partitions: []proto.OffsetReqPartition{
					proto.OffsetReqPartition{
						ID:         0,
						TimeMs:     -2,
						MaxOffsets: 2,
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
}

func TestConnectionProduceNoAck(t *testing.T) {
	ln, err := testServer()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Produce(&proto.ProduceReq{
		ClientID:     "tester",
		RequiredAcks: proto.RequiredAcksNone,
		Timeout:      time.Second,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "first",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Key: []byte("key 1"), Value: []byte("value 1")},
							&proto.Message{Key: []byte("key 2"), Value: []byte("value 2")},
						},
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if resp != nil {
		t.Fatalf("expected no response, got %#v", resp)
	}
	if err := conn.Close(); err != nil {
		t.Fatalf("could not close kafka connection: %s", err)
	}
	if err := ln.Close(); err != nil {
		t.Fatalf("could not close test server: %s", err)
	}
}

func TestClosedConnectionWriter(t *testing.T) {
	// create test server with no messages, so that any client connection will
	// be immediately closed
	ln, err := testServer()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}

	longBytes := []byte(strings.Repeat("xxxxxxxxxxxxxxxxxxxxxx", 1000))
	req := proto.ProduceReq{
		ClientID:     "test-client",
		RequiredAcks: proto.RequiredAcksAll,
		Timeout:      100,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "test-topic",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Value: longBytes},
						},
					},
				},
			},
		},
	}
	for i := 0; i < 10; i++ {
		if _, err := conn.Produce(&req); err == nil {
			t.Fatal("message publishing after closing connection should not be possible")
		}
	}

	// although we produced ten requests, because connection is closed, no
	// response channel should be registered
	if len(conn.respc) != 0 {
		t.Fatalf("expected 0 waiting responses, got %d", len(conn.respc))
	}
}

func TestClosedConnectionReader(t *testing.T) {
	// create test server with no messages, so that any client connection will
	// be immediately closed
	ln, err := testServer()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}

	req := &proto.FetchReq{
		ClientID:    "test-client",
		MaxWaitTime: 100,
		MinBytes:    0,
		Topics: []proto.FetchReqTopic{
			proto.FetchReqTopic{
				Name: "my-topic",
				Partitions: []proto.FetchReqPartition{
					proto.FetchReqPartition{
						ID:          0,
						FetchOffset: 1,
						MaxBytes:    100000,
					},
				},
			},
		},
	}

	for i := 0; i < 10; i++ {
		if _, err := conn.Fetch(req); err == nil {
			t.Fatal("fetching from closed connection succeeded")
		}
	}

	// although we produced ten requests, because connection is closed, no
	// response channel should be registered
	if len(conn.respc) != 0 {
		t.Fatalf("expected 0 waiting responses, got %d", len(conn.respc))
	}
}
<commit_msg>make errcheck happy<commit_after>package kafka

import (
	"net"
	"reflect"
	"strings"
	"testing"
	"time"

	"github.com/optiopay/kafka/proto"
)

type serializableMessage interface {
	Bytes() ([]byte, error)
}

func testServer(messages ...serializableMessage) (net.Listener, error) {
	ln, err := net.Listen("tcp", "")
	if err != nil {
		return nil, err
	}

	responses := make([][]byte, len(messages))
	for i, m := range messages {
		b, err := m.Bytes()
		if err != nil {
			_ = ln.Close()
			return nil, err
		}
		responses[i] = b
	}

	go func() {
		for {
			cli, err := ln.Accept()
			if err != nil {
				return
			}

			go func(conn net.Conn) {
				time.Sleep(time.Millisecond * 50)
				for _, resp := range responses {
					_, _ = cli.Write(resp)
				}
				_ = cli.Close()
			}(cli)
		}
	}()
	return ln, nil
}

func testServer2() (net.Listener, chan serializableMessage, error) {
	ln, err := net.Listen("tcp", "")
	if err != nil {
		return nil, nil, err
	}

	msgs := make(chan serializableMessage)

	go func() {
		for {
			cli, err := ln.Accept()
			if err != nil {
				return
			}

			go func(conn net.Conn) {
				defer func() { _ = cli.Close() }()

				for msg := range msgs {
					b, err := msg.Bytes()
					if err != nil {
						panic(err)
					}
					if _, err = cli.Write(b); err != nil {
						return
					}
				}
			}(cli)
		}
	}()
	return ln, msgs, nil
}

func TestConnectionMetadata(t *testing.T) {
	resp1 := &proto.MetadataResp{
		CorrelationID: 1,
		Brokers: []proto.MetadataRespBroker{
			proto.MetadataRespBroker{
				NodeID: 666,
				Host:   "example.com",
				Port:   999,
			},
		},
		Topics: []proto.MetadataRespTopic{
			proto.MetadataRespTopic{
				Name: "foo",
				Partitions: []proto.MetadataRespPartition{
					proto.MetadataRespPartition{
						ID:       7,
						Leader:   7,
						Replicas: []int32{7},
						Isrs:     []int32{7},
					},
				},
			},
		},
	}
	ln, err := testServer(resp1)
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Metadata(&proto.MetadataReq{
		ClientID: "tester",
		Topics:   []string{"first", "second"},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
	if err := conn.Close(); err != nil {
		t.Fatalf("could not close kafka connection: %s", err)
	}
	if err := ln.Close(); err != nil {
		t.Fatalf("could not close test server: %s", err)
	}
}

func TestConnectionProduce(t *testing.T) {
	resp1 := &proto.ProduceResp{
		CorrelationID: 1,
		Topics: []proto.ProduceRespTopic{
			proto.ProduceRespTopic{
				Name: "first",
				Partitions: []proto.ProduceRespPartition{
					proto.ProduceRespPartition{
						ID:     0,
						Err:    nil,
						Offset: 4,
					},
				},
			},
		},
	}
	resp2 := &proto.ProduceResp{
		CorrelationID: 2,
		Topics: []proto.ProduceRespTopic{
			proto.ProduceRespTopic{
				Name: "first",
				Partitions: []proto.ProduceRespPartition{
					proto.ProduceRespPartition{
						ID:     0,
						Err:    proto.ErrLeaderNotAvailable,
						Offset: -1,
					},
				},
			},
		},
	}

	ln, msgs, err := testServer2()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}

	go func() {
		time.Sleep(time.Millisecond * 10)
		msgs <- resp1

		time.Sleep(time.Millisecond * 10)
		msgs <- resp2
	}()

	resp, err := conn.Produce(&proto.ProduceReq{
		CorrelationID: 1,
		ClientID:      "tester",
		RequiredAcks:  proto.RequiredAcksAll,
		Timeout:       time.Second,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "first",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Key: []byte("key 1"), Value: []byte("value 1")},
							&proto.Message{Key: []byte("key 2"), Value: []byte("value 2")},
						},
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
	resp, err = conn.Produce(&proto.ProduceReq{
		CorrelationID: 2,
		ClientID:      "tester",
		RequiredAcks:  proto.RequiredAcksAll,
		Timeout:       time.Second,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "first",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Key: []byte("key"), Value: []byte("value")},
						},
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp2) {
		t.Fatalf("expected different response %#v", resp)
	}

	if err := conn.Close(); err != nil {
		t.Fatalf("could not close kafka connection: %s", err)
	}
	if err := ln.Close(); err != nil {
		t.Fatalf("could not close test server: %s", err)
	}
}

func TestConnectionFetch(t *testing.T) {
	messages := []*proto.Message{
		&proto.Message{Offset: 4, Key: []byte("f"), Value: []byte("first")},
		&proto.Message{Offset: 5, Key: []byte("s"), Value: []byte("second message")},
	}
	for _, m := range messages {
		m.Crc = proto.ComputeCrc(m)
	}

	resp1 := &proto.FetchResp{
		CorrelationID: 1,
		Topics: []proto.FetchRespTopic{
			proto.FetchRespTopic{
				Name: "foo",
				Partitions: []proto.FetchRespPartition{
					proto.FetchRespPartition{
						ID:        1,
						Err:       nil,
						TipOffset: 20,
						Messages:  messages,
					},
				},
			},
			proto.FetchRespTopic{
				Name: "bar",
				Partitions: []proto.FetchRespPartition{
					proto.FetchRespPartition{
						ID:        6,
						Err:       proto.ErrUnknownTopicOrPartition,
						TipOffset: -1,
						Messages:  []*proto.Message{},
					},
				},
			},
		},
	}
	ln, err := testServer(resp1)
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Fetch(&proto.FetchReq{
		ClientID: "tester",
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}

	// before comparison, set attributes as we expect deserializer to do
	for _, m := range messages {
		m.Topic = "foo"
		m.Partition = 1
	}

	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
}

func TestConnectionOffset(t *testing.T) {
	resp1 := &proto.OffsetResp{
		CorrelationID: 1,
		Topics: []proto.OffsetRespTopic{
			proto.OffsetRespTopic{
				Name: "test",
				Partitions: []proto.OffsetRespPartition{
					proto.OffsetRespPartition{
						ID:      0,
						Offsets: []int64{92, 0},
					},
				},
			},
		},
	}
	ln, err := testServer(resp1)
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Offset(&proto.OffsetReq{
		ClientID: "tester",
		Topics: []proto.OffsetReqTopic{
			proto.OffsetReqTopic{
				Name: "test",
				Partitions: []proto.OffsetReqPartition{
					proto.OffsetReqPartition{
						ID:         0,
						TimeMs:     -2,
						MaxOffsets: 2,
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if !reflect.DeepEqual(resp, resp1) {
		t.Fatalf("expected different response %#v", resp)
	}
}

func TestConnectionProduceNoAck(t *testing.T) {
	ln, err := testServer()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}
	resp, err := conn.Produce(&proto.ProduceReq{
		ClientID:     "tester",
		RequiredAcks: proto.RequiredAcksNone,
		Timeout:      time.Second,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "first",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Key: []byte("key 1"), Value: []byte("value 1")},
							&proto.Message{Key: []byte("key 2"), Value: []byte("value 2")},
						},
					},
				},
			},
		},
	})
	if err != nil {
		t.Fatalf("could not fetch response: %s", err)
	}
	if resp != nil {
		t.Fatalf("expected no response, got %#v", resp)
	}
	if err := conn.Close(); err != nil {
		t.Fatalf("could not close kafka connection: %s", err)
	}
	if err := ln.Close(); err != nil {
		t.Fatalf("could not close test server: %s", err)
	}
}

func TestClosedConnectionWriter(t *testing.T) {
	// create test server with no messages, so that any client connection will
	// be immediately closed
	ln, err := testServer()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}

	longBytes := []byte(strings.Repeat("xxxxxxxxxxxxxxxxxxxxxx", 1000))
	req := proto.ProduceReq{
		ClientID:     "test-client",
		RequiredAcks: proto.RequiredAcksAll,
		Timeout:      100,
		Topics: []proto.ProduceReqTopic{
			proto.ProduceReqTopic{
				Name: "test-topic",
				Partitions: []proto.ProduceReqPartition{
					proto.ProduceReqPartition{
						ID: 0,
						Messages: []*proto.Message{
							&proto.Message{Value: longBytes},
						},
					},
				},
			},
		},
	}
	for i := 0; i < 10; i++ {
		if _, err := conn.Produce(&req); err == nil {
			t.Fatal("message publishing after closing connection should not be possible")
		}
	}

	// although we produced ten requests, because connection is closed, no
	// response channel should be registered
	if len(conn.respc) != 0 {
		t.Fatalf("expected 0 waiting responses, got %d", len(conn.respc))
	}
}

func TestClosedConnectionReader(t *testing.T) {
	// create test server with no messages, so that any client connection will
	// be immediately closed
	ln, err := testServer()
	if err != nil {
		t.Fatalf("test server error: %s", err)
	}
	conn, err := newTCPConnection(ln.Addr().String(), time.Second)
	if err != nil {
		t.Fatalf("could not conect to test server: %s", err)
	}

	req := &proto.FetchReq{
		ClientID:    "test-client",
		MaxWaitTime: 100,
		MinBytes:    0,
		Topics: []proto.FetchReqTopic{
			proto.FetchReqTopic{
				Name: "my-topic",
				Partitions: []proto.FetchReqPartition{
					proto.FetchReqPartition{
						ID:          0,
						FetchOffset: 1,
						MaxBytes:    100000,
					},
				},
			},
		},
	}

	for i := 0; i < 10; i++ {
		if _, err := conn.Fetch(req); err == nil {
			t.Fatal("fetching from closed connection succeeded")
		}
	}

	// although we produced ten requests, because connection is closed, no
	// response channel should be registered
	if len(conn.respc) != 0 {
		t.Fatalf("expected 0 waiting responses, got %d", len(conn.respc))
	}
}
<|endoftext|>


<commit_before>package hdfs

import (
	"os"
	"path"
	"path/filepath"
	"sort"
)

// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order, which makes the output deterministic but means that for very large
// directories Walk can be inefficient. Walk does not follow symbolic links.
func (c *Client) Walk(root string, walkFn filepath.WalkFunc) error {
	return c.walk(root, walkFn)
}

func (c *Client) walk(path string, walkFn filepath.WalkFunc) error {
	file, err := c.Open(path)
	var info os.FileInfo
	if file != nil {
		info = file.Stat()
	}

	err = walkFn(path, info, err)
	if err != nil {
		if info.IsDir() && err == filepath.SkipDir {
			return nil
		}

		return err
	}

	if info == nil || !info.IsDir() {
		return nil
	}

	names, err := file.Readdirnames(0)
	if err != nil {
		return walkFn(path, info, err)
	}

	sort.Strings(names)
	for _, name := range names {
		err = c.walk(path.Join(path, name), walkFn)
		if err != nil {
			return err
		}
	}

	return nil
}
<commit_msg>Revert "Never use filepath.Join on the client"<commit_after>package hdfs

import (
	"os"
	"path/filepath"
	"sort"
)

// Walk walks the file tree rooted at root, calling walkFn for each file or
// directory in the tree, including root. All errors that arise visiting files
// and directories are filtered by walkFn. The files are walked in lexical
// order, which makes the output deterministic but means that for very large
// directories Walk can be inefficient. Walk does not follow symbolic links.
func (c *Client) Walk(root string, walkFn filepath.WalkFunc) error {
	return c.walk(root, walkFn)
}

func (c *Client) walk(path string, walkFn filepath.WalkFunc) error {
	file, err := c.Open(path)
	var info os.FileInfo
	if file != nil {
		info = file.Stat()
	}

	err = walkFn(path, info, err)
	if err != nil {
		if info.IsDir() && err == filepath.SkipDir {
			return nil
		}

		return err
	}

	if info == nil || !info.IsDir() {
		return nil
	}

	names, err := file.Readdirnames(0)
	if err != nil {
		return walkFn(path, info, err)
	}

	sort.Strings(names)
	for _, name := range names {
		err = c.walk(filepath.Join(path, name), walkFn)
		if err != nil {
			return err
		}
	}

	return nil
}
<|endoftext|>


<commit_before>// Copyright (c) 2013 Joshua Elliott
// Released under the MIT License
// http://opensource.org/licenses/MIT

package turnpike

import (
	"encoding/json"
	"net/url"
	"regexp"
	"strconv"
)

const (
	msgWelcome = iota
	msgPrefix
	msgCall
	msgCallResult
	msgCallError
	msgSubscribe
	msgUnsubscribe
	msgPublish
	msgEvent
)

const wampProtocolVersion = 1

var (
	typeReg = regexp.MustCompile("^\\s*\\[\\s*(\\d+)\\s*,")
)

// A WAMPError is returned when attempting to create a message that does not follow the WAMP
// protocol
type WAMPError struct {
	Msg string
}

var (
	// An ErrInvalidURI describes an invalid URI.
	ErrInvalidURI = &WAMPError{"invalid URI"}
	// An ErrInvalidNumArgs describes invalid number of arguments in a message.
	ErrInvalidNumArgs = &WAMPError{"invalid number of arguments in message"}
	// An ErrUnsupportedProtocol describes an unsupported WAMP protocol in a welcome message.
	ErrUnsupportedProtocol = &WAMPError{"unsupported protocol"}
)

// Error implements the error interface to provide a message.
func (e *WAMPError) Error() string {
	return "wamp: " + e.Msg
}

func parseMessageType(msg string) int {
	match := typeReg.FindStringSubmatch(msg)
	if match == nil {
		return -1
	}
	i, _ := strconv.Atoi(match[1])
	return i
}

func messageTypeString(typ int) string {
	types := []string{"WELCOME", "PREFIX", "CALL", "CALLRESULT", "CALLERROR", "SUBSCRIBE", "UNSUBSCRIBE", "PUBLISH", "EVENT"}
	if typ >= 0 && typ < 9 {
		return types[typ]
	}
	return ""
}

// WELCOME
type welcomeMsg struct {
	SessionId       string
	ProtocolVersion int
	ServerIdent     string
}

func (msg *welcomeMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 4 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.SessionId, ok = data[1].(string); !ok {
		return &WAMPError{"invalid session ID"}
	}
	if protocolVersion, ok := data[2].(float64); ok {
		msg.ProtocolVersion = int(protocolVersion)
	} else {
		return ErrUnsupportedProtocol
	}
	if msg.ServerIdent, ok = data[3].(string); !ok {
		return &WAMPError{"invalid server identity"}
	}
	return nil
}

// Welcome returns a json encoded WAMP 'WELCOME' message as a byte slice
// sessionId is a randomly generated string provided by the server, serverIdent
// is a string that identifies the WAMP server
func createWelcome(sessionId, serverIdent string) (string, error) {
	return createWAMPMessage(msgWelcome, sessionId, wampProtocolVersion, serverIdent)
}

// PREFIX
type prefixMsg struct {
	Prefix string
	URI    string
}

func (msg *prefixMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.Prefix, ok = data[1].(string); !ok {
		return &WAMPError{"invalid prefix"}
	}
	if msg.URI, ok = data[2].(string); !ok {
		return &WAMPError{"invalid URI"}
	}
	return nil
}

// Prefix returns a json encoded WAMP 'PREFIX' message as a byte slice
func createPrefix(prefix, URI string) (string, error) {
	if _, err := url.ParseRequestURI(URI); err != nil {
		return "", &WAMPError{"invalid URI: " + URI}
	}
	return createWAMPMessage(msgPrefix, prefix, URI)
}

// CALL
type callMsg struct {
	CallID   string
	ProcURI  string
	CallArgs []interface{}
}

func (msg *callMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) < 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.CallID, ok = data[1].(string); !ok {
		return &WAMPError{"invalid callID"}
	}
	if msg.ProcURI, ok = data[2].(string); !ok {
		return &WAMPError{"invalid procURI"}
	}
	if len(data) > 3 {
		msg.CallArgs = data[3:]
	}
	return nil
}

// Call returns a json encoded WAMP 'CALL' message as a byte slice
// callID must be a randomly generated string, procURI is the URI of the remote
// procedure to be called, followed by zero or more call arguments
func createCall(callID, procURI string, args ...interface{}) (string, error) {
	if _, err := url.ParseRequestURI(procURI); err != nil {
		return "", &WAMPError{"invalid URI: " + procURI}
	}
	var data []interface{}
	data = append(data, msgCall, callID, procURI)
	data = append(data, args...)
	b, err := json.Marshal(data)
	return string(b), err
}

// CALLRESULT
type callResultMsg struct {
	CallID string
	Result interface{}
}

func (msg *callResultMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.CallID, ok = data[1].(string); !ok {
		return &WAMPError{"invalid callID"}
	}
	msg.Result = data[2]

	return nil
}

// CallResult returns a json encoded WAMP 'CALLRESULT' message as a byte slice
// callID is the randomly generated string provided by the client
func createCallResult(callID string, result interface{}) (string, error) {
	return createWAMPMessage(msgCallResult, callID, result)
}

// CALLERROR
type callErrorMsg struct {
	CallID       string
	ErrorURI     string
	ErrorDesc    string
	ErrorDetails interface{}
}

func (msg *callErrorMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) < 4 || len(data) > 5 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.CallID, ok = data[1].(string); !ok {
		return &WAMPError{"invalid callID"}
	}
	if msg.ErrorURI, ok = data[2].(string); !ok {
		return &WAMPError{"invalid errorURI"}
	}
	if msg.ErrorDesc, ok = data[3].(string); !ok {
		return &WAMPError{"invalid error description"}
	}
	if len(data) == 5 {
		msg.ErrorDetails = data[4]
	}
	return nil
}

// CallError returns a json encoded WAMP 'CALLERROR' message as a byte slice
// callID is the randomly generated string provided by the client, errorURI is
// a URI identifying the error, errorDesc is a human-readable description of the
// error (for developers), errorDetails, if present, is a non-nil object
func createCallError(callID, errorURI, errorDesc string, errorDetails ...interface{}) (string, error) {
	if _, err := url.ParseRequestURI(errorURI); err != nil {
		return "", &WAMPError{"invalid URI: " + errorURI}
	}
	var data []interface{}
	data = append(data, msgCallError, callID, errorURI, errorDesc)
	data = append(data, errorDetails...)
	b, err := json.Marshal(data)
	return string(b), err
}

// SUBSCRIBE
type subscribeMsg struct {
	TopicURI string
}

func (msg *subscribeMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 2 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	return nil
}

// Subscribe returns a json encoded WAMP 'SUBSCRIBE' message as a byte slice
// topicURI is the topic that the client wants to subscribe to
func createSubscribe(topicURI string) (string, error) {
	return createWAMPMessagePubSub(msgSubscribe, topicURI)
}

// UNSUBSCRIBE
type unsubscribeMsg struct {
	TopicURI string
}

func (msg *unsubscribeMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 2 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	return nil
}

// Unsubscribe returns a json encoded WAMP 'UNSUBSCRIBE' message as a byte slice
// topicURI is the topic that the client wants to unsubscribe from
func createUnsubscribe(topicURI string) (string, error) {
	return createWAMPMessagePubSub(msgUnsubscribe, topicURI)
}

// PUBLISH
type publishMsg struct {
	TopicURI     string
	Event        interface{}
	ExcludeMe    bool
	ExcludeList  []string
	EligibleList []string
}

func (msg *publishMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) < 3 || len(data) > 5 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	msg.Event = data[2]
	if len(data) > 3 {
		if msg.ExcludeMe, ok = data[3].(bool); !ok {
			var arr []interface{}
			if arr, ok = data[3].([]interface{}); !ok && data[3] != nil {
				return &WAMPError{"invalid exclude argument"}
			}
			for _, v := range arr {
				if val, ok := v.(string); !ok {
					return &WAMPError{"invalid exclude list"}
				} else {
					msg.ExcludeList = append(msg.ExcludeList, val)
				}
			}
			if len(data) == 5 {
				if arr, ok = data[4].([]interface{}); !ok && data[3] != nil {
					return &WAMPError{"invalid eligable list"}
				}
				for _, v := range arr {
					if val, ok := v.(string); !ok {
						return &WAMPError{"invalid eligable list"}
					} else {
						msg.EligibleList = append(msg.EligibleList, val)
					}
				}
			}
		}
	}
	return nil
}

// Publish returns a json encoded WAMP 'PUBLISH' message as a byte slice
// arguments must be given in one of the following formats:
// [ topicURI, event ]
// [ topicURI, event, excludeMe ]
// [ topicURI, event, exclude ]
// [ topicURI, event, exclude, eligible ]
// event can be nil, a simple json type, or a complex json type
func createPublish(topicURI string, event interface{}, opts ...interface{}) (string, error) {
	var data []interface{}
	data = append(data, msgPublish, topicURI, event)
	data = append(data, opts...)
	return createWAMPMessagePubSub(data...)
}

// EVENT
type eventMsg struct {
	TopicURI string
	Event    interface{}
}

func (msg *eventMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	msg.Event = data[2]

	return nil
}

// Event returns a json encoded WAMP 'EVENT' message as a byte slice
// event can be nil, a simple json type, or a complex json type
func createEvent(topicURI string, event interface{}) (string, error) {
	return createWAMPMessagePubSub(msgEvent, topicURI, event)
}

// createWAMPMessagePubSub checks that the second argument (topicURI) is a valid
// URI and then passes the request on to createWAMPMessage
func createWAMPMessagePubSub(args ...interface{}) (string, error) {
	if _, err := url.ParseRequestURI(args[1].(string)); err != nil {
		return "", &WAMPError{"invalid URI: " + args[1].(string)}
	}
	return createWAMPMessage(args...)
}

// createWAMPMessage returns a JSON encoded list from all the arguments passed to it
func createWAMPMessage(args ...interface{}) (string, error) {
	var data []interface{}
	data = append(data, args...)
	b, err := json.Marshal(data)
	return string(b), err
}
<commit_msg>Fix createCallError to fill in tokens<commit_after>// Copyright (c) 2013 Joshua Elliott
// Released under the MIT License
// http://opensource.org/licenses/MIT

package turnpike

import (
	"encoding/json"
	"fmt"
	"net/url"
	"regexp"
	"strconv"
)

const (
	msgWelcome = iota
	msgPrefix
	msgCall
	msgCallResult
	msgCallError
	msgSubscribe
	msgUnsubscribe
	msgPublish
	msgEvent
)

const wampProtocolVersion = 1

var (
	typeReg = regexp.MustCompile("^\\s*\\[\\s*(\\d+)\\s*,")
)

// A WAMPError is returned when attempting to create a message that does not follow the WAMP
// protocol
type WAMPError struct {
	Msg string
}

var (
	// An ErrInvalidURI describes an invalid URI.
	ErrInvalidURI = &WAMPError{"invalid URI"}
	// An ErrInvalidNumArgs describes invalid number of arguments in a message.
	ErrInvalidNumArgs = &WAMPError{"invalid number of arguments in message"}
	// An ErrUnsupportedProtocol describes an unsupported WAMP protocol in a welcome message.
	ErrUnsupportedProtocol = &WAMPError{"unsupported protocol"}
)

// Error implements the error interface to provide a message.
func (e *WAMPError) Error() string {
	return "wamp: " + e.Msg
}

func parseMessageType(msg string) int {
	match := typeReg.FindStringSubmatch(msg)
	if match == nil {
		return -1
	}
	i, _ := strconv.Atoi(match[1])
	return i
}

func messageTypeString(typ int) string {
	types := []string{"WELCOME", "PREFIX", "CALL", "CALLRESULT", "CALLERROR", "SUBSCRIBE", "UNSUBSCRIBE", "PUBLISH", "EVENT"}
	if typ >= 0 && typ < 9 {
		return types[typ]
	}
	return ""
}

// WELCOME
type welcomeMsg struct {
	SessionId       string
	ProtocolVersion int
	ServerIdent     string
}

func (msg *welcomeMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 4 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.SessionId, ok = data[1].(string); !ok {
		return &WAMPError{"invalid session ID"}
	}
	if protocolVersion, ok := data[2].(float64); ok {
		msg.ProtocolVersion = int(protocolVersion)
	} else {
		return ErrUnsupportedProtocol
	}
	if msg.ServerIdent, ok = data[3].(string); !ok {
		return &WAMPError{"invalid server identity"}
	}
	return nil
}

// Welcome returns a json encoded WAMP 'WELCOME' message as a byte slice
// sessionId is a randomly generated string provided by the server, serverIdent
// is a string that identifies the WAMP server
func createWelcome(sessionId, serverIdent string) (string, error) {
	return createWAMPMessage(msgWelcome, sessionId, wampProtocolVersion, serverIdent)
}

// PREFIX
type prefixMsg struct {
	Prefix string
	URI    string
}

func (msg *prefixMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.Prefix, ok = data[1].(string); !ok {
		return &WAMPError{"invalid prefix"}
	}
	if msg.URI, ok = data[2].(string); !ok {
		return &WAMPError{"invalid URI"}
	}
	return nil
}

// Prefix returns a json encoded WAMP 'PREFIX' message as a byte slice
func createPrefix(prefix, URI string) (string, error) {
	if _, err := url.ParseRequestURI(URI); err != nil {
		return "", &WAMPError{"invalid URI: " + URI}
	}
	return createWAMPMessage(msgPrefix, prefix, URI)
}

// CALL
type callMsg struct {
	CallID   string
	ProcURI  string
	CallArgs []interface{}
}

func (msg *callMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) < 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.CallID, ok = data[1].(string); !ok {
		return &WAMPError{"invalid callID"}
	}
	if msg.ProcURI, ok = data[2].(string); !ok {
		return &WAMPError{"invalid procURI"}
	}
	if len(data) > 3 {
		msg.CallArgs = data[3:]
	}
	return nil
}

// Call returns a json encoded WAMP 'CALL' message as a byte slice
// callID must be a randomly generated string, procURI is the URI of the remote
// procedure to be called, followed by zero or more call arguments
func createCall(callID, procURI string, args ...interface{}) (string, error) {
	if _, err := url.ParseRequestURI(procURI); err != nil {
		return "", &WAMPError{"invalid URI: " + procURI}
	}
	var data []interface{}
	data = append(data, msgCall, callID, procURI)
	data = append(data, args...)
	b, err := json.Marshal(data)
	return string(b), err
}

// CALLRESULT
type callResultMsg struct {
	CallID string
	Result interface{}
}

func (msg *callResultMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.CallID, ok = data[1].(string); !ok {
		return &WAMPError{"invalid callID"}
	}
	msg.Result = data[2]

	return nil
}

// CallResult returns a json encoded WAMP 'CALLRESULT' message as a byte slice
// callID is the randomly generated string provided by the client
func createCallResult(callID string, result interface{}) (string, error) {
	return createWAMPMessage(msgCallResult, callID, result)
}

// CALLERROR
type callErrorMsg struct {
	CallID       string
	ErrorURI     string
	ErrorDesc    string
	ErrorDetails interface{}
}

func (msg *callErrorMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) < 4 || len(data) > 5 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.CallID, ok = data[1].(string); !ok {
		return &WAMPError{"invalid callID"}
	}
	if msg.ErrorURI, ok = data[2].(string); !ok {
		return &WAMPError{"invalid errorURI"}
	}
	if msg.ErrorDesc, ok = data[3].(string); !ok {
		return &WAMPError{"invalid error description"}
	}
	if len(data) == 5 {
		msg.ErrorDetails = data[4]
	}
	return nil
}

// CallError returns a json encoded WAMP 'CALLERROR' message as a byte slice
// callID is the randomly generated string provided by the client, errorURI is
// a URI identifying the error, errorDesc is a human-readable description of the
// error (for developers), errorDetails, if present, is a non-nil object
func createCallError(callID, errorURI, errorDesc string, errorDetails ...interface{}) (string, error) {
	if _, err := url.ParseRequestURI(errorURI); err != nil {
		return "", &WAMPError{"invalid URI: " + errorURI}
	}
	var data []interface{}
	filledInErrorDesc := fmt.Sprintf(errorDesc, errorDetails)
	data = append(data, msgCallError, callID, errorURI, filledInErrorDesc)
	data = append(data, errorDetails...)
	b, err := json.Marshal(data)
	return string(b), err
}

// SUBSCRIBE
type subscribeMsg struct {
	TopicURI string
}

func (msg *subscribeMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 2 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	return nil
}

// Subscribe returns a json encoded WAMP 'SUBSCRIBE' message as a byte slice
// topicURI is the topic that the client wants to subscribe to
func createSubscribe(topicURI string) (string, error) {
	return createWAMPMessagePubSub(msgSubscribe, topicURI)
}

// UNSUBSCRIBE
type unsubscribeMsg struct {
	TopicURI string
}

func (msg *unsubscribeMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 2 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	return nil
}

// Unsubscribe returns a json encoded WAMP 'UNSUBSCRIBE' message as a byte slice
// topicURI is the topic that the client wants to unsubscribe from
func createUnsubscribe(topicURI string) (string, error) {
	return createWAMPMessagePubSub(msgUnsubscribe, topicURI)
}

// PUBLISH
type publishMsg struct {
	TopicURI     string
	Event        interface{}
	ExcludeMe    bool
	ExcludeList  []string
	EligibleList []string
}

func (msg *publishMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) < 3 || len(data) > 5 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	msg.Event = data[2]
	if len(data) > 3 {
		if msg.ExcludeMe, ok = data[3].(bool); !ok {
			var arr []interface{}
			if arr, ok = data[3].([]interface{}); !ok && data[3] != nil {
				return &WAMPError{"invalid exclude argument"}
			}
			for _, v := range arr {
				if val, ok := v.(string); !ok {
					return &WAMPError{"invalid exclude list"}
				} else {
					msg.ExcludeList = append(msg.ExcludeList, val)
				}
			}
			if len(data) == 5 {
				if arr, ok = data[4].([]interface{}); !ok && data[3] != nil {
					return &WAMPError{"invalid eligable list"}
				}
				for _, v := range arr {
					if val, ok := v.(string); !ok {
						return &WAMPError{"invalid eligable list"}
					} else {
						msg.EligibleList = append(msg.EligibleList, val)
					}
				}
			}
		}
	}
	return nil
}

// Publish returns a json encoded WAMP 'PUBLISH' message as a byte slice
// arguments must be given in one of the following formats:
// [ topicURI, event ]
// [ topicURI, event, excludeMe ]
// [ topicURI, event, exclude ]
// [ topicURI, event, exclude, eligible ]
// event can be nil, a simple json type, or a complex json type
func createPublish(topicURI string, event interface{}, opts ...interface{}) (string, error) {
	var data []interface{}
	data = append(data, msgPublish, topicURI, event)
	data = append(data, opts...)
	return createWAMPMessagePubSub(data...)
}

// EVENT
type eventMsg struct {
	TopicURI string
	Event    interface{}
}

func (msg *eventMsg) UnmarshalJSON(jsonData []byte) error {
	var data []interface{}
	err := json.Unmarshal(jsonData, &data)
	if err != nil {
		return err
	}
	if len(data) != 3 {
		return ErrInvalidNumArgs
	}
	var ok bool
	if msg.TopicURI, ok = data[1].(string); !ok {
		return &WAMPError{"invalid topicURI"}
	}
	msg.Event = data[2]

	return nil
}

// Event returns a json encoded WAMP 'EVENT' message as a byte slice
// event can be nil, a simple json type, or a complex json type
func createEvent(topicURI string, event interface{}) (string, error) {
	return createWAMPMessagePubSub(msgEvent, topicURI, event)
}

// createWAMPMessagePubSub checks that the second argument (topicURI) is a valid
// URI and then passes the request on to createWAMPMessage
func createWAMPMessagePubSub(args ...interface{}) (string, error) {
	if _, err := url.ParseRequestURI(args[1].(string)); err != nil {
		return "", &WAMPError{"invalid URI: " + args[1].(string)}
	}
	return createWAMPMessage(args...)
}

// createWAMPMessage returns a JSON encoded list from all the arguments passed to it
func createWAMPMessage(args ...interface{}) (string, error) {
	var data []interface{}
	data = append(data, args...)
	b, err := json.Marshal(data)
	return string(b), err
}
<|endoftext|>


<commit_before>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"golang.org/x/net/websocket"
)

const (
	configFile       = "config.json"
	slackURLRTMStart = "https://slack.com/api/rtm.start?token=XXX"
	slackURLOrigin   = "https://api.slack.com/"
)

// Config ...
type Config struct {
	APIToken string `json:"apitoken"`
	Workers  int    `json:"workers"`
}

// RespRTMStart ...
type RespRTMStart struct {
	OK    bool              `json:"ok"`
	Error string            `json:"error"`
	URL   string            `json:"url"`
	Self  *RespRTMStartSelf `json:"self"`
}

// RespRTMStartSelf ...
type RespRTMStartSelf struct {
	ID string `json:"id"`
}

// Message ...
type Message struct {
	ID      uint64 `json:"id"`
	Type    string `json:"type"`
	Channel string `json:"channel"`
	Text    string `json:"text"`
}

func startRTM(token string) (*RespRTMStart, error) {
	u, err := url.Parse(slackURLRTMStart)
	if err != nil {
		return nil, err
	}
	q := u.Query()
	q.Set("token", token)
	u.RawQuery = q.Encode()

	req := &http.Request{
		Method: "GET",
		URL:    u,
	}

	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() {
		err = resp.Body.Close()
		log.Printf("Error closing response body: %+v", err)
	}()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var result RespRTMStart
	err = json.Unmarshal(body, &result)
	if err != nil {
		log.Fatalln(err)
	}
	if !result.OK {
		return nil, fmt.Errorf(result.Error)
	}

	return &result, nil
}

func startWorker(wg *sync.WaitGroup, done <-chan bool, msgs <-chan Message) {
	wg.Add(1)
	defer wg.Done()
	for {
		select {
		case <-done:
			return
		case m := <-msgs:
			log.Printf("Received: %s", m.Text)
			time.Sleep(10 * time.Second)
		}
	}
}

func main() {
	f, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatalln(err)
	}
	var config Config
	err = json.Unmarshal(f, &config)
	if err != nil {
		log.Fatalln(err)
	}

	log.Printf("Starting RTM...")
	respRTMStart, err := startRTM(config.APIToken)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("RTM started")

	log.Printf("Establishing Websocket connection...")
	wsConn, err := websocket.Dial(respRTMStart.URL, "", slackURLOrigin)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("Websocket connection established")
	defer func() {
		err = wsConn.Close()
		log.Printf("Error closing Websocket connection: %+v", err)
	}()

	// Channel for receiving OS error signals
	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
	defer close(sigs)

	// Channel for receiving messages
	msgs := make(chan Message)
	defer close(msgs)

	// Channel for broadcasting "done" signals
	done := make(chan bool, config.Workers)
	defer close(done)

	log.Printf("Receiving messages...")
	go func() {
		for {
			var m Message
			err = websocket.JSON.Receive(wsConn, &m)
			if err != nil {
				log.Printf("Error receiving message: %+v", err)
			}
			if m.Type == "message" {
				msgs <- m
			}
		}
	}()

	var wg sync.WaitGroup

	// Start concurrent workers
	for i := 0; i < config.Workers; i++ {
		go startWorker(&wg, done, msgs)
	}

	// Wait for workers to complete then terminate
	for s := range sigs {
		log.Printf("Signal (%s) received. Waiting for workers to complete...", s)

		// Broadcost done signals to all worker goroutines
		for i := 0; i < config.Workers; i++ {
			done <- true
		}

		wg.Wait()
		log.Fatalf("Aborting...")
	}
}
<commit_msg>Tidyups<commit_after>package main

import (
	"encoding/json"
	"fmt"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"sync"
	"syscall"
	"time"

	"golang.org/x/net/websocket"
)

const (
	configFile       = "config.json"
	slackURLRTMStart = "https://slack.com/api/rtm.start?token=XXX"
	slackURLOrigin   = "https://api.slack.com/"
)

// Config ...
type Config struct {
	APIToken string `json:"apitoken"`
	Workers  int    `json:"workers"`
}

// RespRTMStart ...
type RespRTMStart struct {
	OK    bool              `json:"ok"`
	Error string            `json:"error"`
	URL   string            `json:"url"`
	Self  *RespRTMStartSelf `json:"self"`
}

// RespRTMStartSelf ...
type RespRTMStartSelf struct {
	ID string `json:"id"`
}

// Message ...
type Message struct {
	ID      uint64 `json:"id"`
	Type    string `json:"type"`
	Channel string `json:"channel"`
	Text    string `json:"text"`
}

func startRTM(token string) (*RespRTMStart, error) {
	u, err := url.Parse(slackURLRTMStart)
	if err != nil {
		return nil, err
	}
	q := u.Query()
	q.Set("token", token)
	u.RawQuery = q.Encode()

	req := &http.Request{
		Method: "GET",
		URL:    u,
	}

	client := http.Client{}
	resp, err := client.Do(req)
	if err != nil {
		return nil, err
	}
	defer func() {
		err = resp.Body.Close()
		log.Printf("Error closing response body: %+v", err)
	}()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, err
	}

	var result RespRTMStart
	err = json.Unmarshal(body, &result)
	if err != nil {
		log.Fatalln(err)
	}
	if !result.OK {
		return nil, fmt.Errorf(result.Error)
	}

	return &result, nil
}

func startWorker(wg *sync.WaitGroup, done <-chan bool, msgs <-chan string) {
	wg.Add(1)
	defer wg.Done()
	for {
		select {
		case <-done:
			return
		case m := <-msgs:
			log.Printf("Received: %s", m)
			time.Sleep(10 * time.Second)
		}
	}
}

func main() {
	f, err := ioutil.ReadFile(configFile)
	if err != nil {
		log.Fatalln(err)
	}
	var config Config
	err = json.Unmarshal(f, &config)
	if err != nil {
		log.Fatalln(err)
	}

	log.Printf("Starting RTM...")
	respRTMStart, err := startRTM(config.APIToken)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("RTM started")

	log.Printf("Establishing Websocket connection...")
	wsConn, err := websocket.Dial(respRTMStart.URL, "", slackURLOrigin)
	if err != nil {
		log.Fatalln(err)
	}
	log.Printf("Websocket connection established")
	defer func() {
		err = wsConn.Close()
		log.Printf("Error closing Websocket connection: %+v", err)
	}()

	// Channel for receiving OS error signals
	sigs := make(chan os.Signal)
	signal.Notify(sigs, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
	defer close(sigs)

	// Channel for receiving messages
	msgs := make(chan string)
	defer close(msgs)

	// Channel for broadcasting "done" signals
	done := make(chan bool)

	log.Printf("Receiving messages...")
	go func() {
		for {
			var m Message
			err = websocket.JSON.Receive(wsConn, &m)
			if err != nil {
				log.Printf("Error receiving message: %+v", err)
				continue
			}
			if m.Type == "message" && m.Text != "" {
				msgs <- m.Text
			}
		}
	}()

	var wg sync.WaitGroup

	// Start concurrent workers
	for i := 0; i < config.Workers; i++ {
		go startWorker(&wg, done, msgs)
	}

	// Receive OS error signal
	s := <-sigs
	log.Printf("Signal (%s) received. Waiting for workers to complete...", s)

	// Close channel to broadcast done signals to all worker goroutines
	close(done)

	// Wait for goroutines to complete then terminate
	wg.Wait()
	log.Fatalf("Aborting...")
}
<|endoftext|>


<commit_before>package main

import (
	"os"
	"strings"
	"testing"
	"time"

	"github.com/bmizerany/assert"
)

var emptyArgs []string

var aTime, _ = time.Parse(time.Kitchen, "10:00AM")
var emptyState = State{
	endTime: noTime,
	now:     aTime,
}

func Test_main(t *testing.T) {
	os.Setenv("HOME", os.TempDir())

	main()
}

func Test_parseCommand_start(t *testing.T) {
	newState, output := parseCommand(emptyState, "start")

	expected, _ := time.Parse(time.Kitchen, "10:25AM")

	assert.T(t, expected.Equal(newState.endTime))
	assert.Equal(t, "Timer started, 25 minutes remaining", output.text)
}

func Test_parseCommand_status(t *testing.T) {
	endTime, _ := time.Parse(time.Kitchen, "10:25AM")
	now, _ := time.Parse(time.Kitchen, "10:05AM")

	state := State{endTime: endTime, now: now}

	newState, output := parseCommand(state, "status")

	assert.Equal(t, state, newState)
	assert.Equal(t, "20 🍅 ", output.text)
}

func Test_parseCommand_status_empty(t *testing.T) {
	newState, output := parseCommand(emptyState, "status")

	assert.Equal(t, emptyState, newState)
	assert.Equal(t, "", output.text)
}

func Test_parseCommand_done(t *testing.T) {
	endTime, _ := time.Parse(time.Kitchen, "10:25AM")
	now, _ := time.Parse(time.Kitchen, "10:25AM")

	state := State{endTime: endTime, now: now}

	newState, output := parseCommand(state, "status")

	assert.Equal(t, state, newState)
	assert.Equal(t, "0 🍅 ", output.text)
}

func Test_parseCommand_past(t *testing.T) {
	endTime, _ := time.Parse(time.Kitchen, "10:25AM")
	now, _ := time.Parse(time.Kitchen, "10:35AM")

	state := State{endTime: endTime, now: now}

	newState, output := parseCommand(state, "status")

	assert.Equal(t, state, newState)
	assert.Equal(t, "❗ 🍅 ", output.text)

}

func Test_parseCommand_bad(t *testing.T) {
	newState, output := parseCommand(emptyState, "foobar")

	assert.Equal(t, emptyState, newState)
	assert.Equal(t, usage, output.text)
	assert.Equal(t, 1, output.returnCode)
}

func Test_parseCommand_clear(t *testing.T) {
	newState, output := parseCommand(emptyState, "clear")

	assert.T(t, newState.endTime.Equal(noTime))
	assert.Equal(t, "Pomodoro cleared!", output.text)
}

func Test_readExistingTime(t *testing.T) {
	os.Setenv("HOME", os.TempDir())

	readExistingTime()
}

func Test_filePath(t *testing.T) {
	assert.T(t, strings.HasSuffix(filePath(), "/.pomodoro"))
}
<commit_msg>Replace assert with testify<commit_after>package main

import (
	"os"
	"strings"
	"testing"
	"time"

	"github.com/stretchr/testify/assert"
)

var emptyArgs []string

var aTime, _ = time.Parse(time.Kitchen, "10:00AM")
var emptyState = State{
	endTime: noTime,
	now:     aTime,
}

func Test_main(t *testing.T) {
	os.Setenv("HOME", os.TempDir())

	main()
}

func Test_parseCommand_start(t *testing.T) {
	newState, output := parseCommand(emptyState, "start")

	expected, _ := time.Parse(time.Kitchen, "10:25AM")

	assert.Equal(t, expected, newState.endTime)
	assert.Equal(t, "Timer started, 25 minutes remaining", output.text)
}

func Test_parseCommand_status(t *testing.T) {
	endTime, _ := time.Parse(time.Kitchen, "10:25AM")
	now, _ := time.Parse(time.Kitchen, "10:05AM")

	state := State{endTime: endTime, now: now}

	newState, output := parseCommand(state, "status")

	assert.Equal(t, state, newState)
	assert.Equal(t, "20 🍅 ", output.text)
}

func Test_parseCommand_status_empty(t *testing.T) {
	newState, output := parseCommand(emptyState, "status")

	assert.Equal(t, emptyState, newState)
	assert.Equal(t, "", output.text)
}

func Test_parseCommand_done(t *testing.T) {
	endTime, _ := time.Parse(time.Kitchen, "10:25AM")
	now, _ := time.Parse(time.Kitchen, "10:25AM")

	state := State{endTime: endTime, now: now}

	newState, output := parseCommand(state, "status")

	assert.Equal(t, state, newState)
	assert.Equal(t, "0 🍅 ", output.text)
}

func Test_parseCommand_past(t *testing.T) {
	endTime, _ := time.Parse(time.Kitchen, "10:25AM")
	now, _ := time.Parse(time.Kitchen, "10:35AM")

	state := State{endTime: endTime, now: now}

	newState, output := parseCommand(state, "status")

	assert.Equal(t, state, newState)
	assert.Equal(t, "❗ 🍅 ", output.text)

}

func Test_parseCommand_bad(t *testing.T) {
	newState, output := parseCommand(emptyState, "foobar")

	assert.Equal(t, emptyState, newState)
	assert.Equal(t, usage, output.text)
	assert.Equal(t, 1, output.returnCode)
}

func Test_parseCommand_clear(t *testing.T) {
	newState, output := parseCommand(emptyState, "clear")

	assert.Equal(t, noTime, newState.endTime)
	assert.Equal(t, "Pomodoro cleared!", output.text)
}

func Test_readExistingTime(t *testing.T) {
	os.Setenv("HOME", os.TempDir())

	readExistingTime()
}

func Test_filePath(t *testing.T) {
	assert.Equal(t, true, strings.HasSuffix(filePath(), "/.pomodoro"))
}
<|endoftext|>


<commit_before>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/systemd"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
	"github.com/juju/juju/version"
)

var (
	logger = loggo.GetLogger("juju.service")
)

// These are the names of the init systems regognized by juju.
const (
	InitSystemSystemd = "systemd"
	InitSystemUpstart = "upstart"
	InitSystemWindows = "windows"
)

// linuxInitSystems lists the names of the init systems that juju might
// find on a linux host.
var linuxInitSystems = []string{
	InitSystemSystemd,
	InitSystemUpstart,
}

// ServiceActions represents the actions that may be requested for
// an init system service.
type ServiceActions interface {
	// Start will try to start the service.
	Start() error

	// Stop will try to stop the service.
	Stop() error

	// Install installs a service.
	Install() error

	// Remove will remove the service.
	Remove() error
}

// Service represents a service in the init system running on a host.
type Service interface {
	ServiceActions

	// Name returns the service's name.
	Name() string

	// Conf returns the service's conf data.
	Conf() common.Conf

	// Running returns a boolean value that denotes
	// whether or not the service is running.
	Running() (bool, error)

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() (bool, error)

	// Installed will return a boolean value that denotes
	// whether or not the service is installed.
	Installed() (bool, error)

	// TODO(ericsnow) Move all the commands into a separate interface.

	// InstallCommands returns the list of commands to run on a
	// (remote) host to install the service.
	InstallCommands() ([]string, error)

	// StartCommands returns the list of commands to run on a
	// (remote) host to start the service.
	StartCommands() ([]string, error)
}

// RestartableService is a service that directly supports restarting.
type RestartableService interface {
	// Restart restarts the service.
	Restart() error
}

// TODO(ericsnow) bug #1426458
// Eliminate the need to pass an empty conf for most service methods
// and several helper functions.

// NewService returns a new Service based on the provided info.
func NewService(name string, conf common.Conf, initSystem string) (Service, error) {
	if name == "" {
		return nil, errors.New("missing name")
	}

	switch initSystem {
	case InitSystemWindows:
		return windows.NewService(name, conf), nil
	case InitSystemUpstart:
		return upstart.NewService(name, conf), nil
	case InitSystemSystemd:
		svc, err := systemd.NewService(name, conf)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to wrap service %q", name)
		}
		return svc, nil
	default:
		return nil, errors.NotFoundf("init system %q", initSystem)
	}
}

// ListServices lists all installed services on the running system
func ListServices() ([]string, error) {
	initName, ok := VersionInitSystem(version.Current)
	if !ok {
		return nil, errors.NotFoundf("init system on local host")
	}

	switch initName {
	case InitSystemWindows:
		services, err := windows.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemUpstart:
		services, err := upstart.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemSystemd:
		services, err := systemd.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	default:
		return nil, errors.NotFoundf("init system %q", initName)
	}
}

// ListServicesScript returns the commands that should be run to get
// a list of service names on a host.
func ListServicesScript() string {
	commands := []string{
		"init_system=$(" + DiscoverInitSystemScript() + ")",
		// If the init system is not identified then the script will
		// "exit 1". This is correct since the script should fail if no
		// init system can be identified.
		newShellSelectCommand("init_system", "exit 1", listServicesCommand),
	}
	return strings.Join(commands, "\n")
}

func listServicesCommand(initSystem string) (string, bool) {
	switch initSystem {
	case InitSystemWindows:
		return windows.ListCommand(), true
	case InitSystemUpstart:
		return upstart.ListCommand(), true
	case InitSystemSystemd:
		return systemd.ListCommand(), true
	default:
		return "", false
	}
}

// InstallServicesCommand composes the list of shell commands that install
// and start the given service.
func InstallServiceCommands(name string, conf common.Conf, os string) ([]string, error) {
	start := true

	if os == "windows" {
		cmds, err := installCommands(name, conf, InitSystemWindows, start)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return cmds, nil
	}

	candidates := make(map[string]string)
	for _, initSystem := range linuxInitSystems {
		cmds, err := installCommands(name, conf, initSystem, start)
		if err != nil {
			return nil, errors.Trace(err)
		}
		candidates[initSystem] = "\n  " + strings.Join(cmds, "\n  ")
	}

	handler := func(initSystem string) (string, bool) {
		if cmds, ok := candidates[initSystem]; ok {
			return cmds, true
		}
		return "", false
	}
	script := newShellSelectCommand("init_system", "exit 1", handler)
	cmds := []string{
		"init_system=$(" + DiscoverInitSystemScript() + ")",
		script,
	}
	return cmds, nil
}

func installCommands(name string, conf common.Conf, initSystem string, start bool) ([]string, error) {
	svc, err := NewService(name, conf, initSystem)
	if err != nil {
		return nil, errors.Trace(err)
	}

	cmds, err := svc.InstallCommands()
	if err != nil {
		return nil, errors.Trace(err)
	}
	if !start {
		return cmds, nil
	}

	startCmds, err := svc.StartCommands()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return append(cmds, startCmds...), nil
}

// installStartRetryAttempts defines how much InstallAndStart retries
// upon Start failures.
var installStartRetryAttempts = utils.AttemptStrategy{
	Total: 1 * time.Second,
	Delay: 250 * time.Millisecond,
}

// InstallAndStart installs the provided service and tries starting it.
// The first few Start failures are ignored.
func InstallAndStart(svc ServiceActions) error {
	if err := svc.Install(); err != nil {
		return errors.Trace(err)
	}

	// For various reasons the init system may take a short time to
	// realise that the service has been installed.
	var err error
	for attempt := installStartRetryAttempts.Start(); attempt.Next(); {
		if err != nil {
			logger.Errorf("retrying start request (%v)", errors.Cause(err))
		}

		if err = svc.Start(); err == nil {
			break
		}
	}
	return errors.Trace(err)
}

// discoverService is patched out during some tests.
var discoverService = func(name string) (Service, error) {
	return DiscoverService(name, common.Conf{})
}

// TODO(ericsnow) Add one-off helpers for Start and Stop too?

// Restart restarts the named service.
func Restart(name string) error {
	svc, err := discoverService(name)
	if err != nil {
		return errors.Annotatef(err, "failed to find service %q", name)
	}
	if err := restart(svc); err != nil {
		return errors.Annotatef(err, "failed to restart service %q", name)
	}
	return nil
}

func restart(svc Service) error {
	// Use the Restart method, if there is one.
	if svc, ok := svc.(RestartableService); ok {
		if err := svc.Restart(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	// Otherwise explicitly stop and start the service.
	if err := svc.Stop(); err != nil {
		return errors.Trace(err)
	}
	if err := svc.Start(); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<commit_msg>Always start.<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package service

import (
	"strings"
	"time"

	"github.com/juju/errors"
	"github.com/juju/loggo"
	"github.com/juju/utils"

	"github.com/juju/juju/service/common"
	"github.com/juju/juju/service/systemd"
	"github.com/juju/juju/service/upstart"
	"github.com/juju/juju/service/windows"
	"github.com/juju/juju/version"
)

var (
	logger = loggo.GetLogger("juju.service")
)

// These are the names of the init systems regognized by juju.
const (
	InitSystemSystemd = "systemd"
	InitSystemUpstart = "upstart"
	InitSystemWindows = "windows"
)

// linuxInitSystems lists the names of the init systems that juju might
// find on a linux host.
var linuxInitSystems = []string{
	InitSystemSystemd,
	InitSystemUpstart,
}

// ServiceActions represents the actions that may be requested for
// an init system service.
type ServiceActions interface {
	// Start will try to start the service.
	Start() error

	// Stop will try to stop the service.
	Stop() error

	// Install installs a service.
	Install() error

	// Remove will remove the service.
	Remove() error
}

// Service represents a service in the init system running on a host.
type Service interface {
	ServiceActions

	// Name returns the service's name.
	Name() string

	// Conf returns the service's conf data.
	Conf() common.Conf

	// Running returns a boolean value that denotes
	// whether or not the service is running.
	Running() (bool, error)

	// Exists returns whether the service configuration exists in the
	// init directory with the same content that this Service would have
	// if installed.
	Exists() (bool, error)

	// Installed will return a boolean value that denotes
	// whether or not the service is installed.
	Installed() (bool, error)

	// TODO(ericsnow) Move all the commands into a separate interface.

	// InstallCommands returns the list of commands to run on a
	// (remote) host to install the service.
	InstallCommands() ([]string, error)

	// StartCommands returns the list of commands to run on a
	// (remote) host to start the service.
	StartCommands() ([]string, error)
}

// RestartableService is a service that directly supports restarting.
type RestartableService interface {
	// Restart restarts the service.
	Restart() error
}

// TODO(ericsnow) bug #1426458
// Eliminate the need to pass an empty conf for most service methods
// and several helper functions.

// NewService returns a new Service based on the provided info.
func NewService(name string, conf common.Conf, initSystem string) (Service, error) {
	if name == "" {
		return nil, errors.New("missing name")
	}

	switch initSystem {
	case InitSystemWindows:
		return windows.NewService(name, conf), nil
	case InitSystemUpstart:
		return upstart.NewService(name, conf), nil
	case InitSystemSystemd:
		svc, err := systemd.NewService(name, conf)
		if err != nil {
			return nil, errors.Annotatef(err, "failed to wrap service %q", name)
		}
		return svc, nil
	default:
		return nil, errors.NotFoundf("init system %q", initSystem)
	}
}

// ListServices lists all installed services on the running system
func ListServices() ([]string, error) {
	initName, ok := VersionInitSystem(version.Current)
	if !ok {
		return nil, errors.NotFoundf("init system on local host")
	}

	switch initName {
	case InitSystemWindows:
		services, err := windows.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemUpstart:
		services, err := upstart.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	case InitSystemSystemd:
		services, err := systemd.ListServices()
		if err != nil {
			return nil, errors.Annotatef(err, "failed to list %s services", initName)
		}
		return services, nil
	default:
		return nil, errors.NotFoundf("init system %q", initName)
	}
}

// ListServicesScript returns the commands that should be run to get
// a list of service names on a host.
func ListServicesScript() string {
	commands := []string{
		"init_system=$(" + DiscoverInitSystemScript() + ")",
		// If the init system is not identified then the script will
		// "exit 1". This is correct since the script should fail if no
		// init system can be identified.
		newShellSelectCommand("init_system", "exit 1", listServicesCommand),
	}
	return strings.Join(commands, "\n")
}

func listServicesCommand(initSystem string) (string, bool) {
	switch initSystem {
	case InitSystemWindows:
		return windows.ListCommand(), true
	case InitSystemUpstart:
		return upstart.ListCommand(), true
	case InitSystemSystemd:
		return systemd.ListCommand(), true
	default:
		return "", false
	}
}

// InstallServicesCommand composes the list of shell commands that install
// and start the given service on the given operating system.
func InstallServiceCommands(name string, conf common.Conf, os string) ([]string, error) {
	if os == "windows" {
		cmds, err := installCommands(name, conf, InitSystemWindows)
		if err != nil {
			return nil, errors.Trace(err)
		}
		return cmds, nil
	}

	candidates := make(map[string]string)
	for _, initSystem := range linuxInitSystems {
		cmds, err := installCommands(name, conf, initSystem)
		if err != nil {
			return nil, errors.Trace(err)
		}
		candidates[initSystem] = "\n  " + strings.Join(cmds, "\n  ")
	}

	handler := func(initSystem string) (string, bool) {
		if cmds, ok := candidates[initSystem]; ok {
			return cmds, true
		}
		return "", false
	}
	script := newShellSelectCommand("init_system", "exit 1", handler)
	cmds := []string{
		"init_system=$(" + DiscoverInitSystemScript() + ")",
		script,
	}
	return cmds, nil
}

func installCommands(name string, conf common.Conf, initSystem string) ([]string, error) {
	svc, err := NewService(name, conf, initSystem)
	if err != nil {
		return nil, errors.Trace(err)
	}

	cmds, err := svc.InstallCommands()
	if err != nil {
		return nil, errors.Trace(err)
	}
	// Return here if we want to only install (i.e. skip starting).

	startCmds, err := svc.StartCommands()
	if err != nil {
		return nil, errors.Trace(err)
	}
	return append(cmds, startCmds...), nil
}

// installStartRetryAttempts defines how much InstallAndStart retries
// upon Start failures.
var installStartRetryAttempts = utils.AttemptStrategy{
	Total: 1 * time.Second,
	Delay: 250 * time.Millisecond,
}

// InstallAndStart installs the provided service and tries starting it.
// The first few Start failures are ignored.
func InstallAndStart(svc ServiceActions) error {
	if err := svc.Install(); err != nil {
		return errors.Trace(err)
	}

	// For various reasons the init system may take a short time to
	// realise that the service has been installed.
	var err error
	for attempt := installStartRetryAttempts.Start(); attempt.Next(); {
		if err != nil {
			logger.Errorf("retrying start request (%v)", errors.Cause(err))
		}

		if err = svc.Start(); err == nil {
			break
		}
	}
	return errors.Trace(err)
}

// discoverService is patched out during some tests.
var discoverService = func(name string) (Service, error) {
	return DiscoverService(name, common.Conf{})
}

// TODO(ericsnow) Add one-off helpers for Start and Stop too?

// Restart restarts the named service.
func Restart(name string) error {
	svc, err := discoverService(name)
	if err != nil {
		return errors.Annotatef(err, "failed to find service %q", name)
	}
	if err := restart(svc); err != nil {
		return errors.Annotatef(err, "failed to restart service %q", name)
	}
	return nil
}

func restart(svc Service) error {
	// Use the Restart method, if there is one.
	if svc, ok := svc.(RestartableService); ok {
		if err := svc.Restart(); err != nil {
			return errors.Trace(err)
		}
		return nil
	}

	// Otherwise explicitly stop and start the service.
	if err := svc.Stop(); err != nil {
		return errors.Trace(err)
	}
	if err := svc.Start(); err != nil {
		return errors.Trace(err)
	}
	return nil
}
<|endoftext|>