<commit_before>package trousseau

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io"
	"log"
	"os"
	"strings"
	"time"
)

func hasEnoughArgs(args []string, expected int) bool {
	switch expected {
	case -1:
		if len(args) > 0 {
			return true
		} else {
			return false
		}
	default:
		if len(args) == expected {
			return true
		} else {
			return false
		}
	}
}

func CreateAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to configure command")
	}

	recipients := strings.Split(c.Args()[0], ",")

	meta := Meta{
		CreatedAt:        time.Now().String(),
		LastModifiedAt:   time.Now().String(),
		Recipients:       recipients,
		TrousseauVersion: TROUSSEAU_VERSION,
	}

	// Create and write empty store file
	CreateStoreFile(gStorePath, &meta)

	fmt.Println("trousseau created")
}

func PushAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 0) {
		log.Fatal("Not enough arguments supplied to push command")
	}

	environment := NewEnvironment()
	err := environment.OverrideWith(map[string]string{
		"S3Bucket":       c.String("s3-bucket"),
		"SshPrivateKey":  c.String("ssh-private-key"),
		"RemoteFilename": c.String("remote-filename"),
		"RemoteHost":     c.String("host"),
		"RemotePort":     c.String("port"),
		"RemoteUser":     c.String("user"),
	})
	if err != nil {
		log.Fatal(err)
	}

	switch c.String("remote-storage") {
	case "s3":
		err = uploadUsingS3(environment)
		if err != nil {
			log.Fatal(err)
		}
	case "scp":
		err = uploadUsingScp(environment)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func PullAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 0) {
		log.Fatal("Not enough arguments supplied to push command")
	}

	environment := NewEnvironment()
	err := environment.OverrideWith(map[string]string{
		"S3Bucket":       c.String("s3-bucket"),
		"SshPrivateKey":  c.String("ssh-private-key"),
		"RemoteFilename": c.String("remote-filename"),
		"RemoteHost":     c.String("host"),
		"RemotePort":     c.String("port"),
		"RemoteUser":     c.String("user"),
	})
	if err != nil {
		log.Fatal(err)
	}

	switch c.String("remote-storage") {
	case "s3":
		err = DownloadUsingS3(environment)
		if err != nil {
			log.Fatal(err)
		}
	case "scp":
		err = DownloadUsingScp(environment)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func ExportAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to export command")
	}

	var err error
	var inputFilePath string = gStorePath
	var outputFilePath string = c.Args()[0]

	inputFile, err := os.Open(inputFilePath)
	defer inputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	outputFile, err := os.Create(outputFilePath)
	defer outputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	_, err = io.Copy(outputFile, inputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("trousseau exported")
}

func ImportAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to import command")
	}

	var err error
	var inputFilePath string = c.Args()[0]
	var outputFilePath string = gStorePath
	var inputFile *os.File
	var outputFile *os.File

	inputFile, err = os.Open(inputFilePath)
	defer inputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	// If trousseau data store does not exist create
	// it through the import process, otherwise just
	// override it's content
	if !pathExists(outputFilePath) {
		outputFile, err = os.Create(outputFilePath)
		defer outputFile.Close()
		if err != nil {
			log.Fatal(err)
		}
	} else {
		outputFile, err = os.OpenFile(outputFilePath, os.O_WRONLY, 0744)
		defer outputFile.Close()
		if err != nil {
			log.Fatal(err)
		}
	}

	_, err = io.Copy(outputFile, inputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("trousseau imported")
}

func AddRecipientAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to add-recipient command")
	}

	recipient := c.Args()[0]

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Decrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.DataStore.Meta.AddRecipient(recipient)

	err = store.Encrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s added to trousseau recipients", recipient)
}

func RemoveRecipientAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to remove-recipient command")
	}

	recipient := c.Args()[0]

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Decrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.DataStore.Meta.RemoveRecipient(recipient)

	err = store.Encrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s removed from trousseau recipients", recipient)
}

func GetAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to get command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	value, err := store.Get(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s key's value: %s\n", c.Args()[0], value)
}

func SetAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 2) {
		log.Fatal("Not enough argument supplied to set command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Set(c.Args()[0], c.Args()[1])
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("key-value pair set: %s:%s\n", c.Args()[0], c.Args()[1])
}

func DelAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 1) {
		log.Fatal("Not enough argument supplied to del command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Del(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s key deleted\n", c.Args()[0])
}

func KeysAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 0) {
		log.Fatal("Not enough argument supplied to keys command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	keys, err := store.Keys()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, k := range keys {
			fmt.Println(k)
		}
	}
}

func ShowAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 0) {
		log.Fatal("Not enough argument supplied to show command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Items()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, pair := range pairs {
			fmt.Printf("%s: %s\n", pair.Key, pair.Value)
		}
	}
}

func MetaAction(c *cli.Context) {
	if !hasEnoughArgs(c.Args(), 0) {
		log.Fatal("Not enough argument supplied to show command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Meta()
	if err != nil {
		log.Fatal(err)
	}

	for _, pair := range pairs {
		fmt.Printf("%s: %s\n", pair.Key, pair.Value)
	}
}
<commit_msg>Fix the error message when incorrect number of arguments passed.<commit_after>package trousseau

import (
	"fmt"
	"github.com/codegangsta/cli"
	"io"
	"log"
	"os"
	"strings"
	"time"
)

// hasExpectedArgs checks whether the number of args are as expected.
func hasExpectedArgs(args []string, expected int) bool {
	switch expected {
	case -1:
		if len(args) > 0 {
			return true
		} else {
			return false
		}
	default:
		if len(args) == expected {
			return true
		} else {
			return false
		}
	}
}

func CreateAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'configure' command")
	}

	recipients := strings.Split(c.Args()[0], ",")

	meta := Meta{
		CreatedAt:        time.Now().String(),
		LastModifiedAt:   time.Now().String(),
		Recipients:       recipients,
		TrousseauVersion: TROUSSEAU_VERSION,
	}

	// Create and write empty store file
	CreateStoreFile(gStorePath, &meta)

	fmt.Println("trousseau created")
}

func PushAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'push' command")
	}

	environment := NewEnvironment()
	err := environment.OverrideWith(map[string]string{
		"S3Bucket":       c.String("s3-bucket"),
		"SshPrivateKey":  c.String("ssh-private-key"),
		"RemoteFilename": c.String("remote-filename"),
		"RemoteHost":     c.String("host"),
		"RemotePort":     c.String("port"),
		"RemoteUser":     c.String("user"),
	})
	if err != nil {
		log.Fatal(err)
	}

	switch c.String("remote-storage") {
	case "s3":
		err = uploadUsingS3(environment)
		if err != nil {
			log.Fatal(err)
		}
	case "scp":
		err = uploadUsingScp(environment)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func PullAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'pull' command")
	}

	environment := NewEnvironment()
	err := environment.OverrideWith(map[string]string{
		"S3Bucket":       c.String("s3-bucket"),
		"SshPrivateKey":  c.String("ssh-private-key"),
		"RemoteFilename": c.String("remote-filename"),
		"RemoteHost":     c.String("host"),
		"RemotePort":     c.String("port"),
		"RemoteUser":     c.String("user"),
	})
	if err != nil {
		log.Fatal(err)
	}

	switch c.String("remote-storage") {
	case "s3":
		err = DownloadUsingS3(environment)
		if err != nil {
			log.Fatal(err)
		}
	case "scp":
		err = DownloadUsingScp(environment)
		if err != nil {
			log.Fatal(err)
		}
	}
}

func ExportAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'export' command")
	}

	var err error
	var inputFilePath string = gStorePath
	var outputFilePath string = c.Args()[0]

	inputFile, err := os.Open(inputFilePath)
	defer inputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	outputFile, err := os.Create(outputFilePath)
	defer outputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	_, err = io.Copy(outputFile, inputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("trousseau exported")
}

func ImportAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'import' command")
	}

	var err error
	var inputFilePath string = c.Args()[0]
	var outputFilePath string = gStorePath
	var inputFile *os.File
	var outputFile *os.File

	inputFile, err = os.Open(inputFilePath)
	defer inputFile.Close()
	if err != nil {
		log.Fatal(err)
	}

	// If trousseau data store does not exist create
	// it through the import process, otherwise just
	// override it's content
	if !pathExists(outputFilePath) {
		outputFile, err = os.Create(outputFilePath)
		defer outputFile.Close()
		if err != nil {
			log.Fatal(err)
		}
	} else {
		outputFile, err = os.OpenFile(outputFilePath, os.O_WRONLY, 0744)
		defer outputFile.Close()
		if err != nil {
			log.Fatal(err)
		}
	}

	_, err = io.Copy(outputFile, inputFile)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Println("trousseau imported")
}

func AddRecipientAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'add-recipient' command")
	}

	recipient := c.Args()[0]

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Decrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.DataStore.Meta.AddRecipient(recipient)

	err = store.Encrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s added to trousseau recipients", recipient)
}

func RemoveRecipientAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'remove-recipient' command")
	}

	recipient := c.Args()[0]

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Decrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.DataStore.Meta.RemoveRecipient(recipient)

	err = store.Encrypt()
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s removed from trousseau recipients", recipient)
}

func GetAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'get' command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	value, err := store.Get(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s key's value: %s\n", c.Args()[0], value)
}

func SetAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 2) {
		log.Fatal("Incorrect number of arguments to 'set' command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Set(c.Args()[0], c.Args()[1])
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("key-value pair set: %s:%s\n", c.Args()[0], c.Args()[1])
}

func DelAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 1) {
		log.Fatal("Incorrect number of arguments to 'del' command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	err = store.Del(c.Args()[0])
	if err != nil {
		log.Fatal(err)
	}

	err = store.WriteToFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	fmt.Printf("%s key deleted\n", c.Args()[0])
}

func KeysAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'keys' command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	keys, err := store.Keys()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, k := range keys {
			fmt.Println(k)
		}
	}
}

func ShowAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'show' command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Items()
	if err != nil {
		log.Fatal(err)
	} else {
		for _, pair := range pairs {
			fmt.Printf("%s: %s\n", pair.Key, pair.Value)
		}
	}
}

