<commit_before>package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

const commandMaxLength = 1024

type Client struct {
	state       int
	helo        string
	mail_from   string
	rcpt_to     string
	response    string
	address     string
	data        string
	subject     string
	hash        string
	time        int64
	tls_on      bool
	conn        net.Conn
	bufin       *smtpBufferedReader
	bufout      *bufio.Writer
	kill_time   int64
	errors      int
	clientId    int64
	savedNotify chan int
}

type SmtpdServer struct {
	tlsConfig    *tls.Config
	max_size     int // max email DATA size
	timeout      time.Duration
	allowedHosts map[string]bool
	sem          chan int // currently active client list
	Config       ServerConfig
	logger       *log.Logger
}

func (server *SmtpdServer) logln(level int, s string) {

	if mainConfig.Verbose {
		fmt.Println(s)
	}
	// fatal errors
	if level == 2 {
		server.logger.Fatalf(s)
	}
	// warnings
	if level == 1 && len(server.Config.Log_file) > 0 {
		server.logger.Println(s)
	}

}

func (server *SmtpdServer) openLog() {

	server.logger = log.New(&bytes.Buffer{}, "", log.Lshortfile)
	// custom log file
	if len(server.Config.Log_file) > 0 {
		logfile, err := os.OpenFile(
			server.Config.Log_file,
			os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
		if err != nil {
			server.logln(1, fmt.Sprintf("Unable to open log file [%s]: %s ", server.Config.Log_file, err))
		}
		server.logger.SetOutput(logfile)
	}
}

// Upgrades the connection to TLS
// Sets up buffers with the upgraded connection
func (server *SmtpdServer) upgradeToTls(client *Client) bool {
	var tlsConn *tls.Conn
	tlsConn = tls.Server(client.conn, server.tlsConfig)
	err := tlsConn.Handshake()
	if err == nil {
		client.conn = net.Conn(tlsConn)
		client.bufin = newSmtpBufferedReader(client.conn)
		client.bufout = bufio.NewWriter(client.conn)
		client.tls_on = true

		return true
	} else {
		server.logln(1, fmt.Sprintf("Could not TLS handshake:%v", err))
		return false
	}

}

func (server *SmtpdServer) handleClient(client *Client) {
	defer server.closeClient(client)
	advertiseTls := "250-STARTTLS\r\n"
	if server.Config.Tls_always_on {
		if server.upgradeToTls(client) {
			advertiseTls = ""
		}
	}
	greeting := "220 " + server.Config.Host_name +
		" SMTP Guerrilla-SMTPd #" +
		strconv.FormatInt(client.clientId, 10) +
		" (" + strconv.Itoa(len(server.sem)) + ") " + time.Now().Format(time.RFC1123Z)

	if !server.Config.Start_tls_on {
		// STARTTLS turned off
		advertiseTls = ""
	}
	for i := 0; i < 100; i++ {
		switch client.state {
		case 0:
			responseAdd(client, greeting)
			client.state = 1
		case 1:
			client.bufin.setLimit(commandMaxLength)
			input, err := server.readSmtp(client)
			if err != nil {
				if err == io.EOF {
					// client closed the connection already
					server.logln(0, fmt.Sprintf("%s: %v", client.address, err))
					return
				} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
					// too slow, timeout
					server.logln(0, fmt.Sprintf("%s: %v", client.address, err))
					return
				} else if err == INPUT_LIMIT_EXCEEDED {
					responseAdd(client, "500 Line too long.")
					// kill it so that another one can connect
					killClient(client)
				}
				server.logln(1, fmt.Sprintf("Read error: %v", err))
				break
			}
			input = strings.Trim(input, " \n\r")
			bound := len(input)
			if bound > 16 {
				bound = 16
			}
			cmd := strings.ToUpper(input[0:bound])
			switch {
			case strings.Index(cmd, "HELO") == 0:
				if len(input) > 5 {
					client.helo = input[5:]
				}
				responseAdd(client, "250 "+server.Config.Host_name+" Hello ")
			case strings.Index(cmd, "EHLO") == 0:
				if len(input) > 5 {
					client.helo = input[5:]
				}
				responseAdd(client, "250-"+server.Config.Host_name+
					" Hello "+client.helo+"["+client.address+"]"+"\r\n"+
					"250-SIZE "+strconv.Itoa(server.Config.Max_size)+"\r\n"+
					"250-PIPELINING \r\n"+
					advertiseTls+"250 HELP")
			case strings.Index(cmd, "HELP") == 0:
				responseAdd(client, "250 Help! I need somebody...")
			case strings.Index(cmd, "MAIL FROM:") == 0:
				if len(input) > 10 {
					client.mail_from = input[10:]
				}
				responseAdd(client, "250 Ok")
			case strings.Index(cmd, "XCLIENT") == 0:
				// Nginx sends this
				// XCLIENT ADDR=212.96.64.216 NAME=[UNAVAILABLE]
				client.address = input[13:]
				client.address = client.address[0:strings.Index(client.address, " ")]
				fmt.Println("client address:[" + client.address + "]")
				responseAdd(client, "250 OK")
			case strings.Index(cmd, "RCPT TO:") == 0:
				if len(input) > 8 {
					client.rcpt_to = input[8:]
				}
				responseAdd(client, "250 Accepted")
			case strings.Index(cmd, "NOOP") == 0:
				responseAdd(client, "250 OK")
			case strings.Index(cmd, "RSET") == 0:
				client.mail_from = ""
				client.rcpt_to = ""
				responseAdd(client, "250 OK")
			case strings.Index(cmd, "DATA") == 0:
				responseAdd(client, "354 Enter message, ending with \".\" on a line by itself")
				client.state = 2
			case (strings.Index(cmd, "STARTTLS") == 0) &&
				!client.tls_on &&
				server.Config.Start_tls_on:
				responseAdd(client, "220 Ready to start TLS")
				// go to start TLS state
				client.state = 3
			case strings.Index(cmd, "QUIT") == 0:
				responseAdd(client, "221 Bye")
				killClient(client)
			default:
				responseAdd(client, "500 unrecognized command: "+cmd)
				client.errors++
				if client.errors > 3 {
					responseAdd(client, "500 Too many unrecognized commands")
					killClient(client)
				}
			}
		case 2:
			var err error
			client.bufin.setLimit(int64(server.Config.Max_size) + 1024000) // This is a hard limit.
			client.data, err = server.readSmtp(client)
			if err == nil {
				if _, _, mailErr := validateEmailData(client); mailErr == nil {
					// to do: timeout when adding to SaveMailChan
					// place on the channel so that one of the save mail workers can pick it up
					SaveMailChan <- &savePayload{client: client, server: server}
					// wait for the save to complete
					// or timeout
					select {
					case status := <-client.savedNotify:
						if status == 1 {
							responseAdd(client, "250 OK : queued as "+client.hash)
						} else {
							responseAdd(client, "554 Error: transaction failed, blame it on the weather")
						}
					case <-time.After(time.Second * 30):
						fmt.Println("timeout 1")
						responseAdd(client, "554 Error: transaction timeout")
					}

				} else {
					responseAdd(client, "550 Error: "+mailErr.Error())
				}

			} else {
				if (err == INPUT_LIMIT_EXCEEDED) {
					// hard limit reached, end to make room for other clients
					responseAdd(client, "550 Error: DATA limit exceeded by more than a megabyte!")
					killClient(client)
				} else {
					responseAdd(client, "550 Error: "+err.Error())
				}

				server.logln(1, fmt.Sprintf("DATA read error: %v", err))
			}
			client.state = 1
		case 3:
			// upgrade to TLS
			if server.upgradeToTls(client) {
				advertiseTls = ""
				client.state = 1
			}
		}
		// Send a response back to the client
		err := server.responseWrite(client)
		if err != nil {
			if err == io.EOF {
				// client closed the connection already
				return
			}
			if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
				// too slow, timeout
				return
			}
		}
		if client.kill_time > 1 {
			return
		}
	}

}

// add a response on the response buffer
func responseAdd(client *Client, line string) {
	client.response = line + "\r\n"
}
func (server *SmtpdServer) closeClient(client *Client) {
	client.conn.Close()
	<-server.sem // Done; enable next client to run.
}
func killClient(client *Client) {
	client.kill_time = time.Now().Unix()
}

var INPUT_LIMIT_EXCEEDED = errors.New("Line too long") // 500 Line too long.

// we need to adjust the limit, so we embed io.LimitedReader
type adjustableLimitedReader struct {
	R *io.LimitedReader
}

// bolt this on so we can adjust the limit
func (alr *adjustableLimitedReader) setLimit(n int64) {
	alr.R.N = n
}

// this just delegates to the underlying reader in order to satisfy the Reader interface
func (alr adjustableLimitedReader) Read(p []byte) (n int, err error) {
	n, err = alr.R.Read(p)
	return
}

func newAdjustableLimitedReader(r io.Reader, n int64) *adjustableLimitedReader {
	lr := &io.LimitedReader{R: r, N: n}
	return &adjustableLimitedReader{lr}
}

// We need buffio to use the limited reader, and have access to the limited reader
type smtpBufferedReader struct {
	br  *bufio.Reader
	alr *adjustableLimitedReader
}

// delegate to the bufio.Reader & detect if input limit exceeded
func (sbr *smtpBufferedReader) ReadString(delim byte) (line string, err error) {
	line, err = sbr.br.ReadString(delim)
	if err == io.EOF && sbr.alr.R.N <= 0 {
		// return our custom error since std lib returns EOF
		err = INPUT_LIMIT_EXCEEDED
	}
	return
}

// delegate to the adjustable limited reader
func (sbr *smtpBufferedReader) setLimit(n int64) {
	sbr.alr.setLimit(n)
}

// new limited buffered reader
func newSmtpBufferedReader(rd io.Reader) *smtpBufferedReader {
	alr := newAdjustableLimitedReader(rd, commandMaxLength)
	s := &smtpBufferedReader{bufio.NewReader(alr), alr}
	return s
}

// Reads from the smtpBufferedReader, can be in command state or data state.
func (server *SmtpdServer) readSmtp(client *Client) (input string, err error) {
	var reply string
	// Command state terminator by default
	suffix := "\r\n"
	if client.state == 2 {
		// DATA state ends with a dot on a line by itself
		suffix = "\r\n.\r\n"
	}
	for err == nil {
		client.conn.SetDeadline(time.Now().Add(server.timeout * time.Second))
		reply, err = client.bufin.ReadString('\n')
		if reply != "" {
			input = input + reply
			if len(input) > server.Config.Max_size {
				err = errors.New("Maximum DATA size exceeded (" + strconv.Itoa(server.Config.Max_size) + ")")
				return input, err
			}
			if client.state == 2 {
				// Extract the subject while we are at it.
				scanSubject(client, reply)
			}
		}
		if err != nil {
			break
		}
		if strings.HasSuffix(input, suffix) {
			break
		}
	}
	return input, err
}

// Scan the data part for a Subject line. Can be a multi-line
func scanSubject(client *Client, reply string) {
	if client.subject == "" && (len(reply) > 8) {
		test := strings.ToUpper(reply[0:9])
		if i := strings.Index(test, "SUBJECT: "); i == 0 {
			// first line with \r\n
			client.subject = reply[9:]
		}
	} else if strings.HasSuffix(client.subject, "\r\n") {
		// chop off the \r\n
		client.subject = client.subject[0 : len(client.subject)-2]
		if (strings.HasPrefix(reply, " ")) || (strings.HasPrefix(reply, "\t")) {
			// subject is multi-line
			client.subject = client.subject + reply[1:]
		}
	}
}

func (server *SmtpdServer) responseWrite(client *Client) (err error) {
	var size int
	client.conn.SetDeadline(time.Now().Add(server.timeout * time.Second))
	size, err = client.bufout.WriteString(client.response)
	client.bufout.Flush()
	client.response = client.response[size:]
	return err
}
<commit_msg>a big "aha!" moment with discovering why the type didn't borrow functions form bufio!<commit_after>package main

import (
	"bufio"
	"bytes"
	"crypto/tls"
	"errors"
	"fmt"
	"io"
	"log"
	"net"
	"os"
	"strconv"
	"strings"
	"time"
)

const commandMaxLength = 1024

type Client struct {
	state       int
	helo        string
	mail_from   string
	rcpt_to     string
	response    string
	address     string
	data        string
	subject     string
	hash        string
	time        int64
	tls_on      bool
	conn        net.Conn
	bufin       *smtpBufferedReader
	bufout      *bufio.Writer
	kill_time   int64
	errors      int
	clientId    int64
	savedNotify chan int
}

