<commit_before>package control

import (
	"log"
	"strconv"
	"time"

	"../cost"
	"../orders/"
	. "../utilities/"
)

const debugElevatorControl = true

func MessageLoop(
	newOrder chan bool,
	buttonChannel chan ElevatorButton,
	lightChannel chan ElevatorLight,
	motorChannel chan int,
	floorChannel chan int,
	broadcastOrderChannel chan OrderMessage,
	receiveOrderChannel chan OrderMessage,
	broadcastBackupChannel chan BackupMessage,
	receiveBackupChannel chan BackupMessage,
	OnlineElevators map[string]bool,
	ElevatorStatus map[string]*Elevator,
	HallHallOrderMatrix [NumFloors][2]HallOrder,
	localIP string) {

	floorReached := make(chan int)
	go eventManager(newOrder, broadcastOrderChannel, floorReached, lightChannel, motorChannel, localIP)

	for {
		select {
		case button := <-buttonChannel: // Hardware
			printElevatorControl("New button push from " + localIP + " of type '" + ButtonType[button.Kind] + "' at floor " + strconv.Itoa(button.Floor+1))
			switch button.Kind {
			case ButtonCallUp, ButtonCallDown:
				orderAssignedTo, err := cost.AssignOrderToElevator(button.Floor, button.Kind, OnlineElevators, ElevatorStatus, HallHallOrderMatrix)
				//printElevatorControl("Local assign order to " + orderAssignedTo)
				CheckError("[elevatorControl] Failed to assign Order to Elevator ", err)
				order := OrderMessage{
					Time:       time.Now(),
					Floor:      button.Floor,
					ButtonType: button.Kind,
					AssignedTo: orderAssignedTo,
					OriginIP:   localIP,
					SenderIP:   localIP,
					Event:      EventNewOrder,
				}
				broadcastOrderChannel <- order

			case ButtonCommand:
				orders.AddCabOrder(button, localIP)
				newOrder <- true

				/*
					broadcastBackupChannel <- BackupMessage{
						AskerIP: localIP,
						Event:   EventElevatorBackup,
						State: Elevator{
							LocalIP: localIP,
							//LastFloor: <-floorChannel ,
							//	Direction: ,
							//	IsMoving: ,
							//	DoorStatus: ,
							// CabOrders[button.Floor]: true, // why does this not work
						},
						Cab: CabOrder{
							LocalIP:  localIP,
							OriginIP: localIP,
							Floor:    button.Floor,
							//ConfirmedBy: ,
							Timer: time.Now(),
						},
					}
				*/
			case ButtonStop:
				motorChannel <- MotorStop
				lightChannel <- ElevatorLight{Kind: ButtonStop, Active: true}
				printElevatorControl("Stop button pressed. Elevator " + localIP + " will come to a halt.")
				time.Sleep(1 * time.Second)
				lightChannel <- ElevatorLight{Kind: ButtonStop, Active: false}
				//os.Exit(1)
			}

			//newOrder <- true
		case floor := <-floorChannel: // Hardware
			//floorHandler(floor)
			floorReached <- floor
			printElevatorControl("Elevator " + localIP + " reaced floor " + strconv.Itoa(floor+1))
			// Add cases for tickers
		}
	}
}

func printElevatorControl(s string) {
	if debugElevatorControl {
		log.Println("[elevatorControl] \t", s)
	}
}
<commit_msg>Add setPanelLights<commit_after>package control

import (
	"log"
	"strconv"
	"time"

	"../cost"
	"../orders/"
	. "../utilities/"
)

const debugElevatorControl = true

func MessageLoop(
	newOrder chan bool,
	buttonChannel chan ElevatorButton,
	lightChannel chan ElevatorLight,
	motorChannel chan int,
	floorChannel chan int,
	broadcastOrderChannel chan OrderMessage,
	receiveOrderChannel chan OrderMessage,
	broadcastBackupChannel chan BackupMessage,
	receiveBackupChannel chan BackupMessage,
	OnlineElevators map[string]bool,
	ElevatorStatus map[string]*Elevator,
	HallOrderMatrix [NumFloors][2]HallOrder,
	localIP string) {

	floorReached := make(chan int)
	go eventManager(newOrder, broadcastOrderChannel, floorReached, lightChannel, motorChannel, localIP)
	go setPanelLights(lightChannel, localIP)

	for {
		select {
		case button := <-buttonChannel: // Hardware
			printElevatorControl("New button push from " + localIP + " of type '" + ButtonType[button.Kind] + "' at floor " + strconv.Itoa(button.Floor+1))
			switch button.Kind {
			case ButtonCallUp, ButtonCallDown:
				orderAssignedTo, err := cost.AssignOrderToElevator(button.Floor, button.Kind, OnlineElevators, ElevatorStatus, HallOrderMatrix)
				//printElevatorControl("Local assign order to " + orderAssignedTo)
				CheckError("[elevatorControl] Failed to assign Order to Elevator ", err)
				order := OrderMessage{
					Time:       time.Now(),
					Floor:      button.Floor,
					ButtonType: button.Kind,
					AssignedTo: orderAssignedTo,
					OriginIP:   localIP,
					SenderIP:   localIP,
					Event:      EventNewOrder,
				}
				broadcastOrderChannel <- order

			case ButtonCommand:
				orders.AddCabOrder(button, localIP)
				newOrder <- true

				/*
					broadcastBackupChannel <- BackupMessage{
						AskerIP: localIP,
						Event:   EventElevatorBackup,
						State: Elevator{
							LocalIP: localIP,
							//LastFloor: <-floorChannel ,
							//	Direction: ,
							//	IsMoving: ,
							//	DoorStatus: ,
							// CabOrders[button.Floor]: true, // why does this not work
						},
						Cab: CabOrder{
							LocalIP:  localIP,
							OriginIP: localIP,
							Floor:    button.Floor,
							//ConfirmedBy: ,
							Timer: time.Now(),
						},
					}
				*/
			case ButtonStop:
				motorChannel <- MotorStop
				lightChannel <- ElevatorLight{Kind: ButtonStop, Active: true}
				printElevatorControl("Stop button pressed. Elevator " + localIP + " will come to a halt.")
				time.Sleep(1 * time.Second)
				lightChannel <- ElevatorLight{Kind: ButtonStop, Active: false}
				//os.Exit(1)
			}

			//newOrder <- true
		case floor := <-floorChannel: // Hardware
			//floorHandler(floor)
			floorReached <- floor
			printElevatorControl("Elevator " + localIP + " reaced floor " + strconv.Itoa(floor+1))
			// Add cases for tickers
		}
	}
}

func setPanelLights(lightChannel chan ElevatorLight, localIP string) {
	for f := 0; f < NumFloors; f++ {
		if ElevatorStatus[localIP].CabOrders[f] == true {
			lightChannel <- ElevatorLight{Kind: ButtonCommand, Active: true}
		} else {
			lightChannel <- ElevatorLight{Kind: ButtonCommand, Active: false}
		}
		for k := ButtonCallUp; k <= ButtonCallDown; k++ {
			if HallOrderMatrix[f][k].Status == Awaiting || HallOrderMatrix[f][k].Status == UnderExecution {
				lightChannel <- ElevatorLight{Kind: k, Active: true}
			} else {
				lightChannel <- ElevatorLight{Kind: k, Active: false}
			}
		}
	}
}

func printElevatorControl(s string) {
	if debugElevatorControl {
		log.Println("[elevatorControl] \t", s)
	}
}
<|endoftext|>


<commit_before>package todos

import (
	"net/http"
	"strconv"

	"github.com/13pinj/todoapp/controllers"
	"github.com/13pinj/todoapp/models/todo"
	"github.com/13pinj/todoapp/models/todolist"
	"github.com/13pinj/todoapp/models/user"
	"github.com/gin-gonic/gin"
)

// Index выводит страницу со всеми списками дел текущего пользователя.
// Если пользователь незалогинен, перенаправляет на главную.
// GET /
func Index(c *gin.Context) {

}