func MetaAction(c *cli.Context) {
	if !hasExpectedArgs(c.Args(), 0) {
		log.Fatal("Incorrect number of arguments to 'meta' command")
	}

	store, err := NewEncryptedStoreFromFile(gStorePath)
	if err != nil {
		log.Fatal(err)
	}

	pairs, err := store.Meta()
	if err != nil {
		log.Fatal(err)
	}

	for _, pair := range pairs {
		fmt.Printf("%s: %s\n", pair.Key, pair.Value)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"time"

	twodee "../libs/twodee"
	"github.com/kurrik/tmxgo"
)

type Level struct {
	Height              float32
	Grids               []*twodee.Grid
	Items               [][]*Item
	Geometry            []*twodee.Batch
	GridRatios          []float32
	Layers              int32
	Player              *Player
	Active              int32
	Transitions         []*LinearTween
	eventSystem         *twodee.GameEventHandler
	onPlayerMoveEventId int
}

func LoadLevel(path string, names []string, eventSystem *twodee.GameEventHandler) (l *Level, err error) {
	var player = NewPlayer(1,1)
	l = &Level{
		Height:      0,
		Grids:       []*twodee.Grid{},
		Items:       [][]*Item{},
		Geometry:    []*twodee.Batch{},
		GridRatios:  []float32{},
		Layers:      0,
		Active:      0,
		Player:      player,
		eventSystem: eventSystem,
	}
	l.onPlayerMoveEventId = eventSystem.AddObserver(PlayerMove, l.OnPlayerMoveEvent)
	for _, name := range names {
		if err = l.loadLayer(path, name); err != nil {
			return
		}
	}
	return
}

func (l *Level) loadLayer(path, name string) (err error) {
	var (
		tilemeta twodee.TileMetadata
		maptiles []*tmxgo.Tile
		textiles []twodee.TexturedTile
		maptile  *tmxgo.Tile
		m        *tmxgo.Map
		i        int
		data     []byte
		height   float32
		grid     *twodee.Grid
		items    []*Item
		batch    *twodee.Batch
		ratio    float32
	)
	path = filepath.Join(filepath.Dir(path), name)
	if data, err = ioutil.ReadFile(path); err != nil {
		return
	}
	if m, err = tmxgo.ParseMapString(string(data)); err != nil {
		return
	}
	if path, err = getTexturePath(m, path); err != nil {
		return
	}
	tilemeta = twodee.TileMetadata{
		Path:      path,
		PxPerUnit: 32,
	}
	if maptiles, err = m.TilesFromLayerName("collision"); err != nil {
		return
	}
	grid = twodee.NewGrid(m.Width, m.Height)
	for i, maptile = range maptiles {
		if maptile != nil {
			grid.SetIndex(int32(i), true)
		}
	}
	if maptiles, err = m.TilesFromLayerName("tiles"); err != nil {
		return
	}
	// TODO: Somewhere in here we should load a bunch of *Items into items.
	textiles = make([]twodee.TexturedTile, len(maptiles))
	for i, maptile = range maptiles {
		if maptile != nil {
			textiles[i] = maptile
		}
	}
	if batch, err = twodee.LoadBatch(textiles, tilemeta); err != nil {
		return
	}
	ratio = float32(grid.Width) * float32(tilemeta.PxPerUnit) / float32(m.TileWidth*m.Width)
	height = float32(grid.Height) / ratio
	if l.Height < height {
		l.Height = height
	}
	l.Grids = append(l.Grids, grid)
	l.Items = append(l.Items, items)
	l.Geometry = append(l.Geometry, batch)
	l.Layers += 1
	l.Transitions = append(l.Transitions, nil)
	l.GridRatios = append(l.GridRatios, ratio)
	return
}

func getTexturePath(m *tmxgo.Map, path string) (out string, err error) {
	var prefix = filepath.Dir(path)
	for i := 0; i < len(m.Tilesets); i++ {
		if m.Tilesets[i].Image == nil {
			continue
		}
		out = filepath.Join(prefix, m.Tilesets[i].Image.Source)
		return
	}
	err = fmt.Errorf("Could not find suitable tileset")
	return
}

func (l *Level) Delete() {
	var i int32
	for i = 0; i < l.Layers; i++ {
		l.Geometry[i].Delete()
	}
	l.eventSystem.RemoveObserver(PlayerMove, l.onPlayerMoveEventId)
}

func (l *Level) OnPlayerMoveEvent(e twodee.GETyper) {
	if move, ok := e.(*PlayerMoveEvent); ok {
		l.Player.DesiredMove = move.Dir
	}
}

func (l *Level) GridAlignedX(layer int32, p twodee.Point) twodee.Point {
	var (
		ratio = l.GridRatios[layer]
		x     = int32(p.X*ratio + 0.5)
	)
	return twodee.Pt(float32(x)/ratio, p.Y)
}

func (l *Level) GridAlignedY(layer int32, p twodee.Point) twodee.Point {
	var (
		ratio = l.GridRatios[layer]
		y     = int32(p.Y*ratio + 0.5)
	)
	return twodee.Pt(p.X, float32(y)/ratio)
}

// Given points a,b defining the leading edge of a moving entity; determine
// if there is a collision with something on the grid.
func (l *Level) FrontierCollides(layer int32, a, b twodee.Point) bool {
	var (
		ratio = l.GridRatios[layer]
		xmin  = int32(a.X * ratio)
		xmax  = int32(b.X * ratio)
		ymin  = int32(a.Y * ratio)
		ymax  = int32(b.Y * ratio)
	)
	// fmt.Printf("X %v-%v, Y %v-%v\n", xmin, xmax, ymin, ymax)
	for x := xmin; x <= xmax; x++ {
		for y := ymin; y <= ymax; y++ {
			// fmt.Printf("Checking X %v Y %v\n", x, y)
			if l.Grids[layer].Get(x, y) == true {
				return true
			}
		}
	}
	playerBounds := l.Player.Bounds()
	for _, item := range l.Items[layer] {
		if playerBounds.Overlaps(item.Bounds()) {
			l.eventSystem.Enqueue(NewPlayerPickedUpItemEvent(item))
			break
		}
	}
	return false
}

func (l *Level) GetLayerY(index int32) float32 {
	var tween = l.Transitions[index]
	if tween != nil {
		return tween.Current()
	}
	switch {
	case index > l.Active:
		return -1
	case index < l.Active:
		return l.Height
	case index == l.Active:
		return 0
	}
	return 0
}

const TopSlideSpeed = time.Duration(320) * time.Millisecond
const BotSlideSpeed = time.Duration(320) * time.Millisecond

func (l *Level) LayerAdvance() {
	if l.Active >= l.Layers-1 {
		return
	}
	l.Transitions[l.Active] = NewLinearTween(0, l.Height, TopSlideSpeed)
	l.Active++
	l.Transitions[l.Active] = NewLinearTween(-1, 0, BotSlideSpeed)
}

func (l *Level) LayerRewind() {
	if l.Active <= 0 {
		return
	}
	l.Transitions[l.Active-1] = NewLinearTween(l.Height, 0, TopSlideSpeed)
	l.Transitions[l.Active-1].SetCallback(func() {
		l.Active--
	})
	l.Transitions[l.Active] = NewLinearTween(0, -1, BotSlideSpeed)
}

func (l *Level) Update(elapsed time.Duration) {
	var i int32
	for i = 0; i < l.Layers; i++ {
		if l.Transitions[i] != nil {
			if l.Transitions[i].Done() {
				l.Transitions[i] = nil
			} else {
				l.Transitions[i].Update(elapsed)
			}
		}
	}
	l.Player.Update(elapsed)
	l.Player.AttemptMove(l)
}
<commit_msg>Added method for when player picks up an item<commit_after>package main

import (
	"fmt"
	"io/ioutil"
	"path/filepath"
	"time"

	twodee "../libs/twodee"
	"github.com/kurrik/tmxgo"
)

type Level struct {
	Height                      float32
	Grids                       []*twodee.Grid
	Items                       [][]*Item
	Geometry                    []*twodee.Batch
	GridRatios                  []float32
	Layers                      int32
	Player                      *Player
	Active                      int32
	Transitions                 []*LinearTween
	eventSystem                 *twodee.GameEventHandler
	onPlayerMoveEventId         int
	onPlayerPickedUpItemEventId int
}

func LoadLevel(path string, names []string, eventSystem *twodee.GameEventHandler) (l *Level, err error) {
	var player = NewPlayer(1, 1)
	l = &Level{
		Height:      0,
		Grids:       []*twodee.Grid{},
		Items:       [][]*Item{},
		Geometry:    []*twodee.Batch{},
		GridRatios:  []float32{},
		Layers:      0,
		Active:      0,
		Player:      player,
		eventSystem: eventSystem,
	}
	l.onPlayerMoveEventId = eventSystem.AddObserver(PlayerMove, l.OnPlayerMoveEvent)
	l.onPlayerPickedUpItemEventId = eventSystem.AddObserver(PlayerPickedUpItem, l.OnPlayerPickedUpItemEvent)
	for _, name := range names {
		if err = l.loadLayer(path, name); err != nil {
			return
		}
	}
	return
}

func (l *Level) loadLayer(path, name string) (err error) {
	var (
		tilemeta twodee.TileMetadata
		maptiles []*tmxgo.Tile
		textiles []twodee.TexturedTile
		maptile  *tmxgo.Tile
		m        *tmxgo.Map
		i        int
		data     []byte
		height   float32
		grid     *twodee.Grid
		items    []*Item
		batch    *twodee.Batch
		ratio    float32
	)
	path = filepath.Join(filepath.Dir(path), name)
	if data, err = ioutil.ReadFile(path); err != nil {
		return
	}
	if m, err = tmxgo.ParseMapString(string(data)); err != nil {
		return
	}
	if path, err = getTexturePath(m, path); err != nil {
		return
	}
	tilemeta = twodee.TileMetadata{
		Path:      path,
		PxPerUnit: 32,
	}
	if maptiles, err = m.TilesFromLayerName("collision"); err != nil {
		return
	}
	grid = twodee.NewGrid(m.Width, m.Height)
	for i, maptile = range maptiles {
		if maptile != nil {
			grid.SetIndex(int32(i), true)
		}
	}
	if maptiles, err = m.TilesFromLayerName("tiles"); err != nil {
		return
	}
	// TODO: Somewhere in here we should load a bunch of *Items into items.
	textiles = make([]twodee.TexturedTile, len(maptiles))
	for i, maptile = range maptiles {
		if maptile != nil {
			textiles[i] = maptile
		}
	}
	if batch, err = twodee.LoadBatch(textiles, tilemeta); err != nil {
		return
	}
	ratio = float32(grid.Width) * float32(tilemeta.PxPerUnit) / float32(m.TileWidth*m.Width)
	height = float32(grid.Height) / ratio
	if l.Height < height {
		l.Height = height
	}
	l.Grids = append(l.Grids, grid)
	l.Items = append(l.Items, items)
	l.Geometry = append(l.Geometry, batch)
	l.Layers += 1
	l.Transitions = append(l.Transitions, nil)
	l.GridRatios = append(l.GridRatios, ratio)
	return
}

func getTexturePath(m *tmxgo.Map, path string) (out string, err error) {
	var prefix = filepath.Dir(path)
	for i := 0; i < len(m.Tilesets); i++ {
		if m.Tilesets[i].Image == nil {
			continue
		}
		out = filepath.Join(prefix, m.Tilesets[i].Image.Source)
		return
	}
	err = fmt.Errorf("Could not find suitable tileset")
	return
}

func (l *Level) Delete() {
	var i int32
	for i = 0; i < l.Layers; i++ {
		l.Geometry[i].Delete()
	}
	l.eventSystem.RemoveObserver(PlayerMove, l.onPlayerMoveEventId)
	l.eventSystem.RemoveObserver(PlayerPickedUpItem, l.onPlayerPickedUpItemEventId)
}

func (l *Level) OnPlayerMoveEvent(e twodee.GETyper) {
	if move, ok := e.(*PlayerMoveEvent); ok {
		l.Player.DesiredMove = move.Dir
	}
}

func (l *Level) OnPlayerPickedUpItemEvent(e twodee.GETyper) {
	if pickup, ok := e.(*PlayerPickedUpItemEvent); ok {
		l.Player.AddToInventory(pickup.Item)
	}
}

func (l *Level) GridAlignedX(layer int32, p twodee.Point) twodee.Point {
	var (
		ratio = l.GridRatios[layer]
		x     = int32(p.X*ratio + 0.5)
	)
	return twodee.Pt(float32(x)/ratio, p.Y)
}

func (l *Level) GridAlignedY(layer int32, p twodee.Point) twodee.Point {
	var (
		ratio = l.GridRatios[layer]
		y     = int32(p.Y*ratio + 0.5)
	)
	return twodee.Pt(p.X, float32(y)/ratio)
}

// Given points a,b defining the leading edge of a moving entity; determine
// if there is a collision with something on the grid.
func (l *Level) FrontierCollides(layer int32, a, b twodee.Point) bool {
	var (
		ratio = l.GridRatios[layer]
		xmin  = int32(a.X * ratio)
		xmax  = int32(b.X * ratio)
		ymin  = int32(a.Y * ratio)
		ymax  = int32(b.Y * ratio)
	)
	// fmt.Printf("X %v-%v, Y %v-%v\n", xmin, xmax, ymin, ymax)
	for x := xmin; x <= xmax; x++ {
		for y := ymin; y <= ymax; y++ {
			// fmt.Printf("Checking X %v Y %v\n", x, y)
			if l.Grids[layer].Get(x, y) == true {
				return true
			}
		}
	}
	playerBounds := l.Player.Bounds()
	for _, item := range l.Items[layer] {
		if playerBounds.Overlaps(item.Bounds()) {
			l.eventSystem.Enqueue(NewPlayerPickedUpItemEvent(item))
			break
		}
	}
	return false
}

func (l *Level) GetLayerY(index int32) float32 {
	var tween = l.Transitions[index]
	if tween != nil {
		return tween.Current()
	}
	switch {
	case index > l.Active:
		return -1
	case index < l.Active:
		return l.Height
	case index == l.Active:
		return 0
	}
	return 0
}

const TopSlideSpeed = time.Duration(320) * time.Millisecond
const BotSlideSpeed = time.Duration(320) * time.Millisecond

func (l *Level) LayerAdvance() {
	if l.Active >= l.Layers-1 {
		return
	}
	l.Transitions[l.Active] = NewLinearTween(0, l.Height, TopSlideSpeed)
	l.Active++
	l.Transitions[l.Active] = NewLinearTween(-1, 0, BotSlideSpeed)
}

func (l *Level) LayerRewind() {
	if l.Active <= 0 {
		return
	}
	l.Transitions[l.Active-1] = NewLinearTween(l.Height, 0, TopSlideSpeed)
	l.Transitions[l.Active-1].SetCallback(func() {
		l.Active--
	})
	l.Transitions[l.Active] = NewLinearTween(0, -1, BotSlideSpeed)
}

func (l *Level) Update(elapsed time.Duration) {
	var i int32
	for i = 0; i < l.Layers; i++ {
		if l.Transitions[i] != nil {
			if l.Transitions[i].Done() {
				l.Transitions[i] = nil
			} else {
				l.Transitions[i].Update(elapsed)
			}
		}
	}
	l.Player.Update(elapsed)
	l.Player.AttemptMove(l)
}
<|endoftext|>


<commit_before>package google

import (
	"fmt"
	"strings"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

	"google.golang.org/api/cloudresourcemanager/v1"
)

func dataSourceGoogleOrganization() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceOrganizationRead,
		Schema: map[string]*schema.Schema{
			"domain": {
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ConflictsWith: []string{"organization"},
			},
			"organization": {
				Type:          schema.TypeString,
				Optional:      true,
				ConflictsWith: []string{"domain"},
			},
			"org_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"directory_customer_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"create_time": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"lifecycle_state": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func dataSourceOrganizationRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	var organization *cloudresourcemanager.Organization
	if v, ok := d.GetOk("domain"); ok {
		filter := fmt.Sprintf("domain=%s", v.(string))
		resp, err := config.clientResourceManager.Organizations.Search(&cloudresourcemanager.SearchOrganizationsRequest{
			Filter: filter,
		}).Do()
		if err != nil {
			return fmt.Errorf("Error reading organization: %s", err)
		}

		if len(resp.Organizations) == 0 {
			return fmt.Errorf("Organization not found: %s", v)
		}
		if len(resp.Organizations) > 1 {
			return fmt.Errorf("More than one matching organization found")
		}

		organization = resp.Organizations[0]
	} else if v, ok := d.GetOk("organization"); ok {
		resp, err := config.clientResourceManager.Organizations.Get(canonicalOrganizationName(v.(string))).Do()
		if err != nil {
			return handleNotFoundError(err, d, fmt.Sprintf("Organization Not Found : %s", v))
		}

		organization = resp
	} else {
		return fmt.Errorf("one of domain or organization must be set")
	}

	d.SetId(organization.Name)
	d.Set("name", organization.Name)
	d.Set("org_id", GetResourceNameFromSelfLink(organization.Name))
	d.Set("domain", organization.DisplayName)
	d.Set("create_time", organization.CreationTime)
	d.Set("lifecycle_state", organization.LifecycleState)
	if organization.Owner != nil {
		d.Set("directory_customer_id", organization.Owner.DirectoryCustomerId)
	}

	return nil
}

func canonicalOrganizationName(ba string) string {
	if strings.HasPrefix(ba, "organizations/") {
		return ba
	}

	return "organizations/" + ba
}
<commit_msg>Retry reads of organization datasource (#5246)<commit_after>package google

import (
	"fmt"
	"strings"

	"github.com/hashicorp/terraform-plugin-sdk/helper/schema"

	"google.golang.org/api/cloudresourcemanager/v1"
)

func dataSourceGoogleOrganization() *schema.Resource {
	return &schema.Resource{
		Read: dataSourceOrganizationRead,
		Schema: map[string]*schema.Schema{
			"domain": {
				Type:          schema.TypeString,
				Optional:      true,
				Computed:      true,
				ConflictsWith: []string{"organization"},
			},
			"organization": {
				Type:          schema.TypeString,
				Optional:      true,
				ConflictsWith: []string{"domain"},
			},
			"org_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"name": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"directory_customer_id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"create_time": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"lifecycle_state": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func dataSourceOrganizationRead(d *schema.ResourceData, meta interface{}) error {
	config := meta.(*Config)

	var organization *cloudresourcemanager.Organization
	if v, ok := d.GetOk("domain"); ok {
		filter := fmt.Sprintf("domain=%s", v.(string))
		var resp *cloudresourcemanager.SearchOrganizationsResponse
		err := retryTimeDuration(func() (err error) {
			resp, err = config.clientResourceManager.Organizations.Search(&cloudresourcemanager.SearchOrganizationsRequest{
				Filter: filter,
			}).Do()
			return err
		}, d.Timeout(schema.TimeoutRead))
		if err != nil {
			return fmt.Errorf("Error reading organization: %s", err)
		}

		if len(resp.Organizations) == 0 {
			return fmt.Errorf("Organization not found: %s", v)
		}

		if len(resp.Organizations) > 1 {
			return fmt.Errorf("More than one matching organization found")
		}

		organization = resp.Organizations[0]
	} else if v, ok := d.GetOk("organization"); ok {
		var resp *cloudresourcemanager.Organization
		err := retryTimeDuration(func() (err error) {
			resp, err = config.clientResourceManager.Organizations.Get(canonicalOrganizationName(v.(string))).Do()
			return err
		}, d.Timeout(schema.TimeoutRead))
		if err != nil {
			return handleNotFoundError(err, d, fmt.Sprintf("Organization Not Found : %s", v))
		}

		organization = resp
	} else {
		return fmt.Errorf("one of domain or organization must be set")
	}

	d.SetId(organization.Name)
	d.Set("name", organization.Name)
	d.Set("org_id", GetResourceNameFromSelfLink(organization.Name))
	d.Set("domain", organization.DisplayName)
	d.Set("create_time", organization.CreationTime)
	d.Set("lifecycle_state", organization.LifecycleState)
	if organization.Owner != nil {
		d.Set("directory_customer_id", organization.Owner.DirectoryCustomerId)
	}

	return nil
}

func canonicalOrganizationName(ba string) string {
	if strings.HasPrefix(ba, "organizations/") {
		return ba
	}

	return "organizations/" + ba
}
<|endoftext|>


<commit_before>// Copyright 2015 RedHat, Inc.
// Copyright 2015 CoreOS, Inc.
//
// 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 sdjournal

import (
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"log"
	"strings"
	"time"
)

var (
	ErrExpired = errors.New("Timeout expired")
)

// JournalReaderConfig represents options to drive the behavior of a JournalReader.
type JournalReaderConfig struct {
	// The Since and NumFromTail options are mutually exclusive and determine
	// where the reading begins within the journal.
	Since       time.Duration // start relative to a Duration from now
	NumFromTail uint64        // start relative to the tail

	// Show only journal entries whose fields match the supplied values. If
	// the array is empty, entries will not be filtered.
	Matches []Match

	// If not empty, the journal instance will point to a journal residing
	// in this directory. The supplied path may be relative or absolute.
	Path string
}

// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the
// systemd journal.
type JournalReader struct {
	journal *Journal
}

// NewJournalReader creates a new JournalReader with configuration options that are similar to the
// systemd journalctl tool's iteration and filtering features.
func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
	r := &JournalReader{}

	// Open the journal
	var err error
	if config.Path != "" {
		r.journal, err = NewJournalFromDir(config.Path)
	} else {
		r.journal, err = NewJournal()
	}
	if err != nil {
		return nil, err
	}

	// Add any supplied matches
	for _, m := range config.Matches {
		r.journal.AddMatch(m.String())
	}

	// Set the start position based on options
	if config.Since != 0 {
		// Start based on a relative time
		start := time.Now().Add(config.Since)
		if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil {
			return nil, err
		}
	} else if config.NumFromTail != 0 {
		// Start based on a number of lines before the tail
		if err := r.journal.SeekTail(); err != nil {
			return nil, err
		}

		// Move the read pointer into position near the tail. Go one further than
		// the option so that the initial cursor advancement positions us at the
		// correct starting point.
		if _, err := r.journal.PreviousSkip(config.NumFromTail + 1); err != nil {
			return nil, err
		}
	}

	return r, nil
}

func (r *JournalReader) Read(b []byte) (int, error) {
	var err error
	var c int

	// Advance the journal cursor
	c, err = r.journal.Next()

	// An unexpected error
	if err != nil {
		return 0, err
	}

	// EOF detection
	if c == 0 {
		return 0, io.EOF
	}

	// Build a message
	var msg string
	msg, err = r.buildMessage()

	if err != nil {
		return 0, err
	}

	// Copy and return the message
	copy(b, []byte(msg))

	return len(msg), nil
}

func (r *JournalReader) Close() error {
	return r.journal.Close()
}

// Follow synchronously follows the JournalReader, writing each new journal entry to writer. The
// follow will continue until a single time.Time is received on the until channel.
func (r *JournalReader) Follow(until <-chan time.Time, writer io.Writer) (err error) {

	// Process journal entries and events. Entries are flushed until the tail or
	// timeout is reached, and then we wait for new events or the timeout.
	var msg = make([]byte, 64*1<<(10))
process:
	for {
		c, err := r.Read(msg)
		if err != nil && err != io.EOF {
			break process
		}

		select {
		case <-until:
			return ErrExpired
		default:
			if c > 0 {
				writer.Write(msg[:c])
				continue process
			}
		}

		// We're at the tail, so wait for new events or time out.
		// Holds journal events to process. Tightly bounded for now unless there's a
		// reason to unblock the journal watch routine more quickly.
		events := make(chan int, 1)
		pollDone := make(chan bool, 1)
		go func() {
			for {
				select {
				case <-pollDone:
					return
				default:
					events <- r.journal.Wait(time.Duration(1) * time.Second)
				}
			}
		}()

		select {
		case <-until:
			pollDone <- true
			return ErrExpired
		case e := <-events:
			pollDone <- true
			switch e {
			case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
				// TODO: need to account for any of these?
			default:
				log.Printf("Received unknown event: %d\n", e)
			}
			continue process
		}
	}

	return
}

// FollowWithFields synchronously follows the Journal, writing each full Journal Entry with all its given fields
// as a map to the writer. Similar to Follow(), it will continue until a single time.Time is
// received on the until channel.
func (r *JournalReader) FollowWithFields(until <-chan time.Time, writer io.Writer) (err error) {
	enc := json.NewEncoder(writer)
journal:
	for {
		kvMap := make(map[string]string)
		select {
		case <-until:
			return ErrExpired
		default:
			var err error
			var c int

			// Advance the journal cursor
			c, err = r.journal.Next()

			// An unexpected error
			if err != nil {
				return err
			}

			// We have a new journal entry go over the fields
			// get the data for what we care about and return
			r.journal.RestartData()
			if c > 0 {
			fields:
				for {
					s, err := r.journal.EnumerateData()
					if err != nil || len(s) == 0 {
						break fields
					}
					s = s[:len(s)]
					arr := strings.SplitN(s, "=", 2)
					kvMap[arr[0]] = arr[1]
				}
				if err := enc.Encode(kvMap); err != nil {
					return err
				}
			}
		}

		// we're at the tail, so wait for new events or time out.
		// holds journal events to process. tightly bounded for now unless there's a
		// reason to unblock the journal watch routine more quickly
		events := make(chan int, 1)
		pollDone := make(chan bool, 1)
		go func() {
			for {
				select {
				case <-pollDone:
					return
				default:
					events <- r.journal.Wait(time.Duration(1) * time.Second)
				}
			}
		}()

		select {
		case <-until:
			pollDone <- true
			return ErrExpired
		case e := <-events:
			pollDone <- true
			switch e {
			case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
				// TODO: need to account for any of these?
			default:
				log.Printf("Received unknown event: %d\n", e)
			}
			continue journal
		}
	}
	return
}

// buildMessage returns a string representing the current journal entry in a simple format which
// includes the entry timestamp and MESSAGE field.
func (r *JournalReader) buildMessage() (string, error) {
	var msg string
	var usec uint64
	var err error

	if msg, err = r.journal.GetData("MESSAGE"); err != nil {
		return "", err
	}

	if usec, err = r.journal.GetRealtimeUsec(); err != nil {
		return "", err
	}

	timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))

	return fmt.Sprintf("%s %s\n", timestamp, msg), nil
}
<commit_msg>sdjournal follow with field filters<commit_after>// Copyright 2015 RedHat, Inc.
// Copyright 2015 CoreOS, Inc.
//
// 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 sdjournal

import (
	"errors"
	"fmt"
	"io"
	"log"
	"strings"
	"time"

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

var (
	ErrExpired = errors.New("Timeout expired")
)

// JournalReaderConfig represents options to drive the behavior of a JournalReader.
type JournalReaderConfig struct {
	// The Since and NumFromTail options are mutually exclusive and determine
	// where the reading begins within the journal.
	Since       time.Duration // start relative to a Duration from now
	NumFromTail uint64        // start relative to the tail

	// Show only journal entries whose fields match the supplied values. If
	// the array is empty, entries will not be filtered.
	Matches []Match

	// If not empty, the journal instance will point to a journal residing
	// in this directory. The supplied path may be relative or absolute.
	Path string
}

// JournalReader is an io.ReadCloser which provides a simple interface for iterating through the
// systemd journal.
type JournalReader struct {
	journal *Journal
}

// FollowFilter is a function which you pass into Follow to determine if you to
// filter key value pair from a journal entry.
type FollowFilter func(key, value string) bool

// AllKeys is a FollowFilter that allows all keys of a given journal entry through the filter
func AllKeys(key, value string) bool {
	return true
}

func OnlyMessages(key, value string) bool {
	if key == "MESSAGE" {
		return true
	}
	return false
}

// NewJournalReader creates a new JournalReader with configuration options that are similar to the
// systemd journalctl tool's iteration and filtering features.
func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
	r := &JournalReader{}

	// Open the journal
	var err error
	if config.Path != "" {
		r.journal, err = NewJournalFromDir(config.Path)
	} else {
		r.journal, err = NewJournal()
	}
	if err != nil {
		return nil, err
	}

	// Add any supplied matches
	for _, m := range config.Matches {
		r.journal.AddMatch(m.String())
	}

	// Set the start position based on options
	if config.Since != 0 {
		// Start based on a relative time
		start := time.Now().Add(config.Since)
		if err := r.journal.SeekRealtimeUsec(uint64(start.UnixNano() / 1000)); err != nil {
			return nil, err
		}
	} else if config.NumFromTail != 0 {
		// Start based on a number of lines before the tail
		if err := r.journal.SeekTail(); err != nil {
			return nil, err
		}

		// Move the read pointer into position near the tail. Go one further than
		// the option so that the initial cursor advancement positions us at the
		// correct starting point.
		if _, err := r.journal.PreviousSkip(config.NumFromTail + 1); err != nil {
			return nil, err
		}
	}

	return r, nil
}

func (r *JournalReader) Read(b []byte) (int, error) {
	var err error
	var c int

	// Advance the journal cursor
	c, err = r.journal.Next()

	// An unexpected error
	if err != nil {
		return 0, err
	}

	// EOF detection
	if c == 0 {
		return 0, io.EOF
	}

	// Build a message
	var msg string
	msg, err = r.buildMessage()

	if err != nil {
		return 0, err
	}

	// Copy and return the message
	copy(b, []byte(msg))

	return len(msg), nil
}

func (r *JournalReader) Close() error {
	return r.journal.Close()
}

// Follow asynchronously follows the JournalReader it takes in a context to stop the following the Journal, it returns a
// buffered channel of errors and will stop following the journal on the first given error
func (r *JournalReader) Follow(ctx context.Context, msgs chan<- map[string]interface{}, filter FollowFilter) <-chan error {
	errChan := make(chan error, 1)
	go func() {
		for {
			kvMap := make(map[string]interface{})
			select {
			case <-ctx.Done():
				errChan <- ctx.Err()
				return
			default:
				var err error
				var c int

				// Advance the journal cursor
				c, err = r.journal.Next()

				// An unexpected error
				if err != nil {
					errChan <- err
					return
				}

				// We have a new journal entry go over the fields
				// get the data for what we care about and return
				r.journal.RestartData()
				if c > 0 {
				fields:
					for {
						s, err := r.journal.EnumerateData()
						if err != nil || len(s) == 0 {
							break fields
						}
						s = s[:len(s)]
						arr := strings.SplitN(s, "=", 2)
						// if we want the pair,
						// add it to the map
						if filter(arr[0], arr[1]) {
							kvMap[arr[0]] = arr[1]
						}
					}
					msgs <- kvMap
				}
			}

			// we're at the tail, so wait for new events or time out.
			// holds journal events to process. tightly bounded for now unless there's a
			// reason to unblock the journal watch routine more quickly
			events := make(chan int, 1)
			pollDone := make(chan bool, 1)
			go func() {
				for {
					select {
					case <-pollDone:
						return
					default:
						events <- r.journal.Wait(time.Duration(1) * time.Second)
					}
				}
			}()

			select {
			case <-ctx.Done():
				errChan <- ctx.Err()
				pollDone <- true
				return
			case e := <-events:
				pollDone <- true
				switch e {
				case SD_JOURNAL_NOP, SD_JOURNAL_APPEND, SD_JOURNAL_INVALIDATE:
					// TODO: need to account for any of these?
				default:
					log.Printf("Received unknown event: %d\n", e)
				}
			}
		}
	}()
	return errChan
}

// buildMessage returns a string representing the current journal entry in a simple format which
// includes the entry timestamp and MESSAGE field.
func (r *JournalReader) buildMessage() (string, error) {
	var msg string
	var usec uint64
	var err error

	if msg, err = r.journal.GetData("MESSAGE"); err != nil {
		return "", err
	}

	if usec, err = r.journal.GetRealtimeUsec(); err != nil {
		return "", err
	}

	timestamp := time.Unix(0, int64(usec)*int64(time.Microsecond))

	return fmt.Sprintf("%s %s\n", timestamp, msg), nil
}
<|endoftext|>


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

package http

import (
	"encoding/json"
	"io/ioutil"
	"net/http"

	"github.com/juju/errors"

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

// ExtractAPIError returns the failure serialized in the response
// body.  If there is no failure (an OK status code), it simply returns
// nil.
func ExtractAPIError(resp *http.Response) (*params.Error, error) {
	if resp.StatusCode == http.StatusOK {
		return nil, nil
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Annotate(err, "while reading HTTP response")
	}

	var failure params.Error
	if resp.Header.Get("Content-Type") == "application/json" {
		if err := json.Unmarshal(body, &failure); err != nil {
			return nil, errors.Annotate(err, "while unserializing the error")
		}
	} else {
		switch resp.StatusCode {
		case http.StatusNotFound:
			fallthrough
		case http.StatusMethodNotAllowed:
			failure.Code = params.CodeNotImplemented
		default:
			// Leave Code empty.
		}

		failure.Message = string(body)
	}
	return &failure, nil
}
<commit_msg>Drop fallthrough.<commit_after>// Copyright 2014 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.

package http

import (
	"encoding/json"
	"io/ioutil"
	"net/http"

	"github.com/juju/errors"

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

// ExtractAPIError returns the failure serialized in the response
// body.  If there is no failure (an OK status code), it simply returns
// nil.
func ExtractAPIError(resp *http.Response) (*params.Error, error) {
	if resp.StatusCode == http.StatusOK {
		return nil, nil
	}
	defer resp.Body.Close()

	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return nil, errors.Annotate(err, "while reading HTTP response")
	}

	var failure params.Error
	if resp.Header.Get("Content-Type") == "application/json" {
		if err := json.Unmarshal(body, &failure); err != nil {
			return nil, errors.Annotate(err, "while unserializing the error")
		}
	} else {
		switch resp.StatusCode {
		case http.StatusNotFound, http.StatusMethodNotAllowed:
			failure.Code = params.CodeNotImplemented
		default:
			// Leave Code empty.
		}

		failure.Message = string(body)
	}
	return &failure, nil
}
<|endoftext|>


<commit_before>package server

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/libopenstorage/openstorage/api"
	"github.com/libopenstorage/openstorage/config"
	"github.com/libopenstorage/openstorage/volume"
	"github.com/libopenstorage/openstorage/volume/drivers"
)

const (
	// VolumeDriver is the string returned in the handshake protocol.
	VolumeDriver = "VolumeDriver"
)

// Implementation of the Docker volumes plugin specification.
type driver struct {
	restBase
}

type handshakeResp struct {
	Implements []string
}

type volumeRequest struct {
	Name string
	Opts map[string]string
}

type mountRequest struct {
	Name string
	ID   string
}

type volumeResponse struct {
	Err string
}

type volumePathResponse struct {
	Mountpoint string
	volumeResponse
}

type volumeInfo struct {
	Name       string
	Mountpoint string
}

func newVolumePlugin(name string) restServer {
	return &driver{restBase{name: name, version: "0.3"}}
}

func (d *driver) String() string {
	return d.name
}

func volDriverPath(method string) string {
	return fmt.Sprintf("/%s.%s", VolumeDriver, method)
}

func (d *driver) volNotFound(request string, id string, e error, w http.ResponseWriter) error {
	err := fmt.Errorf("Failed to locate volume: " + e.Error())
	d.logRequest(request, id).Warnln(http.StatusNotFound, " ", err.Error())
	return err
}

func (d *driver) volNotMounted(request string, id string) error {
	err := fmt.Errorf("volume not mounted")
	d.logRequest(request, id).Debugln(http.StatusNotFound, " ", err.Error())
	return err
}

func (d *driver) Routes() []*Route {
	return []*Route{
		&Route{verb: "POST", path: volDriverPath("Create"), fn: d.create},
		&Route{verb: "POST", path: volDriverPath("Remove"), fn: d.remove},
		&Route{verb: "POST", path: volDriverPath("Mount"), fn: d.mount},
		&Route{verb: "POST", path: volDriverPath("Path"), fn: d.path},
		&Route{verb: "POST", path: volDriverPath("List"), fn: d.list},
		&Route{verb: "POST", path: volDriverPath("Get"), fn: d.get},
		&Route{verb: "POST", path: volDriverPath("Unmount"), fn: d.unmount},
		&Route{verb: "POST", path: "/Plugin.Activate", fn: d.handshake},
		&Route{verb: "GET", path: "/status", fn: d.status},
	}
}

func (d *driver) emptyResponse(w http.ResponseWriter) {
	json.NewEncoder(w).Encode(&volumeResponse{})
}

func (d *driver) errorResponse(w http.ResponseWriter, err error) {
	json.NewEncoder(w).Encode(&volumeResponse{Err: err.Error()})
}

func (d *driver) volFromName(name string) (*api.Volume, error) {
	v, err := volumedrivers.Get(d.name)
	if err != nil {
		return nil, fmt.Errorf("Cannot locate volume driver for %s: %s", d.name, err.Error())
	}
	vols, err := v.Inspect([]string{name})
	if err == nil && len(vols) == 1 {
		return vols[0], nil
	}
	vols, err = v.Enumerate(&api.VolumeLocator{Name: name}, nil)
	if err == nil && len(vols) == 1 {
		return vols[0], nil
	}
	return nil, fmt.Errorf("Cannot locate volume %s", name)
}

func (d *driver) decode(method string, w http.ResponseWriter, r *http.Request) (*volumeRequest, error) {
	var request volumeRequest
	err := json.NewDecoder(r.Body).Decode(&request)
	if err != nil {
		e := fmt.Errorf("Unable to decode JSON payload")
		d.sendError(method, "", w, e.Error()+":"+err.Error(), http.StatusBadRequest)
		return nil, e
	}
	d.logRequest(method, request.Name).Debugln("")
	return &request, nil
}

func (d *driver) decodeMount(method string, w http.ResponseWriter, r *http.Request) (*mountRequest, error) {
	var request mountRequest
	err := json.NewDecoder(r.Body).Decode(&request)
	if err != nil {
		e := fmt.Errorf("Unable to decode JSON payload")
		d.sendError(method, "", w, e.Error()+":"+err.Error(), http.StatusBadRequest)
		return nil, e
	}
	d.logRequest(method, request.Name).Debugf("ID: %v", request.ID)
	return &request, nil
}

func (d *driver) handshake(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&handshakeResp{
		[]string{VolumeDriver},
	})
	if err != nil {
		d.sendError("handshake", "", w, "encode error", http.StatusInternalServerError)
		return
	}
	d.logRequest("handshake", "").Debugln("Handshake completed")
}

func (d *driver) status(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintln("osd plugin", d.version))
}

