<commit_before>package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	sm "github.com/ifn/go-statemachine"
)

type DeskMsg struct {
	Desk [][]string `json:"desk"`
}

type PlayerMsg struct {
	Cmd  sm.EventType `json:"command"`
	Card string       `json:"card"`
}

//

var Suits []string = []string{
	"S",
	"C",
	"H",
	"D",
}

var CardValues map[string]int = map[string]int{
	"6": 6, "7": 7, "8": 8, "9": 9, "10": 10,
	"J": 11,
	"Q": 12,
	"K": 13,
	"A": 14,
}

func higher(c0, c1, t string) int {
	// c0 and c1 have the same suit
	if c0[0] == c1[0] {
		if CardValues[c0[1:]] > CardValues[c1[1:]] {
			return 1
		}
		if CardValues[c0[1:]] < CardValues[c1[1:]] {
			return -1
		}
		return 0
	}
	// c0 is trump, c1 is not
	if c0[:1] == t {
		return 1
	}
	// c1 is trump, c0 is not
	if c1[:1] == t {
		return -1
	}
	// suits are different, both are not trump
	return -2
}

var CardRE *regexp.Regexp = regexp.MustCompile(`[SCHD]([6-9JQKA]|10)`)

func isValid(c string) bool {
	return CardRE.MatchString(c)
}

//

const (
	cmdStart sm.EventType = iota
	cmdMove

	cmdCount
)

const (
	stateCollection sm.State = iota

	stateAttack
	stateDefense

	stateCount
)

type roundResult int

const (
	None roundResult = iota
	Beat
	NotBeat
)

func stateToString(s sm.State) string {
	return [...]string{
		stateCollection: "COLLECTION",
		stateDefense:    "DEFENSE",
		stateAttack:     "ATTACK",
	}[s]
}

func cmdToString(t sm.EventType) string {
	return [...]string{
		cmdStart: "START",
		cmdMove:  "MOVE",
	}[t]
}

//

type cmdArgs struct {
	conn *playerConn
	card string
}

//

func logOutOfTurn(pconn *playerConn) {
	log.Printf("out of turn: %v", pconn.conn.RemoteAddr())
}

func logWontBeat(c1, c2, t string) {
	log.Printf("%v won't bit %v, trump is %v", c1, c2, t)
}

func logNoQuorum(n int) {
	log.Printf("no quorum: %v player(s)", n)
}

func logStrangeCard(c string, pc *playerConn) {
	log.Printf("%v doesn't own %v", pc, c)
}

//

type gameState struct {
	// 1. fields that don't change during a game

	sm  *sm.StateMachine
	hub *hub

	// trump suit
	trump string

	// 2. fields that don't change during a round

	deck []string

	// attacker that started a round
	aconnStart *playerConn
	// defender
	dconn *playerConn

	// 3. fields that change during a round

	desk [][]string

	// attacker
	aconn *playerConn
	// card that should be beaten
	cardToBeat string
}

func (self *gameState) popCard() (card string) {
	if deck := self.deck; len(deck) > 0 {
		card = deck[0]
		self.deck = deck[1:]
	}
	return
}

func (self *gameState) initDeck() {
	numCards := len(Suits) * len(CardValues)
	self.deck = make([]string, 0, numCards+ /*for trump*/ 1)

	deck := make([]string, 0, numCards)
	for i := range Suits {
		for cv := range CardValues {
			deck = append(deck, Suits[i]+cv)
		}
	}

	order := rand.Perm(numCards)
	for i := range order {
		self.deck = append(self.deck, deck[order[i]])
	}
}

func (self *gameState) setTrump() {
	card := self.popCard()
	self.trump = card[:1]
	self.deck = append(self.deck, card)
}

func (self *gameState) nextPlayer(c *playerConn) *playerConn {
	return self.hub.conns.(*mapRing).Next(c).(*playerConn)
}

func (self *gameState) chooseStarting() *playerConn {
	conns := self.hub.conns.(*mapRing)

	return conns.Nth(rand.Intn(conns.Len())).(*playerConn)
}

func (self *gameState) setRoles(res roundResult) {
	switch res {
	case None:
		self.aconn = self.chooseStarting()
	case Beat:
		self.aconn = self.dconn
	case NotBeat:
		self.aconn = self.nextPlayer(self.dconn)
	}
	self.dconn = self.nextPlayer(self.aconn)
	self.aconnStart = self.aconn
}

func (self *gameState) dealCards() {
	for i := 0; i < 6; i++ {
		for pc := range self.hub.conns.Enumerate() {
			pc.(*playerConn).takeCard()
		}
	}
}

func (self *gameState) takeCards() {
	conns := self.hub.conns.(*mapRing)

	takeCards := func(pc *playerConn) {
		for len(self.deck) > 0 && len(pc.cards) < 6 {
			pc.takeCard()
		}
	}

	for pc := range conns.EnumerateFrom(self.aconnStart) {
		if pc := pc.(*playerConn); pc != self.dconn {
			takeCards(pc)
		}
	}
	takeCards(self.dconn)
}

func (self *gameState) newRound(res roundResult) {
	switch res {
	case None:
		self.initDeck()
		self.dealCards()
		self.setTrump()
	case NotBeat:
		// add desk to defender's stock
		self.takeCards()
	case Beat:
		self.takeCards()
	}
	self.setRoles(res)
}

// event handlers
// event handlers are actually transition functions.
// in case error event handler should neither change the gameState,
// nor return the state value different from passed to it as an argument.

func (self *gameState) handleStartInCollection(s sm.State, e *sm.Event) sm.State {
	if n := self.hub.conns.(*mapRing).Len(); n < 2 {
		logNoQuorum(n)
		return s
	}

	self.newRound(None)
	return stateAttack
}

func (self *gameState) handleMoveInAttack(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.aconn {
		logOutOfTurn(conn)
		return s
	}

	// attacker sent the card
	if card != "" {
		// throw card to desk
		if _, ok := conn.cards[card]; !ok {
			logStrangeCard(card, conn)
			return s
		}
		conn.toDesk(card)

		self.cardToBeat = card
		return stateDefense
	}

	// attacker sent no card

	aconn := self.nextPlayer(self.aconn)
	if aconn == self.dconn {
		aconn = self.nextPlayer(aconn)
	}

	// check if all attackers have been polled
	if aconn == self.aconnStart {
		self.newRound(Beat)
		return stateAttack
	}

	self.aconn = aconn
	return stateAttack
}

func (self *gameState) handleMoveInDefense(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.dconn {
		logOutOfTurn(conn)
		return s
	}

	// defender takes the cards
	if card == "" {
		self.newRound(NotBeat)
		return stateAttack
	}

	// check that the sent card is capable to beat
	if higher(card, self.cardToBeat, self.trump) != 1 {
		logWontBeat(card, self.cardToBeat, self.trump)
		return s
	}

	// throw card to desk
	if _, ok := conn.cards[card]; !ok {
		logStrangeCard(card, conn)
		return s
	}
	conn.toDesk(card)

	return stateAttack
}

func (self *gameState) showDesk(s sm.State, e *sm.Event) sm.State {
	desk, err := json.Marshal(DeskMsg{})
	if err != nil {
		log.Println(err)
		return s
	}

	self.hub.bcastChan <- desk

	return s
}

func (self *gameState) log(s sm.State, e *sm.Event) sm.State {
	log.Println(self)
	return s
}

//

func NewGameState() *gameState {
	gst := new(gameState)

	gst.sm = sm.New(stateCollection, uint(stateCount), uint(cmdCount))

	//TODO: refactor this
	gst.sm.On(cmdStart,
		[]sm.State{stateCollection},
		gst.handleStartInCollection,
	)
	gst.sm.On(cmdStart,
		[]sm.State{stateCollection},
		gst.showDesk,
	)
	gst.sm.On(cmdStart,
		[]sm.State{stateCollection},
		gst.log,
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack},
		gst.handleMoveInAttack,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateDefense},
		gst.handleMoveInDefense,
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		gst.showDesk,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		gst.log,
	)

	gst.hub = NewHub()

	return gst
}

//

type playerConn struct {
	gst *gameState

	cards map[string]struct{}

	conn      *websocket.Conn
	hubToConn chan []byte
}

func (self *playerConn) takeCard() {
	if card := self.gst.popCard(); card != "" {
		self.cards[card] = struct{}{}
	}
}

func (self *playerConn) toDesk(card string) {
	delete(self.cards, card)

	// add card to desk
}

func (self *playerConn) write() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		select {
		case m, ok := <-self.hubToConn:
			if !ok {
				return
			}
			//TODO: text or binary?
			err := self.conn.WriteMessage(websocket.TextMessage, m)
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (self *playerConn) read() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	var m PlayerMsg

	for {
		err := self.conn.ReadJSON(&m)
		if err != nil {
			log.Println(err)
			return
		}

		var event *sm.Event
		switch m.Cmd {
		case cmdStart:
			event = &sm.Event{cmdStart, nil}
		case cmdMove:
			event = &sm.Event{cmdMove, cmdArgs{self, m.Card}}
		default:
			log.Printf("unknown command: %v", m.Cmd)
			continue
		}

		err = self.gst.sm.Emit(event)
		if err != nil {
			log.Println(err)
		}
	}
}

//

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

func playerHandler(gst *gameState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}

		p := &playerConn{gst, make(map[string]struct{}), conn, make(chan []byte)}

		gst.hub.regChan <- p
		defer func() {
			gst.hub.unregChan <- p
		}()

		go p.write()
		p.read()
	}
}

//