type SmtpdServer struct {
	tlsConfig    *tls.Config
	max_size     int // max email DATA size
	timeout      time.Duration
	allowedHosts map[string]bool
	sem          chan int // currently active client list
	Config       ServerConfig
	logger       *log.Logger
}

func (server *SmtpdServer) logln(level int, s string) {

	if mainConfig.Verbose {
		fmt.Println(s)
	}
	// fatal errors
	if level == 2 {
		server.logger.Fatalf(s)
	}
	// warnings
	if level == 1 && len(server.Config.Log_file) > 0 {
		server.logger.Println(s)
	}

}

func (server *SmtpdServer) openLog() {

	server.logger = log.New(&bytes.Buffer{}, "", log.Lshortfile)
	// custom log file
	if len(server.Config.Log_file) > 0 {
		logfile, err := os.OpenFile(
			server.Config.Log_file,
			os.O_WRONLY|os.O_APPEND|os.O_CREATE|os.O_SYNC, 0600)
		if err != nil {
			server.logln(1, fmt.Sprintf("Unable to open log file [%s]: %s ", server.Config.Log_file, err))
		}
		server.logger.SetOutput(logfile)
	}
}

// Upgrades the connection to TLS
// Sets up buffers with the upgraded connection
func (server *SmtpdServer) upgradeToTls(client *Client) bool {
	var tlsConn *tls.Conn
	tlsConn = tls.Server(client.conn, server.tlsConfig)
	err := tlsConn.Handshake()
	if err == nil {
		client.conn = net.Conn(tlsConn)
		client.bufin = newSmtpBufferedReader(client.conn)
		client.bufout = bufio.NewWriter(client.conn)
		client.tls_on = true

		return true
	} else {
		server.logln(1, fmt.Sprintf("Could not TLS handshake:%v", err))
		return false
	}

}

func (server *SmtpdServer) handleClient(client *Client) {
	defer server.closeClient(client)
	advertiseTls := "250-STARTTLS\r\n"
	if server.Config.Tls_always_on {
		if server.upgradeToTls(client) {
			advertiseTls = ""
		}
	}
	greeting := "220 " + server.Config.Host_name +
		" SMTP Guerrilla-SMTPd #" +
		strconv.FormatInt(client.clientId, 10) +
		" (" + strconv.Itoa(len(server.sem)) + ") " + time.Now().Format(time.RFC1123Z)

	if !server.Config.Start_tls_on {
		// STARTTLS turned off
		advertiseTls = ""
	}
	for i := 0; i < 100; i++ {
		switch client.state {
		case 0:
			responseAdd(client, greeting)
			client.state = 1
		case 1:
			client.bufin.setLimit(commandMaxLength)
			input, err := server.readSmtp(client)
			if err != nil {
				if err == io.EOF {
					// client closed the connection already
					server.logln(0, fmt.Sprintf("%s: %v", client.address, err))
					return
				} else if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
					// too slow, timeout
					server.logln(0, fmt.Sprintf("%s: %v", client.address, err))
					return
				} else if err == INPUT_LIMIT_EXCEEDED {
					responseAdd(client, "500 Line too long.")
					// kill it so that another one can connect
					killClient(client)
				}
				server.logln(1, fmt.Sprintf("Read error: %v", err))
				break
			}
			input = strings.Trim(input, " \n\r")
			bound := len(input)
			if bound > 16 {
				bound = 16
			}
			cmd := strings.ToUpper(input[0:bound])
			switch {
			case strings.Index(cmd, "HELO") == 0:
				if len(input) > 5 {
					client.helo = input[5:]
				}
				responseAdd(client, "250 "+server.Config.Host_name+" Hello ")
			case strings.Index(cmd, "EHLO") == 0:
				if len(input) > 5 {
					client.helo = input[5:]
				}
				responseAdd(client, "250-"+server.Config.Host_name+
					" Hello "+client.helo+"["+client.address+"]"+"\r\n"+
					"250-SIZE "+strconv.Itoa(server.Config.Max_size)+"\r\n"+
					"250-PIPELINING \r\n"+
					advertiseTls+"250 HELP")
			case strings.Index(cmd, "HELP") == 0:
				responseAdd(client, "250 Help! I need somebody...")
			case strings.Index(cmd, "MAIL FROM:") == 0:
				if len(input) > 10 {
					client.mail_from = input[10:]
				}
				responseAdd(client, "250 Ok")
			case strings.Index(cmd, "XCLIENT") == 0:
				// Nginx sends this
				// XCLIENT ADDR=212.96.64.216 NAME=[UNAVAILABLE]
				client.address = input[13:]
				client.address = client.address[0:strings.Index(client.address, " ")]
				fmt.Println("client address:[" + client.address + "]")
				responseAdd(client, "250 OK")
			case strings.Index(cmd, "RCPT TO:") == 0:
				if len(input) > 8 {
					client.rcpt_to = input[8:]
				}
				responseAdd(client, "250 Accepted")
			case strings.Index(cmd, "NOOP") == 0:
				responseAdd(client, "250 OK")
			case strings.Index(cmd, "RSET") == 0:
				client.mail_from = ""
				client.rcpt_to = ""
				responseAdd(client, "250 OK")
			case strings.Index(cmd, "DATA") == 0:
				responseAdd(client, "354 Enter message, ending with \".\" on a line by itself")
				client.state = 2
			case (strings.Index(cmd, "STARTTLS") == 0) &&
				!client.tls_on &&
				server.Config.Start_tls_on:
				responseAdd(client, "220 Ready to start TLS")
				// go to start TLS state
				client.state = 3
			case strings.Index(cmd, "QUIT") == 0:
				responseAdd(client, "221 Bye")
				killClient(client)
			default:
				responseAdd(client, "500 unrecognized command: "+cmd)
				client.errors++
				if client.errors > 3 {
					responseAdd(client, "500 Too many unrecognized commands")
					killClient(client)
				}
			}
		case 2:
			var err error
			client.bufin.setLimit(int64(server.Config.Max_size) + 1024000) // This is a hard limit.
			client.data, err = server.readSmtp(client)
			if err == nil {
				if _, _, mailErr := validateEmailData(client); mailErr == nil {
					// to do: timeout when adding to SaveMailChan
					// place on the channel so that one of the save mail workers can pick it up
					SaveMailChan <- &savePayload{client: client, server: server}
					// wait for the save to complete
					// or timeout
					select {
					case status := <-client.savedNotify:
						if status == 1 {
							responseAdd(client, "250 OK : queued as "+client.hash)
						} else {
							responseAdd(client, "554 Error: transaction failed, blame it on the weather")
						}
					case <-time.After(time.Second * 30):
						fmt.Println("timeout 1")
						responseAdd(client, "554 Error: transaction timeout")
					}

				} else {
					responseAdd(client, "550 Error: "+mailErr.Error())
				}

			} else {
				if (err == INPUT_LIMIT_EXCEEDED) {
					// hard limit reached, end to make room for other clients
					responseAdd(client, "550 Error: DATA limit exceeded by more than a megabyte!")
					killClient(client)
				} else {
					responseAdd(client, "550 Error: "+err.Error())
				}

				server.logln(1, fmt.Sprintf("DATA read error: %v", err))
			}
			client.state = 1
		case 3:
			// upgrade to TLS
			if server.upgradeToTls(client) {
				advertiseTls = ""
				client.state = 1
			}
		}
		// Send a response back to the client
		err := server.responseWrite(client)
		if err != nil {
			if err == io.EOF {
				// client closed the connection already
				return
			}
			if neterr, ok := err.(net.Error); ok && neterr.Timeout() {
				// too slow, timeout
				return
			}
		}
		if client.kill_time > 1 {
			return
		}
	}

}

// add a response on the response buffer
func responseAdd(client *Client, line string) {
	client.response = line + "\r\n"
}
func (server *SmtpdServer) closeClient(client *Client) {
	client.conn.Close()
	<-server.sem // Done; enable next client to run.
}
func killClient(client *Client) {
	client.kill_time = time.Now().Unix()
}

var INPUT_LIMIT_EXCEEDED = errors.New("Line too long") // 500 Line too long.

// we need to adjust the limit, so we embed io.LimitedReader
type adjustableLimitedReader struct {
	R *io.LimitedReader
}

// bolt this on so we can adjust the limit
func (alr *adjustableLimitedReader) setLimit(n int64) {
	alr.R.N = n
}

// this just delegates to the underlying reader in order to satisfy the Reader interface
func (alr adjustableLimitedReader) Read(p []byte) (n int, err error) {
	n, err = alr.R.Read(p)
	return
}

func newAdjustableLimitedReader(r io.Reader, n int64) *adjustableLimitedReader {
	lr := &io.LimitedReader{R: r, N: n}
	return &adjustableLimitedReader{lr}
}

// We need buffio to use the limited reader, and have access to the limited reader
type smtpBufferedReader struct {
	*bufio.Reader
	alr *adjustableLimitedReader
}

// delegate to the bufio.Reader & detect if input limit exceeded
func (sbr *smtpBufferedReader) ReadLimitedString(delim byte) (line string, err error) {
	line, err = sbr.ReadString(delim)
	if err == io.EOF && sbr.alr.R.N <= 0 {
		// return our custom error since std lib returns EOF
		err = INPUT_LIMIT_EXCEEDED
	}
	return
}

// delegate to the adjustable limited reader
func (sbr *smtpBufferedReader) setLimit(n int64) {
	sbr.alr.setLimit(n)
}

// new limited buffered reader
func newSmtpBufferedReader(rd io.Reader) *smtpBufferedReader {
	alr := newAdjustableLimitedReader(rd, commandMaxLength)
	s := &smtpBufferedReader{bufio.NewReader(alr), alr}
	return s
}

// Reads from the smtpBufferedReader, can be in command state or data state.
func (server *SmtpdServer) readSmtp(client *Client) (input string, err error) {
	var reply string
	// Command state terminator by default
	suffix := "\r\n"
	if client.state == 2 {
		// DATA state ends with a dot on a line by itself
		suffix = "\r\n.\r\n"
	}
	for err == nil {
		client.conn.SetDeadline(time.Now().Add(server.timeout * time.Second))
		reply, err = client.bufin.ReadLimitedString('\n')
		if reply != "" {
			input = input + reply
			if len(input) > server.Config.Max_size {
				err = errors.New("Maximum DATA size exceeded (" + strconv.Itoa(server.Config.Max_size) + ")")
				return input, err
			}
			if client.state == 2 {
				// Extract the subject while we are at it.
				scanSubject(client, reply)
			}
		}
		if err != nil {
			break
		}
		if strings.HasSuffix(input, suffix) {
			break
		}
	}
	return input, err
}

// Scan the data part for a Subject line. Can be a multi-line
func scanSubject(client *Client, reply string) {
	if client.subject == "" && (len(reply) > 8) {
		test := strings.ToUpper(reply[0:9])
		if i := strings.Index(test, "SUBJECT: "); i == 0 {
			// first line with \r\n
			client.subject = reply[9:]
		}
	} else if strings.HasSuffix(client.subject, "\r\n") {
		// chop off the \r\n
		client.subject = client.subject[0 : len(client.subject)-2]
		if (strings.HasPrefix(reply, " ")) || (strings.HasPrefix(reply, "\t")) {
			// subject is multi-line
			client.subject = client.subject + reply[1:]
		}
	}
}

func (server *SmtpdServer) responseWrite(client *Client) (err error) {
	var size int
	client.conn.SetDeadline(time.Now().Add(server.timeout * time.Second))
	size, err = client.bufout.WriteString(client.response)
	client.bufout.Flush()
	client.response = client.response[size:]
	return err
}
<|endoftext|>


<commit_before>package integration_test

import (
	. "github.com/bobtfish/AWSnycast/tests/integration"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var internalIPs []string

func TestIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	RegisterFailHandler(Fail)

	RunMake()
	RunTerraform()
	internalIPs = InternalIPs()
	RunSpecs(t, "Integration Suite")
}
<commit_msg>Don't run terraform in the tests<commit_after>package integration_test

import (
	. "github.com/bobtfish/AWSnycast/tests/integration"
	. "github.com/onsi/ginkgo"
	. "github.com/onsi/gomega"

	"testing"
)

var internalIPs []string

func TestIntegration(t *testing.T) {
	if testing.Short() {
		t.Skip("skipping test in short mode.")
	}
	RegisterFailHandler(Fail)

	//RunMake()
	//RunTerraform()
	internalIPs = InternalIPs()
	RunSpecs(t, "Integration Suite")
}
<|endoftext|>


<commit_before>package permissions