func (d *driver) specFromOpts(Opts map[string]string) *api.VolumeSpec {
	spec := api.VolumeSpec{
		VolumeLabels: make(map[string]string),
		Format:       api.FSType_FS_TYPE_EXT4,
	}

	for k, v := range Opts {
		switch k {
		case api.SpecEphemeral:
			spec.Ephemeral, _ = strconv.ParseBool(v)
		case api.SpecSize:
			sizeMulti := uint64(1024 * 1024 * 1024)
			if strings.HasSuffix(v, "G") || strings.HasSuffix(v, "g") {
				sizeMulti = 1024 * 1024 * 1024
				last := len(v) - 1
				v = v[:last]
			}

			size, _ := strconv.ParseUint(v, 10, 64)
			spec.Size = size * sizeMulti
		case api.SpecFilesystem:
			value, _ := api.FSTypeSimpleValueOf(v)
			spec.Format = value
		case api.SpecBlockSize:
			blockSize, _ := strconv.ParseInt(v, 10, 64)
			spec.BlockSize = blockSize
		case api.SpecHaLevel:
			haLevel, _ := strconv.ParseInt(v, 10, 64)
			spec.HaLevel = haLevel
		case api.SpecCos:
			value, _ := strconv.ParseUint(v, 10, 32)
			spec.Cos = uint32(value)
		case api.SpecDedupe:
			spec.Dedupe, _ = strconv.ParseBool(v)
		case api.SpecSnapshotInterval:
			snapshotInterval, _ := strconv.ParseUint(v, 10, 32)
			spec.SnapshotInterval = uint32(snapshotInterval)
		default:
			spec.VolumeLabels[k] = v
		}
	}
	return &spec
}

