<commit_before>package main

import (
	"flag"
	"fmt"
	"github.com/Symantec/Dominator/lib/constants"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/fsbench"
	"github.com/Symantec/Dominator/lib/fsrateio"
	"github.com/Symantec/Dominator/lib/html"
	"github.com/Symantec/Dominator/lib/logbuf"
	"github.com/Symantec/Dominator/lib/memstats"
	"github.com/Symantec/Dominator/lib/rateio"
	"github.com/Symantec/Dominator/sub/httpd"
	"github.com/Symantec/Dominator/sub/rpcd"
	"github.com/Symantec/Dominator/sub/scanner"
	"io"
	"log"
	"os"
	"os/signal"
	"path"
	"runtime"
	"strconv"
	"syscall"
)

var (
	portNum = flag.Uint("portNum", constants.SubPortNumber,
		"Port number to allocate and listen on for HTTP/RPC")
	logbufLines = flag.Uint("logbufLines", 1024,
		"Number of lines to store in the log buffer")
	rootDir = flag.String("rootDir", "/",
		"Name of root of directory tree to manage")
	showStats = flag.Bool("showStats", false,
		"If true, show statistics after each cycle")
	subdDir = flag.String("subdDir", "/.subd",
		"Name of subd private directory. This must be on the same file-system as rootDir")
	unshare = flag.Bool("unshare", true, "Internal use only.")
)

func sanityCheck() bool {
	r_devnum, err := fsbench.GetDevnumForFile(*rootDir)
	if err != nil {
		fmt.Printf("Unable to get device number for: %s\t%s\n", *rootDir, err)
		return false
	}
	s_devnum, err := fsbench.GetDevnumForFile(*subdDir)
	if err != nil {
		fmt.Printf("Unable to get device number for: %s\t%s\n", *subdDir, err)
		return false
	}
	if r_devnum != s_devnum {
		fmt.Printf("rootDir and subdDir must be on the same file-system\n")
		return false
	}
	return true
}

func createDirectory(dirname string) bool {
	err := os.MkdirAll(dirname, 0750)
	if err != nil {
		fmt.Printf("Unable to create directory: %s\t%s\n", dirname, err)
		return false
	}
	return true
}

func mountTmpfs(dirname string) bool {
	var statfs syscall.Statfs_t
	err := syscall.Statfs(dirname, &statfs)
	if err != nil {
		fmt.Printf("Unable to create Statfs: %s\t%s\n", dirname, err)
		return false
	}
	if statfs.Type != 0x01021994 {
		err := syscall.Mount("none", dirname, "tmpfs", 0,
			"size=65536,mode=0750")
		if err == nil {
			fmt.Printf("Mounted tmpfs on: %s\n", dirname)
		} else {
			fmt.Printf("Unable to mount tmpfs on: %s\t%s\n", dirname, err)
			return false
		}
	}
	return true
}

func unshareAndBind(workingRootDir string) bool {
	if *unshare {
		// Re-exec myself using the unshare syscall while on a locked thread.
		// This hack is required because syscall.Unshare() operates on only one
		// thread in the process, and Go switches execution between threads
		// randomly. Thus, the namespace can be suddenly switched for running
		// code. This is an aspect of Go that was not well thought out.
		runtime.LockOSThread()
		err := syscall.Unshare(syscall.CLONE_NEWNS)
		if err != nil {
			fmt.Printf("Unable to unshare mount namesace\t%s\n", err)
			return false
		}
		args := append(os.Args, "-unshare=false")
		err = syscall.Exec(args[0], args, os.Environ())
		if err != nil {
			fmt.Printf("Unable to Exec:%s\t%s\n", args[0], err)
			return false
		}
	}
	err := syscall.Mount("none", "/", "", syscall.MS_REC|syscall.MS_PRIVATE, "")
	if err != nil {
		fmt.Printf("Unable to set mount sharing to private\t%s\n", err)
		return false
	}
	syscall.Unmount(workingRootDir, 0)
	err = syscall.Mount(*rootDir, workingRootDir, "", syscall.MS_BIND, "")
	if err != nil {
		fmt.Printf("Unable to bind mount %s to %s\t%s\n",
			*rootDir, workingRootDir, err)
		return false
	}
	return true
}

func getCachedFsSpeed(workingRootDir string, cacheDirname string) (bytesPerSecond,
	blocksPerSecond uint64, computed, ok bool) {
	bytesPerSecond = 0
	blocksPerSecond = 0
	devnum, err := fsbench.GetDevnumForFile(workingRootDir)
	if err != nil {
		fmt.Printf("Unable to get device number for: %s\t%s\n",
			workingRootDir, err)
		return 0, 0, false, false
	}
	fsbenchDir := path.Join(cacheDirname, "fsbench")
	if !createDirectory(fsbenchDir) {
		return 0, 0, false, false
	}
	cacheFilename := path.Join(fsbenchDir, strconv.FormatUint(devnum, 16))
	file, err := os.Open(cacheFilename)
	if err == nil {
		n, err := fmt.Fscanf(file, "%d %d", &bytesPerSecond, &blocksPerSecond)
		file.Close()
		if n == 2 || err == nil {
			return bytesPerSecond, blocksPerSecond, false, true
		}
	}
	bytesPerSecond, blocksPerSecond, err = fsbench.GetReadSpeed(workingRootDir)
	if err != nil {
		fmt.Printf("Unable to measure read speed\t%s\n", err)
		return 0, 0, true, false
	}
	file, err = os.Create(cacheFilename)
	if err != nil {
		fmt.Printf("Unable to open: %s for write\t%s\n", cacheFilename, err)
		return 0, 0, true, false
	}
	fmt.Fprintf(file, "%d %d\n", bytesPerSecond, blocksPerSecond)
	file.Close()
	return bytesPerSecond, blocksPerSecond, true, true
}

func getCachedNetworkSpeed(cacheFilename string) uint64 {
	file, err := os.Open(cacheFilename)
	if err != nil {
		return 0
	}
	defer file.Close()
	var bytesPerSecond uint64
	n, err := fmt.Fscanf(file, "%d", &bytesPerSecond)
	if n == 1 || err == nil {
		return bytesPerSecond
	}
	return 0
}

type DumpableFileSystemHistory struct {
	fsh *scanner.FileSystemHistory
}

