<commit_before>/*

	filesystem is a storage layer that stores information about games as JSON
	files within a given folder, (or somewhere nested in a folder within base
	folder) one per game. It's extremely inefficient and doesn't even persist
	extended game information to disk. It's most useful for cases where having
	an easy-to-read, diffable representation for games makes sense, for
	example to create golden tester games for use in testing.

	filesystem stores files in the given base folder. If a sub-folder exists
	with the name of the gameType, then the game will be stored in that folder
	instead. For example if the gametype is "checkers" and the checkers subdir
	exists, will store at  'checkers/a22ffcdef.json'. Folders may be soft-
	linked from within the base folder; often when using the filesystem
	storage layer to help generate test cases you set up soft- links from a
	central location to a folder for test files in each game's sub-directory,
	so the test files can be in the same place.

*/
package filesystem

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/server/api/extendedgame"
	"github.com/jkomoros/boardgame/server/api/listing"
	"github.com/jkomoros/boardgame/storage/filesystem/record"
	"github.com/jkomoros/boardgame/storage/internal/helpers"
)

type StorageManager struct {
	//Fall back on those methods
	*helpers.ExtendedMemoryStorageManager
	basePath string
	managers []*boardgame.GameManager
	//Only shoiuld be on in testing scenarios
	forceFullEncoding bool
}

//Store seen ids and remember where the path was
var idToPath map[string]string

func init() {
	idToPath = make(map[string]string)
}

//NewStorageManager returns a new filesystem storage manager. basePath is the
//folder, relative to this executable, to have as the root of the storage
//pool.
func NewStorageManager(basePath string) *StorageManager {

	result := &StorageManager{
		basePath: basePath,
	}

	result.ExtendedMemoryStorageManager = helpers.NewExtendedMemoryStorageManager(result)

	return result
}

func (s *StorageManager) Name() string {
	return "filesystem"
}

func (s *StorageManager) Connect(config string) error {

	if _, err := os.Stat(s.basePath); os.IsNotExist(err) {
		if err := os.Mkdir(s.basePath, 0700); err != nil {
			return errors.New("Base path didn't exist and couldn't create it: " + err.Error())
		}
	}

	return nil
}

func (s *StorageManager) WithManagers(managers []*boardgame.GameManager) {
	s.managers = managers
}

func (s *StorageManager) CleanUp() {
	os.RemoveAll(s.basePath)
}

//pathForId will look through each sub-folder and look for a file named
//gameId.json, returning its relative path if it is found, "" otherwise.
func pathForId(basePath, gameId string) string {

	if path, ok := idToPath[gameId]; ok {
		return path
	}

	items, err := ioutil.ReadDir(basePath)
	if err != nil {
		return ""
	}
	for _, item := range items {
		if item.IsDir() {
			if recursiveResult := pathForId(filepath.Join(basePath, item.Name()), gameId); recursiveResult != "" {
				return recursiveResult
			}
			continue
		}

		if item.Name() == gameId+".json" {
			result := filepath.Join(basePath, item.Name())
			idToPath[gameId] = result
			return result
		}
	}
	return ""
}

func (s *StorageManager) recordForId(gameId string) (*record.Record, error) {
	if s.basePath == "" {
		return nil, errors.New("No base path provided")
	}

	gameId = strings.ToLower(gameId)

	path := pathForId(s.basePath, gameId)

	if path == "" {
		return nil, errors.New("Couldn't find file matching: " + gameId)
	}

	return record.New(path)
}

func (s *StorageManager) saveRecordForId(gameId string, rec *record.Record) error {
	if s.basePath == "" {
		return errors.New("Invalid base path")
	}

	if rec.Game() == nil {
		return errors.New("Game record in rec was nil")
	}

	gameId = strings.ToLower(gameId)

	//If a sub directory for that game type exists, save there. If not, save in the root of basePath.
	gameTypeSubDir := filepath.Join(s.basePath, rec.Game().Name)

	var path string

	if _, err := os.Stat(gameTypeSubDir); err == nil {
		path = filepath.Join(gameTypeSubDir, gameId+".json")
	} else {
		path = filepath.Join(s.basePath, gameId+".json")
	}

	if err := rec.Save(path, false); err != nil {
		return err
	}

	idToPath[gameId] = path

	return nil
}

func (s *StorageManager) State(gameId string, version int) (boardgame.StateStorageRecord, error) {
	rec, err := s.recordForId(gameId)

	if err != nil {
		return nil, err
	}

	result, err := rec.State(version)

	if err != nil {
		return nil, err
	}

	return boardgame.StateStorageRecord(result), nil

}

func (s *StorageManager) Move(gameId string, version int) (*boardgame.MoveStorageRecord, error) {
	rec, err := s.recordForId(gameId)

	if err != nil {
		return nil, err
	}

	return rec.Move(version)
}

func (s *StorageManager) Moves(gameId string, fromVersion, toVersion int) ([]*boardgame.MoveStorageRecord, error) {
	return helpers.MovesHelper(s, gameId, fromVersion, toVersion)
}

func (s *StorageManager) Game(id string) (*boardgame.GameStorageRecord, error) {

	rec, err := s.recordForId(id)

	if err != nil {
		return nil, err
	}

	return rec.Game(), nil
}

func (s *StorageManager) SaveGameAndCurrentState(game *boardgame.GameStorageRecord, state boardgame.StateStorageRecord, move *boardgame.MoveStorageRecord) error {
	rec, err := s.recordForId(game.ID)

	if err != nil {
		//Must be the first save.
		if s.forceFullEncoding {
			rec = record.EmptyWithFullStateEncoding()
		} else {
			rec = &record.Record{}
		}
	}

	if err := rec.AddGameAndCurrentState(game, state, move); err != nil {
		return errors.New("Couldn't add state: " + err.Error())
	}

	return s.saveRecordForId(game.ID, rec)

}

func (s *StorageManager) CombinedGame(id string) (*extendedgame.CombinedStorageRecord, error) {
	rec, err := s.recordForId(id)

	if err != nil {
		return nil, err
	}

	eGame, err := s.ExtendedGame(id)

	if err != nil {
		return nil, err
	}

	return &extendedgame.CombinedStorageRecord{
		GameStorageRecord: *rec.Game(),
		StorageRecord:     *eGame,
	}, nil
}

func idFromPath(path string) string {
	_, filename := filepath.Split(path)
	return strings.TrimSuffix(filename, ".json")
}

func (s *StorageManager) recursiveAllGames(basePath string) []*boardgame.GameStorageRecord {

	files, err := ioutil.ReadDir(basePath)

	if err != nil {
		return nil
	}

	var result []*boardgame.GameStorageRecord

	for _, file := range files {

		if file.IsDir() {
			result = append(result, s.recursiveAllGames(filepath.Join(basePath, file.Name()))...)
			continue
		}
		ext := filepath.Ext(file.Name())
		if ext != ".json" {
			continue
		}
		rec, err := s.recordForId(idFromPath(file.Name()))
		if err != nil {
			return nil
		}
		result = append(result, rec.Game())
	}
	return result
}

func (s *StorageManager) AllGames() []*boardgame.GameStorageRecord {
	return s.recursiveAllGames(s.basePath)
}

func (s *StorageManager) ListGames(max int, list listing.Type, userId string, gameType string) []*extendedgame.CombinedStorageRecord {
	return helpers.ListGamesHelper(s, max, list, userId, gameType)
}
<commit_msg>Fix lint warniings for filesystem Part of #552.<commit_after>/*

Package filesystem is a storage layer that stores information about games as
JSON files within a given folder, (or somewhere nested in a folder within base
folder) one per game. It's extremely inefficient and doesn't even persist
extended game information to disk. It's most useful for cases where having an
easy-to-read, diffable representation for games makes sense, for example to
create golden tester games for use in testing.

filesystem stores files in the given base folder. If a sub-folder exists with
the name of the gameType, then the game will be stored in that folder instead.
For example if the gametype is "checkers" and the checkers subdir exists, will
store at  'checkers/a22ffcdef.json'. Folders may be soft- linked from within the
base folder; often when using the filesystem storage layer to help generate test
cases you set up soft- links from a central location to a folder for test files
in each game's sub-directory, so the test files can be in the same place.

*/
package filesystem

import (
	"errors"
	"io/ioutil"
	"os"
	"path/filepath"
	"strings"

	"github.com/jkomoros/boardgame"
	"github.com/jkomoros/boardgame/server/api/extendedgame"
	"github.com/jkomoros/boardgame/server/api/listing"
	"github.com/jkomoros/boardgame/storage/filesystem/record"
	"github.com/jkomoros/boardgame/storage/internal/helpers"
)

//StorageManager is the primary type for this package.
type StorageManager struct {
	//Fall back on those methods
	*helpers.ExtendedMemoryStorageManager
	basePath string
	managers []*boardgame.GameManager
	//Only shoiuld be on in testing scenarios
	forceFullEncoding bool
}

//Store seen ids and remember where the path was
var idToPath map[string]string

func init() {
	idToPath = make(map[string]string)
}

//NewStorageManager returns a new filesystem storage manager. basePath is the
//folder, relative to this executable, to have as the root of the storage
//pool.
func NewStorageManager(basePath string) *StorageManager {

	result := &StorageManager{
		basePath: basePath,
	}

	result.ExtendedMemoryStorageManager = helpers.NewExtendedMemoryStorageManager(result)

	return result
}

//Name returns 'filesystem'
func (s *StorageManager) Name() string {
	return "filesystem"
}

//Connect verifies the given basePath exists.
func (s *StorageManager) Connect(config string) error {

	if _, err := os.Stat(s.basePath); os.IsNotExist(err) {
		if err := os.Mkdir(s.basePath, 0700); err != nil {
			return errors.New("Base path didn't exist and couldn't create it: " + err.Error())
		}
	}

	return nil
}

//WithManagers sets the managers
func (s *StorageManager) WithManagers(managers []*boardgame.GameManager) {
	s.managers = managers
}

//CleanUp cleans up evertyhing in basePath.
func (s *StorageManager) CleanUp() {
	os.RemoveAll(s.basePath)
}

//pathForID will look through each sub-folder and look for a file named
//gameId.json, returning its relative path if it is found, "" otherwise.
func pathForID(basePath, gameID string) string {

	if path, ok := idToPath[gameID]; ok {
		return path
	}

	items, err := ioutil.ReadDir(basePath)
	if err != nil {
		return ""
	}
	for _, item := range items {
		if item.IsDir() {
			if recursiveResult := pathForID(filepath.Join(basePath, item.Name()), gameID); recursiveResult != "" {
				return recursiveResult
			}
			continue
		}

		if item.Name() == gameID+".json" {
			result := filepath.Join(basePath, item.Name())
			idToPath[gameID] = result
			return result
		}
	}
	return ""
}

func (s *StorageManager) recordForID(gameID string) (*record.Record, error) {
	if s.basePath == "" {
		return nil, errors.New("No base path provided")
	}

	gameID = strings.ToLower(gameID)

	path := pathForID(s.basePath, gameID)

	if path == "" {
		return nil, errors.New("Couldn't find file matching: " + gameID)
	}

	return record.New(path)
}

func (s *StorageManager) saveRecordForID(gameID string, rec *record.Record) error {
	if s.basePath == "" {
		return errors.New("Invalid base path")
	}

	if rec.Game() == nil {
		return errors.New("Game record in rec was nil")
	}

	gameID = strings.ToLower(gameID)

	//If a sub directory for that game type exists, save there. If not, save in the root of basePath.
	gameTypeSubDir := filepath.Join(s.basePath, rec.Game().Name)

	var path string

	if _, err := os.Stat(gameTypeSubDir); err == nil {
		path = filepath.Join(gameTypeSubDir, gameID+".json")
	} else {
		path = filepath.Join(s.basePath, gameID+".json")
	}

	if err := rec.Save(path, false); err != nil {
		return err
	}

	idToPath[gameID] = path

	return nil
}