func startDurakSrv() error {
	gst := NewGameState()

	http.HandleFunc("/", playerHandler(gst))

	return http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func main() {
	err := startDurakSrv()
	if err != nil {
		log.Fatal(err)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())

	runtime.GOMAXPROCS(runtime.NumCPU())
}
<commit_msg>added fromDesk<commit_after>package main

import (
	"encoding/json"
	"log"
	"math/rand"
	"net/http"
	"os"
	"regexp"
	"runtime"
	"time"

	"github.com/gorilla/websocket"
	sm "github.com/ifn/go-statemachine"
)

type DeskMsg struct {
	Desk []string `json:"desk"`
}

type PlayerMsg struct {
	Cmd  sm.EventType `json:"command"`
	Card string       `json:"card"`
}

//

var Suits []string = []string{
	"S",
	"C",
	"H",
	"D",
}

var CardValues map[string]int = map[string]int{
	"6": 6, "7": 7, "8": 8, "9": 9, "10": 10,
	"J": 11,
	"Q": 12,
	"K": 13,
	"A": 14,
}

func higher(c0, c1, t string) int {
	// c0 and c1 have the same suit
	if c0[0] == c1[0] {
		if CardValues[c0[1:]] > CardValues[c1[1:]] {
			return 1
		}
		if CardValues[c0[1:]] < CardValues[c1[1:]] {
			return -1
		}
		return 0
	}
	// c0 is trump, c1 is not
	if c0[:1] == t {
		return 1
	}
	// c1 is trump, c0 is not
	if c1[:1] == t {
		return -1
	}
	// suits are different, both are not trump
	return -2
}

var CardRE *regexp.Regexp = regexp.MustCompile(`[SCHD]([6-9JQKA]|10)`)

func isValid(c string) bool {
	return CardRE.MatchString(c)
}

//

const (
	cmdStart sm.EventType = iota
	cmdMove

	cmdCount
)

const (
	stateCollection sm.State = iota

	stateAttack
	stateDefense

	stateCount
)

type roundResult int

const (
	None roundResult = iota
	Beat
	NotBeat
)

func stateToString(s sm.State) string {
	return [...]string{
		stateCollection: "COLLECTION",
		stateDefense:    "DEFENSE",
		stateAttack:     "ATTACK",
	}[s]
}

func cmdToString(t sm.EventType) string {
	return [...]string{
		cmdStart: "START",
		cmdMove:  "MOVE",
	}[t]
}

//

type cmdArgs struct {
	conn *playerConn
	card string
}

//

func logOutOfTurn(pconn *playerConn) {
	log.Printf("out of turn: %v", pconn.conn.RemoteAddr())
}

func logWontBeat(c1, c2, t string) {
	log.Printf("%v won't bit %v, trump is %v", c1, c2, t)
}

func logNoQuorum(n int) {
	log.Printf("no quorum: %v player(s)", n)
}

func logStrangeCard(c string, pc *playerConn) {
	log.Printf("%v doesn't own %v", pc, c)
}

//

type gameState struct {
	// 1. fields that don't change during a game

	sm  *sm.StateMachine
	hub *hub

	// trump suit
	trump string

	// 2. fields that don't change during a round

	deck []string

	// attacker that started a round
	aconnStart *playerConn
	// defender
	dconn *playerConn

	// 3. fields that change during a round

	desk []string

	// attacker
	aconn *playerConn
	// card that should be beaten
	cardToBeat string
}

func (self *gameState) popCard() (card string) {
	if deck := self.deck; len(deck) > 0 {
		card = deck[0]
		self.deck = deck[1:]
	}
	return
}

func (self *gameState) initDeck() {
	numCards := len(Suits) * len(CardValues)

	deck := make([]string, 0, numCards)
	for i := range Suits {
		for cv := range CardValues {
			deck = append(deck, Suits[i]+cv)
		}
	}

	order := rand.Perm(numCards)
	for i := range order {
		self.deck = append(self.deck, deck[order[i]])
	}
}

func (self *gameState) setTrump() {
	card := self.popCard()
	self.trump = card[:1]
	self.deck = append(self.deck, card)
}

func (self *gameState) nextPlayer(c *playerConn) *playerConn {
	return self.hub.conns.(*mapRing).Next(c).(*playerConn)
}

func (self *gameState) chooseStarting() *playerConn {
	conns := self.hub.conns.(*mapRing)

	return conns.Nth(rand.Intn(conns.Len())).(*playerConn)
}

func (self *gameState) setRoles(res roundResult) {
	switch res {
	case None:
		self.aconn = self.chooseStarting()
	case Beat:
		self.aconn = self.dconn
	case NotBeat:
		self.aconn = self.nextPlayer(self.dconn)
	}
	self.dconn = self.nextPlayer(self.aconn)
	self.aconnStart = self.aconn
}

func (self *gameState) dealCards() {
	for i := 0; i < 6; i++ {
		for pc := range self.hub.conns.Enumerate() {
			pc.(*playerConn).takeCard()
		}
	}
}

func (self *gameState) takeCards() {
	conns := self.hub.conns.(*mapRing)

	takeCards := func(pc *playerConn) {
		for len(self.deck) > 0 && len(pc.cards) < 6 {
			pc.takeCard()
		}
	}

	for pc := range conns.EnumerateFrom(self.aconnStart) {
		if pc := pc.(*playerConn); pc != self.dconn {
			takeCards(pc)
		}
	}
	takeCards(self.dconn)
}

func (self *gameState) newRound(res roundResult) {
	switch res {
	case None:
		self.initDeck()
		self.dealCards()
		self.setTrump()
	case NotBeat:
		self.dconn.fromDesk()
		self.takeCards()
	case Beat:
		self.desk = self.desk[:0]
		self.takeCards()
	}
	self.setRoles(res)
}

// event handlers
// event handlers are actually transition functions.
// in case error event handler should neither change the gameState,
// nor return the state value different from passed to it as an argument.

func (self *gameState) handleStartInCollection(s sm.State, e *sm.Event) sm.State {
	if n := self.hub.conns.(*mapRing).Len(); n < 2 {
		logNoQuorum(n)
		return s
	}

	self.newRound(None)
	return stateAttack
}

func (self *gameState) handleMoveInAttack(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.aconn {
		logOutOfTurn(conn)
		return s
	}

	// attacker sent the card
	if card != "" {
		// throw card to desk
		if _, ok := conn.cards[card]; !ok {
			logStrangeCard(card, conn)
			return s
		}
		conn.toDesk(card)

		self.cardToBeat = card
		return stateDefense
	}

	// attacker sent no card

	aconn := self.nextPlayer(self.aconn)
	if aconn == self.dconn {
		aconn = self.nextPlayer(aconn)
	}

	// check if all attackers have been polled
	if aconn == self.aconnStart {
		self.newRound(Beat)
		return stateAttack
	}

	self.aconn = aconn
	return stateAttack
}

func (self *gameState) handleMoveInDefense(s sm.State, e *sm.Event) sm.State {
	conn := e.Data.(cmdArgs).conn
	card := e.Data.(cmdArgs).card

	// check that it's conn's turn to move
	if conn != self.dconn {
		logOutOfTurn(conn)
		return s
	}

	// defender takes the cards
	if card == "" {
		self.newRound(NotBeat)
		return stateAttack
	}

	// check that the sent card is capable to beat
	if higher(card, self.cardToBeat, self.trump) != 1 {
		logWontBeat(card, self.cardToBeat, self.trump)
		return s
	}

	// throw card to desk
	if _, ok := conn.cards[card]; !ok {
		logStrangeCard(card, conn)
		return s
	}
	conn.toDesk(card)

	return stateAttack
}

func (self *gameState) showDesk(s sm.State, e *sm.Event) sm.State {
	desk, err := json.Marshal(DeskMsg{self.desk})
	if err != nil {
		log.Println(err)
		return s
	}

	self.hub.bcastChan <- desk

	return s
}

func (self *gameState) log(s sm.State, e *sm.Event) sm.State {
	log.Println(self)
	return s
}

//

func NewGameState() *gameState {
	gst := new(gameState)

	gst.sm = sm.New(stateCollection, uint(stateCount), uint(cmdCount))

	//TODO: refactor this
	gst.sm.On(cmdStart,
		[]sm.State{stateCollection},
		gst.handleStartInCollection,
	)
	gst.sm.On(cmdStart,
		[]sm.State{stateCollection},
		gst.showDesk,
	)
	gst.sm.On(cmdStart,
		[]sm.State{stateCollection},
		gst.log,
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack},
		gst.handleMoveInAttack,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateDefense},
		gst.handleMoveInDefense,
	)

	gst.sm.On(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		gst.showDesk,
	)
	gst.sm.On(cmdMove,
		[]sm.State{stateAttack, stateDefense},
		gst.log,
	)

	gst.deck = make([]string, 0, len(Suits)*len(CardValues)+ /*for trump*/ 1)
	gst.desk = make([]string, 0, 12)

	gst.hub = NewHub()

	return gst
}

//

type playerConn struct {
	gst *gameState

	cards map[string]struct{}

	conn      *websocket.Conn
	hubToConn chan []byte
}

func (self *playerConn) takeCard() {
	if card := self.gst.popCard(); card != "" {
		self.cards[card] = struct{}{}
	}
}

func (self *playerConn) toDesk(card string) {
	delete(self.cards, card)

	self.gst.desk = append(self.gst.desk, card)
}

func (self *playerConn) fromDesk() {
	for _, card := range self.gst.desk {
		self.cards[card] = struct{}{}
	}

	self.gst.desk = self.gst.desk[:0]
}

func (self *playerConn) write() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	for {
		select {
		case m, ok := <-self.hubToConn:
			if !ok {
				return
			}
			//TODO: text or binary?
			err := self.conn.WriteMessage(websocket.TextMessage, m)
			if err != nil {
				log.Println(err)
				return
			}
		}
	}
}

func (self *playerConn) read() {
	defer func() {
		err := self.conn.Close()
		if err != nil {
			log.Println(err)
		}
	}()

	var m PlayerMsg

	for {
		err := self.conn.ReadJSON(&m)
		if err != nil {
			log.Println(err)
			return
		}

		var event *sm.Event
		switch m.Cmd {
		case cmdStart:
			event = &sm.Event{cmdStart, nil}
		case cmdMove:
			event = &sm.Event{cmdMove, cmdArgs{self, m.Card}}
		default:
			log.Printf("unknown command: %v", m.Cmd)
			continue
		}

		err = self.gst.sm.Emit(event)
		if err != nil {
			log.Println(err)
		}
	}
}

//

var upgrader = websocket.Upgrader{
	ReadBufferSize:  1024,
	WriteBufferSize: 1024,
	CheckOrigin:     func(r *http.Request) bool { return true },
}

func playerHandler(gst *gameState) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			log.Println(err)
			return
		}

		p := &playerConn{gst, make(map[string]struct{}), conn, make(chan []byte)}

		gst.hub.regChan <- p
		defer func() {
			gst.hub.unregChan <- p
		}()

		go p.write()
		p.read()
	}
}

//

func startDurakSrv() error {
	gst := NewGameState()

	http.HandleFunc("/", playerHandler(gst))

	return http.ListenAndServe(":"+os.Getenv("PORT"), nil)
}

func main() {
	err := startDurakSrv()
	if err != nil {
		log.Fatal(err)
	}
}

func init() {
	rand.Seed(time.Now().UnixNano())

	runtime.GOMAXPROCS(runtime.NumCPU())
}
<|endoftext|>


<commit_before>// Copyright 2018 Istio Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kubernetes

import (
	"bytes"
	"errors"
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/remotecommand"

	"istio.io/istio/pkg/kube"
)

var (
	proxyContainer     = "istio-proxy"
	discoveryContainer = "discovery"
)

// Client is a helper wrapper around the Kube RESTClient for istioctl -> Pilot/Envoy/Mesh related things
type Client struct {
	Config *rest.Config
	*rest.RESTClient
}

// ExecClient is an interface for remote execution
type ExecClient interface {
	EnvoyDo(podName, podNamespace, method, path string, body []byte) ([]byte, error)
	AllPilotsDiscoveryDo(pilotNamespace, method, path string, body []byte) (map[string][]byte, error)
}

// NewClient is the contructor for the client wrapper
func NewClient(kubeconfig, configContext string) (*Client, error) {
	config, err := defaultRestConfig(kubeconfig, configContext)
	if err != nil {
		return nil, err
	}
	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}
	return &Client{config, restClient}, nil
}

// NewExecClient is the contructor for the mockable ExecClient interface
func NewExecClient(kubeconfig, configContext string) (ExecClient, error) {
	config, err := defaultRestConfig(kubeconfig, configContext)
	if err != nil {
		return nil, err
	}
	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}
	return &Client{config, restClient}, nil
}

func defaultRestConfig(kubeconfig, configContext string) (*rest.Config, error) {
	config, err := kube.BuildClientConfig(kubeconfig, configContext)
	if err != nil {
		return nil, err
	}
	config.APIPath = "/api"
	config.GroupVersion = &v1.SchemeGroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
	return config, nil
}

// PodExec takes a command and the pod data to run the command in the specified pod
func (client *Client) PodExec(podName, podNamespace, container string, command []string) (*bytes.Buffer, *bytes.Buffer, error) {
	req := client.Post().
		Resource("pods").
		Name(podName).
		Namespace(podNamespace).
		SubResource("exec").
		Param("container", container).
		VersionedParams(&v1.PodExecOptions{
			Container: container,
			Command:   command,
			Stdin:     false,
			Stdout:    true,
			Stderr:    true,
			TTY:       false,
		}, scheme.ParameterCodec)

	exec, err := remotecommand.NewSPDYExecutor(client.Config, "POST", req.URL())
	if err != nil {
		return nil, nil, err
	}

	var stdout, stderr bytes.Buffer
	err = exec.Stream(remotecommand.StreamOptions{
		Stdin:  nil,
		Stdout: &stdout,
		Stderr: &stderr,
		Tty:    false,
	})

	return &stdout, &stderr, err
}

// AllPilotsDiscoveryDo makes an http request to each Pilot discovery instance
func (client *Client) AllPilotsDiscoveryDo(pilotNamespace, method, path string, body []byte) (map[string][]byte, error) {
	pilots, err := client.GetPilotPods(pilotNamespace)
	if err != nil {
		return nil, err
	}
	if len(pilots) == 0 {
		return nil, errors.New("unable to find any Pilot instances")
	}
	cmd := []string{"/usr/local/bin/pilot-discovery", "request", method, path, string(body)}
	var stdout, stderr *bytes.Buffer
	result := map[string][]byte{}
	for _, pilot := range pilots {
		stdout, stderr, err = client.PodExec(pilot.Name, pilot.Namespace, discoveryContainer, cmd)
		if err != nil {
			return nil, fmt.Errorf("error execing into %v %v container: %v", pilot.Name, discoveryContainer, err)
		} else if stdout.String() != "" {
			result[pilot.Name] = stdout.Bytes()
		} else if stderr.String() != "" {
			return nil, fmt.Errorf("error execing into %v %v container: %v", pilot.Name, discoveryContainer, stderr.String())
		}
	}
	return result, err
}

// PilotDiscoveryDo makes an http request to a single Pilot discovery instance
func (client *Client) PilotDiscoveryDo(pilotNamespace, method, path string, body []byte) ([]byte, error) {
	pilots, err := client.GetPilotPods(pilotNamespace)
	if err != nil {
		return nil, err
	}
	if len(pilots) == 0 {
		return nil, errors.New("unable to find any Pilot instances")
	}
	cmd := []string{"/usr/local/bin/pilot-discovery", "request", method, path, string(body)}
	var stdout, stderr *bytes.Buffer
	stdout, stderr, err = client.PodExec(pilots[0].Name, pilots[0].Namespace, discoveryContainer, cmd)
	if err != nil {
		return nil, fmt.Errorf("error execing into %v %v container: %v", pilots[0].Name, discoveryContainer, err)
	} else if stderr.String() != "" {
		return nil, fmt.Errorf("error execing into %v %v container: %v", pilots[0].Name, discoveryContainer, stderr.String())
	}
	return stdout.Bytes(), err
}

// EnvoyDo makes an http request to the Envoy in the specified pod
func (client *Client) EnvoyDo(podName, podNamespace, method, path string, body []byte) ([]byte, error) {
	container, err := client.GetPilotAgentContainer(podName, podNamespace)
	if err != nil {
		return nil, fmt.Errorf("unable to retrieve proxy container name: %v", err)
	}
	cmd := []string{"/usr/local/bin/pilot-agent", "request", method, path, string(body)}
	stdout, stderr, err := client.PodExec(podName, podNamespace, container, cmd)
	if err != nil {
		return stdout.Bytes(), err
	} else if stderr.String() != "" {
		return nil, fmt.Errorf("error execing into %v: %v", podName, stderr.String())
	}
	return stdout.Bytes(), nil
}