func (d *driver) mountpath(request *mountRequest) string {
	if len(request.ID) != 0 {
		return path.Join(config.MountBase, request.Name+"_"+request.ID)
	}
	return path.Join(config.MountBase, request.Name)
}

func (d *driver) create(w http.ResponseWriter, r *http.Request) {
	method := "create"
	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}
	d.logRequest(method, request.Name).Infoln("")
	if _, err = d.volFromName(request.Name); err != nil {
		v, err := volumedrivers.Get(d.name)
		if err != nil {
			d.errorResponse(w, err)
			return
		}
		spec := d.specFromOpts(request.Opts)
		if _, err := v.Create(&api.VolumeLocator{Name: request.Name}, nil, spec); err != nil {
			d.errorResponse(w, err)
			return
		}
	}
	json.NewEncoder(w).Encode(&volumeResponse{})
}

func (d *driver) remove(w http.ResponseWriter, r *http.Request) {
	method := "remove"
	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}
	d.logRequest(method, request.Name).Infoln("")
	// It is an error if the volume doesn't exist.
	if _, err := d.volFromName(request.Name); err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}
	json.NewEncoder(w).Encode(&volumeResponse{})
}

func (d *driver) mount(w http.ResponseWriter, r *http.Request) {
	var response volumePathResponse
	method := "mount"

	v, err := volumedrivers.Get(d.name)
	if err != nil {
		d.logRequest(method, "").Warnf("Cannot locate volume driver")
		d.errorResponse(w, err)
		return
	}

	request, err := d.decodeMount(method, w, r)
	if err != nil {
		d.errorResponse(w, err)
		return
	}

	vol, err := d.volFromName(request.Name)
	if err != nil {
		d.errorResponse(w, err)
		return
	}

	// If this is a block driver, first attach the volume.
	if v.Type() == api.DriverType_DRIVER_TYPE_BLOCK {
		attachPath, err := v.Attach(vol.Id)
		if err != nil {
			if err == volume.ErrVolAttachedOnRemoteNode {
				d.logRequest(method, request.Name).Infof("Volume is attached on a remote node... will attempt to mount it.")
			} else {
				d.logRequest(method, request.Name).Warnf("Cannot attach volume: %v", err.Error())
				d.errorResponse(w, err)
				return
			}
		} else {
			d.logRequest(method, request.Name).Debugf("response %v", attachPath)
		}
	}

	// Now mount it.
	response.Mountpoint = d.mountpath(request)
	os.MkdirAll(response.Mountpoint, 0755)

	err = v.Mount(vol.Id, response.Mountpoint)
	if err != nil {
		d.logRequest(method, request.Name).Warnf("Cannot mount volume %v, %v",
			response.Mountpoint, err)
		d.errorResponse(w, err)
		return
	}

	d.logRequest(method, request.Name).Infof("response %v", response.Mountpoint)
	json.NewEncoder(w).Encode(&response)
}