//State returns the state for that gameID and version.
func (s *StorageManager) State(gameID string, version int) (boardgame.StateStorageRecord, error) {
	rec, err := s.recordForID(gameID)

	if err != nil {
		return nil, err
	}

	result, err := rec.State(version)

	if err != nil {
		return nil, err
	}

	return boardgame.StateStorageRecord(result), nil

}

//Move returns the move for that gameID and version
func (s *StorageManager) Move(gameID string, version int) (*boardgame.MoveStorageRecord, error) {
	rec, err := s.recordForID(gameID)

	if err != nil {
		return nil, err
	}

	return rec.Move(version)
}

//Moves returns all of the moves
func (s *StorageManager) Moves(gameID string, fromVersion, toVersion int) ([]*boardgame.MoveStorageRecord, error) {
	return helpers.MovesHelper(s, gameID, fromVersion, toVersion)
}

//Game returns the game storage record for that game.
func (s *StorageManager) Game(id string) (*boardgame.GameStorageRecord, error) {

	rec, err := s.recordForID(id)

	if err != nil {
		return nil, err
	}

	return rec.Game(), nil
}

//SaveGameAndCurrentState saves the game and current state.
func (s *StorageManager) SaveGameAndCurrentState(game *boardgame.GameStorageRecord, state boardgame.StateStorageRecord, move *boardgame.MoveStorageRecord) error {
	rec, err := s.recordForID(game.ID)

	if err != nil {
		//Must be the first save.
		if s.forceFullEncoding {
			rec = record.EmptyWithFullStateEncoding()
		} else {
			rec = &record.Record{}
		}
	}

	if err := rec.AddGameAndCurrentState(game, state, move); err != nil {
		return errors.New("Couldn't add state: " + err.Error())
	}

	return s.saveRecordForID(game.ID, rec)

}

//CombinedGame returns the combined game
func (s *StorageManager) CombinedGame(id string) (*extendedgame.CombinedStorageRecord, error) {
	rec, err := s.recordForID(id)

	if err != nil {
		return nil, err
	}

	eGame, err := s.ExtendedGame(id)

	if err != nil {
		return nil, err
	}

	return &extendedgame.CombinedStorageRecord{
		GameStorageRecord: *rec.Game(),
		StorageRecord:     *eGame,
	}, nil
}

func idFromPath(path string) string {
	_, filename := filepath.Split(path)
	return strings.TrimSuffix(filename, ".json")
}

func (s *StorageManager) recursiveAllGames(basePath string) []*boardgame.GameStorageRecord {

	files, err := ioutil.ReadDir(basePath)

	if err != nil {
		return nil
	}

	var result []*boardgame.GameStorageRecord

	for _, file := range files {

		if file.IsDir() {
			result = append(result, s.recursiveAllGames(filepath.Join(basePath, file.Name()))...)
			continue
		}
		ext := filepath.Ext(file.Name())
		if ext != ".json" {
			continue
		}
		rec, err := s.recordForID(idFromPath(file.Name()))
		if err != nil {
			return nil
		}
		result = append(result, rec.Game())
	}
	return result
}

//AllGames returns all games
func (s *StorageManager) AllGames() []*boardgame.GameStorageRecord {
	return s.recursiveAllGames(s.basePath)
}

//ListGames returns all of the games
func (s *StorageManager) ListGames(max int, list listing.Type, userID string, gameType string) []*extendedgame.CombinedStorageRecord {
	return helpers.ListGamesHelper(s, max, list, userID, gameType)
}
<|endoftext|>


<commit_before>package libcontainer

import (
	"encoding/json"
	"fmt"
	"io"

	"github.com/opencontainers/runc/libcontainer/utils"
)

type syncType string

// Constants that are used for synchronisation between the parent and child
// during container setup. They come in pairs (with procError being a generic
// response which is followed by a &genericError).
//
// [  child  ] <-> [   parent   ]
//
// procHooks   --> [run hooks]
//             <-- procResume
//
// procConsole -->
//             <-- procConsoleReq
//  [send(fd)] --> [recv(fd)]
//             <-- procConsoleAck
//
// procReady   --> [final setup]
//             <-- procRun
const (
	procError  syncType = "procError"
	procReady  syncType = "procReady"
	procRun    syncType = "procRun"
	procHooks  syncType = "procHooks"
	procResume syncType = "procResume"
)

type syncT struct {
	Type syncType `json:"type"`
}

// writeSync is used to write to a synchronisation pipe. An error is returned
// if there was a problem writing the payload.
func writeSync(pipe io.Writer, sync syncType) error {
	if err := utils.WriteJSON(pipe, syncT{sync}); err != nil {
		return err
	}
	return nil
}

// readSync is used to read from a synchronisation pipe. An error is returned
// if we got a genericError, the pipe was closed, or we got an unexpected flag.
func readSync(pipe io.Reader, expected syncType) error {
	var procSync syncT
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}

		if procSync.Type == procError {
			var ierr genericError

			if err := json.NewDecoder(pipe).Decode(&ierr); err != nil {
				return fmt.Errorf("failed reading error from parent: %v", err)
			}

			return &ierr
		}

		if procSync.Type != expected {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// parseSync runs the given callback function on each syncT received from the
// child. It will return once io.EOF is returned from the given pipe.
func parseSync(pipe io.Reader, fn func(*syncT) error) error {
	dec := json.NewDecoder(pipe)
	for {
		var sync syncT
		if err := dec.Decode(&sync); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		// We handle this case outside fn for cleanliness reasons.
		var ierr *genericError
		if sync.Type == procError {
			if err := dec.Decode(&ierr); err != nil && err != io.EOF {
				return newSystemErrorWithCause(err, "decoding proc error from init")
			}
			if ierr != nil {
				return ierr
			}
			// Programmer error.
			panic("No error following JSON procError payload.")
		}

		if err := fn(&sync); err != nil {
			return err
		}
	}
	return nil
}
<commit_msg>libcontainer/sync: Drop procConsole transaction from comments<commit_after>package libcontainer

import (
	"encoding/json"
	"fmt"
	"io"

	"github.com/opencontainers/runc/libcontainer/utils"
)

type syncType string

// Constants that are used for synchronisation between the parent and child
// during container setup. They come in pairs (with procError being a generic
// response which is followed by a &genericError).
//
// [  child  ] <-> [   parent   ]
//
// procHooks   --> [run hooks]
//             <-- procResume
//
// procReady   --> [final setup]
//             <-- procRun
const (
	procError  syncType = "procError"
	procReady  syncType = "procReady"
	procRun    syncType = "procRun"
	procHooks  syncType = "procHooks"
	procResume syncType = "procResume"
)

type syncT struct {
	Type syncType `json:"type"`
}

// writeSync is used to write to a synchronisation pipe. An error is returned
// if there was a problem writing the payload.
func writeSync(pipe io.Writer, sync syncType) error {
	if err := utils.WriteJSON(pipe, syncT{sync}); err != nil {
		return err
	}
	return nil
}

// readSync is used to read from a synchronisation pipe. An error is returned
// if we got a genericError, the pipe was closed, or we got an unexpected flag.
func readSync(pipe io.Reader, expected syncType) error {
	var procSync syncT
	if err := json.NewDecoder(pipe).Decode(&procSync); err != nil {
		if err == io.EOF {
			return fmt.Errorf("parent closed synchronisation channel")
		}

		if procSync.Type == procError {
			var ierr genericError

			if err := json.NewDecoder(pipe).Decode(&ierr); err != nil {
				return fmt.Errorf("failed reading error from parent: %v", err)
			}

			return &ierr
		}

		if procSync.Type != expected {
			return fmt.Errorf("invalid synchronisation flag from parent")
		}
	}
	return nil
}

// parseSync runs the given callback function on each syncT received from the
// child. It will return once io.EOF is returned from the given pipe.
func parseSync(pipe io.Reader, fn func(*syncT) error) error {
	dec := json.NewDecoder(pipe)
	for {
		var sync syncT
		if err := dec.Decode(&sync); err != nil {
			if err == io.EOF {
				break
			}
			return err
		}

		// We handle this case outside fn for cleanliness reasons.
		var ierr *genericError
		if sync.Type == procError {
			if err := dec.Decode(&ierr); err != nil && err != io.EOF {
				return newSystemErrorWithCause(err, "decoding proc error from init")
			}
			if ierr != nil {
				return ierr
			}
			// Programmer error.
			panic("No error following JSON procError payload.")
		}

		if err := fn(&sync); err != nil {
			return err
		}
	}
	return nil
}
<|endoftext|>


<commit_before>package stringutils

import (
	"crypto/md5"
	"crypto/rand"
	"fmt"
	"io"
	"math"
	r "math/rand"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode/utf16"
)

func GetAfter(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index+len(find) < len(src) {
		return src[(index + len(find)):], true
	}
	return src, false
}

func GetBefore(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index < len(src) {
		return src[:index], true
	}
	return src, false
}

func UnicodeDecode(text string) string {
	regex, err := regexp.Compile(`(\\u[a-fA-F0-9]{4})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 16, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	regex, err = regexp.Compile(`(&#[\d]{2,6})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 10, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	return text
}

func UnicodeEncode(str string) (js, html string) {
	rs := []rune(str)
	js = ""
	html = ""
	for _, r := range rs {
		rint := int(r)
		if rint < 128 {
			js += string(r)
			html += string(r)
		} else {
			js += `\u` + strconv.FormatInt(int64(rint), 16) // json
			html += `&#` + strconv.Itoa(int(r)) + ";"       // 网页
		}
	}
	fmt.Printf("JSON: %s\n", js)
	fmt.Printf("HTML: %s\n", html)
	return
}

//比较是否在切片中存在，不区分大小写
func ExistSliceFold(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if strings.EqualFold(v, elem) {
			return true
		}
	}

	return false
}

//比较是否在切片中存在,区分大小写
func ExistSlice(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if v == elem {
			return true
		}
	}

	return false
}

//字符串转换uint16
func StringToUTF16(s string) []uint16 {
	return utf16.Encode([]rune(s + "\x00"))
}

func Float64IsZero(s float64) bool {
	if math.Abs(s) < 0.0001 {
		return true
	}
	return false
}

//取子串
func Substr(s string, start, length int) string {
	bt := []rune(s)
	if start < 0 {
		start = 0
	}
	if start > len(bt) {
		start = start % len(bt)
	}
	var end int
	if (start + length) > (len(bt) - 1) {
		end = len(bt)
	} else {
		end = start + length
	}
	return string(bt[start:end])
}

//删除slice中的元素
func RemoveSliceElement(val interface{}, index int) interface{} {

	if reflect.TypeOf(val).Kind() != reflect.Slice {
		fmt.Println("val类型非slice")
		return nil
	}

	s := reflect.ValueOf(val)
	if index < 0 || index >= s.Len() {
		fmt.Println("传入参数有误")
		return nil
	}

	prev := s.Index(index)
	for i := index + 1; i < s.Len(); i++ {
		value := s.Index(i)
		prev.Set(value)
		prev = value
	}

	return s.Slice(0, s.Len()-1).Interface()
}

// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	var randby bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randby = true
	}
	for i, b := range bytes {
		if len(alphabets) == 0 {
			if randby {
				bytes[i] = alphanum[r.Intn(len(alphanum))]
			} else {
				bytes[i] = alphanum[b%byte(len(alphanum))]
			}
		} else {
			if randby {
				bytes[i] = alphabets[r.Intn(len(alphabets))]
			} else {
				bytes[i] = alphabets[b%byte(len(alphabets))]
			}
		}
	}
	return bytes
}

func SumMd5(txtInput string) string {
	h := md5.New()
	io.WriteString(h, txtInput)
	return fmt.Sprintf("%x", h.Sum(nil))
}
<commit_msg>add make guid function<commit_after>package stringutils

import (
	"crypto/md5"
	"crypto/rand"
	"encoding/base64"
	"fmt"
	"io"
	"math"
	r "math/rand"
	"reflect"
	"regexp"
	"strconv"
	"strings"
	"time"
	"unicode/utf16"
)

