<commit_before>package main

import (
	"fmt"
	"os"
	"github.com/urfave/cli"
)

func main() {
	app := cli.NewApp()
	app.Name = "heartbeat"
	app.Usage = "Produce a periodic heartbeat message"
	app.Version = "0.1.0"

	app.Flags = []cli.Flag{
		cli.BoolTFlag{
			Name:  "verbose",
			Usage: "Print verbose output",
		},
	}

	//cfg := &Config{ Verbose: false}

	app.Commands = []cli.Command{
		{
			Name:    "version",
			Aliases: []string{"v"},
			Usage:   "Print the version and exit",
			Action: func(c *cli.Context) error {
				fmt.Printf("heartbeat version %s\n", app.Version)
				return nil
			},
		},
	}

	app.Run(os.Args)
}
<commit_msg>Create a simple program to publish a heartbeat message once per second.<commit_after>package main

import (
	"fmt"
	"github.com/urfave/cli"
	"os"
	"time"
)

func main() {
	app := cli.NewApp()
	app.Name = "heartbeat"
	app.Usage = "Produce a periodic heartbeat message"
	app.Version = "0.1.0"

	app.Flags = []cli.Flag{
		cli.BoolTFlag{
			Name:  "verbose",
			Usage: "Print verbose output",
		},
	}

	app.Commands = []cli.Command{
		{
			Name:    "version",
			Aliases: []string{"v"},
			Usage:   "Print the version and exit",
			Action: func(c *cli.Context) error {
				fmt.Printf("heartbeat version %s\n", app.Version)
				return nil
			},
		},
		{
			Name:   "run",
			Usage:  "Run the program",
			Action: RunPeriodically,
		},
	}

	app.Run(os.Args)
}

func RunPeriodically(c *cli.Context) error {
	var period time.Duration = 1 * time.Second

	fmt.Printf("Running %s periodically\n", c.App.Name)
	for {
		go func() {
			PrintHeartbeat()
		}()

		time.Sleep(period)
	}

	return nil
}

func PrintHeartbeat() {
	now := time.Now().UTC()
	fmt.Printf("%v Every heartbeat bears your name\n", now.Format(time.RFC3339))
}
<|endoftext|>


<commit_before>package main

import (
	"bufio"
	"io"
	"strings"
	"sync"
	"time"

	"github.com/seletskiy/hierr"
	"github.com/theairkit/runcmd"
)

const (
	heartbeatPing = "PING"
)

func heartbeat(
	period time.Duration,
	node *distributedLockNode,
	canceler *sync.Cond,
) {
	abort := make(chan struct{}, 0)

	go func() {
		canceler.L.Lock()
		canceler.Wait()
		canceler.L.Unlock()

		abort <- struct{}{}
	}()

	finish := func(code int) {
		canceler.L.Lock()
		canceler.Broadcast()
		canceler.L.Unlock()

		<-abort

		if remote, ok := node.runner.(*runcmd.Remote); ok {
			tracef("%s closing connection", node.String())
			err := remote.CloseConnection()
			if err != nil {
				warningf(
					"%s",
					hierr.Errorf(
						err,
						"%s error while closing connection",
						node.String(),
					),
				)
			}
		}

		exit(code)
	}

	ticker := time.Tick(period)

	for {
		_, err := io.WriteString(node.connection.stdin, heartbeatPing+"\n")
		if err != nil {
			logger.Error(
				hierr.Errorf(
					err,
					`%s can't send heartbeat`,
					node.String(),
				).Error(),
			)

			finish(2)
		}

		select {
		case <-abort:
			return

		case <-ticker:
			// pass
		}

		ping, err := bufio.NewReader(node.connection.stdout).ReadString('\n')
		if err != nil {
			logger.Error(
				hierr.Errorf(
					err,
					`%s can't receive heartbeat`,
					node.String(),
				),
			)

			finish(2)
		}

		if strings.TrimSpace(ping) != heartbeatPing {
			logger.Errorf(
				`%s received unexpected heartbeat ping: '%s'`,
				node.String(),
				ping,
			)

			finish(2)
		}

		tracef(`%s heartbeat`, node.String())
	}
}
<commit_msg>add comments for the heartbeat function<commit_after>package main

import (
	"bufio"
	"io"
	"strings"
	"sync"
	"time"

	"github.com/seletskiy/hierr"
	"github.com/theairkit/runcmd"
)

const (
	heartbeatPing = "PING"
)

// heartbeat runs infinite process of sending test messages to the connected
// node. All heartbeats to all nodes are connected to each other, so if one
// heartbeat routine exits, all heartbeat routines will exit, because in that
// case orgalorg can't guarantee global lock.
func heartbeat(
	period time.Duration,
	node *distributedLockNode,
	canceler *sync.Cond,
) {
	abort := make(chan struct{}, 0)

	// Internal go-routine for listening abort broadcast and finishing current
	// heartbeat process.
	go func() {
		canceler.L.Lock()
		canceler.Wait()
		canceler.L.Unlock()

		abort <- struct{}{}
	}()

	// Finish finishes current go-routine and send abort broadcast to all
	// connected go-routines.
	finish := func(code int) {
		canceler.L.Lock()
		canceler.Broadcast()
		canceler.L.Unlock()

		<-abort

		if remote, ok := node.runner.(*runcmd.Remote); ok {
			tracef("%s closing connection", node.String())
			err := remote.CloseConnection()
			if err != nil {
				warningf(
					"%s",
					hierr.Errorf(
						err,
						"%s error while closing connection",
						node.String(),
					),
				)
			}
		}

		exit(code)
	}

	ticker := time.Tick(period)

	// Infinite loop of heartbeating. It will send heartbeat message, wait
	// fraction of send timeout time and try to receive heartbeat response.
	// If no response received, heartbeat process aborts.
	for {
		_, err := io.WriteString(node.connection.stdin, heartbeatPing+"\n")
		if err != nil {
			logger.Error(
				hierr.Errorf(
					err,
					`%s can't send heartbeat`,
					node.String(),
				).Error(),
			)

			finish(2)
		}

		select {
		case <-abort:
			return

		case <-ticker:
			// pass
		}

		ping, err := bufio.NewReader(node.connection.stdout).ReadString('\n')
		if err != nil {
			logger.Error(
				hierr.Errorf(
					err,
					`%s can't receive heartbeat`,
					node.String(),
				),
			)

			finish(2)
		}

		if strings.TrimSpace(ping) != heartbeatPing {
			logger.Errorf(
				`%s received unexpected heartbeat ping: '%s'`,
				node.String(),
				ping,
			)

			finish(2)
		}

		tracef(`%s heartbeat`, node.String())
	}
}
<|endoftext|>


<commit_before>package gearcmd

import (
	"bufio"
	"bytes"
	"container/ring"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/Clever/baseworker-go"
	"github.com/Clever/gearcmd/argsparser"
	"gopkg.in/Clever/kayvee-go.v2"
)

// TaskConfig defines the configuration for the task.
type TaskConfig struct {
	FunctionName, FunctionCmd string
	WarningLines              int
	ParseArgs                 bool
	CmdTimeout                time.Duration
	RetryCount                int
}

// Process runs the Gearman job by running the configured task.
// We need to implement the Task interface so we return (byte[], error)
// though the byte[] is always nil.
func (conf TaskConfig) Process(job baseworker.Job) ([]byte, error) {
	// This wraps the actual processing to do some logging
	log.Println(kayvee.FormatLog("gearcmd", kayvee.Info, "START",
		map[string]interface{}{"function": conf.FunctionName, "job_id": getJobId(job), "job_data": string(job.Data())}))

	for {
		start := time.Now()
		err := conf.doProcess(job)
		end := time.Now()
		data := map[string]interface{}{
			"function": conf.FunctionName,
			"job_id":   getJobId(job),
			"job_data": string(job.Data()),
			"type":     "gauge",
		}
		// Return if the job was successful.
		if err == nil {
			data["value"] = 1
			data["success"] = true
			log.Println(kayvee.FormatLog("gearcmd", kayvee.Info, "END", data))
			// Hopefully none of our jobs last long enough for a uint64...
			log.Printf(kayvee.FormatLog("gearcmd", kayvee.Info, "duration",
				map[string]interface{}{
					"value":    uint64(end.Sub(start).Seconds() * 1000),
					"type":     "gauge",
					"function": conf.FunctionName,
				},
			))
			return nil, nil
		}
		data["error_message"] = err.Error()
		data["value"] = 0
		data["success"] = false
		// Return if the job has no more retries.
		if conf.RetryCount <= 0 {
			log.Println(kayvee.FormatLog("gearcmd", kayvee.Error, "END", data))
			return nil, err
		}
		conf.RetryCount--
		log.Println(kayvee.FormatLog("gearcmd", kayvee.Error, "RETRY", data))
	}
}

// getJobId returns the jobId from the job handle
func getJobId(job baseworker.Job) string {
	splits := strings.Split(job.Handle(), ":")
	return splits[len(splits)-1]
}

func (conf TaskConfig) doProcess(job baseworker.Job) error {

	defer func() {
		// If we panicked then set the panic message as a warning. Gearman-go will
		// handle marking this job as failed.
		if r := recover(); r != nil {
			err := r.(error)
			job.SendWarning([]byte(err.Error()))
		}
	}()
	var args []string
	var err error
	if conf.ParseArgs {
		args, err = argsparser.ParseArgs(string(job.Data()))
		if err != nil {
			return err
		}
	} else {
		args = []string{string(job.Data())}

	}
	cmd := exec.Command(conf.FunctionCmd, args...)

	// Write the stdout and stderr of the process to both this process' stdout and stderr
	// and also write it to a byte buffer so that we can return it with the Gearman job
	// data as necessary.
	var stderrbuf bytes.Buffer
	cmd.Stderr = io.MultiWriter(os.Stderr, &stderrbuf)
	defer sendStderrWarnings(&stderrbuf, job, conf.WarningLines)

	stdoutReader, stdoutWriter := io.Pipe()
	cmd.Stdout = io.MultiWriter(os.Stdout, stdoutWriter)

	done := make(chan error)
	go func() {
		defer close(done)

		finishedProcessingStdout := make(chan error)
		go func() {
			finishedProcessingStdout <- streamToGearman(stdoutReader, job)
		}()

		// Save the cmdErr. We want to process stdout and stderr before we return it
		cmdErr := cmd.Run()
		stdoutWriter.Close()

		stdoutErr := <-finishedProcessingStdout
		if cmdErr != nil {
			done <- cmdErr
		} else if stdoutErr != nil {
			done <- stdoutErr
		}
	}()
	// No timeout
	if conf.CmdTimeout == 0 {
		// Will be nil if the channel was closed without any errors
		return <-done
	}
	select {
	case err := <-done:
		// Will be nil if the channel was closed without any errors
		return err
	case <-time.After(conf.CmdTimeout):
		if err := cmd.Process.Kill(); err != nil {
			return fmt.Errorf("process timeout after %s. error: %s", conf.CmdTimeout.String(), err.Error())
		}
		return fmt.Errorf("process timed out after %s", conf.CmdTimeout.String())
	}
}

// This function streams the reader to the Gearman job (through job.SendData())
func streamToGearman(reader io.Reader, job baseworker.Job) error {
	buffer := make([]byte, 1024)
	for {
		n, err := reader.Read(buffer)
		// Process the data before processing the error (as per the io.Reader docs)
		if n > 0 {
			job.SendData(buffer[:n])
		}
		if err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}
	}
}