func (d *driver) path(w http.ResponseWriter, r *http.Request) {
	method := "path"
	var response volumePathResponse

	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}

	vol, err := d.volFromName(request.Name)
	if err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}

	d.logRequest(method, request.Name).Debugf("")

	if len(vol.AttachPath) == 0 || len(vol.AttachPath) == 0 {
		e := d.volNotMounted(method, request.Name)
		d.errorResponse(w, e)
		return
	}
	response.Mountpoint = vol.AttachPath[0]
	response.Mountpoint = path.Join(response.Mountpoint, config.DataDir)
	d.logRequest(method, request.Name).Debugf("response %v", response.Mountpoint)
	json.NewEncoder(w).Encode(&response)
}

func (d *driver) list(w http.ResponseWriter, r *http.Request) {
	method := "list"

	v, err := volumedrivers.Get(d.name)
	if err != nil {
		d.logRequest(method, "").Warnf("Cannot locate volume driver: %v", err.Error())
		d.errorResponse(w, err)
		return
	}

	vols, err := v.Enumerate(nil, nil)
	if err != nil {
		d.errorResponse(w, err)
		return
	}

	volInfo := make([]volumeInfo, len(vols))
	for i, v := range vols {
		volInfo[i].Name = v.Locator.Name
		if len(v.AttachPath) > 0 || len(v.AttachPath) > 0 {
			volInfo[i].Mountpoint = path.Join(v.AttachPath[0], config.DataDir)
		}
	}
	json.NewEncoder(w).Encode(map[string][]volumeInfo{"Volumes": volInfo})
}