func GetAfter(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index+len(find) < len(src) {
		return src[(index + len(find)):], true
	}
	return src, false
}

func GetBefore(src string, find string) (string, bool) {
	index := strings.Index(src, find)
	if index > -1 && index < len(src) {
		return src[:index], true
	}
	return src, false
}

func UnicodeDecode(text string) string {
	regex, err := regexp.Compile(`(\\u[a-fA-F0-9]{4})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 16, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	regex, err = regexp.Compile(`(&#[\d]{2,6})`)
	if err != nil {
		return text
	}

	text = regex.ReplaceAllStringFunc(text, func(match string) string {
		_txt := match[2:]
		char, err := strconv.ParseInt(_txt, 10, 32)
		if err != nil {
			return match
		}
		return string(rune(int(char)))
	})

	return text
}

func UnicodeEncode(str string) (js, html string) {
	rs := []rune(str)
	js = ""
	html = ""
	for _, r := range rs {
		rint := int(r)
		if rint < 128 {
			js += string(r)
			html += string(r)
		} else {
			js += `\u` + strconv.FormatInt(int64(rint), 16) // json
			html += `&#` + strconv.Itoa(int(r)) + ";"       // 网页
		}
	}
	fmt.Printf("JSON: %s\n", js)
	fmt.Printf("HTML: %s\n", html)
	return
}

//比较是否在切片中存在，不区分大小写
func ExistSliceFold(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if strings.EqualFold(v, elem) {
			return true
		}
	}

	return false
}

//比较是否在切片中存在,区分大小写
func ExistSlice(srcSlice []string, elem string) bool {
	for _, v := range srcSlice {
		if v == elem {
			return true
		}
	}

	return false
}

//字符串转换uint16
func StringToUTF16(s string) []uint16 {
	return utf16.Encode([]rune(s + "\x00"))
}

func Float64IsZero(s float64) bool {
	if math.Abs(s) < 0.0001 {
		return true
	}
	return false
}

//取子串
func Substr(s string, start, length int) string {
	bt := []rune(s)
	if start < 0 {
		start = 0
	}
	if start > len(bt) {
		start = start % len(bt)
	}
	var end int
	if (start + length) > (len(bt) - 1) {
		end = len(bt)
	} else {
		end = start + length
	}
	return string(bt[start:end])
}

//删除slice中的元素
func RemoveSliceElement(val interface{}, index int) interface{} {

	if reflect.TypeOf(val).Kind() != reflect.Slice {
		fmt.Println("val类型非slice")
		return nil
	}

	s := reflect.ValueOf(val)
	if index < 0 || index >= s.Len() {
		fmt.Println("传入参数有误")
		return nil
	}

	prev := s.Index(index)
	for i := index + 1; i < s.Len(); i++ {
		value := s.Index(i)
		prev.Set(value)
		prev = value
	}

	return s.Slice(0, s.Len()-1).Interface()
}

// RandomCreateBytes generate random []byte by specify chars.
func RandomCreateBytes(n int, alphabets ...byte) []byte {
	const alphanum = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
	var bytes = make([]byte, n)
	var randby bool
	if num, err := rand.Read(bytes); num != n || err != nil {
		r.Seed(time.Now().UnixNano())
		randby = true
	}
	for i, b := range bytes {
		if len(alphabets) == 0 {
			if randby {
				bytes[i] = alphanum[r.Intn(len(alphanum))]
			} else {
				bytes[i] = alphanum[b%byte(len(alphanum))]
			}
		} else {
			if randby {
				bytes[i] = alphabets[r.Intn(len(alphabets))]
			} else {
				bytes[i] = alphabets[b%byte(len(alphabets))]
			}
		}
	}
	return bytes
}

func SumMd5(txtInput string) string {
	h := md5.New()
	io.WriteString(h, txtInput)
	return fmt.Sprintf("%x", h.Sum(nil))
}

//生成Guid字串
func GetGuid() string {
	b := make([]byte, 48)

	if _, err := io.ReadFull(rand.Reader, b); err != nil {
		return ""
	}
	return SumMd5(base64.URLEncoding.EncodeToString(b))
}
<|endoftext|>


<commit_before>package method

import "github.com/qlova/ilang/src"
import "github.com/qlova/ilang/src/modules/function"

var Flag = ilang.NewFlag()
var New = ilang.NewFlag()

func init() {
	ilang.RegisterToken([]string{"method"}, ScanMethod)
	ilang.RegisterListener(New, NewEnd)

	ilang.RegisterDefault(func(ic *ilang.Compiler) bool {
		token := ic.LastToken
		if ic.GetFlag(Flag) {
			if _, ok := ic.LastDefinedType.Detail.Table[token]; ok {
				ic.NextToken = ic.LastDefinedType.Name
				ic.NextNextToken = "."
				ic.NextNextNextToken = token
				ic.ScanStatement()
				return true
			}
		}
		return false
	})

	ilang.RegisterExpression(func(ic *ilang.Compiler) string {
		token := ic.LastToken
		if ic.GetFlag(Flag) {
			if ic.TypeExists(token) && ic.LastDefinedType.Super == token {
	 			ic.ExpressionType = ic.DefinedTypes[ic.LastDefinedType.Super]
				return ic.Shunt(ic.LastDefinedType.Name)
		 	}

			//Scope methods with multiple arguments inside the method.
			//eg. method Package.dosomething(22)
			// in a Package method, dosomething(22) should be local.
			if _, ok  := ic.DefinedFunctions[token+"_m_"+ic.LastDefinedType.Name]; ok {
				var f = token+"_m_"+ic.LastDefinedType.Name

				if !ic.LastDefinedType.Empty() {
					ic.Assembly(ic.LastDefinedType.Push," ", ic.LastDefinedType.Name)
				}

				ic.ExpressionType = function.Flag

				return ic.Shunt(f)
			}
		}

		if ic.TypeExists(token) {
			if ic.DefinedTypes[token].Empty() && ic.Peek() == "." {
				ic.Scan('.')
				ic.ExpressionType = function.Flag
				var name = ic.Scan(ilang.Name)
				return name+"_m_"+token
			}
		}

	 	return ""
	})

	ilang.RegisterVariable(func(ic *ilang.Compiler, name string) ilang.Type {
		//Allow table values to be indexed in a method.
		if ic.GetFlag(Flag) && ic.LastDefinedType.Detail != nil {
			if _, ok := ic.LastDefinedType.Detail.Table[name]; ok {
				var value = ic.IndexUserType(ic.LastDefinedType.Name, name)
				ic.AssembleVar(name, value)
				ic.SetVariable(name+"_use", ilang.Used)
				return ic.ExpressionType
			}
		}
		return ilang.Undefined
	})

	ilang.RegisterShunt(".", ShuntMethodCall)
}

func ShuntMethodCall(ic *ilang.Compiler, name string) string {
	var index = ic.Scan(ilang.Name)

	if f, ok  := ic.DefinedFunctions[index+"_m_"+ic.ExpressionType.GetComplexName()]; ok && len(f.Args) > 0 {
		var f = index+"_m_"+ic.ExpressionType.GetComplexName()
		ic.Assembly(ic.ExpressionType.Push," ", name)
		ic.ExpressionType = function.Flag
		return ic.Shunt(f)
	}

	ic.NextToken = index
	return ""
}

func NewEnd(ic *ilang.Compiler) {
	ic.Assembly("SHARE ", ic.LastDefinedType.Name)
	ic.LoseScope()
}

func Call(ic *ilang.Compiler, name string, t ilang.Type) {
	ic.Assembly(ic.RunFunction(name+"_m_"+t.GetComplexName()))
}

func ScanMethod(ic *ilang.Compiler) {
	ic.Header = false

	var name string = ic.Scan(ilang.Name)

	f := ic.DefinedFunctions[name]
	f.Method = true
	ic.DefinedFunctions[name] = f

	/*if name == "new" {
		ic.Scan('(')
		ic.ScanNew()
		return
	}*/

	var MethodType = ic.LastDefinedType

	var token = ic.Scan(0)
	if token == "(" {
		token = ic.Scan(0)
		if token != ")" {
			if t, ok := ic.DefinedTypes[token]; ok {
				MethodType = t
			} else {
				ic.NextToken = token
			}
		}

		ic.LastDefinedType = MethodType

		if MethodType.Name == "Game" && name == "new" {
			ic.NewGame = true
		}
		if MethodType.Name == "Game" && name == "draw" {
			ic.DrawGame = true
		}
		if MethodType.Name == "Game" && name == "update" {
			ic.UpdateGame = true
		}

		name += "_m_"+MethodType.Name

		ic.Assembly("FUNCTION ", name)
		ic.GainScope()

		if name == "new_m_"+MethodType.Name {

			ic.Assembly("PUSH ", len(MethodType.Detail.Elements))
			ic.Assembly("MAKE")
			ic.Assembly("GRAB ", MethodType.Name)

			ic.SetVariable(MethodType.Name, MethodType)
			ic.SetVariable(MethodType.Name+"_use", ilang.Used)
			ic.SetVariable(MethodType.Name+".", ilang.Protected)

		} else if len(MethodType.Detail.Elements) > 0 {
			ic.Assembly("%v %v", MethodType.Pop, MethodType.Name)
			ic.SetVariable(MethodType.Name, MethodType)
			ic.SetVariable(MethodType.Name+"_use", ilang.Used)
			ic.SetVariable(MethodType.Name+".", ilang.Protected)
		}

		function.CreateFromArguments(name, ic)
		ic.SetFlag(Flag)

		f = ic.DefinedFunctions[name]
		if name == "new_m_"+MethodType.Name {
			ic.GainScope()
			ic.SetFlag(New)
			f.Returns = []ilang.Type{MethodType}

		}

		f.Method = true
		ic.DefinedFunctions[name] = f

		ic.InsertPlugins(name)

	//Functional methods.
	} else if token == "." {

		if !ic.TypeExists(name) {
			ic.RaiseError("Undefined type: ", name)
		}

		t := ic.DefinedTypes[name]
		ic.LastDefinedType = t

		name = ic.Scan(ilang.Name)
		name += "_m_"+t.Name

		ic.Assembly("FUNCTION ", name)
		ic.GainScope()
		ic.Scan('(')

		function.CreateFromArguments(name, ic)

		if len(t.Detail.Elements) > 0 {
			ic.Assembly("%v %v", t.Pop, t.Name)
			ic.SetVariable(t.Name, t)
			ic.SetVariable(t.Name+"_use", ilang.Used)
		}

		f = ic.DefinedFunctions[name]
		ic.SetFlag(Flag)

		f.Method = true
		ic.DefinedFunctions[name] = f

		ic.InsertPlugins(name)

	} else {
		var symbol = token
		var other = ic.Scan(ilang.Name)
		ic.Scan('{')

		if t, ok := ic.DefinedTypes[name]; ok {
			MethodType = t
		}

		var a = MethodType

		MethodType = ic.DefinedTypes[other]

		ic.LastDefinedType = MethodType

		var b = MethodType

		ilang.NewOperator(a, symbol, b, "SHARE %a\n SHARE %b\nRUN "+a.Name+"_"+symbol+"_"+b.Name+"\nGRAB %c", true)

		ic.Assembly("FUNCTION %s_%s_%s\n", a.Name, symbol, b.Name)
		ic.GainScope()
		ic.Assembly("GRAB b\nGRAB a\nARRAY c\n")
		for range a.Detail.Elements {
			ic.Assembly("PUT 0\n")
		}
		ic.InOperatorFunction = true

		ic.SetFlag(function.Flag)

		ic.SetVariable("c", a)
		ic.SetVariable("a", a)
		ic.SetVariable("b", b)
	}
}

<commit_msg>Fix crash.<commit_after>package method

import "github.com/qlova/ilang/src"
import "github.com/qlova/ilang/src/modules/function"

var Flag = ilang.NewFlag()
var New = ilang.NewFlag()

func init() {
	ilang.RegisterToken([]string{"method"}, ScanMethod)
	ilang.RegisterListener(New, NewEnd)

	ilang.RegisterDefault(func(ic *ilang.Compiler) bool {
		token := ic.LastToken
		if ic.GetFlag(Flag) {
			if _, ok := ic.LastDefinedType.Detail.Table[token]; ok {
				ic.NextToken = ic.LastDefinedType.Name
				ic.NextNextToken = "."
				ic.NextNextNextToken = token
				ic.ScanStatement()
				return true
			}
		}
		return false
	})

	ilang.RegisterExpression(func(ic *ilang.Compiler) string {
		token := ic.LastToken
		if ic.GetFlag(Flag) {
			if ic.TypeExists(token) && ic.LastDefinedType.Super == token {
	 			ic.ExpressionType = ic.DefinedTypes[ic.LastDefinedType.Super]
				return ic.Shunt(ic.LastDefinedType.Name)
		 	}

			//Scope methods with multiple arguments inside the method.
			//eg. method Package.dosomething(22)
			// in a Package method, dosomething(22) should be local.
			if _, ok  := ic.DefinedFunctions[token+"_m_"+ic.LastDefinedType.Name]; ok {
				var f = token+"_m_"+ic.LastDefinedType.Name

				if !ic.LastDefinedType.Empty() {
					ic.Assembly(ic.LastDefinedType.Push," ", ic.LastDefinedType.Name)
				}

				ic.ExpressionType = function.Flag

				return ic.Shunt(f)
			}
		}

		if ic.TypeExists(token) {
			if ic.DefinedTypes[token].Empty() && ic.Peek() == "." {
				ic.Scan('.')
				ic.ExpressionType = function.Flag
				var name = ic.Scan(ilang.Name)
				return name+"_m_"+token
			}
		}

	 	return ""
	})

	ilang.RegisterVariable(func(ic *ilang.Compiler, name string) ilang.Type {
		//Allow table values to be indexed in a method.
		if ic.GetFlag(Flag) && ic.LastDefinedType.Detail != nil {
			if _, ok := ic.LastDefinedType.Detail.Table[name]; ok {
				var value = ic.IndexUserType(ic.LastDefinedType.Name, name)
				ic.AssembleVar(name, value)
				ic.SetVariable(name+"_use", ilang.Used)
				return ic.ExpressionType
			}
		}
		return ilang.Undefined
	})

	ilang.RegisterShunt(".", ShuntMethodCall)
}

func ShuntMethodCall(ic *ilang.Compiler, name string) string {
	var index = ic.Scan(ilang.Name)

	if f, ok  := ic.DefinedFunctions[index+"_m_"+ic.ExpressionType.GetComplexName()]; ok && len(f.Args) > 0 {
		var f = index+"_m_"+ic.ExpressionType.GetComplexName()
		ic.Assembly(ic.ExpressionType.Push," ", name)
		ic.ExpressionType = function.Flag
		return ic.Shunt(f)
	}

	ic.NextToken = index
	return ""
}

func NewEnd(ic *ilang.Compiler) {
	ic.Assembly("SHARE ", ic.LastDefinedType.Name)
	ic.LoseScope()
}

func Call(ic *ilang.Compiler, name string, t ilang.Type) {
	ic.Assembly(ic.RunFunction(name+"_m_"+t.GetComplexName()))
}

func ScanMethod(ic *ilang.Compiler) {
	ic.Header = false

	var name string = ic.Scan(ilang.Name)

	f := ic.DefinedFunctions[name]
	f.Method = true
	ic.DefinedFunctions[name] = f

	/*if name == "new" {
		ic.Scan('(')
		ic.ScanNew()
		return
	}*/

	var MethodType = ic.LastDefinedType

	var token = ic.Scan(0)
	if token == "(" {
		token = ic.Scan(0)
		if token != ")" {
			if t, ok := ic.DefinedTypes[token]; ok {
				MethodType = t
			} else {
				ic.NextToken = token
			}
		}

		ic.LastDefinedType = MethodType

		if MethodType.Name == "Game" && name == "new" {
			ic.NewGame = true
		}
		if MethodType.Name == "Game" && name == "draw" {
			ic.DrawGame = true
		}
		if MethodType.Name == "Game" && name == "update" {
			ic.UpdateGame = true
		}

		name += "_m_"+MethodType.Name

		ic.Assembly("FUNCTION ", name)
		ic.GainScope()

		if name == "new_m_"+MethodType.Name {

			ic.Assembly("PUSH ", len(MethodType.Detail.Elements))
			ic.Assembly("MAKE")
			ic.Assembly("GRAB ", MethodType.Name)

			ic.SetVariable(MethodType.Name, MethodType)
			ic.SetVariable(MethodType.Name+"_use", ilang.Used)
			ic.SetVariable(MethodType.Name+".", ilang.Protected)

		} else if  MethodType.Detail != nil && len(MethodType.Detail.Elements) > 0 {
			ic.Assembly("%v %v", MethodType.Pop, MethodType.Name)
			ic.SetVariable(MethodType.Name, MethodType)
			ic.SetVariable(MethodType.Name+"_use", ilang.Used)
			ic.SetVariable(MethodType.Name+".", ilang.Protected)
		}

		function.CreateFromArguments(name, ic)
		ic.SetFlag(Flag)

		f = ic.DefinedFunctions[name]
		if name == "new_m_"+MethodType.Name {
			ic.GainScope()
			ic.SetFlag(New)
			f.Returns = []ilang.Type{MethodType}

		}

		f.Method = true
		ic.DefinedFunctions[name] = f

		ic.InsertPlugins(name)

	//Functional methods.
	} else if token == "." {

		if !ic.TypeExists(name) {
			ic.RaiseError("Undefined type: ", name)
		}

		t := ic.DefinedTypes[name]
		ic.LastDefinedType = t

		name = ic.Scan(ilang.Name)
		name += "_m_"+t.Name

		ic.Assembly("FUNCTION ", name)
		ic.GainScope()
		ic.Scan('(')

		function.CreateFromArguments(name, ic)

		if len(t.Detail.Elements) > 0 {
			ic.Assembly("%v %v", t.Pop, t.Name)
			ic.SetVariable(t.Name, t)
			ic.SetVariable(t.Name+"_use", ilang.Used)
		}

		f = ic.DefinedFunctions[name]
		ic.SetFlag(Flag)

		f.Method = true
		ic.DefinedFunctions[name] = f

		ic.InsertPlugins(name)

	} else {
		var symbol = token
		var other = ic.Scan(ilang.Name)
		ic.Scan('{')

		if t, ok := ic.DefinedTypes[name]; ok {
			MethodType = t
		}

		var a = MethodType

		MethodType = ic.DefinedTypes[other]

		ic.LastDefinedType = MethodType

		var b = MethodType

		ilang.NewOperator(a, symbol, b, "SHARE %a\n SHARE %b\nRUN "+a.Name+"_"+symbol+"_"+b.Name+"\nGRAB %c", true)

		ic.Assembly("FUNCTION %s_%s_%s\n", a.Name, symbol, b.Name)
		ic.GainScope()
		ic.Assembly("GRAB b\nGRAB a\nARRAY c\n")
		for range a.Detail.Elements {
			ic.Assembly("PUT 0\n")
		}
		ic.InOperatorFunction = true

		ic.SetFlag(function.Flag)

		ic.SetVariable("c", a)
		ic.SetVariable("a", a)
		ic.SetVariable("b", b)
	}
}

<|endoftext|>


<commit_before>package autoupdate

/*
Update sets are sets of packages generated by Gemnasium, aim to be test
in projects to determine if updates are going to pass.
These functions are meant to be used during CI tests.
*/

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/gemnasium/toolbelt/config"
	"github.com/gemnasium/toolbelt/gemnasium"
	"github.com/gemnasium/toolbelt/models"
	"github.com/gemnasium/toolbelt/utils"
)