// CreateList создает новый список дел с заголовком из POST-параметра title
// и перенаправляет на страницу этого списка.
// POST /list-create
func CreateList(c *gin.Context) {
	u, ok := user.FromContext(c)
	if !ok {
		ctl.Render403(c)
		return
	}
	l := todolist.New(c.PostForm("title"))
	l.UserID = u.ID
	err := l.Save()
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// ShowList выводит страницу списка дел, на которой отображается его заголовок
// и содержание.
// GET /list/:id
func ShowList(c *gin.Context) {

}

// UpdateList изменяет заголовок списка на тот, который был получен
// POST-параметре title и перенаправляет на страницу этого списка.
// POST /list/:id/update
func UpdateList(c *gin.Context) {
	l, ok := getlist(c)
	if !ok {
		return
	}
	l.Title = c.PostForm("title")
	err := l.Save()
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// DestroyList стирает список из базы и перенаправляет на главную.
// POST /list/:id/destroy
func DestroyList(c *gin.Context) {
	l, ok := getlist(c)
	if !ok {
		return
	}
	l.Destroy()
	ctl.Redirect(c, "/")
}

// CreateTask создает новое задание в списке с текстом
// из POST-параметра label и перенаправляет на страницу списка.
// POST /list/:id/add
func CreateTask(c *gin.Context) {
	l, ok := getlist(c)
	if !ok {
		return
	}
	err := l.Add(c.PostForm("label"))
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// UpdateTask изменяет поля задания используя POST-параметры done и label.
// Поля, для который не заданы значения в параметрах запроса, должны остаться
// неизменными.
// После выполнения запроса UpdateTask перенаправляет клиент на страницу списка.
// POST /task/:id/update
func UpdateTask(c *gin.Context) {
	td, l, ok := gettask(c)
	if !ok {
		return
	}
	label := c.PostForm("label")
	if label != "" {
		td.Label = label
	}
	done := c.PostForm("done")
	if done != "" {
		td.Done = (done != "0")
	}
	err := td.Save()
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// DestroyTask стирает задание из списка и перенаправляет на страницу списка.
// POST /task/:id/destroy
func DestroyTask(c *gin.Context) {
	td, l, ok := gettask(c)
	if !ok {
		return
	}
	td.Destroy()
	ctl.Redirect(c, l.Path())
}

func getlist(c *gin.Context) (*todolist.TodoList, bool) {
	u, ok := user.FromContext(c)
	if !ok {
		ctl.Render403(c)
		return nil, false
	}
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		ctl.Render404(c)
		return nil, false
	}
	l, ok := todolist.Find(uint(id))
	if !ok {
		ctl.Render404(c)
		return nil, false
	}
	if l.UserID != u.ID {
		ctl.Render403(c)
		return nil, false
	}
	return l, true
}

func gettask(c *gin.Context) (*todo.Todo, *todolist.TodoList, bool) {
	u, ok := user.FromContext(c)
	if !ok {
		ctl.Render403(c)
		return nil, nil, false
	}
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		ctl.Render404(c)
		return nil, nil, false
	}
	td, ok := todo.Find(uint(id))
	if !ok {
		ctl.Render404(c)
		return nil, nil, false
	}
	l, ok := todolist.Find(td.TodoListID)
	if !ok {
		ctl.Render500(c)
		return nil, nil, false
	}
	if u.ID != l.UserID {
		ctl.Render403(c)
		return nil, nil, false
	}
	return td, l, true
}
<commit_msg>Make use of gin.Context.GetPostForm<commit_after>package todos

import (
	"net/http"
	"strconv"

	"github.com/13pinj/todoapp/controllers"
	"github.com/13pinj/todoapp/models/todo"
	"github.com/13pinj/todoapp/models/todolist"
	"github.com/13pinj/todoapp/models/user"
	"github.com/gin-gonic/gin"
)

// Index выводит страницу со всеми списками дел текущего пользователя.
// Если пользователь незалогинен, перенаправляет на главную.
// GET /
func Index(c *gin.Context) {

}

// CreateList создает новый список дел с заголовком из POST-параметра title
// и перенаправляет на страницу этого списка.
// POST /list-create
func CreateList(c *gin.Context) {
	u, ok := user.FromContext(c)
	if !ok {
		ctl.Render403(c)
		return
	}
	l := todolist.New(c.PostForm("title"))
	l.UserID = u.ID
	err := l.Save()
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// ShowList выводит страницу списка дел, на которой отображается его заголовок
// и содержание.
// GET /list/:id
func ShowList(c *gin.Context) {

}

// UpdateList изменяет заголовок списка на тот, который был получен
// POST-параметре title и перенаправляет на страницу этого списка.
// POST /list/:id/update
func UpdateList(c *gin.Context) {
	l, ok := getlist(c)
	if !ok {
		return
	}
	l.Title = c.PostForm("title")
	err := l.Save()
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// DestroyList стирает список из базы и перенаправляет на главную.
// POST /list/:id/destroy
func DestroyList(c *gin.Context) {
	l, ok := getlist(c)
	if !ok {
		return
	}
	l.Destroy()
	ctl.Redirect(c, "/")
}

// CreateTask создает новое задание в списке с текстом
// из POST-параметра label и перенаправляет на страницу списка.
// POST /list/:id/add
func CreateTask(c *gin.Context) {
	l, ok := getlist(c)
	if !ok {
		return
	}
	err := l.Add(c.PostForm("label"))
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// UpdateTask изменяет поля задания используя POST-параметры done и label.
// Поля, для который не заданы значения в параметрах запроса, должны остаться
// неизменными.
// После выполнения запроса UpdateTask перенаправляет клиент на страницу списка.
// POST /task/:id/update
func UpdateTask(c *gin.Context) {
	td, l, ok := gettask(c)
	if !ok {
		return
	}
	label, ok := c.GetPostForm("label")
	if ok {
		td.Label = label
	}
	done, ok := c.GetPostForm("done")
	if ok {
		td.Done = (done != "0")
	}
	err := td.Save()
	if err != nil {
		c.String(http.StatusOK, err.Error())
		return
	}
	ctl.Redirect(c, l.Path())
}

// DestroyTask стирает задание из списка и перенаправляет на страницу списка.
// POST /task/:id/destroy
func DestroyTask(c *gin.Context) {
	td, l, ok := gettask(c)
	if !ok {
		return
	}
	td.Destroy()
	ctl.Redirect(c, l.Path())
}

func getlist(c *gin.Context) (*todolist.TodoList, bool) {
	u, ok := user.FromContext(c)
	if !ok {
		ctl.Render403(c)
		return nil, false
	}
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		ctl.Render404(c)
		return nil, false
	}
	l, ok := todolist.Find(uint(id))
	if !ok {
		ctl.Render404(c)
		return nil, false
	}
	if l.UserID != u.ID {
		ctl.Render403(c)
		return nil, false
	}
	return l, true
}

func gettask(c *gin.Context) (*todo.Todo, *todolist.TodoList, bool) {
	u, ok := user.FromContext(c)
	if !ok {
		ctl.Render403(c)
		return nil, nil, false
	}
	id, err := strconv.Atoi(c.Param("id"))
	if err != nil {
		ctl.Render404(c)
		return nil, nil, false
	}
	td, ok := todo.Find(uint(id))
	if !ok {
		ctl.Render404(c)
		return nil, nil, false
	}
	l, ok := todolist.Find(td.TodoListID)
	if !ok {
		ctl.Render500(c)
		return nil, nil, false
	}
	if u.ID != l.UserID {
		ctl.Render403(c)
		return nil, nil, false
	}
	return td, l, true
}
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 Santiago Arias | Remy Jourde
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package users

import (
	"errors"
	"net/http"

	"appengine"

	"github.com/santiaago/gonawin/extract"
	"github.com/santiaago/gonawin/helpers"
	templateshlp "github.com/santiaago/gonawin/helpers/templates"
	mdl "github.com/santiaago/gonawin/models"
)

// Score handler, returns the score data of the requested user.
//
func Score(w http.ResponseWriter, r *http.Request, u *mdl.User) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)
	extract := extract.NewContext(c, "User Score Handler:", r)

	var user *mdl.User
	var err error
	if user, err = extract.User(); err != nil {
		return err
	}

	scores := user.TournamentsScores(c)

	vm := buildScoreUserViewModel(scores)

	return templateshlp.RenderJson(w, c, vm)
}

type scoreUserViewModel struct {
	Scores []*mdl.ScoreOverall
}

func buildScoreUserViewModel(scores []*mdl.ScoreOverall) scoreUserViewModel {
	return scoreUserViewModel{scores}
}
<commit_msg>Revert "refactor score ctrl"<commit_after>/*
 * Copyright (c) 2014 Santiago Arias | Remy Jourde
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */

package users

import (
	"errors"
	"net/http"

	"appengine"

	"github.com/santiaago/gonawin/extract"
	"github.com/santiaago/gonawin/helpers"
	templateshlp "github.com/santiaago/gonawin/helpers/templates"
	mdl "github.com/santiaago/gonawin/models"
)

// User score handler, returns the JSON data of the requested user.
func Score(w http.ResponseWriter, r *http.Request, u *mdl.User) error {
	if r.Method != "GET" {
		return &helpers.BadRequest{Err: errors.New(helpers.ErrorCodeNotSupported)}
	}

	c := appengine.NewContext(r)
	extract := extract.NewContext(c, "User Score Handler:", r)

	user, err := extract.User()
	if err != nil {
		return err
	}

	scores := user.TournamentsScores(c)

	data := struct {
		Scores []*mdl.ScoreOverall
	}{
		scores,
	}

	return templateshlp.RenderJson(w, c, data)
}
<|endoftext|>


<commit_before>package servers

import (
	val "github.com/asaskevich/govalidator"
	"github.com/ncodes/patchain"
	"github.com/ncodes/patchain/cockroach/tables"
	"github.com/ncodes/patchain/object"
	"github.com/ncodes/safehold/servers/common"
	"github.com/ncodes/safehold/servers/proto_rpc"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/net/context"
)

// validateIdentity validates a requested identity
func validateIdentity(req *proto_rpc.CreateIdentityMsg) []common.Error {
	var errs []common.Error
	if val.IsNull(req.Email) {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Email is required", Field: "email"})
	}
	if !val.IsEmail(req.Email) {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Email is not valid", Field: "email"})
	}
	if val.IsNull(req.Password) {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Password is required", Field: "password"})
	}
	if len(req.Password) < 6 {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Password is too short. Must be at least 6 characters long", Field: "password"})
	}
	return errs
}

// GetSystemIdentity fetches the system identity
func (s *RPC) GetSystemIdentity() (*tables.Object, error) {
	return s.object.GetLast(&tables.Object{
		Key: object.MakeIdentityKey(systemEmail),
	})
}

// CreateIdentity creates a new identity for object
func (s *RPC) CreateIdentity(ctx context.Context, req *proto_rpc.CreateIdentityMsg) (*proto_rpc.ObjectResponse, error) {

	// validate request
	if errs := validateIdentity(req); len(errs) > 0 {
		return nil, common.NewMultiAPIErr(400, "validation errors", errs)
	}

	systemIdentity, err := s.GetSystemIdentity()
	if err != nil {
		logRPC.Errorf("%+v", err)
		if err == patchain.ErrNotFound {
			return nil, common.NewSingleAPIErr(400, "", "", "system identity not found", nil)
		}
		return nil, common.ServerError
	}

	objHandler := object.NewObject(s.db)

	// ensure identity does not already exists
	_, err = objHandler.GetLast(&tables.Object{OwnerID: systemIdentity.ID, QueryParams: patchain.KeyStartsWith(object.MakeIdentityKey(req.Email))})
	if err != nil {
		if err != patchain.ErrNotFound {
			return nil, common.ServerError
		}
	}

	if err == nil {
		return nil, common.NewSingleAPIErr(400, "used_email", "email", "Email is not available", nil)
	}

	var numPartitions = int64(5)
	var newIdentity *tables.Object

	// create the identity and partitions
	var do = func() error {
		return s.db.Transact(func(txDB patchain.DB, commit patchain.CommitFunc, rollback patchain.RollbackFunc) error {

			var err error
			dbOpt := &patchain.UseDBOption{DB: txDB}
			passwordHash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)

			if req.Developer {
				newIdentity = object.MakeDeveloperIdentityObject(systemIdentity.ID, systemIdentity.ID, req.Email, string(passwordHash), true)
			} else {
				newIdentity = object.MakeIdentityObject(systemIdentity.ID, systemIdentity.ID, req.Email, string(passwordHash), true)
			}

			err = s.object.Put(newIdentity, dbOpt)
			if err != nil {
				return err
			}

			_, err = s.object.CreatePartitions(numPartitions, newIdentity.ID, systemIdentity.ID, dbOpt)
			if err != nil {
				return err
			}

			return nil
		})
	}

	err = objHandler.Retry(func(stopRetry func()) error { return do() })
	if err != nil {
		logRPC.Errorf("%+v", err)
		return nil, common.ServerError
	}

	resp, _ := NewObjectResponse("identity", newIdentity, nil)
	return resp, nil
}
<commit_msg>return response error for direct use<commit_after>package servers

import (
	val "github.com/asaskevich/govalidator"
	"github.com/ncodes/patchain"
	"github.com/ncodes/patchain/cockroach/tables"
	"github.com/ncodes/patchain/object"
	"github.com/ncodes/safehold/servers/common"
	"github.com/ncodes/safehold/servers/proto_rpc"
	"golang.org/x/crypto/bcrypt"
	"golang.org/x/net/context"
)

// validateIdentity validates a requested identity
func validateIdentity(req *proto_rpc.CreateIdentityMsg) []common.Error {
	var errs []common.Error
	if val.IsNull(req.Email) {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Email is required", Field: "email"})
	}
	if !val.IsEmail(req.Email) {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Email is not valid", Field: "email"})
	}
	if val.IsNull(req.Password) {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Password is required", Field: "password"})
	}
	if len(req.Password) < 6 {
		errs = append(errs, common.Error{Code: common.CodeInvalidParam, Message: "Password is too short. Must be at least 6 characters long", Field: "password"})
	}
	return errs
}

// getSystemIdentity returns system identity or a response error
func (s *RPC) getSystemIdentity() (*tables.Object, error) {
	obj, err := s.object.GetLast(&tables.Object{Key: object.MakeIdentityKey(systemEmail)})
	if err != nil {
		if err == patchain.ErrNotFound {
			return nil, common.NewSingleAPIErr(400, "", "", "system identity not found", nil)
		}
		return nil, common.ServerError
	}
	return obj, nil
}

// CreateIdentity creates a new identity for object
func (s *RPC) CreateIdentity(ctx context.Context, req *proto_rpc.CreateIdentityMsg) (*proto_rpc.ObjectResponse, error) {

	// validate request
	if errs := validateIdentity(req); len(errs) > 0 {
		return nil, common.NewMultiAPIErr(400, "validation errors", errs)
	}

	systemIdentity, err := s.getSystemIdentity()
	if err != nil {
		logRPC.Errorf("%+v", err)
		return nil, err
	}

	objHandler := object.NewObject(s.db)

	// ensure identity does not already exists
	_, err = objHandler.GetLast(&tables.Object{OwnerID: systemIdentity.ID, QueryParams: patchain.KeyStartsWith(object.MakeIdentityKey(req.Email))})
	if err != nil {
		if err != patchain.ErrNotFound {
			return nil, common.ServerError
		}
	}

	if err == nil {
		return nil, common.NewSingleAPIErr(400, "used_email", "email", "Email is not available", nil)
	}

	var numPartitions = int64(5)
	var newIdentity *tables.Object

	// create the identity and partitions
	var do = func() error {
		return s.db.Transact(func(txDB patchain.DB, commit patchain.CommitFunc, rollback patchain.RollbackFunc) error {

			var err error
			dbOpt := &patchain.UseDBOption{DB: txDB}
			passwordHash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)

			if req.Developer {
				newIdentity = object.MakeDeveloperIdentityObject(systemIdentity.ID, systemIdentity.ID, req.Email, string(passwordHash), true)
			} else {
				newIdentity = object.MakeIdentityObject(systemIdentity.ID, systemIdentity.ID, req.Email, string(passwordHash), true)
			}

			err = s.object.Put(newIdentity, dbOpt)
			if err != nil {
				return err
			}

			_, err = s.object.CreatePartitions(numPartitions, newIdentity.ID, systemIdentity.ID, dbOpt)
			if err != nil {
				return err
			}

			return nil
		})
	}

	err = objHandler.Retry(func(stopRetry func()) error { return do() })
	if err != nil {
		logRPC.Errorf("%+v", err)
		return nil, common.ServerError
	}

	resp, _ := NewObjectResponse("identity", newIdentity, nil)
	return resp, nil
}
<|endoftext|>