func (d *driver) get(w http.ResponseWriter, r *http.Request) {
	method := "get"

	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}
	vol, err := d.volFromName(request.Name)
	if err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}

	volInfo := volumeInfo{Name: request.Name}
	if len(vol.AttachPath) > 0 || len(vol.AttachPath) > 0 {
		volInfo.Mountpoint = path.Join(vol.AttachPath[0], config.DataDir)
	}

	json.NewEncoder(w).Encode(map[string]volumeInfo{"Volume": volInfo})
}

func (d *driver) unmount(w http.ResponseWriter, r *http.Request) {
	method := "unmount"

	v, err := volumedrivers.Get(d.name)
	if err != nil {
		d.logRequest(method, "").Warnf("Cannot locate volume driver: %v", err.Error())
		d.errorResponse(w, err)
		return
	}

	request, err := d.decodeMount(method, w, r)
	if err != nil {
		return
	}

	vol, err := d.volFromName(request.Name)
	if err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}

	mountpoint := d.mountpath(request)
	err = v.Unmount(vol.Id, mountpoint)
	if err != nil {
		d.logRequest(method, request.Name).Warnf("Cannot unmount volume %v, %v",
			mountpoint, err)
		d.errorResponse(w, err)
		return
	}

	if v.Type() == api.DriverType_DRIVER_TYPE_BLOCK {
		_ = v.Detach(vol.Id)
	}
	d.emptyResponse(w)
}
<commit_msg>adhere to go style<commit_after>package server

import (
	"encoding/json"
	"fmt"
	"io"
	"net/http"
	"os"
	"path"
	"strconv"
	"strings"

	"github.com/libopenstorage/openstorage/api"
	"github.com/libopenstorage/openstorage/config"
	"github.com/libopenstorage/openstorage/volume"
	"github.com/libopenstorage/openstorage/volume/drivers"
)

const (
	// VolumeDriver is the string returned in the handshake protocol.
	VolumeDriver = "VolumeDriver"
)

// Implementation of the Docker volumes plugin specification.
type driver struct {
	restBase
}

type handshakeResp struct {
	Implements []string
}

type volumeRequest struct {
	Name string
	Opts map[string]string
}

type mountRequest struct {
	Name string
	ID   string
}

type volumeResponse struct {
	Err string
}

type volumePathResponse struct {
	Mountpoint string
	volumeResponse
}

type volumeInfo struct {
	Name       string
	Mountpoint string
}

func newVolumePlugin(name string) restServer {
	return &driver{restBase{name: name, version: "0.3"}}
}

func (d *driver) String() string {
	return d.name
}

func volDriverPath(method string) string {
	return fmt.Sprintf("/%s.%s", VolumeDriver, method)
}

func (d *driver) volNotFound(request string, id string, e error, w http.ResponseWriter) error {
	err := fmt.Errorf("Failed to locate volume: " + e.Error())
	d.logRequest(request, id).Warnln(http.StatusNotFound, " ", err.Error())
	return err
}

func (d *driver) volNotMounted(request string, id string) error {
	err := fmt.Errorf("volume not mounted")
	d.logRequest(request, id).Debugln(http.StatusNotFound, " ", err.Error())
	return err
}

func (d *driver) Routes() []*Route {
	return []*Route{
		&Route{verb: "POST", path: volDriverPath("Create"), fn: d.create},
		&Route{verb: "POST", path: volDriverPath("Remove"), fn: d.remove},
		&Route{verb: "POST", path: volDriverPath("Mount"), fn: d.mount},
		&Route{verb: "POST", path: volDriverPath("Path"), fn: d.path},
		&Route{verb: "POST", path: volDriverPath("List"), fn: d.list},
		&Route{verb: "POST", path: volDriverPath("Get"), fn: d.get},
		&Route{verb: "POST", path: volDriverPath("Unmount"), fn: d.unmount},
		&Route{verb: "POST", path: "/Plugin.Activate", fn: d.handshake},
		&Route{verb: "GET", path: "/status", fn: d.status},
	}
}

func (d *driver) emptyResponse(w http.ResponseWriter) {
	json.NewEncoder(w).Encode(&volumeResponse{})
}

func (d *driver) errorResponse(w http.ResponseWriter, err error) {
	json.NewEncoder(w).Encode(&volumeResponse{Err: err.Error()})
}

func (d *driver) volFromName(name string) (*api.Volume, error) {
	v, err := volumedrivers.Get(d.name)
	if err != nil {
		return nil, fmt.Errorf("Cannot locate volume driver for %s: %s", d.name, err.Error())
	}
	vols, err := v.Inspect([]string{name})
	if err == nil && len(vols) == 1 {
		return vols[0], nil
	}
	vols, err = v.Enumerate(&api.VolumeLocator{Name: name}, nil)
	if err == nil && len(vols) == 1 {
		return vols[0], nil
	}
	return nil, fmt.Errorf("Cannot locate volume %s", name)
}

func (d *driver) decode(method string, w http.ResponseWriter, r *http.Request) (*volumeRequest, error) {
	var request volumeRequest
	err := json.NewDecoder(r.Body).Decode(&request)
	if err != nil {
		e := fmt.Errorf("Unable to decode JSON payload")
		d.sendError(method, "", w, e.Error()+":"+err.Error(), http.StatusBadRequest)
		return nil, e
	}
	d.logRequest(method, request.Name).Debugln("")
	return &request, nil
}

func (d *driver) decodeMount(method string, w http.ResponseWriter, r *http.Request) (*mountRequest, error) {
	var request mountRequest
	if err := json.NewDecoder(r.Body).Decode(&request); err != nil {
		e := fmt.Errorf("Unable to decode JSON payload")
		d.sendError(method, "", w, e.Error()+":"+err.Error(), http.StatusBadRequest)
		return nil, e
	}
	d.logRequest(method, request.Name).Debugf("ID: %v", request.ID)
	return &request, nil
}

func (d *driver) handshake(w http.ResponseWriter, r *http.Request) {
	err := json.NewEncoder(w).Encode(&handshakeResp{
		[]string{VolumeDriver},
	})
	if err != nil {
		d.sendError("handshake", "", w, "encode error", http.StatusInternalServerError)
		return
	}
	d.logRequest("handshake", "").Debugln("Handshake completed")
}

func (d *driver) status(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, fmt.Sprintln("osd plugin", d.version))
}

func (d *driver) specFromOpts(Opts map[string]string) *api.VolumeSpec {
	spec := api.VolumeSpec{
		VolumeLabels: make(map[string]string),
		Format:       api.FSType_FS_TYPE_EXT4,
	}

	for k, v := range Opts {
		switch k {
		case api.SpecEphemeral:
			spec.Ephemeral, _ = strconv.ParseBool(v)
		case api.SpecSize:
			sizeMulti := uint64(1024 * 1024 * 1024)
			if strings.HasSuffix(v, "G") || strings.HasSuffix(v, "g") {
				sizeMulti = 1024 * 1024 * 1024
				last := len(v) - 1
				v = v[:last]
			}

			size, _ := strconv.ParseUint(v, 10, 64)
			spec.Size = size * sizeMulti
		case api.SpecFilesystem:
			value, _ := api.FSTypeSimpleValueOf(v)
			spec.Format = value
		case api.SpecBlockSize:
			blockSize, _ := strconv.ParseInt(v, 10, 64)
			spec.BlockSize = blockSize
		case api.SpecHaLevel:
			haLevel, _ := strconv.ParseInt(v, 10, 64)
			spec.HaLevel = haLevel
		case api.SpecCos:
			value, _ := strconv.ParseUint(v, 10, 32)
			spec.Cos = uint32(value)
		case api.SpecDedupe:
			spec.Dedupe, _ = strconv.ParseBool(v)
		case api.SpecSnapshotInterval:
			snapshotInterval, _ := strconv.ParseUint(v, 10, 32)
			spec.SnapshotInterval = uint32(snapshotInterval)
		default:
			spec.VolumeLabels[k] = v
		}
	}
	return &spec
}

func (d *driver) mountpath(request *mountRequest) string {
	if len(request.ID) != 0 {
		return path.Join(config.MountBase, request.Name+"_"+request.ID)
	}
	return path.Join(config.MountBase, request.Name)
}

func (d *driver) create(w http.ResponseWriter, r *http.Request) {
	method := "create"
	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}
	d.logRequest(method, request.Name).Infoln("")
	if _, err = d.volFromName(request.Name); err != nil {
		v, err := volumedrivers.Get(d.name)
		if err != nil {
			d.errorResponse(w, err)
			return
		}
		spec := d.specFromOpts(request.Opts)
		if _, err := v.Create(&api.VolumeLocator{Name: request.Name}, nil, spec); err != nil {
			d.errorResponse(w, err)
			return
		}
	}
	json.NewEncoder(w).Encode(&volumeResponse{})
}