const (
	AUTOUPDATE_MAX_DURATION = 3600
	UPDATE_SET_INVALID      = "invalid"
	UPDATE_SET_SUCCESS      = "test_passed"
	UPDATE_SET_FAIL         = "test_failed"
)

type RequirementUpdate struct {
	File  models.DependencyFile `json:"file"`
	Patch string                `json:"patch"`
}

type VersionUpdate struct {
	Package       models.Package
	OldVersion    string `json:"old_version"`
	TargetVersion string `json:"target_version"`
}

type UpdateSet struct {
	ID                 int                            `json:"id"`
	RequirementUpdates map[string][]RequirementUpdate `json:"requirement_updates"`
	VersionUpdates     map[string][]VersionUpdate     `json:"version_updates"`
}

type UpdateSetResult struct {
	UpdateSetID     int                     `json:"-"`
	ProjectSlug     string                  `json:"-"`
	State           string                  `json:"state"`
	DependencyFiles []models.DependencyFile `json:"dependency_files"`
}

var ErrProjectRevisionEmpty error = fmt.Errorf("The current revision (%s) is unknown on Gemnasium, please push your dependency files before running autoupdate.\nSee `gemnasium df help push`.\n", utils.GetCurrentRevision())

// Apply the best dependency files that have been found so far
func Apply(projectSlug string, testSuite []string) error {
	err := checkProject(projectSlug)
	if err != nil {
		return err
	}

	dfiles, err := fetchDependencyFiles(projectSlug)
	if err != nil {
		return err
	}

	err = updateDepFiles(dfiles)
	if err != nil {
		fmt.Printf("Error while restoring files: %s\n", err)
		return err
	}
	// No need to try the update, it will fail

	return nil
}

// Fetch the best dependency files that have been found so far
func fetchDependencyFiles(projectSlug string) (dfiles []models.DependencyFile, err error) {
	revision, err := getRevision()
	if err != nil {
		return nil, err
	}

	opts := &gemnasium.APIRequestOptions{
		Method: "GET",
		URI:    fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/best", projectSlug, revision),
		Result: &dfiles,
	}
	err = gemnasium.APIRequest(opts)
	return dfiles, err
}

// Update dependency files with given one (best dependency files)
// REFACTOR: this is very similar to restoreDepFiles
func updateDepFiles(dfiles []models.DependencyFile) error {
	fmt.Printf("%d file(s) to be updated.\n", len(dfiles))
	for _, df := range dfiles {
		fmt.Printf("Updating file %s: ", df.Path)
		err := ioutil.WriteFile(df.Path, df.Content, 0644)
		if err != nil {
			return err
		}
		fmt.Printf("done\n")
	}
	return nil
}

// Download and loop over update sets, apply changes, run test suite, and finally notify gemnasium
func Run(projectSlug string, testSuite []string) error {
	err := checkProject(projectSlug)
	if err != nil {
		return err
	}

	if envTS := os.Getenv(config.ENV_GEMNASIUM_TESTSUITE); envTS != "" {
		testSuite = strings.Fields(envTS)
	}
	if len(testSuite) == 0 {
		return errors.New("Arg [testSuite] can't be empty")
	}

	out, err := executeTestSuite(testSuite)
	if err != nil {
		fmt.Println("Aborting, initial test suite run is failing:")
		fmt.Printf("%s\n", out)
		return err
	}

	// We'll be checking loop duration on each iteration
	startTime := time.Now()
	// Loop until tests are green
	for {
		if time.Since(startTime).Seconds() > AUTOUPDATE_MAX_DURATION {
			fmt.Println("Max loop duration reached, aborting.")
			break
		}
		updateSet, err := fetchUpdateSet(projectSlug)
		if err != nil {
			return err
		}
		if updateSet.ID == 0 {
			fmt.Println("Job done!")
			break
		}
		fmt.Printf("\n========= [UpdateSet #%d] =========\n", updateSet.ID)

		// We have an updateSet, let's patch files and run tests
		// We need to keep a list of updated files to restore them after this run
		orgDepFiles, uptDepFiles, err := applyUpdateSet(updateSet)
		resultSet := &UpdateSetResult{UpdateSetID: updateSet.ID, ProjectSlug: projectSlug, DependencyFiles: uptDepFiles}
		if err == cantInstallRequirements || err == cantUpdateVersions {
			resultSet.State = UPDATE_SET_INVALID
			err := pushUpdateSetResult(resultSet)
			if err != nil {
				return err
			}

			err = restoreDepFiles(orgDepFiles)
			if err != nil {
				fmt.Printf("Error while restoring files: %s\n", err)
			}
			// No need to try the update, it will fail
			continue
		}
		if err != nil {
			return err
		}

		out, err := executeTestSuite(testSuite)
		if err == nil {
			// we found a valid candidate
			resultSet.State = UPDATE_SET_SUCCESS
			err := pushUpdateSetResult(resultSet)
			if err != nil {
				return err
			}

			err = restoreDepFiles(orgDepFiles)
			if err != nil {
				return err
			}

			continue
		}
		// display cmd output
		fmt.Printf("%s\n", out)
		resultSet.State = UPDATE_SET_FAIL
		err = pushUpdateSetResult(resultSet)
		if err != nil {
			return err
		}
		err = restoreDepFiles(orgDepFiles)
		if err != nil {
			fmt.Printf("Error while restoring files: %s\n", err)
		}
		// Let's continue with another set
	}
	return nil
}