// Middleware handler for handling requests and permissions

import (
	"fmt"
	"net/http"
	"strings"
)

type Permissions struct {
	state              *UserState
	adminPathPrefixes  []string
	userPathPrefixes   []string
	publicPathPrefixes []string
	rootIsPublic       bool
	denied             http.HandlerFunc
}

const (
	// Version number. Stable API within major version numbers.
	Version = 1.0
)

// Initialize a Permissions struct with all the default settings.
// This will also connect to the redis host at localhost:6379.
func New() *Permissions {
	return NewPermissions(NewUserStateSimple())
}

// Initialize a Permissions struct with Redis DB index and host:port
func NewWithRedisConf(dbindex int, hostPort string) *Permissions {
	return NewPermissions(NewUserState(dbindex, true, hostPort))
}

// Initialize a Permissions struct with the given UserState and
// a few default paths for admin/user/public path prefixes.
func NewPermissions(state *UserState) *Permissions {
	// default permissions
	return &Permissions{state,
		[]string{"/admin"},         // admin path prefixes
		[]string{"/repo", "/data"}, // user path prefixes
		[]string{"/", "/login", "/register", "/favicon.ico", "/style", "/img", "/js",
			"/favicon.ico", "/robots.txt", "/sitemap_index.xml"}, // public
		true,
		PermissionDenied}
}

// Specify the http.HandlerFunc for when the permissions are denied
func (perm *Permissions) SetDenyFunction(f http.HandlerFunc) {
	perm.denied = f
}

// Retrieve the UserState struct
func (perm *Permissions) UserState() *UserState {
	return perm.state
}

// Add an url path prefix that is a page for the logged in administrators
func (perm *Permissions) AddAdminPath(prefix string) {
	perm.adminPathPrefixes = append(perm.adminPathPrefixes, prefix)
}

// Add an url path prefix that is a page for the logged in users
func (perm *Permissions) AddUserPath(prefix string) {
	perm.userPathPrefixes = append(perm.userPathPrefixes, prefix)
}

// Add an url path prefix that is a public page
func (perm *Permissions) AddPublicPath(prefix string) {
	perm.publicPathPrefixes = append(perm.publicPathPrefixes, prefix)
}

// Set all url path prefixes that are for the logged in administrator pages
func (perm *Permissions) SetAdminPath(pathPrefixes []string) {
	perm.adminPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the logged in user pages
func (perm *Permissions) SetUserPath(pathPrefixes []string) {
	perm.userPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the public pages
func (perm *Permissions) SetPublicPath(pathPrefixes []string) {
	perm.publicPathPrefixes = pathPrefixes
}

// The default "permission denied" http handler
func PermissionDenied(rw http.ResponseWriter, req *http.Request) {
	fmt.Fprintf(rw, "Permission denied.")
}

// Check if a given request should be rejected.
func (perm *Permissions) Rejected(rw http.ResponseWriter, req *http.Request) bool {
	reject := false
	path := req.URL.Path // the path of the url that the user wish to visit

	// If it's not "/" and set to be public regardless of permissions
	if !(perm.rootIsPublic && path == "/") {

		// Reject if it is an admin page and user does not have admin permissions
		for _, prefix := range perm.adminPathPrefixes {
			if strings.HasPrefix(path, prefix) {
				if !perm.state.AdminRights(req) {
					reject = true
					break
				}
			}
		}

		if !reject {
			// Reject if it's a user page and the user does not have user rights
			for _, prefix := range perm.userPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					if !perm.state.UserRights(req) {
						reject = true
						break
					}
				}
			}
		}

		if !reject {
			// Reject if it's not a public page
			found := false
			for _, prefix := range perm.publicPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					found = true
					break
				}
			}
			if !found {
				reject = true
			}
		}

	}

	return reject
}

// Negroni middleware handler
func (perm *Permissions) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	// Check if the user has the right admin/user rights
	if perm.Rejected(rw, req) {
		// Permission denied function
		perm.denied(rw, req)
		// Reject the request by not calling the next handler below
		return
	}

	// Call the next middleware handler
	next(rw, req)
}
<commit_msg>Introduced GetDenyFunction()<commit_after>package permissions

// Middleware handler for handling requests and permissions

import (
	"net/http"
	"strings"
)

type Permissions struct {
	state              *UserState
	adminPathPrefixes  []string
	userPathPrefixes   []string
	publicPathPrefixes []string
	rootIsPublic       bool
	denied             http.HandlerFunc
}

const (
	// Version number. Stable API within major version numbers.
	Version = 1.0
)

// Initialize a Permissions struct with all the default settings.
// This will also connect to the redis host at localhost:6379.
func New() *Permissions {
	return NewPermissions(NewUserStateSimple())
}

// Initialize a Permissions struct with Redis DB index and host:port
func NewWithRedisConf(dbindex int, hostPort string) *Permissions {
	return NewPermissions(NewUserState(dbindex, true, hostPort))
}

// Initialize a Permissions struct with the given UserState and
// a few default paths for admin/user/public path prefixes.
func NewPermissions(state *UserState) *Permissions {
	// default permissions
	return &Permissions{state,
		[]string{"/admin"},         // admin path prefixes
		[]string{"/repo", "/data"}, // user path prefixes
		[]string{"/", "/login", "/register", "/favicon.ico", "/style", "/img", "/js",
			"/favicon.ico", "/robots.txt", "/sitemap_index.xml"}, // public
		true,
		PermissionDenied}
}

// Specify the http.HandlerFunc for when the permissions are denied
func (perm *Permissions) SetDenyFunction(f http.HandlerFunc) {
	perm.denied = f
}

// Get the current http.HandlerFunc for when permissions are denied
func (perm *Permissions) GetDenyFunction() http.HandlerFunc {
	return perm.denied
}

// Retrieve the UserState struct
func (perm *Permissions) UserState() *UserState {
	return perm.state
}

// Add an url path prefix that is a page for the logged in administrators
func (perm *Permissions) AddAdminPath(prefix string) {
	perm.adminPathPrefixes = append(perm.adminPathPrefixes, prefix)
}

// Add an url path prefix that is a page for the logged in users
func (perm *Permissions) AddUserPath(prefix string) {
	perm.userPathPrefixes = append(perm.userPathPrefixes, prefix)
}

// Add an url path prefix that is a public page
func (perm *Permissions) AddPublicPath(prefix string) {
	perm.publicPathPrefixes = append(perm.publicPathPrefixes, prefix)
}

// Set all url path prefixes that are for the logged in administrator pages
func (perm *Permissions) SetAdminPath(pathPrefixes []string) {
	perm.adminPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the logged in user pages
func (perm *Permissions) SetUserPath(pathPrefixes []string) {
	perm.userPathPrefixes = pathPrefixes
}

// Set all url path prefixes that are for the public pages
func (perm *Permissions) SetPublicPath(pathPrefixes []string) {
	perm.publicPathPrefixes = pathPrefixes
}

// The default "permission denied" http handler.
func PermissionDenied(w http.ResponseWriter, req *http.Request) {
	http.Error(w, "Permission denied.", http.StatusForbidden)
}

// Check if a given request should be rejected.
func (perm *Permissions) Rejected(w http.ResponseWriter, req *http.Request) bool {
	reject := false
	path := req.URL.Path // the path of the url that the user wish to visit

	// If it's not "/" and set to be public regardless of permissions
	if !(perm.rootIsPublic && path == "/") {

		// Reject if it is an admin page and user does not have admin permissions
		for _, prefix := range perm.adminPathPrefixes {
			if strings.HasPrefix(path, prefix) {
				if !perm.state.AdminRights(req) {
					reject = true
					break
				}
			}
		}

		if !reject {
			// Reject if it's a user page and the user does not have user rights
			for _, prefix := range perm.userPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					if !perm.state.UserRights(req) {
						reject = true
						break
					}
				}
			}
		}

		if !reject {
			// Reject if it's not a public page
			found := false
			for _, prefix := range perm.publicPathPrefixes {
				if strings.HasPrefix(path, prefix) {
					found = true
					break
				}
			}
			if !found {
				reject = true
			}
		}

	}

	return reject
}

// Negroni middleware handler
func (perm *Permissions) ServeHTTP(w http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
	// Check if the user has the right admin/user rights
	if perm.Rejected(w, req) {
		// Get and call the Permission Denied function
		perm.GetDenyFunction()(w, req)
		// Reject the request by not calling the next handler below
		return
	}

	// Call the next middleware handler
	next(w, req)
}
<|endoftext|>


<commit_before><commit_msg>ConfigManager (transitional) - remove clientstring handling from ethutil.Config - ReadConfig takes no Identifier argument - members Ver, ClientString, ClientIdentifier removed from Config - type ConfValue removed - expose public type ethutil.ConfigManager - Set -> Save(key string, value interface{}) now takes any value to allow for persisting non-string values directly - TODO: eliminate all eth specific configs, just a wrapper around globalconf<commit_after><|endoftext|>


<commit_before>package main

import "testing"

func TestParseInput(t *testing.T) {
	tests := []struct {
		in  string
		out character
	}{
		{
			"Hit Points: 1\nDamage: 2\nArmor: 3\n",
			character{1, 2, 3},
		},
	}

	for _, tt := range tests {
		c, err := parseInput(tt.in)
		if err != nil {
			t.Errorf("parseInput(%q) = error %s, want %v", tt.in, err, tt.out)
		} else if c != tt.out {
			t.Errorf("parseInput(%q) = %v, want %v", tt.in, c, tt.out)
		}
	}
}

func TestFight(t *testing.T) {
	tests := []struct {
		inP, inB character
		out      bool
	}{
		{
			character{8, 5, 5}, character{12, 7, 2},
			true,
		},
	}

	for _, tt := range tests {
		w := fight(tt.inP, tt.inB)
		if w != tt.out {
			t.Errorf("fight(%v, %v) = %v, want %v", tt.inP, tt.inB, w, tt.out)
		}
	}
}

func TestProcess(t *testing.T) {
	tests := []struct {
		in        string
		inH       int
		out, out2 int
	}{
		{
			"Hit Points: 12\nDamage: 7\nArmor: 2\n", 10,
			65, -1,
		},
		{
			"Hit Points: 12\nDamage: 7\nArmor: 2\n", 100,
			8, -1,
		},
		{
			"Hit Points: 12\nDamage: 7\nArmor: 2\n", 5,
			116, 255,
		},
	}

	for _, tt := range tests {
		c, c2, err := process(tt.in, tt.inH)
		if err != nil {
			t.Errorf("process(%q, %d) = error %s, want %d, %d", tt.in, tt.inH, err, tt.out, tt.out2)
		} else if c != tt.out {
			t.Errorf("process(%q, %d) = %d, %d, want %d, %d", tt.in, tt.inH, c, c2, tt.out, tt.out2)
		}
	}
}
<commit_msg>day 21 2015 fix tests<commit_after>package main

import "testing"

func TestParseInput(t *testing.T) {
	tests := []struct {
		in  string
		out character
	}{
		{
			"Hit Points: 1\nDamage: 2\nArmor: 3\n",
			character{1, 2, 3},
		},
	}

	for _, tt := range tests {
		c, err := parseInput(tt.in)
		if err != nil {
			t.Errorf("parseInput(%q) = error %s, want %v", tt.in, err, tt.out)
		} else if c != tt.out {
			t.Errorf("parseInput(%q) = %v, want %v", tt.in, c, tt.out)
		}
	}
}

func TestFight(t *testing.T) {
	tests := []struct {
		inP, inB character
		out      bool
	}{
		{
			character{8, 5, 5}, character{12, 7, 2},
			true,
		},
	}

	for _, tt := range tests {
		w := fight(tt.inP, tt.inB)
		if w != tt.out {
			t.Errorf("fight(%v, %v) = %v, want %v", tt.inP, tt.inB, w, tt.out)
		}
	}
}

func TestProcess(t *testing.T) {
	tests := []struct {
		in        string
		inH       int
		out, out2 int
	}{
		{
			"Hit Points: 12\nDamage: 7\nArmor: 2\n", 10,
			65, 148,
		},
		{
			"Hit Points: 12\nDamage: 7\nArmor: 2\n", 100,
			8, -1,
		},
		{
			"Hit Points: 12\nDamage: 7\nArmor: 2\n", 5,
			116, 255,
		},
	}

	for _, tt := range tests {
		c, c2, err := process(tt.in, tt.inH)
		if err != nil {
			t.Errorf("process(%q, %d) = error %s, want %d, %d", tt.in, tt.inH, err, tt.out, tt.out2)
		} else if c != tt.out || c2 != tt.out2 {
			t.Errorf("process(%q, %d) = %d, %d, want %d, %d", tt.in, tt.inH, c, c2, tt.out, tt.out2)
		}
	}
}
<|endoftext|>