// sendStderrWarnings sends the last X lines in the stderr output and to the job's warnings
// field
func sendStderrWarnings(buffer io.Reader, job baseworker.Job, warningLines int) error {
	scanner := bufio.NewScanner(buffer)
	// Create a circular buffer for the last X lines
	lastStderrLines := ring.New(warningLines)
	for scanner.Scan() {
		lastStderrLines = lastStderrLines.Next()
		lastStderrLines.Value = scanner.Bytes()
	}
	// Walk forward through the buffer to get all the last X entries. Note that we call next first
	// so that we start at the oldest entry.
	for i := 0; i < lastStderrLines.Len(); i++ {
		if lastStderrLines = lastStderrLines.Next(); lastStderrLines.Value != nil {
			job.SendWarning(append(lastStderrLines.Value.([]byte), byte('\n')))
		}
	}
	return scanner.Err()
}
<commit_msg>Added log line of type counter.  Will be used for alerting.<commit_after>package gearcmd

import (
	"bufio"
	"bytes"
	"container/ring"
	"fmt"
	"io"
	"log"
	"os"
	"os/exec"
	"strings"
	"time"

	"github.com/Clever/baseworker-go"
	"github.com/Clever/gearcmd/argsparser"
	"gopkg.in/Clever/kayvee-go.v2"
)

// TaskConfig defines the configuration for the task.
type TaskConfig struct {
	FunctionName, FunctionCmd string
	WarningLines              int
	ParseArgs                 bool
	CmdTimeout                time.Duration
	RetryCount                int
}

// Process runs the Gearman job by running the configured task.
// We need to implement the Task interface so we return (byte[], error)
// though the byte[] is always nil.
func (conf TaskConfig) Process(job baseworker.Job) ([]byte, error) {
	// This wraps the actual processing to do some logging
	log.Println(kayvee.FormatLog("gearcmd", kayvee.Info, "START",
		map[string]interface{}{"function": conf.FunctionName, "job_id": getJobId(job), "job_data": string(job.Data())}))

	for {
		start := time.Now()
		err := conf.doProcess(job)
		end := time.Now()
		data := map[string]interface{}{
			"function": conf.FunctionName,
			"job_id":   getJobId(job),
			"job_data": string(job.Data()),
			"type":     "gauge",
		}

		var status string
		if err == nil {
			status = "success"
		} else {
			status = "failure"
		}
		log.Println(kayvee.FormatLog("gearcmd", kayvee.Info, status, map[string]interface{}{
			"type":     "counter",
			"function": conf.FunctionName,
		}))

		// Return if the job was successful.
		if err == nil {
			data["value"] = 1
			data["success"] = true
			log.Println(kayvee.FormatLog("gearcmd", kayvee.Info, "END", data))
			// Hopefully none of our jobs last long enough for a uint64...
			log.Printf(kayvee.FormatLog("gearcmd", kayvee.Info, "duration",
				map[string]interface{}{
					"value":    uint64(end.Sub(start).Seconds() * 1000),
					"type":     "gauge",
					"function": conf.FunctionName,
				},
			))
			return nil, nil
		}
		data["error_message"] = err.Error()
		data["value"] = 0
		data["success"] = false
		// Return if the job has no more retries.
		if conf.RetryCount <= 0 {
			log.Println(kayvee.FormatLog("gearcmd", kayvee.Error, "END", data))
			return nil, err
		}
		conf.RetryCount--
		log.Println(kayvee.FormatLog("gearcmd", kayvee.Error, "RETRY", data))
	}
}

// getJobId returns the jobId from the job handle
func getJobId(job baseworker.Job) string {
	splits := strings.Split(job.Handle(), ":")
	return splits[len(splits)-1]
}

func (conf TaskConfig) doProcess(job baseworker.Job) error {

	defer func() {
		// If we panicked then set the panic message as a warning. Gearman-go will
		// handle marking this job as failed.
		if r := recover(); r != nil {
			err := r.(error)
			job.SendWarning([]byte(err.Error()))
		}
	}()
	var args []string
	var err error
	if conf.ParseArgs {
		args, err = argsparser.ParseArgs(string(job.Data()))
		if err != nil {
			return err
		}
	} else {
		args = []string{string(job.Data())}

	}
	cmd := exec.Command(conf.FunctionCmd, args...)

	// Write the stdout and stderr of the process to both this process' stdout and stderr
	// and also write it to a byte buffer so that we can return it with the Gearman job
	// data as necessary.
	var stderrbuf bytes.Buffer
	cmd.Stderr = io.MultiWriter(os.Stderr, &stderrbuf)
	defer sendStderrWarnings(&stderrbuf, job, conf.WarningLines)

	stdoutReader, stdoutWriter := io.Pipe()
	cmd.Stdout = io.MultiWriter(os.Stdout, stdoutWriter)

	done := make(chan error)
	go func() {
		defer close(done)

		finishedProcessingStdout := make(chan error)
		go func() {
			finishedProcessingStdout <- streamToGearman(stdoutReader, job)
		}()

		// Save the cmdErr. We want to process stdout and stderr before we return it
		cmdErr := cmd.Run()
		stdoutWriter.Close()

		stdoutErr := <-finishedProcessingStdout
		if cmdErr != nil {
			done <- cmdErr
		} else if stdoutErr != nil {
			done <- stdoutErr
		}
	}()
	// No timeout
	if conf.CmdTimeout == 0 {
		// Will be nil if the channel was closed without any errors
		return <-done
	}
	select {
	case err := <-done:
		// Will be nil if the channel was closed without any errors
		return err
	case <-time.After(conf.CmdTimeout):
		if err := cmd.Process.Kill(); err != nil {
			return fmt.Errorf("process timeout after %s. error: %s", conf.CmdTimeout.String(), err.Error())
		}
		return fmt.Errorf("process timed out after %s", conf.CmdTimeout.String())
	}
}

// This function streams the reader to the Gearman job (through job.SendData())
func streamToGearman(reader io.Reader, job baseworker.Job) error {
	buffer := make([]byte, 1024)
	for {
		n, err := reader.Read(buffer)
		// Process the data before processing the error (as per the io.Reader docs)
		if n > 0 {
			job.SendData(buffer[:n])
		}
		if err == io.EOF {
			return nil
		} else if err != nil {
			return err
		}
	}
}

// sendStderrWarnings sends the last X lines in the stderr output and to the job's warnings
// field
func sendStderrWarnings(buffer io.Reader, job baseworker.Job, warningLines int) error {
	scanner := bufio.NewScanner(buffer)
	// Create a circular buffer for the last X lines
	lastStderrLines := ring.New(warningLines)
	for scanner.Scan() {
		lastStderrLines = lastStderrLines.Next()
		lastStderrLines.Value = scanner.Bytes()
	}
	// Walk forward through the buffer to get all the last X entries. Note that we call next first
	// so that we start at the oldest entry.
	for i := 0; i < lastStderrLines.Len(); i++ {
		if lastStderrLines = lastStderrLines.Next(); lastStderrLines.Value != nil {
			job.SendWarning(append(lastStderrLines.Value.([]byte), byte('\n')))
		}
	}
	return scanner.Err()
}
<|endoftext|>


<commit_before>package emitter

import (
	"fmt"
	"time"
	"gopkg.in/vmihailenco/msgpack.v1"
	"github.com/garyburd/redigo/redis"
)

const (
	event              = 2
	binaryEvent        = 5
	redisPoolMaxIdle   = 80
	redisPoolMaxActive = 12000 // max number of connections
)

type EmitterOptions struct {
	Host       string
	Password   string
	Key        string
}

type Emitter struct {
	_opts      EmitterOptions
	_key       string
	_flags     map[string]string
	_rooms     map[string]bool
	_pool      *redis.Pool
}

func New(opts EmitterOptions) Emitter {
	emitter := Emitter{_opts:opts}

	initRedisConnPool(&emitter, opts)

	if opts.Key != "" {
		emitter._key = fmt.Sprintf("%s#emitter", opts.Key)
	}else {
		emitter._key = "socket.io#emitter"
	}

	emitter._rooms = make(map[string]bool)
	emitter._flags = make(map[string]string)

	return emitter
}

func (emitter Emitter) In(room string) Emitter {
	if _, ok := emitter._rooms[room]; ok == false {
		emitter._rooms[room] = true
	}
	return emitter
}

func (emitter Emitter) To(room string) Emitter {
	return emitter.In(room)
}

func (emitter Emitter) Of(nsp string) Emitter {
	emitter._flags["nsp"] = nsp
	return emitter
}

func (emitter Emitter) Emit(args ...interface{}) bool {
	packet := make(map[string]interface{})
	extras := make(map[string]interface{})

	if ok := emitter.hasBin(args); ok {
		packet["type"] = binaryEvent
	}else {
		packet["type"] = event
	}

	packet["data"] = args

	if value, ok := emitter._flags["nsp"]; ok {
		packet["nsp"] = value
		delete(emitter._flags, "nsp")
	}else {
		packet["nsp"] = "/"
	}

	if ok := len(emitter._rooms); ok > 0 {
		//TODO:Cast??
		extras["rooms"] = getKeys(emitter._rooms)
	}else {
		extras["rooms"] = make([]string, 0, 0)
	}

	if ok := len(emitter._flags); ok > 0 {
		extras["flags"] = emitter._flags
	}else {
		extras["flags"] = make(map[string]string)
	}

	//TODO: Gorotunes
	//Pack & Publish
	b, err := msgpack.Marshal([]interface{}{packet, extras})
	if err != nil {
		panic(err)
	}else {
		publish(emitter, emitter._key, b)
	}

	emitter._rooms = make(map[string]bool)
	emitter._flags = make(map[string]string)

	return true
}

func (emitter Emitter) hasBin(args ...interface{}) bool {
	//NOT implemented yet!
	return true
}

func initRedisConnPool(emitter *Emitter , opts EmitterOptions) () {
	if opts.Host == "" {
		panic("Missing redis `host`")
	}

	emitter._pool = newPool(opts)
}