func fetchUpdateSet(projectSlug string) (*UpdateSet, error) {
	revision, revision_err := getRevision()
	if revision_err != nil {
		return nil, revision_err
	}

	var updateSet *UpdateSet
	opts := &gemnasium.APIRequestOptions{
		Method: "POST",
		URI:    fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/next", projectSlug, revision),
		Result: &updateSet,
	}
	err := gemnasium.APIRequest(opts)
	if err != nil {
		return nil, err
	}

	return updateSet, nil
}

// Patch files if needed, and update packages
// Will return a slice of original files and a slice of the updated files, with
// their content
func applyUpdateSet(updateSet *UpdateSet) (orgDepFiles, uptDepFiles []models.DependencyFile, err error) {
	for packageType, reqUpdates := range updateSet.RequirementUpdates {
		installer, err := NewRequirementsInstaller(packageType)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}

		err = installer(reqUpdates, &orgDepFiles, &uptDepFiles)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}
	}

	for packageType, versionUpdates := range updateSet.VersionUpdates {
		// Update Versions
		updater, err := NewUpdater(packageType)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}
		err = updater(versionUpdates, &orgDepFiles, &uptDepFiles)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}
	}
	fmt.Println("Done")
	return orgDepFiles, uptDepFiles, nil
}

// Once update set has been tested, we must send the result to Gemnasium,
// in order to update statitics.
func pushUpdateSetResult(rs *UpdateSetResult) error {
	fmt.Printf("Pushing result (status='%s'): ", rs.State)

	if rs.UpdateSetID == 0 || rs.State == "" {
		return errors.New("Missing updateSet ID and/or State args")
	}

	revision, revision_err := getRevision()
	if revision_err != nil {
		return revision_err
	}

	opts := &gemnasium.APIRequestOptions{
		Method: "PATCH",
		URI:    fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/%d", rs.ProjectSlug, revision, rs.UpdateSetID),
		Body:   rs,
	}
	err := gemnasium.APIRequest(opts)
	if err != nil {
		return err
	}

	fmt.Printf("done\n")
	return nil
}

// Restore original files.
// Needed after each run
func restoreDepFiles(dfiles []models.DependencyFile) error {
	fmt.Printf("%d file(s) to be restored.\n", len(dfiles))
	for _, df := range dfiles {
		fmt.Printf("Restoring file %s: ", df.Path)
		err := ioutil.WriteFile(df.Path, df.Content, 0644)
		if err != nil {
			return err
		}
		fmt.Printf("done\n")
	}
	return nil
}

func executeTestSuite(ts []string) ([]byte, error) {
	type Result struct {
		Output []byte
		Err    error
	}
	done := make(chan Result)
	defer close(done)
	var out []byte
	var err error
	fmt.Printf("Executing test script: ")
	start := time.Now()
	go func() {
		result, err := exec.Command(ts[0], ts[1:]...).Output()
		done <- Result{result, err}
	}()
	var stop bool
	for {
		select {
		case result := <-done:
			stop = true
			out = result.Output
			err = result.Err
		default:
			fmt.Print(".")
			time.Sleep(1 * time.Second)
		}
		if stop {
			break
		}
	}
	fmt.Printf("done (%fs)\n", time.Since(start).Seconds())
	return out, err
}

func checkProject(slug string) error {
	p := &models.Project{Slug: slug}
	err := p.Fetch()
	if err != nil {
		return err
	}
	if p.CommitSHA == "" {
		return ErrProjectRevisionEmpty
	}
	return nil
}

func getRevision() (string, error) {
	revision := utils.GetCurrentRevision()
	if revision == "" {
		return revision, errors.New("Can't determine current revision, please use REVISION env var to specify it")
	}
	return revision, nil
}
<commit_msg>cleanup autoupdate<commit_after>package autoupdate

/*
Update sets are sets of packages generated by Gemnasium, aim to be test
in projects to determine if updates are going to pass.
These functions are meant to be used during CI tests.
*/

import (
	"errors"
	"fmt"
	"io/ioutil"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/gemnasium/toolbelt/config"
	"github.com/gemnasium/toolbelt/gemnasium"
	"github.com/gemnasium/toolbelt/models"
	"github.com/gemnasium/toolbelt/utils"
)

const (
	AUTOUPDATE_MAX_DURATION = 3600
	UPDATE_SET_INVALID      = "invalid"
	UPDATE_SET_SUCCESS      = "test_passed"
	UPDATE_SET_FAIL         = "test_failed"
)

type RequirementUpdate struct {
	File  models.DependencyFile `json:"file"`
	Patch string                `json:"patch"`
}

type VersionUpdate struct {
	Package       models.Package
	OldVersion    string `json:"old_version"`
	TargetVersion string `json:"target_version"`
}

type UpdateSet struct {
	ID                 int                            `json:"id"`
	RequirementUpdates map[string][]RequirementUpdate `json:"requirement_updates"`
	VersionUpdates     map[string][]VersionUpdate     `json:"version_updates"`
}

type UpdateSetResult struct {
	UpdateSetID     int                     `json:"-"`
	ProjectSlug     string                  `json:"-"`
	State           string                  `json:"state"`
	DependencyFiles []models.DependencyFile `json:"dependency_files"`
}

var ErrProjectRevisionEmpty error = fmt.Errorf("The current revision (%s) is unknown on Gemnasium, please push your dependency files before running autoupdate.\nSee `gemnasium df help push`.\n", utils.GetCurrentRevision())

// Apply the best dependency files that have been found so far
func Apply(projectSlug string, testSuite []string) error {
	err := checkProject(projectSlug)
	if err != nil {
		return err
	}

	dfiles, err := fetchDependencyFiles(projectSlug)
	if err != nil {
		return err
	}

	err = updateDepFiles(dfiles)
	if err != nil {
		fmt.Printf("Error while restoring files: %s\n", err)
		return err
	}
	// No need to try the update, it will fail

	return nil
}

// Fetch the best dependency files that have been found so far
func fetchDependencyFiles(projectSlug string) (dfiles []models.DependencyFile, err error) {
	revision, err := getRevision()
	if err != nil {
		return nil, err
	}

	opts := &gemnasium.APIRequestOptions{
		Method: "GET",
		URI:    fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/best", projectSlug, revision),
		Result: &dfiles,
	}
	err = gemnasium.APIRequest(opts)
	return dfiles, err
}

// Update dependency files with given one (best dependency files)
// REFACTOR: this is very similar to restoreDepFiles
func updateDepFiles(dfiles []models.DependencyFile) error {
	fmt.Printf("%d file(s) to be updated.\n", len(dfiles))
	for _, df := range dfiles {
		fmt.Printf("Updating file %s: ", df.Path)
		err := ioutil.WriteFile(df.Path, df.Content, 0644)
		if err != nil {
			return err
		}
		fmt.Printf("done\n")
	}
	return nil
}

// Download and loop over update sets, apply changes, run test suite, and finally notify gemnasium
func Run(projectSlug string, testSuite []string) error {
	err := checkProject(projectSlug)
	if err != nil {
		return err
	}

	if envTS := os.Getenv(config.ENV_GEMNASIUM_TESTSUITE); envTS != "" {
		testSuite = strings.Fields(envTS)
	}
	if len(testSuite) == 0 {
		return errors.New("Arg [testSuite] can't be empty")
	}

	out, err := executeTestSuite(testSuite)
	if err != nil {
		fmt.Println("Aborting, initial test suite run is failing:")
		fmt.Printf("%s\n", out)
		return err
	}

	// We'll be checking loop duration on each iteration
	startTime := time.Now()
	// Loop until tests are green
	for {
		if time.Since(startTime).Seconds() > AUTOUPDATE_MAX_DURATION {
			fmt.Println("Max loop duration reached, aborting.")
			break
		}
		updateSet, err := fetchUpdateSet(projectSlug)
		if err != nil {
			return err
		}
		if updateSet.ID == 0 {
			fmt.Println("Job done!")
			break
		}
		fmt.Printf("\n========= [UpdateSet #%d] =========\n", updateSet.ID)

		// We have an updateSet, let's patch files and run tests
		// We need to keep a list of updated files to restore them after this run
		orgDepFiles, uptDepFiles, err := applyUpdateSet(updateSet)
		resultSet := &UpdateSetResult{UpdateSetID: updateSet.ID, ProjectSlug: projectSlug, DependencyFiles: uptDepFiles}
		if err == cantInstallRequirements || err == cantUpdateVersions {
			resultSet.State = UPDATE_SET_INVALID
			err := pushUpdateSetResult(resultSet)
			if err != nil {
				return err
			}

			err = restoreDepFiles(orgDepFiles)
			if err != nil {
				fmt.Printf("Error while restoring files: %s\n", err)
			}
			// No need to try the update, it will fail
			continue
		}
		if err != nil {
			return err
		}

		out, err := executeTestSuite(testSuite)
		if err == nil {
			// we found a valid candidate
			resultSet.State = UPDATE_SET_SUCCESS
			err := pushUpdateSetResult(resultSet)
			if err != nil {
				return err
			}

			err = restoreDepFiles(orgDepFiles)
			if err != nil {
				return err
			}

			continue
		}
		// display cmd output
		fmt.Printf("%s\n", out)
		resultSet.State = UPDATE_SET_FAIL
		err = pushUpdateSetResult(resultSet)
		if err != nil {
			return err
		}
		err = restoreDepFiles(orgDepFiles)
		if err != nil {
			fmt.Printf("Error while restoring files: %s\n", err)
		}
		// Let's continue with another set
	}
	return nil
}

func fetchUpdateSet(projectSlug string) (*UpdateSet, error) {
	revision, err := getRevision()
	if err != nil {
		return nil, err
	}

	var updateSet *UpdateSet
	opts := &gemnasium.APIRequestOptions{
		Method: "POST",
		URI:    fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/next", projectSlug, revision),
		Result: &updateSet,
	}
	err = gemnasium.APIRequest(opts)
	return updateSet, err
}

// Patch files if needed, and update packages
// Will return a slice of original files and a slice of the updated files, with
// their content
func applyUpdateSet(updateSet *UpdateSet) (orgDepFiles, uptDepFiles []models.DependencyFile, err error) {
	for packageType, reqUpdates := range updateSet.RequirementUpdates {
		installer, err := NewRequirementsInstaller(packageType)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}

		err = installer(reqUpdates, &orgDepFiles, &uptDepFiles)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}
	}

	for packageType, versionUpdates := range updateSet.VersionUpdates {
		// Update Versions
		updater, err := NewUpdater(packageType)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}
		err = updater(versionUpdates, &orgDepFiles, &uptDepFiles)
		if err != nil {
			return orgDepFiles, uptDepFiles, err
		}
	}
	fmt.Println("Done")
	return orgDepFiles, uptDepFiles, nil
}