<commit_before>// Package pgxpool is a connection pool for pgx.
/*
pgxpool implements a nearly identical interface to pgx connections.

Establishing a Connection

The primary way of establishing a connection is with `pgxpool.Connect`.

    pool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))

The database connection string can be in URL or DSN format. PostgreSQL settings, pgx settings, and pool settings can be
specified here. In addition, a config struct can be created by `ParseConfig` and modified before establishing the
connection with `ConnectConfig`.

    config, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
    if err != nil {
        // ...
    }
    config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
        // do something with every new connection
    }

    pool, err := pgxpool.ConnectConfig(context.Background(), config)
*/
package pgxpool
<commit_msg>Explicityly state pgxpool's concurrency-safety<commit_after>// Package pgxpool is a concurrency-safe connection pool for pgx.
/*
pgxpool implements a nearly identical interface to pgx connections.

Establishing a Connection

The primary way of establishing a connection is with `pgxpool.Connect`.

    pool, err := pgxpool.Connect(context.Background(), os.Getenv("DATABASE_URL"))

The database connection string can be in URL or DSN format. PostgreSQL settings, pgx settings, and pool settings can be
specified here. In addition, a config struct can be created by `ParseConfig` and modified before establishing the
connection with `ConnectConfig`.

    config, err := pgxpool.ParseConfig(os.Getenv("DATABASE_URL"))
    if err != nil {
        // ...
    }
    config.AfterConnect = func(ctx context.Context, conn *pgx.Conn) error {
        // do something with every new connection
    }

    pool, err := pgxpool.ConnectConfig(context.Background(), config)
*/
package pgxpool
<|endoftext|>


<commit_before><commit_msg>Very simple example<commit_after><|endoftext|>


<commit_before><commit_msg>fix tests<commit_after><|endoftext|>


<commit_before>// Copyright 2013 Prometheus Team
// 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 metric

import (
	"time"

	clientmodel "github.com/prometheus/client_golang/model"

	"github.com/prometheus/prometheus/storage"
)

// AppendBatch models a batch of samples to be stored.
type AppendBatch map[clientmodel.Fingerprint]SampleSet

// MetricPersistence is a system for storing metric samples in a persistence
// layer.
type MetricPersistence interface {
	// A storage system may rely on external resources and thusly should be
	// closed when finished.
	Close()

	// Commit all pending operations, if any, since some of the storage components
	// queue work on channels and operate on it in bulk.
	// Flush() error

	// Record a group of new samples in the storage layer.
	AppendSamples(clientmodel.Samples) error

	// Get all of the metric fingerprints that are associated with the provided
	// label set.
	GetFingerprintsForLabelSet(clientmodel.LabelSet) (clientmodel.Fingerprints, error)

	// Get all of the metric fingerprints that are associated for a given label
	// name.
	GetFingerprintsForLabelName(clientmodel.LabelName) (clientmodel.Fingerprints, error)

	// Get the metric associated with the provided fingerprint.
	GetMetricForFingerprint(*clientmodel.Fingerprint) (clientmodel.Metric, error)

	// Get the two metric values that are immediately adjacent to a given time.
	GetValueAtTime(*clientmodel.Fingerprint, time.Time) Values
	// Get the boundary values of an interval: the first value older than the
	// interval start, and the first value younger than the interval end.
	GetBoundaryValues(*clientmodel.Fingerprint, Interval) Values
	// Get all values contained within a provided interval.
	GetRangeValues(*clientmodel.Fingerprint, Interval) Values
	// Get all label values that are associated with a given label name.
	GetAllValuesForLabel(clientmodel.LabelName) (clientmodel.LabelValues, error)

	// Requests the storage stack to build a materialized View of the values
	// contained therein.
	// MakeView(builder ViewRequestBuilder, deadline time.Duration) (View, error)
}

// View provides a view of the values in the datastore subject to the request
// of a preloading operation.
type View interface {
	GetValueAtTime(*clientmodel.Fingerprint, time.Time) Values
	GetBoundaryValues(*clientmodel.Fingerprint, Interval) Values
	GetRangeValues(*clientmodel.Fingerprint, Interval) Values

	// Destroy this view.
	Close()
}

type Series interface {
	Fingerprint() *clientmodel.Fingerprint
	Metric() clientmodel.Metric
}

type IteratorsForFingerprintBuilder interface {
	ForStream(stream *stream) (storage.RecordDecoder, storage.RecordFilter, storage.RecordOperator)
}
<commit_msg>Kill interface cruft.<commit_after>// Copyright 2013 Prometheus Team
// 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 metric

import (
	"time"

	clientmodel "github.com/prometheus/client_golang/model"

	"github.com/prometheus/prometheus/storage"
)

// AppendBatch models a batch of samples to be stored.
type AppendBatch map[clientmodel.Fingerprint]SampleSet

// MetricPersistence is a system for storing metric samples in a persistence
// layer.
type MetricPersistence interface {
	// A storage system may rely on external resources and thusly should be
	// closed when finished.
	Close()

	// Record a group of new samples in the storage layer.
	AppendSamples(clientmodel.Samples) error

	// Get all of the metric fingerprints that are associated with the provided
	// label set.
	GetFingerprintsForLabelSet(clientmodel.LabelSet) (clientmodel.Fingerprints, error)

	// Get all of the metric fingerprints that are associated for a given label
	// name.
	GetFingerprintsForLabelName(clientmodel.LabelName) (clientmodel.Fingerprints, error)

	// Get the metric associated with the provided fingerprint.
	GetMetricForFingerprint(*clientmodel.Fingerprint) (clientmodel.Metric, error)

	// Get the two metric values that are immediately adjacent to a given time.
	GetValueAtTime(*clientmodel.Fingerprint, time.Time) Values
	// Get the boundary values of an interval: the first value older than the
	// interval start, and the first value younger than the interval end.
	GetBoundaryValues(*clientmodel.Fingerprint, Interval) Values
	// Get all values contained within a provided interval.
	GetRangeValues(*clientmodel.Fingerprint, Interval) Values
	// Get all label values that are associated with a given label name.
	GetAllValuesForLabel(clientmodel.LabelName) (clientmodel.LabelValues, error)
}

// View provides a view of the values in the datastore subject to the request
// of a preloading operation.
type View interface {
	GetValueAtTime(*clientmodel.Fingerprint, time.Time) Values
	GetBoundaryValues(*clientmodel.Fingerprint, Interval) Values
	GetRangeValues(*clientmodel.Fingerprint, Interval) Values

	// Destroy this view.
	Close()
}

type Series interface {
	Fingerprint() *clientmodel.Fingerprint
	Metric() clientmodel.Metric
}

type IteratorsForFingerprintBuilder interface {
	ForStream(stream *stream) (storage.RecordDecoder, storage.RecordFilter, storage.RecordOperator)
}
<|endoftext|>


<commit_before>// Copyright (c) 2019 The OpenSDS Authors.
//
//    Licensed under the Apache License, Version 2.0 (the "License"); you may
//    not use this file except in compliance with the License. You may obtain
//    a copy of the License at
//
//         http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//    License for the specific language governing permissions and limitations
//    under the License.
package lvm

import (
	"testing"
)

func TestMetricDriverSetup(t *testing.T) {
	var d = &MetricDriver{}

	if err := d.Setup(); err != nil {
		t.Errorf("Setup lvm metric  driver failed: %+v\n", err)
	}

}

func TestCollectMetrics(t *testing.T) {

	metricList := []string{"IOPS", "ReadThroughput", "WriteThroughput", "ResponseTime", "ServiceTime", "UtilizationPercentage"}
	var metricDriver = &MetricDriver{}
	metricDriver.Setup()
	metricArray, err := metricDriver.CollectMetrics(metricList, "sda")
	if err != nil {
		t.Errorf("collectMetrics call to lvm driver failed: %+v\n", err)
	}
	for _, m := range metricArray {
		t.Log(*m)
	}

}
<commit_msg> mock test for lvm metrics driver<commit_after>// Copyright (c) 2019 The OpenSDS Authors.
//
//    Licensed under the Apache License, Version 2.0 (the "License"); you may
//    not use this file except in compliance with the License. You may obtain
//    a copy of the License at
//
//         http://www.apache.org/licenses/LICENSE-2.0
//
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
//    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
//    License for the specific language governing permissions and limitations
//    under the License.
package lvm

import (
	"fmt"
	"reflect"
	"testing"

	"github.com/opensds/opensds/pkg/model"
	"github.com/opensds/opensds/pkg/utils/exec"
)

var metricMap map[string]float64 = map[string]float64{"IOPS": 3.16, "ReadThroughput": 4.17, "WriteThroughput": 134.74, "ResponseTime": 2.67, "ServiceTime": 4.00, "UtilizationPercentage": 1.26}
var metricToUnitMap map[string]string = map[string]string{"IOPS": "tps", "ReadThroughput": "KB/s", "WriteThroughput": "KB/s", "ResponseTime": "ms", "ServiceTime": "ms", "UtilizationPercentage": "%"}
var respMap map[string]*MetricFakeResp = map[string]*MetricFakeResp{
	"sar": {`05:26:43  IST       DEV       tps     rkB/s     wkB/s   areq-sz    aqu-sz     await     svctm     %util
			05:26:44      loop0      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop1      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop2      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop3      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop4      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop5      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop6      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop7      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      opensds--volumes--default-volume--b902e771--8e02--4099--b601--a6b3881f8      3.16      4.17    134.74     42.67      0.01      2.67      4.00      1.26
			05:26:44      loop8      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop9      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00
			05:26:44      loop10      0.00      0.00      0.00      0.00      0.00      0.00      0.00      0.00`, nil},
}
var expctdMetricList []string = []string{"IOPS", "ReadThroughput", "WriteThroughput", "ResponseTime", "ServiceTime", "UtilizationPercentage"}

func TestMetricDriverSetup(t *testing.T) {
	var d = &MetricDriver{}
	if err := d.Setup(); err != nil {
		t.Errorf("setup lvm metric  driver failed: %+v\n", err)
	}
}

type MetricFakeExecuter struct {
	RespMap map[string]*MetricFakeResp
}

type MetricFakeResp struct {
	out string
	err error
}

func (f *MetricFakeExecuter) Run(name string, args ...string) (string, error) {
	var cmd = name
	if name == "env" {
		cmd = args[1]
	}
	v, ok := f.RespMap[cmd]
	if !ok {
		return "", fmt.Errorf("can't find specified op: %s", args[1])
	}
	return v.out, v.err
}

func NewMetricFakeExecuter(respMap map[string]*MetricFakeResp) exec.Executer {
	return &MetricFakeExecuter{RespMap: respMap}
}

func TestValidateMetricsSupportList(t *testing.T) {
	var md = &MetricDriver{}
	md.Setup()
	returnedMetricList, err := md.ValidateMetricsSupportList(expctdMetricList, "volume")
	if err != nil {
		t.Error("failed to validate metric list:", err)
	}
	if !reflect.DeepEqual(expctdMetricList, returnedMetricList) {
		t.Errorf("expected %+v, got %+v\n", expctdMetricList, returnedMetricList)
	}
}