func newPool(opts EmitterOptions) *redis.Pool {
	return &redis.Pool{
		MaxIdle: redisPoolMaxIdle,
		MaxActive: redisPoolMaxActive,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", opts.Host)
			if err != nil {
				return nil, err
			}

			if opts.Password != "" {
				if _, err := c.Do("AUTH", opts.Password); err != nil {
					c.Close()
					return nil, err
				}
				return c, err
			}

			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

}

func publish(emitter Emitter, channel string, value interface{}) {
	c := emitter._pool.Get()
	defer c.Close()
	c.Do("PUBLISH", channel, value)

}

func getKeys(m map[string]bool) []string {
	keys := make([]string, 0, len(m))

	for key := range m {
		keys = append(keys, key)
	}

	return keys
}

func dump(emitter Emitter, args ...interface{}) {
	fmt.Println("Emit params : ", args)
	fmt.Println("Emitter key: ", emitter._key)
	fmt.Println("Emitter flags: ", emitter._flags)
	fmt.Println("Emitter rooms: ", emitter._rooms)
}<commit_msg>fix typo<commit_after>package emitter

import (
	"fmt"
	"time"
	"gopkg.in/vmihailenco/msgpack.v1"
	"github.com/garyburd/redigo/redis"
)

const (
	event              = 2
	binaryEvent        = 5
	redisPoolMaxIdle   = 80
	redisPoolMaxActive = 12000 // max number of connections
)

type EmitterOptions struct {
	Host       string
	Password   string
	Key        string
}

type Emitter struct {
	_opts      EmitterOptions
	_key       string
	_flags     map[string]string
	_rooms     map[string]bool
	_pool      *redis.Pool
}

func New(opts EmitterOptions) Emitter {
	emitter := Emitter{_opts:opts}

	initRedisConnPool(&emitter, opts)

	if opts.Key != "" {
		emitter._key = fmt.Sprintf("%s#emitter", opts.Key)
	}else {
		emitter._key = "socket.io#emitter"
	}

	emitter._rooms = make(map[string]bool)
	emitter._flags = make(map[string]string)

	return emitter
}

func (emitter Emitter) In(room string) Emitter {
	if _, ok := emitter._rooms[room]; ok == false {
		emitter._rooms[room] = true
	}
	return emitter
}

func (emitter Emitter) To(room string) Emitter {
	return emitter.In(room)
}

func (emitter Emitter) Of(nsp string) Emitter {
	emitter._flags["nsp"] = nsp
	return emitter
}

func (emitter Emitter) Emit(args ...interface{}) bool {
	packet := make(map[string]interface{})
	extras := make(map[string]interface{})

	if ok := emitter.hasBin(args); ok {
		packet["type"] = binaryEvent
	}else {
		packet["type"] = event
	}

	packet["data"] = args

	if value, ok := emitter._flags["nsp"]; ok {
		packet["nsp"] = value
		delete(emitter._flags, "nsp")
	}else {
		packet["nsp"] = "/"
	}

	if ok := len(emitter._rooms); ok > 0 {
		//TODO:Cast??
		extras["rooms"] = getKeys(emitter._rooms)
	}else {
		extras["rooms"] = make([]string, 0, 0)
	}

	if ok := len(emitter._flags); ok > 0 {
		extras["flags"] = emitter._flags
	}else {
		extras["flags"] = make(map[string]string)
	}

	//TODO: Goroutines
	//Pack & Publish
	b, err := msgpack.Marshal([]interface{}{packet, extras})
	if err != nil {
		panic(err)
	}else {
		publish(emitter, emitter._key, b)
	}

	emitter._rooms = make(map[string]bool)
	emitter._flags = make(map[string]string)

	return true
}

func (emitter Emitter) hasBin(args ...interface{}) bool {
	//NOT implemented yet!
	return true
}

func initRedisConnPool(emitter *Emitter , opts EmitterOptions) () {
	if opts.Host == "" {
		panic("Missing redis `host`")
	}

	emitter._pool = newPool(opts)
}

func newPool(opts EmitterOptions) *redis.Pool {
	return &redis.Pool{
		MaxIdle: redisPoolMaxIdle,
		MaxActive: redisPoolMaxActive,
		Dial: func() (redis.Conn, error) {
			c, err := redis.Dial("tcp", opts.Host)
			if err != nil {
				return nil, err
			}

			if opts.Password != "" {
				if _, err := c.Do("AUTH", opts.Password); err != nil {
					c.Close()
					return nil, err
				}
				return c, err
			}

			return c, err
		},
		TestOnBorrow: func(c redis.Conn, t time.Time) error {
			_, err := c.Do("PING")
			return err
		},
	}

}

func publish(emitter Emitter, channel string, value interface{}) {
	c := emitter._pool.Get()
	defer c.Close()
	c.Do("PUBLISH", channel, value)

}

func getKeys(m map[string]bool) []string {
	keys := make([]string, 0, len(m))

	for key := range m {
		keys = append(keys, key)
	}

	return keys
}

func dump(emitter Emitter, args ...interface{}) {
	fmt.Println("Emit params : ", args)
	fmt.Println("Emitter key: ", emitter._key)
	fmt.Println("Emitter flags: ", emitter._flags)
	fmt.Println("Emitter rooms: ", emitter._rooms)
}
<|endoftext|>


<commit_before>package apexovernsq

import (
	"fmt"
	"testing"

	"github.com/avct/apexovernsq/protobuf"

	alog "github.com/apex/log"
	"github.com/apex/log/handlers/memory"
	nsq "github.com/nsqio/go-nsq"
)

type entryList []*alog.Entry

// simulateMessageFromNSQ packs an Apex Log Entry into an NSQ Message
// and pushes it through the NSQApexLogHandler.  The NSQApexLogHandler
// will use the Apex Log memory handler to return the Apex Log Entry
// that would be logged.
func simulateMessageFromNSQ(sourceEntry *alog.Entry, filter *[]string) (*alog.Entry, error) {
	var handler *NSQApexLogHandler
	marshalledEntry, err := protobuf.Marshal(sourceEntry)
	if err != nil {
		return nil, fmt.Errorf("Couldn't marshal log entry: %s", err.Error())
	}

	msg := nsq.NewMessage(nsq.MessageID{'a', 'b', 'c'}, []byte(marshalledEntry))

	innerHandler := memory.New()
	if filter == nil {
		handler = NewNSQApexLogHandler(innerHandler, protobuf.Unmarshal)
	} else {
		filterHandler := NewApexLogServiceFilterHandler(innerHandler, filter)
		handler = NewNSQApexLogHandler(filterHandler, protobuf.Unmarshal)
	}

	handler.HandleMessage(msg)
	return innerHandler.Entries[0], nil
}

// simulateMessagesFromNSQ packs an entryList into a series of NSQ Messages and pushes them through the NSQApexLogHandler.  The NSQApexLogHandler will use the Apex Log memory handler to return an entryList that would be logged locally.
func simulateMessagesFromNSQ(sourceEntries *entryList, filter *[]string) (*entryList, error) {
	var marshalledEntry []byte
	var err error
	var msg *nsq.Message
	var handler nsq.Handler

	innerHandler := memory.New()
	if filter == nil {
		handler = NewNSQApexLogHandler(innerHandler, protobuf.Unmarshal)
	} else {
		filterHandler := NewApexLogServiceFilterHandler(innerHandler, filter)
		handler = NewNSQApexLogHandler(filterHandler, protobuf.Unmarshal)
	}

	for _, sourceEntry := range *sourceEntries {
		marshalledEntry, err = protobuf.Marshal(sourceEntry)
		if err != nil {
			return nil, fmt.Errorf("Couldn't marshal log entry: %s", err.Error())
		}

		msg = nsq.NewMessage(nsq.MessageID{'a', 'b', 'c'}, marshalledEntry)
		handler.HandleMessage(msg)
	}
	result := innerHandler.Entries[:]
	return (*entryList)(&result), nil

}

// simulateEntry returns a finalised Entry from the memory handler as
// it would appear in normal logging.
func simulateEntry(logger alog.Interface, fields alog.Fielder, msg string) *alog.Entry {
	memoryHandler := memory.New()
	alog.SetHandler(memoryHandler)
	if fields == nil {
		logger.Info(msg)
	} else {
		logger.WithFields(fields).Info(msg)
	}
	return memoryHandler.Entries[0]
}

// simulateEntries makes a stipulated number of finalised Apex Log
// Entry instances and stores them in a provided store (a pointer to
// an entryList), starting from a given offset. ⚠ Danger Will
// Robinson, here be side-effects. The provided offset and store will
// both be mutated. ⚠
func simulateEntries(ctx alog.Interface, count int, offset *int, store *entryList) {
	var m int
	for m = 0; m < count; m++ {
		entry := simulateEntry(ctx, nil, fmt.Sprintf("%d", *offset))
		(*store)[*offset] = entry
		*offset++
	}
}

func TestHandleMessage(t *testing.T) {
	logger := alog.Log
	sourceEntry := simulateEntry(logger, alog.Fields{
		"flavour": "pistachio",
		"scoops":  "2",
	}, "it's ice cream time!")

	entry, err := simulateMessageFromNSQ(sourceEntry, nil)
	if err != nil {
		t.Fatalf("%s", err.Error())
	}
	if entry.Message != sourceEntry.Message {
		t.Errorf("Expected %q, got %q", sourceEntry.Message, entry.Message)
	}
	if entry.Level != sourceEntry.Level {
		t.Errorf("Expected %s, got %s", sourceEntry.Level, entry.Level)
	}
	if entry.Timestamp != sourceEntry.Timestamp {
		t.Errorf("Expected %q, got %q", sourceEntry.Timestamp, entry.Timestamp)
	}
	expectedFieldCount := len(sourceEntry.Fields)
	actualFieldCount := len(entry.Fields)
	if expectedFieldCount != actualFieldCount {
		t.Errorf("Expected %d fields, but got %d fields", expectedFieldCount, actualFieldCount)
	}
	for fieldName, value := range sourceEntry.Fields {
		recieved := entry.Fields.Get(fieldName)
		if recieved != value {
			t.Errorf("Expected %s=%q, got %s=%q", fieldName, value, fieldName, recieved)
		}
	}
}

func TestFilterMessagesByService(t *testing.T) {
	var ctx alog.Interface
	var entries *entryList
	var err error
	var generatedMessages int
	var resultEntryCount int
	var sourceEntries entryList
	var totalMessages int

	var thisProcessFilter = &[]string{processName()}
	var otherProcessFilter = &[]string{"other"}
	var unknownProcessFilter = &[]string{"unknown"}
	var multiServiceFilter = &[]string{processName(), "other"}

	var caseTable = []struct {
		serviceMessages      int
		otherServiceMessages int
		nonServiceMessages   int
		resultEntryCount     int
		filter               *[]string
		messages             []string
	}{
		{1, 1, 1, 3, nil, []string{"0", "1", "2"}},           // Default case, no filtering
		{1, 1, 1, 1, thisProcessFilter, []string{"0"}},       // Whitelist service messages
		{1, 1, 1, 1, otherProcessFilter, []string{"1"}},      // Whitelist other service messages
		{1, 1, 1, 0, unknownProcessFilter, []string{}},       // Whitelist service that isn't present
		{1, 1, 1, 2, multiServiceFilter, []string{"0", "1"}}, // Whitelist both this service and "other" service

	}

	for caseNum, testCase := range caseTable {
		totalMessages = testCase.serviceMessages + testCase.nonServiceMessages + testCase.otherServiceMessages
		generatedMessages = 0
		sourceEntries = make([]*alog.Entry, totalMessages)

		// Generate service messages
		ctx = NewApexLogServiceContext()
		simulateEntries(ctx, testCase.serviceMessages, &generatedMessages, &sourceEntries)
		// Generate other service messages
		ctx = ctx.WithFields(alog.Fields{"service": "other"})
		simulateEntries(ctx, testCase.otherServiceMessages, &generatedMessages, &sourceEntries)

		// Generate non-service messages
		ctx = alog.Log
		simulateEntries(ctx, testCase.nonServiceMessages, &generatedMessages, &sourceEntries)

		entries, err = simulateMessagesFromNSQ(&sourceEntries, testCase.filter)
		if err != nil {
			t.Fatalf("Error in message simulation: %s", err.Error())
		}
		if entries == nil && testCase.resultEntryCount > 0 {
			t.Errorf("Expected %d entries from test case %d but got nil. Message counts: service %d, other-service %d, non-service %d.  Filter: %+v.", testCase.resultEntryCount, caseNum, testCase.serviceMessages, testCase.otherServiceMessages, testCase.nonServiceMessages, testCase.filter)
			continue
		}
		resultEntryCount = len(*entries)
		if resultEntryCount != testCase.resultEntryCount {
			t.Errorf("[Case %d] Expected %d entries, but got %d. Message counts: service %d, other-service %d, non-service %d.  Filter: %+v.", caseNum+1, testCase.resultEntryCount, resultEntryCount, testCase.serviceMessages, testCase.otherServiceMessages, testCase.nonServiceMessages, testCase.filter)
			continue
		}

		for idx, entry := range *entries {
			testMessage := testCase.messages[idx]
			if entry.Message != testMessage {
				t.Errorf("[Case %d] Expected Entry[%d] to have message %q, but got %q", caseNum, idx, testMessage, entry.Message)
			}
		}
	}
}
<commit_msg>Use time.Time.Equal instead of = operator when comparing timestamps<commit_after>package apexovernsq

import (
	"fmt"
	"testing"

	"github.com/avct/apexovernsq/protobuf"

	alog "github.com/apex/log"
	"github.com/apex/log/handlers/memory"
	nsq "github.com/nsqio/go-nsq"
)

type entryList []*alog.Entry

// simulateMessageFromNSQ packs an Apex Log Entry into an NSQ Message
// and pushes it through the NSQApexLogHandler.  The NSQApexLogHandler
// will use the Apex Log memory handler to return the Apex Log Entry
// that would be logged.
func simulateMessageFromNSQ(sourceEntry *alog.Entry, filter *[]string) (*alog.Entry, error) {
	var handler *NSQApexLogHandler
	marshalledEntry, err := protobuf.Marshal(sourceEntry)
	if err != nil {
		return nil, fmt.Errorf("Couldn't marshal log entry: %s", err.Error())
	}

	msg := nsq.NewMessage(nsq.MessageID{'a', 'b', 'c'}, []byte(marshalledEntry))

	innerHandler := memory.New()
	if filter == nil {
		handler = NewNSQApexLogHandler(innerHandler, protobuf.Unmarshal)
	} else {
		filterHandler := NewApexLogServiceFilterHandler(innerHandler, filter)
		handler = NewNSQApexLogHandler(filterHandler, protobuf.Unmarshal)
	}

	handler.HandleMessage(msg)
	return innerHandler.Entries[0], nil
}

// simulateMessagesFromNSQ packs an entryList into a series of NSQ Messages and pushes them through the NSQApexLogHandler.  The NSQApexLogHandler will use the Apex Log memory handler to return an entryList that would be logged locally.
func simulateMessagesFromNSQ(sourceEntries *entryList, filter *[]string) (*entryList, error) {
	var marshalledEntry []byte
	var err error
	var msg *nsq.Message
	var handler nsq.Handler

	innerHandler := memory.New()
	if filter == nil {
		handler = NewNSQApexLogHandler(innerHandler, protobuf.Unmarshal)
	} else {
		filterHandler := NewApexLogServiceFilterHandler(innerHandler, filter)
		handler = NewNSQApexLogHandler(filterHandler, protobuf.Unmarshal)
	}

	for _, sourceEntry := range *sourceEntries {
		marshalledEntry, err = protobuf.Marshal(sourceEntry)
		if err != nil {
			return nil, fmt.Errorf("Couldn't marshal log entry: %s", err.Error())
		}

		msg = nsq.NewMessage(nsq.MessageID{'a', 'b', 'c'}, marshalledEntry)
		handler.HandleMessage(msg)
	}
	result := innerHandler.Entries[:]
	return (*entryList)(&result), nil

}

// simulateEntry returns a finalised Entry from the memory handler as
// it would appear in normal logging.
func simulateEntry(logger alog.Interface, fields alog.Fielder, msg string) *alog.Entry {
	memoryHandler := memory.New()
	alog.SetHandler(memoryHandler)
	if fields == nil {
		logger.Info(msg)
	} else {
		logger.WithFields(fields).Info(msg)
	}
	return memoryHandler.Entries[0]
}

// simulateEntries makes a stipulated number of finalised Apex Log
// Entry instances and stores them in a provided store (a pointer to
// an entryList), starting from a given offset. ⚠ Danger Will
// Robinson, here be side-effects. The provided offset and store will
// both be mutated. ⚠
func simulateEntries(ctx alog.Interface, count int, offset *int, store *entryList) {
	var m int
	for m = 0; m < count; m++ {
		entry := simulateEntry(ctx, nil, fmt.Sprintf("%d", *offset))
		(*store)[*offset] = entry
		*offset++
	}
}

func TestHandleMessage(t *testing.T) {
	logger := alog.Log
	sourceEntry := simulateEntry(logger, alog.Fields{
		"flavour": "pistachio",
		"scoops":  "2",
	}, "it's ice cream time!")

	entry, err := simulateMessageFromNSQ(sourceEntry, nil)
	if err != nil {
		t.Fatalf("%s", err.Error())
	}
	if entry.Message != sourceEntry.Message {
		t.Errorf("Expected %q, got %q", sourceEntry.Message, entry.Message)
	}
	if entry.Level != sourceEntry.Level {
		t.Errorf("Expected %s, got %s", sourceEntry.Level, entry.Level)
	}
	if !entry.Timestamp.Equal(sourceEntry.Timestamp) {
		t.Errorf("Expected %q, got %q", sourceEntry.Timestamp, entry.Timestamp)
	}
	expectedFieldCount := len(sourceEntry.Fields)
	actualFieldCount := len(entry.Fields)
	if expectedFieldCount != actualFieldCount {
		t.Errorf("Expected %d fields, but got %d fields", expectedFieldCount, actualFieldCount)
	}
	for fieldName, value := range sourceEntry.Fields {
		recieved := entry.Fields.Get(fieldName)
		if recieved != value {
			t.Errorf("Expected %s=%q, got %s=%q", fieldName, value, fieldName, recieved)
		}
	}
}

func TestFilterMessagesByService(t *testing.T) {
	var ctx alog.Interface
	var entries *entryList
	var err error
	var generatedMessages int
	var resultEntryCount int
	var sourceEntries entryList
	var totalMessages int

	var thisProcessFilter = &[]string{processName()}
	var otherProcessFilter = &[]string{"other"}
	var unknownProcessFilter = &[]string{"unknown"}
	var multiServiceFilter = &[]string{processName(), "other"}

	var caseTable = []struct {
		serviceMessages      int
		otherServiceMessages int
		nonServiceMessages   int
		resultEntryCount     int
		filter               *[]string
		messages             []string
	}{
		{1, 1, 1, 3, nil, []string{"0", "1", "2"}},           // Default case, no filtering
		{1, 1, 1, 1, thisProcessFilter, []string{"0"}},       // Whitelist service messages
		{1, 1, 1, 1, otherProcessFilter, []string{"1"}},      // Whitelist other service messages
		{1, 1, 1, 0, unknownProcessFilter, []string{}},       // Whitelist service that isn't present
		{1, 1, 1, 2, multiServiceFilter, []string{"0", "1"}}, // Whitelist both this service and "other" service

	}

	for caseNum, testCase := range caseTable {
		totalMessages = testCase.serviceMessages + testCase.nonServiceMessages + testCase.otherServiceMessages
		generatedMessages = 0
		sourceEntries = make([]*alog.Entry, totalMessages)

		// Generate service messages
		ctx = NewApexLogServiceContext()
		simulateEntries(ctx, testCase.serviceMessages, &generatedMessages, &sourceEntries)
		// Generate other service messages
		ctx = ctx.WithFields(alog.Fields{"service": "other"})
		simulateEntries(ctx, testCase.otherServiceMessages, &generatedMessages, &sourceEntries)

		// Generate non-service messages
		ctx = alog.Log
		simulateEntries(ctx, testCase.nonServiceMessages, &generatedMessages, &sourceEntries)

		entries, err = simulateMessagesFromNSQ(&sourceEntries, testCase.filter)
		if err != nil {
			t.Fatalf("Error in message simulation: %s", err.Error())
		}
		if entries == nil && testCase.resultEntryCount > 0 {
			t.Errorf("Expected %d entries from test case %d but got nil. Message counts: service %d, other-service %d, non-service %d.  Filter: %+v.", testCase.resultEntryCount, caseNum, testCase.serviceMessages, testCase.otherServiceMessages, testCase.nonServiceMessages, testCase.filter)
			continue
		}
		resultEntryCount = len(*entries)
		if resultEntryCount != testCase.resultEntryCount {
			t.Errorf("[Case %d] Expected %d entries, but got %d. Message counts: service %d, other-service %d, non-service %d.  Filter: %+v.", caseNum+1, testCase.resultEntryCount, resultEntryCount, testCase.serviceMessages, testCase.otherServiceMessages, testCase.nonServiceMessages, testCase.filter)
			continue
		}

		for idx, entry := range *entries {
			testMessage := testCase.messages[idx]
			if entry.Message != testMessage {
				t.Errorf("[Case %d] Expected Entry[%d] to have message %q, but got %q", caseNum, idx, testMessage, entry.Message)
			}
		}
	}
}
<|endoftext|>


<commit_before>package container

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strings"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/node"
	"github.com/zenoss/serviced/validation"
)