func (fsh *DumpableFileSystemHistory) WriteHtml(writer io.Writer) {
	fs := fsh.fsh.FileSystem()
	if fs == nil {
		return
	}
	fmt.Fprintln(writer, "
")
	fs.DebugWrite(writer, "")
	fmt.Fprintln(writer, "
")
}

func main() {
	flag.Parse()
	workingRootDir := path.Join(*subdDir, "root")
	objectsDir := path.Join(*subdDir, "objects")
	tmpDir := path.Join(*subdDir, "tmp")
	netbenchFilename := path.Join(*subdDir, "netbench")
	if !createDirectory(workingRootDir) {
		os.Exit(1)
	}
	if !sanityCheck() {
		os.Exit(1)
	}
	if !createDirectory(objectsDir) {
		os.Exit(1)
	}
	if !createDirectory(tmpDir) {
		os.Exit(1)
	}
	if !mountTmpfs(tmpDir) {
		os.Exit(1)
	}
	if !unshareAndBind(workingRootDir) {
		os.Exit(1)
	}
	bytesPerSecond, blocksPerSecond, firstScan, ok := getCachedFsSpeed(
		workingRootDir, tmpDir)
	if !ok {
		os.Exit(1)
	}
	circularBuffer := logbuf.New(*logbufLines)
	logger := log.New(circularBuffer, "", log.LstdFlags)
	var configuration scanner.Configuration
	var err error
	configuration.ScanFilter, err = filter.NewFilter(constants.ScanExcludeList)
	if err != nil {
		fmt.Printf("Unable to set default scan exclusions\t%s\n", err)
		os.Exit(1)
	}
	configuration.FsScanContext = fsrateio.NewReaderContext(bytesPerSecond,
		blocksPerSecond, 0)
	defaultSpeed := configuration.FsScanContext.GetContext().SpeedPercent()
	if firstScan {
		configuration.FsScanContext.GetContext().SetSpeedPercent(100)
	}
	if *showStats {
		fmt.Println(configuration.FsScanContext)
	}
	var fsh scanner.FileSystemHistory
	fsChannel := scanner.StartScannerDaemon(workingRootDir, objectsDir,
		&configuration, logger)
	networkReaderContext := rateio.NewReaderContext(
		getCachedNetworkSpeed(netbenchFilename),
		constants.DefaultNetworkSpeedPercent, &rateio.ReadMeasurer{})
	configuration.NetworkReaderContext = networkReaderContext
	rescanObjectCacheChannel := rpcd.Setup(&configuration, &fsh, objectsDir,
		networkReaderContext, netbenchFilename, logger)
	httpd.AddHtmlWriter(&fsh)
	httpd.AddHtmlWriter(&configuration)
	httpd.AddHtmlWriter(circularBuffer)
	html.RegisterHtmlWriterForPattern("/dumpFileSystem", "Scanned File System",
		&DumpableFileSystemHistory{&fsh})
	err = httpd.StartServer(*portNum)
	if err != nil {
		fmt.Printf("Unable to create http server\t%s\n", err)
		os.Exit(1)
	}
	fsh.Update(nil)
	invalidateNextScanObjectCache := false
	sighupChannel := make(chan os.Signal)
	signal.Notify(sighupChannel, syscall.SIGHUP)
	for iter := 0; true; {
		select {
		case <-sighupChannel:
			err = syscall.Exec(os.Args[0], os.Args, os.Environ())
			if err != nil {
				logger.Printf("Unable to Exec:%s\t%s\n", os.Args[0], err)
			}
		case fs := <-fsChannel:
			if *showStats {
				fmt.Printf("Completed cycle: %d\n", iter)
			}
			if invalidateNextScanObjectCache {
				fs.ScanObjectCache()
				invalidateNextScanObjectCache = false
			}
			fsh.Update(fs)
			iter++
			runtime.GC() // An opportune time to take out the garbage.
			if *showStats {
				fmt.Print(fsh)
				fmt.Print(fsh.FileSystem())
				memstats.WriteMemoryStats(os.Stdout)
				fmt.Println()
			}
			if firstScan {
				configuration.FsScanContext.GetContext().SetSpeedPercent(
					defaultSpeed)
				firstScan = false
				if *showStats {
					fmt.Println(configuration.FsScanContext)
				}
			}
		case <-rescanObjectCacheChannel:
			invalidateNextScanObjectCache = true
			fsh.UpdateObjectCacheOnly()
		}
	}
}
<commit_msg>Add -pidfile option to subd.<commit_after>package main

import (
	"flag"
	"fmt"
	"github.com/Symantec/Dominator/lib/constants"
	"github.com/Symantec/Dominator/lib/filter"
	"github.com/Symantec/Dominator/lib/fsbench"
	"github.com/Symantec/Dominator/lib/fsrateio"
	"github.com/Symantec/Dominator/lib/html"
	"github.com/Symantec/Dominator/lib/logbuf"
	"github.com/Symantec/Dominator/lib/memstats"
	"github.com/Symantec/Dominator/lib/rateio"
	"github.com/Symantec/Dominator/sub/httpd"
	"github.com/Symantec/Dominator/sub/rpcd"
	"github.com/Symantec/Dominator/sub/scanner"
	"io"
	"log"
	"os"
	"os/signal"
	"path"
	"runtime"
	"strconv"
	"syscall"
)

var (
	pidfile = flag.String("pidfile", "/var/run/subd.pid",
		"Name of file to write my PID to")
	portNum = flag.Uint("portNum", constants.SubPortNumber,
		"Port number to allocate and listen on for HTTP/RPC")
	logbufLines = flag.Uint("logbufLines", 1024,
		"Number of lines to store in the log buffer")
	rootDir = flag.String("rootDir", "/",
		"Name of root of directory tree to manage")
	showStats = flag.Bool("showStats", false,
		"If true, show statistics after each cycle")
	subdDir = flag.String("subdDir", "/.subd",
		"Name of subd private directory. This must be on the same file-system as rootDir")
	unshare = flag.Bool("unshare", true, "Internal use only.")
)

func sanityCheck() bool {
	r_devnum, err := fsbench.GetDevnumForFile(*rootDir)
	if err != nil {
		fmt.Printf("Unable to get device number for: %s\t%s\n", *rootDir, err)
		return false
	}
	s_devnum, err := fsbench.GetDevnumForFile(*subdDir)
	if err != nil {
		fmt.Printf("Unable to get device number for: %s\t%s\n", *subdDir, err)
		return false
	}
	if r_devnum != s_devnum {
		fmt.Printf("rootDir and subdDir must be on the same file-system\n")
		return false
	}
	return true
}

func createDirectory(dirname string) bool {
	err := os.MkdirAll(dirname, 0750)
	if err != nil {
		fmt.Printf("Unable to create directory: %s\t%s\n", dirname, err)
		return false
	}
	return true
}

func mountTmpfs(dirname string) bool {
	var statfs syscall.Statfs_t
	err := syscall.Statfs(dirname, &statfs)
	if err != nil {
		fmt.Printf("Unable to create Statfs: %s\t%s\n", dirname, err)
		return false
	}
	if statfs.Type != 0x01021994 {
		err := syscall.Mount("none", dirname, "tmpfs", 0,
			"size=65536,mode=0750")
		if err == nil {
			fmt.Printf("Mounted tmpfs on: %s\n", dirname)
		} else {
			fmt.Printf("Unable to mount tmpfs on: %s\t%s\n", dirname, err)
			return false
		}
	}
	return true
}

func unshareAndBind(workingRootDir string) bool {
	if *unshare {
		// Re-exec myself using the unshare syscall while on a locked thread.
		// This hack is required because syscall.Unshare() operates on only one
		// thread in the process, and Go switches execution between threads
		// randomly. Thus, the namespace can be suddenly switched for running
		// code. This is an aspect of Go that was not well thought out.
		runtime.LockOSThread()
		err := syscall.Unshare(syscall.CLONE_NEWNS)
		if err != nil {
			fmt.Printf("Unable to unshare mount namesace\t%s\n", err)
			return false
		}
		args := append(os.Args, "-unshare=false")
		err = syscall.Exec(args[0], args, os.Environ())
		if err != nil {
			fmt.Printf("Unable to Exec:%s\t%s\n", args[0], err)
			return false
		}
	}
	err := syscall.Mount("none", "/", "", syscall.MS_REC|syscall.MS_PRIVATE, "")
	if err != nil {
		fmt.Printf("Unable to set mount sharing to private\t%s\n", err)
		return false
	}
	syscall.Unmount(workingRootDir, 0)
	err = syscall.Mount(*rootDir, workingRootDir, "", syscall.MS_BIND, "")
	if err != nil {
		fmt.Printf("Unable to bind mount %s to %s\t%s\n",
			*rootDir, workingRootDir, err)
		return false
	}
	return true
}

func getCachedFsSpeed(workingRootDir string, cacheDirname string) (bytesPerSecond,
	blocksPerSecond uint64, computed, ok bool) {
	bytesPerSecond = 0
	blocksPerSecond = 0
	devnum, err := fsbench.GetDevnumForFile(workingRootDir)
	if err != nil {
		fmt.Printf("Unable to get device number for: %s\t%s\n",
			workingRootDir, err)
		return 0, 0, false, false
	}
	fsbenchDir := path.Join(cacheDirname, "fsbench")
	if !createDirectory(fsbenchDir) {
		return 0, 0, false, false
	}
	cacheFilename := path.Join(fsbenchDir, strconv.FormatUint(devnum, 16))
	file, err := os.Open(cacheFilename)
	if err == nil {
		n, err := fmt.Fscanf(file, "%d %d", &bytesPerSecond, &blocksPerSecond)
		file.Close()
		if n == 2 || err == nil {
			return bytesPerSecond, blocksPerSecond, false, true
		}
	}
	bytesPerSecond, blocksPerSecond, err = fsbench.GetReadSpeed(workingRootDir)
	if err != nil {
		fmt.Printf("Unable to measure read speed\t%s\n", err)
		return 0, 0, true, false
	}
	file, err = os.Create(cacheFilename)
	if err != nil {
		fmt.Printf("Unable to open: %s for write\t%s\n", cacheFilename, err)
		return 0, 0, true, false
	}
	fmt.Fprintf(file, "%d %d\n", bytesPerSecond, blocksPerSecond)
	file.Close()
	return bytesPerSecond, blocksPerSecond, true, true
}

func getCachedNetworkSpeed(cacheFilename string) uint64 {
	file, err := os.Open(cacheFilename)
	if err != nil {
		return 0
	}
	defer file.Close()
	var bytesPerSecond uint64
	n, err := fmt.Fscanf(file, "%d", &bytesPerSecond)
	if n == 1 || err == nil {
		return bytesPerSecond
	}
	return 0
}

type DumpableFileSystemHistory struct {
	fsh *scanner.FileSystemHistory
}

func (fsh *DumpableFileSystemHistory) WriteHtml(writer io.Writer) {
	fs := fsh.fsh.FileSystem()
	if fs == nil {
		return
	}
	fmt.Fprintln(writer, "
")
	fs.DebugWrite(writer, "")
	fmt.Fprintln(writer, "
")
}

func gracefulCleanup() {
	os.Remove(*pidfile)
	os.Exit(1)
}

func writePidfile() {
	file, err := os.Create(*pidfile)
	if err != nil {
		fmt.Println(err.Error())
		os.Exit(1)
	}
	defer file.Close()
	fmt.Fprintln(file, os.Getpid())
}

func main() {
	flag.Parse()
	workingRootDir := path.Join(*subdDir, "root")
	objectsDir := path.Join(*subdDir, "objects")
	tmpDir := path.Join(*subdDir, "tmp")
	netbenchFilename := path.Join(*subdDir, "netbench")
	if !createDirectory(workingRootDir) {
		os.Exit(1)
	}
	if !sanityCheck() {
		os.Exit(1)
	}
	if !createDirectory(objectsDir) {
		os.Exit(1)
	}
	if !createDirectory(tmpDir) {
		os.Exit(1)
	}
	if !mountTmpfs(tmpDir) {
		os.Exit(1)
	}
	if !unshareAndBind(workingRootDir) {
		os.Exit(1)
	}
	bytesPerSecond, blocksPerSecond, firstScan, ok := getCachedFsSpeed(
		workingRootDir, tmpDir)
	if !ok {
		os.Exit(1)
	}
	circularBuffer := logbuf.New(*logbufLines)
	logger := log.New(circularBuffer, "", log.LstdFlags)
	var configuration scanner.Configuration
	var err error
	configuration.ScanFilter, err = filter.NewFilter(constants.ScanExcludeList)
	if err != nil {
		fmt.Printf("Unable to set default scan exclusions\t%s\n", err)
		os.Exit(1)
	}
	configuration.FsScanContext = fsrateio.NewReaderContext(bytesPerSecond,
		blocksPerSecond, 0)
	defaultSpeed := configuration.FsScanContext.GetContext().SpeedPercent()
	if firstScan {
		configuration.FsScanContext.GetContext().SetSpeedPercent(100)
	}
	if *showStats {
		fmt.Println(configuration.FsScanContext)
	}
	var fsh scanner.FileSystemHistory
	fsChannel := scanner.StartScannerDaemon(workingRootDir, objectsDir,
		&configuration, logger)
	networkReaderContext := rateio.NewReaderContext(
		getCachedNetworkSpeed(netbenchFilename),
		constants.DefaultNetworkSpeedPercent, &rateio.ReadMeasurer{})
	configuration.NetworkReaderContext = networkReaderContext
	rescanObjectCacheChannel := rpcd.Setup(&configuration, &fsh, objectsDir,
		networkReaderContext, netbenchFilename, logger)
	httpd.AddHtmlWriter(&fsh)
	httpd.AddHtmlWriter(&configuration)
	httpd.AddHtmlWriter(circularBuffer)
	html.RegisterHtmlWriterForPattern("/dumpFileSystem", "Scanned File System",
		&DumpableFileSystemHistory{&fsh})
	err = httpd.StartServer(*portNum)
	if err != nil {
		fmt.Printf("Unable to create http server\t%s\n", err)
		os.Exit(1)
	}
	fsh.Update(nil)
	invalidateNextScanObjectCache := false
	sighupChannel := make(chan os.Signal)
	signal.Notify(sighupChannel, syscall.SIGHUP)
	sigtermChannel := make(chan os.Signal)
	signal.Notify(sigtermChannel, syscall.SIGTERM, syscall.SIGINT)
	writePidfile()
	for iter := 0; true; {
		select {
		case <-sighupChannel:
			err = syscall.Exec(os.Args[0], os.Args, os.Environ())
			if err != nil {
				logger.Printf("Unable to Exec:%s\t%s\n", os.Args[0], err)
			}
		case <-sigtermChannel:
			gracefulCleanup()
		case fs := <-fsChannel:
			if *showStats {
				fmt.Printf("Completed cycle: %d\n", iter)
			}
			if invalidateNextScanObjectCache {
				fs.ScanObjectCache()
				invalidateNextScanObjectCache = false
			}
			fsh.Update(fs)
			iter++
			runtime.GC() // An opportune time to take out the garbage.
			if *showStats {
				fmt.Print(fsh)
				fmt.Print(fsh.FileSystem())
				memstats.WriteMemoryStats(os.Stdout)
				fmt.Println()
			}
			if firstScan {
				configuration.FsScanContext.GetContext().SetSpeedPercent(
					defaultSpeed)
				firstScan = false
				if *showStats {
					fmt.Println(configuration.FsScanContext)
				}
			}
		case <-rescanObjectCacheChannel:
			invalidateNextScanObjectCache = true
			fsh.UpdateObjectCacheOnly()
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2012 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.

/*
tsuru is a command line tool for application developers.

It provides some commands that allow a developer to register himself/herself,
manage teams, apps and services.

Usage:

	% tsuru <command> [args]

The currently available commands are (grouped by subject):

	target            changes or retrive the current tsuru server
	version           displays current tsuru version

	user-create       creates a new user
	login             authenticates the user with tsuru server
	logout            finishes the session with tsuru server
	key-add           adds a public key to tsuru deploy server
	key-remove        removes a public key from tsuru deploy server

	team-create       creates a new team (adding the current user to it automatically)
	team-list         list teams that the user is member
	team-user-add     adds a user to a team
	team-user-remove  removes a user from a team

	app-create        creates an app
	app-remove        removes an app
	app-list          lists apps that the user has access (see app-grant and team-user-add)
	app-grant         allows a team to have access to an app
	app-revoke        revokes access to an app from a team
	log               shows log for an app
	run               runs a command in all units of an app
	restart           restarts the app's application server

	env-get           display environment variables for an app
	env-set           set environment variable(s) to an app
	env-unset         unset environment variable(s) from an app

	bind              binds an app to a service instance
	unbind            unbinds an app from a service instance

	service-list      list all services, and instances of each service
	service-add       creates a new instance of a service
	service-remove    removes a instance of a service
	service-status    checks the status of a service instance
	service-info      list instances of a service, and apps binded to each instance
	service-doc       displays documentation for a service

Use "tsuru help <command>" for more information about a command.

Change or retrieve remote tsuru server

Usage:

	% tsuru target [target]

This command should be used to set current tsuru target, or retrieve current
target.

The target is the tsuru server to which all operations will be directed to.

Check current version

Usage:

	% tsuru version

This command returns the current version of tsuru command.

Create a user

Usage:

	% tsuru user-create <email>

user-create creates a user within tsuru remote server. It will ask for the
password before issue the request.

Authenticate within remote tsuru server

Usage:

	% tsuru login <email>

Login will ask for the password and check if the user is successfully
authenticated. If so, the token generated by the tsuru server will be stored in
${HOME}/.tsuru_token.

All tsuru actions require the user to be authenticated (except login and
user-create, obviously).

Logout from remote tsuru server

Usage:

	% tsuru logout

Logout will delete the token file and terminate the session within tsuru
server.

Add SSH public key to tsuru's git server

Usage:

	% tsuru key-add [${HOME}/.ssh/id_rsa.pub]

key-add sends your public key to tsuru's git server. By default, it will try
send a public RSA key, located at ${HOME}/.ssh/id_rsa.pub. If you want to send
other file, you can call it with the path to the file. For example:

	% tsuru key-add /etc/my-keys/id_dsa.pub

The key will be added to the current logged in user.

Remove SSH public key from tsuru's git server

Usage:

	% tsuru key-remove [${HOME}/.ssh/id_rsa.pub]

key-remove removes your public key from tsuru's git server. By default, it will
try to remove a key that match you public RSA key located at
${HOME}/.ssh/id_rsa.pub. If you want to remove a key located somewhere else,
you can pass it as parameter to key-remove:

	% tsuru key-remove /etc/my-keys/id_dsa.pub

The key will be removed from the current logged in user.

Create a new team for the user

Usage:

	% tsuru team-create <teamname>

team-create will create a team for the user. Tsuru requires a user to be a
member of at least one team in order to create an app or a service instance.

When you create a team, you're automatically member of this team.

List teams that the user is member of

Usage:

	% tsuru team-list

team-list will list all teams that you are member of.

Add a user to a team

Usage:

	% tsuru team-user-add <teamname> <useremail>

team-user-add adds a user to a team. You need to be a member of the team to be
able to add another user to it.

Remove a user from a team

Usage:

	% tsuru team-user-remove <teamname> <useremail>

team-user-remove removes a user from a team. You need to be a member of the
team to be able to remove a user from it.

A team can never have 0 users. If you are the last member of a team, you can't
remove yourself from it.

Create an app

Usage:

	% tsuru app-create <appname> <platform>

app-create will create a new app using the given name and platform. For tsuru,
a platform is a Juju charm. To check the available platforms/charms, check this
URL: https://github.com/globocom/charms/tree/master/precise.

In order to create an app, you need to be member of at least one team. All
teams that you are member (see "tsuru team-list") will be able to access the
app.

Remove an app

Usage:

	% tsuru app-remove <appname>

app-remove removes an app. If the app is binded to any service instance, it
will be unbinded before be removed (see "tsuru unbind"). You need to be a
member of a team that has access to the app to be able to remove it (you are
able to remove any app that you see in "tsuru app-list").

List apps that the user has access to

Usage:

	% tsuru app-list

app-list will list all apps that you have access to. App access is controlled
by teams. If your team has access to an app, then you have access to it.

Allow a team to access an app

Usage:

	% tsuru app-grant <appname> <teamname>

app-grant will allow a team to access an app. You need to be a member of a team
that has access to the app to allow another team to access it.

Revoke from a team access to an app

Usage:

	% tsuru app-revoke <appname> <teamname>

app-revoke will revoke the permission to access an app from a team. You need to
have access to the app to revoke access from a team.

An app cannot be orphaned, so it will always have at least one authorized team.

See app's logs

Usage:

	% tsuru log <appname>

Log will show log entries for an app. These logs are not related to the code of
the app itself, but to actions of the app in tsuru server (deployments,
restarts, etc.).

Run an arbitrary command in the app machine

Usage:

	% tsuru run <appname> <command> [commandarg1] [commandarg2] ... [commandargn]

Run will run an arbitrary command in the app machine. Base directory for all
commands is the root of the app. For example, in a Django app, "tsuru run" may
show the following output:

	% tsuru run polls ls -l
	app.conf
	brogui
	deploy
	foo
	__init__.py
	__init__.pyc
	main.go
	manage.py
	settings.py
	settings.pyc
	templates
	urls.py
	urls.pyc

Restart the app's application server

Usage:

	% tsuru restart <appname>

Restart will call the restart hook from the app platform (the "restart" hook
from the Juju charm).
*/
package documentation
<commit_msg>cmd/tsuru/docs: change title of app-list section<commit_after>// Copyright 2012 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.

/*
tsuru is a command line tool for application developers.

It provides some commands that allow a developer to register himself/herself,
manage teams, apps and services.

Usage:

	% tsuru <command> [args]

The currently available commands are (grouped by subject):

	target            changes or retrive the current tsuru server
	version           displays current tsuru version

	user-create       creates a new user
	login             authenticates the user with tsuru server
	logout            finishes the session with tsuru server
	key-add           adds a public key to tsuru deploy server
	key-remove        removes a public key from tsuru deploy server

	team-create       creates a new team (adding the current user to it automatically)
	team-list         list teams that the user is member
	team-user-add     adds a user to a team
	team-user-remove  removes a user from a team

	app-create        creates an app
	app-remove        removes an app
	app-list          lists apps that the user has access (see app-grant and team-user-add)
	app-grant         allows a team to have access to an app
	app-revoke        revokes access to an app from a team
	log               shows log for an app
	run               runs a command in all units of an app
	restart           restarts the app's application server

	env-get           display environment variables for an app
	env-set           set environment variable(s) to an app
	env-unset         unset environment variable(s) from an app

	bind              binds an app to a service instance
	unbind            unbinds an app from a service instance

	service-list      list all services, and instances of each service
	service-add       creates a new instance of a service
	service-remove    removes a instance of a service
	service-status    checks the status of a service instance
	service-info      list instances of a service, and apps binded to each instance
	service-doc       displays documentation for a service

Use "tsuru help <command>" for more information about a command.

Change or retrieve remote tsuru server

Usage:

	% tsuru target [target]

This command should be used to set current tsuru target, or retrieve current
target.

The target is the tsuru server to which all operations will be directed to.

Check current version

Usage:

	% tsuru version

This command returns the current version of tsuru command.

Create a user

Usage:

	% tsuru user-create <email>

user-create creates a user within tsuru remote server. It will ask for the
password before issue the request.

Authenticate within remote tsuru server

Usage:

	% tsuru login <email>

Login will ask for the password and check if the user is successfully
authenticated. If so, the token generated by the tsuru server will be stored in
${HOME}/.tsuru_token.

All tsuru actions require the user to be authenticated (except login and
user-create, obviously).

Logout from remote tsuru server

Usage:

	% tsuru logout

Logout will delete the token file and terminate the session within tsuru
server.

Add SSH public key to tsuru's git server

Usage:

	% tsuru key-add [${HOME}/.ssh/id_rsa.pub]

key-add sends your public key to tsuru's git server. By default, it will try
send a public RSA key, located at ${HOME}/.ssh/id_rsa.pub. If you want to send
other file, you can call it with the path to the file. For example:

	% tsuru key-add /etc/my-keys/id_dsa.pub

The key will be added to the current logged in user.

Remove SSH public key from tsuru's git server

Usage:

	% tsuru key-remove [${HOME}/.ssh/id_rsa.pub]

key-remove removes your public key from tsuru's git server. By default, it will
try to remove a key that match you public RSA key located at
${HOME}/.ssh/id_rsa.pub. If you want to remove a key located somewhere else,
you can pass it as parameter to key-remove:

	% tsuru key-remove /etc/my-keys/id_dsa.pub

The key will be removed from the current logged in user.

Create a new team for the user

Usage:

	% tsuru team-create <teamname>

team-create will create a team for the user. Tsuru requires a user to be a
member of at least one team in order to create an app or a service instance.

When you create a team, you're automatically member of this team.

List teams that the user is member of

Usage:

	% tsuru team-list

team-list will list all teams that you are member of.

Add a user to a team

Usage:

	% tsuru team-user-add <teamname> <useremail>

team-user-add adds a user to a team. You need to be a member of the team to be
able to add another user to it.

Remove a user from a team

Usage:

	% tsuru team-user-remove <teamname> <useremail>

team-user-remove removes a user from a team. You need to be a member of the
team to be able to remove a user from it.

A team can never have 0 users. If you are the last member of a team, you can't
remove yourself from it.

Create an app

Usage:

	% tsuru app-create <appname> <platform>

app-create will create a new app using the given name and platform. For tsuru,
a platform is a Juju charm. To check the available platforms/charms, check this
URL: https://github.com/globocom/charms/tree/master/precise.

In order to create an app, you need to be member of at least one team. All
teams that you are member (see "tsuru team-list") will be able to access the
app.

Remove an app

Usage:

	% tsuru app-remove <appname>

app-remove removes an app. If the app is binded to any service instance, it
will be unbinded before be removed (see "tsuru unbind"). You need to be a
member of a team that has access to the app to be able to remove it (you are
able to remove any app that you see in "tsuru app-list").

List apps that you has access to

Usage:

	% tsuru app-list

app-list will list all apps that you have access to. App access is controlled
by teams. If your team has access to an app, then you have access to it.

Allow a team to access an app

Usage:

	% tsuru app-grant <appname> <teamname>

app-grant will allow a team to access an app. You need to be a member of a team
that has access to the app to allow another team to access it.

Revoke from a team access to an app

Usage:

	% tsuru app-revoke <appname> <teamname>

app-revoke will revoke the permission to access an app from a team. You need to
have access to the app to revoke access from a team.

An app cannot be orphaned, so it will always have at least one authorized team.

See app's logs

Usage:

	% tsuru log <appname>

Log will show log entries for an app. These logs are not related to the code of
the app itself, but to actions of the app in tsuru server (deployments,
restarts, etc.).

Run an arbitrary command in the app machine

Usage:

	% tsuru run <appname> <command> [commandarg1] [commandarg2] ... [commandargn]

Run will run an arbitrary command in the app machine. Base directory for all
commands is the root of the app. For example, in a Django app, "tsuru run" may
show the following output:

	% tsuru run polls ls -l
	app.conf
	brogui
	deploy
	foo
	__init__.py
	__init__.pyc
	main.go
	manage.py
	settings.py
	settings.pyc
	templates
	urls.py
	urls.pyc

Restart the app's application server

Usage:

	% tsuru restart <appname>

Restart will call the restart hook from the app platform (the "restart" hook
from the Juju charm).
*/
package documentation
<|endoftext|>


<commit_before>package goworld

import (
	"time"

	"github.com/xiaonanln/goTimer"
	"github.com/xiaonanln/goworld/components/game"
	"github.com/xiaonanln/goworld/engine/common"
	"github.com/xiaonanln/goworld/engine/config"
	"github.com/xiaonanln/goworld/engine/entity"
	"github.com/xiaonanln/goworld/engine/gwlog"
	"github.com/xiaonanln/goworld/engine/kvdb"
	"github.com/xiaonanln/goworld/engine/post"
	"github.com/xiaonanln/goworld/engine/service"
	"github.com/xiaonanln/goworld/engine/storage"
)

// GoWorld是一个分布式的游戏服务器，提供

type GameID = uint16
type GateID = uint16
type DispatcherID uint16

// Export useful types
type Vector3 = entity.Vector3

// Entity 类型代表游戏服务器中的一个对象
type Entity = entity.Entity

// Space 类型代表一个游戏服务器中的一个场景。一个场景中可以包含多个Entity。Space和其中的Entity都存在于一个game进程中。
type Space = entity.Space
type EntityID = common.EntityID

// Run runs the server endless loop
//
// This is the main routine for the server and all entity logic,
// and this function never quit
func Run() {
	game.Run()
}

// RegisterEntity registers the entity type so that entities can be created or loaded
//
// returns the entity type description object which can be used to define more properties
// of entity type
func RegisterEntity(typeName string, entityPtr entity.IEntity) *entity.EntityTypeDesc {
	return entity.RegisterEntity(typeName, entityPtr, false)
}

// RegisterService registeres an service type
// After registeration, the service entity will be created automatically on some game
func RegisterService(typeName string, entityPtr entity.IEntity) {
	service.RegisterService(typeName, entityPtr)
}

// CreateSpaceAnywhere creates a space with specified kind in any game server
func CreateSpaceAnywhere(kind int) EntityID {
	if kind == 0 {
		gwlog.Panicf("Can not create nil space with kind=0. Game will create 1 nil space automatically.")
	}
	return entity.CreateSpaceSomewhere(kind)
}

// CreateSpaceLocally creates a space with specified kind in the local game server
//
// returns the space EntityID
func CreateSpaceLocally(kind int) *Space {
	if kind == 0 {
		gwlog.Panicf("Can not create nil space with kind=0. Game will create 1 nil space automatically.")
	}
	return entity.CreateSpaceLocally(kind)
}

// CreateEntityLocally creates a entity on the local server
//
// returns EntityID
func CreateEntityLocally(typeName string) *Entity {
	return entity.CreateEntityLocally(typeName, nil)
}

// CreateEntitySomewhere creates a entity on any server
func CreateEntityAnywhere(typeName string) EntityID {
	return entity.CreateEntitySomewhere(typeName)
}

// LoadEntityAnywhere loads the specified entity from entity storage
func LoadEntityAnywhere(typeName string, entityID EntityID) {
	entity.LoadEntityAnywhere(typeName, entityID)
}

// LoadEntityOnGame loads entity in the specified game
// If the entity already exists on any server, LoadEntityOnGame will do nothing
func LoadEntityOnGame(typeName string, entityID EntityID, gameid GameID) {
	entity.LoadEntityOnGame(typeName, entityID, gameid)
}

// LoadEntityLocally load entity in the local game
// If the entity already exists on any server, LoadEntityLocally will do nothing
func LoadEntityLocally(typeName string, entityID EntityID) {
	entity.LoadEntityOnGame(typeName, entityID, GetGameID())
}

// ListEntityIDs gets all saved entity ids in storage, may take long time and block the main routine
//
// returns result in callback
func ListEntityIDs(typeName string, callback storage.ListCallbackFunc) {
	storage.ListEntityIDs(typeName, callback)
}

// Exists checks if entityID exists in entity storage
//
// returns result in callback
func Exists(typeName string, entityID EntityID, callback storage.ExistsCallbackFunc) {
	storage.Exists(typeName, entityID, callback)
}

// GetEntity gets the entity by EntityID
func GetEntity(id EntityID) *Entity {
	return entity.GetEntity(id)
}

// GetSpace gets the space by ID
func GetSpace(id EntityID) *Space {
	return entity.GetSpace(id)
}

// GetGameID gets the local server ID
//
// server ID is a uint16 number starts from 1, which should be different for each servers
// server ID is also in the game config section name of goworld.ini
func GetGameID() uint16 {
	return game.GetGameID()
}

// MapAttr creates a new MapAttr
func MapAttr() *entity.MapAttr {
	return entity.NewMapAttr()
}

// ListAttr creates a new ListAttr
func ListAttr() *entity.ListAttr {
	return entity.NewListAttr()
}

// RegisterSpace registers the space entity type.
//
// All spaces will be created as an instance of this type
func RegisterSpace(spacePtr entity.ISpace) {
	entity.RegisterSpace(spacePtr)
}

// Entities gets all entities as an EntityMap (do not modify it!)
func Entities() entity.EntityMap {
	return entity.Entities()
}

// Call other entities
func Call(id EntityID, method string, args ...interface{}) {
	entity.Call(id, method, args)
}

// CallService calls a service entity
func CallService(serviceName string, method string, args ...interface{}) {
	service.CallService(serviceName, method, args)
}

// GetServiceEntityID returns the entityid of the service
func GetServiceEntityID(serviceName string) common.EntityID {
	return service.GetServiceEntityID(serviceName)
}

// CallNilSpaces calls methods of all nil spaces on all games
func CallNilSpaces(method string, args ...interface{}) {
	entity.CallNilSpaces(method, args, game.GetGameID())
}

// GetNilSpaceID returns the Entity ID of nil space on the specified game
func GetNilSpaceID(gameid GameID) EntityID {
	return entity.GetNilSpaceID(gameid)
}

// GetNilSpace returns the nil space on this game
// Nil space is a special space with Kind = 0. Nil space is the default space for all created entities.
// Each game has one nil space with fixed EntityID for each game, which can be acquired by calling `GetNilSpaceID`
//
// Since nil game exists on each game with fixed EntityID, an entity can migrate to target game by calling `e.EnterSpace(GetNilSpaceID(gameid), Vector3{})`
func GetNilSpace() *Space {
	return entity.GetNilSpace()
}

// GetKVDB gets value of key from KVDB
func GetKVDB(key string, callback kvdb.KVDBGetCallback) {
	kvdb.Get(key, callback)
}

// PutKVDB puts key-value to KVDB
func PutKVDB(key string, val string, callback kvdb.KVDBPutCallback) {
	kvdb.Put(key, val, callback)
}

// GetOrPut gets value of key from KVDB, if val not exists or is "", put key-value to KVDB.
func GetOrPutKVDB(key string, val string, callback kvdb.KVDBGetOrPutCallback) {
	kvdb.GetOrPut(key, val, callback)
}

// ListGameIDs returns all game IDs
func ListGameIDs() []uint16 {
	return config.GetGameIDs()
}

// AddTimer adds a timer to be executed after specified duration
func AddCallback(d time.Duration, callback func()) {
	timer.AddCallback(d, callback)
}

// AddTimer adds a repeat timer to be executed every specified duration
func AddTimer(d time.Duration, callback func()) {
	timer.AddTimer(d, callback)
}

// Post posts a callback to be executed
// It is almost same as AddCallback(0, callback)
func Post(callback post.PostCallback) {
	post.Post(callback)
}
<commit_msg>CreateSpaceOnGame<commit_after>package goworld

import (
	"time"

	"github.com/xiaonanln/goTimer"
	"github.com/xiaonanln/goworld/components/game"
	"github.com/xiaonanln/goworld/engine/common"
	"github.com/xiaonanln/goworld/engine/config"
	"github.com/xiaonanln/goworld/engine/entity"
	"github.com/xiaonanln/goworld/engine/gwlog"
	"github.com/xiaonanln/goworld/engine/kvdb"
	"github.com/xiaonanln/goworld/engine/post"
	"github.com/xiaonanln/goworld/engine/service"
	"github.com/xiaonanln/goworld/engine/storage"
)

// GoWorld是一个分布式的游戏服务器，提供

type GameID = uint16
type GateID = uint16
type DispatcherID uint16

// Export useful types
type Vector3 = entity.Vector3

// Entity 类型代表游戏服务器中的一个对象
type Entity = entity.Entity

// Space 类型代表一个游戏服务器中的一个场景。一个场景中可以包含多个Entity。Space和其中的Entity都存在于一个game进程中。
type Space = entity.Space
type EntityID = common.EntityID

// Run runs the server endless loop
//
// This is the main routine for the server and all entity logic,
// and this function never quit
func Run() {
	game.Run()
}

// RegisterEntity registers the entity type so that entities can be created or loaded
//
// returns the entity type description object which can be used to define more properties
// of entity type
func RegisterEntity(typeName string, entityPtr entity.IEntity) *entity.EntityTypeDesc {
	return entity.RegisterEntity(typeName, entityPtr, false)
}

// RegisterService registeres an service type
// After registeration, the service entity will be created automatically on some game
func RegisterService(typeName string, entityPtr entity.IEntity) {
	service.RegisterService(typeName, entityPtr)
}

// CreateSpaceAnywhere creates a space with specified kind in any game server
func CreateSpaceAnywhere(kind int) EntityID {
	if kind == 0 {
		gwlog.Panicf("Can not create nil space with kind=0. Game will create 1 nil space automatically.")
	}
	return entity.CreateSpaceSomewhere(0, kind)
}

// CreateSpaceOnGame creates a space with specified kind on the specified game
//
// returns the space EntityID
func CreateSpaceOnGame(gameid uint16, kind int) EntityID {
	return entity.CreateSpaceSomewhere(gameid, kind)
}

// CreateSpaceLocally creates a space with specified kind in the local game server
//
// returns the space EntityID
func CreateSpaceLocally(kind int) *Space {
	if kind == 0 {
		gwlog.Panicf("Can not create nil space with kind=0. Game will create 1 nil space automatically.")
	}
	return entity.CreateSpaceLocally(kind)
}

// CreateEntityLocally creates a entity on the local server
//
// returns EntityID
func CreateEntityLocally(typeName string) *Entity {
	return entity.CreateEntityLocally(typeName, nil)
}

// CreateEntitySomewhere creates a entity on any server
func CreateEntityAnywhere(typeName string) EntityID {
	return entity.CreateEntitySomewhere(0, typeName)
}

func CreateEntityOnGame(gameid uint16, typeName string) EntityID {
	return entity.CreateEntitySomewhere(gameid, typeName)
}

// LoadEntityAnywhere loads the specified entity from entity storage
func LoadEntityAnywhere(typeName string, entityID EntityID) {
	entity.LoadEntityAnywhere(typeName, entityID)
}

// LoadEntityOnGame loads entity in the specified game
// If the entity already exists on any server, LoadEntityOnGame will do nothing
func LoadEntityOnGame(typeName string, entityID EntityID, gameid GameID) {
	entity.LoadEntityOnGame(typeName, entityID, gameid)
}

// LoadEntityLocally load entity in the local game
// If the entity already exists on any server, LoadEntityLocally will do nothing
func LoadEntityLocally(typeName string, entityID EntityID) {
	entity.LoadEntityOnGame(typeName, entityID, GetGameID())
}

// ListEntityIDs gets all saved entity ids in storage, may take long time and block the main routine
//
// returns result in callback
func ListEntityIDs(typeName string, callback storage.ListCallbackFunc) {
	storage.ListEntityIDs(typeName, callback)
}

// Exists checks if entityID exists in entity storage
//
// returns result in callback
func Exists(typeName string, entityID EntityID, callback storage.ExistsCallbackFunc) {
	storage.Exists(typeName, entityID, callback)
}

// GetEntity gets the entity by EntityID
func GetEntity(id EntityID) *Entity {
	return entity.GetEntity(id)
}

// GetSpace gets the space by ID
func GetSpace(id EntityID) *Space {
	return entity.GetSpace(id)
}

// GetGameID gets the local server ID
//
// server ID is a uint16 number starts from 1, which should be different for each servers
// server ID is also in the game config section name of goworld.ini
func GetGameID() uint16 {
	return game.GetGameID()
}

// MapAttr creates a new MapAttr
func MapAttr() *entity.MapAttr {
	return entity.NewMapAttr()
}

// ListAttr creates a new ListAttr
func ListAttr() *entity.ListAttr {
	return entity.NewListAttr()
}

// RegisterSpace registers the space entity type.
//
// All spaces will be created as an instance of this type
func RegisterSpace(spacePtr entity.ISpace) {
	entity.RegisterSpace(spacePtr)
}

// Entities gets all entities as an EntityMap (do not modify it!)
func Entities() entity.EntityMap {
	return entity.Entities()
}

// Call other entities
func Call(id EntityID, method string, args ...interface{}) {
	entity.Call(id, method, args)
}

// CallService calls a service entity
func CallService(serviceName string, method string, args ...interface{}) {
	service.CallService(serviceName, method, args)
}

// GetServiceEntityID returns the entityid of the service
func GetServiceEntityID(serviceName string) common.EntityID {
	return service.GetServiceEntityID(serviceName)
}

// CallNilSpaces calls methods of all nil spaces on all games
func CallNilSpaces(method string, args ...interface{}) {
	entity.CallNilSpaces(method, args, game.GetGameID())
}

// GetNilSpaceID returns the Entity ID of nil space on the specified game
func GetNilSpaceID(gameid GameID) EntityID {
	return entity.GetNilSpaceID(gameid)
}

// GetNilSpace returns the nil space on this game
// Nil space is a special space with Kind = 0. Nil space is the default space for all created entities.
// Each game has one nil space with fixed EntityID for each game, which can be acquired by calling `GetNilSpaceID`
//
// Since nil game exists on each game with fixed EntityID, an entity can migrate to target game by calling `e.EnterSpace(GetNilSpaceID(gameid), Vector3{})`
func GetNilSpace() *Space {
	return entity.GetNilSpace()
}

// GetKVDB gets value of key from KVDB
func GetKVDB(key string, callback kvdb.KVDBGetCallback) {
	kvdb.Get(key, callback)
}

// PutKVDB puts key-value to KVDB
func PutKVDB(key string, val string, callback kvdb.KVDBPutCallback) {
	kvdb.Put(key, val, callback)
}

// GetOrPut gets value of key from KVDB, if val not exists or is "", put key-value to KVDB.
func GetOrPutKVDB(key string, val string, callback kvdb.KVDBGetOrPutCallback) {
	kvdb.GetOrPut(key, val, callback)
}

// ListGameIDs returns all game IDs
func ListGameIDs() []uint16 {
	return config.GetGameIDs()
}

// AddTimer adds a timer to be executed after specified duration
func AddCallback(d time.Duration, callback func()) {
	timer.AddCallback(d, callback)
}

// AddTimer adds a repeat timer to be executed every specified duration
func AddTimer(d time.Duration, callback func()) {
	timer.AddTimer(d, callback)
}

// Post posts a callback to be executed
// It is almost same as AddCallback(0, callback)
func Post(callback post.PostCallback) {
	post.Post(callback)
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// 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 shader

import (
	"github.com/go-gl/gl"
	"unsafe"
)

func glMatrix(m [4][4]float64) [16]float32 {
	result := [16]float32{}
	for j := 0; j < 4; j++ {
		for i := 0; i < 4; i++ {
			result[i+j*4] = float32(m[i][j])
		}
	}
	return result
}

type Matrix interface {
	Element(i, j int) float64
}

type vbo struct {
	indexBuffer  gl.Buffer
	vertexBuffer gl.Buffer
}

const size = 10000

var currentVBO *vbo

const uint16Size = 2
const short32Size = 4

func DrawTexture(native gl.Texture, projectionMatrix [4][4]float64, quads []TextureQuad, geo Matrix, color Matrix) {
	// TODO: Check len(quads) and gl.MAX_ELEMENTS_INDICES?
	const stride = 4 * 4
	if currentVBO == nil {
		initialize()
		currentVBO = &vbo{
			indexBuffer:  gl.GenBuffer(),
			vertexBuffer: gl.GenBuffer(),
		}

		currentVBO.indexBuffer.Bind(gl.ELEMENT_ARRAY_BUFFER)
		indices := make([]uint16, 6*size)
		for i := uint16(0); i < size; i++ {
			indices[6*i+0] = 4*i + 0
			indices[6*i+1] = 4*i + 1
			indices[6*i+2] = 4*i + 2
			indices[6*i+3] = 4*i + 1
			indices[6*i+4] = 4*i + 2
			indices[6*i+5] = 4*i + 3
		}
		gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, uint16Size*len(indices), indices, gl.STATIC_DRAW)

		currentVBO.vertexBuffer.Bind(gl.ARRAY_BUFFER)
		vertices := make([]float32, stride*size)
		gl.BufferData(gl.ARRAY_BUFFER, short32Size*len(vertices), vertices, gl.DYNAMIC_DRAW)
	}

	if len(quads) == 0 {
		return
	}
	// TODO: Check performance
	program := useProgramColorMatrix(glMatrix(projectionMatrix), geo, color)

	gl.ActiveTexture(gl.TEXTURE0)
	native.Bind(gl.TEXTURE_2D)

	vertexAttrLocation := getAttributeLocation(program, "vertex")
	texCoordAttrLocation := getAttributeLocation(program, "tex_coord")

	gl.EnableClientState(gl.VERTEX_ARRAY)
	gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
	vertexAttrLocation.EnableArray()
	texCoordAttrLocation.EnableArray()
	defer func() {
		texCoordAttrLocation.DisableArray()
		vertexAttrLocation.DisableArray()
		gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)
		gl.DisableClientState(gl.VERTEX_ARRAY)
	}()

	// TODO: Fix this dirty hack after fixing https://github.com/go-gl/gl/issues/174
	v := (*int)(unsafe.Pointer(uintptr(4 * 0)))
	t := (*int)(unsafe.Pointer(uintptr(4 * 2)))
	vertexAttrLocation.AttribPointer(2, gl.FLOAT, false, stride, v)
	texCoordAttrLocation.AttribPointer(2, gl.FLOAT, false, stride, t)

	vertices := []float32{}
	for _, quad := range quads {
		x0 := quad.VertexX0
		x1 := quad.VertexX1
		y0 := quad.VertexY0
		y1 := quad.VertexY1
		u0 := quad.TextureCoordU0
		u1 := quad.TextureCoordU1
		v0 := quad.TextureCoordV0
		v1 := quad.TextureCoordV1
		vertices = append(vertices,
			x0, y0, u0, v0,
			x1, y0, u1, v0,
			x0, y1, u0, v1,
			x1, y1, u1, v1,
		)
	}
	gl.BufferSubData(gl.ARRAY_BUFFER, 0, short32Size*len(vertices), vertices)
	// TODO: Pass 0 after fixing the gl bug
	gl.DrawElements(gl.TRIANGLES, 6*len(quads), gl.UNSIGNED_SHORT, nil)

	gl.Flush()
}
<commit_msg>Remove currentVBO<commit_after>// Copyright 2014 Hajime Hoshi
//
// 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 shader

import (
	"github.com/go-gl/gl"
	"unsafe"
)

func glMatrix(m [4][4]float64) [16]float32 {
	result := [16]float32{}
	for j := 0; j < 4; j++ {
		for i := 0; i < 4; i++ {
			result[i+j*4] = float32(m[i][j])
		}
	}
	return result
}

type Matrix interface {
	Element(i, j int) float64
}

var initialized = false

const size = 10000
const uint16Size = 2
const short32Size = 4

func DrawTexture(native gl.Texture, projectionMatrix [4][4]float64, quads []TextureQuad, geo Matrix, color Matrix) {
	// TODO: Check len(quads) and gl.MAX_ELEMENTS_INDICES?
	const stride = 4 * 4
	if !initialized {
		initialize()

		indexBuffer := gl.GenBuffer()
		indexBuffer.Bind(gl.ELEMENT_ARRAY_BUFFER)
		indices := make([]uint16, 6*size)
		for i := uint16(0); i < size; i++ {
			indices[6*i+0] = 4*i + 0
			indices[6*i+1] = 4*i + 1
			indices[6*i+2] = 4*i + 2
			indices[6*i+3] = 4*i + 1
			indices[6*i+4] = 4*i + 2
			indices[6*i+5] = 4*i + 3
		}
		gl.BufferData(gl.ELEMENT_ARRAY_BUFFER, uint16Size*len(indices), indices, gl.STATIC_DRAW)

		vertexBuffer := gl.GenBuffer()
		vertexBuffer.Bind(gl.ARRAY_BUFFER)
		vertices := make([]float32, stride*size)
		gl.BufferData(gl.ARRAY_BUFFER, short32Size*len(vertices), vertices, gl.DYNAMIC_DRAW)

		initialized = true
	}

	if len(quads) == 0 {
		return
	}
	// TODO: Check performance
	program := useProgramColorMatrix(glMatrix(projectionMatrix), geo, color)

	gl.ActiveTexture(gl.TEXTURE0)
	native.Bind(gl.TEXTURE_2D)

	vertexAttrLocation := getAttributeLocation(program, "vertex")
	texCoordAttrLocation := getAttributeLocation(program, "tex_coord")

	gl.EnableClientState(gl.VERTEX_ARRAY)
	gl.EnableClientState(gl.TEXTURE_COORD_ARRAY)
	vertexAttrLocation.EnableArray()
	texCoordAttrLocation.EnableArray()
	defer func() {
		texCoordAttrLocation.DisableArray()
		vertexAttrLocation.DisableArray()
		gl.DisableClientState(gl.TEXTURE_COORD_ARRAY)
		gl.DisableClientState(gl.VERTEX_ARRAY)
	}()

	// TODO: Fix this dirty hack after fixing https://github.com/go-gl/gl/issues/174
	v := (*int)(unsafe.Pointer(uintptr(4 * 0)))
	t := (*int)(unsafe.Pointer(uintptr(4 * 2)))
	vertexAttrLocation.AttribPointer(2, gl.FLOAT, false, stride, v)
	texCoordAttrLocation.AttribPointer(2, gl.FLOAT, false, stride, t)

	vertices := []float32{}
	for _, quad := range quads {
		x0 := quad.VertexX0
		x1 := quad.VertexX1
		y0 := quad.VertexY0
		y1 := quad.VertexY1
		u0 := quad.TextureCoordU0
		u1 := quad.TextureCoordU1
		v0 := quad.TextureCoordV0
		v1 := quad.TextureCoordV1
		vertices = append(vertices,
			x0, y0, u0, v0,
			x1, y0, u1, v0,
			x0, y1, u0, v1,
			x1, y1, u1, v1,
		)
	}
	gl.BufferSubData(gl.ARRAY_BUFFER, 0, short32Size*len(vertices), vertices)
	// TODO: Pass 0 after fixing the gl bug
	gl.DrawElements(gl.TRIANGLES, 6*len(quads), gl.UNSIGNED_SHORT, nil)

	gl.Flush()
}
<|endoftext|>


<commit_before>/*
Package horo is context friendly, Simple Web framework.
*/
package horo

import (
	"errors"
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/k2wanko/horo/log"
	"golang.org/x/net/context"
)

type (
	// Horo freamwork instance.
	Horo struct {
		ErrorHandler ErrorHandlerFunc
		Logger       log.Logger

		router     *httprouter.Router
		middleware []MiddlewareFunc
	}

	// HandlerFunc is server HTTP requests.
	HandlerFunc func(context.Context) error

	// MiddlewareFunc is process middleware.
	MiddlewareFunc func(HandlerFunc) HandlerFunc

	// ErrorHandlerFunc is error handling function.
	ErrorHandlerFunc func(context.Context, error)

	// HTTPError handling a request.
	HTTPError struct {
		Code    int
		Message string
	}
)

var (
	// ErrNotContext is thrown if the context does not have Horo context.
	ErrNotContext = errors.New("Not Context")

	// ErrInvalidRedirectCode is thrown if invalid redirect code.
	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
)

// New is create Horo instance.
func New() (h *Horo) {
	h = &Horo{
		ErrorHandler: DefaultErrorHandler,
		Logger:       log.DefaultLogger,
		router:       httprouter.New(),
	}
	return
}

// Use is add middleware.
func (h *Horo) Use(mw ...MiddlewareFunc) {
	h.middleware = append(h.middleware, mw...)
}

// GET registers a new GET handler
func (h *Horo) GET(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.GET(path, hf.hrHandle(h, mw...))
}

// POST registers a new POST handler
func (h *Horo) POST(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.POST(path, hf.hrHandle(h, mw...))
}

// PATCH registers a new PATCH handler
func (h *Horo) PATCH(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.PATCH(path, hf.hrHandle(h, mw...))
}

// PUT registers a new PUT handler
func (h *Horo) PUT(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.PUT(path, hf.hrHandle(h, mw...))
}

// OPTIONS registers a new OPTIONS handler
func (h *Horo) OPTIONS(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.OPTIONS(path, hf.hrHandle(h, mw...))
}

// DELETE registers a new DELETE handler
func (h *Horo) DELETE(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.DELETE(path, hf.hrHandle(h, mw...))
}

// HEAD registers a new HEAD handler
func (h *Horo) HEAD(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.HEAD(path, hf.hrHandle(h, mw...))
}

func (hf HandlerFunc) hrHandle(h *Horo, mw ...MiddlewareFunc) httprouter.Handle {
	return func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		w := &response{ResponseWriter: rw}
		var c context.Context = &horoCtx{
			Context: context.Background(),
			w:       w,
			r:       r,
			ps:      ps,
		}

		c, cancel := context.WithCancel(c)

		c = log.WithContext(c, h.Logger)

		hf := func(c context.Context) error {
			mw := append(h.middleware, mw...)
			for i := len(mw) - 1; i >= 0; i-- {
				hf = mw[i](hf)
			}

			return hf(c)
		}

		if err := hf(c); err != nil {
			h.ErrorHandler(c, err)
		}

		cancel()
	}
}

// DefaultErrorHandler invoke HTTP Error Handler
func DefaultErrorHandler(c context.Context, err error) {
	code := 500
	msg := http.StatusText(code)
	if he, ok := err.(*HTTPError); ok {
		code = he.Code
		msg = he.Message
	}
	Text(c, code, msg)
}

// ServeHTTP implements http.Handler interface.
func (h *Horo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.router.ServeHTTP(w, r)
}

// ListenAndServe is Start HTTP Server
func (h *Horo) ListenAndServe(addr string) error {
	return http.ListenAndServe(addr, h)
}

func (e *HTTPError) Error() string {
	return e.Message
}
<commit_msg>Modify package doc<commit_after>/*
Package horo is context friendly, Simple Web framework.

Basic Example:
    package main

    import (
        "net/http"

        "golang.org/x/net/context"

        "github.com/k2wanko/horo"
        "github.com/k2wanko/horo/middleware"
    )

    func Index(c context.Context) error {
        return horo.Text(c, http.StatusOK, "Hello World!")
    }

    func main() {
        h := horo.New()
        h.Use(middleware.Logger())

        h.GET("/", Index)

        h.ListenAndServe(":8080")
    }

Google App Engine Example:
    package main

    import (
        "net/http"

        "golang.org/x/net/context"

        "github.com/k2wanko/horo"
    )

    func Index(c context.Context) error {
        return horo.Text(c, http.StatusOK, "Hello World!")
    }

    func init() {
        h := horo.New()

        h.GET("/", Index)

        http.Handle("/", h)
    }
*/
package horo

import (
	"errors"
	"net/http"

	"github.com/julienschmidt/httprouter"
	"github.com/k2wanko/horo/log"
	"golang.org/x/net/context"
)

type (
	// Horo freamwork instance.
	Horo struct {
		ErrorHandler ErrorHandlerFunc
		Logger       log.Logger

		router     *httprouter.Router
		middleware []MiddlewareFunc
	}

	// HandlerFunc is server HTTP requests.
	HandlerFunc func(context.Context) error

	// MiddlewareFunc is process middleware.
	MiddlewareFunc func(HandlerFunc) HandlerFunc

	// ErrorHandlerFunc is error handling function.
	ErrorHandlerFunc func(context.Context, error)

	// HTTPError handling a request.
	HTTPError struct {
		Code    int
		Message string
	}
)

var (
	// ErrNotContext is thrown if the context does not have Horo context.
	ErrNotContext = errors.New("Not Context")

	// ErrInvalidRedirectCode is thrown if invalid redirect code.
	ErrInvalidRedirectCode = errors.New("invalid redirect status code")
)

// New is create Horo instance.
func New() (h *Horo) {
	h = &Horo{
		ErrorHandler: DefaultErrorHandler,
		Logger:       log.DefaultLogger,
		router:       httprouter.New(),
	}
	return
}

// Use is add middleware.
func (h *Horo) Use(mw ...MiddlewareFunc) {
	h.middleware = append(h.middleware, mw...)
}

// GET registers a new GET handler
func (h *Horo) GET(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.GET(path, hf.hrHandle(h, mw...))
}

// POST registers a new POST handler
func (h *Horo) POST(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.POST(path, hf.hrHandle(h, mw...))
}

// PATCH registers a new PATCH handler
func (h *Horo) PATCH(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.PATCH(path, hf.hrHandle(h, mw...))
}

// PUT registers a new PUT handler
func (h *Horo) PUT(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.PUT(path, hf.hrHandle(h, mw...))
}

// OPTIONS registers a new OPTIONS handler
func (h *Horo) OPTIONS(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.OPTIONS(path, hf.hrHandle(h, mw...))
}

// DELETE registers a new DELETE handler
func (h *Horo) DELETE(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.DELETE(path, hf.hrHandle(h, mw...))
}

// HEAD registers a new HEAD handler
func (h *Horo) HEAD(path string, hf HandlerFunc, mw ...MiddlewareFunc) {
	h.router.HEAD(path, hf.hrHandle(h, mw...))
}

func (hf HandlerFunc) hrHandle(h *Horo, mw ...MiddlewareFunc) httprouter.Handle {
	return func(rw http.ResponseWriter, r *http.Request, ps httprouter.Params) {
		w := &response{ResponseWriter: rw}
		var c context.Context = &horoCtx{
			Context: context.Background(),
			w:       w,
			r:       r,
			ps:      ps,
		}

		c, cancel := context.WithCancel(c)

		c = log.WithContext(c, h.Logger)

		hf := func(c context.Context) error {
			mw := append(h.middleware, mw...)
			for i := len(mw) - 1; i >= 0; i-- {
				hf = mw[i](hf)
			}

			return hf(c)
		}

		if err := hf(c); err != nil {
			h.ErrorHandler(c, err)
		}

		cancel()
	}
}

// DefaultErrorHandler invoke HTTP Error Handler
func DefaultErrorHandler(c context.Context, err error) {
	code := 500
	msg := http.StatusText(code)
	if he, ok := err.(*HTTPError); ok {
		code = he.Code
		msg = he.Message
	}
	Text(c, code, msg)
}

// ServeHTTP implements http.Handler interface.
func (h *Horo) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	h.router.ServeHTTP(w, r)
}

// ListenAndServe is Start HTTP Server
func (h *Horo) ListenAndServe(addr string) error {
	return http.ListenAndServe(addr, h)
}

func (e *HTTPError) Error() string {
	return e.Message
}
<|endoftext|>


<commit_before>package vizzini_test

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"code.cloudfoundry.org/bbs/models"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/routing-info/cfroutes"

	. "code.cloudfoundry.org/vizzini/matchers"
	"github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const HealthyCheckInterval = 30 * time.Second
const ConvergerInterval = 30 * time.Second
const CrashRestartTimeout = 30 * time.Second

const GraceTarballURL = "https://storage.googleapis.com/diego-assets-bucket/grace.tar.gz"
const GraceTarballURLSHAChecksum = "b98d75d7c7e3afe2d24e3c97339b15957a8f146a"
const GraceBusyboxImageURL = "docker:///cfdiegodocker/grace"

//Tasks

func TaskGetter(logger lager.Logger, guid string) func() (*models.Task, error) {
	return func() (*models.Task, error) {
		return bbsClient.TaskByGuid(logger, guid)
	}
}

func TasksByDomainGetter(logger lager.Logger, domain string) func() ([]*models.Task, error) {
	return func() ([]*models.Task, error) {
		return bbsClient.TasksByDomain(logger, domain)
	}
}

func ClearOutTasksInDomain(domain string) {
	tasks, err := bbsClient.TasksByDomain(logger, domain)
	Expect(err).NotTo(HaveOccurred())
	for _, task := range tasks {
		if task.State != models.Task_Completed {
			bbsClient.CancelTask(logger, task.TaskGuid)
			Eventually(TaskGetter(logger, task.TaskGuid)).Should(HaveTaskState(models.Task_Completed))
		}
		Expect(bbsClient.ResolvingTask(logger, task.TaskGuid)).To(Succeed())
		Expect(bbsClient.DeleteTask(logger, task.TaskGuid)).To(Succeed())
	}
	Eventually(TasksByDomainGetter(logger, domain)).Should(BeEmpty())
}

func Task() *models.TaskDefinition {
	return &models.TaskDefinition{
		Action: models.WrapAction(&models.RunAction{
			Path: "bash",
			Args: []string{"-c", "echo 'some output' > /tmp/bar"},
			User: "vcap",
		}),
		RootFs:        defaultRootFS,
		MemoryMb:      128,
		DiskMb:        128,
		CpuWeight:     100,
		LogGuid:       guid,
		LogSource:     "VIZ",
		ResultFile:    "/tmp/bar",
		Annotation:    "arbitrary-data",
		PlacementTags: PlacementTags(),
	}
}

//LRPs

func LRPGetter(logger lager.Logger, guid string) func() (*models.DesiredLRP, error) {
	return func() (*models.DesiredLRP, error) {
		return bbsClient.DesiredLRPByProcessGuid(logger, guid)
	}
}

func ActualLRPByProcessGuidAndIndex(logger lager.Logger, guid string, index int) (models.ActualLRP, error) {
	i := int32(index)
	lrps, err := bbsClient.ActualLRPs(logger, models.ActualLRPFilter{ProcessGuid: guid, Index: &i})
	if err != nil {
		return models.ActualLRP{}, err
	}
	if len(lrps) != 1 {
		return models.ActualLRP{}, fmt.Errorf("found more than one or no matching ActualLRP ProcessGuid: %s Index: %d", guid, index)
	}
	return *lrps[0], nil
}

func ActualsByProcessGuid(logger lager.Logger, guid string) ([]models.ActualLRP, error) {
	lrps, err := bbsClient.ActualLRPs(logger, models.ActualLRPFilter{ProcessGuid: guid})
	if err != nil {
		return nil, err
	}
	actualLRPs := make([]models.ActualLRP, len(lrps))
	for k, v := range lrps {
		actualLRPs[k] = *v
	}

	return actualLRPs, nil
}

func ActualsByDomain(logger lager.Logger, domain string) ([]models.ActualLRP, error) {
	lrps, err := bbsClient.ActualLRPs(logger, models.ActualLRPFilter{Domain: domain})
	if err != nil {
		return nil, err
	}

	actualLRPs := make([]models.ActualLRP, len(lrps))
	for k, v := range lrps {
		actualLRPs[k] = *v
	}

	return actualLRPs, nil
}

func ActualGetter(logger lager.Logger, guid string, index int) func() (models.ActualLRP, error) {
	return func() (models.ActualLRP, error) {
		return ActualLRPByProcessGuidAndIndex(logger, guid, index)
	}
}

func ActualByProcessGuidGetter(logger lager.Logger, guid string) func() ([]models.ActualLRP, error) {
	return func() ([]models.ActualLRP, error) {
		return ActualsByProcessGuid(logger, guid)
	}
}

func ActualByDomainGetter(logger lager.Logger, domain string) func() ([]models.ActualLRP, error) {
	return func() ([]models.ActualLRP, error) {
		return ActualsByDomain(logger, domain)
	}
}

func ClearOutDesiredLRPsInDomain(domain string) {
	lrps, err := bbsClient.DesiredLRPs(logger, models.DesiredLRPFilter{Domain: domain})
	Expect(err).NotTo(HaveOccurred())
	for _, lrp := range lrps {
		Expect(bbsClient.RemoveDesiredLRP(logger, lrp.ProcessGuid)).To(Succeed())
	}
	// Wait enough time for the Grace app to exit if it was run with -catchTerminate
	Eventually(ActualByDomainGetter(logger, domain), timeout+5*time.Second).Should(BeEmpty())
}

func EndpointCurler(endpoint string) func() int {
	return func() int {
		resp, err := http.Get(endpoint)
		// workaround the GCP Load Balancer issue until our case with GCP is resolved
		if err != nil {
			return -1
		}
		resp.Body.Close()
		return resp.StatusCode
	}
}

func EndpointContentCurler(endpoint string) func() string {
	return func() string {
		resp, err := http.Get(endpoint)
		// workaround the GCP Load Balancer issue until our case with GCP is resolved
		if err != nil {
			return ""
		}
		defer resp.Body.Close()
		content, err := ioutil.ReadAll(resp.Body)
		Expect(err).NotTo(HaveOccurred())
		return string(content)
	}
}

func IndexCounter(guid string, optionalHttpClient ...*http.Client) func() int {
	return IndexCounterWithAttempts(guid, 100, optionalHttpClient...)
}

func IndexCounterWithAttempts(guid string, attempts int, optionalHttpClient ...*http.Client) func() int {
	return func() int {
		counts := map[int]bool{}
		for i := 0; i < attempts; i++ {
			index := GetIndexFromEndpointFor(guid, optionalHttpClient...)
			if index == -1 {
				continue
			}
			counts[index] = true
		}
		return len(counts)
	}
}

func GetIndexFromEndpointFor(guid string, optionalHttpClient ...*http.Client) int {
	httpClient := http.DefaultClient
	if len(optionalHttpClient) == 1 {
		httpClient = optionalHttpClient[0]
	}
	url := "http://" + RouteForGuid(guid) + "/index"
	resp, err := httpClient.Get(url)
	// workaround the GCP Load Balancer issue until our case with GCP is resolved
	if err != nil {
		return -1
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return -1
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	Expect(err).NotTo(HaveOccurred())

	index, err := strconv.Atoi(string(content))
	Expect(err).NotTo(HaveOccurred())
	return index
}

func GraceCounterGetter(guid string) func() (int, error) {
	return func() (int, error) {
		resp, err := http.Get("http://" + RouteForGuid(guid) + "/counter")
		if err != nil {
			return 0, err
		}
		defer resp.Body.Close()
		content, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return 0, err
		}
		return strconv.Atoi(string(content))
	}
}

func StartedAtGetter(guid string) func() (int64, error) {
	url := "http://" + RouteForGuid(guid) + "/started-at"
	return func() (int64, error) {
		resp, err := http.Get(url)
		if err != nil {
			return 0, err
		}
		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()
			return 0, errors.New(fmt.Sprintf("invalid status code: %d", resp.StatusCode))
		}
		content, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return 0, err
		}
		return strconv.ParseInt(string(content), 10, 64)
	}
}

func RouteForGuid(guid string) string {
	return fmt.Sprintf("%s.%s", guid, routableDomainSuffix)
}

func DirectAddressFor(guid string, index int, containerPort uint32) string {
	actualLRP, err := ActualGetter(logger, guid, index)()
	Expect(err).NotTo(HaveOccurred())
	Expect(actualLRP).NotTo(BeZero())

	for _, portMapping := range actualLRP.Ports {
		if portMapping.ContainerPort == containerPort {
			return fmt.Sprintf("%s:%d", actualLRP.Address, portMapping.HostPort)
		}
	}

	ginkgo.Fail(fmt.Sprintf("could not find port %d for ActualLRP %d with ProcessGuid %s", containerPort, index, guid))
	return ""
}

func TLSDirectAddressFor(guid string, index int, containerPort uint32) string {
	actualLRP, err := ActualGetter(logger, guid, index)()
	Expect(err).NotTo(HaveOccurred())
	Expect(actualLRP).NotTo(BeZero())

	for _, portMapping := range actualLRP.Ports {
		if portMapping.ContainerPort == containerPort {
			return fmt.Sprintf("%s:%d", actualLRP.Address, portMapping.HostTlsProxyPort)
		}
	}

	ginkgo.Fail(fmt.Sprintf("could not find port %d for ActualLRP %d with ProcessGuid %s", containerPort, index, guid))
	return ""
}

func DesiredLRPWithGuid(guid string) *models.DesiredLRP {
	routingInfo := cfroutes.CFRoutes{
		{Port: 8080, Hostnames: []string{RouteForGuid(guid)}},
	}.RoutingInfo()

	return &models.DesiredLRP{
		ProcessGuid:   guid,
		PlacementTags: PlacementTags(),
		Domain:        domain,
		Instances:     1,
		CachedDependencies: []*models.CachedDependency{
			&models.CachedDependency{
				From:              GraceTarballURL,
				To:                "/tmp/grace",
				CacheKey:          "grace",
				ChecksumAlgorithm: "sha1",
				ChecksumValue:     GraceTarballURLSHAChecksum,
			},
		},
		Action: models.WrapAction(&models.RunAction{
			Path: "/tmp/grace/grace",
			User: "vcap",
			Env:  []*models.EnvironmentVariable{{Name: "PORT", Value: "8080"}, {"ACTION_LEVEL", "COYOTE"}, {"OVERRIDE", "DAQUIRI"}},
		}),
		Monitor: models.WrapAction(&models.RunAction{
			Path: "nc",
			Args: []string{"-z", "0.0.0.0", "8080"},
			User: "vcap",
		}),
		RootFs:     defaultRootFS,
		MemoryMb:   128,
		DiskMb:     128,
		CpuWeight:  100,
		Ports:      []uint32{8080},
		Routes:     &routingInfo,
		LogGuid:    guid,
		LogSource:  "VIZ",
		Annotation: "arbitrary-data",
	}
}

func PlacementTags() []string {
	return repPlacementTags
}
<commit_msg>updated grace app<commit_after>package vizzini_test

import (
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"strconv"
	"time"

	"code.cloudfoundry.org/bbs/models"
	"code.cloudfoundry.org/lager"
	"code.cloudfoundry.org/routing-info/cfroutes"

	. "code.cloudfoundry.org/vizzini/matchers"
	"github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"
)

const HealthyCheckInterval = 30 * time.Second
const ConvergerInterval = 30 * time.Second
const CrashRestartTimeout = 30 * time.Second

const GraceTarballURL = "https://storage.googleapis.com/diego-assets-bucket/grace.tar.gz"
const GraceTarballURLSHAChecksum = "35b52ecc2c305fa89d6fe37e73416359c87df0a0"
const GraceBusyboxImageURL = "docker:///cfdiegodocker/grace"

//Tasks

func TaskGetter(logger lager.Logger, guid string) func() (*models.Task, error) {
	return func() (*models.Task, error) {
		return bbsClient.TaskByGuid(logger, guid)
	}
}

func TasksByDomainGetter(logger lager.Logger, domain string) func() ([]*models.Task, error) {
	return func() ([]*models.Task, error) {
		return bbsClient.TasksByDomain(logger, domain)
	}
}

func ClearOutTasksInDomain(domain string) {
	tasks, err := bbsClient.TasksByDomain(logger, domain)
	Expect(err).NotTo(HaveOccurred())
	for _, task := range tasks {
		if task.State != models.Task_Completed {
			bbsClient.CancelTask(logger, task.TaskGuid)
			Eventually(TaskGetter(logger, task.TaskGuid)).Should(HaveTaskState(models.Task_Completed))
		}
		Expect(bbsClient.ResolvingTask(logger, task.TaskGuid)).To(Succeed())
		Expect(bbsClient.DeleteTask(logger, task.TaskGuid)).To(Succeed())
	}
	Eventually(TasksByDomainGetter(logger, domain)).Should(BeEmpty())
}

func Task() *models.TaskDefinition {
	return &models.TaskDefinition{
		Action: models.WrapAction(&models.RunAction{
			Path: "bash",
			Args: []string{"-c", "echo 'some output' > /tmp/bar"},
			User: "vcap",
		}),
		RootFs:        defaultRootFS,
		MemoryMb:      128,
		DiskMb:        128,
		CpuWeight:     100,
		LogGuid:       guid,
		LogSource:     "VIZ",
		ResultFile:    "/tmp/bar",
		Annotation:    "arbitrary-data",
		PlacementTags: PlacementTags(),
	}
}

//LRPs

func LRPGetter(logger lager.Logger, guid string) func() (*models.DesiredLRP, error) {
	return func() (*models.DesiredLRP, error) {
		return bbsClient.DesiredLRPByProcessGuid(logger, guid)
	}
}

func ActualLRPByProcessGuidAndIndex(logger lager.Logger, guid string, index int) (models.ActualLRP, error) {
	i := int32(index)
	lrps, err := bbsClient.ActualLRPs(logger, models.ActualLRPFilter{ProcessGuid: guid, Index: &i})
	if err != nil {
		return models.ActualLRP{}, err
	}
	if len(lrps) != 1 {
		return models.ActualLRP{}, fmt.Errorf("found more than one or no matching ActualLRP ProcessGuid: %s Index: %d", guid, index)
	}
	return *lrps[0], nil
}

func ActualsByProcessGuid(logger lager.Logger, guid string) ([]models.ActualLRP, error) {
	lrps, err := bbsClient.ActualLRPs(logger, models.ActualLRPFilter{ProcessGuid: guid})
	if err != nil {
		return nil, err
	}
	actualLRPs := make([]models.ActualLRP, len(lrps))
	for k, v := range lrps {
		actualLRPs[k] = *v
	}

	return actualLRPs, nil
}

func ActualsByDomain(logger lager.Logger, domain string) ([]models.ActualLRP, error) {
	lrps, err := bbsClient.ActualLRPs(logger, models.ActualLRPFilter{Domain: domain})
	if err != nil {
		return nil, err
	}

	actualLRPs := make([]models.ActualLRP, len(lrps))
	for k, v := range lrps {
		actualLRPs[k] = *v
	}

	return actualLRPs, nil
}

func ActualGetter(logger lager.Logger, guid string, index int) func() (models.ActualLRP, error) {
	return func() (models.ActualLRP, error) {
		return ActualLRPByProcessGuidAndIndex(logger, guid, index)
	}
}

func ActualByProcessGuidGetter(logger lager.Logger, guid string) func() ([]models.ActualLRP, error) {
	return func() ([]models.ActualLRP, error) {
		return ActualsByProcessGuid(logger, guid)
	}
}

func ActualByDomainGetter(logger lager.Logger, domain string) func() ([]models.ActualLRP, error) {
	return func() ([]models.ActualLRP, error) {
		return ActualsByDomain(logger, domain)
	}
}

func ClearOutDesiredLRPsInDomain(domain string) {
	lrps, err := bbsClient.DesiredLRPs(logger, models.DesiredLRPFilter{Domain: domain})
	Expect(err).NotTo(HaveOccurred())
	for _, lrp := range lrps {
		Expect(bbsClient.RemoveDesiredLRP(logger, lrp.ProcessGuid)).To(Succeed())
	}
	// Wait enough time for the Grace app to exit if it was run with -catchTerminate
	Eventually(ActualByDomainGetter(logger, domain), timeout+5*time.Second).Should(BeEmpty())
}

func EndpointCurler(endpoint string) func() int {
	return func() int {
		resp, err := http.Get(endpoint)
		// workaround the GCP Load Balancer issue until our case with GCP is resolved
		if err != nil {
			return -1
		}
		resp.Body.Close()
		return resp.StatusCode
	}
}

func EndpointContentCurler(endpoint string) func() string {
	return func() string {
		resp, err := http.Get(endpoint)
		// workaround the GCP Load Balancer issue until our case with GCP is resolved
		if err != nil {
			return ""
		}
		defer resp.Body.Close()
		content, err := ioutil.ReadAll(resp.Body)
		Expect(err).NotTo(HaveOccurred())
		return string(content)
	}
}

func IndexCounter(guid string, optionalHttpClient ...*http.Client) func() int {
	return IndexCounterWithAttempts(guid, 100, optionalHttpClient...)
}

func IndexCounterWithAttempts(guid string, attempts int, optionalHttpClient ...*http.Client) func() int {
	return func() int {
		counts := map[int]bool{}
		for i := 0; i < attempts; i++ {
			index := GetIndexFromEndpointFor(guid, optionalHttpClient...)
			if index == -1 {
				continue
			}
			counts[index] = true
		}
		return len(counts)
	}
}

func GetIndexFromEndpointFor(guid string, optionalHttpClient ...*http.Client) int {
	httpClient := http.DefaultClient
	if len(optionalHttpClient) == 1 {
		httpClient = optionalHttpClient[0]
	}
	url := "http://" + RouteForGuid(guid) + "/index"
	resp, err := httpClient.Get(url)
	// workaround the GCP Load Balancer issue until our case with GCP is resolved
	if err != nil {
		return -1
	}
	if resp.StatusCode != http.StatusOK {
		resp.Body.Close()
		return -1
	}
	content, err := ioutil.ReadAll(resp.Body)
	resp.Body.Close()
	Expect(err).NotTo(HaveOccurred())

	index, err := strconv.Atoi(string(content))
	Expect(err).NotTo(HaveOccurred())
	return index
}

func GraceCounterGetter(guid string) func() (int, error) {
	return func() (int, error) {
		resp, err := http.Get("http://" + RouteForGuid(guid) + "/counter")
		if err != nil {
			return 0, err
		}
		defer resp.Body.Close()
		content, err := ioutil.ReadAll(resp.Body)
		if err != nil {
			return 0, err
		}
		return strconv.Atoi(string(content))
	}
}

func StartedAtGetter(guid string) func() (int64, error) {
	url := "http://" + RouteForGuid(guid) + "/started-at"
	return func() (int64, error) {
		resp, err := http.Get(url)
		if err != nil {
			return 0, err
		}
		if resp.StatusCode != http.StatusOK {
			resp.Body.Close()
			return 0, errors.New(fmt.Sprintf("invalid status code: %d", resp.StatusCode))
		}
		content, err := ioutil.ReadAll(resp.Body)
		resp.Body.Close()
		if err != nil {
			return 0, err
		}
		return strconv.ParseInt(string(content), 10, 64)
	}
}

func RouteForGuid(guid string) string {
	return fmt.Sprintf("%s.%s", guid, routableDomainSuffix)
}

func DirectAddressFor(guid string, index int, containerPort uint32) string {
	actualLRP, err := ActualGetter(logger, guid, index)()
	Expect(err).NotTo(HaveOccurred())
	Expect(actualLRP).NotTo(BeZero())

	for _, portMapping := range actualLRP.Ports {
		if portMapping.ContainerPort == containerPort {
			return fmt.Sprintf("%s:%d", actualLRP.Address, portMapping.HostPort)
		}
	}

	ginkgo.Fail(fmt.Sprintf("could not find port %d for ActualLRP %d with ProcessGuid %s", containerPort, index, guid))
	return ""
}

func TLSDirectAddressFor(guid string, index int, containerPort uint32) string {
	actualLRP, err := ActualGetter(logger, guid, index)()
	Expect(err).NotTo(HaveOccurred())
	Expect(actualLRP).NotTo(BeZero())

	for _, portMapping := range actualLRP.Ports {
		if portMapping.ContainerPort == containerPort {
			return fmt.Sprintf("%s:%d", actualLRP.Address, portMapping.HostTlsProxyPort)
		}
	}

	ginkgo.Fail(fmt.Sprintf("could not find port %d for ActualLRP %d with ProcessGuid %s", containerPort, index, guid))
	return ""
}

func DesiredLRPWithGuid(guid string) *models.DesiredLRP {
	routingInfo := cfroutes.CFRoutes{
		{Port: 8080, Hostnames: []string{RouteForGuid(guid)}},
	}.RoutingInfo()

	return &models.DesiredLRP{
		ProcessGuid:   guid,
		PlacementTags: PlacementTags(),
		Domain:        domain,
		Instances:     1,
		CachedDependencies: []*models.CachedDependency{
			&models.CachedDependency{
				From:              GraceTarballURL,
				To:                "/tmp/grace",
				CacheKey:          "grace",
				ChecksumAlgorithm: "sha1",
				ChecksumValue:     GraceTarballURLSHAChecksum,
			},
		},
		Action: models.WrapAction(&models.RunAction{
			Path: "/tmp/grace/grace",
			User: "vcap",
			Env:  []*models.EnvironmentVariable{{Name: "PORT", Value: "8080"}, {"ACTION_LEVEL", "COYOTE"}, {"OVERRIDE", "DAQUIRI"}},
		}),
		Monitor: models.WrapAction(&models.RunAction{
			Path: "nc",
			Args: []string{"-z", "0.0.0.0", "8080"},
			User: "vcap",
		}),
		RootFs:     defaultRootFS,
		MemoryMb:   128,
		DiskMb:     128,
		CpuWeight:  100,
		Ports:      []uint32{8080},
		Routes:     &routingInfo,
		LogGuid:    guid,
		LogSource:  "VIZ",
		Annotation: "arbitrary-data",
	}
}

func PlacementTags() []string {
	return repPlacementTags
}
<|endoftext|>


<commit_before>package citadel

import (
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/citadel/citadel/utils"
	"github.com/cloudfoundry/gosigar"
	"github.com/samalba/dockerclient"
)

// Host represents a host machine on the cluster
// running docker containers
type Host struct {
	mux sync.Mutex

	// ID is a unique identifier for a host
	ID string `json:"id,omitempty"`
	// Cpus is the number of cpus the host has available
	Cpus int `json:"cpus,omitempty"`
	// Memory is the amount of memory in mb the host has available
	Memory int `json:"memory,omitempty"`
	// Label is specific attributes of a host
	Labels []string `json:"labels,omitempty"`
	// Address where the host's api can be reached
	Addr string `json:"addr,omitempty"`

	logger   *logrus.Logger
	docker   *dockerclient.DockerClient
	registry Registry
}

func NewHost(addr string, labels []string, etcdMachines []string, docker *dockerclient.DockerClient, logger *logrus.Logger) (*Host, error) {
	mem := sigar.Mem{}
	if err := mem.Get(); err != nil {
		return nil, err
	}

	cpus := sigar.CpuList{}
	if err := cpus.Get(); err != nil {
		return nil, err
	}

	id, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	h := &Host{
		ID:       id,
		Cpus:     len(cpus.List),
		Memory:   int(mem.Total / 1024 / 1024),
		Labels:   labels,
		Addr:     addr,
		docker:   docker,
		logger:   logger,
		registry: NewRegistry(etcdMachines),
	}

	if err := h.verifyState(); err != nil {
		return nil, err
	}

	if err := h.registerHost(); err != nil {
		return nil, err
	}

	docker.StartMonitorEvents(h.eventHandler, nil)

	return h, nil
}

// Close stops the events monitor
func (h *Host) Close() error {
	h.mux.Lock()
	defer h.mux.Unlock()

	h.docker.StopAllMonitorEvents()

	return h.deregisterHost()
}

// RunContainer takes an application ID to lookup how a container is supposed to be run.
// A container is created with a unique ID and run on the host with the container saved back
// to the central registry
func (h *Host) RunContainer(applicationID string) *Transaction {
	h.mux.Lock()
	defer h.mux.Unlock()

	tran := NewTransaction(RunTransaction)

	app, err := h.registry.FetchApplication(applicationID)
	if err != nil {
		return tran.Error(err)
	}

	for i := 0; i < app.Instances; i++ {
		config := &dockerclient.ContainerConfig{
			Image:  app.Image,
			Cmd:    app.Args,
			Memory: app.Memory * 1024 * 1024,
			Cpuset: utils.IToCpuset(app.Cpus),
		}

		id, err := h.docker.CreateContainer(config, "")
		if err != nil {
			return tran.Error(err)
		}

		c := &Container{
			ID:            id,
			ApplicationID: app.ID,
			HostID:        h.ID,
		}

		if err := h.startContainer(app, c); err != nil {
			return tran.Error(err)
		}

		if err := h.registry.SaveContainer(h.ID, c); err != nil {
			return tran.Error(err)
		}

		tran.Containers = append(tran.Containers, c)
	}

	return tran
}

func (h *Host) startContainer(app *Application, c *Container) error {
	var hostConfig *dockerclient.HostConfig

	if app.Ports != nil {
		hostConfig = &dockerclient.HostConfig{
			PortBindings: make(map[string][]dockerclient.PortBinding),
		}

		for _, p := range app.Ports {
			proto := "tcp"
			if p.Proto != "" {
				proto = p.Proto
			}

			hostConfig.PortBindings[fmt.Sprintf("%d/%s", p.Container, proto)] = []dockerclient.PortBinding{
				{
					HostPort: fmt.Sprint(p.Host),
				},
			}
		}
	}

	if err := h.docker.StartContainer(c.ID, hostConfig); err != nil {
		return err
	}

	info, err := h.docker.InspectContainer(c.ID)
	if err != nil {
		return err
	}

	ports, err := createPorts(info)
	if err != nil {
		return err
	}

	c.Ports = ports

	state := h.getState(info)
	c.State = state
	c.State.StartedAt = time.Now()
	c.State.ExitedAt = time.Time{}

	return nil
}

// StopContainer will stop the running container, remove it from the hosts registry
// and delete the container from docker
func (h *Host) StopContainer(id string) *Transaction {
	h.mux.Lock()
	defer h.mux.Unlock()

	tran := NewTransaction(StopTransaction)

	containers, err := h.registry.FetchContainers(h.ID)
	if err != nil {
		return tran.Error(err)
	}

	for _, c := range containers {
		tran.Containers = append(tran.Containers, c)

		if err := h.docker.StopContainer(c.ID, 10); err != nil {
			return tran.Error(err)
		}

		err := h.registry.DeleteContainer(h.ID, c.ID)

		if nerr := h.docker.RemoveContainer(c.ID); err == nil {
			err = nerr
		}

		if err != nil {
			return tran.Error(err)
		}
	}

	return tran
}

// Register ensures that the host can run the given application based on the requirements
func (h *Host) Register(id string) error {
	app, err := h.registry.FetchApplication(id)
	if err != nil {
		return err
	}

	return h.docker.PullImage(app.Image, "latest")
}

// getState inspects the container's state in docker and returns the updated information
func (h *Host) getState(info *dockerclient.ContainerInfo) State {
	state := State{}

	state.ExitCode = info.State.ExitCode

	if info.State.Running {
		state.Status = Running
	} else {
		state.Status = Stopped
	}

	return state
}

func (h *Host) verifyState() error {
	containers, err := h.registry.FetchContainers(h.ID)
	if err != nil {
		return err
	}

	for _, c := range containers {
		info, err := h.docker.InspectContainer(c.ID)
		if err != nil {
			if err == dockerclient.ErrNotFound {
				h.logger.WithField("id", c.ID).Warn("container no longer exists in docker")

				if derr := h.registry.DeleteContainer(h.ID, c.ID); derr != nil {
					h.logger.WithField("error", derr).Warn("error deleting non-existant container")
				}

				continue
			}

			return err
		}

		if c.State.Status == Running && !info.State.Running {
			h.logger.WithField("id", c.ID).Warn("state mismatch")

			c.State.Status = Stopped
			// TODO: make it run again
		}
	}

	return nil
}

func (h *Host) eventHandler(event *dockerclient.Event, _ ...interface{}) {
	switch event.Status {
	case "die":
		h.mux.Lock()
		defer h.mux.Unlock()

		// only restart it if it's a managed container
		container, err := h.registry.FetchContainer(h.ID, event.Id)
		if err != nil {
			h.logger.WithField("error", err).Error("fetch container from registry")

			return
		}

		app, err := h.registry.FetchApplication(container.ApplicationID)
		if err != nil {
			h.logger.WithField("error", err).Error("fetch container's application from registry")

			return
		}

		if app.Type == Service {
			container.State.ExitedAt = time.Now()

			if err := h.startContainer(app, container); err != nil {
				h.logger.WithField("error", err).Error("restarting dead container")
			}
		}

	default:
		h.logger.WithFields(logrus.Fields{
			"type": event.Status,
			"id":   event.Id,
			"from": event.From,
		}).Debug("docker event")
	}
}

func (h *Host) registerHost() error {
	return h.registry.SaveHost(h)
}

func (h *Host) deregisterHost() error {
	return h.registry.DeleteHost(h.ID)
}
<commit_msg>Be sure to save container after restart<commit_after>package citadel

import (
	"fmt"
	"os"
	"sync"
	"time"

	"github.com/Sirupsen/logrus"
	"github.com/citadel/citadel/utils"
	"github.com/cloudfoundry/gosigar"
	"github.com/samalba/dockerclient"
)

// Host represents a host machine on the cluster
// running docker containers
type Host struct {
	mux sync.Mutex

	// ID is a unique identifier for a host
	ID string `json:"id,omitempty"`
	// Cpus is the number of cpus the host has available
	Cpus int `json:"cpus,omitempty"`
	// Memory is the amount of memory in mb the host has available
	Memory int `json:"memory,omitempty"`
	// Label is specific attributes of a host
	Labels []string `json:"labels,omitempty"`
	// Address where the host's api can be reached
	Addr string `json:"addr,omitempty"`

	logger   *logrus.Logger
	docker   *dockerclient.DockerClient
	registry Registry
}

func NewHost(addr string, labels []string, etcdMachines []string, docker *dockerclient.DockerClient, logger *logrus.Logger) (*Host, error) {
	mem := sigar.Mem{}
	if err := mem.Get(); err != nil {
		return nil, err
	}

	cpus := sigar.CpuList{}
	if err := cpus.Get(); err != nil {
		return nil, err
	}

	id, err := os.Hostname()
	if err != nil {
		return nil, err
	}

	h := &Host{
		ID:       id,
		Cpus:     len(cpus.List),
		Memory:   int(mem.Total / 1024 / 1024),
		Labels:   labels,
		Addr:     addr,
		docker:   docker,
		logger:   logger,
		registry: NewRegistry(etcdMachines),
	}

	if err := h.verifyState(); err != nil {
		return nil, err
	}

	if err := h.registerHost(); err != nil {
		return nil, err
	}

	docker.StartMonitorEvents(h.eventHandler, nil)

	return h, nil
}

// Close stops the events monitor
func (h *Host) Close() error {
	h.mux.Lock()
	defer h.mux.Unlock()

	h.docker.StopAllMonitorEvents()

	return h.deregisterHost()
}

// RunContainer takes an application ID to lookup how a container is supposed to be run.
// A container is created with a unique ID and run on the host with the container saved back
// to the central registry
func (h *Host) RunContainer(applicationID string) *Transaction {
	h.mux.Lock()
	defer h.mux.Unlock()

	tran := NewTransaction(RunTransaction)

	app, err := h.registry.FetchApplication(applicationID)
	if err != nil {
		return tran.Error(err)
	}

	for i := 0; i < app.Instances; i++ {
		config := &dockerclient.ContainerConfig{
			Image:  app.Image,
			Cmd:    app.Args,
			Memory: app.Memory * 1024 * 1024,
			Cpuset: utils.IToCpuset(app.Cpus),
		}

		id, err := h.docker.CreateContainer(config, "")
		if err != nil {
			return tran.Error(err)
		}

		c := &Container{
			ID:            id,
			ApplicationID: app.ID,
			HostID:        h.ID,
		}

		if err := h.startContainer(app, c); err != nil {
			return tran.Error(err)
		}

		if err := h.registry.SaveContainer(h.ID, c); err != nil {
			return tran.Error(err)
		}

		tran.Containers = append(tran.Containers, c)
	}

	return tran
}

func (h *Host) startContainer(app *Application, c *Container) error {
	var hostConfig *dockerclient.HostConfig

	if app.Ports != nil {
		hostConfig = &dockerclient.HostConfig{
			PortBindings: make(map[string][]dockerclient.PortBinding),
		}

		for _, p := range app.Ports {
			proto := "tcp"
			if p.Proto != "" {
				proto = p.Proto
			}

			hostConfig.PortBindings[fmt.Sprintf("%d/%s", p.Container, proto)] = []dockerclient.PortBinding{
				{
					HostPort: fmt.Sprint(p.Host),
				},
			}
		}
	}

	if err := h.docker.StartContainer(c.ID, hostConfig); err != nil {
		return err
	}

	info, err := h.docker.InspectContainer(c.ID)
	if err != nil {
		return err
	}

	ports, err := createPorts(info)
	if err != nil {
		return err
	}

	c.Ports = ports

	state := h.getState(info)
	c.State = state
	c.State.StartedAt = time.Now()
	c.State.ExitedAt = time.Time{}

	return nil
}

// StopContainer will stop the running container, remove it from the hosts registry
// and delete the container from docker
func (h *Host) StopContainer(id string) *Transaction {
	h.mux.Lock()
	defer h.mux.Unlock()

	tran := NewTransaction(StopTransaction)

	containers, err := h.registry.FetchContainers(h.ID)
	if err != nil {
		return tran.Error(err)
	}

	for _, c := range containers {
		tran.Containers = append(tran.Containers, c)

		if err := h.docker.StopContainer(c.ID, 10); err != nil {
			return tran.Error(err)
		}

		err := h.registry.DeleteContainer(h.ID, c.ID)

		if nerr := h.docker.RemoveContainer(c.ID); err == nil {
			err = nerr
		}

		if err != nil {
			return tran.Error(err)
		}
	}

	return tran
}

// Register ensures that the host can run the given application based on the requirements
func (h *Host) Register(id string) error {
	app, err := h.registry.FetchApplication(id)
	if err != nil {
		return err
	}

	return h.docker.PullImage(app.Image, "latest")
}

// getState inspects the container's state in docker and returns the updated information
func (h *Host) getState(info *dockerclient.ContainerInfo) State {
	state := State{}

	state.ExitCode = info.State.ExitCode

	if info.State.Running {
		state.Status = Running
	} else {
		state.Status = Stopped
	}

	return state
}

func (h *Host) verifyState() error {
	containers, err := h.registry.FetchContainers(h.ID)
	if err != nil {
		return err
	}

	for _, c := range containers {
		info, err := h.docker.InspectContainer(c.ID)
		if err != nil {
			if err == dockerclient.ErrNotFound {
				h.logger.WithField("id", c.ID).Warn("container no longer exists in docker")

				if derr := h.registry.DeleteContainer(h.ID, c.ID); derr != nil {
					h.logger.WithField("error", derr).Warn("error deleting non-existant container")
				}

				continue
			}

			return err
		}

		if c.State.Status == Running && !info.State.Running {
			h.logger.WithField("id", c.ID).Warn("state mismatch")

			c.State.Status = Stopped
			// TODO: make it run again
		}
	}

	return nil
}

func (h *Host) eventHandler(event *dockerclient.Event, _ ...interface{}) {
	switch event.Status {
	case "die":
		h.mux.Lock()
		defer h.mux.Unlock()

		// only restart it if it's a managed container
		container, err := h.registry.FetchContainer(h.ID, event.Id)
		if err != nil {
			h.logger.WithField("error", err).Error("fetch container from registry")

			return
		}

		app, err := h.registry.FetchApplication(container.ApplicationID)
		if err != nil {
			h.logger.WithField("error", err).Error("fetch container's application from registry")

			return
		}

		if app.Type == Service {
			container.State.ExitedAt = time.Now()

			if err := h.startContainer(app, container); err != nil {
				h.logger.WithField("error", err).Error("restarting dead container")
			}

			if err := h.registry.SaveContainer(h.ID, container); err != nil {
				h.logger.WithField("error", err).Error("saving after restart of dead container")
			}
		}

	default:
		h.logger.WithFields(logrus.Fields{
			"type": event.Status,
			"id":   event.Id,
			"from": event.From,
		}).Debug("docker event")
	}
}

func (h *Host) registerHost() error {
	return h.registry.SaveHost(h)
}

func (h *Host) deregisterHost() error {
	return h.registry.DeleteHost(h.ID)
}
<|endoftext|>


<commit_before>package echo

import (
	"fmt"
	"regexp"
)

type Hoster interface {
	Name() string
	Alias() string
	Format(args ...interface{}) string
	FormatMap(params H) string
	RegExp() *regexp.Regexp
	Match(host string) (r []string, hasExpr bool)
}

type host struct {
	name   string
	alias  string
	format string
	regExp *regexp.Regexp
	names  []string
}

func (h *host) Name() string {
	return h.name
}

func (h *host) Alias() string {
	return h.alias
}

func (h *host) Format(args ...interface{}) string {
	if len(args) > 0 {
		return fmt.Sprintf(h.format, args...)
	}
	return h.format
}

func (h *host) FormatMap(params H) string {
	if len(params) > 0 {
		args := make([]interface{}, len(h.names))
		for index, name := range h.names {
			v, y := params[name]
			if y {
				args[index] = v
			} else {
				args[index] = ``
			}
		}
		return fmt.Sprintf(h.format, args...)
	}
	return h.format
}

func (h *host) RegExp() *regexp.Regexp {
	return h.regExp
}

func (h *host) Match(host string) (r []string, hasExpr bool) {
	if h.regExp != nil {
		match := h.regExp.FindStringSubmatch(host)
		if len(match) > 0 {
			return match[1:], true
		}
		return nil, true
	}
	return nil, false
}

func NewHost(name string) *host {
	return &host{name: name}
}

var hostRegExp = regexp.MustCompile(`<([^:]+)(?:\:(.+?))?>`)

func (h *host) Parse() *host {
	matches := hostRegExp.FindAllStringSubmatchIndex(h.name, -1)
	if len(matches) == 0 {
		return h
	}
	var format string
	var regExpr string
	var lastPosition int
	for _, matchIndex := range matches {
		if matchIndex[0] > 0 {
			v := h.name[lastPosition:matchIndex[0]]
			format += v
			regExpr += regexp.QuoteMeta(v)
		}
		lastPosition = matchIndex[1]
		format += `%v`
		name := h.name[matchIndex[2]:matchIndex[3]]
		if matchIndex[4] > 0 {
			regExpr += `(` + h.name[matchIndex[4]:matchIndex[5]] + `)`
		} else {
			regExpr += `([^.]+)`
		}
		h.names = append(h.names, name)
	}
	if lastPosition > 0 {
		if lastPosition < len(h.name) {
			v := h.name[lastPosition:]
			format += v
			regExpr += regexp.QuoteMeta(v)
		}
	}
	h.format = format
	h.regExp = regexp.MustCompile(`^` + regExpr + `$`)
	return h
}
<commit_msg>update<commit_after>package echo

import (
	"fmt"
	"regexp"
)

type Hoster interface {
	Name() string
	Alias() string
	Format(args ...interface{}) string
	FormatMap(params H) string
	RegExp() *regexp.Regexp
	Match(host string) (r []string, hasExpr bool)
}

type host struct {
	name   string
	alias  string
	format string
	regExp *regexp.Regexp
	names  []string
}

func (h *host) Name() string {
	return h.name
}

func (h *host) Alias() string {
	return h.alias
}

func (h *host) Format(args ...interface{}) string {
	if len(args) > 0 {
		return fmt.Sprintf(h.format, args...)
	}
	return h.format
}

func (h *host) FormatMap(params H) string {
	if len(params) > 0 {
		args := make([]interface{}, len(h.names))
		for index, name := range h.names {
			v, y := params[name]
			if y {
				args[index] = v
			} else {
				args[index] = ``
			}
		}
		return fmt.Sprintf(h.format, args...)
	}
	return h.format
}

func (h *host) RegExp() *regexp.Regexp {
	return h.regExp
}

func (h *host) Match(host string) (r []string, hasExpr bool) {
	if h.regExp != nil {
		match := h.regExp.FindStringSubmatch(host)
		if len(match) > 0 {
			return match[1:], true
		}
		return nil, true
	}
	return nil, false
}

func NewHost(name string) *host {
	return &host{name: name}
}

var hostRegExp = regexp.MustCompile(`<([^:]+)(?:\:(.+?))?>`)

func ParseURIRegExp(uriRegexp string, dflRegexp string) (names []string, format string, regExp *regexp.Regexp) {
	matches := hostRegExp.FindAllStringSubmatchIndex(uriRegexp, -1)
	if len(matches) == 0 {
		return
	}
	if len(dflRegexp) == 0 {
		dflRegexp = `[^.]+`
	}
	var regExpr string
	var lastPosition int
	for _, matchIndex := range matches {
		if matchIndex[0] > 0 {
			v := uriRegexp[lastPosition:matchIndex[0]]
			format += v
			regExpr += regexp.QuoteMeta(v)
		}
		lastPosition = matchIndex[1]
		format += `%v`
		name := uriRegexp[matchIndex[2]:matchIndex[3]]
		if matchIndex[4] > 0 {
			regExpr += `(` + uriRegexp[matchIndex[4]:matchIndex[5]] + `)`
		} else {
			regExpr += `(` + dflRegexp + `)`
		}
		names = append(names, name)
	}
	if lastPosition > 0 {
		if lastPosition < len(uriRegexp) {
			v := uriRegexp[lastPosition:]
			format += v
			regExpr += regexp.QuoteMeta(v)
		}
	}
	regExp = regexp.MustCompile(`^` + regExpr + `$`)
	return
}

func (h *host) Parse() *host {
	h.names, h.format, h.regExp = ParseURIRegExp(h.name, ``)
	return h
}
<|endoftext|>