func TestCollectMetrics(t *testing.T) {
	var md = &MetricDriver{}
	md.Setup()
	md.cli.RootExecuter = NewMetricFakeExecuter(respMap)
	md.cli.BaseExecuter = NewMetricFakeExecuter(respMap)
	var tempMetricArray []*model.MetricSpec
	for _, element := range expctdMetricList {
		val := metricMap[element]
		expctdLabels := make(map[string]string)
		expctdLabels["device"] = "opensds--volumes--default-volume--b902e771--8e02--4099--b601--a6b3881f8"
		expctdmetricValue := &model.Metric{
			Timestamp: 123456,
			Value:     val,
		}
		expctdMetricValues := make([]*model.Metric, 0)
		expctdMetricValues = append(expctdMetricValues, expctdmetricValue)
		metric := &model.MetricSpec{
			InstanceID:   "b902e771--8e02--4099--b601--a6b3881f8",
			InstanceName: "opensds--volumes--default-volume--b902e771--8e02--4099--b601--a6b3881f8",
			Job:          "OpenSDS",
			Labels:       expctdLabels,
			Component:    "Volume",
			Name:         element,
			Unit:         metricToUnitMap[element],
			AggrType:     "",
			MetricValues: expctdMetricValues,
		}
		tempMetricArray = append(tempMetricArray, metric)
	}
	expectedMetrics := tempMetricArray
	retunMetrics, err := md.CollectMetrics(expctdMetricList, "b902e771--8e02--4099--b601--a6b3881f8")
	if err != nil {
		t.Error("failed to collect stats:", err)
	}
	// we can't use deep equal on metric spec objects as the timesatmp calulation is time.Now() in driver
	// validate equivalence of each metricspec fields against expected except timestamp
	var b bool = true
	for i, m := range expectedMetrics {
		b = b && reflect.DeepEqual(m.InstanceName, retunMetrics[i].InstanceName)
		b = b && reflect.DeepEqual(m.InstanceID, retunMetrics[i].InstanceID)
		b = b && reflect.DeepEqual(m.Job, retunMetrics[i].Job)
		b = b && reflect.DeepEqual(m.Labels, retunMetrics[i].Labels)
		b = b && reflect.DeepEqual(m.Component, retunMetrics[i].Component)
		b = b && reflect.DeepEqual(m.Unit, retunMetrics[i].Unit)
		b = b && reflect.DeepEqual(m.AggrType, retunMetrics[i].AggrType)
		for j, v := range m.MetricValues {
			b = b && reflect.DeepEqual(v.Value, retunMetrics[i].MetricValues[j].Value)
		}
	}
	if !b {
		t.Errorf("expected metric spec")
		printMetricSpec(expectedMetrics)
		t.Errorf("returned metric spec")
		printMetricSpec(retunMetrics)
	}
}

func printMetricSpec(m []*model.MetricSpec) {
	for _, p := range m {
		fmt.Errorf("%+v\n", p)
		for _, v := range p.MetricValues {
			fmt.Errorf("%+v\n", v)
		}
	}

}
<|endoftext|>


<commit_before><commit_msg>always log here<commit_after><|endoftext|>


<commit_before><commit_msg>read team.leave<commit_after><|endoftext|>


<commit_before>package sercom

import (
	sys "github.com/knieriem/g/syscall"
	"os"
	"path/filepath"
	"syscall"
)

const (
	initDefault = "b115200 l8 pn s1"
)

type hw struct {
	*os.File
	inCtl          bool
	t, tsav, tOrig sys.Termios
}

// Open a local serial port.
// Inictl is an optional string containing one ore more commands in Ctl() style
func Open(filename string, inictl string) (port Port, err error) {
	var file *os.File

	// NONBLOCK prevents Open from blocking
	// until DCD is asserted from modem
	if file, err = os.OpenFile(filename, os.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0); err != nil {
		return
	}
	p := new(dev)
	p.File = file
	p.name = filename
	p.encaps = p
	fd := file.Fd()
	err = setBlocking(fd)
	if err != nil {
		err = p.error("set blocking", err)
	}

	t := &p.t
	if err = sys.IoctlTermios(fd, sys.TCGETS, t); err != nil {
		err = p.error("get term attr", err)
		return
	}
	p.tOrig = p.t
	p.tsav = p.t

	t.Cflag |= sys.CLOCAL
	t.Lflag &^= sys.ICANON | sys.ECHO | sys.ISIG | sys.IEXTEN
	t.Iflag &^= sys.BRKINT | sys.ICRNL | sys.INPCK | sys.ISTRIP | sys.IXON
	t.Iflag |= sys.IGNPAR
	t.Oflag &^= sys.OPOST

	// timeout 1/10s after the last byte (at least one) has been read
	t.Cc[sys.VMIN] = 1
	t.Cc[sys.VTIME] = 1

	if err = p.Ctl(initDefault, inictl); err != nil {
		return
	}

	port = p
	return
}

func (p *dev) Close() error {
	sys.IoctlTermios(p.Fd(), sys.TCSETSW, &p.tOrig)
	return p.hw.Close()
}

func (d *dev) Drain() (err error) {
	err = sys.IoctlTermios(d.Fd(), sys.TCSETSW, &d.tsav) // drain and set parameters
	if err != nil {
		err = d.error("drain", err)
	}
	return
}

func (d *dev) Purge(in, out bool) {

}

func (d *dev) SetBaudrate(val int) (err error) {
	if speed, ok := speedMap[val]; !ok {
		err = d.errorf("open", "unsupported baud rate: %d", val)
		return
	} else {
		d.t.SetInSpeed(speed)
		d.t.SetOutSpeed(speed)
	}
	return d.updateCtl()
}

func (d *dev) SetWordlen(n int) error {
	t := &d.t

	t.Cflag &^= sys.CSIZE
	switch n {
	case 5:
		t.Cflag |= sys.CS5
	case 6:
		t.Cflag |= sys.CS6
	case 7:
		t.Cflag |= sys.CS7
	case 8:
		t.Cflag |= sys.CS8
	default:
		return d.errorf("open", "unsupported word len: %d", n)
	}
	return d.updateCtl()
}

func (d *dev) SetParity(parity byte) error {
	t := &d.t

	t.Cflag &^= sys.PARENB | sys.PARODD
	switch parity {
	case 'o':
		t.Cflag |= sys.PARODD
		fallthrough
	case 'e':
		t.Cflag |= sys.PARENB
	}
	return d.updateCtl()
}

func (d *dev) SetStopbits(n int) (err error) {
	switch n {
	case 1:
		d.t.Cflag &^= sys.CSTOPB
	case 2:
		d.t.Cflag |= sys.CSTOPB
	default:
		return d.errorf("open", "invalid number of stopbits: %d", n)
	}
	return d.updateCtl()
}

func (p *dev) SetRts(on bool) error {
	p.rts = on
	if on {
		return p.commfn("set rts", sys.TIOCMBIS, sys.TIOCM_RTS)
	}
	return p.commfn("clr rts", sys.TIOCMBIC, sys.TIOCM_RTS)
}
func (p *dev) SetDtr(on bool) error {
	p.dtr = on
	if on {
		return p.commfn("set dtr", sys.TIOCMBIS, sys.TIOCM_DTR)
	}
	return p.commfn("clr dtr", sys.TIOCMBIC, sys.TIOCM_DTR)
}

func (p *dev) commfn(name string, cmd int, f sys.Int) (err error) {
	if err = sys.IoctlModem(p.Fd(), cmd, &f); err != nil {
		return p.error(name, err)
	}
	return
}

func (d *dev) SetRtsCts(on bool) error {
	if on {
		d.t.Cflag |= sys.CRTSCTS
	} else {
		d.t.Cflag &^= sys.CRTSCTS
		d.SetRts(d.rts)
	}
	return d.updateCtl()
}

func (d *dev) updateCtl() (err error) {
	if d.inCtl {
		return
	}
	t := &d.t
	tsav := &d.tsav
	if t.Cflag == tsav.Cflag &&
		t.Lflag == tsav.Lflag &&
		t.Oflag == tsav.Oflag &&
		t.Cc[sys.VTIME] == tsav.Cc[sys.VTIME] &&
		t.Cc[sys.VMIN] == tsav.Cc[sys.VMIN] {
		return
	}
	if err = sys.IoctlTermios(d.Fd(), sys.TCSETSW, t); err == nil { // drain and set parameters
		d.tsav = d.t

		// It seems changing parameters also resets DTR/RTS lines;
		// put in the previously requested states again:
		d.SetRts(d.rts)
		d.SetDtr(d.dtr)
	}
	return
}

func (p *dev) ModemLines() LineState {
	var ls LineState
	return ls
}

var speedMap = map[int]int{
	50:      sys.B50,
	75:      sys.B75,
	110:     sys.B110,
	134:     sys.B134,
	150:     sys.B150,
	200:     sys.B200,
	300:     sys.B300,
	600:     sys.B600,
	1200:    sys.B1200,
	1800:    sys.B1800,
	2400:    sys.B2400,
	4800:    sys.B4800,
	9600:    sys.B9600,
	19200:   sys.B19200,
	38400:   sys.B38400,
	57600:   sys.B57600,
	115200:  sys.B115200,
	230400:  sys.B230400,
	460800:  sys.B460800,
	500000:  sys.B500000,
	576000:  sys.B576000,
	921600:  sys.B921600,
	1000000: sys.B1000000,
	1152000: sys.B1152000,
	1500000: sys.B1500000,
	2000000: sys.B2000000,
	2500000: sys.B2500000,
	3000000: sys.B3000000,
	3500000: sys.B3500000,
	4000000: sys.B4000000,
}

func setBlocking(fd uintptr) (err error) {
	var flags int

	flags, err = sys.Fcntl(fd, syscall.F_GETFL, 0)
	if err == nil {
		_, err = sys.Fcntl(fd, syscall.F_SETFL, flags&^syscall.O_NONBLOCK)
	}
	return
}

// Get a list of (probably) present serial devices
func DeviceList() (list []string) {
	list = append(list, globDev("/dev/ttyUSB[0-9]*")...)
	list = append(list, globDev("/dev/ttyACM[0-9]*")...)
	list = append(list, globDev("/dev/ttyS[0-9]*")...)
	return
}

func globDev(pat string) []string {
	s, _ := filepath.Glob(pat)
	return s
}
<commit_msg>sercom/linux: use CREAD<commit_after>package sercom

import (
	sys "github.com/knieriem/g/syscall"
	"os"
	"path/filepath"
	"syscall"
)

const (
	initDefault = "b115200 l8 pn s1"
)

type hw struct {
	*os.File
	inCtl          bool
	t, tsav, tOrig sys.Termios
}

// Open a local serial port.
// Inictl is an optional string containing one ore more commands in Ctl() style
func Open(filename string, inictl string) (port Port, err error) {
	var file *os.File

	// NONBLOCK prevents Open from blocking
	// until DCD is asserted from modem
	if file, err = os.OpenFile(filename, os.O_RDWR|syscall.O_NOCTTY|syscall.O_NONBLOCK, 0); err != nil {
		return
	}
	p := new(dev)
	p.File = file
	p.name = filename
	p.encaps = p
	fd := file.Fd()
	err = setBlocking(fd)
	if err != nil {
		err = p.error("set blocking", err)
	}

	t := &p.t
	if err = sys.IoctlTermios(fd, sys.TCGETS, t); err != nil {
		err = p.error("get term attr", err)
		return
	}
	p.tOrig = p.t
	p.tsav = p.t

	t.Cflag |= sys.CLOCAL | sys.CREAD
	t.Lflag &^= sys.ICANON | sys.ECHO | sys.ISIG | sys.IEXTEN
	t.Iflag &^= sys.BRKINT | sys.ICRNL | sys.INPCK | sys.ISTRIP | sys.IXON
	t.Iflag |= sys.IGNPAR
	t.Oflag &^= sys.OPOST

	// timeout 1/10s after the last byte (at least one) has been read
	t.Cc[sys.VMIN] = 1
	t.Cc[sys.VTIME] = 1

	if err = p.Ctl(initDefault, inictl); err != nil {
		return
	}

	port = p
	return
}

func (p *dev) Close() error {
	sys.IoctlTermios(p.Fd(), sys.TCSETSW, &p.tOrig)
	return p.hw.Close()
}

func (d *dev) Drain() (err error) {
	err = sys.IoctlTermios(d.Fd(), sys.TCSETSW, &d.tsav) // drain and set parameters
	if err != nil {
		err = d.error("drain", err)
	}
	return
}

func (d *dev) Purge(in, out bool) {

}

func (d *dev) SetBaudrate(val int) (err error) {
	if speed, ok := speedMap[val]; !ok {
		err = d.errorf("open", "unsupported baud rate: %d", val)
		return
	} else {
		d.t.SetInSpeed(speed)
		d.t.SetOutSpeed(speed)
	}
	return d.updateCtl()
}

func (d *dev) SetWordlen(n int) error {
	t := &d.t

	t.Cflag &^= sys.CSIZE
	switch n {
	case 5:
		t.Cflag |= sys.CS5
	case 6:
		t.Cflag |= sys.CS6
	case 7:
		t.Cflag |= sys.CS7
	case 8:
		t.Cflag |= sys.CS8
	default:
		return d.errorf("open", "unsupported word len: %d", n)
	}
	return d.updateCtl()
}

func (d *dev) SetParity(parity byte) error {
	t := &d.t

	t.Cflag &^= sys.PARENB | sys.PARODD
	switch parity {
	case 'o':
		t.Cflag |= sys.PARODD
		fallthrough
	case 'e':
		t.Cflag |= sys.PARENB
	}
	return d.updateCtl()
}

func (d *dev) SetStopbits(n int) (err error) {
	switch n {
	case 1:
		d.t.Cflag &^= sys.CSTOPB
	case 2:
		d.t.Cflag |= sys.CSTOPB
	default:
		return d.errorf("open", "invalid number of stopbits: %d", n)
	}
	return d.updateCtl()
}