const defaultSubnet string = "10.3" // /16 subnet for virtual addresses

type vif struct {
	name     string
	ip       string
	hostname string
	tcpPorts map[string]string
	udpPorts map[string]string
}

// VIFRegistry holds state regarding virtual interfaces. It is meant to be
// created in the proxy to manage vifs in the running service container.
type VIFRegistry struct {
	subnet string
	vifs   map[string]*vif
}

// NewVIFRegistry initializes a new VIFRegistry.
func NewVIFRegistry() *VIFRegistry {
	return &VIFRegistry{subnet: defaultSubnet, vifs: make(map[string]*vif)}
}

// SetSubnet sets the subnet used for virtual addresses
func (reg *VIFRegistry) SetSubnet(subnet string) error {
	if err := validation.IsSubnet16(subnet); err != nil {
		return err
	}
	reg.subnet = subnet
	glog.Infof("vif subnet is: %s", reg.subnet)
	return nil
}

func (reg *VIFRegistry) nextIP() (string, error) {
	n := len(reg.vifs) + 2
	if n > (255 * 255) {
		return "", fmt.Errorf("unable to allocate IPs for %d interfaces", n)
	}
	o3 := (n / 255)
	o4 := (n - (o3 * 255))
	// ZEN-11478: made the subnet configurable
	return fmt.Sprintf("%s.%d.%d", reg.subnet, o3, o4), nil
}

// RegisterVirtualAddress takes care of the entire virtual address setup. It
// creates a virtual interface if one does not yet exist, allocates an IP
// address, assigns it to the virtual interface, adds an entry to /etc/hosts,
// and sets up the iptables rule to redirect traffic to the specified port.
func (reg *VIFRegistry) RegisterVirtualAddress(address, toport, protocol string) error {
	glog.Infof("RegisterVirtualAddress address:%s toport:%s protocol:%s", address, toport, protocol)
	var (
		host, port string
		viface     *vif
		err        error
		ok         bool
		portmap    *map[string]string
	)
	if host, port, err = net.SplitHostPort(address); err != nil {
		return err
	}
	if viface, ok = reg.vifs[host]; !ok {
		// vif doesn't exist yet
		ip, err := reg.nextIP()
		if err != nil {
			return err
		}
		viface = &vif{
			hostname: host,
			ip:       ip,
			name:     "eth0:" + host,
			tcpPorts: make(map[string]string),
			udpPorts: make(map[string]string),
		}
		if err = viface.createCommand(); err != nil {
			return err
		}
		reg.vifs[host] = viface
	}
	switch strings.ToLower(protocol) {
	case "tcp":
		portmap = &viface.tcpPorts
	case "udp":
		portmap = &viface.udpPorts
	default:
		return fmt.Errorf("invalid protocol: %s", protocol)
	}

	glog.Infof("portmap: %+v", *portmap)
	if _, ok := (*portmap)[toport]; !ok {
		// dest isn't there, let's DO IT!!!!!
		if err := viface.redirectCommand(port, toport, protocol); err != nil {
			return err
		}
		(*portmap)[toport] = port
	}
	return nil
}