// GetPilotPods retrieves the pod objects for all known pilots
func (client *Client) GetPilotPods(namespace string) ([]v1.Pod, error) {
	req := client.Get().
		Resource("pods").
		Namespace(namespace).
		Param("labelSelector", "istio=pilot")

	res := req.Do()
	if res.Error() != nil {
		return nil, fmt.Errorf("unable to retrieve Pods: %v", res.Error())
	}
	list := &v1.PodList{}
	if err := res.Into(list); err != nil {
		return nil, fmt.Errorf("unable to parse PodList: %v", res.Error())
	}
	return list.Items, nil
}

// GetPilotAgentContainer retrieves the pilot-agent container name for the specified pod
func (client *Client) GetPilotAgentContainer(podName, podNamespace string) (string, error) {
	req := client.Get().
		Resource("pods").
		Namespace(podNamespace).
		Name(podName)

	res := req.Do()
	if res.Error() != nil {
		return "", fmt.Errorf("unable to retrieve Pod: %v", res.Error())
	}
	pod := &v1.Pod{}
	if err := res.Into(pod); err != nil {
		return "", fmt.Errorf("unable to parse Pod: %v", res.Error())
	}
	for _, c := range pod.Spec.Containers {
		switch c.Name {
		case "egressgateway", "ingress", "ingressgateway":
			return c.Name, nil
		}
	}
	return proxyContainer, nil
}
<commit_msg>Abstract some common codes in istioctl/pkg/kubernetes/client.go (#7897)<commit_after>// Copyright 2018 Istio Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package kubernetes

import (
	"bytes"
	"errors"
	"fmt"

	"k8s.io/api/core/v1"
	"k8s.io/apimachinery/pkg/runtime/serializer"
	"k8s.io/client-go/kubernetes/scheme"
	"k8s.io/client-go/rest"
	"k8s.io/client-go/tools/remotecommand"

	"istio.io/istio/pkg/kube"
)

var (
	proxyContainer     = "istio-proxy"
	discoveryContainer = "discovery"
	pilotDiscoveryPath = "/usr/local/bin/pilot-discovery"
	pilotAgentPath     = "/usr/local/bin/pilot-agent"
)

// Client is a helper wrapper around the Kube RESTClient for istioctl -> Pilot/Envoy/Mesh related things
type Client struct {
	Config *rest.Config
	*rest.RESTClient
}

// ExecClient is an interface for remote execution
type ExecClient interface {
	EnvoyDo(podName, podNamespace, method, path string, body []byte) ([]byte, error)
	AllPilotsDiscoveryDo(pilotNamespace, method, path string, body []byte) (map[string][]byte, error)
}

// NewClient is the contructor for the client wrapper
func NewClient(kubeconfig, configContext string) (*Client, error) {
	config, err := defaultRestConfig(kubeconfig, configContext)
	if err != nil {
		return nil, err
	}
	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}
	return &Client{config, restClient}, nil
}

// NewExecClient is the contructor for the mockable ExecClient interface
func NewExecClient(kubeconfig, configContext string) (ExecClient, error) {
	config, err := defaultRestConfig(kubeconfig, configContext)
	if err != nil {
		return nil, err
	}
	restClient, err := rest.RESTClientFor(config)
	if err != nil {
		return nil, err
	}
	return &Client{config, restClient}, nil
}

func defaultRestConfig(kubeconfig, configContext string) (*rest.Config, error) {
	config, err := kube.BuildClientConfig(kubeconfig, configContext)
	if err != nil {
		return nil, err
	}
	config.APIPath = "/api"
	config.GroupVersion = &v1.SchemeGroupVersion
	config.NegotiatedSerializer = serializer.DirectCodecFactory{CodecFactory: scheme.Codecs}
	return config, nil
}

// PodExec takes a command and the pod data to run the command in the specified pod
func (client *Client) PodExec(podName, podNamespace, container string, command []string) (*bytes.Buffer, *bytes.Buffer, error) {
	req := client.Post().
		Resource("pods").
		Name(podName).
		Namespace(podNamespace).
		SubResource("exec").
		Param("container", container).
		VersionedParams(&v1.PodExecOptions{
			Container: container,
			Command:   command,
			Stdin:     false,
			Stdout:    true,
			Stderr:    true,
			TTY:       false,
		}, scheme.ParameterCodec)

	exec, err := remotecommand.NewSPDYExecutor(client.Config, "POST", req.URL())
	if err != nil {
		return nil, nil, err
	}

	var stdout, stderr bytes.Buffer
	err = exec.Stream(remotecommand.StreamOptions{
		Stdin:  nil,
		Stdout: &stdout,
		Stderr: &stderr,
		Tty:    false,
	})

	return &stdout, &stderr, err
}

// AllPilotsDiscoveryDo makes an http request to each Pilot discovery instance
func (client *Client) AllPilotsDiscoveryDo(pilotNamespace, method, path string, body []byte) (map[string][]byte, error) {
	pilots, err := client.GetPilotPods(pilotNamespace)
	if err != nil {
		return nil, err
	}
	if len(pilots) == 0 {
		return nil, errors.New("unable to find any Pilot instances")
	}
	cmd := []string{pilotDiscoveryPath, "request", method, path, string(body)}
	result := map[string][]byte{}
	for _, pilot := range pilots {
		res, err := client.ExtractExecResult(pilot.Name, pilot.Namespace, discoveryContainer, cmd)
		if err != nil {
			return nil, err
		}
		if len(res) > 0 {
			result[pilot.Name] = res
		}
	}
	return result, err
}

// PilotDiscoveryDo makes an http request to a single Pilot discovery instance
func (client *Client) PilotDiscoveryDo(pilotNamespace, method, path string, body []byte) ([]byte, error) {
	pilots, err := client.GetPilotPods(pilotNamespace)
	if err != nil {
		return nil, err
	}
	if len(pilots) == 0 {
		return nil, errors.New("unable to find any Pilot instances")
	}
	cmd := []string{pilotDiscoveryPath, "request", method, path, string(body)}
	return client.ExtractExecResult(pilots[0].Name, pilots[0].Namespace, discoveryContainer, cmd)
}

// EnvoyDo makes an http request to the Envoy in the specified pod
func (client *Client) EnvoyDo(podName, podNamespace, method, path string, body []byte) ([]byte, error) {
	container, err := client.GetPilotAgentContainer(podName, podNamespace)
	if err != nil {
		return nil, fmt.Errorf("unable to retrieve proxy container name: %v", err)
	}
	cmd := []string{pilotAgentPath, "request", method, path, string(body)}
	return client.ExtractExecResult(podName, podNamespace, container, cmd)
}

// ExtractExecResult wraps PodExec and return the execution result and error if has any.
func (client *Client) ExtractExecResult(podName, podNamespace, container string, cmd []string) ([]byte, error) {
	stdout, stderr, err := client.PodExec(podName, podNamespace, container, cmd)
	if err != nil {
		return nil, fmt.Errorf("error execing into %v/%v %v container: %v", podName, podNamespace, container, err)
	} else if stderr.String() != "" {
		return nil, fmt.Errorf("error execing into %v/%v %v container: %v", podName, podNamespace, container, stderr.String())
	}
	return stdout.Bytes(), nil
}

// GetPilotPods retrieves the pod objects for all known pilots
func (client *Client) GetPilotPods(namespace string) ([]v1.Pod, error) {
	req := client.Get().
		Resource("pods").
		Namespace(namespace).
		Param("labelSelector", "istio=pilot")

	res := req.Do()
	if res.Error() != nil {
		return nil, fmt.Errorf("unable to retrieve Pods: %v", res.Error())
	}
	list := &v1.PodList{}
	if err := res.Into(list); err != nil {
		return nil, fmt.Errorf("unable to parse PodList: %v", res.Error())
	}
	return list.Items, nil
}

// GetPilotAgentContainer retrieves the pilot-agent container name for the specified pod
func (client *Client) GetPilotAgentContainer(podName, podNamespace string) (string, error) {
	req := client.Get().
		Resource("pods").
		Namespace(podNamespace).
		Name(podName)

	res := req.Do()
	if res.Error() != nil {
		return "", fmt.Errorf("unable to retrieve Pod: %v", res.Error())
	}
	pod := &v1.Pod{}
	if err := res.Into(pod); err != nil {
		return "", fmt.Errorf("unable to parse Pod: %v", res.Error())
	}
	for _, c := range pod.Spec.Containers {
		switch c.Name {
		case "egressgateway", "ingress", "ingressgateway":
			return c.Name, nil
		}
	}
	return proxyContainer, nil
}
<|endoftext|>


<commit_before>package paths

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"testing"

	. "github.com/VonC/godbg"
	"github.com/VonC/senvgo/prgs"
	. "github.com/smartystreets/goconvey/convey"
)