func (p *dev) SetRts(on bool) error {
	p.rts = on
	if on {
		return p.commfn("set rts", sys.TIOCMBIS, sys.TIOCM_RTS)
	}
	return p.commfn("clr rts", sys.TIOCMBIC, sys.TIOCM_RTS)
}
func (p *dev) SetDtr(on bool) error {
	p.dtr = on
	if on {
		return p.commfn("set dtr", sys.TIOCMBIS, sys.TIOCM_DTR)
	}
	return p.commfn("clr dtr", sys.TIOCMBIC, sys.TIOCM_DTR)
}

func (p *dev) commfn(name string, cmd int, f sys.Int) (err error) {
	if err = sys.IoctlModem(p.Fd(), cmd, &f); err != nil {
		return p.error(name, err)
	}
	return
}

func (d *dev) SetRtsCts(on bool) error {
	if on {
		d.t.Cflag |= sys.CRTSCTS
	} else {
		d.t.Cflag &^= sys.CRTSCTS
		d.SetRts(d.rts)
	}
	return d.updateCtl()
}

func (d *dev) updateCtl() (err error) {
	if d.inCtl {
		return
	}
	t := &d.t
	tsav := &d.tsav
	if t.Cflag == tsav.Cflag &&
		t.Lflag == tsav.Lflag &&
		t.Oflag == tsav.Oflag &&
		t.Cc[sys.VTIME] == tsav.Cc[sys.VTIME] &&
		t.Cc[sys.VMIN] == tsav.Cc[sys.VMIN] {
		return
	}
	if err = sys.IoctlTermios(d.Fd(), sys.TCSETSW, t); err == nil { // drain and set parameters
		d.tsav = d.t

		// It seems changing parameters also resets DTR/RTS lines;
		// put in the previously requested states again:
		d.SetRts(d.rts)
		d.SetDtr(d.dtr)
	}
	return
}

func (p *dev) ModemLines() LineState {
	var ls LineState
	return ls
}

var speedMap = map[int]int{
	50:      sys.B50,
	75:      sys.B75,
	110:     sys.B110,
	134:     sys.B134,
	150:     sys.B150,
	200:     sys.B200,
	300:     sys.B300,
	600:     sys.B600,
	1200:    sys.B1200,
	1800:    sys.B1800,
	2400:    sys.B2400,
	4800:    sys.B4800,
	9600:    sys.B9600,
	19200:   sys.B19200,
	38400:   sys.B38400,
	57600:   sys.B57600,
	115200:  sys.B115200,
	230400:  sys.B230400,
	460800:  sys.B460800,
	500000:  sys.B500000,
	576000:  sys.B576000,
	921600:  sys.B921600,
	1000000: sys.B1000000,
	1152000: sys.B1152000,
	1500000: sys.B1500000,
	2000000: sys.B2000000,
	2500000: sys.B2500000,
	3000000: sys.B3000000,
	3500000: sys.B3500000,
	4000000: sys.B4000000,
}

func setBlocking(fd uintptr) (err error) {
	var flags int

	flags, err = sys.Fcntl(fd, syscall.F_GETFL, 0)
	if err == nil {
		_, err = sys.Fcntl(fd, syscall.F_SETFL, flags&^syscall.O_NONBLOCK)
	}
	return
}

// Get a list of (probably) present serial devices
func DeviceList() (list []string) {
	list = append(list, globDev("/dev/ttyUSB[0-9]*")...)
	list = append(list, globDev("/dev/ttyACM[0-9]*")...)
	list = append(list, globDev("/dev/ttyS[0-9]*")...)
	return
}

func globDev(pat string) []string {
	s, _ := filepath.Glob(pat)
	return s
}
<|endoftext|>


<commit_before><commit_msg>Fix received message handling and chat/thread focus<commit_after><|endoftext|>


<commit_before><commit_msg>Remove admin and api endpoint environment variable references<commit_after><|endoftext|>


<commit_before>// send.go - mixnet client send
// Copyright (C) 2018  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package session

import (
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"time"

	cConstants "github.com/katzenpost/client/constants"
	"github.com/katzenpost/core/constants"
	"github.com/katzenpost/core/crypto/rand"
	sConstants "github.com/katzenpost/core/sphinx/constants"
)

const roundTripTimeSlop = time.Duration(88 * time.Second)

// WaitForSent blocks until the message with the corresponding message ID is sent.
func (s *Session) WaitForSent(id MessageID) error {
	s.log.Debug("Waiting for message to be sent.")
	var waitCh chan Event
	var err error
	s.mapLock.Lock()
	msg, ok := s.messageIDMap[*id]
	if !ok {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	} else {
		if msg.Sent {
			return nil
		}
	}
	waitCh, ok = s.waitSentChans[*id]
	if ok {
		defer delete(s.waitSentChans, *id)
	} else {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	}
	s.mapLock.Unlock()
	if err != nil {
		return err
	}
	select {
	case <-waitCh:
	case <-time.After(1 * time.Minute):
		return fmt.Errorf("[%v] Failure waiting for reply, timeout", id)
	}
	s.log.Debug("Finished waiting. Message was sent.")
	return nil
}

// WaitForReply blocks until a reply is received.
func (s *Session) WaitForReply(id MessageID) ([]byte, error) {
	s.log.Debugf("WaitForReply message ID: %x\n", *id)
	err := s.WaitForSent(id)
	if err != nil {
		return nil, err
	}
	s.mapLock.Lock()
	waitCh, ok := s.waitChans[*id]
	if ok {
		defer delete(s.waitChans, *id)
	} else {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	}
	msg, ok := s.messageIDMap[*id]
	if ok {
		// XXX Consider what will happen because of this deletion
		// when we implement an ARQ based reliability.
		defer delete(s.messageIDMap, *id)
	} else {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	}
	s.log.Debug("reply eta is %v", msg.ReplyETA)
	s.mapLock.Unlock()
	if err != nil {
		return nil, err
	}
	select {
	case event := <-waitCh:
		e, ok := event.(*MessageReplyEvent)
		if !ok {
			s.log.Debug("UNKNOWN EVENT TYPE FOUND IN WAIT CHANNEL FOR THE GIVEN MESSAGE ID.")
		}
		return e.Payload, nil
	case <-time.After(msg.ReplyETA + roundTripTimeSlop):
		return nil, fmt.Errorf("[%v] Failure waiting for reply, timeout reached", id)
	}
	// unreachable
}

func (s *Session) sendNext() error {
	msg, err := s.egressQueue.Peek()
	if err != nil {
		return err
	}
	if msg == nil {
		return errors.New("send next failure, message is nil")
	}
	m, ok := msg.(*Message)
	if !ok {
		return errors.New("send next failure, unknown message type")
	}
	err = s.doSend(m)
	if err != nil {
		return err
	}
	_, err = s.egressQueue.Pop()
	return err
}

func (s *Session) doSend(msg *Message) error {
	surbID := [sConstants.SURBIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, surbID[:])
	if err != nil {
		return err
	}
	idStr := fmt.Sprintf("[%v]", hex.EncodeToString(surbID[:]))
	s.log.Debugf("doSend with SURB ID %x", idStr)
	key := []byte{}
	var eta time.Duration
	if msg.WithSURB {
		key, eta, err = s.minclient.SendCiphertext(msg.Recipient, msg.Provider, &surbID, msg.Payload)
	} else {
		err = s.minclient.SendUnreliableCiphertext(msg.Recipient, msg.Provider, msg.Payload)
	}
	if err != nil {
		return err
	}
	if msg.WithSURB {
		s.log.Debugf("doSend setting ReplyETA to %v", eta)
		msg.Key = key
		msg.SentAt = time.Now()
		msg.Sent = true
		msg.ReplyETA = eta
		s.mapLock.Lock()
		s.surbIDMap[surbID] = msg
		s.mapLock.Unlock()
	}

	eventCh, ok := s.waitSentChans[*msg.ID]
	if ok {
		select {
		case eventCh <- &MessageSentEvent{
			MessageID: msg.ID,
			Err:       nil,
		}:
		case <-time.After(3 * time.Second):
			s.log.Debug("timeout reached when attempting to sent to waitSentChans")
			break
		}
	} else {
		s.log.Debug("no waitSentChans map entry found for that message ID")
	}
	return nil
}

func (s *Session) sendLoopDecoy() error {
	s.log.Info("sending loop decoy")
	const loopService = "loop"
	serviceDesc, err := s.GetService(loopService)
	if err != nil {
		return err
	}
	payload := [constants.UserForwardPayloadLength]byte{}
	id := [cConstants.MessageIDLength]byte{}
	_, err = io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return err
	}
	msg := &Message{
		ID:        &id,
		Recipient: serviceDesc.Name,
		Provider:  serviceDesc.Provider,
		Payload:   payload[:],
		WithSURB:  true,
		IsDecoy:   true,
	}
	defer s.incrementDecoyLoopTally()
	return s.doSend(msg)
}

func (s *Session) sendDropDecoy() error {
	s.log.Info("sending drop decoy")
	const loopService = "loop"
	serviceDesc, err := s.GetService(loopService)
	if err != nil {
		return err
	}
	payload := [constants.UserForwardPayloadLength]byte{}
	id := [cConstants.MessageIDLength]byte{}
	_, err = io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return err
	}
	msg := &Message{
		ID:        &id,
		Recipient: serviceDesc.Name,
		Provider:  serviceDesc.Provider,
		Payload:   payload[:],
		WithSURB:  false,
		IsDecoy:   true,
	}
	return s.doSend(msg)
}

func (s *Session) composeMessage(recipient, provider string, message []byte) (*Message, error) {
	s.log.Debug("SendMessage")
	if len(message) > constants.UserForwardPayloadLength {
		return nil, fmt.Errorf("invalid message size: %v", len(message))
	}
	payload := make([]byte, constants.UserForwardPayloadLength)
	copy(payload, message)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return nil, err
	}
	var msg = Message{
		ID:        &id,
		Recipient: recipient,
		Provider:  provider,
		Payload:   payload,
		WithSURB:  true,
	}
	msg.SURBType = cConstants.SurbTypeKaetzchen
	return &msg, nil
}

// SendUnreliableMessage sends message without any automatic retransmissions.
func (s *Session) SendUnreliableMessage(recipient, provider string, message []byte) (*[cConstants.MessageIDLength]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message)
	if err != nil {
		return nil, err
	}

	s.mapLock.Lock()
	s.messageIDMap[*msg.ID] = msg
	s.waitChans[*msg.ID] = make(chan Event)
	s.waitSentChans[*msg.ID] = make(chan Event)
	s.mapLock.Unlock()

	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}
	return msg.ID, err
}
<commit_msg>big endian length prefix message payload<commit_after>// send.go - mixnet client send
// Copyright (C) 2018  David Stainton.
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as
// published by the Free Software Foundation, either version 3 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

package session

import (
	"encoding/binary"
	"encoding/hex"
	"errors"
	"fmt"
	"io"
	"time"

	cConstants "github.com/katzenpost/client/constants"
	"github.com/katzenpost/core/constants"
	"github.com/katzenpost/core/crypto/rand"
	sConstants "github.com/katzenpost/core/sphinx/constants"
)

const roundTripTimeSlop = time.Duration(88 * time.Second)

// WaitForSent blocks until the message with the corresponding message ID is sent.
func (s *Session) WaitForSent(id MessageID) error {
	s.log.Debug("Waiting for message to be sent.")
	var waitCh chan Event
	var err error
	s.mapLock.Lock()
	msg, ok := s.messageIDMap[*id]
	if !ok {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	} else {
		if msg.Sent {
			return nil
		}
	}
	waitCh, ok = s.waitSentChans[*id]
	if ok {
		defer delete(s.waitSentChans, *id)
	} else {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	}
	s.mapLock.Unlock()
	if err != nil {
		return err
	}
	select {
	case <-waitCh:
	case <-time.After(1 * time.Minute):
		return fmt.Errorf("[%v] Failure waiting for reply, timeout", id)
	}
	s.log.Debug("Finished waiting. Message was sent.")
	return nil
}