func (d *driver) remove(w http.ResponseWriter, r *http.Request) {
	method := "remove"
	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}
	d.logRequest(method, request.Name).Infoln("")
	// It is an error if the volume doesn't exist.
	if _, err := d.volFromName(request.Name); err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}
	json.NewEncoder(w).Encode(&volumeResponse{})
}

func (d *driver) mount(w http.ResponseWriter, r *http.Request) {
	var response volumePathResponse
	method := "mount"

	v, err := volumedrivers.Get(d.name)
	if err != nil {
		d.logRequest(method, "").Warnf("Cannot locate volume driver")
		d.errorResponse(w, err)
		return
	}

	request, err := d.decodeMount(method, w, r)
	if err != nil {
		d.errorResponse(w, err)
		return
	}

	vol, err := d.volFromName(request.Name)
	if err != nil {
		d.errorResponse(w, err)
		return
	}

	// If this is a block driver, first attach the volume.
	if v.Type() == api.DriverType_DRIVER_TYPE_BLOCK {
		attachPath, err := v.Attach(vol.Id)
		if err != nil {
			if err == volume.ErrVolAttachedOnRemoteNode {
				d.logRequest(method, request.Name).Infof("Volume is attached on a remote node... will attempt to mount it.")
			} else {
				d.logRequest(method, request.Name).Warnf("Cannot attach volume: %v", err.Error())
				d.errorResponse(w, err)
				return
			}
		} else {
			d.logRequest(method, request.Name).Debugf("response %v", attachPath)
		}
	}

	// Now mount it.
	response.Mountpoint = d.mountpath(request)
	os.MkdirAll(response.Mountpoint, 0755)

	err = v.Mount(vol.Id, response.Mountpoint)
	if err != nil {
		d.logRequest(method, request.Name).Warnf("Cannot mount volume %v, %v",
			response.Mountpoint, err)
		d.errorResponse(w, err)
		return
	}

	d.logRequest(method, request.Name).Infof("response %v", response.Mountpoint)
	json.NewEncoder(w).Encode(&response)
}

func (d *driver) path(w http.ResponseWriter, r *http.Request) {
	method := "path"
	var response volumePathResponse

	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}

	vol, err := d.volFromName(request.Name)
	if err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}

	d.logRequest(method, request.Name).Debugf("")

	if len(vol.AttachPath) == 0 || len(vol.AttachPath) == 0 {
		e := d.volNotMounted(method, request.Name)
		d.errorResponse(w, e)
		return
	}
	response.Mountpoint = vol.AttachPath[0]
	response.Mountpoint = path.Join(response.Mountpoint, config.DataDir)
	d.logRequest(method, request.Name).Debugf("response %v", response.Mountpoint)
	json.NewEncoder(w).Encode(&response)
}

func (d *driver) list(w http.ResponseWriter, r *http.Request) {
	method := "list"

	v, err := volumedrivers.Get(d.name)
	if err != nil {
		d.logRequest(method, "").Warnf("Cannot locate volume driver: %v", err.Error())
		d.errorResponse(w, err)
		return
	}

	vols, err := v.Enumerate(nil, nil)
	if err != nil {
		d.errorResponse(w, err)
		return
	}

	volInfo := make([]volumeInfo, len(vols))
	for i, v := range vols {
		volInfo[i].Name = v.Locator.Name
		if len(v.AttachPath) > 0 || len(v.AttachPath) > 0 {
			volInfo[i].Mountpoint = path.Join(v.AttachPath[0], config.DataDir)
		}
	}
	json.NewEncoder(w).Encode(map[string][]volumeInfo{"Volumes": volInfo})
}

func (d *driver) get(w http.ResponseWriter, r *http.Request) {
	method := "get"

	request, err := d.decode(method, w, r)
	if err != nil {
		return
	}
	vol, err := d.volFromName(request.Name)
	if err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}

	volInfo := volumeInfo{Name: request.Name}
	if len(vol.AttachPath) > 0 || len(vol.AttachPath) > 0 {
		volInfo.Mountpoint = path.Join(vol.AttachPath[0], config.DataDir)
	}

	json.NewEncoder(w).Encode(map[string]volumeInfo{"Volume": volInfo})
}

func (d *driver) unmount(w http.ResponseWriter, r *http.Request) {
	method := "unmount"

	v, err := volumedrivers.Get(d.name)
	if err != nil {
		d.logRequest(method, "").Warnf("Cannot locate volume driver: %v", err.Error())
		d.errorResponse(w, err)
		return
	}

	request, err := d.decodeMount(method, w, r)
	if err != nil {
		return
	}

	vol, err := d.volFromName(request.Name)
	if err != nil {
		e := d.volNotFound(method, request.Name, err, w)
		d.errorResponse(w, e)
		return
	}

	mountpoint := d.mountpath(request)
	err = v.Unmount(vol.Id, mountpoint)
	if err != nil {
		d.logRequest(method, request.Name).Warnf("Cannot unmount volume %v, %v",
			mountpoint, err)
		d.errorResponse(w, err)
		return
	}

	if v.Type() == api.DriverType_DRIVER_TYPE_BLOCK {
		_ = v.Detach(vol.Id)
	}
	d.emptyResponse(w)
}
<|endoftext|>


<commit_before>package server

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"golang.org/x/net/context"

	"encoding/json"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/iron-io/functions/api/models"
	"github.com/iron-io/functions/api/runner"
	titancommon "github.com/iron-io/titan/common"
	"github.com/satori/go.uuid"
)

func handleSpecial(c *gin.Context) {
	ctx := c.MustGet("ctx").(context.Context)
	log := titancommon.Logger(ctx)

	err := Api.UseSpecialHandlers(c)
	if err != nil {
		log.WithError(err).Errorln("Error using special handler!")
		// todo: what do we do here? Should probably return a 500 or something
	}
}

func handleRunner(c *gin.Context) {
	if strings.HasPrefix(c.Request.URL.Path, "/v1") {
		c.Status(http.StatusNotFound)
		return
	}

	ctx := c.MustGet("ctx").(context.Context)
	log := titancommon.Logger(ctx)

	reqID := uuid.NewV5(uuid.Nil, fmt.Sprintf("%s%s%d", c.Request.RemoteAddr, c.Request.URL.Path, time.Now().Unix())).String()
	c.Set("reqID", reqID) // todo: put this in the ctx instead of gin's

	log = log.WithFields(logrus.Fields{"request_id": reqID})

	var err error

	var payload []byte
	if c.Request.Method == "POST" || c.Request.Method == "PUT" {
		payload, err = ioutil.ReadAll(c.Request.Body)
	} else if c.Request.Method == "GET" {
		qPL := c.Request.URL.Query()["payload"]
		if len(qPL) > 0 {
			payload = []byte(qPL[0])
		}
	}

	if len(payload) > 0 {
		var emptyJSON map[string]interface{}
		if err := json.Unmarshal(payload, &emptyJSON); err != nil {
			log.WithError(err).Error(models.ErrInvalidJSON)
			c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
			return
		}
	}

	log.WithField("payload", string(payload)).Debug("Got payload")

	appName := c.Param("app")
	if appName == "" {
		// check context, app can be added via special handlers
		a, ok := c.Get("app")
		if ok {
			appName = a.(string)
		}
	}
	// if still no appName, we gotta exit
	if appName == "" {
		log.WithError(err).Error(models.ErrAppsNotFound)
		c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsNotFound))
		return
	}
	route := c.Param("route")
	if route == "" {
		route = c.Request.URL.Path
	}

	log.WithFields(logrus.Fields{"app": appName, "path": route}).Debug("Finding route on datastore")

	app, err := Api.Datastore.GetApp(appName)
	if err != nil || app == nil {
		log.WithError(err).Error(models.ErrAppsNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrAppsNotFound))
		return
	}

	routes, err := Api.Datastore.GetRoutesByApp(appName, &models.RouteFilter{})
	if err != nil {
		log.WithError(err).Error(models.ErrRoutesList)
		c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesList))
		return
	}

	if routes == nil || len(routes) == 0 {
		log.WithError(err).Error(models.ErrRunnerRouteNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
		return
	}

	log.WithField("routes", routes).Debug("Got routes from datastore")
	for _, el := range routes {
		if params, match := matchRoute(el.Path, route); match {
			var stdout, stderr bytes.Buffer

			envVars := map[string]string{
				"METHOD":      c.Request.Method,
				"ROUTE":       el.Path,
				"PAYLOAD":     string(payload),
				"REQUEST_URL": c.Request.URL.String(),
			}

			// app config
			for k, v := range app.Config {
				envVars["CONFIG_"+strings.ToUpper(k)] = v
			}

			// route config
			for k, v := range el.Config {
				envVars["CONFIG_"+strings.ToUpper(k)] = v
			}

			// params
			for _, param := range params {
				envVars["PARAM_"+strings.ToUpper(param.Key)] = param.Value
			}

			cfg := &runner.Config{
				Image:   el.Image,
				Timeout: 30 * time.Second,
				ID:      reqID,
				AppName: appName,
				Stdout:  &stdout,
				Stderr:  &stderr,
				Env:     envVars,
			}

			if result, err := Api.Runner.Run(c, cfg); err != nil {
				log.WithError(err).Error(models.ErrRunnerRunRoute)
				c.JSON(http.StatusInternalServerError, simpleError(models.ErrRunnerRunRoute))
			} else {
				for k, v := range el.Headers {
					c.Header(k, v[0])
				}

				if result.Status() == "success" {
					c.Data(http.StatusOK, "", stdout.Bytes())
				} else {
					log.WithFields(logrus.Fields{"app": appName, "route": el, "req_id": reqID}).Debug(stderr.String())
					c.AbortWithStatus(http.StatusInternalServerError)
				}
			}
			return
		}
	}

}

var fakeHandler = func(http.ResponseWriter, *http.Request, Params) {}

func matchRoute(baseRoute, route string) (Params, bool) {
	tree := &node{}
	tree.addRoute(baseRoute, fakeHandler)
	handler, p, _ := tree.getValue(route)
	if handler == nil {
		return nil, false
	}

	return p, true
}
<commit_msg>fix runner route not found<commit_after>package server

import (
	"bytes"
	"fmt"
	"io/ioutil"
	"net/http"
	"strings"
	"time"

	"golang.org/x/net/context"

	"encoding/json"

	"github.com/Sirupsen/logrus"
	"github.com/gin-gonic/gin"
	"github.com/iron-io/functions/api/models"
	"github.com/iron-io/functions/api/runner"
	titancommon "github.com/iron-io/titan/common"
	"github.com/satori/go.uuid"
)