<commit_before>
package main

import (
	"errors"
	"fmt"
	//"strconv"
	//"time"
	//"strings"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"encoding/json"
)

type RecruitmentChainCode struct {
}

type Demand struct{
	RRDNo string `json:"RRDNo"`
	Role string `json:"Role"`
	RoleDescription string `json:"RoleDescription"`
	YearsOfExperience string `json:"YearsOfExperience"`
	Location string `json:"Location"`
	Priority string `json:"Priority"`
	Status string `json:"Status"`
}

type Certification struct{
	Name []string `json:"Name"`
}

type JobDescription struct{
	Description []string `json:"Description"`
}

type MustHave struct{
	Skill []string `json:"Skill"`
}

type GoodToHave struct{
	Skill []string `json:"Skill"`
}

type SalaryRange struct{
	Min string `json:"Min"`
	Max string `json:"Max"`
	Currency string `json:"Currency"`

}

type Status struct{
	RRDNo string `json:"RRDNo"`
	Status string `json:"Status"`
}

func (self *RecruitmentChainCode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("In Init start ")
	fmt.Println("Nothing to be initialized ")
	return nil, nil
}

func (self *RecruitmentChainCode) Invoke(stub shim.ChaincodeStubInterface,
	function string, args []string) ([]byte, error) {
	fmt.Println("In Invoke with function  " + function)

	if function == "AddDemand" {
		fmt.Println("invoking AddDemand " + function)
		testBytes,err := AddDemand(args[0],stub)
		if err != nil {
			fmt.Println("Error performing AddDemand ")
			return nil, err
		}
		fmt.Println("Processed AddDemand successfully. ")
		return testBytes, nil
	}

	fmt.Println("invoke did not find func: " + function)
	return nil, errors.New("Received unknown function invocation: " + function)
}

func (self *RecruitmentChainCode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error){
	fmt.Println("In Query with function " + function)
	//bytes, err:= query.Query(stub, function,args)
	//if err != nil {
		//fmt.Println("Error retrieving function  ")
		//return nil, err
	//}

	bytes, err:= QueryDetails(stub, function,args)
	if err != nil {
		fmt.Println("Error retrieving function  ")
		return nil, err
	}
	return bytes,nil

}

func QueryDetails(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	if function == "GetDemand" {
		fmt.Println("Invoking GetDemand " + function)
		var demand Demand
		demand,err := GetDemand(args[0], stub)
		if err != nil {
			fmt.Println("Error retrieving  the Demand")
			return nil, errors.New("Error retrieving  Demand")
		}
		fmt.Println("All success, returning demand")
		return json.Marshal(demand)
	}
	return nil, errors.New("Received unknown query function name")

}

func GetDemand(RRDNo string, stub shim.ChaincodeStubInterface)(Demand, error) {
	fmt.Println("In query.GetDemand start ")

	key := RRDNo
	var demand Demand
	demandBytes, err := stub.GetState(key)
	if err != nil {
		fmt.Println("Error retrieving demand" , RRDNo)
		return demand, errors.New("Error retrieving demand" + RRDNo)
	}
	err = json.Unmarshal(demandBytes, &demand)
	fmt.Println("Demand   : " , demand);
	fmt.Println("In query.GetDemand end ")
	return demand, nil
}

func GetStatus(RRDNo string, stub shim.ChaincodeStubInterface)(Status, error) {
	fmt.Println("In query.GetStatus start ")
	key := RRDNo
	var status Status
	demand,err := GetDemand(key, stub)
	if err != nil {
		fmt.Println("Error retrieving Status" , RRDNo)
		return demand, errors.New("Error retrieving Status" + RRDNo)
	}
	status.RRDNo = key
	status.Status = demand.Status
	fmt.Println("Demand Status   : " , status);
	fmt.Println("In query.GetStatus end ")
	return status, nil
}

func AddDemand(demandJSON string, stub shim.ChaincodeStubInterface) ([]byte, error) {
	fmt.Println("In services.AddUser start ")
	//smartMeterId :=args[0]
	//userType 	:=args[1]

	//var user User
	res := &Demand{}
	//user := &User{}
	err := json.Unmarshal([]byte(demandJSON), res)
	if err != nil {
		fmt.Println("Failed to unmarshal Demand ")
	}
	fmt.Println("RRD Number : ",res.RRDNo)

	body, err := json.Marshal(res)
	if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
	err = stub.PutState(res.RRDNo, []byte(string(body)))
	if err != nil {
		fmt.Println("Failed to create Demand ")
	}
	err = stub.PutState(res.RRDNo + "_" + res.Status, []byte(res.Status))
	if err != nil {
		fmt.Println("Failed to set RRD Status ")
	}
	fmt.Println("Created Demand  with Key : "+ res.RRDNo)
	fmt.Println("In initialize.AddDemand end ")
	return nil,nil
}

func StatusUpdate(demandStatusJSON string, stub shim.ChaincodeStubInterface) ([]byte, error) {
	fmt.Println("In services.StatusUpdate start ")

	res := &Status{}
	err := json.Unmarshal([]byte(demandStatusJSON), res)
	if err != nil {
		fmt.Println("Failed to unmarshal Status ")
	}
	fmt.Println("Status of Demand   : ",res.Status)

	demandDet,err := GetDemand(res.RRDNo, stub)
	demandDet.Status = res.Status

	demandStatusUpdate, err := json.Marshal(demandDet)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(demandStatusUpdate))
	err = stub.PutState(res.RRDNo, []byte(string(demandStatusUpdate)))
	if err != nil {
		fmt.Println("Failed to update Demand Status ")
	}
	fmt.Println("Updated Demand Status  with Key : "+ res.RRDNo)
	fmt.Println("In initialize.StatusUpdate end ")
	return nil,nil
}