// Once update set has been tested, we must send the result to Gemnasium,
// in order to update statitics.
func pushUpdateSetResult(rs *UpdateSetResult) error {
	fmt.Printf("Pushing result (status='%s'): ", rs.State)

	if rs.UpdateSetID == 0 || rs.State == "" {
		return errors.New("Missing updateSet ID and/or State args")
	}

	revision, err := getRevision()
	if err != nil {
		return err
	}

	opts := &gemnasium.APIRequestOptions{
		Method: "PATCH",
		URI:    fmt.Sprintf("/projects/%s/revisions/%s/auto_update_steps/%d", rs.ProjectSlug, revision, rs.UpdateSetID),
		Body:   rs,
	}
	err = gemnasium.APIRequest(opts)
	if err != nil {
		return err
	}

	fmt.Printf("done\n")
	return nil
}

// Restore original files.
// Needed after each run
func restoreDepFiles(dfiles []models.DependencyFile) error {
	fmt.Printf("%d file(s) to be restored.\n", len(dfiles))
	for _, df := range dfiles {
		fmt.Printf("Restoring file %s: ", df.Path)
		err := ioutil.WriteFile(df.Path, df.Content, 0644)
		if err != nil {
			return err
		}
		fmt.Printf("done\n")
	}
	return nil
}

func executeTestSuite(ts []string) ([]byte, error) {
	type Result struct {
		Output []byte
		Err    error
	}
	done := make(chan Result)
	defer close(done)
	var out []byte
	var err error
	fmt.Printf("Executing test script: ")
	start := time.Now()
	go func() {
		result, err := exec.Command(ts[0], ts[1:]...).Output()
		done <- Result{result, err}
	}()
	var stop bool
	for {
		select {
		case result := <-done:
			stop = true
			out = result.Output
			err = result.Err
		default:
			fmt.Print(".")
			time.Sleep(1 * time.Second)
		}
		if stop {
			break
		}
	}
	fmt.Printf("done (%fs)\n", time.Since(start).Seconds())
	return out, err
}

func checkProject(slug string) error {
	p := &models.Project{Slug: slug}
	err := p.Fetch()
	if err != nil {
		return err
	}
	if p.CommitSHA == "" {
		return ErrProjectRevisionEmpty
	}
	return nil
}

func getRevision() (string, error) {
	revision := utils.GetCurrentRevision()
	if revision == "" {
		return revision, errors.New("Can't determine current revision, please use REVISION env var to specify it")
	}
	return revision, nil
}
<|endoftext|>


<commit_before>// Package tl provides TeamLiquid API wrappers
package tl

import (
	"context"
	"sort"
	"strconv"
	"time"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	cache "github.com/patrickmn/go-cache"
	"github.com/robfig/cron"
	"github.com/scbizu/Astral/talker"
	"github.com/sirupsen/logrus"
)

const (
	timelineCacheKey = "timelines"
)

type timelines []int64

func (t timelines) getTheLastestTimeline() int64 {
	if len(t) == 0 {
		return 0
	}
	sort.SliceStable([]int64(t), func(i int, j int) bool {
		return []int64(t)[i] < []int64(t)[j]
	})
	return []int64(t)[0]
}

var (
	matchCache = cache.New(6*time.Hour, 12*time.Hour)
)

type mCron struct {
	ctx context.Context
	c   *cron.Cron
}

func NewCron() *mCron {
	return &mCron{
		ctx: context.TODO(),
		c:   cron.New(),
	}
}

type Fetcher struct {
	c     *mCron
	cache *cache.Cache
	Bot   *tgbotapi.BotAPI
}

func NewFetcher(bot *tgbotapi.BotAPI) *Fetcher {
	return &Fetcher{
		c:     new(mCron),
		cache: matchCache,
		Bot:   bot,
	}
}

func (f *Fetcher) Do() error {
	f.c = NewCron()
	f.c.c.AddFunc("@every 1m", func() {
		if f.cache.ItemCount() > 0 {
			now := time.Now()
			timeLines, ok := f.cache.Get(timelineCacheKey)
			if !ok {
				logrus.Errorf("get timeline cache failed: %s", "no cache key")
				return
			}
			cn, err := time.LoadLocation("Asia/Shanghai")
			if err != nil {
				logrus.Errorf("tl load location failed: %s", err.Error())
				return
			}
			timeLineInts, ok := timeLines.(timelines)
			if !ok {
				logrus.Error("convert timeline interface{} -> []int{} failed")
				return
			}
			if now.In(cn).Unix() < timeLineInts.getTheLastestTimeline() {
				logrus.Infof("now is %d, the lasted match is at %d, no need to refresh cache.",
					now.In(cn).Unix(), timeLineInts.getTheLastestTimeline())
				return
			}
		}
		logrus.Infof("warming TL cache...")
		if err := f.refreshCache(); err != nil {
			logrus.Errorf("refresh cache failed: %s", err.Error())
		}
	})
	f.c.c.Start()
	return nil
}

func (f *Fetcher) refreshCache() error {
	f.expireAllMatches()
	p, err := NewMatchParser()
	if err != nil {
		return err
	}
	timelines, err := p.GetTimelines()
	if err != nil {
		return err
	}
	f.cache.Set(timelineCacheKey, timelines, -1)
	matches, err := p.GetTimeMatches()
	if err != nil {
		return err
	}

	go f.pushMSG(timelines, matches)

	for t, m := range matches {
		f.cache.Set(strconv.FormatInt(t, 10), m, -1)
	}
	return nil
}

func (f *Fetcher) expireAllMatches() {
	f.cache.Flush()
}

func (f *Fetcher) pushMSG(tls []int64, matches map[int64][]Match) {
	sort.SliceStable(tls, func(i, j int) bool {
		return tls[i] < tls[j]
	})

	var sortedMatches []string
	for _, tl := range tls {
		ms, ok := matches[tl]
		if !ok {
			continue
		}
		for _, m := range ms {
			sortedMatches = append(sortedMatches, m.GetMDMatchInfo())
		}
	}
	f.pushWithLimit(sortedMatches, 5)
}

func (f *Fetcher) pushWithLimit(matches []string, limit int) {
	splitMatches := split(matches, limit)
	for _, sm := range splitMatches {
		matchPush := talker.NewMatchPush(sm)
		f.Bot.Send(matchPush.GetPushMessage())
	}
}

func split(buf []string, lim int) [][]string {
	var chunk []string
	chunks := make([][]string, 0, len(buf)/lim+1)
	for len(buf) >= lim {
		chunk, buf = buf[:lim], buf[lim:]
		chunks = append(chunks, chunk)
	}
	if len(buf) > 0 {
		chunks = append(chunks, buf[:len(buf)])
	}
	return chunks
}
<commit_msg>tl: fix store timeline format<commit_after>// Package tl provides TeamLiquid API wrappers
package tl

import (
	"context"
	"encoding/json"
	"sort"
	"strconv"
	"time"

	tgbotapi "github.com/go-telegram-bot-api/telegram-bot-api"
	cache "github.com/patrickmn/go-cache"
	"github.com/robfig/cron"
	"github.com/scbizu/Astral/talker"
	"github.com/sirupsen/logrus"
)

const (
	timelineCacheKey = "timelines"
)

type timelines []int64

func (t timelines) getTheLastestTimeline() int64 {
	if len(t) == 0 {
		return 0
	}
	sort.SliceStable([]int64(t), func(i int, j int) bool {
		return []int64(t)[i] < []int64(t)[j]
	})
	return []int64(t)[0]
}

var (
	matchCache = cache.New(6*time.Hour, 12*time.Hour)
)

type mCron struct {
	ctx context.Context
	c   *cron.Cron
}

func NewCron() *mCron {
	return &mCron{
		ctx: context.TODO(),
		c:   cron.New(),
	}
}

type Fetcher struct {
	c     *mCron
	cache *cache.Cache
	Bot   *tgbotapi.BotAPI
}

func NewFetcher(bot *tgbotapi.BotAPI) *Fetcher {
	return &Fetcher{
		c:     new(mCron),
		cache: matchCache,
		Bot:   bot,
	}
}

func (f *Fetcher) Do() error {
	f.c = NewCron()
	f.c.c.AddFunc("@every 1m", func() {
		if f.cache.ItemCount() > 0 {
			now := time.Now()
			timeLines, ok := f.cache.Get(timelineCacheKey)
			if !ok {
				logrus.Errorf("get timeline cache failed: %s", "no cache key")
				return
			}
			cn, err := time.LoadLocation("Asia/Shanghai")
			if err != nil {
				logrus.Errorf("tl load location failed: %s", err.Error())
				return
			}
			t := new(timelines)
			if err = json.Unmarshal([]byte(timeLines.(string)), t); err != nil {
				return
			}
			if now.In(cn).Unix() < t.getTheLastestTimeline() {
				logrus.Infof("now is %d, the lasted match is at %d, no need to refresh cache.",
					now.In(cn).Unix(), t.getTheLastestTimeline())
				return
			}
		}

		logrus.Infof("warming TL cache...")
		if err := f.refreshCache(); err != nil {
			logrus.Errorf("refresh cache failed: %s", err.Error())
		}
	})
	f.c.c.Start()
	return nil
}

func (f *Fetcher) refreshCache() error {
	f.expireAllMatches()
	p, err := NewMatchParser()
	if err != nil {
		return err
	}
	timelines, err := p.GetTimelines()
	if err != nil {
		return err
	}
	tlJSON, err := json.Marshal(timelines)
	if err != nil {
		return err
	}
	f.cache.Set(timelineCacheKey, tlJSON, -1)
	matches, err := p.GetTimeMatches()
	if err != nil {
		return err
	}

	go f.pushMSG(timelines, matches)

	for t, m := range matches {
		f.cache.Set(strconv.FormatInt(t, 10), m, -1)
	}
	return nil
}

func (f *Fetcher) expireAllMatches() {
	f.cache.Flush()
}

func (f *Fetcher) pushMSG(tls []int64, matches map[int64][]Match) {
	sort.SliceStable(tls, func(i, j int) bool {
		return tls[i] < tls[j]
	})

	var sortedMatches []string
	for _, tl := range tls {
		ms, ok := matches[tl]
		if !ok {
			continue
		}
		for _, m := range ms {
			sortedMatches = append(sortedMatches, m.GetMDMatchInfo())
		}
	}
	f.pushWithLimit(sortedMatches, 5)
}

func (f *Fetcher) pushWithLimit(matches []string, limit int) {
	splitMatches := split(matches, limit)
	for _, sm := range splitMatches {
		matchPush := talker.NewMatchPush(sm)
		f.Bot.Send(matchPush.GetPushMessage())
	}
}

func split(buf []string, lim int) [][]string {
	var chunk []string
	chunks := make([][]string, 0, len(buf)/lim+1)
	for len(buf) >= lim {
		chunk, buf = buf[:lim], buf[lim:]
		chunks = append(chunks, chunk)
	}
	if len(buf) > 0 {
		chunks = append(chunks, buf[:len(buf)])
	}
	return chunks
}
<|endoftext|>


<commit_before>package lib

import (
	"fmt"
	"io"
	"io/ioutil"
	"os"

	"github.com/flavioribeiro/gonfig"
)

// GetLogOutput returns the output we want to use
// for http requests log
func GetLogOutput() io.Writer {
	var logOutput io.Writer
	currentDir, _ := os.Getwd()
	cfg, _ := gonfig.FromJsonFile(currentDir + "/config.json")
	logfile, _ := cfg.GetString("LOGFILE", "")
	if logfile == "" {
		logOutput = ioutil.Discard
	} else {
		fmt.Println("Logging requests on", logfile)
		f, err := os.Create(logfile)
		if err != nil {
			panic(err)
		}

		logOutput = f
	}

	return logOutput
}
<commit_msg>default to Stderr<commit_after>package lib