func handleSpecial(c *gin.Context) {
	ctx := c.MustGet("ctx").(context.Context)
	log := titancommon.Logger(ctx)

	err := Api.UseSpecialHandlers(c)
	if err != nil {
		log.WithError(err).Errorln("Error using special handler!")
		// todo: what do we do here? Should probably return a 500 or something
	}
}

func handleRunner(c *gin.Context) {
	if strings.HasPrefix(c.Request.URL.Path, "/v1") {
		c.Status(http.StatusNotFound)
		return
	}

	ctx := c.MustGet("ctx").(context.Context)
	log := titancommon.Logger(ctx)

	reqID := uuid.NewV5(uuid.Nil, fmt.Sprintf("%s%s%d", c.Request.RemoteAddr, c.Request.URL.Path, time.Now().Unix())).String()
	c.Set("reqID", reqID) // todo: put this in the ctx instead of gin's

	log = log.WithFields(logrus.Fields{"request_id": reqID})

	var err error

	var payload []byte
	if c.Request.Method == "POST" || c.Request.Method == "PUT" {
		payload, err = ioutil.ReadAll(c.Request.Body)
	} else if c.Request.Method == "GET" {
		qPL := c.Request.URL.Query()["payload"]
		if len(qPL) > 0 {
			payload = []byte(qPL[0])
		}
	}

	if len(payload) > 0 {
		var emptyJSON map[string]interface{}
		if err := json.Unmarshal(payload, &emptyJSON); err != nil {
			log.WithError(err).Error(models.ErrInvalidJSON)
			c.JSON(http.StatusBadRequest, simpleError(models.ErrInvalidJSON))
			return
		}
	}

	log.WithField("payload", string(payload)).Debug("Got payload")

	appName := c.Param("app")
	if appName == "" {
		// check context, app can be added via special handlers
		a, ok := c.Get("app")
		if ok {
			appName = a.(string)
		}
	}
	// if still no appName, we gotta exit
	if appName == "" {
		log.WithError(err).Error(models.ErrAppsNotFound)
		c.JSON(http.StatusBadRequest, simpleError(models.ErrAppsNotFound))
		return
	}
	route := c.Param("route")
	if route == "" {
		route = c.Request.URL.Path
	}

	log.WithFields(logrus.Fields{"app": appName, "path": route}).Debug("Finding route on datastore")

	app, err := Api.Datastore.GetApp(appName)
	if err != nil || app == nil {
		log.WithError(err).Error(models.ErrAppsNotFound)
		c.JSON(http.StatusNotFound, simpleError(models.ErrAppsNotFound))
		return
	}

	routes, err := Api.Datastore.GetRoutesByApp(appName, &models.RouteFilter{})
	if err != nil {
		log.WithError(err).Error(models.ErrRoutesList)
		c.JSON(http.StatusInternalServerError, simpleError(models.ErrRoutesList))
		return
	}

	log.WithField("routes", routes).Debug("Got routes from datastore")
	for _, el := range routes {
		if params, match := matchRoute(el.Path, route); match {
			var stdout, stderr bytes.Buffer

			envVars := map[string]string{
				"METHOD":      c.Request.Method,
				"ROUTE":       el.Path,
				"PAYLOAD":     string(payload),
				"REQUEST_URL": c.Request.URL.String(),
			}

			// app config
			for k, v := range app.Config {
				envVars["CONFIG_"+strings.ToUpper(k)] = v
			}

			// route config
			for k, v := range el.Config {
				envVars["CONFIG_"+strings.ToUpper(k)] = v
			}

			// params
			for _, param := range params {
				envVars["PARAM_"+strings.ToUpper(param.Key)] = param.Value
			}

			cfg := &runner.Config{
				Image:   el.Image,
				Timeout: 30 * time.Second,
				ID:      reqID,
				AppName: appName,
				Stdout:  &stdout,
				Stderr:  &stderr,
				Env:     envVars,
			}

			if result, err := Api.Runner.Run(c, cfg); err != nil {
				log.WithError(err).Error(models.ErrRunnerRunRoute)
				c.JSON(http.StatusInternalServerError, simpleError(models.ErrRunnerRunRoute))
			} else {
				for k, v := range el.Headers {
					c.Header(k, v[0])
				}

				if result.Status() == "success" {
					c.Data(http.StatusOK, "", stdout.Bytes())
				} else {
					log.WithFields(logrus.Fields{"app": appName, "route": el, "req_id": reqID}).Debug(stderr.String())
					c.AbortWithStatus(http.StatusInternalServerError)
				}
			}
			return
		}
	}

	log.WithError(err).Error(models.ErrRunnerRouteNotFound)
	c.JSON(http.StatusNotFound, simpleError(models.ErrRunnerRouteNotFound))
}

var fakeHandler = func(http.ResponseWriter, *http.Request, Params) {}

func matchRoute(baseRoute, route string) (Params, bool) {
	tree := &node{}
	tree.addRoute(baseRoute, fakeHandler)
	handler, p, _ := tree.getValue(route)
	if handler == nil {
		return nil, false
	}

	return p, true
}
<|endoftext|>


<commit_before>package server

import (
	"github.com/mattimo/fluxtftp"
	"encoding/json"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
)

type controlServer struct {
	listen *net.UnixListener
	flux   *FluxServer
}

func newControlServer(addr *net.UnixAddr) (*controlServer, error) {
	server := &controlServer{}
	l, err := net.ListenUnix("unix", addr)
	server.listen = l
	if err != nil {
		return nil, err
	}
	return server, nil
}

func (c *controlServer) sendResponse(conn net.Conn, errCode int64, message string) error {
	resp := &fluxtftp.ControlResponse{Error: errCode, Message: message}
	wireResp, err := json.Marshal(resp)
	if err != nil {
		panic("Could nor marshal response")
	}
	_, err = conn.Write(wireResp)
	if err != nil {
		log.Printf("Could not write Response (val=%d): %s\n", errCode, err)
		return err
	}
	return nil
}

func (c *controlServer) handleControl(conn net.Conn) {
	defer conn.Close()
	decoder := json.NewDecoder(conn)
	req := &fluxtftp.ControlRequest{}
	err := decoder.Decode(req)
	if err != nil || req.Verb == "" || len(req.Data) == 0 {
		if err != nil {
			log.Println("Control: Received:", req)
		}
		c.sendResponse(conn, fluxtftp.ControlErrMalformed, "Something went wrong")
		return
	}

	err = c.flux.PutDefault(req.Data)
	if err != nil {
		c.sendResponse(conn, fluxtftp.ControlErrUnknown, err.Error())
		return
	}
	err = c.sendResponse(conn, fluxtftp.ControlErrOk, "")
	if err != nil {
		return
	}
	log.Println("Control: Handled message from ", conn.RemoteAddr())

	return

}

func (c *controlServer) acceptControl() (net.Conn, error) {
	conn, err := c.listen.Accept()
	if err != nil {
		return nil, err
	}

	return conn, nil
}

func closeSocket(l *net.UnixListener) {
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func(c chan os.Signal) {
		sig := <-c
		log.Printf("Caught signal %s: shutting down.", sig)
		l.Close()
		os.Exit(0)
	}(sigc)
}

func (flux *FluxServer) StartControl() error {

	addr, err := net.ResolveUnixAddr("unix", flux.Conf.ControlAddr)
	if err != nil {
		return err
	}

	server, err := newControlServer(addr)
	if err != nil {
		return err
	}
	closeSocket(server.listen)
	server.flux = flux

	for {
		conn, err := server.acceptControl()
		if err != nil {
			log.Println("failed Control Request:", err)
			continue
		}

		go server.handleControl(conn)
	}
}
<commit_msg>server: Improved logging for control requests<commit_after>package server

import (
	"github.com/mattimo/fluxtftp"
	"encoding/json"
	"log"
	"net"
	"os"
	"os/signal"
	"syscall"
)

type controlServer struct {
	listen *net.UnixListener
	flux   *FluxServer
}

func newControlServer(addr *net.UnixAddr) (*controlServer, error) {
	server := &controlServer{}
	l, err := net.ListenUnix("unix", addr)
	server.listen = l
	if err != nil {
		return nil, err
	}
	return server, nil
}

func (c *controlServer) sendResponse(conn net.Conn, errCode int64, message string) error {
	resp := &fluxtftp.ControlResponse{Error: errCode, Message: message}
	wireResp, err := json.Marshal(resp)
	if err != nil {
		log.Printf("Error: Could nor marshal response")
	}
	_, err = conn.Write(wireResp)
	if err != nil {
		log.Printf("Could not write Response (val=%d): %s\n", errCode, err)
		return err
	}
	return nil
}

func (c *controlServer) handleControl(conn net.Conn) {
	defer conn.Close()
	decoder := json.NewDecoder(conn)
	req := &fluxtftp.ControlRequest{}
	err := decoder.Decode(req)
	if err != nil || req.Verb == "" || len(req.Data) == 0 {
		if err != nil {
			log.Println("Control: Received:", req)
		} else {
			log.Printf("Error: handling Request failed: Verb=%s len=%d",
					req.Verb, len(req.Data))
		}
		c.sendResponse(conn, fluxtftp.ControlErrMalformed, "Something went wrong")
		return
	}

	err = c.flux.PutDefault(req.Data)
	if err != nil {
		c.sendResponse(conn, fluxtftp.ControlErrUnknown, err.Error())
		return
	}
	err = c.sendResponse(conn, fluxtftp.ControlErrOk, "")
	if err != nil {
		return
	}
	log.Println("Control: Handled message from ", conn.RemoteAddr())

	return

}

func (c *controlServer) acceptControl() (net.Conn, error) {
	conn, err := c.listen.Accept()
	if err != nil {
		return nil, err
	}

	return conn, nil
}

func closeSocket(l *net.UnixListener) {
	sigc := make(chan os.Signal, 1)
	signal.Notify(sigc, os.Interrupt, os.Kill, syscall.SIGTERM)
	go func(c chan os.Signal) {
		sig := <-c
		log.Printf("Caught signal %s: shutting down.", sig)
		l.Close()
		os.Exit(0)
	}(sigc)
}

func (flux *FluxServer) StartControl() error {

	addr, err := net.ResolveUnixAddr("unix", flux.Conf.ControlAddr)
	if err != nil {
		return err
	}

	server, err := newControlServer(addr)
	if err != nil {
		return err
	}
	closeSocket(server.listen)
	server.flux = flux

	for {
		conn, err := server.acceptControl()
		if err != nil {
			log.Println("failed Control Request:", err)
			continue
		}

		go server.handleControl(conn)
	}
}
<|endoftext|>