func main() {
	err := shim.Start(new(RecruitmentChainCode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}

}
<commit_msg>updated<commit_after>
package main

import (
	"errors"
	"fmt"
	//"strconv"
	//"time"
	//"strings"
	"github.com/hyperledger/fabric/core/chaincode/shim"
	"encoding/json"
)

type RecruitmentChainCode struct {
}

type Demand struct{
	RRDNo string `json:"RRDNo"`
	Role string `json:"Role"`
	RoleDescription string `json:"RoleDescription"`
	YearsOfExperience string `json:"YearsOfExperience"`
	Location string `json:"Location"`
	Priority string `json:"Priority"`
	Status string `json:"Status"`
}

type Certification struct{
	Name []string `json:"Name"`
}

type JobDescription struct{
	Description []string `json:"Description"`
}

type MustHave struct{
	Skill []string `json:"Skill"`
}

type GoodToHave struct{
	Skill []string `json:"Skill"`
}

type SalaryRange struct{
	Min string `json:"Min"`
	Max string `json:"Max"`
	Currency string `json:"Currency"`

}

type Status struct{
	RRDNo string `json:"RRDNo"`
	Status string `json:"Status"`
}

func (self *RecruitmentChainCode) Init(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {
	fmt.Println("In Init start ")
	fmt.Println("Nothing to be initialized ")
	return nil, nil
}

func (self *RecruitmentChainCode) Invoke(stub shim.ChaincodeStubInterface,
	function string, args []string) ([]byte, error) {
	fmt.Println("In Invoke with function  " + function)

	if function == "AddDemand" {
		fmt.Println("invoking AddDemand " + function)
		testBytes,err := AddDemand(args[0],stub)
		if err != nil {
			fmt.Println("Error performing AddDemand ")
			return nil, err
		}
		fmt.Println("Processed AddDemand successfully. ")
		return testBytes, nil
	}

	fmt.Println("invoke did not find func: " + function)
	return nil, errors.New("Received unknown function invocation: " + function)
}

func (self *RecruitmentChainCode) Query(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error){
	fmt.Println("In Query with function " + function)
	//bytes, err:= query.Query(stub, function,args)
	//if err != nil {
		//fmt.Println("Error retrieving function  ")
		//return nil, err
	//}

	bytes, err:= QueryDetails(stub, function,args)
	if err != nil {
		fmt.Println("Error retrieving function  ")
		return nil, err
	}
	return bytes,nil

}

func QueryDetails(stub shim.ChaincodeStubInterface, function string, args []string) ([]byte, error) {

	if function == "GetDemand" {
		fmt.Println("Invoking GetDemand " + function)
		var demand Demand
		demand,err := GetDemand(args[0], stub)
		if err != nil {
			fmt.Println("Error retrieving  the Demand")
			return nil, errors.New("Error retrieving  Demand")
		}
		fmt.Println("All success, returning demand")
		return json.Marshal(demand)
	}
	return nil, errors.New("Received unknown query function name")

}

func GetDemand(RRDNo string, stub shim.ChaincodeStubInterface)(Demand, error) {
	fmt.Println("In query.GetDemand start ")

	key := RRDNo
	var demand Demand
	demandBytes, err := stub.GetState(key)
	if err != nil {
		fmt.Println("Error retrieving demand" , RRDNo)
		return demand, errors.New("Error retrieving demand" + RRDNo)
	}
	err = json.Unmarshal(demandBytes, &demand)
	fmt.Println("Demand   : " , demand);
	fmt.Println("In query.GetDemand end ")
	return demand, nil
}

func GetStatus(RRDNo string, stub shim.ChaincodeStubInterface)(Status, error) {
	fmt.Println("In query.GetStatus start ")
	key := RRDNo
	var status Status
	demand,err := GetDemand(key, stub)
	if err != nil {
		fmt.Println("Error retrieving Status" , RRDNo)
		return status, errors.New("Error retrieving Status" + RRDNo)
	}
	status.RRDNo = key
	status.Status = demand.Status
	fmt.Println("Demand Status   : " , status);
	fmt.Println("In query.GetStatus end ")
	return status, nil
}

func AddDemand(demandJSON string, stub shim.ChaincodeStubInterface) ([]byte, error) {
	fmt.Println("In services.AddUser start ")
	//smartMeterId :=args[0]
	//userType 	:=args[1]

	//var user User
	res := &Demand{}
	//user := &User{}
	err := json.Unmarshal([]byte(demandJSON), res)
	if err != nil {
		fmt.Println("Failed to unmarshal Demand ")
	}
	fmt.Println("RRD Number : ",res.RRDNo)

	body, err := json.Marshal(res)
	if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
	err = stub.PutState(res.RRDNo, []byte(string(body)))
	if err != nil {
		fmt.Println("Failed to create Demand ")
	}
	err = stub.PutState(res.RRDNo + "_" + res.Status, []byte(res.Status))
	if err != nil {
		fmt.Println("Failed to set RRD Status ")
	}
	fmt.Println("Created Demand  with Key : "+ res.RRDNo)
	fmt.Println("In initialize.AddDemand end ")
	return nil,nil
}

func StatusUpdate(demandStatusJSON string, stub shim.ChaincodeStubInterface) ([]byte, error) {
	fmt.Println("In services.StatusUpdate start ")

	res := &Status{}
	err := json.Unmarshal([]byte(demandStatusJSON), res)
	if err != nil {
		fmt.Println("Failed to unmarshal Status ")
	}
	fmt.Println("Status of Demand   : ",res.Status)

	demandDet,err := GetDemand(res.RRDNo, stub)
	demandDet.Status = res.Status

	demandStatusUpdate, err := json.Marshal(demandDet)
	if err != nil {
		panic(err)
	}
	fmt.Println(string(demandStatusUpdate))
	err = stub.PutState(res.RRDNo, []byte(string(demandStatusUpdate)))
	if err != nil {
		fmt.Println("Failed to update Demand Status ")
	}
	fmt.Println("Updated Demand Status  with Key : "+ res.RRDNo)
	fmt.Println("In initialize.StatusUpdate end ")
	return nil,nil
}

func main() {
	err := shim.Start(new(RecruitmentChainCode))
	if err != nil {
		fmt.Printf("Error starting Simple chaincode: %s", err)
	}

}
<|endoftext|>


<commit_before>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package service

import (
	"encoding/json"
	"fmt"
	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/log"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

type Client struct {
	endpoint string
	username string
	password string
}

func (c *Client) buildErrorMessage(err error, resp *http.Response) string {
	if err != nil {
		return err.Error()
	}
	if resp != nil {
		b, _ := ioutil.ReadAll(resp.Body)
		return string(b)
	}
	return ""
}

func (c *Client) issueRequest(path, method string, params map[string][]string) (*http.Response, error) {
	log.Debug("Issuing request...")
	v := url.Values(params)
	var suffix string
	var body io.Reader
	if method == "DELETE" || method == "GET" {
		suffix = "?" + v.Encode()
	} else {
		body = strings.NewReader(v.Encode())
	}
	url := strings.TrimRight(c.endpoint, "/") + "/" + strings.Trim(path, "/") + suffix
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		log.Errorf("Got error while creating request: %s", err)
		return nil, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")
	req.SetBasicAuth(c.username, c.password)
	return http.DefaultClient.Do(req)
}

func (c *Client) jsonFromResponse(resp *http.Response, v interface{}) error {
	log.Debug("Parsing response json...")
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Got error while parsing json: %s", err)
		return err
	}
	return json.Unmarshal(body, &v)
}

func (c *Client) Create(instance *ServiceInstance, user string) error {
	var err error
	log.Debug("Attempting to call creation of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	var resp *http.Response
	params := map[string][]string{
		"name": {instance.Name},
		"user": {user},
		"team": {instance.TeamOwner},
	}
	if instance.PlanName != "" {
		params["plan"] = []string{instance.PlanName}
	}
	if resp, err = c.issueRequest("/resources", "POST", params); err == nil && resp.StatusCode < 300 {
		return nil
	}
	msg := "Failed to create the instance " + instance.Name + ": " + c.buildErrorMessage(err, resp)
	log.Error(msg)
	return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
}

func (c *Client) Destroy(instance *ServiceInstance) error {
	log.Debug("Attempting to call destroy of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	resp, err := c.issueRequest("/resources/"+instance.Name, "DELETE", nil)
	if err == nil && resp.StatusCode > 299 {
		msg := "Failed to destroy the instance " + instance.Name + ": " + c.buildErrorMessage(err, resp)
		log.Error(msg)
		return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
	}
	return err
}

func (c *Client) Bind(instance *ServiceInstance, app bind.App, unit bind.Unit) (map[string]string, error) {
	log.Debugf("Calling bind of instance %q and unit %q at %q API",
		instance.Name, unit.GetIp(), instance.ServiceName)
	var resp *http.Response
	params := map[string][]string{
		"unit-host": {unit.GetIp()},
		"app-host":  {app.GetIp()},
	}
	resp, err := c.issueRequest("/resources/"+instance.Name, "POST", params)
	if err != nil {
		if m, _ := regexp.MatchString("", err.Error()); m {
			return nil, fmt.Errorf("%s api is down.", instance.Name)
		}
		return nil, err
	}
	if err == nil && resp.StatusCode < 300 {
		var result map[string]string
		err = c.jsonFromResponse(resp, &result)
		if err != nil {
			return nil, err
		}
		return result, nil
	}
	if resp.StatusCode == http.StatusPreconditionFailed {
		return nil, &errors.HTTP{Code: resp.StatusCode, Message: "You cannot bind any app to this service instance because it is not ready yet."}
	}
	msg := "Failed to bind instance " + instance.Name + " to the unit " + unit.GetIp() + ": " + c.buildErrorMessage(err, resp)
	log.Error(msg)
	return nil, &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
}

func (c *Client) Unbind(instance *ServiceInstance, unit bind.Unit) error {
	log.Debug("Attempting to call unbind of service instance " + instance.Name + " and unit " + unit.GetIp() + " at " + instance.ServiceName + " api")
	var resp *http.Response
	url := "/resources/" + instance.Name + "/hostname/" + unit.GetIp()
	resp, err := c.issueRequest(url, "DELETE", nil)
	if err == nil && resp.StatusCode > 299 {
		msg := fmt.Sprintf("Failed to unbind (%q): %s", url, c.buildErrorMessage(err, resp))
		log.Error(msg)
		return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
	}
	return err
}

// Connects into service's api
// The api should be prepared to receive the request,
// like below:
// GET /resources/<name>/status/
// The service host here is the private ip of the service instance
// 204 means the service is up, 500 means the service is down
func (c *Client) Status(instance *ServiceInstance) (string, error) {
	log.Debug("Attempting to call status of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	var (
		resp *http.Response
		err  error
	)
	url := "/resources/" + instance.Name + "/status"
	if resp, err = c.issueRequest(url, "GET", nil); err == nil {
		switch resp.StatusCode {
		case 202:
			return "pending", nil
		case 204:
			return "up", nil
		case 500:
			return "down", nil
		}
	}
	msg := "Failed to get status of instance " + instance.Name + ": " + c.buildErrorMessage(err, resp)
	log.Error(msg)
	err = &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
	return "", err
}

// Info returns the additional info about a service instance.
// The api should be prepared to receive the request,
// like below:
// GET /resources/<name>
func (c *Client) Info(instance *ServiceInstance) ([]map[string]string, error) {
	log.Debug("Attempting to call info of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	url := "/resources/" + instance.Name
	resp, err := c.issueRequest(url, "GET", nil)
	if err != nil || resp.StatusCode != 200 {
		return nil, err
	}
	result := []map[string]string{}
	err = c.jsonFromResponse(resp, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// Plans returns the service plans.
// The api should be prepared to receive the request,
// like below:
// GET /resources/plans
func (c *Client) Plans() ([]Plan, error) {
	url := "/resources/plans"
	resp, err := c.issueRequest(url, "GET", nil)
	if err != nil || resp.StatusCode != 200 {
		return nil, err
	}
	result := []Plan{}
	err = c.jsonFromResponse(resp, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// Proxy is a proxy between tsuru and the service.
// This method allow customized service methods.
func (c *Client) Proxy(method, path string, body io.ReadCloser) (*http.Response, error) {
	url := strings.TrimRight(c.endpoint, "/") + "/" + strings.Trim(path, "/")
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		log.Errorf("Got error while creating request: %s", err)
		return nil, err
	}
	req.SetBasicAuth(c.username, c.password)
	return http.DefaultClient.Do(req)
}
<commit_msg>service/endpoint: add request.Close flag to issueRequest.<commit_after>// Copyright 2014 tsuru authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package service

import (
	"encoding/json"
	"fmt"
	"github.com/tsuru/tsuru/app/bind"
	"github.com/tsuru/tsuru/errors"
	"github.com/tsuru/tsuru/log"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"regexp"
	"strings"
)

type Client struct {
	endpoint string
	username string
	password string
}

func (c *Client) buildErrorMessage(err error, resp *http.Response) string {
	if err != nil {
		return err.Error()
	}
	if resp != nil {
		b, _ := ioutil.ReadAll(resp.Body)
		return string(b)
	}
	return ""
}

func (c *Client) issueRequest(path, method string, params map[string][]string) (*http.Response, error) {
	log.Debug("Issuing request...")
	v := url.Values(params)
	var suffix string
	var body io.Reader
	if method == "DELETE" || method == "GET" {
		suffix = "?" + v.Encode()
	} else {
		body = strings.NewReader(v.Encode())
	}
	url := strings.TrimRight(c.endpoint, "/") + "/" + strings.Trim(path, "/") + suffix
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		log.Errorf("Got error while creating request: %s", err)
		return nil, err
	}
	req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
	req.Header.Add("Accept", "application/json")
	req.SetBasicAuth(c.username, c.password)
	req.Close = true
	return http.DefaultClient.Do(req)
}

func (c *Client) jsonFromResponse(resp *http.Response, v interface{}) error {
	log.Debug("Parsing response json...")
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		log.Errorf("Got error while parsing json: %s", err)
		return err
	}
	return json.Unmarshal(body, &v)
}

func (c *Client) Create(instance *ServiceInstance, user string) error {
	var err error
	log.Debug("Attempting to call creation of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	var resp *http.Response
	params := map[string][]string{
		"name": {instance.Name},
		"user": {user},
		"team": {instance.TeamOwner},
	}
	if instance.PlanName != "" {
		params["plan"] = []string{instance.PlanName}
	}
	if resp, err = c.issueRequest("/resources", "POST", params); err == nil && resp.StatusCode < 300 {
		return nil
	}
	msg := "Failed to create the instance " + instance.Name + ": " + c.buildErrorMessage(err, resp)
	log.Error(msg)
	return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
}

func (c *Client) Destroy(instance *ServiceInstance) error {
	log.Debug("Attempting to call destroy of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	resp, err := c.issueRequest("/resources/"+instance.Name, "DELETE", nil)
	if err == nil && resp.StatusCode > 299 {
		msg := "Failed to destroy the instance " + instance.Name + ": " + c.buildErrorMessage(err, resp)
		log.Error(msg)
		return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
	}
	return err
}

func (c *Client) Bind(instance *ServiceInstance, app bind.App, unit bind.Unit) (map[string]string, error) {
	log.Debugf("Calling bind of instance %q and unit %q at %q API",
		instance.Name, unit.GetIp(), instance.ServiceName)
	var resp *http.Response
	params := map[string][]string{
		"unit-host": {unit.GetIp()},
		"app-host":  {app.GetIp()},
	}
	resp, err := c.issueRequest("/resources/"+instance.Name, "POST", params)
	if err != nil {
		if m, _ := regexp.MatchString("", err.Error()); m {
			return nil, fmt.Errorf("%s api is down.", instance.Name)
		}
		return nil, err
	}
	if err == nil && resp.StatusCode < 300 {
		var result map[string]string
		err = c.jsonFromResponse(resp, &result)
		if err != nil {
			return nil, err
		}
		return result, nil
	}
	if resp.StatusCode == http.StatusPreconditionFailed {
		return nil, &errors.HTTP{Code: resp.StatusCode, Message: "You cannot bind any app to this service instance because it is not ready yet."}
	}
	msg := "Failed to bind instance " + instance.Name + " to the unit " + unit.GetIp() + ": " + c.buildErrorMessage(err, resp)
	log.Error(msg)
	return nil, &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
}

func (c *Client) Unbind(instance *ServiceInstance, unit bind.Unit) error {
	log.Debug("Attempting to call unbind of service instance " + instance.Name + " and unit " + unit.GetIp() + " at " + instance.ServiceName + " api")
	var resp *http.Response
	url := "/resources/" + instance.Name + "/hostname/" + unit.GetIp()
	resp, err := c.issueRequest(url, "DELETE", nil)
	if err == nil && resp.StatusCode > 299 {
		msg := fmt.Sprintf("Failed to unbind (%q): %s", url, c.buildErrorMessage(err, resp))
		log.Error(msg)
		return &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
	}
	return err
}

// Connects into service's api
// The api should be prepared to receive the request,
// like below:
// GET /resources/<name>/status/
// The service host here is the private ip of the service instance
// 204 means the service is up, 500 means the service is down
func (c *Client) Status(instance *ServiceInstance) (string, error) {
	log.Debug("Attempting to call status of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	var (
		resp *http.Response
		err  error
	)
	url := "/resources/" + instance.Name + "/status"
	if resp, err = c.issueRequest(url, "GET", nil); err == nil {
		switch resp.StatusCode {
		case 202:
			return "pending", nil
		case 204:
			return "up", nil
		case 500:
			return "down", nil
		}
	}
	msg := "Failed to get status of instance " + instance.Name + ": " + c.buildErrorMessage(err, resp)
	log.Error(msg)
	err = &errors.HTTP{Code: http.StatusInternalServerError, Message: msg}
	return "", err
}

// Info returns the additional info about a service instance.
// The api should be prepared to receive the request,
// like below:
// GET /resources/<name>
func (c *Client) Info(instance *ServiceInstance) ([]map[string]string, error) {
	log.Debug("Attempting to call info of service instance " + instance.Name + " at " + instance.ServiceName + " api")
	url := "/resources/" + instance.Name
	resp, err := c.issueRequest(url, "GET", nil)
	if err != nil || resp.StatusCode != 200 {
		return nil, err
	}
	result := []map[string]string{}
	err = c.jsonFromResponse(resp, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// Plans returns the service plans.
// The api should be prepared to receive the request,
// like below:
// GET /resources/plans
func (c *Client) Plans() ([]Plan, error) {
	url := "/resources/plans"
	resp, err := c.issueRequest(url, "GET", nil)
	if err != nil || resp.StatusCode != 200 {
		return nil, err
	}
	result := []Plan{}
	err = c.jsonFromResponse(resp, &result)
	if err != nil {
		return nil, err
	}
	return result, nil
}

// Proxy is a proxy between tsuru and the service.
// This method allow customized service methods.
func (c *Client) Proxy(method, path string, body io.ReadCloser) (*http.Response, error) {
	url := strings.TrimRight(c.endpoint, "/") + "/" + strings.Trim(path, "/")
	req, err := http.NewRequest(method, url, body)
	if err != nil {
		log.Errorf("Got error while creating request: %s", err)
		return nil, err
	}
	req.SetBasicAuth(c.username, c.password)
	return http.DefaultClient.Do(req)
}
<|endoftext|>


<commit_before>package service

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sail/internal"
)

var (
	redeployBody         Redeploy
	redeployPublished    []string
	redeployLink         []string
	redeployNetwork      []string
	redeployNetworkAllow string
	redeployGateway      []string
	redeployVolume       []string
	redeployBatch        bool
)

func redeployCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:   "redeploy",
		Short: "Redeploy a docker service: sail service redeploy <applicationName>/<serviceId>",
		Long:  `Redeploy a docker service: sail service redeploy <applicationName>/<serviceId>`,
		Run:   cmdRedeploy,
	}

	cmd.Flags().StringVarP(&redeployBody.ContainerModel, "model", "", "", "Container model")
	cmd.Flags().IntVarP(&redeployBody.ContainerNumber, "number", "", 0, "Number of container to run")
	cmd.Flags().StringSliceVarP(&redeployBody.ContainerEnvironment, "env", "e", nil, "override docker environment")
	cmd.Flags().StringVarP(&redeployBody.RestartPolicy, "restart", "", "", "{no|always[:<max>]|on-failure[:<max>]}")
	cmd.Flags().StringSliceVarP(&redeployBody.ContainerCommand, "command", "", nil, "override docker run command")
	cmd.Flags().StringVarP(&redeployBody.RepositoryTag, "tag", "", "", "deploy from new image version")
	cmd.Flags().StringVarP(&redeployBody.ContainerWorkdir, "workdir", "", "", "override docker workdir")
	cmd.Flags().StringVarP(&redeployBody.ContainerEntrypoint, "entrypoint", "", "", "override docker entrypoint")
	cmd.Flags().StringVarP(&redeployBody.ContainerUser, "user", "", "", "override docker user")

	cmd.Flags().StringSliceVar(&redeployNetwork, "network", nil, "public|private|<namespace name>")
	cmd.Flags().StringVarP(&redeployNetworkAllow, "network-allow", "", "", "[network:]ip[/mask] Use IPs whitelist")
	cmd.Flags().StringSliceVarP(&redeployPublished, "publish", "", nil, "Publish a container's port to the host")
	cmd.Flags().StringSliceVarP(&redeployGateway, "gateway", "", nil, "network-input:network-output")
	cmd.Flags().StringSliceVarP(&redeployVolume, "volume", "", nil, "/path:size] (Size in GB)")
	cmd.Flags().BoolVarP(&redeployBatch, "batch", "", false, "do not attach console on start")
	cmd.Flags().StringSliceVarP(&redeployLink, "link", "", nil, "name:alias")
	return cmd
}

// Redeploy struct holds all parameters sent to /applications/%s/services/%s/redeploy
type Redeploy struct {
	Service              string                         `json:"-"`
	Volumes              map[string]VolumeConfig        `json:"volumes,omitempty"`
	Repository           string                         `json:"repository,omitempty"`
	ContainerUser        string                         `json:"container_user,omitempty"`
	RestartPolicy        string                         `json:"restart_policy,omitempty"`
	ContainerCommand     []string                       `json:"container_command,omitempty"`
	ContainerNetwork     map[string]map[string][]string `json:"container_network,omitempty"`
	ContainerEntrypoint  string                         `json:"container_user,omitempty"`
	ContainerNumber      int                            `json:"container_number,omitempty"`
	RepositoryTag        string                         `json:"repository_tag,omitempty"`
	Links                map[string]string              `json:"links,omitempty"`
	Application          string                         `json:"namespace,omitempty"`
	ContainerWorkdir     string                         `json:"container_workdir,omitempty"`
	ContainerEnvironment []string                       `json:"container_environment,omitempty"`
	ContainerModel       string                         `json:"container_model,omitempty"`
	ContainerPorts       map[string][]PortConfig        `json:"container_ports,omitempty"`
}

func cmdRedeploy(cmd *cobra.Command, args []string) {
	usage := "Invalid usage. sail service redeploy <applicationName>/<serviceId>. Please see sail service redeploy --help\n"
	if len(args) != 1 {
		fmt.Printf(usage)
		return
	}

	split := strings.Split(args[0], "/")
	if len(split) != 2 {
		fmt.Printf(usage)
		return
	}

	// Get args
	redeployBody.Application = split[0]
	redeployBody.Service = split[1]
	serviceRedeploy(redeployBody)
}

func serviceRedeploy(args Redeploy) {

	// Parse volumes
	if len(redeployVolume) > 0 {
		args.Volumes = make(map[string]VolumeConfig)
	}

	for _, vol := range redeployVolume {
		t := strings.Split(vol, ":")
		if len(t) == 2 {
			args.Volumes[t[0]] = VolumeConfig{Size: t[1]}
		} else if len(t) == 1 {
			args.Volumes[t[0]] = VolumeConfig{Size: "10"}
		} else {
			fmt.Printf("Error: Volume parameter '%s' not formated correctly\n", vol)
			os.Exit(1)
		}
	}

	// Parse links
	for _, link := range redeployLink {
		t := strings.Split(link, ":")
		if len(t) == 1 {
			args.Links[t[0]] = t[0]
		} else {
			args.Links[t[0]] = t[1]
		}
	}

	// Parse ContainerNetworks arguments
	if len(redeployNetwork) > 0 {
		args.ContainerNetwork = make(map[string]map[string][]string)
	}
	for _, network := range redeployNetwork {
		args.ContainerNetwork[network] = make(map[string][]string)
	}

	for _, gat := range redeployGateway {
		t := strings.Split(gat, ":")
		if len(t) != 2 {
			fmt.Printf("Invalid gateway parameter, should be \"input:output\"")
			os.Exit(1)
		}
		if _, ok := args.ContainerNetwork[t[0]]; !ok {
			fmt.Printf("Not configured input network %s\n", t[0])
			os.Exit(1)
		}
		if _, ok := args.ContainerNetwork[t[1]]; !ok {
			fmt.Printf("Not configured onput network %s\n", t[1])
			os.Exit(1)
		}
		args.ContainerNetwork[t[0]]["gateway_to"] = append(args.ContainerNetwork[t[0]]["gateway_to"], t[1])
	}

	// Parse ContainerPorts
	args.ContainerPorts = parsePublishedPort(redeployPublished)

	path := fmt.Sprintf("/applications/%s/services/%s/redeploy", args.Application, args.Service)
	body, err := json.MarshalIndent(args, " ", " ")
	if err != nil {
		fmt.Printf("Fatal: %s\n", err)
		return
	}

	if redeployBatch {
		ret := internal.ReqWant("POST", http.StatusOK, path, body)
		e := internal.DecodeError(ret)
		if e != nil {
			fmt.Printf("%s\n", e)
		} else {
			fmt.Printf("%s\n", ret)
		}
		return
	}

	buffer, _, err := internal.Stream("POST", path+"?stream", body)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		os.Exit(1)
	}

	err = internal.DisplayStream(buffer)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		os.Exit(1)
	}
}
<commit_msg>(fix) --batch on redeploy should print progress<commit_after>package service

import (
	"encoding/json"
	"fmt"
	"net/http"
	"os"
	"strings"

	"github.com/spf13/cobra"

	"stash.ovh.net/sailabove/sail/internal"
)

var (
	redeployBody         Redeploy
	redeployPublished    []string
	redeployLink         []string
	redeployNetwork      []string
	redeployNetworkAllow string
	redeployGateway      []string
	redeployVolume       []string
	redeployBatch        bool
)

func redeployCmd() *cobra.Command {

	cmd := &cobra.Command{
		Use:   "redeploy",
		Short: "Redeploy a docker service: sail service redeploy <applicationName>/<serviceId>",
		Long:  `Redeploy a docker service: sail service redeploy <applicationName>/<serviceId>`,
		Run:   cmdRedeploy,
	}

	cmd.Flags().StringVarP(&redeployBody.ContainerModel, "model", "", "", "Container model")
	cmd.Flags().IntVarP(&redeployBody.ContainerNumber, "number", "", 0, "Number of container to run")
	cmd.Flags().StringSliceVarP(&redeployBody.ContainerEnvironment, "env", "e", nil, "override docker environment")
	cmd.Flags().StringVarP(&redeployBody.RestartPolicy, "restart", "", "", "{no|always[:<max>]|on-failure[:<max>]}")
	cmd.Flags().StringSliceVarP(&redeployBody.ContainerCommand, "command", "", nil, "override docker run command")
	cmd.Flags().StringVarP(&redeployBody.RepositoryTag, "tag", "", "", "deploy from new image version")
	cmd.Flags().StringVarP(&redeployBody.ContainerWorkdir, "workdir", "", "", "override docker workdir")
	cmd.Flags().StringVarP(&redeployBody.ContainerEntrypoint, "entrypoint", "", "", "override docker entrypoint")
	cmd.Flags().StringVarP(&redeployBody.ContainerUser, "user", "", "", "override docker user")

	cmd.Flags().StringSliceVar(&redeployNetwork, "network", nil, "public|private|<namespace name>")
	cmd.Flags().StringVarP(&redeployNetworkAllow, "network-allow", "", "", "[network:]ip[/mask] Use IPs whitelist")
	cmd.Flags().StringSliceVarP(&redeployPublished, "publish", "", nil, "Publish a container's port to the host")
	cmd.Flags().StringSliceVarP(&redeployGateway, "gateway", "", nil, "network-input:network-output")
	cmd.Flags().StringSliceVarP(&redeployVolume, "volume", "", nil, "/path:size] (Size in GB)")
	cmd.Flags().BoolVarP(&redeployBatch, "batch", "", false, "do not attach console on start")
	cmd.Flags().StringSliceVarP(&redeployLink, "link", "", nil, "name:alias")
	return cmd
}

// Redeploy struct holds all parameters sent to /applications/%s/services/%s/redeploy
type Redeploy struct {
	Service              string                         `json:"-"`
	Volumes              map[string]VolumeConfig        `json:"volumes,omitempty"`
	Repository           string                         `json:"repository,omitempty"`
	ContainerUser        string                         `json:"container_user,omitempty"`
	RestartPolicy        string                         `json:"restart_policy,omitempty"`
	ContainerCommand     []string                       `json:"container_command,omitempty"`
	ContainerNetwork     map[string]map[string][]string `json:"container_network,omitempty"`
	ContainerEntrypoint  string                         `json:"container_user,omitempty"`
	ContainerNumber      int                            `json:"container_number,omitempty"`
	RepositoryTag        string                         `json:"repository_tag,omitempty"`
	Links                map[string]string              `json:"links,omitempty"`
	Application          string                         `json:"namespace,omitempty"`
	ContainerWorkdir     string                         `json:"container_workdir,omitempty"`
	ContainerEnvironment []string                       `json:"container_environment,omitempty"`
	ContainerModel       string                         `json:"container_model,omitempty"`
	ContainerPorts       map[string][]PortConfig        `json:"container_ports,omitempty"`
}

func cmdRedeploy(cmd *cobra.Command, args []string) {
	usage := "Invalid usage. sail service redeploy <applicationName>/<serviceId>. Please see sail service redeploy --help\n"
	if len(args) != 1 {
		fmt.Printf(usage)
		return
	}

	split := strings.Split(args[0], "/")
	if len(split) != 2 {
		fmt.Printf(usage)
		return
	}

	// Get args
	redeployBody.Application = split[0]
	redeployBody.Service = split[1]
	serviceRedeploy(redeployBody)
}

func serviceRedeploy(args Redeploy) {

	// Parse volumes
	if len(redeployVolume) > 0 {
		args.Volumes = make(map[string]VolumeConfig)
	}

	for _, vol := range redeployVolume {
		t := strings.Split(vol, ":")
		if len(t) == 2 {
			args.Volumes[t[0]] = VolumeConfig{Size: t[1]}
		} else if len(t) == 1 {
			args.Volumes[t[0]] = VolumeConfig{Size: "10"}
		} else {
			fmt.Printf("Error: Volume parameter '%s' not formated correctly\n", vol)
			os.Exit(1)
		}
	}

	// Parse links
	for _, link := range redeployLink {
		t := strings.Split(link, ":")
		if len(t) == 1 {
			args.Links[t[0]] = t[0]
		} else {
			args.Links[t[0]] = t[1]
		}
	}

	// Parse ContainerNetworks arguments
	if len(redeployNetwork) > 0 {
		args.ContainerNetwork = make(map[string]map[string][]string)
	}
	for _, network := range redeployNetwork {
		args.ContainerNetwork[network] = make(map[string][]string)
	}

	for _, gat := range redeployGateway {
		t := strings.Split(gat, ":")
		if len(t) != 2 {
			fmt.Printf("Invalid gateway parameter, should be \"input:output\"")
			os.Exit(1)
		}
		if _, ok := args.ContainerNetwork[t[0]]; !ok {
			fmt.Printf("Not configured input network %s\n", t[0])
			os.Exit(1)
		}
		if _, ok := args.ContainerNetwork[t[1]]; !ok {
			fmt.Printf("Not configured onput network %s\n", t[1])
			os.Exit(1)
		}
		args.ContainerNetwork[t[0]]["gateway_to"] = append(args.ContainerNetwork[t[0]]["gateway_to"], t[1])
	}

	// Parse ContainerPorts
	args.ContainerPorts = parsePublishedPort(redeployPublished)

	path := fmt.Sprintf("/applications/%s/services/%s/redeploy", args.Application, args.Service)
	body, err := json.MarshalIndent(args, " ", " ")
	if err != nil {
		fmt.Printf("Fatal: %s\n", err)
		return
	}

	buffer, _, err := internal.Stream("POST", path+"?stream", body)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		os.Exit(1)
	}

	err = internal.DisplayStream(buffer)
	if err != nil {
		fmt.Printf("Error: %s\n", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package discover

import (
	"encoding/json"
	"strings"
	"sync"

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

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, err := b.getCurrentState(name)
	if err != nil {
		return nil, err
	}
	go func() {
		for _, u := range response.Kvs {
			if update := b.responseToUpdate(response, &u); update != nil {
				stream.ch <- update
			}
		}
		stream.ch <- &ServiceUpdate{}
		for u := range watch {
			if update := b.responseToUpdate(u, nil); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, kvp *etcd.KeyValuePair) *ServiceUpdate {
	var key, value string
	if kvp != nil {
		key = kvp.Key
		value = kvp.Value
	} else {
		key = resp.Key
		value = resp.Value
	}
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || ("set" == resp.Action && resp.Value != resp.PrevValue) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:   serviceName,
			Addr:   serviceAddr,
			Online: true,
			Attrs:  serviceAttrs,
		}
	} else if "delete" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.GetAll(servicePath(name, ""), false)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.WatchAll(servicePath(name, ""), 0, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJson, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	_, err = b.Client.Set(servicePath(name, addr), string(attrsJson), HeartbeatIntervalSecs+MissedHearbeatTTL)
	return err
}

func (b *EtcdBackend) Heartbeat(name, addr string) error {
	resp, err := b.Client.Get(servicePath(name, addr), false)
	if err != nil {
		return err
	}
	// ignore test failure, it doesn't need a heartbeat if it was just set.
	_, err = b.Client.CompareAndSwap(servicePath(name, addr), resp.Value, HeartbeatIntervalSecs+MissedHearbeatTTL, resp.Value, resp.ModifiedIndex)
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr))
	return err
}
<commit_msg>Adjust to latest go-etcd recursive get/watch/delete API<commit_after>package discover

import (
	"encoding/json"
	"strings"
	"sync"

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

const KeyPrefix = "/discover"

type EtcdBackend struct {
	Client *etcd.Client
}

func servicePath(name, addr string) string {
	if addr == "" {
		return KeyPrefix + "/services/" + name
	}
	return KeyPrefix + "/services/" + name + "/" + addr
}

func (b *EtcdBackend) Subscribe(name string) (UpdateStream, error) {
	stream := &etcdStream{ch: make(chan *ServiceUpdate), stop: make(chan bool)}
	watch := b.getStateChanges(name, stream.stop)
	response, err := b.getCurrentState(name)
	if err != nil {
		return nil, err
	}
	go func() {
		for _, u := range response.Kvs {
			if update := b.responseToUpdate(response, &u); update != nil {
				stream.ch <- update
			}
		}
		stream.ch <- &ServiceUpdate{}
		for u := range watch {
			if update := b.responseToUpdate(u, nil); update != nil {
				stream.ch <- update
			}
		}
	}()
	return stream, nil
}

type etcdStream struct {
	ch       chan *ServiceUpdate
	stop     chan bool
	stopOnce sync.Once
}

func (s *etcdStream) Chan() chan *ServiceUpdate { return s.ch }

func (s *etcdStream) Close() { s.stopOnce.Do(func() { close(s.stop) }) }

func (b *EtcdBackend) responseToUpdate(resp *etcd.Response, kvp *etcd.KeyValuePair) *ServiceUpdate {
	var key, value string
	if kvp != nil {
		key = kvp.Key
		value = kvp.Value
	} else {
		key = resp.Key
		value = resp.Value
	}
	// expected key structure: /PREFIX/services/NAME/ADDR
	splitKey := strings.SplitN(key, "/", 5)
	if len(splitKey) < 5 {
		return nil
	}
	serviceName := splitKey[3]
	serviceAddr := splitKey[4]
	if "get" == resp.Action || ("set" == resp.Action && resp.Value != resp.PrevValue) {
		// GET is because getCurrentState returns responses of Action GET.
		// some SETs are heartbeats, so we ignore SETs where value didn't change.
		var serviceAttrs map[string]string
		err := json.Unmarshal([]byte(value), &serviceAttrs)
		if err != nil {
			return nil
		}
		return &ServiceUpdate{
			Name:   serviceName,
			Addr:   serviceAddr,
			Online: true,
			Attrs:  serviceAttrs,
		}
	} else if "delete" == resp.Action {
		return &ServiceUpdate{
			Name: serviceName,
			Addr: serviceAddr,
		}
	} else {
		return nil
	}
}

func (b *EtcdBackend) getCurrentState(name string) (*etcd.Response, error) {
	return b.Client.Get(servicePath(name, ""), false, true)
}

func (b *EtcdBackend) getStateChanges(name string, stop chan bool) chan *etcd.Response {
	watch := make(chan *etcd.Response)
	go b.Client.Watch(servicePath(name, ""), 0, true, watch, stop)
	return watch
}

func (b *EtcdBackend) Register(name, addr string, attrs map[string]string) error {
	attrsJson, err := json.Marshal(attrs)
	if err != nil {
		return err
	}
	_, err = b.Client.Set(servicePath(name, addr), string(attrsJson), HeartbeatIntervalSecs+MissedHearbeatTTL)
	return err
}

func (b *EtcdBackend) Heartbeat(name, addr string) error {
	resp, err := b.Client.Get(servicePath(name, addr), false, false)
	if err != nil {
		return err
	}
	// ignore test failure, it doesn't need a heartbeat if it was just set.
	_, err = b.Client.CompareAndSwap(servicePath(name, addr), resp.Value, HeartbeatIntervalSecs+MissedHearbeatTTL, resp.Value, resp.ModifiedIndex)
	return err
}

func (b *EtcdBackend) Unregister(name, addr string) error {
	_, err := b.Client.Delete(servicePath(name, addr), false)
	return err
}
<|endoftext|>


<commit_before>// Licensed under the Apache License, Version 2.0 (the "License");
// Copyright © 2017 Sascha Andres <sascha.andres@outlook.com>
// 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 shell

import (
	"fmt"
	"path"

	"github.com/sascha-andres/devenv/helper"
)

type branchCommand struct{}

func (c branchCommand) Execute(i *Interpreter, repository string, args []string) error {
	for _, repo := range i.EnvConfiguration.Repositories {
		if repo.Disabled {
			continue
		}
		fmt.Printf("Push for '%s'\n", repo.Name)
		repoPath := path.Join(i.ExecuteScriptDirectory, repo.Path)
		hasBranch, err := helper.HasBranch(i.EnvConfiguration.Environment, repoPath, args[0])
		if err != nil {
			return err
		}
		var arguments []string
		arguments = append(arguments, "checkout")
		if !hasBranch {
			arguments = append(arguments, "-b")
		}
		arguments = append(arguments, args...)
		vars, err := i.EnvConfiguration.GetReplacedEnvironment()
		if err != nil {
			return err
		}
		if _, err = helper.Git(vars, repoPath, arguments...); err != nil {
			return err
		}
	}
	return nil
}

func (c branchCommand) IsResponsible(commandName string) bool {
	return commandName == "branch" || commandName == "br"
}

func init() {
	commands = append(commands, branchCommand{})
}
<commit_msg>Refactored variable names<commit_after>// Licensed under the Apache License, Version 2.0 (the "License");
// Copyright © 2017 Sascha Andres <sascha.andres@outlook.com>
// 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 shell

import (
	"fmt"
	"path"

	"github.com/sascha-andres/devenv/helper"
)

type branchCommand struct{}

func (c branchCommand) Execute(i *Interpreter, repository string, args []string) error {
	for _, repository := range i.EnvConfiguration.Repositories {
		if repository.Disabled {
			continue
		}
		fmt.Printf("Branch for '%s'\n", repository.Name)
		repositoryPath := path.Join(i.ExecuteScriptDirectory, repository.Path)
		hasBranch, err := helper.HasBranch(i.EnvConfiguration.Environment, repositoryPath, args[0])
		if err != nil {
			return err
		}
		var arguments []string
		arguments = append(arguments, "checkout")
		if !hasBranch {
			arguments = append(arguments, "-b")
		}
		arguments = append(arguments, args...)
		vars, err := i.EnvConfiguration.GetReplacedEnvironment()
		if err != nil {
			return err
		}
		if _, err = helper.Git(vars, repositoryPath, arguments...); err != nil {
			return err
		}
	}
	return nil
}

func (c branchCommand) IsResponsible(commandName string) bool {
	return commandName == "branch" || commandName == "br"
}

func init() {
	commands = append(commands, branchCommand{})
}
<|endoftext|>


<commit_before>package slave

import (
	"bufio"
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"golang.org/x/sys/unix"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

const skeletonDirPermissions = 0700
const KeyfilePermissions = 0600

// Create directories managed by ProcessManager
// returns nil if already exists and permissions are suitable
func (p *ProcessManager) CreateManagedDirs() (err error) {

	if err = unix.Access(p.dataDir, unix.W_OK); err != nil {
		return fmt.Errorf("`%s` does not exist or is not writable", p.dataDir)
	}

	if err = unix.Access(p.dataDir, unix.R_OK|unix.W_OK|unix.X_OK); err != nil {
		if err := os.Mkdir(p.managedDirMongods(), os.ModeDir|0700); err != nil {
			return err
		}
	}

	return nil

}

// directory for mongod process root dirs
func (p *ProcessManager) managedDirMongods() string {
	return filepath.Join(p.dataDir, "mongods")
}

// Create root directory for a process in a subdirectory of
// a directory created by CreateManagedDirs()
// returns nil if already exists and permissions are suitable
func (p *ProcessManager) createDirSkeleton(m msp.Mongod) (err error) {

	if err = os.MkdirAll(p.processRootDir(m), os.ModeDir|skeletonDirPermissions); err != nil {
		goto wrapError
	}

	if err = os.MkdirAll(p.processDBPathDir(m), os.ModeDir|skeletonDirPermissions); err != nil {
		goto wrapError
	}

	if err = os.MkdirAll(p.processConfDir(m), os.ModeDir|skeletonDirPermissions); err != nil {
		goto wrapError
	}

	return nil

wrapError:
	return fmt.Errorf("could not create directory skeleton for Mongod: %s", err)

}

func (p *ProcessManager) destroyDataDirectory(m msp.Mongod) error {
	return os.RemoveAll(p.processRootDir(m))
}

// Generate list of process root directories in process root directory tree
func (p *ProcessManager) parseProcessDirTree() (replSetNameByPortNumber map[msp.PortNumber]string, err error) {

	entries, err := ioutil.ReadDir(p.managedDirMongods())
	if err != nil {
		return
	}

	replSetNameByPortNumber = make(map[msp.PortNumber]string)
	for _, entry := range entries {
		if entry.IsDir() {
			port, replSet, err := p.parseProcessRootDirentry(entry)
			if err != nil {
				log.Errorf("error parsing process root directory entry: %s", err)
			} else {
				replSetNameByPortNumber[port] = replSet
			}
		} else {
			log.Errorf("unexpected directory entry in `%s` (not a directory)", entry.Name())
			continue
		}
	}

	return

}

// Root directory of a process
// process data should not be directly stored there
func (p *ProcessManager) processRootDir(m msp.Mongod) string {
	return filepath.Join(p.managedDirMongods(), fmt.Sprintf("%d:%s", m.Port, m.ReplicaSetConfig.ReplicaSetName))
}

// Given a direntry (no prefix!), extract the data encoded in the direntry
func (p *ProcessManager) parseProcessRootDirentry(entry os.FileInfo) (port msp.PortNumber, replSet string, err error) {
	dirname := entry.Name()
	colonLoc := strings.Index(entry.Name(), ":")
	if colonLoc < 0 {
		err = fmt.Errorf("directory with unparsable name `%s` in db dir", entry.Name())
		return
	}
	portStr := dirname[0:colonLoc]
	port64, parseErr := strconv.ParseUint(portStr, 10, 16)
	if parseErr != nil {
		err = fmt.Errorf("could not parse port number `%s`", portStr)
		return
	}
	port = msp.PortNumber(port64)
	replSet = dirname[colonLoc+1:]
	return
}

func (p *ProcessManager) processDBPathDir(m msp.Mongod) string {
	return filepath.Join(p.processRootDir(m), "dbpath")
}

func (p *ProcessManager) processConfDir(m msp.Mongod) string {
	return filepath.Join(p.processRootDir(m), "conf")
}

func (p *ProcessManager) processKeyfilePath(m msp.Mongod) string {
	return filepath.Join(p.processConfDir(m), "keyfile")
}

// Update or create the keyfile of a Mongod
func (p *ProcessManager) UpdateKeyfile(m msp.Mongod) (err error) {

	var keyFilePath = p.processKeyfilePath(m)

	equal, err := fileContentEqualToBytes(keyFilePath, m.KeyfileContent)
	switch {
	case equal:
		return nil
	case err != nil:
		return err
	}

	keyFile, err := os.OpenFile(keyFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, KeyfilePermissions)
	if err != nil {
		return err
	}
	defer keyFile.Close()

	w := bufio.NewWriter(keyFile)
	_, err = w.WriteString(m.KeyfileContent)
	if err != nil {
		return err
	}
	if err = w.Flush(); err != nil {
		return err
	}

	return nil

}

func fileContentEqualToBytes(path string, content string) (equal bool, err error) {

	fileContent, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) { // nonexistent equivalent to unequal
			return false, nil
		} else {
			return false, err
		}
	}

	fileContentStr := string(fileContent)

	return content == fileContentStr, nil

}
<commit_msg>FIX: slave/procmgr: create mongods root directory<commit_after>package slave

import (
	"bufio"
	"fmt"
	"github.com/KIT-MAMID/mamid/msp"
	"golang.org/x/sys/unix"
	"io/ioutil"
	"os"
	"path/filepath"
	"strconv"
	"strings"
)

const skeletonDirPermissions = 0700
const KeyfilePermissions = 0600

// Create directories managed by ProcessManager
// returns nil if already exists and permissions are suitable
func (p *ProcessManager) CreateManagedDirs() (err error) {

	if err = unix.Access(p.dataDir, unix.W_OK); err != nil {
		return fmt.Errorf("`%s` does not exist or is not writable", p.dataDir)
	}

	if err := os.Mkdir(p.managedDirMongods(), os.ModeDir|0700); err != nil {
		return err
	}

	return nil

}

// directory for mongod process root dirs
func (p *ProcessManager) managedDirMongods() string {
	return filepath.Join(p.dataDir, "mongods")
}

// Create root directory for a process in a subdirectory of
// a directory created by CreateManagedDirs()
// returns nil if already exists and permissions are suitable
func (p *ProcessManager) createDirSkeleton(m msp.Mongod) (err error) {

	if err = os.MkdirAll(p.processRootDir(m), os.ModeDir|skeletonDirPermissions); err != nil {
		goto wrapError
	}

	if err = os.MkdirAll(p.processDBPathDir(m), os.ModeDir|skeletonDirPermissions); err != nil {
		goto wrapError
	}

	if err = os.MkdirAll(p.processConfDir(m), os.ModeDir|skeletonDirPermissions); err != nil {
		goto wrapError
	}

	return nil

wrapError:
	return fmt.Errorf("could not create directory skeleton for Mongod: %s", err)

}

func (p *ProcessManager) destroyDataDirectory(m msp.Mongod) error {
	return os.RemoveAll(p.processRootDir(m))
}

// Generate list of process root directories in process root directory tree
func (p *ProcessManager) parseProcessDirTree() (replSetNameByPortNumber map[msp.PortNumber]string, err error) {

	entries, err := ioutil.ReadDir(p.managedDirMongods())
	if err != nil {
		return
	}

	replSetNameByPortNumber = make(map[msp.PortNumber]string)
	for _, entry := range entries {
		if entry.IsDir() {
			port, replSet, err := p.parseProcessRootDirentry(entry)
			if err != nil {
				log.Errorf("error parsing process root directory entry: %s", err)
			} else {
				replSetNameByPortNumber[port] = replSet
			}
		} else {
			log.Errorf("unexpected directory entry in `%s` (not a directory)", entry.Name())
			continue
		}
	}

	return

}

// Root directory of a process
// process data should not be directly stored there
func (p *ProcessManager) processRootDir(m msp.Mongod) string {
	return filepath.Join(p.managedDirMongods(), fmt.Sprintf("%d:%s", m.Port, m.ReplicaSetConfig.ReplicaSetName))
}

// Given a direntry (no prefix!), extract the data encoded in the direntry
func (p *ProcessManager) parseProcessRootDirentry(entry os.FileInfo) (port msp.PortNumber, replSet string, err error) {
	dirname := entry.Name()
	colonLoc := strings.Index(entry.Name(), ":")
	if colonLoc < 0 {
		err = fmt.Errorf("directory with unparsable name `%s` in db dir", entry.Name())
		return
	}
	portStr := dirname[0:colonLoc]
	port64, parseErr := strconv.ParseUint(portStr, 10, 16)
	if parseErr != nil {
		err = fmt.Errorf("could not parse port number `%s`", portStr)
		return
	}
	port = msp.PortNumber(port64)
	replSet = dirname[colonLoc+1:]
	return
}

func (p *ProcessManager) processDBPathDir(m msp.Mongod) string {
	return filepath.Join(p.processRootDir(m), "dbpath")
}

func (p *ProcessManager) processConfDir(m msp.Mongod) string {
	return filepath.Join(p.processRootDir(m), "conf")
}

func (p *ProcessManager) processKeyfilePath(m msp.Mongod) string {
	return filepath.Join(p.processConfDir(m), "keyfile")
}

// Update or create the keyfile of a Mongod
func (p *ProcessManager) UpdateKeyfile(m msp.Mongod) (err error) {

	var keyFilePath = p.processKeyfilePath(m)

	equal, err := fileContentEqualToBytes(keyFilePath, m.KeyfileContent)
	switch {
	case equal:
		return nil
	case err != nil:
		return err
	}

	keyFile, err := os.OpenFile(keyFilePath, os.O_CREATE|os.O_WRONLY|os.O_TRUNC, KeyfilePermissions)
	if err != nil {
		return err
	}
	defer keyFile.Close()

	w := bufio.NewWriter(keyFile)
	_, err = w.WriteString(m.KeyfileContent)
	if err != nil {
		return err
	}
	if err = w.Flush(); err != nil {
		return err
	}

	return nil

}

func fileContentEqualToBytes(path string, content string) (equal bool, err error) {

	fileContent, err := ioutil.ReadFile(path)
	if err != nil {
		if os.IsNotExist(err) { // nonexistent equivalent to unequal
			return false, nil
		} else {
			return false, err
		}
	}

	fileContentStr := string(fileContent)

	return content == fileContentStr, nil

}
<|endoftext|>


<commit_before>/*
Copyright 2019 The Knative 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 resources

import (
	"encoding/json"
	"fmt"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"knative.dev/pkg/kmeta"

	v1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// SequenceChannelName creates a name for the Channel fronting a specific step.
func SequenceChannelName(sequenceName string, step int) string {
	return fmt.Sprintf("%s-kn-sequence-%d", sequenceName, step)
}

// NewChannel returns an unstructured.Unstructured based on the ChannelTemplateSpec
// for a given sequence.
func NewChannel(name string, p *v1alpha1.Sequence) (*unstructured.Unstructured, error) {
	// Set the name of the resource we're creating as well as the namespace, etc.
	template := v1alpha1.ChannelTemplateSpecInternal{
		metav1.TypeMeta{
			Kind:       p.Spec.ChannelTemplate.Kind,
			APIVersion: p.Spec.ChannelTemplate.APIVersion,
		},
		metav1.ObjectMeta{
			OwnerReferences: []metav1.OwnerReference{
				*kmeta.NewControllerRef(p),
			},
			Name:      name,
			Namespace: p.Namespace,
		},
		p.Spec.ChannelTemplate.Spec,
	}
	raw, err := json.Marshal(template)
	if err != nil {
		return nil, err
	}
	u := &unstructured.Unstructured{}
	err = json.Unmarshal(raw, u)
	if err != nil {
		return nil, err
	}
	return u, nil
}
<commit_msg>Named field initialization. (#1549)<commit_after>/*
Copyright 2019 The Knative 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 resources

import (
	"encoding/json"
	"fmt"

	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
	"knative.dev/pkg/kmeta"

	v1alpha1 "github.com/knative/eventing/pkg/apis/messaging/v1alpha1"
	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// SequenceChannelName creates a name for the Channel fronting a specific step.
func SequenceChannelName(sequenceName string, step int) string {
	return fmt.Sprintf("%s-kn-sequence-%d", sequenceName, step)
}

// NewChannel returns an unstructured.Unstructured based on the ChannelTemplateSpec
// for a given sequence.
func NewChannel(name string, p *v1alpha1.Sequence) (*unstructured.Unstructured, error) {
	// Set the name of the resource we're creating as well as the namespace, etc.
	template := v1alpha1.ChannelTemplateSpecInternal{
		TypeMeta: metav1.TypeMeta{
			Kind:       p.Spec.ChannelTemplate.Kind,
			APIVersion: p.Spec.ChannelTemplate.APIVersion,
		},
		ObjectMeta: metav1.ObjectMeta{
			OwnerReferences: []metav1.OwnerReference{
				*kmeta.NewControllerRef(p),
			},
			Name:      name,
			Namespace: p.Namespace,
		},
		Spec: p.Spec.ChannelTemplate.Spec,
	}
	raw, err := json.Marshal(template)
	if err != nil {
		return nil, err
	}
	u := &unstructured.Unstructured{}
	err = json.Unmarshal(raw, u)
	if err != nil {
		return nil, err
	}
	return u, nil
}
<|endoftext|>


<commit_before>// Warnings about deprecated operations in Starlark

package warn

import (
	"fmt"
	"strings"

	"github.com/bazelbuild/buildtools/build"
)

func dictionaryConcatenationWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	var addWarning = func(expr build.Expr) {
		findings = append(findings,
			makeLinterFinding(expr, "Dictionary concatenation is deprecated."))
	}

	types := detectTypes(f)
	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
		switch expr := expr.(type) {
		case *build.BinaryExpr:
			if expr.Op != "+" {
				return
			}
			if types[expr.X] == Dict || types[expr.Y] == Dict {
				addWarning(expr)
			}
		case *build.AssignExpr:
			if expr.Op != "+=" {
				return
			}
			if types[expr.LHS] == Dict || types[expr.RHS] == Dict {
				addWarning(expr)
			}
		}
	})
	return findings
}

func stringIterationWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	addWarning := func(expr build.Expr) {
		findings = append(findings,
			makeLinterFinding(expr, "String iteration is deprecated."))
	}

	types := detectTypes(f)
	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
		switch expr := expr.(type) {
		case *build.ForStmt:
			if types[expr.X] == String {
				addWarning(expr.X)
			}
		case *build.ForClause:
			if types[expr.X] == String {
				addWarning(expr.X)
			}
		case *build.CallExpr:
			ident, ok := expr.X.(*build.Ident)
			if !ok {
				return
			}
			switch ident.Name {
			case "all", "any", "reversed", "max", "min":
				if len(expr.List) != 1 {
					return
				}
				if types[expr.List[0]] == String {
					addWarning(expr.List[0])
				}
			case "zip":
				for _, arg := range expr.List {
					if types[arg] == String {
						addWarning(arg)
					}
				}
			}
		}
	})
	return findings
}

func integerDivisionWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) {
		switch expr := (*e).(type) {
		case *build.BinaryExpr:
			if expr.Op != "/" {
				return
			}
			newBinary := *expr
			newBinary.Op = "//"
			findings = append(findings,
				makeLinterFinding(expr, `The "/" operator for integer division is deprecated in favor of "//".`,
					LinterReplacement{e, &newBinary}))

		case *build.AssignExpr:
			if expr.Op != "/=" {
				return
			}
			newAssign := *expr
			newAssign.Op = "//="
			findings = append(findings,
				makeLinterFinding(expr, `The "/=" operator for integer division is deprecated in favor of "//=".`,
					LinterReplacement{e, &newAssign}))
		}
	})
	return findings
}

func stringEscapeWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) {
		str, ok := (*expr).(*build.StringExpr)
		if !ok {
			return
		}

		var quotes, value string
		if str.TripleQuote {
			quotes = str.Token[:3]
			value = str.Token[3 : len(str.Token)-3]
		} else {
			quotes = str.Token[:1]
			value = str.Token[1 : len(str.Token)-1]
		}

		var problems []int  // positions of the problems (unidentified escape sequences)

		escaped := false
		// This for-loop doesn't correctly check for a backlash at the end of the string literal, but
		// such string can't be parsed anyway, neither by Bazel nor by Buildifier.
		for i, ch := range value {
			if !escaped {
				if ch == '\\' {
					escaped = true
				}
				continue
			}

			switch ch {
			case '\n', '\\', 'n', 'r', 't', 'x', '\'', '"', '0', '1', '2', '3', '4', '5', '6', '7':
				// According to https://github.com/Quarz0/bazel/blob/207a6103393908aba64ddb96239fbdd56cdfec05/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java
				// \x is also included to the list, although it's not supported by Bazel, but it's supported
				// by Buildifier. This is safe for the migration because it's never been supported in Bazel,
				// even before --incompatible_restrict_string_escapes was flipped.
			default:
				problems = append(problems, i)
			}
			escaped = false
		}

		if len(problems) == 0 {
			return
		}

		var msg string
		if len(problems) == 1 {
			msg = fmt.Sprintf(
				"Invalid escape sequence \\%s at position %d.",
				string(value[problems[0]]),
				problems[0],
				)
		} else {
			var builder strings.Builder
			builder.WriteString("Invalid escape sequences:\n")
			for _, pos := range problems {
				builder.WriteString(fmt.Sprintf(
					"    \\%s at position %d\n",
					string(value[pos]),
					pos,
					))
			}
			msg = builder.String()
		}
		finding := makeLinterFinding(str, msg)

		// Fix
		var bytes []byte
		index := 0
		for _, backslashPos := range problems {
			for ; index < backslashPos; index++ {
				bytes = append(bytes, value[index])
			}
			bytes = append(bytes, '\\')
		}
		for ; index < len(value); index++ {
			bytes = append(bytes, value[index])
		}

		token := quotes + string(bytes) + quotes
		val, _, err := build.Unquote(token)
		if err == nil {
			newStr := *str
			newStr.Token = token
			newStr.Value = val
			finding.Replacement = []LinterReplacement{{expr, &newStr}}
		}

		findings = append(findings, finding)
	})
	return findings
}
<commit_msg>Fix the string escape warning (#695)<commit_after>// Warnings about deprecated operations in Starlark

package warn

import (
	"fmt"
	"strings"

	"github.com/bazelbuild/buildtools/build"
)

func dictionaryConcatenationWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	var addWarning = func(expr build.Expr) {
		findings = append(findings,
			makeLinterFinding(expr, "Dictionary concatenation is deprecated."))
	}

	types := detectTypes(f)
	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
		switch expr := expr.(type) {
		case *build.BinaryExpr:
			if expr.Op != "+" {
				return
			}
			if types[expr.X] == Dict || types[expr.Y] == Dict {
				addWarning(expr)
			}
		case *build.AssignExpr:
			if expr.Op != "+=" {
				return
			}
			if types[expr.LHS] == Dict || types[expr.RHS] == Dict {
				addWarning(expr)
			}
		}
	})
	return findings
}

func stringIterationWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	addWarning := func(expr build.Expr) {
		findings = append(findings,
			makeLinterFinding(expr, "String iteration is deprecated."))
	}

	types := detectTypes(f)
	build.Walk(f, func(expr build.Expr, stack []build.Expr) {
		switch expr := expr.(type) {
		case *build.ForStmt:
			if types[expr.X] == String {
				addWarning(expr.X)
			}
		case *build.ForClause:
			if types[expr.X] == String {
				addWarning(expr.X)
			}
		case *build.CallExpr:
			ident, ok := expr.X.(*build.Ident)
			if !ok {
				return
			}
			switch ident.Name {
			case "all", "any", "reversed", "max", "min":
				if len(expr.List) != 1 {
					return
				}
				if types[expr.List[0]] == String {
					addWarning(expr.List[0])
				}
			case "zip":
				for _, arg := range expr.List {
					if types[arg] == String {
						addWarning(arg)
					}
				}
			}
		}
	})
	return findings
}

func integerDivisionWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	build.WalkPointers(f, func(e *build.Expr, stack []build.Expr) {
		switch expr := (*e).(type) {
		case *build.BinaryExpr:
			if expr.Op != "/" {
				return
			}
			newBinary := *expr
			newBinary.Op = "//"
			findings = append(findings,
				makeLinterFinding(expr, `The "/" operator for integer division is deprecated in favor of "//".`,
					LinterReplacement{e, &newBinary}))

		case *build.AssignExpr:
			if expr.Op != "/=" {
				return
			}
			newAssign := *expr
			newAssign.Op = "//="
			findings = append(findings,
				makeLinterFinding(expr, `The "/=" operator for integer division is deprecated in favor of "//=".`,
					LinterReplacement{e, &newAssign}))
		}
	})
	return findings
}

func stringEscapeWarning(f *build.File) []*LinterFinding {
	var findings []*LinterFinding

	build.WalkPointers(f, func(expr *build.Expr, stack []build.Expr) {
		str, ok := (*expr).(*build.StringExpr)
		if !ok || len(str.Token) == 0 {
			// String literals with empty Token field may appear if they are manually created StringExpr nodes
			// (token is only used as a hint to the printer, if it doesn't exist, the Value field is used to
			// generate the string literal).
			return
		}

		var quotes, value string
		if str.TripleQuote {
			quotes = str.Token[:3]
			value = str.Token[3 : len(str.Token)-3]
		} else {
			quotes = str.Token[:1]
			value = str.Token[1 : len(str.Token)-1]
		}

		var problems []int  // positions of the problems (unidentified escape sequences)

		escaped := false
		// This for-loop doesn't correctly check for a backlash at the end of the string literal, but
		// such string can't be parsed anyway, neither by Bazel nor by Buildifier.
		for i, ch := range value {
			if !escaped {
				if ch == '\\' {
					escaped = true
				}
				continue
			}

			switch ch {
			case '\n', '\\', 'n', 'r', 't', 'x', '\'', '"', '0', '1', '2', '3', '4', '5', '6', '7':
				// According to https://github.com/Quarz0/bazel/blob/207a6103393908aba64ddb96239fbdd56cdfec05/src/main/java/com/google/devtools/build/lib/syntax/Lexer.java
				// \x is also included to the list, although it's not supported by Bazel, but it's supported
				// by Buildifier. This is safe for the migration because it's never been supported in Bazel,
				// even before --incompatible_restrict_string_escapes was flipped.
			default:
				problems = append(problems, i)
			}
			escaped = false
		}

		if len(problems) == 0 {
			return
		}

		var msg string
		if len(problems) == 1 {
			msg = fmt.Sprintf(
				"Invalid escape sequence \\%s at position %d.",
				string(value[problems[0]]),
				problems[0],
				)
		} else {
			var builder strings.Builder
			builder.WriteString("Invalid escape sequences:\n")
			for _, pos := range problems {
				builder.WriteString(fmt.Sprintf(
					"    \\%s at position %d\n",
					string(value[pos]),
					pos,
					))
			}
			msg = builder.String()
		}
		finding := makeLinterFinding(str, msg)

		// Fix
		var bytes []byte
		index := 0
		for _, backslashPos := range problems {
			for ; index < backslashPos; index++ {
				bytes = append(bytes, value[index])
			}
			bytes = append(bytes, '\\')
		}
		for ; index < len(value); index++ {
			bytes = append(bytes, value[index])
		}

		token := quotes + string(bytes) + quotes
		val, _, err := build.Unquote(token)
		if err == nil {
			newStr := *str
			newStr.Token = token
			newStr.Value = val
			finding.Replacement = []LinterReplacement{{expr, &newStr}}
		}

		findings = append(findings, finding)
	})
	return findings
}
<|endoftext|>