import (
	"fmt"
	"io"
	"os"

	"github.com/flavioribeiro/gonfig"
)

// GetLogOutput returns the output we want to use
// for http requests log
func GetLogOutput() io.Writer {
	var logOutput io.Writer
	currentDir, _ := os.Getwd()
	cfg, _ := gonfig.FromJsonFile(currentDir + "/config.json")
	logfile, _ := cfg.GetString("LOGFILE", "")
	if logfile == "" {
		logOutput = os.Stderr
	} else {
		fmt.Println("Logging requests on", logfile)
		f, err := os.Create(logfile)
		if err != nil {
			panic(err)
		}

		logOutput = f
	}

	return logOutput
}
<|endoftext|>


<commit_before>// Package ae provides tools to synchronize state between local and remote consul servers.
package ae

import (
	"errors"
	"log"
	"math"
	"sync"
	"time"

	"github.com/hashicorp/consul/lib"
)

// scaleThreshold is the number of nodes after which regular sync runs are
// spread out farther apart. The value should be a power of 2 since the
// scale function uses log2.
//
// When set to 128 nodes the delay between regular runs is doubled when the
// cluster is larger than 128 nodes. It doubles again when it passes 256
// nodes, and again at 512 nodes and so forth. At 8192 nodes, the delay
// factor is 8.
//
// If you update this, you may need to adjust the tuning of
// CoordinateUpdatePeriod and CoordinateUpdateMaxBatchSize.
const scaleThreshold = 128

// scaleFactor returns a factor by which the next sync run should be delayed to
// avoid saturation of the cluster. The larger the cluster grows the farther
// the sync runs should be spread apart.
//
// The current implementation uses a log2 scale which doubles the delay between
// runs every time the cluster doubles in size.
func scaleFactor(nodes int) int {
	if nodes <= scaleThreshold {
		return 1.0
	}
	return int(math.Ceil(math.Log2(float64(nodes))-math.Log2(float64(scaleThreshold))) + 1.0)
}

type State interface {
	SyncChanges() error
	SyncFull() error
}

// StateSyncer manages background synchronization of the given state.
//
// The state is synchronized on a regular basis or on demand when either
// the state has changed or a new Consul server has joined the cluster.
//
// The regular state sychronization provides a self-healing mechanism
// for the cluster which is also called anti-entropy.
type StateSyncer struct {
	// State contains the data that needs to be synchronized.
	State State

	// Interval is the time between two regular sync runs.
	Interval time.Duration

	// ShutdownCh is closed when the application is shutting down.
	ShutdownCh chan struct{}

	// Logger is the logger.
	Logger *log.Logger

	// ClusterSize returns the number of members in the cluster to
	// allow staggering the sync runs based on cluster size.
	// This needs to be set before Run() is called.
	ClusterSize func() int

	// SyncFull allows triggering an immediate but staggered full sync
	// in a non-blocking way.
	SyncFull *Trigger

	// SyncChanges allows triggering an immediate partial sync
	// in a non-blocking way.
	SyncChanges *Trigger

	// paused stores whether sync runs are temporarily disabled.
	pauseLock sync.Mutex
	paused    bool
}

func NewStateSyner(state State, intv time.Duration, shutdownCh chan struct{}, logger *log.Logger) *StateSyncer {
	return &StateSyncer{
		State:       state,
		Interval:    intv,
		ShutdownCh:  shutdownCh,
		Logger:      logger,
		SyncFull:    NewTrigger(),
		SyncChanges: NewTrigger(),
	}
}

const (
	// serverUpIntv is the max time to wait before a sync is triggered
	// when a consul server has been added to the cluster.
	serverUpIntv = 3 * time.Second

	// retryFailIntv is the min time to wait before a failed sync is retried.
	retryFailIntv = 15 * time.Second
)

var errPaused = errors.New("paused")

// Run is the long running method to perform state synchronization
// between local and remote servers.
func (s *StateSyncer) Run() {
	if s.ClusterSize == nil {
		panic("ClusterSize not set")
	}

	stagger := func(d time.Duration) time.Duration {
		f := scaleFactor(s.ClusterSize())
		return lib.RandomStagger(time.Duration(f) * d)
	}

FullSync:
	for {
		// attempt a full sync
		err := s.ifNotPausedRun(s.State.SyncFull)
		if err != nil {
			if err != errPaused {
				s.Logger.Printf("[ERR] agent: failed to sync remote state: %v", err)
			}

			select {
			// trigger a full sync immediately.
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
				case <-s.ShutdownCh:
					return
				}

			// retry full sync after some time
			// todo(fs): why don't we use s.Interval here?
			case <-time.After(retryFailIntv + stagger(retryFailIntv)):

			case <-s.ShutdownCh:
				return
			}

			continue
		}

		// do partial syncs until it is time for a full sync again
		for {
			select {
			// trigger a full sync immediately
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
					continue FullSync
				case <-s.ShutdownCh:
					return
				}

			// time for a full sync again
			case <-time.After(s.Interval + stagger(s.Interval)):
				continue FullSync

			// do partial syncs on demand
			case <-s.SyncChanges.Notif():
				err := s.ifNotPausedRun(s.State.SyncChanges)
				if err != nil && err != errPaused {
					s.Logger.Printf("[ERR] agent: failed to sync changes: %v", err)
				}

			case <-s.ShutdownCh:
				return
			}
		}
	}
}

func (s *StateSyncer) ifNotPausedRun(f func() error) error {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	if s.paused {
		return errPaused
	}
	return f()
}

// Pause temporarily disables sync runs.
func (s *StateSyncer) Pause() {
	s.pauseLock.Lock()
	if s.paused {
		panic("pause while paused")
	}
	s.paused = true
	s.pauseLock.Unlock()
}

// Paused returns whether sync runs are temporarily disabled.
func (s *StateSyncer) Paused() bool {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	return s.paused
}

// Resume re-enables sync runs.
func (s *StateSyncer) Resume() {
	s.pauseLock.Lock()
	if !s.paused {
		panic("resume while not paused")
	}
	s.paused = false
	s.pauseLock.Unlock()
	s.SyncChanges.Trigger()
}
<commit_msg>ae: restore previous pause/resume behavior<commit_after>// Package ae provides tools to synchronize state between local and remote consul servers.
package ae

import (
	"errors"
	"log"
	"math"
	"sync"
	"time"

	"github.com/hashicorp/consul/lib"
)

// scaleThreshold is the number of nodes after which regular sync runs are
// spread out farther apart. The value should be a power of 2 since the
// scale function uses log2.
//
// When set to 128 nodes the delay between regular runs is doubled when the
// cluster is larger than 128 nodes. It doubles again when it passes 256
// nodes, and again at 512 nodes and so forth. At 8192 nodes, the delay
// factor is 8.
//
// If you update this, you may need to adjust the tuning of
// CoordinateUpdatePeriod and CoordinateUpdateMaxBatchSize.
const scaleThreshold = 128

// scaleFactor returns a factor by which the next sync run should be delayed to
// avoid saturation of the cluster. The larger the cluster grows the farther
// the sync runs should be spread apart.
//
// The current implementation uses a log2 scale which doubles the delay between
// runs every time the cluster doubles in size.
func scaleFactor(nodes int) int {
	if nodes <= scaleThreshold {
		return 1.0
	}
	return int(math.Ceil(math.Log2(float64(nodes))-math.Log2(float64(scaleThreshold))) + 1.0)
}

type State interface {
	SyncChanges() error
	SyncFull() error
}

// StateSyncer manages background synchronization of the given state.
//
// The state is synchronized on a regular basis or on demand when either
// the state has changed or a new Consul server has joined the cluster.
//
// The regular state sychronization provides a self-healing mechanism
// for the cluster which is also called anti-entropy.
type StateSyncer struct {
	// State contains the data that needs to be synchronized.
	State State

	// Interval is the time between two regular sync runs.
	Interval time.Duration

	// ShutdownCh is closed when the application is shutting down.
	ShutdownCh chan struct{}

	// Logger is the logger.
	Logger *log.Logger

	// ClusterSize returns the number of members in the cluster to
	// allow staggering the sync runs based on cluster size.
	// This needs to be set before Run() is called.
	ClusterSize func() int

	// SyncFull allows triggering an immediate but staggered full sync
	// in a non-blocking way.
	SyncFull *Trigger

	// SyncChanges allows triggering an immediate partial sync
	// in a non-blocking way.
	SyncChanges *Trigger

	// paused stores whether sync runs are temporarily disabled.
	pauseLock sync.Mutex
	paused    int
}

func NewStateSyner(state State, intv time.Duration, shutdownCh chan struct{}, logger *log.Logger) *StateSyncer {
	return &StateSyncer{
		State:       state,
		Interval:    intv,
		ShutdownCh:  shutdownCh,
		Logger:      logger,
		SyncFull:    NewTrigger(),
		SyncChanges: NewTrigger(),
	}
}

const (
	// serverUpIntv is the max time to wait before a sync is triggered
	// when a consul server has been added to the cluster.
	serverUpIntv = 3 * time.Second

	// retryFailIntv is the min time to wait before a failed sync is retried.
	retryFailIntv = 15 * time.Second
)

var errPaused = errors.New("paused")

// Run is the long running method to perform state synchronization
// between local and remote servers.
func (s *StateSyncer) Run() {
	if s.ClusterSize == nil {
		panic("ClusterSize not set")
	}

	stagger := func(d time.Duration) time.Duration {
		f := scaleFactor(s.ClusterSize())
		return lib.RandomStagger(time.Duration(f) * d)
	}

FullSync:
	for {
		// attempt a full sync
		err := s.ifNotPausedRun(s.State.SyncFull)
		if err != nil {
			if err != errPaused {
				s.Logger.Printf("[ERR] agent: failed to sync remote state: %v", err)
			}

			select {
			// trigger a full sync immediately.
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
				case <-s.ShutdownCh:
					return
				}

			// retry full sync after some time
			// todo(fs): why don't we use s.Interval here?
			case <-time.After(retryFailIntv + stagger(retryFailIntv)):

			case <-s.ShutdownCh:
				return
			}

			continue
		}

		// do partial syncs until it is time for a full sync again
		for {
			select {
			// trigger a full sync immediately
			// this is usually called when a consul server was added to the cluster.
			// stagger the delay to avoid a thundering herd.
			case <-s.SyncFull.Notif():
				select {
				case <-time.After(stagger(serverUpIntv)):
					continue FullSync
				case <-s.ShutdownCh:
					return
				}

			// time for a full sync again
			case <-time.After(s.Interval + stagger(s.Interval)):
				continue FullSync

			// do partial syncs on demand
			case <-s.SyncChanges.Notif():
				err := s.ifNotPausedRun(s.State.SyncChanges)
				if err != nil && err != errPaused {
					s.Logger.Printf("[ERR] agent: failed to sync changes: %v", err)
				}

			case <-s.ShutdownCh:
				return
			}
		}
	}
}

func (s *StateSyncer) ifNotPausedRun(f func() error) error {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	if s.paused != 0 {
		return errPaused
	}
	return f()
}

// Pause temporarily disables sync runs.
func (s *StateSyncer) Pause() {
	s.pauseLock.Lock()
	s.paused++
	s.pauseLock.Unlock()
}

// Paused returns whether sync runs are temporarily disabled.
func (s *StateSyncer) Paused() bool {
	s.pauseLock.Lock()
	defer s.pauseLock.Unlock()
	return s.paused != 0
}

// Resume re-enables sync runs.
func (s *StateSyncer) Resume() {
	s.pauseLock.Lock()
	s.paused--
	if s.paused < 0 {
		panic("unbalanced pause/resume")
	}
	if s.paused == 0 {
		s.SyncChanges.Trigger()
	}
	s.pauseLock.Unlock()
}
<|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 docker

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh/terminal"
	"encoding/json"
	"fmt"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/errors"
	tsuruIo "github.com/tsuru/tsuru/io"
	"io"
	"launchpad.net/gnuflag"
	"net"
	"net/http"
	"net/url"
	"os"
	"regexp"
	"strconv"
	"strings"
)