// WaitForReply blocks until a reply is received.
func (s *Session) WaitForReply(id MessageID) ([]byte, error) {
	s.log.Debugf("WaitForReply message ID: %x\n", *id)
	err := s.WaitForSent(id)
	if err != nil {
		return nil, err
	}
	s.mapLock.Lock()
	waitCh, ok := s.waitChans[*id]
	if ok {
		defer delete(s.waitChans, *id)
	} else {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	}
	msg, ok := s.messageIDMap[*id]
	if ok {
		// XXX Consider what will happen because of this deletion
		// when we implement an ARQ based reliability.
		defer delete(s.messageIDMap, *id)
	} else {
		err = fmt.Errorf("[%v] Failure waiting for reply, invalid message ID", id)
	}
	s.log.Debug("reply eta is %v", msg.ReplyETA)
	s.mapLock.Unlock()
	if err != nil {
		return nil, err
	}
	select {
	case event := <-waitCh:
		e, ok := event.(*MessageReplyEvent)
		if !ok {
			s.log.Debug("UNKNOWN EVENT TYPE FOUND IN WAIT CHANNEL FOR THE GIVEN MESSAGE ID.")
		}
		return e.Payload, nil
	case <-time.After(msg.ReplyETA + roundTripTimeSlop):
		return nil, fmt.Errorf("[%v] Failure waiting for reply, timeout reached", id)
	}
	// unreachable
}

func (s *Session) sendNext() error {
	msg, err := s.egressQueue.Peek()
	if err != nil {
		return err
	}
	if msg == nil {
		return errors.New("send next failure, message is nil")
	}
	m, ok := msg.(*Message)
	if !ok {
		return errors.New("send next failure, unknown message type")
	}
	err = s.doSend(m)
	if err != nil {
		return err
	}
	_, err = s.egressQueue.Pop()
	return err
}

func (s *Session) doSend(msg *Message) error {
	surbID := [sConstants.SURBIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, surbID[:])
	if err != nil {
		return err
	}
	idStr := fmt.Sprintf("[%v]", hex.EncodeToString(surbID[:]))
	s.log.Debugf("doSend with SURB ID %x", idStr)
	key := []byte{}
	var eta time.Duration
	if msg.WithSURB {
		key, eta, err = s.minclient.SendCiphertext(msg.Recipient, msg.Provider, &surbID, msg.Payload)
	} else {
		err = s.minclient.SendUnreliableCiphertext(msg.Recipient, msg.Provider, msg.Payload)
	}
	if err != nil {
		return err
	}
	if msg.WithSURB {
		s.log.Debugf("doSend setting ReplyETA to %v", eta)
		msg.Key = key
		msg.SentAt = time.Now()
		msg.Sent = true
		msg.ReplyETA = eta
		s.mapLock.Lock()
		s.surbIDMap[surbID] = msg
		s.mapLock.Unlock()
	}

	eventCh, ok := s.waitSentChans[*msg.ID]
	if ok {
		select {
		case eventCh <- &MessageSentEvent{
			MessageID: msg.ID,
			Err:       nil,
		}:
		case <-time.After(3 * time.Second):
			s.log.Debug("timeout reached when attempting to sent to waitSentChans")
			break
		}
	} else {
		s.log.Debug("no waitSentChans map entry found for that message ID")
	}
	return nil
}

func (s *Session) sendLoopDecoy() error {
	s.log.Info("sending loop decoy")
	const loopService = "loop"
	serviceDesc, err := s.GetService(loopService)
	if err != nil {
		return err
	}
	payload := [constants.UserForwardPayloadLength]byte{}
	id := [cConstants.MessageIDLength]byte{}
	_, err = io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return err
	}
	msg := &Message{
		ID:        &id,
		Recipient: serviceDesc.Name,
		Provider:  serviceDesc.Provider,
		Payload:   payload[:],
		WithSURB:  true,
		IsDecoy:   true,
	}
	defer s.incrementDecoyLoopTally()
	return s.doSend(msg)
}

func (s *Session) sendDropDecoy() error {
	s.log.Info("sending drop decoy")
	const loopService = "loop"
	serviceDesc, err := s.GetService(loopService)
	if err != nil {
		return err
	}
	payload := [constants.UserForwardPayloadLength]byte{}
	id := [cConstants.MessageIDLength]byte{}
	_, err = io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return err
	}
	msg := &Message{
		ID:        &id,
		Recipient: serviceDesc.Name,
		Provider:  serviceDesc.Provider,
		Payload:   payload[:],
		WithSURB:  false,
		IsDecoy:   true,
	}
	return s.doSend(msg)
}

func (s *Session) composeMessage(recipient, provider string, message []byte) (*Message, error) {
	s.log.Debug("SendMessage")
	if len(message) > constants.UserForwardPayloadLength {
		return nil, fmt.Errorf("invalid message size: %v", len(message))
	}
	rawLen := make([]byte, 4)
	binary.BigEndian.PutUint32(rawLen, uint32(len(message)))

	payload := [constants.UserForwardPayloadLength]byte{}
	copy(payload[:4], rawLen)
	copy(payload[4:], message)
	id := [cConstants.MessageIDLength]byte{}
	_, err := io.ReadFull(rand.Reader, id[:])
	if err != nil {
		return nil, err
	}
	var msg = Message{
		ID:        &id,
		Recipient: recipient,
		Provider:  provider,
		Payload:   payload[:],
		WithSURB:  true,
	}
	msg.SURBType = cConstants.SurbTypeKaetzchen
	return &msg, nil
}

// SendUnreliableMessage sends message without any automatic retransmissions.
func (s *Session) SendUnreliableMessage(recipient, provider string, message []byte) (*[cConstants.MessageIDLength]byte, error) {
	msg, err := s.composeMessage(recipient, provider, message)
	if err != nil {
		return nil, err
	}

	s.mapLock.Lock()
	s.messageIDMap[*msg.ID] = msg
	s.waitChans[*msg.ID] = make(chan Event)
	s.waitSentChans[*msg.ID] = make(chan Event)
	s.mapLock.Unlock()

	err = s.egressQueue.Push(msg)
	if err != nil {
		return nil, err
	}
	return msg.ID, err
}
<|endoftext|>


<commit_before>package shell

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"
	"syscall"

	"github.com/googollee/go-socket.io"
	"github.com/zenoss/glog"

	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/dfs"
)

var empty interface{}

const (
	PROCESSKEY string = "process"
	MAXBUFFER  int    = 8192
)

var webroot string

func init() {
	servicedHome := os.Getenv("SERVICED_HOME")
	if len(servicedHome) > 0 {
		webroot = servicedHome + "/share/shell/static"
	}
}

func staticRoot() string {
	if len(webroot) == 0 {
		_, filename, _, _ := runtime.Caller(1)
		return path.Join(path.Dir(path.Dir(filename)), "shell", "static")
	}
	return webroot
}

func NewProcessForwarderServer(addr string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Forwarder{addr: addr},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onForwarderDisconnect)
	// BUG: ZEN-10320
	// server.Handle("/", http.FileServer(http.Dir(staticRoot())))
	return server
}

func NewProcessExecutorServer(port string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Executor{port: port},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onExecutorDisconnect)
	// BUG: ZEN-10320
	// server.Handle("/", http.FileServer(http.Dir(staticRoot())))
	return server
}

func (p *ProcessServer) Handle(pattern string, handler http.Handler) error {
	p.sio.Handle(pattern, handler)
	return nil
}

func (p *ProcessServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.sio.ServeHTTP(w, r)
}

func (s *ProcessServer) onConnect(ns *socketio.NameSpace) {
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("Received connection")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")

	ns.On("process", func(ns *socketio.NameSpace, cfg *ProcessConfig) {
		// Kick it off
		glog.Infof("Received process packet")
		proc := s.actor.Exec(cfg)
		ns.Session.Values[PROCESSKEY] = proc

		// Wire up output
		go proc.ReadRequest(ns)
		go proc.WriteResponse(ns)
	})
	glog.Infof("Waiting for process packet")
}

func onForwarderDisconnect(ns *socketio.NameSpace) {
	// ns.Session.Values[PROCESSKEY].(*ProcessInstance)
}

func onExecutorDisconnect(ns *socketio.NameSpace) {
	inst := ns.Session.Values[PROCESSKEY].(*ProcessInstance)
	// Client disconnected, so kill the process
	inst.Signal <- int(syscall.SIGKILL)
	inst.closeIncoming()
}

func (p *ProcessInstance) Close() {
	p.closeIncoming()
	p.closeOutgoing()
}

func (p *ProcessInstance) closeIncoming() {
	glog.V(0).Infof("Closing incoming channels")
	if _, ok := <-p.Stdin; ok {
		close(p.Stdin)
	}
	if _, ok := <-p.Signal; ok {
		close(p.Signal)
	}
}

func (p *ProcessInstance) closeOutgoing() {
	glog.V(0).Infof("Closing outgoing channels")
	if _, ok := <-p.Stdout; ok {
		close(p.Stdout)
	}
	if _, ok := <-p.Stderr; ok {
		close(p.Stderr)
	}
	if _, ok := <-p.Result; ok {
		close(p.Result)
	}
}

func (p *ProcessInstance) ReadRequest(ns *socketio.NameSpace) {
	ns.On("signal", func(n *socketio.NameSpace, signal int) {
		p.Signal <- signal
	})

	ns.On("stdin", func(n *socketio.NameSpace, stdin string) {
		glog.Infof("Received stdin: %s", stdin)
		p.Stdin <- stdin
	})

	glog.V(0).Info("Hooked up incoming events!")
}

func (p *ProcessInstance) WriteRequest(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up input channels!")
	for p.Stdin != nil || p.Signal != nil {
		select {
		case m, ok := <-p.Stdin:
			if !ok {
				glog.V(0).Infof("Setting stdin to nil")
				p.Stdin = nil
				continue
			} else {
				ns.Emit("stdin", m)
			}
		case m, ok := <-p.Signal:
			if !ok {
				p.Signal = nil
				continue
			} else {
				ns.Emit("signal", m)
			}
		}
	}
}

func (p *ProcessInstance) ReadResponse(ns *socketio.NameSpace) {
	ns.On("stdout", func(n *socketio.NameSpace, stdout string) {
		glog.Infof("Process received stdout: %s", stdout)
		p.Stdout <- stdout
	})

	ns.On("stderr", func(n *socketio.NameSpace, stderr string) {
		glog.Infof("Process received stderr: %s", stderr)
		p.Stderr <- stderr
	})

	ns.On("result", func(n *socketio.NameSpace, result Result) {
		glog.Infof("Process received stderr: %s", result)
		p.Result <- result
	})
	glog.V(0).Info("Hooked up outgoing events!")

}

func (p *ProcessInstance) WriteResponse(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up output channels!")
	for p.Stdout != nil || p.Stderr != nil || p.Result != nil {
		select {
		case m, ok := <-p.Stdout:
			if !ok {
				p.Stdout = nil
				continue
			} else {
				glog.Infof("Emitting stdout: %s", m)
				ns.Emit("stdout", m)
			}
		case m, ok := <-p.Stderr:
			if !ok {
				p.Stderr = nil
				continue
			} else {
				glog.Infof("Emitting stderr: %s", m)
				ns.Emit("stderr", m)
			}
		case m, ok := <-p.Result:
			if !ok {
				p.Result = nil
				continue
			} else {
				glog.Infof("Emitting result: %s", m)
				ns.Emit("result", m)
			}
		}
	}
}