func TestPath(t *testing.T) {

	Convey("Tests for NewPath", t, func() {
		Convey("An empty path remains empty", func() {
			SetBuffers(nil)
			p := NewPath("")
			So(p.path, ShouldEqual, "")
			So(NoOutput(), ShouldBeTrue)
		})

		Convey("An http path remains unchanged", func() {
			SetBuffers(nil)
			p := NewPath(`http://a\b/../c`)
			So(p.path, ShouldEqual, `http://a\b/../c`)
			So(NoOutput(), ShouldBeTrue)
		})
		Convey("A path without trailing / must have one if it is an existing folder", func() {
			SetBuffers(nil)
			p := NewPath(`../paths`)
			So(p.path, ShouldEqual, `..\paths\`)
			So(NoOutput(), ShouldBeTrue)
		})
		Convey("A path without trailing / must keep it even if it is not an existing folder", func() {
			SetBuffers(nil)
			p := NewPath(`xxx\`)
			p = NewPath(`xxx/`)
			So(p.path, ShouldEqual, `xxx\`)
			So(NoOutput(), ShouldBeTrue)
		})
	})

	Convey("Tests for IsDir", t, func() {

		Convey("A Path can test if it is a Dir", func() {
			SetBuffers(nil)
			p := NewPath("")
			So(p.IsDir(), ShouldBeFalse)
			So(OutString(), ShouldBeEmpty)
			So(ErrString(), ShouldEqual, `open : The system cannot find the file specified.
`)
		})

		Convey("An existing file is not a dir", func() {
			SetBuffers(nil)
			p := NewPath("paths_test.go")
			So(p.IsDir(), ShouldBeFalse)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `paths_test.go`)
		})

		Convey("An existing folder is a dir", func() {
			SetBuffers(nil)
			p := NewPath("..")
			So(p.IsDir(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `..\`)
			p = NewPath("../paths")
			So(p.IsDir(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `..\paths\`)
		})
		Convey("IsDir() can fail on f.Stat()", func() {
			// Errors
			SetBuffers(nil)
			fstat = testerrfstat
			p := NewPath("..")
			So(p.IsDir(), ShouldBeFalse)
			So(OutString(), ShouldBeEmpty)
			So(ErrString(), ShouldEqual, `fstat error on '..'
fstat error on '..'
`)
			So(p.path, ShouldEqual, `..`)
			fstat = ifstat
		})
	})

	Convey("Tests for Exists()", t, func() {

		Convey("Non-existing path must not exist", func() {
			SetBuffers(nil)
			p := NewPath("")
			So(p.Exists(), ShouldBeFalse)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, ``)

			SetBuffers(nil)
			p = NewPath("xxx")
			So(p.Exists(), ShouldBeFalse)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `xxx`)
		})
		Convey("Existing path must exist", func() {
			// Existing paths (files or folders)
			SetBuffers(nil)
			p := NewPath("paths_test.go")
			So(p.Exists(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `paths_test.go`)

			SetBuffers(nil)
			p = NewPath("../paths")
			So(p.Exists(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `..\paths\`)
		})

		Convey("Exists() can fail on os.Stat()", func() {
			// Stat error on path
			fosstat = testerrosfstat
			p := NewPath("test")
			SetBuffers(nil)
			So(p.Exists(), ShouldBeFalse)
			So(OutString(), ShouldBeEmpty)
			So(ErrString(), ShouldEqual, `os.Stat() error on 'test'
`)
			So(p.path, ShouldEqual, `test`)
			fosstat = ifosstat
		})
	})

	Convey("Tests for Path.String()", t, func() {

		Convey("nil path is '<nil>'", func() {
			SetBuffers(nil)
			var p *Path
			So(p.String(), ShouldEqual, `<nil>`)
			So(NoOutput(), ShouldBeTrue)

		})

		Convey("files or folders are unchanged", func() {
			SetBuffers(nil)
			p := NewPath("test")
			So(p.String(), ShouldEqual, `test`)
			So(NoOutput(), ShouldBeTrue)
		})

		Convey("long files or folders are abbreviated", func() {
			SetBuffers(nil)
			var data []byte
			data = append(data, ([]byte("long string with "))...)
			for i := 0; i < 100; i++ {
				data = append(data, ([]byte("abcd"))...)
			}
			p := NewPath(string(data))
			So(p.String(), ShouldEqual, `long string with abc (417)`)
			So(NoOutput(), ShouldBeTrue)
		})
	})

	Convey("Tests for NewPathDir()", t, func() {
		Convey("Path with a trailing separator should keep it", func() {
			// Path with a trailing separator should keep it
			SetBuffers(nil)
			p := NewPathDir("xxx/")
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `xxx\`)
		})
		Convey("Path without a trailing separator should have one", func() {
			SetBuffers(nil)
			p := NewPathDir("yyy")
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `yyy\`)
		})
	})
}

func testerrfstat(f *os.File) (fi os.FileInfo, err error) {
	return nil, fmt.Errorf("fstat error on '%+v'", f.Name())
}
func testerrosfstat(name string) (fi os.FileInfo, err error) {
	err = fmt.Errorf("os.Stat() error on '%s'", name)
	return nil, err
}

type testPrg struct{ name string }

func (tp *testPrg) Name() string { return tp.name }

type testPathWriter struct{ b *bytes.Buffer }

type testWriter struct{ w io.Writer }

func (tw *testWriter) Write(p []byte) (n int, err error) {
	s := string(p)
	if s == "prg2" {
		return 0, fmt.Errorf("Error writing '%s'", s)
	}
	return tw.w.Write(p)
}

func (tpw *testPathWriter) WritePath(prgs []prgs.Prg, w io.Writer) error {
	if err := pw.WritePath(prgs, w); err != nil {
		return err
	}
	return nil
}

func TestMain(t *testing.T) {
	tpw := &testPathWriter{b: bytes.NewBuffer(nil)}
	prgs := []prgs.Prg{&testPrg{name: "prg1"}, &testPrg{name: "prg2"}}
	Convey("Tests for Path Writer", t, func() {

		Convey("A Path writer writes any empty path if no prgs", func() {
			SetBuffers(nil)
			err := tpw.WritePath(prgs, tpw.b)
			So(err, ShouldBeNil)
			So(tpw.b.String(), ShouldEqual, "prg1prg2")
			So(NoOutput(), ShouldBeTrue)
		})

		Convey("A Path writer can report error during writing", func() {
			SetBuffers(nil)
			tpw.b = bytes.NewBuffer(nil)
			tw := &testWriter{w: tpw.b}
			err := tpw.WritePath(prgs, tw)
			So(err.Error(), ShouldEqual, "Error writing 'prg2'")
			So(tpw.b.String(), ShouldEqual, "prg1")
			So(NoOutput(), ShouldBeTrue)
		})
	})
}
<commit_msg>Test IsDir() f.Stat() error more precisely<commit_after>package paths

import (
	"bytes"
	"fmt"
	"io"
	"os"
	"testing"

	. "github.com/VonC/godbg"
	"github.com/VonC/senvgo/prgs"
	. "github.com/smartystreets/goconvey/convey"
)

func TestPath(t *testing.T) {

	Convey("Tests for NewPath", t, func() {
		Convey("An empty path remains empty", func() {
			SetBuffers(nil)
			p := NewPath("")
			So(p.path, ShouldEqual, "")
			So(NoOutput(), ShouldBeTrue)
		})

		Convey("An http path remains unchanged", func() {
			SetBuffers(nil)
			p := NewPath(`http://a\b/../c`)
			So(p.path, ShouldEqual, `http://a\b/../c`)
			So(NoOutput(), ShouldBeTrue)
		})
		Convey("A path without trailing / must have one if it is an existing folder", func() {
			SetBuffers(nil)
			p := NewPath(`../paths`)
			So(p.path, ShouldEqual, `..\paths\`)
			So(NoOutput(), ShouldBeTrue)
		})
		Convey("A path without trailing / must keep it even if it is not an existing folder", func() {
			SetBuffers(nil)
			p := NewPath(`xxx\`)
			p = NewPath(`xxx/`)
			So(p.path, ShouldEqual, `xxx\`)
			So(NoOutput(), ShouldBeTrue)
		})
	})

	Convey("Tests for IsDir", t, func() {

		Convey("A Path can test if it is a Dir", func() {
			SetBuffers(nil)
			p := NewPath("")
			So(p.IsDir(), ShouldBeFalse)
			So(OutString(), ShouldBeEmpty)
			So(ErrString(), ShouldEqual, `open : The system cannot find the file specified.
`)
		})

		Convey("An existing file is not a dir", func() {
			SetBuffers(nil)
			p := NewPath("paths_test.go")
			So(p.IsDir(), ShouldBeFalse)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `paths_test.go`)
		})

		Convey("An existing folder is a dir", func() {
			SetBuffers(nil)
			p := NewPath("..")
			So(p.IsDir(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `..\`)
			p = NewPath("../paths")
			So(p.IsDir(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `..\paths\`)
		})
		Convey("IsDir() can fail on f.Stat()", func() {
			// Errors
			fstat = testerrfstat
			p := NewPath("..")
			SetBuffers(nil)
			So(p.IsDir(), ShouldBeFalse)
			So(OutString(), ShouldBeEmpty)
			So(ErrString(), ShouldEqual, `fstat error on '..'
`)
			So(p.path, ShouldEqual, `..`)
			fstat = ifstat
		})
	})

	Convey("Tests for Exists()", t, func() {

		Convey("Non-existing path must not exist", func() {
			SetBuffers(nil)
			p := NewPath("")
			So(p.Exists(), ShouldBeFalse)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, ``)

			SetBuffers(nil)
			p = NewPath("xxx")
			So(p.Exists(), ShouldBeFalse)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `xxx`)
		})
		Convey("Existing path must exist", func() {
			// Existing paths (files or folders)
			SetBuffers(nil)
			p := NewPath("paths_test.go")
			So(p.Exists(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `paths_test.go`)

			SetBuffers(nil)
			p = NewPath("../paths")
			So(p.Exists(), ShouldBeTrue)
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `..\paths\`)
		})

		Convey("Exists() can fail on os.Stat()", func() {
			// Stat error on path
			fosstat = testerrosfstat
			p := NewPath("test")
			SetBuffers(nil)
			So(p.Exists(), ShouldBeFalse)
			So(OutString(), ShouldBeEmpty)
			So(ErrString(), ShouldEqual, `os.Stat() error on 'test'
`)
			So(p.path, ShouldEqual, `test`)
			fosstat = ifosstat
		})
	})

	Convey("Tests for Path.String()", t, func() {

		Convey("nil path is '<nil>'", func() {
			SetBuffers(nil)
			var p *Path
			So(p.String(), ShouldEqual, `<nil>`)
			So(NoOutput(), ShouldBeTrue)

		})

		Convey("files or folders are unchanged", func() {
			SetBuffers(nil)
			p := NewPath("test")
			So(p.String(), ShouldEqual, `test`)
			So(NoOutput(), ShouldBeTrue)
		})

		Convey("long files or folders are abbreviated", func() {
			SetBuffers(nil)
			var data []byte
			data = append(data, ([]byte("long string with "))...)
			for i := 0; i < 100; i++ {
				data = append(data, ([]byte("abcd"))...)
			}
			p := NewPath(string(data))
			So(p.String(), ShouldEqual, `long string with abc (417)`)
			So(NoOutput(), ShouldBeTrue)
		})
	})

	Convey("Tests for NewPathDir()", t, func() {
		Convey("Path with a trailing separator should keep it", func() {
			// Path with a trailing separator should keep it
			SetBuffers(nil)
			p := NewPathDir("xxx/")
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `xxx\`)
		})
		Convey("Path without a trailing separator should have one", func() {
			SetBuffers(nil)
			p := NewPathDir("yyy")
			So(NoOutput(), ShouldBeTrue)
			So(p.path, ShouldEqual, `yyy\`)
		})
	})
}

func testerrfstat(f *os.File) (fi os.FileInfo, err error) {
	return nil, fmt.Errorf("fstat error on '%+v'", f.Name())
}
func testerrosfstat(name string) (fi os.FileInfo, err error) {
	err = fmt.Errorf("os.Stat() error on '%s'", name)
	return nil, err
}

type testPrg struct{ name string }

func (tp *testPrg) Name() string { return tp.name }

type testPathWriter struct{ b *bytes.Buffer }

type testWriter struct{ w io.Writer }

func (tw *testWriter) Write(p []byte) (n int, err error) {
	s := string(p)
	if s == "prg2" {
		return 0, fmt.Errorf("Error writing '%s'", s)
	}
	return tw.w.Write(p)
}

func (tpw *testPathWriter) WritePath(prgs []prgs.Prg, w io.Writer) error {
	if err := pw.WritePath(prgs, w); err != nil {
		return err
	}
	return nil
}

func TestMain(t *testing.T) {
	tpw := &testPathWriter{b: bytes.NewBuffer(nil)}
	prgs := []prgs.Prg{&testPrg{name: "prg1"}, &testPrg{name: "prg2"}}
	Convey("Tests for Path Writer", t, func() {

		Convey("A Path writer writes any empty path if no prgs", func() {
			SetBuffers(nil)
			err := tpw.WritePath(prgs, tpw.b)
			So(err, ShouldBeNil)
			So(tpw.b.String(), ShouldEqual, "prg1prg2")
			So(NoOutput(), ShouldBeTrue)
		})

		Convey("A Path writer can report error during writing", func() {
			SetBuffers(nil)
			tpw.b = bytes.NewBuffer(nil)
			tw := &testWriter{w: tpw.b}
			err := tpw.WritePath(prgs, tw)
			So(err.Error(), ShouldEqual, "Error writing 'prg2'")
			So(tpw.b.String(), ShouldEqual, "prg1")
			So(NoOutput(), ShouldBeTrue)
		})
	})
}
<|endoftext|>


<commit_before>package registration

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/coreos/go-etcd/etcd"
	"github.com/nsqio/go-nsq"
)

// /opsee.co/routes/customer_id/instance_id/svcname = ip:port

const (
	routesPath = "/opsee.co/routes"
	// RegistrationTTL defines the number of seconds that a registration will be
	// valid.
	RegistrationTTL = 60
)

var (
	nsqLookupds = []string{
		"nsqlookupd-1.opsy.co",
		"nsqlookupd-2.opsy.co",
	}
)

type consumerService struct {
	etcdClient   *etcd.Client
	consumer     *nsq.Consumer
	stopChan     chan struct{}
	lookupdHosts []string
}

// NewConsumer creates a new consumer service connected to the "connected" topic
// in NSQ.
func NewConsumer(consumerName, etcdHost string, nsqLookupdHosts []string, concurrency int) (*consumerService, error) {
	consumer, err := nsq.NewConsumer("_.connected", consumerName, nsq.NewConfig())
	if err != nil {
		return nil, err
	}

	consumer.SetLogger(log.New(os.Stderr, "", log.LstdFlags), nsq.LogLevelInfo)

	svc := &consumerService{
		etcd.NewClient([]string{etcdHost}),
		consumer,
		make(chan struct{}, 1),
		nsqLookupdHosts,
	}

	svc.consumer.AddConcurrentHandlers(nsq.HandlerFunc(svc.registerConnection), concurrency)

	return svc, nil
}

// /opsee.co/routes/customer_id/instance_id/svcname = ip:port
func (c *consumerService) registerConnection(msg *nsq.Message) error {
	cMsg := &ConnectedMessage{}
	if err := json.Unmarshal(msg.Body, cMsg); err != nil {
		log.Println("Error unmarshaling connected message:", msg)
		return err
	}
	log.Println("Handling message:", msg.Body)

	for _, connectedSvc := range cMsg.Services {
		log.Printf("Registering %s service for customer %s, bastion %s, at IP: %s, port: %s", connectedSvc.Name, cMsg.CustomerID, cMsg.InstanceID, cMsg.IPAddress, connectedSvc.Port)
		key := fmt.Sprintf("/opsee.co/routes/%s/%s/%s", cMsg.CustomerID, cMsg.InstanceID, connectedSvc.Name)
		value := fmt.Sprintf("%s:%d", cMsg.IPAddress, connectedSvc.Port)

		resp, err := c.etcdClient.Set(key, value, 60)
		if err != nil {
			return err
		}
		log.Printf("ETCD Response Node: %s", *resp.Node)
	}

	return nil
}

func (c *consumerService) Start() error {
	return c.consumer.ConnectToNSQLookupds(c.lookupdHosts)
}

func (c *consumerService) Stop() error {
	c.consumer.Stop()
	return nil
}
<commit_msg>Store the serialized portmap service array in the instance ID key.<commit_after>package registration

import (
	"encoding/json"
	"fmt"
	"log"
	"os"

	"github.com/coreos/go-etcd/etcd"
	"github.com/nsqio/go-nsq"
)

// /opsee.co/routes/customer_id/instance_id/svcname = ip:port

const (
	routesPath = "/opsee.co/routes"
	// RegistrationTTL defines the number of seconds that a registration will be
	// valid.
	RegistrationTTL = 60
)

var (
	nsqLookupds = []string{
		"nsqlookupd-1.opsy.co",
		"nsqlookupd-2.opsy.co",
	}
)

type consumerService struct {
	etcdClient   *etcd.Client
	consumer     *nsq.Consumer
	stopChan     chan struct{}
	lookupdHosts []string
}

// NewConsumer creates a new consumer service connected to the "connected" topic
// in NSQ.
func NewConsumer(consumerName, etcdHost string, nsqLookupdHosts []string, concurrency int) (*consumerService, error) {
	consumer, err := nsq.NewConsumer("_.connected", consumerName, nsq.NewConfig())
	if err != nil {
		return nil, err
	}

	consumer.SetLogger(log.New(os.Stderr, "", log.LstdFlags), nsq.LogLevelInfo)

	svc := &consumerService{
		etcd.NewClient([]string{etcdHost}),
		consumer,
		make(chan struct{}, 1),
		nsqLookupdHosts,
	}

	svc.consumer.AddConcurrentHandlers(nsq.HandlerFunc(svc.registerConnection), concurrency)

	return svc, nil
}

// /opsee.co/routes/customer_id/instance_id
func (c *consumerService) registerConnection(msg *nsq.Message) error {
	cMsg := &ConnectedMessage{}
	if err := json.Unmarshal(msg.Body, cMsg); err != nil {
		log.Println("Error unmarshaling connected message:", msg)
		return err
	}
	log.Println("Handling message:", string(msg.Body))

	svcs, err := json.Marshal(cMsg.Services)
	if err != nil {
		return err
	}

	key := fmt.Sprintf("/opsee.co/routes/%s/%s", cMsg.CustomerID, cMsg.InstanceID)
	value := string(svcs)

	if _, err := c.etcdClient.Set(key, value, 60); err != nil {
		return err
	}

	return nil
}

func (c *consumerService) Start() error {
	return c.consumer.ConnectToNSQLookupds(c.lookupdHosts)
}

func (c *consumerService) Stop() error {
	c.consumer.Stop()
	return nil
}
<|endoftext|>


<commit_before>package pg

import (
	"fmt"
	"net"
)

var (
	ErrSSLNotSupported = errorf("pg: SSL is not enabled on the server")

	ErrNoRows    = errorf("pg: no rows in result set")
	ErrMultiRows = errorf("pg: multiple rows in result set")

	errClosed         = errorf("pg: database is closed")
	errTxDone         = errorf("pg: transaction has already been committed or rolled back")
	errStmtClosed     = errorf("pg: statement is closed")
	errListenerClosed = errorf("pg: listener is closed")
)

var (
	_ Error = &pgError{}
	_ Error = &pgError{}
)

type Error interface {
	Field(byte) string
}

type dbError struct {
	s string
}

func errorf(s string, args ...interface{}) dbError {
	return dbError{s: fmt.Sprintf(s, args...)}
}

func (err dbError) Error() string {
	return err.s
}

type pgError struct {
	c map[byte]string
}

func (err *pgError) Field(k byte) string {
	return err.c[k]
}

func (err *pgError) Error() string {
	return fmt.Sprintf(
		"%s #%s %s: %s",
		err.Field('S'), err.Field('C'), err.Field('M'), err.Field('D'),
	)
}

type IntegrityError struct {
	*pgError
}

func canRetry(e error) bool {
	if e == nil {
		return false
	}
	if neterr, ok := e.(net.Error); ok && neterr.Timeout() {
		return false
	}
	if _, ok := e.(*dbError); ok {
		return false
	}
	if pgerr, ok := e.(Error); ok && pgerr.Field('C') != "40001" {
		return false
	}
	return true
}
<commit_msg>Don't retry on dbError.<commit_after>package pg

import (
	"fmt"
	"net"
)

var (
	ErrSSLNotSupported = errorf("pg: SSL is not enabled on the server")

	ErrNoRows    = errorf("pg: no rows in result set")
	ErrMultiRows = errorf("pg: multiple rows in result set")

	errClosed         = errorf("pg: database is closed")
	errTxDone         = errorf("pg: transaction has already been committed or rolled back")
	errStmtClosed     = errorf("pg: statement is closed")
	errListenerClosed = errorf("pg: listener is closed")
)

var (
	_ Error = &pgError{}
	_ Error = &pgError{}
)

type Error interface {
	Field(byte) string
}

type dbError struct {
	s string
}

func errorf(s string, args ...interface{}) dbError {
	return dbError{s: fmt.Sprintf(s, args...)}
}

func (err dbError) Error() string {
	return err.s
}

type pgError struct {
	c map[byte]string
}

func (err *pgError) Field(k byte) string {
	return err.c[k]
}

func (err *pgError) Error() string {
	return fmt.Sprintf(
		"%s #%s %s: %s",
		err.Field('S'), err.Field('C'), err.Field('M'), err.Field('D'),
	)
}

type IntegrityError struct {
	*pgError
}

func canRetry(e error) bool {
	if e == nil {
		return false
	}
	if neterr, ok := e.(net.Error); ok && neterr.Timeout() {
		return false
	}
	if _, ok := e.(dbError); ok {
		return false
	}
	if pgerr, ok := e.(Error); ok && pgerr.Field('C') != "40001" {
		return false
	}
	return true
}
<|endoftext|>


<commit_before>/**
 * Copyright (C) 2016 tech0522.tk
 */
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"
	"unicode/utf8"

	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/line/line-bot-sdk-go/linebot/httphandler"
)

const ButtonLabel = "レシピを開く"
const NotFoundReplyStickerPackageID = "2"
const NotFoundReplyStickerID = "38"
const MaxRecipesToReply = 5
const MaxRecipeDescLength = 60
const RecipeDescTailIfTooLong = "..."
const RecipeCarouselAltTextTailing = "..."

type RecipeDBSearchQuery struct {
	Size int `json:"size"`
	Query struct {
		MultiMatch struct {
			Query string `json:"query"`
			Type string `json:"type"`
			Fields []string `json:"fields"`
		} `json:"multi_match"`
	} `json:"query"`
}

type RecipeDBSearchResult struct {
	Hits struct {
		Total int `json:"total"`
		Hits  []struct {
			Source struct {
				Title    string `json:"title"`
				Desc     string `json:"description"`
				ImageUrl string `json:"image_url"`
				Url      string `json:"detail_url"`
			} `json:"_source"`
		} `json:"hits"`
	} `json:"hits"`
}

func buildSearchQuery(rawQuery string) ([]byte, error) {
	var query RecipeDBSearchQuery
	query.Size = MaxRecipesToReply
	query.Query.MultiMatch.Query = rawQuery
	query.Query.MultiMatch.Type = "cross_fields"
	query.Query.MultiMatch.Fields = []string{"materials.keyword^100", "materials^5", "title", "description"}
	return json.Marshal(query)
}

func replyRecipe(bot *linebot.Client, replyToken string, rawQuery string, config *RecipeLinebotConfig) {
	query, err := buildSearchQuery(rawQuery)
	if err != nil {
		log.Fatal(err)
	}
	apiUrl := url.URL{Scheme: "http", Host: config.RecipeDB.Host,
		Path: path.Join(config.RecipeDB.Index, config.RecipeDB.RecipeDoctype, "_search")}
	resp, err := http.Post(apiUrl.String(), "application/json", bytes.NewReader(query))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()
	if resp.StatusCode != 200 {
		body, _ := ioutil.ReadAll(resp.Body)
		log.Fatal("Bad status code: code=" + resp.Status + " body=" + string(body))
	}
	var result RecipeDBSearchResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		log.Fatal(err)
	}
	var replyMsg linebot.Message
	if result.Hits.Total == 0 {
		replyMsg = linebot.NewStickerMessage(NotFoundReplyStickerPackageID, NotFoundReplyStickerID)
	} else {
		var cols []*linebot.CarouselColumn
		for _, hit := range result.Hits.Hits {
			desc := hit.Source.Desc
			if len(desc) > MaxRecipeDescLength {
				descEnd := MaxRecipeDescLength - len(RecipeDescTailIfTooLong)
				for !utf8.RuneStart(desc[descEnd]) {
					descEnd--
				}
				desc = desc[0:descEnd] + RecipeDescTailIfTooLong
			}
			cols = append(cols, linebot.NewCarouselColumn(hit.Source.ImageUrl, hit.Source.Title, desc,
				linebot.NewURITemplateAction(ButtonLabel, hit.Source.Url)))
		}
		altText := result.Hits.Hits[0].Source.Title + RecipeCarouselAltTextTailing
		tmpl := linebot.NewCarouselTemplate(cols...)
		replyMsg = linebot.NewTemplateMessage(altText, tmpl)
	}
	if _, err = bot.ReplyMessage(replyToken, replyMsg).Do(); err != nil {
		log.Fatal(err)
	}
}

func onMessageEvent(bot *linebot.Client, event *linebot.Event, config *RecipeLinebotConfig) {
	resp, err := bot.GetProfile((*event.Source).UserID).Do()
	if err != nil {
		log.Fatal(err)
	}
	switch recvMsg := event.Message.(type) {
	case *linebot.TextMessage:
		log.Printf("receive text message: from=%s, text=%s\n", resp.DisplayName, recvMsg.Text)
		replyRecipe(bot, (*event).ReplyToken, recvMsg.Text, config)
	default:
		log.Printf("receive a some kind of event: from=%s\n", resp.DisplayName)
	}
}

func serveAsBot(config *RecipeLinebotConfig) {
	log.Printf("start bot server: addr=%s, path=%s", config.BotServer.ListenAddr, config.BotServer.APIPath)
	handler, err := httphandler.New(config.BotServer.ChSecret, config.BotServer.ChToken)
	if err != nil {
		log.Fatal(err)
	}
	handler.HandleEvents(func(events []*linebot.Event, req *http.Request) {
		bot, err := handler.NewClient()
		if err != nil {
			log.Fatal(err)
			return
		}
		for _, event := range events {
			if event.Type == linebot.EventTypeMessage {
				onMessageEvent(bot, event, config)
			} else {
				log.Printf("%s\n", event.Timestamp.String())
			}
		}
	})
	http.Handle(config.BotServer.APIPath, handler)
	if err := http.ListenAndServeTLS(config.BotServer.ListenAddr, config.BotServer.CertFilePath,
		config.BotServer.KeyFilePath, nil); err != nil {
		log.Fatal(err)
	}
	log.Print("bot server finished")
}
<commit_msg>:art: Refactor the codes of bot-server<commit_after>/**
 * Copyright (C) 2016 tech0522.tk
 */
package main

import (
	"bytes"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"
	"net/url"
	"path"
	"unicode/utf8"

	"github.com/line/line-bot-sdk-go/linebot"
	"github.com/line/line-bot-sdk-go/linebot/httphandler"
)

const ButtonLabel = "レシピを開く"
const NotFoundReplyStickerPackageID = "2"
const NotFoundReplyStickerID = "38"
const MaxRecipesToReply = 5
const MaxRecipeDescLength = 60
const RecipeDescTailIfTooLong = "..."
const RecipeCarouselAltTextTailing = "..."

type RecipeDBSearchQuery struct {
	Size  int `json:"size"`
	Query struct {
		MultiMatch struct {
			Query  string   `json:"query"`
			Type   string   `json:"type"`
			Fields []string `json:"fields"`
		} `json:"multi_match"`
	} `json:"query"`
}

type RecipeDBSearchResult struct {
	Hits struct {
		Total int `json:"total"`
		Hits  []struct {
			Source struct {
				Title    string `json:"title"`
				Desc     string `json:"description"`
				ImageUrl string `json:"image_url"`
				Url      string `json:"detail_url"`
			} `json:"_source"`
		} `json:"hits"`
	} `json:"hits"`
}

func searchRecipes(rawQuery string, config *RecipeLinebotConfig) RecipeDBSearchResult {
	// Build the search query
	var query RecipeDBSearchQuery
	query.Size = MaxRecipesToReply
	query.Query.MultiMatch.Query = rawQuery
	query.Query.MultiMatch.Type = "cross_fields"
	query.Query.MultiMatch.Fields = []string{"materials.keyword^100", "materials^5", "title", "description"}
	queryAsJson, err := json.Marshal(query)
	if err != nil {
		log.Fatal(err)
	}

	// Post the search request
	apiUrl := url.URL{Scheme: "http", Host: config.RecipeDB.Host,
		Path: path.Join(config.RecipeDB.Index, config.RecipeDB.RecipeDoctype, "_search")}
	resp, err := http.Post(apiUrl.String(), "application/json", bytes.NewReader(queryAsJson))
	if err != nil {
		log.Fatal(err)
	}
	defer resp.Body.Close()

	// Interpret the response as search result
	if resp.StatusCode != 200 {
		body, _ := ioutil.ReadAll(resp.Body)
		log.Fatal("Bad status code: code=" + resp.Status + " body=" + string(body))
	}
	var result RecipeDBSearchResult
	if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
		log.Fatal(err)
	}
	return result
}

func roundRecipeDescription(desc string) string {
	if len(desc) <= MaxRecipeDescLength {
		return desc
	}
	descEnd := MaxRecipeDescLength - len(RecipeDescTailIfTooLong)
	for !utf8.RuneStart(desc[descEnd]) {
		descEnd--
	}
	return desc[0:descEnd] + RecipeDescTailIfTooLong
}

func newRecipesMessage(result *RecipeDBSearchResult) *linebot.TemplateMessage {
	// build the carousel columns
	var cols []*linebot.CarouselColumn
	for _, hit := range result.Hits.Hits {
		col := linebot.NewCarouselColumn(hit.Source.ImageUrl, hit.Source.Title,
			roundRecipeDescription(hit.Source.Desc),
			linebot.NewURITemplateAction(ButtonLabel, hit.Source.Url))
		cols = append(cols, col)
	}

	// build as carousel message
	altText := result.Hits.Hits[0].Source.Title + RecipeCarouselAltTextTailing
	tmpl := linebot.NewCarouselTemplate(cols...)
	return linebot.NewTemplateMessage(altText, tmpl)
}

func onMessageEvent(bot *linebot.Client, event *linebot.Event, config *RecipeLinebotConfig) {
	resp, err := bot.GetProfile(event.Source.UserID).Do()
	if err != nil {
		log.Fatal(err)
	}
	switch recvMsg := event.Message.(type) {
	case *linebot.TextMessage:
		log.Printf("receive text message: from=%s, text=%s\n", resp.DisplayName, recvMsg.Text)
		result := searchRecipes(recvMsg.Text, config)
		var replyMsg linebot.Message
		if result.Hits.Total == 0 {
			replyMsg = linebot.NewStickerMessage(NotFoundReplyStickerPackageID, NotFoundReplyStickerID)
		} else {
			replyMsg = newRecipesMessage(&result)
		}
		if _, err := bot.ReplyMessage(event.ReplyToken, replyMsg).Do(); err != nil {
			log.Fatal(err)
		}
	default:
		log.Printf("receive a some kind of event: from=%s\n", resp.DisplayName)
	}
}

func serveAsBot(config *RecipeLinebotConfig) {
	log.Printf("start bot server: addr=%s, path=%s", config.BotServer.ListenAddr, config.BotServer.APIPath)
	handler, err := httphandler.New(config.BotServer.ChSecret, config.BotServer.ChToken)
	if err != nil {
		log.Fatal(err)
	}
	handler.HandleEvents(func(events []*linebot.Event, req *http.Request) {
		bot, err := handler.NewClient()
		if err != nil {
			log.Fatal(err)
			return
		}
		for _, event := range events {
			if event.Type == linebot.EventTypeMessage {
				onMessageEvent(bot, event, config)
			} else {
				log.Printf("%s\n", event.Timestamp.String())
			}
		}
	})
	http.Handle(config.BotServer.APIPath, handler)
	if err := http.ListenAndServeTLS(config.BotServer.ListenAddr, config.BotServer.CertFilePath,
		config.BotServer.KeyFilePath, nil); err != nil {
		log.Fatal(err)
	}
	log.Print("bot server finished")
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"text/template"
)

var g_hwaf_version = flag.String("hwaf-version", "20131204", "hwaf version to use")
var g_hwaf_variant = flag.String("hwaf-variant", "x86_64-slc6-gcc47-opt", "hwaf variant to use")
var g_siteroot = flag.String("siteroot", "/opt/atlas-sw", "where to install software")
var g_worch_profile = flag.String("profile", "build-lcg.cfg", "worch-profile to run for the build")
var g_docker_tag = flag.String("docker-tag", "binet/lcg-65", "tag to apply as a result of the build")

type Dockerfile struct {
	Container   string
	Siteroot    string
	HwafVersion string
	HwafVariant string
	Profile     string
	LcgBranch   string
	DockerTag   string
}

const tmpl = `
## a Dockerfile to install the whole LCG stack
FROM {{.Container}}
MAINTAINER binet@cern.ch

ENV HOME     /root
RUN export   HOME

ENV SITEROOT {{.Siteroot}}
RUN export SITEROOT

ENV HWAF_VARIANT {{.HwafVariant}}
ENV HWAF_VERSION {{.HwafVersion}}
ENV HWAF_ROOT    $SITEROOT/hwaf/hwaf-$HWAF_VERSION/linux-amd64
ENV PATH         $HWAF_ROOT/bin:$PATH

RUN export HWAF_VARIANT
RUN export HWAF_VERSION
RUN export HWAF_ROOT
RUN export PATH

VOLUME ["/build"]
ADD build-lcg.sh /build/build-lcg.sh

RUN /build/build-lcg.sh /build $HWAF_VERSION $HWAF_VARIANT {{.LcgBranch}} $SITEROOT {{.Profile}}

ENV MODULEPATH $SITEROOT/sw/modules/$HWAF_VARIANT:$MODULEPATH
RUN export MODULEPATH

## EOF
`

func main() {
	flag.Parse()

	pwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}

	dir, err := ioutil.TempDir("", "go-docker-build-")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	//script := "/build/build-lcg.sh"
	voldir := filepath.Join(pwd, "lcg", *g_hwaf_variant)

	err = copytree(dir, voldir)
	if err != nil {
		panic(err)
	}

	cfg := Dockerfile{
		Container:   "binet/slc",
		Siteroot:    *g_siteroot,
		HwafVersion: *g_hwaf_version,
		HwafVariant: *g_hwaf_variant,
		Profile:     *g_worch_profile,
		LcgBranch:   "lcg-65-branch",
		DockerTag:   *g_docker_tag + "-" + *g_hwaf_variant,
	}

	docker_tmpl, err := template.New("Dockerfile").Parse(tmpl)
	if err != nil {
		panic(err)
	}

	docker_file, err := os.Create(filepath.Join(dir, "Dockerfile"))
	if err != nil {
		panic(err)
	}
	defer docker_file.Close()

	err = docker_tmpl.Execute(docker_file, cfg)
	if err != nil {
		panic(err)
	}
	err = docker_file.Sync()
	if err != nil {
		panic(err)
	}
	err = docker_file.Close()
	if err != nil {
		panic(err)
	}

	cmdargs := []string{
		"docker",
		"build",
		"-rm",
		"-t=" + cfg.DockerTag,
		dir,
	}

	fmt.Printf(">>> [%v]\n", cmdargs)

	docker := exec.Command("sudo", cmdargs...)

	docker.Stdout = os.Stdout
	docker.Stderr = os.Stderr
	docker.Stdin = os.Stdin

	err = docker.Run()
	if err != nil {
		panic(err)
	}
}

func copytree(dstdir, srcdir string) error {
	var err error

	if !path_exists(dstdir) {
		err = os.MkdirAll(dstdir, 0755)
		if err != nil {
			return err
		}
	}

	err = filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error {
		rel := ""
		rel, err = filepath.Rel(srcdir, path)
		out := filepath.Join(dstdir, rel)
		fmode := info.Mode()
		if fmode.IsDir() {
			err = os.MkdirAll(out, fmode.Perm())
			if err != nil {
				return err
			}
		} else if fmode.IsRegular() {
			dst, err := os.OpenFile(out, os.O_CREATE|os.O_RDWR, fmode.Perm())
			if err != nil {
				return nil
			}
			src, err := os.Open(path)
			if err != nil {
				return nil
			}
			_, err = io.Copy(dst, src)
			if err != nil {
				return nil
			}
		} else if (fmode & os.ModeSymlink) != 0 {
			rlink, err := os.Readlink(path)
			if err != nil {
				return err
			}
			err = os.Symlink(rlink, out)
			if err != nil {
				return err
			}
		} else {
			return fmt.Errorf("unhandled mode (%v) for path [%s]", fmode, path)
		}
		return nil
	})
	return err
}

func path_exists(name string) bool {
	_, err := os.Stat(name)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

// EOF
<commit_msg>lcg: display build time.<commit_after>package main

import (
	"flag"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"path/filepath"
	"text/template"
	"time"
)

var g_hwaf_version = flag.String("hwaf-version", "20131204", "hwaf version to use")
var g_hwaf_variant = flag.String("hwaf-variant", "x86_64-slc6-gcc47-opt", "hwaf variant to use")
var g_siteroot = flag.String("siteroot", "/opt/atlas-sw", "where to install software")
var g_worch_profile = flag.String("profile", "build-lcg.cfg", "worch-profile to run for the build")
var g_docker_tag = flag.String("docker-tag", "binet/lcg-65", "tag to apply as a result of the build")

type Dockerfile struct {
	Container   string
	Siteroot    string
	HwafVersion string
	HwafVariant string
	Profile     string
	LcgBranch   string
	DockerTag   string
}

const tmpl = `
## a Dockerfile to install the whole LCG stack
FROM {{.Container}}
MAINTAINER binet@cern.ch

ENV HOME     /root
RUN export   HOME

ENV SITEROOT {{.Siteroot}}
RUN export SITEROOT

ENV HWAF_VARIANT {{.HwafVariant}}
ENV HWAF_VERSION {{.HwafVersion}}
ENV HWAF_ROOT    $SITEROOT/hwaf/hwaf-$HWAF_VERSION/linux-amd64
ENV PATH         $HWAF_ROOT/bin:$PATH

RUN export HWAF_VARIANT
RUN export HWAF_VERSION
RUN export HWAF_ROOT
RUN export PATH

VOLUME ["/build"]
ADD build-lcg.sh /build/build-lcg.sh

RUN /build/build-lcg.sh /build $HWAF_VERSION $HWAF_VARIANT {{.LcgBranch}} $SITEROOT {{.Profile}}

ENV MODULEPATH $SITEROOT/sw/modules/$HWAF_VARIANT:$MODULEPATH
RUN export MODULEPATH

## EOF
`

func main() {
	flag.Parse()

	start := time.Now()
	defer func() {
		fmt.Printf(">>> build: [%v]\n", time.Since(start))
	}()

	pwd, err := os.Getwd()
	if err != nil {
		panic(err)
	}

	dir, err := ioutil.TempDir("", "go-docker-build-")
	if err != nil {
		panic(err)
	}
	defer os.RemoveAll(dir)

	//script := "/build/build-lcg.sh"
	voldir := filepath.Join(pwd, "lcg", *g_hwaf_variant)

	err = copytree(dir, voldir)
	if err != nil {
		panic(err)
	}

	cfg := Dockerfile{
		Container:   "binet/slc",
		Siteroot:    *g_siteroot,
		HwafVersion: *g_hwaf_version,
		HwafVariant: *g_hwaf_variant,
		Profile:     *g_worch_profile,
		LcgBranch:   "lcg-65-branch",
		DockerTag:   *g_docker_tag + "-" + *g_hwaf_variant,
	}

	docker_tmpl, err := template.New("Dockerfile").Parse(tmpl)
	if err != nil {
		panic(err)
	}

	docker_file, err := os.Create(filepath.Join(dir, "Dockerfile"))
	if err != nil {
		panic(err)
	}
	defer docker_file.Close()

	err = docker_tmpl.Execute(docker_file, cfg)
	if err != nil {
		panic(err)
	}
	err = docker_file.Sync()
	if err != nil {
		panic(err)
	}
	err = docker_file.Close()
	if err != nil {
		panic(err)
	}

	cmdargs := []string{
		"docker",
		"build",
		"-rm",
		"-t=" + cfg.DockerTag,
		dir,
	}

	fmt.Printf(">>> [%v]\n", cmdargs)

	docker := exec.Command("sudo", cmdargs...)

	docker.Stdout = os.Stdout
	docker.Stderr = os.Stderr
	docker.Stdin = os.Stdin

	err = docker.Run()
	if err != nil {
		panic(err)
	}
}

func copytree(dstdir, srcdir string) error {
	var err error

	if !path_exists(dstdir) {
		err = os.MkdirAll(dstdir, 0755)
		if err != nil {
			return err
		}
	}

	err = filepath.Walk(srcdir, func(path string, info os.FileInfo, err error) error {
		rel := ""
		rel, err = filepath.Rel(srcdir, path)
		out := filepath.Join(dstdir, rel)
		fmode := info.Mode()
		if fmode.IsDir() {
			err = os.MkdirAll(out, fmode.Perm())
			if err != nil {
				return err
			}
		} else if fmode.IsRegular() {
			dst, err := os.OpenFile(out, os.O_CREATE|os.O_RDWR, fmode.Perm())
			if err != nil {
				return nil
			}
			src, err := os.Open(path)
			if err != nil {
				return nil
			}
			_, err = io.Copy(dst, src)
			if err != nil {
				return nil
			}
		} else if (fmode & os.ModeSymlink) != 0 {
			rlink, err := os.Readlink(path)
			if err != nil {
				return err
			}
			err = os.Symlink(rlink, out)
			if err != nil {
				return err
			}
		} else {
			return fmt.Errorf("unhandled mode (%v) for path [%s]", fmode, path)
		}
		return nil
	})
	return err
}

func path_exists(name string) bool {
	_, err := os.Stat(name)
	if err == nil {
		return true
	}
	if os.IsNotExist(err) {
		return false
	}
	return false
}

// EOF
<|endoftext|>


<commit_before>package s3api

import (
	"context"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/filer2"
	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html

	// collect parameters
	bucket, _ := getBucketAndObject(r)

	originalPrefix, marker, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query())

	if maxKeys < 0 {
		writeErrorResponse(w, ErrInvalidMaxKeys, r.URL)
		return
	}
	if delimiter != "" && delimiter != "/" {
		writeErrorResponse(w, ErrNotImplemented, r.URL)
		return
	}

	if marker == "" {
		marker = startAfter
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		writeErrorResponse(w, ErrInternalError, r.URL)
		return
	}

	writeSuccessResponseXML(w, encodeResponse(response))
}

func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html

	// collect parameters
	bucket, _ := getBucketAndObject(r)

	originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query())

	if maxKeys < 0 {
		writeErrorResponse(w, ErrInvalidMaxKeys, r.URL)
		return
	}
	if delimiter != "" && delimiter != "/" {
		writeErrorResponse(w, ErrNotImplemented, r.URL)
		return
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		writeErrorResponse(w, ErrInternalError, r.URL)
		return
	}

	writeSuccessResponseXML(w, encodeResponse(response))
}

func (s3a *S3ApiServer) listFilerEntries(bucket, originalPrefix string, maxKeys int, marker string, delimiter string) (response ListBucketResult, err error) {
	// convert full path prefix into directory name and prefix for entry name
	dir, prefix := filepath.Split(originalPrefix)
	if strings.HasPrefix(dir, "/") {
		dir = dir[1:]
	}

	// check filer
	err = s3a.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		request := &filer_pb.ListEntriesRequest{
			Directory:          fmt.Sprintf("%s/%s/%s", s3a.option.BucketsPath, bucket, dir),
			Prefix:             prefix,
			Limit:              uint32(maxKeys + 1),
			StartFromFileName:  marker,
			InclusiveStartFrom: false,
		}

		stream, err := client.ListEntries(context.Background(), request)
		if err != nil {
			return fmt.Errorf("list buckets: %v", err)
		}

		var contents []ListEntry
		var commonPrefixes []PrefixEntry
		var counter int
		var lastEntryName string
		var isTruncated bool

		for {
			resp, recvErr := stream.Recv()
			if recvErr != nil {
				if recvErr == io.EOF {
					break
				} else {
					return recvErr
				}
			}

			entry := resp.Entry
			counter++
			if counter > maxKeys {
				isTruncated = true
				break
			}
			lastEntryName = entry.Name
			if entry.IsDirectory {
				if entry.Name != ".uploads" {
					prefix = fmt.Sprintf("%s%s/", dir, entry.Name)

					commonPrefixes = append(commonPrefixes, PrefixEntry{
						Prefix: prefix,
					})

					if delimiter != "/" {
						response, _ := s3a.listFilerEntries(bucket, prefix, maxKeys, marker, delimiter)
						for _, content := range response.Contents {
							contents = append(contents, content)
						}
					}
				}
			} else {
				contents = append(contents, ListEntry{
					Key:          fmt.Sprintf("%s%s", dir, entry.Name),
					LastModified: time.Unix(entry.Attributes.Mtime, 0).UTC(),
					ETag:         "\"" + filer2.ETag(entry) + "\"",
					Size:         int64(filer2.TotalSize(entry.Chunks)),
					Owner: CanonicalUser{
						ID:          fmt.Sprintf("%x", entry.Attributes.Uid),
						DisplayName: entry.Attributes.UserName,
					},
					StorageClass: "STANDARD",
				})
			}

		}

		response = ListBucketResult{
			Name:           bucket,
			Prefix:         originalPrefix,
			Marker:         marker,
			NextMarker:     lastEntryName,
			MaxKeys:        maxKeys,
			Delimiter:      delimiter,
			IsTruncated:    isTruncated,
			Contents:       contents,
			CommonPrefixes: commonPrefixes,
		}

		glog.V(4).Infof("read directory: %v, found: %v, %+v", request, counter, response)

		return nil
	})

	return
}

func getListObjectsV2Args(values url.Values) (prefix, token, startAfter, delimiter string, fetchOwner bool, maxkeys int) {
	prefix = values.Get("prefix")
	token = values.Get("continuation-token")
	startAfter = values.Get("start-after")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	fetchOwner = values.Get("fetch-owner") == "true"
	return
}

func getListObjectsV1Args(values url.Values) (prefix, marker, delimiter string, maxkeys int) {
	prefix = values.Get("prefix")
	marker = values.Get("marker")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	return
}
<commit_msg>istObjects和listObjectsV2不能查询子目录 #1418<commit_after>package s3api

import (
	"context"
	"encoding/xml"
	"fmt"
	"io"
	"net/http"
	"net/url"
	"path/filepath"
	"strconv"
	"strings"
	"time"

	"github.com/chrislusf/seaweedfs/weed/filer2"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
)

type ListBucketResultV2 struct {
	XMLName               xml.Name        `xml:"http://s3.amazonaws.com/doc/2006-03-01/ ListBucketResult"`
	Name                  string          `xml:"Name"`
	Prefix                string          `xml:"Prefix"`
	MaxKeys               int             `xml:"MaxKeys"`
	Delimiter             string          `xml:"Delimiter,omitempty"`
	IsTruncated           bool            `xml:"IsTruncated"`
	Contents              []ListEntry     `xml:"Contents,omitempty"`
	CommonPrefixes        []PrefixEntry   `xml:"CommonPrefixes,omitempty"`
	ContinuationToken     string          `xml:"ContinuationToken,omitempty"`
	NextContinuationToken string          `xml:"NextContinuationToken,omitempty"`
	KeyCount              int             `xml:"KeyCount"`
	StartAfter            string          `xml:"StartAfter,omitempty"`
}

func (s3a *S3ApiServer) ListObjectsV2Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/v2-RESTBucketGET.html

	// collect parameters
	bucket, _ := getBucketAndObject(r)

	originalPrefix, continuationToken, startAfter, delimiter, _, maxKeys := getListObjectsV2Args(r.URL.Query())

	if maxKeys < 0 {
		writeErrorResponse(w, ErrInvalidMaxKeys, r.URL)
		return
	}
	if delimiter != "" && delimiter != "/" {
		writeErrorResponse(w, ErrNotImplemented, r.URL)
		return
	}

	marker := continuationToken
	if continuationToken == "" {
		marker = startAfter
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		writeErrorResponse(w, ErrInternalError, r.URL)
		return
	}
	responseV2 := &ListBucketResultV2{
		XMLName:               response.XMLName,
		Name:                  response.Name,
		CommonPrefixes:        response.CommonPrefixes,
		Contents:              response.Contents,
		ContinuationToken:     continuationToken,
		Delimiter:             response.Delimiter,
		IsTruncated:           response.IsTruncated,
		KeyCount:              len(response.Contents),
		MaxKeys:               response.MaxKeys,
		NextContinuationToken: response.NextMarker,
		Prefix:                response.Prefix,
		StartAfter:            startAfter,
	}

	writeSuccessResponseXML(w, encodeResponse(responseV2))
}

func (s3a *S3ApiServer) ListObjectsV1Handler(w http.ResponseWriter, r *http.Request) {

	// https://docs.aws.amazon.com/AmazonS3/latest/API/RESTBucketGET.html

	// collect parameters
	bucket, _ := getBucketAndObject(r)

	originalPrefix, marker, delimiter, maxKeys := getListObjectsV1Args(r.URL.Query())

	if maxKeys < 0 {
		writeErrorResponse(w, ErrInvalidMaxKeys, r.URL)
		return
	}
	if delimiter != "" && delimiter != "/" {
		writeErrorResponse(w, ErrNotImplemented, r.URL)
		return
	}

	response, err := s3a.listFilerEntries(bucket, originalPrefix, maxKeys, marker, delimiter)

	if err != nil {
		writeErrorResponse(w, ErrInternalError, r.URL)
		return
	}

	writeSuccessResponseXML(w, encodeResponse(response))
}

func (s3a *S3ApiServer) listFilerEntries(bucket string, originalPrefix string, maxKeys int, marker string, delimiter string) (response ListBucketResult, err error) {
	// convert full path prefix into directory name and prefix for entry name
	reqDir, prefix := filepath.Split(originalPrefix)
	if strings.HasPrefix(reqDir, "/") {
		reqDir = reqDir[1:]
	}
	if strings.HasSuffix(reqDir, "/") {
		// remove trailing "/"
		reqDir = reqDir[:len(reqDir)-1]
	}
	bucketPrefix := fmt.Sprintf("%s/%s/", s3a.option.BucketsPath, bucket)
	reqDir = fmt.Sprintf("%s%s", bucketPrefix, reqDir)

	var contents []ListEntry
	var commonPrefixes []PrefixEntry
	var isTruncated bool
	var doErr error
	var nextMarker string

	// check filer
	err = s3a.WithFilerClient(func(client filer_pb.SeaweedFilerClient) error {

		_, isTruncated, nextMarker, doErr = s3a.doListFilerEntries(client, reqDir, prefix, maxKeys, marker, delimiter, func(dir string, entry *filer_pb.Entry) {
			if entry.IsDirectory {
				if delimiter == "/" {
					prefix = fmt.Sprintf("%s%s/", dir, entry.Name)
					commonPrefixes = append(commonPrefixes, PrefixEntry{
						Prefix: prefix[len(bucketPrefix):],
					})
				}
			} else {
				contents = append(contents, ListEntry{
					Key:          fmt.Sprintf("%s%s", dir[len(bucketPrefix):], entry.Name),
					LastModified: time.Unix(entry.Attributes.Mtime, 0).UTC(),
					ETag:         "\"" + filer2.ETag(entry) + "\"",
					Size:         int64(filer2.TotalSize(entry.Chunks)),
					Owner: CanonicalUser{
						ID:          fmt.Sprintf("%x", entry.Attributes.Uid),
						DisplayName: entry.Attributes.UserName,
					},
					StorageClass: "STANDARD",
				})
			}
		})
		if doErr != nil {
			return doErr
		}

		if !isTruncated {
			nextMarker = ""
		}

		response = ListBucketResult{
			Name:           bucket,
			Prefix:         originalPrefix,
			Marker:         marker,
			NextMarker:     nextMarker,
			MaxKeys:        maxKeys,
			Delimiter:      delimiter,
			IsTruncated:    isTruncated,
			Contents:       contents,
			CommonPrefixes: commonPrefixes,
		}

		return nil
	})

	return
}

func (s3a *S3ApiServer) doListFilerEntries(client filer_pb.SeaweedFilerClient, dir, prefix string, maxKeys int, marker, delimiter string, eachEntryFn func(dir string, entry *filer_pb.Entry)) (counter int, isTruncated bool, nextMarker string, err error) {
	// invariants
	//   prefix and marker should be under dir, marker may contain "/"
	//   maxKeys should be updated for each recursion

	if prefix == "/" && delimiter == "/" {
		return
	}
	if maxKeys <= 0 {
		return
	}

	if strings.Contains(marker, "/") {
		sepIndex := strings.Index(marker, "/")
		subDir, subMarker := marker[0:sepIndex], marker[sepIndex+1:]
		// println("doListFilerEntries dir", dir+"/"+subDir, "subMarker", subMarker, "maxKeys", maxKeys)
		subCounter, _, subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+subDir, "", maxKeys, subMarker, delimiter, eachEntryFn)
		if subErr != nil {
			err = subErr
			return
		}
		maxKeys -= subCounter
		nextMarker = subDir + "/" + subNextMarker
		counter += subCounter
		// finished processing this sub directory
		marker = subDir
	}

	// now marker is also a direct child of dir
	request := &filer_pb.ListEntriesRequest{
		Directory:          dir,
		Prefix:             prefix,
		Limit:              uint32(maxKeys + 1),
		StartFromFileName:  marker,
		InclusiveStartFrom: false,
	}

	stream, listErr := client.ListEntries(context.Background(), request)
	if listErr != nil {
		err = fmt.Errorf("list entires %+v: %v", request, listErr)
		return
	}

	for {
		resp, recvErr := stream.Recv()
		if recvErr != nil {
			if recvErr == io.EOF {
				break
			} else {
				err = fmt.Errorf("iterating entires %+v: %v", request, recvErr)
				return
			}
		}
		if counter >= maxKeys {
			isTruncated = true
			return
		}
		entry := resp.Entry
		nextMarker = entry.Name
		if entry.IsDirectory {
			println("ListEntries", dir, "dir:", entry.Name)
			if entry.Name != ".uploads" { // FIXME no need to apply to all directories. this extra also affects maxKeys
				eachEntryFn(dir, entry)
				if delimiter != "/" {
					// println("doListFilerEntries2 dir", dir+"/"+entry.Name, "maxKeys", maxKeys-counter)
					subCounter, subIsTruncated, subNextMarker, subErr := s3a.doListFilerEntries(client, dir+"/"+entry.Name, "", maxKeys-counter, "", delimiter, eachEntryFn)
					if subErr != nil {
						err = fmt.Errorf("doListFilerEntries2: %v", subErr)
						return
					}
					// println("doListFilerEntries2 dir", dir+"/"+entry.Name, "maxKeys", maxKeys-counter, "subCounter", subCounter, "subNextMarker", subNextMarker, "subIsTruncated", subIsTruncated)
					counter += subCounter
					nextMarker = entry.Name + "/" + subNextMarker
					if subIsTruncated {
						isTruncated = true
						return
					}
				} else {
					counter++
				}
			}
		} else {
			// println("ListEntries", dir, "file:", entry.Name)
			eachEntryFn(dir, entry)
			counter++
		}
	}
	return
}

func getListObjectsV2Args(values url.Values) (prefix, token, startAfter, delimiter string, fetchOwner bool, maxkeys int) {
	prefix = values.Get("prefix")
	token = values.Get("continuation-token")
	startAfter = values.Get("start-after")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	fetchOwner = values.Get("fetch-owner") == "true"
	return
}

func getListObjectsV1Args(values url.Values) (prefix, marker, delimiter string, maxkeys int) {
	prefix = values.Get("prefix")
	marker = values.Get("marker")
	delimiter = values.Get("delimiter")
	if values.Get("max-keys") != "" {
		maxkeys, _ = strconv.Atoi(values.Get("max-keys"))
	} else {
		maxkeys = maxObjectListSizeLimit
	}
	return
}
<|endoftext|>


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

package storageprovisioner

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/utils/set"

	"github.com/juju/juju/apiserver/params"
)

// volumeBlockDevicesChanged is called when the block devices of the scoped
// machine have been seen to have changed. This triggers a refresh of all
// block devices for attached volumes backing pending filesystems.
func volumeBlockDevicesChanged(ctx *context) error {
	if len(ctx.pendingFilesystems) == 0 {
		return nil
	}
	volumeTags := make([]names.VolumeTag, 0, len(ctx.pendingFilesystems))
	for _, params := range ctx.pendingFilesystems {
		if params.Volume == (names.VolumeTag{}) {
			continue
		}
		if _, ok := ctx.volumeBlockDevices[params.Volume]; ok {
			continue
		}
		volumeTags = append(volumeTags, params.Volume)
	}
	if len(volumeTags) == 0 {
		return nil
	}
	return updateVolumeBlockDevices(ctx, volumeTags)
}

// processPendingVolumeBlockDevices is called before waiting for any events,
// to force a block-device query for any volumes for which we have not
// previously observed block devices.
func processPendingVolumeBlockDevices(ctx *context) error {
	if len(ctx.pendingVolumeBlockDevices) == 0 {
		return nil
	}
	volumeTags := make([]names.VolumeTag, len(ctx.pendingVolumeBlockDevices))
	for i, tag := range ctx.pendingVolumeBlockDevices.SortedValues() {
		volumeTags[i] = tag.(names.VolumeTag)
	}
	// Clear out the pending set, so we don't force-refresh again.
	ctx.pendingVolumeBlockDevices = set.NewTags()
	return updateVolumeBlockDevices(ctx, volumeTags)
}

// updateVolumeBlockDevices refreshes the block devices for the specified
// volumes.
func updateVolumeBlockDevices(ctx *context, volumeTags []names.VolumeTag) error {
	machineTag, ok := ctx.scope.(names.MachineTag)
	if !ok {
		// This function should only be called by machine-scoped
		// storage provisioners.
		panic(errors.New("expected machine tag"))
	}
	ids := make([]params.MachineStorageId, len(volumeTags))
	for i, volumeTag := range volumeTags {
		ids[i] = params.MachineStorageId{
			MachineTag:    machineTag.String(),
			AttachmentTag: volumeTag.String(),
		}
	}
	results, err := ctx.volumeAccessor.VolumeBlockDevices(ids)
	if err != nil {
		return errors.Annotate(err, "updating volume block devices")
	}
	for i, result := range results {
		if result.Error == nil {
			ctx.volumeBlockDevices[volumeTags[i]] = result.Result
			continue
		}
		if params.IsCodeNotProvisioned(result.Error) || params.IsCodeNotFound(result.Error) {
			// Either the volume (attachment) isn't provisioned,
			// or the corresponding block device is not yet known.
			//
			// Neither of these errors is fatal; we just wait for
			// the block device watcher to notify us again.
		} else if result.Error != nil {
			return errors.Annotatef(
				err, "getting block device info for volume attachment %v",
				ids[i],
			)
		}
	}
	return nil
}
<commit_msg>worker/storageprovisioner: rejigger if-else-if<commit_after>// Copyright 2015 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package storageprovisioner

import (
	"github.com/juju/errors"
	"github.com/juju/names"
	"github.com/juju/utils/set"

	"github.com/juju/juju/apiserver/params"
)

// volumeBlockDevicesChanged is called when the block devices of the scoped
// machine have been seen to have changed. This triggers a refresh of all
// block devices for attached volumes backing pending filesystems.
func volumeBlockDevicesChanged(ctx *context) error {
	if len(ctx.pendingFilesystems) == 0 {
		return nil
	}
	volumeTags := make([]names.VolumeTag, 0, len(ctx.pendingFilesystems))
	for _, params := range ctx.pendingFilesystems {
		if params.Volume == (names.VolumeTag{}) {
			continue
		}
		if _, ok := ctx.volumeBlockDevices[params.Volume]; ok {
			continue
		}
		volumeTags = append(volumeTags, params.Volume)
	}
	if len(volumeTags) == 0 {
		return nil
	}
	return updateVolumeBlockDevices(ctx, volumeTags)
}

// processPendingVolumeBlockDevices is called before waiting for any events,
// to force a block-device query for any volumes for which we have not
// previously observed block devices.
func processPendingVolumeBlockDevices(ctx *context) error {
	if len(ctx.pendingVolumeBlockDevices) == 0 {
		return nil
	}
	volumeTags := make([]names.VolumeTag, len(ctx.pendingVolumeBlockDevices))
	for i, tag := range ctx.pendingVolumeBlockDevices.SortedValues() {
		volumeTags[i] = tag.(names.VolumeTag)
	}
	// Clear out the pending set, so we don't force-refresh again.
	ctx.pendingVolumeBlockDevices = set.NewTags()
	return updateVolumeBlockDevices(ctx, volumeTags)
}

// updateVolumeBlockDevices refreshes the block devices for the specified
// volumes.
func updateVolumeBlockDevices(ctx *context, volumeTags []names.VolumeTag) error {
	machineTag, ok := ctx.scope.(names.MachineTag)
	if !ok {
		// This function should only be called by machine-scoped
		// storage provisioners.
		panic(errors.New("expected machine tag"))
	}
	ids := make([]params.MachineStorageId, len(volumeTags))
	for i, volumeTag := range volumeTags {
		ids[i] = params.MachineStorageId{
			MachineTag:    machineTag.String(),
			AttachmentTag: volumeTag.String(),
		}
	}
	results, err := ctx.volumeAccessor.VolumeBlockDevices(ids)
	if err != nil {
		return errors.Annotate(err, "updating volume block devices")
	}
	for i, result := range results {
		if result.Error == nil {
			ctx.volumeBlockDevices[volumeTags[i]] = result.Result
		} else if params.IsCodeNotProvisioned(result.Error) || params.IsCodeNotFound(result.Error) {
			// Either the volume (attachment) isn't provisioned,
			// or the corresponding block device is not yet known.
			//
			// Neither of these errors is fatal; we just wait for
			// the block device watcher to notify us again.
		} else {
			return errors.Annotatef(
				err, "getting block device info for volume attachment %v",
				ids[i],
			)
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package execdrivers

import (
	"fmt"
	"github.com/dotcloud/docker/daemon/execdriver"
	"github.com/dotcloud/docker/daemon/execdriver/lxc"
	"github.com/dotcloud/docker/daemon/execdriver/native"
	"github.com/dotcloud/docker/pkg/sysinfo"
	"path"
)

func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
	switch name {
	case "lxc":
		// we want to five the lxc driver the full docker root because it needs
		// to access and write config and template files in /var/lib/docker/containers/*
		// to be backwards compatible
		return lxc.NewDriver(root, sysInfo.AppArmor)
	case "native":
		return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
	}
	return nil, fmt.Errorf("unknown exec driver %s", name)
}
<commit_msg>Typo in execdrivers.go five => give<commit_after>package execdrivers

import (
	"fmt"
	"github.com/dotcloud/docker/daemon/execdriver"
	"github.com/dotcloud/docker/daemon/execdriver/lxc"
	"github.com/dotcloud/docker/daemon/execdriver/native"
	"github.com/dotcloud/docker/pkg/sysinfo"
	"path"
)

func NewDriver(name, root, initPath string, sysInfo *sysinfo.SysInfo) (execdriver.Driver, error) {
	switch name {
	case "lxc":
		// we want to give the lxc driver the full docker root because it needs
		// to access and write config and template files in /var/lib/docker/containers/*
		// to be backwards compatible
		return lxc.NewDriver(root, sysInfo.AppArmor)
	case "native":
		return native.NewDriver(path.Join(root, "execdriver", "native"), initPath)
	}
	return nil, fmt.Errorf("unknown exec driver %s", name)
}
<|endoftext|>


<commit_before>package rabbithole

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// Feature flags are a mechanism that controls what features are considered to be enabled or available on all cluster nodes.
// If a FeatureFlag is enabled so is its associated feature (or behavior).
// If not then all nodes in the cluster will disable the feature (behavior).
type FeatureFlag struct {
	Name string `json:"name"`
	// Desc is the description of the feature flag.
	Desc string `json:"desc,omitempty"`
	// DocURL is the URL to a webpage to learn more about the feature flag.
	DocURL    string    `json:"doc_url,omitempty"`
	State     State     `json:"state,omitempty"`
	Stability Stability `json:"stability,omitempty"`
	// ProvidedBy is the RabbitMQ component or plugin which provides the feature flag.
	ProvidedBy string `json:"provided_by,omitempty"`
}

type State string

const (
	StateEnabled  State = "enabled"
	StateDisabled State = "disabled"
	// StateUnsupported means that one or more nodes in the cluster do not know this feature flag (and therefore it cannot be enabled).
	StateUnsupported State = "unsupported"
)

type Stability string

const (
	StabilityStable       Stability = "stable"
	StabilityExperimental Stability = "experimental"
)

//
// GET /api/feature-flags
//

// ListFeatureFlags lists all feature flags.
func (c *Client) ListFeatureFlags() (rec []FeatureFlag, err error) {
	req, err := newGETRequest(c, "feature-flags")
	if err != nil {
		return nil, err
	}

	if err = executeAndParseRequest(c, req, &rec); err != nil {
		return nil, err
	}

	return rec, nil
}

//
// PUT /api/feature-flags/{name}/enable
//

// EnableFeatureFlag enables a feature flag.
func (c *Client) EnableFeatureFlag(featureFlagName string) (res *http.Response, err error) {
	body, err := json.Marshal(FeatureFlag{Name: featureFlagName})
	if err != nil {
		return nil, err
	}

	path := fmt.Sprintf("feature-flags/%s/enable", url.PathEscape(featureFlagName))
	req, err := newRequestWithBody(c, "PUT", path, body)
	if err != nil {
		return nil, err
	}

	if res, err = executeRequest(c, req); err != nil {
		return nil, err
	}

	return res, nil
}
<commit_msg>Squash a few lint warnings<commit_after>package rabbithole

import (
	"encoding/json"
	"fmt"
	"net/http"
	"net/url"
)

// FeatureFlag represents a feature flag.
// Feature flags are a mechanism that controls what features are considered to be enabled or available on all cluster nodes.
// If a FeatureFlag is enabled so is its associated feature (or behavior).
// If not then all nodes in the cluster will disable the feature (behavior).
type FeatureFlag struct {
	Name string `json:"name"`
	// Desc is the description of the feature flag.
	Desc string `json:"desc,omitempty"`
	// DocURL is the URL to a webpage to learn more about the feature flag.
	DocURL    string    `json:"doc_url,omitempty"`
	State     State     `json:"state,omitempty"`
	Stability Stability `json:"stability,omitempty"`
	// ProvidedBy is the RabbitMQ component or plugin which provides the feature flag.
	ProvidedBy string `json:"provided_by,omitempty"`
}

// State is an enumeration for supported feature flag states
type State string

const (
	// StateEnabled means that the flag is enabled
	StateEnabled State = "enabled"
	// StateDisabled means that the flag is disabled
	StateDisabled State = "disabled"
	// StateUnsupported means that one or more nodes in the cluster do not support this feature flag
	// (and therefore it cannot be enabled)
	StateUnsupported State = "unsupported"
)

// Stability status of a feature flag
type Stability string

const (
	// StabilityStable means a feature flag enables a fully supported feature
	StabilityStable Stability = "stable"
	// StabilityExperimental means a feature flag enables an experimental feature
	StabilityExperimental Stability = "experimental"
)

//
// GET /api/feature-flags
//

// ListFeatureFlags lists all feature flags.
func (c *Client) ListFeatureFlags() (rec []FeatureFlag, err error) {
	req, err := newGETRequest(c, "feature-flags")
	if err != nil {
		return nil, err
	}

	if err = executeAndParseRequest(c, req, &rec); err != nil {
		return nil, err
	}

	return rec, nil
}

//
// PUT /api/feature-flags/{name}/enable
//

// EnableFeatureFlag enables a feature flag.
func (c *Client) EnableFeatureFlag(featureFlagName string) (res *http.Response, err error) {
	body, err := json.Marshal(FeatureFlag{Name: featureFlagName})
	if err != nil {
		return nil, err
	}

	path := fmt.Sprintf("feature-flags/%s/enable", url.PathEscape(featureFlagName))
	req, err := newRequestWithBody(c, "PUT", path, body)
	if err != nil {
		return nil, err
	}

	if res, err = executeRequest(c, req); err != nil {
		return nil, err
	}

	return res, nil
}
<|endoftext|>