func (viface *vif) createCommand() error {
	command := []string{
		"ip", "link", "add", "link", "eth0",
		"name", viface.name,
		"type", "veth",
		"peer", "name", viface.name + "-peer",
	}
	c := exec.Command(command[0], command[1:]...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stdout

	if err := c.Run(); err != nil {
		glog.Errorf("Adding virtual interface failed: %+v", err)
		return err
	}
	command = []string{
		"ip", "addr", "add", viface.ip + "/16", "dev", viface.name,
	}
	c = exec.Command(command[0], command[1:]...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stdout
	if err := c.Run(); err != nil {
		glog.Errorf("Adding IP to virtual interface failed: %+v", err)
		return err
	}
	command = []string{
		"ip", "link", "set", viface.name, "up",
	}
	c = exec.Command(command[0], command[1:]...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stdout
	if err := c.Run(); err != nil {
		glog.Errorf("Bringing interface %s up failed: %+v", viface.name, err)
		return err
	}
	return nil
}

func (viface *vif) redirectCommand(from, to, protocol string) error {
	for _, chain := range []string{"OUTPUT", "PREROUTING"} {
		command := []string{
			"iptables",
			"-t", "nat",
			"-A", chain,
			"-d", viface.ip,
			"-p", protocol,
			"--dport", from,
			"-j", "REDIRECT",
			"--to-ports", to,
		}
		c := exec.Command(command[0], command[1:]...)

		c.Stdout = os.Stdout
		c.Stderr = os.Stdout
		if err := c.Run(); err != nil {
			glog.Errorf("Unable to set up redirect %s:%s->:%s", viface.hostname, from, to)
			return err
		}
	}

	glog.Infof("AddToEtcHosts(%s, %s)", viface.hostname, viface.ip)
	err := node.AddToEtcHosts(viface.hostname, viface.ip)
	if err != nil {
		glog.Errorf("Unable to add %s to /etc/hosts", viface.hostname)
		return err
	}
	return nil
}
<commit_msg>lock when calling vif.RegisterVirtualAddress()<commit_after>package container

import (
	"fmt"
	"net"
	"os"
	"os/exec"
	"strings"
	"sync"

	"github.com/zenoss/glog"
	"github.com/zenoss/serviced/node"
	"github.com/zenoss/serviced/validation"
)

const defaultSubnet string = "10.3" // /16 subnet for virtual addresses

type vif struct {
	name     string
	ip       string
	hostname string
	tcpPorts map[string]string
	udpPorts map[string]string
}

// VIFRegistry holds state regarding virtual interfaces. It is meant to be
// created in the proxy to manage vifs in the running service container.
type VIFRegistry struct {
	sync.RWMutex
	subnet string
	vifs   map[string]*vif
}

// NewVIFRegistry initializes a new VIFRegistry.
func NewVIFRegistry() *VIFRegistry {
	return &VIFRegistry{subnet: defaultSubnet, vifs: make(map[string]*vif)}
}

// SetSubnet sets the subnet used for virtual addresses
func (reg *VIFRegistry) SetSubnet(subnet string) error {
	if err := validation.IsSubnet16(subnet); err != nil {
		return err
	}
	reg.subnet = subnet
	glog.Infof("vif subnet is: %s", reg.subnet)
	return nil
}

func (reg *VIFRegistry) nextIP() (string, error) {
	n := len(reg.vifs) + 2
	if n > (255 * 255) {
		return "", fmt.Errorf("unable to allocate IPs for %d interfaces", n)
	}
	o3 := (n / 255)
	o4 := (n - (o3 * 255))
	// ZEN-11478: made the subnet configurable
	return fmt.Sprintf("%s.%d.%d", reg.subnet, o3, o4), nil
}

// RegisterVirtualAddress takes care of the entire virtual address setup. It
// creates a virtual interface if one does not yet exist, allocates an IP
// address, assigns it to the virtual interface, adds an entry to /etc/hosts,
// and sets up the iptables rule to redirect traffic to the specified port.
func (reg *VIFRegistry) RegisterVirtualAddress(address, toport, protocol string) error {
	glog.Infof("RegisterVirtualAddress address:%s toport:%s protocol:%s  locking...", address, toport, protocol)
	reg.Lock()
	defer reg.Unlock()
	glog.Infof("RegisterVirtualAddress address:%s toport:%s protocol:%s  locked", address, toport, protocol)

	var (
		host, port string
		viface     *vif
		err        error
		ok         bool
		portmap    *map[string]string
	)
	if host, port, err = net.SplitHostPort(address); err != nil {
		return err
	}
	if viface, ok = reg.vifs[host]; !ok {
		// vif doesn't exist yet
		ip, err := reg.nextIP()
		if err != nil {
			return err
		}
		viface = &vif{
			hostname: host,
			ip:       ip,
			name:     "eth0:" + host,
			tcpPorts: make(map[string]string),
			udpPorts: make(map[string]string),
		}
		if err = viface.createCommand(); err != nil {
			return err
		}
		reg.vifs[host] = viface
	}
	switch strings.ToLower(protocol) {
	case "tcp":
		portmap = &viface.tcpPorts
	case "udp":
		portmap = &viface.udpPorts
	default:
		return fmt.Errorf("invalid protocol: %s", protocol)
	}

	glog.Infof("RegisterVirtualAddress portmap: %+v", *portmap)
	if _, ok := (*portmap)[toport]; !ok {
		// dest isn't there, let's DO IT!!!!!
		if err := viface.redirectCommand(port, toport, protocol); err != nil {
			return err
		}
		(*portmap)[toport] = port
	}
	return nil
}

func (viface *vif) createCommand() error {
	command := []string{
		"ip", "link", "add", "link", "eth0",
		"name", viface.name,
		"type", "veth",
		"peer", "name", viface.name + "-peer",
	}
	c := exec.Command(command[0], command[1:]...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stdout

	if err := c.Run(); err != nil {
		glog.Errorf("Adding virtual interface failed: %+v", err)
		return err
	}
	command = []string{
		"ip", "addr", "add", viface.ip + "/16", "dev", viface.name,
	}
	c = exec.Command(command[0], command[1:]...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stdout
	if err := c.Run(); err != nil {
		glog.Errorf("Adding IP to virtual interface failed: %+v", err)
		return err
	}
	command = []string{
		"ip", "link", "set", viface.name, "up",
	}
	c = exec.Command(command[0], command[1:]...)
	c.Stdout = os.Stdout
	c.Stderr = os.Stdout
	if err := c.Run(); err != nil {
		glog.Errorf("Bringing interface %s up failed: %+v", viface.name, err)
		return err
	}
	return nil
}

func (viface *vif) redirectCommand(from, to, protocol string) error {
	glog.Infof("Trying to set up redirect %s:%s->:%s %s", viface.hostname, from, to, protocol)
	for _, chain := range []string{"OUTPUT", "PREROUTING"} {
		command := []string{
			"iptables",
			"-t", "nat",
			"-A", chain,
			"-d", viface.ip,
			"-p", protocol,
			"--dport", from,
			"-j", "REDIRECT",
			"--to-ports", to,
		}
		c := exec.Command(command[0], command[1:]...)

		c.Stdout = os.Stdout
		c.Stderr = os.Stdout
		if err := c.Run(); err != nil {
			glog.Errorf("Unable to set up redirect %s:%s->:%s %s command:%+v", viface.hostname, from, to, protocol, command)
			return err
		}
	}

	glog.Infof("AddToEtcHosts(%s, %s)", viface.hostname, viface.ip)
	err := node.AddToEtcHosts(viface.hostname, viface.ip)
	if err != nil {
		glog.Errorf("Unable to add %s %s to /etc/hosts", viface.ip, viface.hostname)
		return err
	}
	return nil
}
<|endoftext|>


<commit_before>package lazycache
// A generic cache that favors returning stale data
// than blocking a caller

import (
  "sync"
  "time"
)

type Fetcher func(id string) (interface{}, error)

type Item struct {
  object interface{}
  expires time.Time
}

type LazyCache struct {
  fetcher Fetcher
  ttl time.Duration
  lock sync.RWMutex
  items map[string]*Item
}

func New(fetcher Fetcher, ttl time.Duration, size int) *LazyCache {
  return &LazyCache{
    ttl: ttl,
    fetcher: fetcher,
    items: make(map[string]*Item, size),
  }
}

func (cache *LazyCache) Get(id string) (interface{}, bool) {
  cache.lock.RLock()
  item, exists := cache.items[id]
  cache.lock.RUnlock()
  if exists == false {
    return cache.Fetch(id, item)
  }
  if time.Now().After(item.expires) {
    if item.object == nil {
      return cache.Fetch(id, item)
    } else {
      go cache.Fetch(id, item)
    }
  }
  return item.object, true
}

func (cache *LazyCache) Set(id string, object interface{}) {
  cache.lock.Lock()
  defer cache.lock.Unlock()
  cache.items[id] = &Item{expires: time.Now().Add(cache.ttl), object: object}
}

func (cache *LazyCache) Fetch(id string, current *Item) (interface{}, bool) {
  object, err := cache.fetcher(id)
  if err != nil { return nil, false }

  if current == nil {
    if object == nil { return nil, false }
    cache.Set(id, object)
    return object, true
  }

  cache.lock.Lock()
  defer cache.lock.Unlock()
  if object == nil {
    delete(cache.items, id)
  } else {
    current.expires = time.Now().Add(cache.ttl)
    current.object = object
  }
  return object, object != nil
}
<commit_msg>shoul set misses<commit_after>package lazycache
// A generic cache that favors returning stale data
// than blocking a caller

import (
  "sync"
  "time"
)

type Fetcher func(id string) (interface{}, error)

type Item struct {
  object interface{}
  expires time.Time
}

type LazyCache struct {
  fetcher Fetcher
  ttl time.Duration
  lock sync.RWMutex
  items map[string]*Item
}

func New(fetcher Fetcher, ttl time.Duration, size int) *LazyCache {
  return &LazyCache{
    ttl: ttl,
    fetcher: fetcher,
    items: make(map[string]*Item, size),
  }
}

func (cache *LazyCache) Get(id string) (interface{}, bool) {
  cache.lock.RLock()
  item, exists := cache.items[id]
  cache.lock.RUnlock()
  if exists == false {
    return cache.Fetch(id, item)
  }
  if time.Now().After(item.expires) {
    if item.object == nil {
      return cache.Fetch(id, item)
    } else {
      go cache.Fetch(id, item)
    }
  }
  return item.object, true
}

func (cache *LazyCache) Set(id string, object interface{}) {
  cache.lock.Lock()
  defer cache.lock.Unlock()
  cache.items[id] = &Item{expires: time.Now().Add(cache.ttl), object: object}
}

func (cache *LazyCache) Fetch(id string, current *Item) (interface{}, bool) {
  object, err := cache.fetcher(id)
  if err != nil { return nil, false }

  if current == nil {
    cache.Set(id, object)
    return object, object != nil
  }

  cache.lock.Lock()
  defer cache.lock.Unlock()
  if object == nil {
    delete(cache.items, id)
  } else {
    current.expires = time.Now().Add(cache.ttl)
    current.object = object
  }
  return object, object != nil
}
<|endoftext|>


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

package os

import (
	"io"
	"runtime"
	"sync"
	"syscall"
	"unicode/utf16"
)

// File represents an open file descriptor.
type File struct {
	*file
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	fd      syscall.Handle
	name    string
	dirinfo *dirInfo   // nil unless directory being read
	nepipe  int        // number of consecutive EPIPE in Write
	l       sync.Mutex // used to implement windows pread/pwrite
}

// Fd returns the Windows handle referencing the open file.
func (file *File) Fd() syscall.Handle {
	if file == nil {
		return syscall.InvalidHandle
	}
	return file.fd
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd syscall.Handle, name string) *File {
	if fd < 0 {
		return nil
	}
	f := &File{&file{fd: fd, name: name}}
	runtime.SetFinalizer(f.file, (*file).close)
	return f
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
	data     syscall.Win32finddata
	needdata bool
}

const DevNull = "NUL"

func (file *File) isdir() bool { return file != nil && file.dirinfo != nil }

func openFile(name string, flag int, perm uint32) (file *File, err error) {
	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec.go
	if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
		syscall.CloseOnExec(r)
	}

	return NewFile(r, name), nil
}

func openDir(name string) (file *File, err error) {
	d := new(dirInfo)
	r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
	if e != nil {
		return nil, &PathError{"open", name, e}
	}
	f := NewFile(r, name)
	f.dirinfo = d
	return f, nil
}

// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// It returns the File and an error, if any.
func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
	// TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
	r, e := openDir(name)
	if e == nil {
		if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
			r.Close()
			return nil, &PathError{"open", name, EISDIR}
		}
		return r, nil
	}
	r, e = openFile(name, flag, perm)
	if e == nil {
		return r, nil
	}
	return nil, e
}

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (file *File) Close() error {
	return file.file.close()
}

func (file *file) close() error {
	if file == nil || file.fd < 0 {
		return EINVAL
	}
	var e error
	if file.isdir() {
		e = syscall.FindClose(syscall.Handle(file.fd))
	} else {
		e = syscall.CloseHandle(syscall.Handle(file.fd))
	}
	var err error
	if e != nil {
		err = &PathError{"close", file.name, e}
	}
	file.fd = syscall.InvalidHandle // so it can't be closed again

	// no need for a finalizer anymore
	runtime.SetFinalizer(file, nil)
	return err
}

// Readdir reads the contents of the directory associated with file and
// returns an array of up to n FileInfo structures, as would be returned
// by Lstat, in directory order. Subsequent calls on the same file will yield
// further FileInfos.
//
// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
// Readdir returns an empty slice, it will return a non-nil error
// explaining why. At the end of a directory, the error is io.EOF.
//
// If n <= 0, Readdir returns all the FileInfo from the directory in
// a single slice. In this case, if Readdir succeeds (reads all
// the way to the end of the directory), it returns the slice and a
// nil error. If it encounters an error before the end of the
// directory, Readdir returns the FileInfo read until that point
// and a non-nil error.
func (file *File) Readdir(n int) (fi []FileInfo, err error) {
	if file == nil || file.fd < 0 {
		return nil, EINVAL
	}
	if !file.isdir() {
		return nil, &PathError{"Readdir", file.name, ENOTDIR}
	}
	wantAll := n <= 0
	size := n
	if wantAll {
		n = -1
		size = 100
	}
	fi = make([]FileInfo, 0, size) // Empty with room to grow.
	d := &file.dirinfo.data
	for n != 0 {
		if file.dirinfo.needdata {
			e := syscall.FindNextFile(syscall.Handle(file.fd), d)
			if e != nil {
				if e == syscall.ERROR_NO_MORE_FILES {
					break
				} else {
					err = &PathError{"FindNextFile", file.name, e}
					if !wantAll {
						fi = nil
					}
					return
				}
			}
		}
		var f FileInfo
		setFileInfo(&f, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
		file.dirinfo.needdata = true
		if f.Name == "." || f.Name == ".." { // Useless names
			continue
		}
		n--
		fi = append(fi, f)
	}
	if !wantAll && len(fi) == 0 {
		return fi, io.EOF
	}
	return fi, nil
}

// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Read(f.fd, b)
}

// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to 0.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != nil {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
	if e != nil {
		return 0, e
	}
	return int(done), nil
}

// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Write(f.fd, b)
}

// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != nil {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
	if e != nil {
		return 0, e
	}
	return int(done), nil
}

// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Seek(f.fd, offset, whence)
}

// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
func Truncate(name string, size int64) error {
	f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
	if e != nil {
		return e
	}
	defer f.Close()
	e1 := f.Truncate(size)
	if e1 != nil {
		return e1
	}
	return nil
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an error, if any.
func Pipe() (r *File, w *File, err error) {
	var p [2]syscall.Handle

	// See ../syscall/exec.go for description of lock.
	syscall.ForkLock.RLock()
	e := syscall.Pipe(p[0:])
	if e != nil {
		syscall.ForkLock.RUnlock()
		return nil, nil, NewSyscallError("pipe", e)
	}
	syscall.CloseOnExec(p[0])
	syscall.CloseOnExec(p[1])
	syscall.ForkLock.RUnlock()

	return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	const pathSep = '\\'
	dirw := make([]uint16, syscall.MAX_PATH)
	n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
	if n > uint32(len(dirw)) {
		dirw = make([]uint16, n)
		n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
		if n > uint32(len(dirw)) {
			n = 0
		}
	}
	if n > 0 && dirw[n-1] == pathSep {
		n--
	}
	return string(utf16.Decode(dirw[0:n]))
}
<commit_msg>os: fix windows build<commit_after>// Copyright 2009 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package os

import (
	"io"
	"runtime"
	"sync"
	"syscall"
	"unicode/utf16"
)

// File represents an open file descriptor.
type File struct {
	*file
}

// file is the real representation of *File.
// The extra level of indirection ensures that no clients of os
// can overwrite this data, which could cause the finalizer
// to close the wrong file descriptor.
type file struct {
	fd      syscall.Handle
	name    string
	dirinfo *dirInfo   // nil unless directory being read
	nepipe  int        // number of consecutive EPIPE in Write
	l       sync.Mutex // used to implement windows pread/pwrite
}

// Fd returns the Windows handle referencing the open file.
func (file *File) Fd() syscall.Handle {
	if file == nil {
		return syscall.InvalidHandle
	}
	return file.fd
}

// NewFile returns a new File with the given file descriptor and name.
func NewFile(fd syscall.Handle, name string) *File {
	if fd < 0 {
		return nil
	}
	f := &File{&file{fd: fd, name: name}}
	runtime.SetFinalizer(f.file, (*file).close)
	return f
}

// Auxiliary information if the File describes a directory
type dirInfo struct {
	data     syscall.Win32finddata
	needdata bool
}

const DevNull = "NUL"

func (file *file) isdir() bool { return file != nil && file.dirinfo != nil }

func openFile(name string, flag int, perm uint32) (file *File, err error) {
	r, e := syscall.Open(name, flag|syscall.O_CLOEXEC, perm)
	if e != nil {
		return nil, &PathError{"open", name, e}
	}

	// There's a race here with fork/exec, which we are
	// content to live with.  See ../syscall/exec.go
	if syscall.O_CLOEXEC == 0 { // O_CLOEXEC not supported
		syscall.CloseOnExec(r)
	}

	return NewFile(r, name), nil
}

func openDir(name string) (file *File, err error) {
	d := new(dirInfo)
	r, e := syscall.FindFirstFile(syscall.StringToUTF16Ptr(name+`\*`), &d.data)
	if e != nil {
		return nil, &PathError{"open", name, e}
	}
	f := NewFile(r, name)
	f.dirinfo = d
	return f, nil
}

// OpenFile is the generalized open call; most users will use Open
// or Create instead.  It opens the named file with specified flag
// (O_RDONLY etc.) and perm, (0666 etc.) if applicable.  If successful,
// methods on the returned File can be used for I/O.
// It returns the File and an error, if any.
func OpenFile(name string, flag int, perm uint32) (file *File, err error) {
	// TODO(brainman): not sure about my logic of assuming it is dir first, then fall back to file
	r, e := openDir(name)
	if e == nil {
		if flag&O_WRONLY != 0 || flag&O_RDWR != 0 {
			r.Close()
			return nil, &PathError{"open", name, EISDIR}
		}
		return r, nil
	}
	r, e = openFile(name, flag, perm)
	if e == nil {
		return r, nil
	}
	return nil, e
}

// Close closes the File, rendering it unusable for I/O.
// It returns an error, if any.
func (file *File) Close() error {
	return file.file.close()
}

func (file *file) close() error {
	if file == nil || file.fd < 0 {
		return EINVAL
	}
	var e error
	if file.isdir() {
		e = syscall.FindClose(syscall.Handle(file.fd))
	} else {
		e = syscall.CloseHandle(syscall.Handle(file.fd))
	}
	var err error
	if e != nil {
		err = &PathError{"close", file.name, e}
	}
	file.fd = syscall.InvalidHandle // so it can't be closed again

	// no need for a finalizer anymore
	runtime.SetFinalizer(file, nil)
	return err
}

// Readdir reads the contents of the directory associated with file and
// returns an array of up to n FileInfo structures, as would be returned
// by Lstat, in directory order. Subsequent calls on the same file will yield
// further FileInfos.
//
// If n > 0, Readdir returns at most n FileInfo structures. In this case, if
// Readdir returns an empty slice, it will return a non-nil error
// explaining why. At the end of a directory, the error is io.EOF.
//
// If n <= 0, Readdir returns all the FileInfo from the directory in
// a single slice. In this case, if Readdir succeeds (reads all
// the way to the end of the directory), it returns the slice and a
// nil error. If it encounters an error before the end of the
// directory, Readdir returns the FileInfo read until that point
// and a non-nil error.
func (file *File) Readdir(n int) (fi []FileInfo, err error) {
	if file == nil || file.fd < 0 {
		return nil, EINVAL
	}
	if !file.isdir() {
		return nil, &PathError{"Readdir", file.name, ENOTDIR}
	}
	wantAll := n <= 0
	size := n
	if wantAll {
		n = -1
		size = 100
	}
	fi = make([]FileInfo, 0, size) // Empty with room to grow.
	d := &file.dirinfo.data
	for n != 0 {
		if file.dirinfo.needdata {
			e := syscall.FindNextFile(syscall.Handle(file.fd), d)
			if e != nil {
				if e == syscall.ERROR_NO_MORE_FILES {
					break
				} else {
					err = &PathError{"FindNextFile", file.name, e}
					if !wantAll {
						fi = nil
					}
					return
				}
			}
		}
		var f FileInfo
		setFileInfo(&f, string(syscall.UTF16ToString(d.FileName[0:])), d.FileAttributes, d.FileSizeHigh, d.FileSizeLow, d.CreationTime, d.LastAccessTime, d.LastWriteTime)
		file.dirinfo.needdata = true
		if f.Name == "." || f.Name == ".." { // Useless names
			continue
		}
		n--
		fi = append(fi, f)
	}
	if !wantAll && len(fi) == 0 {
		return fi, io.EOF
	}
	return fi, nil
}

// read reads up to len(b) bytes from the File.
// It returns the number of bytes read and an error, if any.
func (f *File) read(b []byte) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Read(f.fd, b)
}