var httpHeaderRegexp = regexp.MustCompile(`HTTP/.*? (\d+)`)

type moveContainersCmd struct{}

type progressFormatter struct{}

func (progressFormatter) Format(out io.Writer, data []byte) error {
	var logEntry progressLog
	err := json.Unmarshal(data, &logEntry)
	if err != nil {
		return err
	}
	fmt.Fprintf(out, "%s\n", logEntry.Message)
	return nil
}

func (c *moveContainersCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "containers-move",
		Usage:   "containers-move <from host> <to host>",
		Desc:    "Move all containers from one host to another.\nThis command is especially useful for host maintenance.",
		MinArgs: 2,
	}
}

func (c *moveContainersCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/containers/move")
	if err != nil {
		return err
	}
	params := map[string]string{
		"from": context.Args[0],
		"to":   context.Args[1],
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(b)
	request, err := http.NewRequest("POST", url, buffer)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	w := tsuruIo.NewStreamWriter(context.Stdout, progressFormatter{})
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(w, response.Body) {
	}
	return nil
}

type fixContainersCmd struct{}

func (fixContainersCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "fix-containers",
		Usage: "fix-containers",
		Desc:  "Fix containers that are broken in the cluster.",
	}
}

func (fixContainersCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/fix-containers")
	if err != nil {
		return err
	}
	request, err := http.NewRequest("POST", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	return err
}

type moveContainerCmd struct{}

func (c *moveContainerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "container-move",
		Usage:   "container-move <container id> <to host>",
		Desc:    "Move specified container to another host.",
		MinArgs: 2,
	}
}

func (c *moveContainerCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL(fmt.Sprintf("/docker/container/%s/move", context.Args[0]))
	if err != nil {
		return err
	}
	params := map[string]string{
		"to": context.Args[1],
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(b)
	request, err := http.NewRequest("POST", url, buffer)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	w := tsuruIo.NewStreamWriter(context.Stdout, progressFormatter{})
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(w, response.Body) {
	}
	return nil
}

type rebalanceContainersCmd struct {
	fs  *gnuflag.FlagSet
	dry bool
}

func (c *rebalanceContainersCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "containers-rebalance",
		Usage:   "containers-rebalance [--dry]",
		Desc:    "Move containers creating a more even distribution between docker nodes.",
		MinArgs: 0,
	}
}

func (c *rebalanceContainersCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/containers/rebalance")
	if err != nil {
		return err
	}
	params := map[string]string{
		"dry": fmt.Sprintf("%t", c.dry),
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(b)
	request, err := http.NewRequest("POST", url, buffer)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	w := tsuruIo.NewStreamWriter(context.Stdout, progressFormatter{})
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(w, response.Body) {
	}
	return nil
}

func (c *rebalanceContainersCmd) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = gnuflag.NewFlagSet("containers-rebalance", gnuflag.ExitOnError)
		c.fs.BoolVar(&c.dry, "dry", false, "Dry run, only shows what would be done")
	}
	return c.fs
}

type sshToContainerCmd struct{}

func (sshToContainerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "ssh",
		Usage:   "ssh <container-id>",
		Desc:    "Open a SSH shell to the given container.",
		MinArgs: 1,
	}
}

func (sshToContainerCmd) Run(context *cmd.Context, _ *cmd.Client) error {
	serverURL, err := cmd.GetURL("/docker/ssh/" + context.Args[0])
	if err != nil {
		return err
	}
	request, err := http.NewRequest("GET", serverURL, nil)
	if err != nil {
		return err
	}
	request.Close = true
	token, err := cmd.ReadToken()
	if err == nil {
		request.Header.Set("Authorization", "bearer "+token)
	}
	parsedURL, _ := url.Parse(serverURL)
	conn, err := net.Dial("tcp", parsedURL.Host)
	if err != nil {
		return err
	}
	defer conn.Close()
	request.Write(conn)
	if stdin, ok := context.Stdin.(*os.File); ok {
		fd := int(stdin.Fd())
		if terminal.IsTerminal(fd) {
			oldState, err := terminal.MakeRaw(fd)
			if err != nil {
				return err
			}
			defer terminal.Restore(fd, oldState)
		}
	}
	bytesLimit := 50
	var readStr string
	byteBuffer := make([]byte, 1)
	for i := 0; i < bytesLimit && byteBuffer[0] != '\n'; i++ {
		_, err := conn.Read(byteBuffer)
		if err != nil {
			break
		}
		readStr += string(byteBuffer)
	}
	matches := httpHeaderRegexp.FindAllStringSubmatch(readStr, -1)
	if len(matches) > 0 && len(matches[0]) > 1 {
		code, _ := strconv.Atoi(matches[0][1])
		return &errors.HTTP{
			Code:    code,
			Message: strings.TrimSpace(readStr),
		}
	} else {
		context.Stdout.Write([]byte(readStr))
	}
	errs := make(chan error, 2)
	quit := make(chan bool)
	go io.Copy(conn, context.Stdin)
	go func() {
		defer close(quit)
		_, err := io.Copy(context.Stdout, conn)
		if err != nil && err != io.EOF {
			errs <- err
		}
	}()
	<-quit
	close(errs)
	return <-errs
}
<commit_msg>provision/docker/admin: restore terminal on signals<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 docker

import (
	"bytes"
	"code.google.com/p/go.crypto/ssh/terminal"
	"encoding/json"
	"fmt"
	"github.com/tsuru/tsuru/cmd"
	"github.com/tsuru/tsuru/errors"
	tsuruIo "github.com/tsuru/tsuru/io"
	"io"
	"launchpad.net/gnuflag"
	"net"
	"net/http"
	"net/url"
	"os"
	"os/signal"
	"regexp"
	"strconv"
	"strings"
	"syscall"
)

var httpHeaderRegexp = regexp.MustCompile(`HTTP/.*? (\d+)`)

type moveContainersCmd struct{}

type progressFormatter struct{}

func (progressFormatter) Format(out io.Writer, data []byte) error {
	var logEntry progressLog
	err := json.Unmarshal(data, &logEntry)
	if err != nil {
		return err
	}
	fmt.Fprintf(out, "%s\n", logEntry.Message)
	return nil
}

func (c *moveContainersCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "containers-move",
		Usage:   "containers-move <from host> <to host>",
		Desc:    "Move all containers from one host to another.\nThis command is especially useful for host maintenance.",
		MinArgs: 2,
	}
}

func (c *moveContainersCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/containers/move")
	if err != nil {
		return err
	}
	params := map[string]string{
		"from": context.Args[0],
		"to":   context.Args[1],
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(b)
	request, err := http.NewRequest("POST", url, buffer)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	w := tsuruIo.NewStreamWriter(context.Stdout, progressFormatter{})
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(w, response.Body) {
	}
	return nil
}

type fixContainersCmd struct{}

func (fixContainersCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:  "fix-containers",
		Usage: "fix-containers",
		Desc:  "Fix containers that are broken in the cluster.",
	}
}

func (fixContainersCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/fix-containers")
	if err != nil {
		return err
	}
	request, err := http.NewRequest("POST", url, nil)
	if err != nil {
		return err
	}
	_, err = client.Do(request)
	return err
}

type moveContainerCmd struct{}

func (c *moveContainerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "container-move",
		Usage:   "container-move <container id> <to host>",
		Desc:    "Move specified container to another host.",
		MinArgs: 2,
	}
}

func (c *moveContainerCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL(fmt.Sprintf("/docker/container/%s/move", context.Args[0]))
	if err != nil {
		return err
	}
	params := map[string]string{
		"to": context.Args[1],
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(b)
	request, err := http.NewRequest("POST", url, buffer)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	w := tsuruIo.NewStreamWriter(context.Stdout, progressFormatter{})
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(w, response.Body) {
	}
	return nil
}

type rebalanceContainersCmd struct {
	fs  *gnuflag.FlagSet
	dry bool
}

func (c *rebalanceContainersCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "containers-rebalance",
		Usage:   "containers-rebalance [--dry]",
		Desc:    "Move containers creating a more even distribution between docker nodes.",
		MinArgs: 0,
	}
}

func (c *rebalanceContainersCmd) Run(context *cmd.Context, client *cmd.Client) error {
	url, err := cmd.GetURL("/docker/containers/rebalance")
	if err != nil {
		return err
	}
	params := map[string]string{
		"dry": fmt.Sprintf("%t", c.dry),
	}
	b, err := json.Marshal(params)
	if err != nil {
		return err
	}
	buffer := bytes.NewBuffer(b)
	request, err := http.NewRequest("POST", url, buffer)
	if err != nil {
		return err
	}
	request.Header.Set("Content-Type", "application/json")
	response, err := client.Do(request)
	if err != nil {
		return err
	}
	defer response.Body.Close()
	w := tsuruIo.NewStreamWriter(context.Stdout, progressFormatter{})
	for n := int64(1); n > 0 && err == nil; n, err = io.Copy(w, response.Body) {
	}
	return nil
}

func (c *rebalanceContainersCmd) Flags() *gnuflag.FlagSet {
	if c.fs == nil {
		c.fs = gnuflag.NewFlagSet("containers-rebalance", gnuflag.ExitOnError)
		c.fs.BoolVar(&c.dry, "dry", false, "Dry run, only shows what would be done")
	}
	return c.fs
}

type sshToContainerCmd struct{}

func (sshToContainerCmd) Info() *cmd.Info {
	return &cmd.Info{
		Name:    "ssh",
		Usage:   "ssh <container-id>",
		Desc:    "Open a SSH shell to the given container.",
		MinArgs: 1,
	}
}

func (sshToContainerCmd) Run(context *cmd.Context, _ *cmd.Client) error {
	serverURL, err := cmd.GetURL("/docker/ssh/" + context.Args[0])
	if err != nil {
		return err
	}
	request, err := http.NewRequest("GET", serverURL, nil)
	if err != nil {
		return err
	}
	request.Close = true
	token, err := cmd.ReadToken()
	if err == nil {
		request.Header.Set("Authorization", "bearer "+token)
	}
	parsedURL, _ := url.Parse(serverURL)
	conn, err := net.Dial("tcp", parsedURL.Host)
	if err != nil {
		return err
	}
	defer conn.Close()
	request.Write(conn)
	if stdin, ok := context.Stdin.(*os.File); ok {
		fd := int(stdin.Fd())
		if terminal.IsTerminal(fd) {
			oldState, err := terminal.MakeRaw(fd)
			if err != nil {
				return err
			}
			defer terminal.Restore(fd, oldState)
			sigChan := make(chan os.Signal, 2)
			go func(c <-chan os.Signal) {
				if _, ok := <-c; ok {
					terminal.Restore(fd, oldState)
				}
			}(sigChan)
			signal.Notify(sigChan, syscall.SIGINT, syscall.SIGTERM, syscall.SIGKILL, syscall.SIGQUIT)
		}
	}
	bytesLimit := 50
	var readStr string
	byteBuffer := make([]byte, 1)
	for i := 0; i < bytesLimit && byteBuffer[0] != '\n'; i++ {
		_, err := conn.Read(byteBuffer)
		if err != nil {
			break
		}
		readStr += string(byteBuffer)
	}
	matches := httpHeaderRegexp.FindAllStringSubmatch(readStr, -1)
	if len(matches) > 0 && len(matches[0]) > 1 {
		code, _ := strconv.Atoi(matches[0][1])
		return &errors.HTTP{
			Code:    code,
			Message: strings.TrimSpace(readStr),
		}
	} else {
		context.Stdout.Write([]byte(readStr))
	}
	errs := make(chan error, 2)
	quit := make(chan bool)
	go io.Copy(conn, context.Stdin)
	go func() {
		defer close(quit)
		_, err := io.Copy(context.Stdout, conn)
		if err != nil && err != io.EOF {
			errs <- err
		}
	}()
	<-quit
	close(errs)
	return <-errs
}
<|endoftext|>