func (f *Forwarder) Exec(cfg *ProcessConfig) *ProcessInstance {

	// Dial the remote ProcessServer
	host := strings.Split(f.addr, ":")[0]
	client, err := socketio.Dial(fmt.Sprintf("http://%s:50000/", host))

	if err != nil {
		glog.Fatalf("Unable to contact remote process server: %v", err)
	}

	client.On("connect", func(ns *socketio.NameSpace) {
		ns.Emit("process", cfg)
	})

	ns := client.Of("")
	proc := &ProcessInstance{
		Stdin:  make(chan string),
		Stdout: make(chan string),
		Stderr: make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	client.On("disconnect", func(ns *socketio.NameSpace) {
		glog.Infof("Disconnected!")
		proc.Close()
	})

	go proc.ReadResponse(ns)
	go proc.WriteRequest(ns)

	go client.Run()

	return proc
}

func (e *Executor) Exec(cfg *ProcessConfig) *ProcessInstance {
	return StartDocker(cfg, e.port)
}

func StartDocker(cfg *ProcessConfig, port string) *ProcessInstance {
	dfs.Lock.Lock()
	dfs.Lock.Unlock()

	var (
		runner   Runner
		service  *dao.Service
		services []*dao.Service
	)

	// Create a control plane client to look up the service
	cp, err := serviced.NewControlClient(port)
	if err != nil {
		glog.Fatalf("Could not create a control plane client %v", err)
	}
	glog.Infof("Connected to the control plane at %s", port)

	err = (*cp).GetServices(&empty, &services)
	for _, svc := range services {
		if svc.Id == cfg.ServiceId || svc.Name == cfg.ServiceId {
			service = svc
			break
		}
	}

	if service == nil {
		glog.Fatalf("Unable to find service %s", cfg.ServiceId)
	}
	glog.Infof("Found service %s", cfg.ServiceId)

	// Bind mount on /serviced
	dir, bin, err := serviced.ExecPath()
	if err != nil {
		glog.Fatalf("Unable to find serviced binary: %v", err)
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// Bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// Get the shell command
	shellcmd := cfg.Command
	if cfg.Command == "" {
		shellcmd = "su -"
	}

	// Get the serviced command
	svcdcmd := fmt.Sprintf("/serviced/%s", bin)

	// Get the proxy command
	proxycmd := []string{svcdcmd, "-logtostderr=false", "proxy", "-logstash=false", "-autorestart=false", service.Id, shellcmd}

	// Get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		glog.Fatalf("Docker not found: %v", err)
	}
	argv := []string{"run", "-rm", "-v", servicedVolume, "-v", pwdVolume}
	argv = append(argv, cfg.Envv...)

	if cfg.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	argv = append(argv, service.ImageId)
	argv = append(argv, proxycmd...)

	glog.Infof("%s %s", docker, argv)
	runner, err = CreateCommand(docker, argv)
	if err != nil {
		glog.Fatalf("Unable to run command {%s %s} %v", docker, argv, err)
	}

	// Wire it up
	inst := &ProcessInstance{
		Stdout: runner.StdoutPipe(),
		Stderr: runner.StderrPipe(),
		Stdin:  make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	go func() {
		go func() {
			glog.Infof("Reading from stdin/signal channels")
			for inst.Stdin != nil || inst.Signal != nil {
				select {
				case m, ok := <-inst.Stdin:
					if ok {
						runner.Write([]byte(m))
					} else {
						inst.Stdin = nil
					}
				case s, ok := <-inst.Signal:
					if ok {
						runner.Signal(syscall.Signal(s))
					} else {
						inst.Signal = nil
					}
				}
			}
		}()

		if err := runner.Reader(MAXBUFFER); err != nil {
			inst.Result <- Result{1, err, NORMAL}
		} else {
			inst.Result <- Result{0, err, NORMAL}
		}
	}()

	return inst
}
<commit_msg>clarifying dfs lock acquisition<commit_after>package shell

import (
	"fmt"
	"net/http"
	"os"
	"os/exec"
	"path"
	"runtime"
	"strings"
	"syscall"

	"github.com/googollee/go-socket.io"
	"github.com/zenoss/glog"

	"github.com/zenoss/serviced"
	"github.com/zenoss/serviced/dao"
	"github.com/zenoss/serviced/dfs"
)

var empty interface{}

const (
	PROCESSKEY string = "process"
	MAXBUFFER  int    = 8192
)

var webroot string

func init() {
	servicedHome := os.Getenv("SERVICED_HOME")
	if len(servicedHome) > 0 {
		webroot = servicedHome + "/share/shell/static"
	}
}

func staticRoot() string {
	if len(webroot) == 0 {
		_, filename, _, _ := runtime.Caller(1)
		return path.Join(path.Dir(path.Dir(filename)), "shell", "static")
	}
	return webroot
}

func NewProcessForwarderServer(addr string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Forwarder{addr: addr},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onForwarderDisconnect)
	// BUG: ZEN-10320
	// server.Handle("/", http.FileServer(http.Dir(staticRoot())))
	return server
}

func NewProcessExecutorServer(port string) *ProcessServer {
	server := &ProcessServer{
		sio:   socketio.NewSocketIOServer(&socketio.Config{}),
		actor: &Executor{port: port},
	}
	server.sio.On("connect", server.onConnect)
	server.sio.On("disconnect", onExecutorDisconnect)
	// BUG: ZEN-10320
	// server.Handle("/", http.FileServer(http.Dir(staticRoot())))
	return server
}

func (p *ProcessServer) Handle(pattern string, handler http.Handler) error {
	p.sio.Handle(pattern, handler)
	return nil
}

func (p *ProcessServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.sio.ServeHTTP(w, r)
}

func (s *ProcessServer) onConnect(ns *socketio.NameSpace) {
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("Received connection")
	glog.Infof("===========================================")
	glog.Infof("===========================================")
	glog.Infof("===========================================")

	ns.On("process", func(ns *socketio.NameSpace, cfg *ProcessConfig) {
		// Kick it off
		glog.Infof("Received process packet")
		proc := s.actor.Exec(cfg)
		ns.Session.Values[PROCESSKEY] = proc

		// Wire up output
		go proc.ReadRequest(ns)
		go proc.WriteResponse(ns)
	})
	glog.Infof("Waiting for process packet")
}

func onForwarderDisconnect(ns *socketio.NameSpace) {
	// ns.Session.Values[PROCESSKEY].(*ProcessInstance)
}

func onExecutorDisconnect(ns *socketio.NameSpace) {
	inst := ns.Session.Values[PROCESSKEY].(*ProcessInstance)
	// Client disconnected, so kill the process
	inst.Signal <- int(syscall.SIGKILL)
	inst.closeIncoming()
}

func (p *ProcessInstance) Close() {
	p.closeIncoming()
	p.closeOutgoing()
}

func (p *ProcessInstance) closeIncoming() {
	glog.V(0).Infof("Closing incoming channels")
	if _, ok := <-p.Stdin; ok {
		close(p.Stdin)
	}
	if _, ok := <-p.Signal; ok {
		close(p.Signal)
	}
}

func (p *ProcessInstance) closeOutgoing() {
	glog.V(0).Infof("Closing outgoing channels")
	if _, ok := <-p.Stdout; ok {
		close(p.Stdout)
	}
	if _, ok := <-p.Stderr; ok {
		close(p.Stderr)
	}
	if _, ok := <-p.Result; ok {
		close(p.Result)
	}
}

func (p *ProcessInstance) ReadRequest(ns *socketio.NameSpace) {
	ns.On("signal", func(n *socketio.NameSpace, signal int) {
		p.Signal <- signal
	})

	ns.On("stdin", func(n *socketio.NameSpace, stdin string) {
		glog.Infof("Received stdin: %s", stdin)
		p.Stdin <- stdin
	})

	glog.V(0).Info("Hooked up incoming events!")
}

func (p *ProcessInstance) WriteRequest(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up input channels!")
	for p.Stdin != nil || p.Signal != nil {
		select {
		case m, ok := <-p.Stdin:
			if !ok {
				glog.V(0).Infof("Setting stdin to nil")
				p.Stdin = nil
				continue
			} else {
				ns.Emit("stdin", m)
			}
		case m, ok := <-p.Signal:
			if !ok {
				p.Signal = nil
				continue
			} else {
				ns.Emit("signal", m)
			}
		}
	}
}

func (p *ProcessInstance) ReadResponse(ns *socketio.NameSpace) {
	ns.On("stdout", func(n *socketio.NameSpace, stdout string) {
		glog.Infof("Process received stdout: %s", stdout)
		p.Stdout <- stdout
	})

	ns.On("stderr", func(n *socketio.NameSpace, stderr string) {
		glog.Infof("Process received stderr: %s", stderr)
		p.Stderr <- stderr
	})

	ns.On("result", func(n *socketio.NameSpace, result Result) {
		glog.Infof("Process received stderr: %s", result)
		p.Result <- result
	})
	glog.V(0).Info("Hooked up outgoing events!")

}

func (p *ProcessInstance) WriteResponse(ns *socketio.NameSpace) {
	glog.V(0).Info("Hooking up output channels!")
	for p.Stdout != nil || p.Stderr != nil || p.Result != nil {
		select {
		case m, ok := <-p.Stdout:
			if !ok {
				p.Stdout = nil
				continue
			} else {
				glog.Infof("Emitting stdout: %s", m)
				ns.Emit("stdout", m)
			}
		case m, ok := <-p.Stderr:
			if !ok {
				p.Stderr = nil
				continue
			} else {
				glog.Infof("Emitting stderr: %s", m)
				ns.Emit("stderr", m)
			}
		case m, ok := <-p.Result:
			if !ok {
				p.Result = nil
				continue
			} else {
				glog.Infof("Emitting result: %s", m)
				ns.Emit("result", m)
			}
		}
	}
}

func (f *Forwarder) Exec(cfg *ProcessConfig) *ProcessInstance {

	// Dial the remote ProcessServer
	host := strings.Split(f.addr, ":")[0]
	client, err := socketio.Dial(fmt.Sprintf("http://%s:50000/", host))

	if err != nil {
		glog.Fatalf("Unable to contact remote process server: %v", err)
	}

	client.On("connect", func(ns *socketio.NameSpace) {
		ns.Emit("process", cfg)
	})

	ns := client.Of("")
	proc := &ProcessInstance{
		Stdin:  make(chan string),
		Stdout: make(chan string),
		Stderr: make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	client.On("disconnect", func(ns *socketio.NameSpace) {
		glog.Infof("Disconnected!")
		proc.Close()
	})

	go proc.ReadResponse(ns)
	go proc.WriteRequest(ns)

	go client.Run()

	return proc
}

func (e *Executor) Exec(cfg *ProcessConfig) *ProcessInstance {
	return StartDocker(cfg, e.port)
}

func StartDocker(cfg *ProcessConfig, port string) *ProcessInstance {
	// Acquire and release the lock to start a container from the latest image
	dfs.Lock.Lock()
	dfs.Lock.Unlock()

	var (
		runner   Runner
		service  *dao.Service
		services []*dao.Service
	)

	// Create a control plane client to look up the service
	cp, err := serviced.NewControlClient(port)
	if err != nil {
		glog.Fatalf("Could not create a control plane client %v", err)
	}
	glog.Infof("Connected to the control plane at %s", port)

	err = (*cp).GetServices(&empty, &services)
	for _, svc := range services {
		if svc.Id == cfg.ServiceId || svc.Name == cfg.ServiceId {
			service = svc
			break
		}
	}

	if service == nil {
		glog.Fatalf("Unable to find service %s", cfg.ServiceId)
	}
	glog.Infof("Found service %s", cfg.ServiceId)

	// Bind mount on /serviced
	dir, bin, err := serviced.ExecPath()
	if err != nil {
		glog.Fatalf("Unable to find serviced binary: %v", err)
	}
	servicedVolume := fmt.Sprintf("%s:/serviced", dir)

	// Bind mount the pwd
	dir, err = os.Getwd()
	pwdVolume := fmt.Sprintf("%s:/mnt/pwd", dir)

	// Get the shell command
	shellcmd := cfg.Command
	if cfg.Command == "" {
		shellcmd = "su -"
	}

	// Get the serviced command
	svcdcmd := fmt.Sprintf("/serviced/%s", bin)

	// Get the proxy command
	proxycmd := []string{svcdcmd, "-logtostderr=false", "proxy", "-logstash=false", "-autorestart=false", service.Id, shellcmd}

	// Get the docker start command
	docker, err := exec.LookPath("docker")
	if err != nil {
		glog.Fatalf("Docker not found: %v", err)
	}
	argv := []string{"run", "-rm", "-v", servicedVolume, "-v", pwdVolume}
	argv = append(argv, cfg.Envv...)

	if cfg.IsTTY {
		argv = append(argv, "-i", "-t")
	}

	argv = append(argv, service.ImageId)
	argv = append(argv, proxycmd...)

	glog.Infof("%s %s", docker, argv)
	runner, err = CreateCommand(docker, argv)
	if err != nil {
		glog.Fatalf("Unable to run command {%s %s} %v", docker, argv, err)
	}

	// Wire it up
	inst := &ProcessInstance{
		Stdout: runner.StdoutPipe(),
		Stderr: runner.StderrPipe(),
		Stdin:  make(chan string),
		Signal: make(chan int),
		Result: make(chan Result),
	}

	go func() {
		go func() {
			glog.Infof("Reading from stdin/signal channels")
			for inst.Stdin != nil || inst.Signal != nil {
				select {
				case m, ok := <-inst.Stdin:
					if ok {
						runner.Write([]byte(m))
					} else {
						inst.Stdin = nil
					}
				case s, ok := <-inst.Signal:
					if ok {
						runner.Signal(syscall.Signal(s))
					} else {
						inst.Signal = nil
					}
				}
			}
		}()

		if err := runner.Reader(MAXBUFFER); err != nil {
			inst.Result <- Result{1, err, NORMAL}
		} else {
			inst.Result <- Result{0, err, NORMAL}
		}
	}()

	return inst
}
<|endoftext|>