// pread reads len(b) bytes from the File starting at byte offset off.
// It returns the number of bytes read and the error, if any.
// EOF is signaled by a zero count with err set to 0.
func (f *File) pread(b []byte, off int64) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != nil {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.ReadFile(syscall.Handle(f.fd), b, &done, &o)
	if e != nil {
		return 0, e
	}
	return int(done), nil
}

// write writes len(b) bytes to the File.
// It returns the number of bytes written and an error, if any.
func (f *File) write(b []byte) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Write(f.fd, b)
}

// pwrite writes len(b) bytes to the File starting at byte offset off.
// It returns the number of bytes written and an error, if any.
func (f *File) pwrite(b []byte, off int64) (n int, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	curoffset, e := syscall.Seek(f.fd, 0, 1)
	if e != nil {
		return 0, e
	}
	defer syscall.Seek(f.fd, curoffset, 0)
	o := syscall.Overlapped{
		OffsetHigh: uint32(off >> 32),
		Offset:     uint32(off),
	}
	var done uint32
	e = syscall.WriteFile(syscall.Handle(f.fd), b, &done, &o)
	if e != nil {
		return 0, e
	}
	return int(done), nil
}

// seek sets the offset for the next Read or Write on file to offset, interpreted
// according to whence: 0 means relative to the origin of the file, 1 means
// relative to the current offset, and 2 means relative to the end.
// It returns the new offset and an error, if any.
func (f *File) seek(offset int64, whence int) (ret int64, err error) {
	f.l.Lock()
	defer f.l.Unlock()
	return syscall.Seek(f.fd, offset, whence)
}

// Truncate changes the size of the named file.
// If the file is a symbolic link, it changes the size of the link's target.
func Truncate(name string, size int64) error {
	f, e := OpenFile(name, O_WRONLY|O_CREATE, 0666)
	if e != nil {
		return e
	}
	defer f.Close()
	e1 := f.Truncate(size)
	if e1 != nil {
		return e1
	}
	return nil
}

// Pipe returns a connected pair of Files; reads from r return bytes written to w.
// It returns the files and an error, if any.
func Pipe() (r *File, w *File, err error) {
	var p [2]syscall.Handle

	// See ../syscall/exec.go for description of lock.
	syscall.ForkLock.RLock()
	e := syscall.Pipe(p[0:])
	if e != nil {
		syscall.ForkLock.RUnlock()
		return nil, nil, NewSyscallError("pipe", e)
	}
	syscall.CloseOnExec(p[0])
	syscall.CloseOnExec(p[1])
	syscall.ForkLock.RUnlock()

	return NewFile(p[0], "|0"), NewFile(p[1], "|1"), nil
}

// TempDir returns the default directory to use for temporary files.
func TempDir() string {
	const pathSep = '\\'
	dirw := make([]uint16, syscall.MAX_PATH)
	n, _ := syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
	if n > uint32(len(dirw)) {
		dirw = make([]uint16, n)
		n, _ = syscall.GetTempPath(uint32(len(dirw)), &dirw[0])
		if n > uint32(len(dirw)) {
			n = 0
		}
	}
	if n > 0 && dirw[n-1] == pathSep {
		n--
	}
	return string(utf16.Decode(dirw[0:n]))
}
<|endoftext|>


<commit_before>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package http_test

import (
	"fmt"
	"io"
	"log"
	nethttp "net/http"
	"os"

	"go.uber.org/yarpc"
	"go.uber.org/yarpc/internal/iopool"
	"go.uber.org/yarpc/transport/http"
)

func ExampleInbound() {
	transport := http.NewTransport()
	inbound := transport.NewInbound(":8888")

	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "myservice",
		Inbounds: yarpc.Inbounds{inbound},
	})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()
}

func ExampleMux() {
	// import nethttp "net/http"

	// We set up a ServeMux which provides a /health endpoint.
	mux := nethttp.NewServeMux()
	mux.HandleFunc("/health", func(w nethttp.ResponseWriter, _ *nethttp.Request) {
		if _, err := fmt.Fprintln(w, "hello from /health"); err != nil {
			panic(err)
		}
	})

	// This inbound will serve the YARPC service on the path /yarpc.  The
	// /health endpoint on the Mux will be left alone.
	transport := http.NewTransport()
	inbound := transport.NewInbound(":8888", http.Mux("/yarpc", mux))

	// Fire up a dispatcher with the new inbound.
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "server",
		Inbounds: yarpc.Inbounds{inbound},
	})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()

	// Make a request to the /health endpoint.
	res, err := nethttp.Get("http://127.0.0.1:8888/health")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if _, err := iopool.Copy(os.Stdout, res.Body); err != nil {
		log.Fatal(err)
	}
	// Output: hello from /health
}

func ExampleInterceptor() {
	// import nethttp "net/http"

	// We create an interceptor which executes an override http.Handler if
	// the HTTP request is not a well-formed YARPC request.
	override := func(overrideHandler nethttp.Handler) http.InboundOption {
		return http.Interceptor(func(transportHandler nethttp.Handler) nethttp.Handler {
			return nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) {
				if r.Header.Get("RPC-Encoding") == "" {
					overrideHandler.ServeHTTP(w, r)
				} else {
					transportHandler.ServeHTTP(w, r)
				}
			})
		})
	}

	// This handler will be executed for non-YARPC HTTP requests.
	handler := nethttp.HandlerFunc(func(w nethttp.ResponseWriter, req *nethttp.Request) {
		io.WriteString(w, "hello, world!")
	})

	// We create the inbound, attaching the override interceptor.
	transport := http.NewTransport()
	inbound := transport.NewInbound(":8889", override(handler))

	// Fire up a dispatcher with the new inbound.
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "server",
		Inbounds: yarpc.Inbounds{inbound},
	})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()

	// Make a non-YARPC request to the / endpoint.
	res, err := nethttp.Get("http://127.0.0.1:8889/")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if _, err := iopool.Copy(os.Stdout, res.Body); err != nil {
		log.Fatal(err)
	}
	// Output: hello, world!
}
<commit_msg>Simplify http.Interceptor example (#1337)<commit_after>// Copyright (c) 2017 Uber Technologies, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.

package http_test

import (
	"fmt"
	"io"
	"log"
	nethttp "net/http"
	"os"

	"go.uber.org/yarpc"
	"go.uber.org/yarpc/transport/http"
)

func ExampleInbound() {
	transport := http.NewTransport()
	inbound := transport.NewInbound(":8888")

	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "myservice",
		Inbounds: yarpc.Inbounds{inbound},
	})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()
}

func ExampleMux() {
	// import nethttp "net/http"

	// We set up a ServeMux which provides a /health endpoint.
	mux := nethttp.NewServeMux()
	mux.HandleFunc("/health", func(w nethttp.ResponseWriter, _ *nethttp.Request) {
		if _, err := fmt.Fprintln(w, "hello from /health"); err != nil {
			panic(err)
		}
	})

	// This inbound will serve the YARPC service on the path /yarpc.  The
	// /health endpoint on the Mux will be left alone.
	transport := http.NewTransport()
	inbound := transport.NewInbound(":8888", http.Mux("/yarpc", mux))

	// Fire up a dispatcher with the new inbound.
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "server",
		Inbounds: yarpc.Inbounds{inbound},
	})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()

	// Make a request to the /health endpoint.
	res, err := nethttp.Get("http://127.0.0.1:8888/health")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if _, err := io.Copy(os.Stdout, res.Body); err != nil {
		log.Fatal(err)
	}
	// Output: hello from /health
}

func ExampleInterceptor() {
	// import nethttp "net/http"

	// Given a fallback http.Handler
	fallback := nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) {
		io.WriteString(w, "hello, world!")
	})

	// Create an interceptor that falls back to a handler when the HTTP request is
	// missing the RPC-Encoding header.
	intercept := func(transportHandler nethttp.Handler) nethttp.Handler {
		return nethttp.HandlerFunc(func(w nethttp.ResponseWriter, r *nethttp.Request) {
			if r.Header.Get(http.EncodingHeader) == "" {
				// Not a YARPC request, use fallback handler.
				fallback.ServeHTTP(w, r)
			} else {
				transportHandler.ServeHTTP(w, r)
			}
		})
	}

	// Create a new inbound, attaching the interceptor
	transport := http.NewTransport()
	inbound := transport.NewInbound(":8889", http.Interceptor(intercept))

	// Fire up a dispatcher with the new inbound.
	dispatcher := yarpc.NewDispatcher(yarpc.Config{
		Name:     "server",
		Inbounds: yarpc.Inbounds{inbound},
	})
	if err := dispatcher.Start(); err != nil {
		log.Fatal(err)
	}
	defer dispatcher.Stop()

	// Make a non-YARPC request to the / endpoint.
	res, err := nethttp.Get("http://127.0.0.1:8889/")
	if err != nil {
		log.Fatal(err)
	}
	defer res.Body.Close()

	if _, err := io.Copy(os.Stdout, res.Body); err != nil {
		log.Fatal(err)
	}
	// Output: hello, world!
}
<|endoftext|>


<commit_before>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"fmt"
	"github.com/gogf/gf/container/gset"
	"github.com/gogf/gf/text/gstr"
)

// Filter marks filtering the fields which does not exist in the fields of the operated table.
// Note that this function supports only single table operations.
func (m *Model) Filter() *Model {
	if gstr.Contains(m.tables, " ") {
		panic("function Filter supports only single table operations")
	}
	model := m.getModel()
	model.filter = true
	return model
}

// Fields sets the operation fields of the model, multiple fields joined using char ','.
func (m *Model) Fields(fields string) *Model {
	model := m.getModel()
	model.fields = fields
	return model
}

// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
// Note that this function supports only single table operations.
func (m *Model) FieldsEx(fields string) *Model {
	if gstr.Contains(m.tables, " ") {
		panic("function FieldsEx supports only single table operations")
	}
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	model := m.getModel()
	model.fieldsEx = fields
	fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	model.fields = ""
	for _, k := range fieldsArray {
		if fieldsExSet.Contains(k) {
			continue
		}
		if len(model.fields) > 0 {
			model.fields += ","
		}
		model.fields += k
	}
	model.fields = model.db.QuoteString(model.fields)
	return model
}

// FieldsStr retrieves and returns all fields from the table, joined with char ','.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsStr("u.").
func (m *Model) FieldsStr(prefix ...string) string {
	prefixStr := ""
	if len(prefix) > 0 {
		prefixStr = prefix[0]
	}
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	newFields := ""
	for _, k := range fieldsArray {
		if len(newFields) > 0 {
			newFields += ","
		}
		newFields += prefixStr + k
	}
	newFields = m.db.QuoteString(newFields)
	return newFields
}

// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
// joined with char ','.
// The parameter <fields> specifies the fields that are excluded.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsExStr("id", "u.").
func (m *Model) FieldsExStr(fields string, prefix ...string) string {
	prefixStr := ""
	if len(prefix) > 0 {
		prefixStr = prefix[0]
	}
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	newFields := ""
	for _, k := range fieldsArray {
		if fieldsExSet.Contains(k) {
			continue
		}
		if len(newFields) > 0 {
			newFields += ","
		}
		newFields += prefixStr + k
	}
	newFields = m.db.QuoteString(newFields)
	return newFields
}
<commit_msg>add table field method<commit_after>// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.

package gdb

import (
	"fmt"
	"github.com/gogf/gf/container/gset"
	"github.com/gogf/gf/text/gstr"
)

// Filter marks filtering the fields which does not exist in the fields of the operated table.
// Note that this function supports only single table operations.
func (m *Model) Filter() *Model {
	if gstr.Contains(m.tables, " ") {
		panic("function Filter supports only single table operations")
	}
	model := m.getModel()
	model.filter = true
	return model
}

// Fields sets the operation fields of the model, multiple fields joined using char ','.
func (m *Model) Fields(fields string) *Model {
	model := m.getModel()
	model.fields = fields
	return model
}

// FieldsEx sets the excluded operation fields of the model, multiple fields joined using char ','.
// Note that this function supports only single table operations.
func (m *Model) FieldsEx(fields string) *Model {
	if gstr.Contains(m.tables, " ") {
		panic("function FieldsEx supports only single table operations")
	}
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	model := m.getModel()
	model.fieldsEx = fields
	fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	model.fields = ""
	for _, k := range fieldsArray {
		if fieldsExSet.Contains(k) {
			continue
		}
		if len(model.fields) > 0 {
			model.fields += ","
		}
		model.fields += k
	}
	model.fields = model.db.QuoteString(model.fields)
	return model
}

// FieldsStr retrieves and returns all fields from the table, joined with char ','.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsStr("u.").
func (m *Model) FieldsStr(prefix ...string) string {
	prefixStr := ""
	if len(prefix) > 0 {
		prefixStr = prefix[0]
	}
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	newFields := ""
	for _, k := range fieldsArray {
		if len(newFields) > 0 {
			newFields += ","
		}
		newFields += prefixStr + k
	}
	newFields = m.db.QuoteString(newFields)
	return newFields
}

// FieldsExStr retrieves and returns fields which are not in parameter <fields> from the table,
// joined with char ','.
// The parameter <fields> specifies the fields that are excluded.
// The optional parameter <prefix> specifies the prefix for each field, eg: FieldsExStr("id", "u.").
func (m *Model) FieldsExStr(fields string, prefix ...string) string {
	prefixStr := ""
	if len(prefix) > 0 {
		prefixStr = prefix[0]
	}
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	fieldsExSet := gset.NewStrSetFrom(gstr.SplitAndTrim(fields, ","))
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	newFields := ""
	for _, k := range fieldsArray {
		if fieldsExSet.Contains(k) {
			continue
		}
		if len(newFields) > 0 {
			newFields += ","
		}
		newFields += prefixStr + k
	}
	newFields = m.db.QuoteString(newFields)
	return newFields
}

// HasField determine whether the field exists in the table.
func (m *Model) HasField(field string) bool {
	tableFields, err := m.db.TableFields(m.tables)
	if err != nil {
		panic(err)
	}
	if len(tableFields) == 0 {
		panic(fmt.Sprintf(`empty table fields for table "%s"`, m.tables))
	}
	fieldsArray := make([]string, len(tableFields))
	for k, v := range tableFields {
		fieldsArray[v.Index] = k
	}
	for _, f := range fieldsArray {
		if f == field {
			return true
		}
	}
	return false
}<|endoftext|>


<commit_before>package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"testing"

	r "github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

var testProviders = map[string]terraform.ResourceProvider{
	"template": Provider(),
}

func TestTemplateRendering(t *testing.T) {
	var cases = []struct {
		vars     string
		template string
		want     string
	}{
		{`{}`, `ABC`, `ABC`},
		{`{a="foo"}`, `$${a}`, `foo`},
		{`{a="hello"}`, `$${replace(a, "ello", "i")}`, `hi`},
		{`{}`, `${1+2+3}`, `6`},
	}

	for _, tt := range cases {
		r.UnitTest(t, r.TestCase{
			Providers: testProviders,
			Steps: []r.TestStep{
				r.TestStep{
					Config: testTemplateConfig(tt.template, tt.vars),
					Check: func(s *terraform.State) error {
						got := s.RootModule().Outputs["rendered"]
						if tt.want != got.Value {
							return fmt.Errorf("template:\n%s\nvars:\n%s\ngot:\n%s\nwant:\n%s\n", tt.template, tt.vars, got, tt.want)
						}
						return nil
					},
				},
			},
		})
	}
}

func TestValidateTemplateAttribute(t *testing.T) {
	file, err := ioutil.TempFile("", "testtemplate")
	if err != nil {
		t.Fatal(err)
	}
	file.WriteString("Hello world.")
	file.Close()
	defer os.Remove(file.Name())

	ws, es := validateTemplateAttribute(file.Name(), "test")

	if len(es) != 0 {
		t.Fatalf("Unexpected errors: %#v", es)
	}

	if len(ws) != 1 {
		t.Fatalf("Expected 1 warning, got %d", len(ws))
	}

	if !strings.Contains(ws[0], "Specifying a path directly is deprecated") {
		t.Fatalf("Expected warning about path, got: %s", ws[0])
	}
}

func TestValidateVarsAttribute(t *testing.T) {
	cases := map[string]struct {
		Vars      map[string]interface{}
		ExpectErr string
	}{
		"lists are invalid": {
			map[string]interface{}{
				"list": []interface{}{},
			},
			`vars: cannot contain non-primitives`,
		},
		"maps are invalid": {
			map[string]interface{}{
				"map": map[string]interface{}{},
			},
			`vars: cannot contain non-primitives`,
		},
		"strings, integers, floats, and bools are AOK": {
			map[string]interface{}{
				"string": "foo",
				"int":    1,
				"bool":   true,
				"float":  float64(1.0),
			},
			``,
		},
	}

	for tn, tc := range cases {
		_, es := validateVarsAttribute(tc.Vars, "vars")
		if len(es) > 0 {
			if tc.ExpectErr == "" {
				t.Fatalf("%s: expected no err, got: %#v", tn, es)
			}
			if !strings.Contains(es[0].Error(), tc.ExpectErr) {
				t.Fatalf("%s: expected\n%s\nto contain\n%s", tn, es[0], tc.ExpectErr)
			}
		} else if tc.ExpectErr != "" {
			t.Fatalf("%s: expected err containing %q, got none!", tn, tc.ExpectErr)
		}
	}
}

// This test covers a panic due to config.Func formerly being a
// shared map, causing multiple template_file resources to try and
// accessing it parallel during their lang.Eval() runs.
//
// Before fix, test fails under `go test -race`
func TestTemplateSharedMemoryRace(t *testing.T) {
	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		go func(wg sync.WaitGroup, t *testing.T, i int) {
			wg.Add(1)
			out, err := execute("don't panic!", map[string]interface{}{})
			if err != nil {
				t.Fatalf("err: %s", err)
			}
			if out != "don't panic!" {
				t.Fatalf("bad output: %s", out)
			}
			wg.Done()
		}(wg, t, i)
	}
	wg.Wait()
}

func testTemplateConfig(template, vars string) string {
	return fmt.Sprintf(`
		data "template_file" "t0" {
			template = "%s"
			vars = %s
		}
		output "rendered" {
				value = "${data.template_file.t0.rendered}"
		}`, template, vars)
}
<commit_msg>fix make issues (supersedes #7868) (#7876)<commit_after>package template

import (
	"fmt"
	"io/ioutil"
	"os"
	"strings"
	"sync"
	"testing"

	r "github.com/hashicorp/terraform/helper/resource"
	"github.com/hashicorp/terraform/terraform"
)

var testProviders = map[string]terraform.ResourceProvider{
	"template": Provider(),
}

func TestTemplateRendering(t *testing.T) {
	var cases = []struct {
		vars     string
		template string
		want     string
	}{
		{`{}`, `ABC`, `ABC`},
		{`{a="foo"}`, `$${a}`, `foo`},
		{`{a="hello"}`, `$${replace(a, "ello", "i")}`, `hi`},
		{`{}`, `${1+2+3}`, `6`},
	}

	for _, tt := range cases {
		r.UnitTest(t, r.TestCase{
			Providers: testProviders,
			Steps: []r.TestStep{
				r.TestStep{
					Config: testTemplateConfig(tt.template, tt.vars),
					Check: func(s *terraform.State) error {
						got := s.RootModule().Outputs["rendered"]
						if tt.want != got.Value {
							return fmt.Errorf("template:\n%s\nvars:\n%s\ngot:\n%s\nwant:\n%s\n", tt.template, tt.vars, got, tt.want)
						}
						return nil
					},
				},
			},
		})
	}
}

func TestValidateTemplateAttribute(t *testing.T) {
	file, err := ioutil.TempFile("", "testtemplate")
	if err != nil {
		t.Fatal(err)
	}
	file.WriteString("Hello world.")
	file.Close()
	defer os.Remove(file.Name())

	ws, es := validateTemplateAttribute(file.Name(), "test")

	if len(es) != 0 {
		t.Fatalf("Unexpected errors: %#v", es)
	}

	if len(ws) != 1 {
		t.Fatalf("Expected 1 warning, got %d", len(ws))
	}

	if !strings.Contains(ws[0], "Specifying a path directly is deprecated") {
		t.Fatalf("Expected warning about path, got: %s", ws[0])
	}
}

func TestValidateVarsAttribute(t *testing.T) {
	cases := map[string]struct {
		Vars      map[string]interface{}
		ExpectErr string
	}{
		"lists are invalid": {
			map[string]interface{}{
				"list": []interface{}{},
			},
			`vars: cannot contain non-primitives`,
		},
		"maps are invalid": {
			map[string]interface{}{
				"map": map[string]interface{}{},
			},
			`vars: cannot contain non-primitives`,
		},
		"strings, integers, floats, and bools are AOK": {
			map[string]interface{}{
				"string": "foo",
				"int":    1,
				"bool":   true,
				"float":  float64(1.0),
			},
			``,
		},
	}

	for tn, tc := range cases {
		_, es := validateVarsAttribute(tc.Vars, "vars")
		if len(es) > 0 {
			if tc.ExpectErr == "" {
				t.Fatalf("%s: expected no err, got: %#v", tn, es)
			}
			if !strings.Contains(es[0].Error(), tc.ExpectErr) {
				t.Fatalf("%s: expected\n%s\nto contain\n%s", tn, es[0], tc.ExpectErr)
			}
		} else if tc.ExpectErr != "" {
			t.Fatalf("%s: expected err containing %q, got none!", tn, tc.ExpectErr)
		}
	}
}

// This test covers a panic due to config.Func formerly being a
// shared map, causing multiple template_file resources to try and
// accessing it parallel during their lang.Eval() runs.
//
// Before fix, test fails under `go test -race`
func TestTemplateSharedMemoryRace(t *testing.T) {
	var wg sync.WaitGroup
	for i := 0; i < 100; i++ {
		go func(wg *sync.WaitGroup, t *testing.T, i int) {
			wg.Add(1)
			out, err := execute("don't panic!", map[string]interface{}{})
			if err != nil {
				t.Fatalf("err: %s", err)
			}
			if out != "don't panic!" {
				t.Fatalf("bad output: %s", out)
			}
			wg.Done()
		}(&wg, t, i)
	}
	wg.Wait()
}

func testTemplateConfig(template, vars string) string {
	return fmt.Sprintf(`
		data "template_file" "t0" {
			template = "%s"
			vars = %s
		}
		output "rendered" {
				value = "${data.template_file.t0.rendered}"
		}`, template, vars)
}
<|endoftext|>